import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

1
devtools/docs/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
_book

41
devtools/docs/README.md Normal file
View file

@ -0,0 +1,41 @@
# Firefox Developer Tools
Hello! This documentation is for developers who want to work on the
developer tools. If you are looking for general docs about how to use
the tools, checkout [this MDN
page](https://developer.mozilla.org/en-US/docs/Tools).
These docs explain how the developer tools work at high-level, as well
as providing links to reference documentation. This is a good starting
point if you are a new contributor, or want to learn how our protocol,
a specific tool, or something else works.
If you are looking to **start hacking** on the developer tools, all of
this information is documented on the
[Hacking](https://wiki.mozilla.org/DevTools/Hacking) wiki page.
A very quick version:
```
$ hg clone http://hg.mozilla.org/integration/fx-team
$ ./mach build
$ ./mach run -P development
```
You can also clone via git from
`https://github.com/mozilla/gecko-dev.git`. Note that the workflow for
submitting patches may be a little different if using git.
Please see the [Hacking](https://wiki.mozilla.org/DevTools/Hacking)
page for a lot more information!
All of our **coding standards** are documented on the [Coding
Standards](https://wiki.mozilla.org/DevTools/CodingStandards) wiki
page.
We use ESLint to enforce coding standards, and if you can run it
straight from the command like this:
```
./mach eslint path/to/directory
```

19
devtools/docs/SUMMARY.md Normal file
View file

@ -0,0 +1,19 @@
# Summary
* [Tool Architectures](tools.md)
* [Inspector](inspector-panel.md)
* [Memory](memory-panel.md)
* [Debugger](debugger-panel.md)
* [Responsive Design Mode](responsive-design-mode.md)
* [Frontend](frontend.md)
* [Panel SVGs](svgs.md)
* [React](react.md)
* [Guidelines](react-guidelines.md)
* [Tips](react-tips.md)
* [Redux](redux.md)
* [Guidelines](redux-guidelines.md)
* [Tips](redux-tips.md)
* [Backend](backend.md)
* [Protocol](protocol.md)
* [Debugger API](debugger-api.md)

2
devtools/docs/backend.md Normal file
View file

@ -0,0 +1,2 @@
These files provide information about general backend architecture.

View file

@ -0,0 +1,9 @@
The Debugger API is a low-level API that provides methods for
introspecting and affecting a target environment like a page. You can
find JavaScript sources, set breakpoints on them, and more.
This API is completely documented on the [Debgger
API](https://developer.mozilla.org/en-US/docs/Tools/Debugger-API) MDN
page

View file

@ -0,0 +1,2 @@
This is just a stub until we write debugger docs.

View file

@ -0,0 +1,2 @@
These files provide information about general frontend architecture.

View file

@ -0,0 +1,167 @@
# HTTP Inspector (aka XHR Spy)
This document is intended as a description of HTTP Inspector feature allowing
inline inspection of HTTP logs displayed in the Console panel. The documents
focuses on internal architecture.
For detailed feature description see the following doc
(many screenshots included):
https://docs.google.com/document/d/1zQniwU_dkt-VX1qY1Vp-SWxEVA4uFcDCrtH03tGoHHM/edit#
_HTTP Inspector feature is available in the Console panel (for web developers)
as well as in the Browser Console (for devtools and extension developers)._
The current implementation is based on React (no XUL) and some of the existing
components should also be used when porting the Network panel to HTML.
The entire feature lives in `devtools/client/webconsole/net` directory.
## General Description
The entry point for HTTP log inspection is represented by an expand/toggle
button displayed in front a log in the Console panel:
[+] GET XHR http://example.com/test-request.php
Clicking on the [+] button expands the log and shows a body with HTTP details
right underneath. The info body is rendered by:
`devtools/client/webconsole/net/components/net-info-body` component.
HTTP info is divided into several tabs:
* Headers: send and received HTTP headers
* Params: URL parameters (query string)
* Post: HTTP post body
* Response: HTTP response body
* Cookies: Sent and received cookies
### Headers Tab
`devtools/client/webconsole/net/components/headers-tab`
This is the default active tab and it's responsible for rendering
HTTP headers. There are several header groups displayed:
* Response Headers
* Requests Headers
* Cached Headers (not implemented yet)
Individual sections are expandable/collapsible.
Rendering of the groups is done by `NetInfoGroup` and `NetInfoGroupList`
components.
### Params Tab
`devtools/client/webconsole/net/components/params-tab`
This tab is responsible for rendering URL parameters (query string)
and it's available only if the URL has any parameters. Individual
parameters are parsed and displayed as a list of name/value pairs.
Rendering of the parameter list is done by `NetInfoParams` component.
### Post Tab
`devtools/client/webconsole/net/components/post-tab`
This tab is responsible for rendering HTTP post body sent to the server.
### Response Tab
`devtools/client/webconsole/net/components/response-tab`
This tab is responsible for rendering HTTP response body received from
the server. There might be more than one section displaying the data
depending on the current response mime-type.
* Raw Data: This section is always displayed. It renders data in a raw
form just like they are received from the server.
* JSON: This section is available in case of JSON responses [1].
It parses the response and displays it as an expandable tree.
* Image: This section is available in case of image responses [2].
The response is decoded and displayed as an image.
* XML: this section is available in case of HTML/XML responses [3]
The response is parsed using DOM parser and displayed as an XML markup.
[1] List of JSON mime-types: `devtools/client/webconsole/net/utils/json`
[2] List of Image mime-types: `devtools/client/webconsole/net/utils/json`
[3] List of XML/HTML mime-types: `devtools/client/webconsole/net/utils/net`
Response data are fetched using `LongStringClient`, so if data are bigger
than defined limit (see `devtools/server/main.js - LONG_STRING_LENGTH)
the user needs to manually require the rest (there is a link at the end
of incomplete response body that allows this).
The raw section is collapsed by default if there is another presentation
of the data.
### Cookies Tab
`devtools/client/webconsole/net/components/cookies-tab`
This tab is responsible for displaying HTTP cookies.
There are two groups:
* Request Cookies
* Response Cookies
Rendering of the groups is done by `NetInfoGroup` and `NetInfoGroupList`
components. The tab is not presented if there are no cookies.
## Architecture
This sections describes internal architecture of HTTPi feature.
### Main
`devtools/client/webconsole/net/main`
This is the main module of HTTPi. It represents the root module
of the feature.
The main responsibility of the module is handling network logs forwarded
from webconsole.js. This modules creates one instance of `NetRequest`
object for every `NetworkEvent` (one object for every HTTP request).
### NetRequest
`devtools/client/webconsole/net/net-request`
This module represents `NetRequest` object. It's the internal representation
of HTTP request and it keeps its state. All HTTP details fetched dynamically
from the backend are stored in this object.
This object is responsible for:
* Adding a toggle button in Console UI (displayed in front of HTTP requests)
* Listening for a click event on the toggle button.
* Sending messages to web console client object to request HTTP details.
* Refreshing the UI as HTTP details are coming from the overlay.
Note that `NetRequest` is using a small helper object `DataProvider` for
requesting HTTP details. `DataProvider` is the connection between `NetRequest`
and the backend.
### Data Provider
`devtools/client/webconsole/net/data-provider`
This module is using webconsole client object to get data from the backend.
### Utils
`devtools/client/webconsole/net/utils`
There are also some utility modules implementing helper functions.
The important thing is that these modules doesn't require any chrome
privileges and are ready to run inside content scope.
### Components
* `NetInfoBody` Renders the entire HTTP details body displayed when the
user expands a network log.
* `NetInfoGroup` Renders a group (a section within tab). For example,
Request Headers section in Headers tab corresponds to one group.
* `NetInfoGroupList` List of groups. There might be more groups of data
within one tab. For example, the Headers tab has Requested and Response
headers groups.
* `NetInfoParams` List of name-value pairs. It's used e.g. by the Headers
or Params tab.
* `HeadersTab` Displays HTTP headers.
* `PostTab` Displays HTTP posted body data.
* `ParamsTab` Displays URL query string.
* `ResponseTab` Displays HTTP response body data.
* `CookiesTab` Displays cookies.
* `Spinner` Represents a throbber displayed when the UI is waiting for
incoming data.
* `SizeLimit` Represents a link that can be used to fetch the
rest of data from the backend (debugger server). Used for HTTP post
and response bodies.
* `XmlView` Renders XML markup.

View file

@ -0,0 +1,98 @@
# High-Level Inspector Architecture
## UI structure
The Inspector panel is a tab in the toolbox. Like all tabs, it's in its own iframe.
The high-level hierarchy looks something like this:
Toolbox
|
InspectorPanel
|
+-------------+------------------+---------------+
| | | |
MarkupView SelectorSearch HTMLBreadcrumbs ToolSidebar widget (iframes)
|
+- RuleView
|
+- ComputedView
|
+- LayoutView
|
+- FontInspector
|
+- AnimationInspector
## Server dependencies
- The inspector panel relies on a series of actors that live on the server.
- Some of the most important actors are actually instantiated by the toolbox, because these actors are needed for other panels to preview and link to DOM nodes. For example, the webconsole needs to output DOM nodes, highlight them in the page on mouseover, and open them in the inspector on click. This is achieved using some of the same actors that the inspector panel uses.
- See Toolbox.prototype.initInspector: This method instantiates the InspectorActor, WalkerActor and HighlighterActor lazily, whenever they're needed by a panel.
## Panel loading overview
- As with other panels, this starts with Toolbox.prototype.loadTool(id)
- For the inspector though, this calls Toolbox.prototype.initInspector
- When the panel's open method is called:
- It uses the WalkerActor for the first time to know the default selected node (which could be a node that was selected before on the same page).
- It starts listening to the WalkerActor's "new-root" events to know when to display a new DOM tree (when there's a page navigation).
- It creates the breadcrumbs widget, the sidebar widget, the search widget, the markup-view
- Sidebar:
- When this widget initializes, it loads its sub-iframes (rule-view, ...)
- Each of these iframes contain panel that, in turn, listen to inspector events like "new-node-front" to know when to retrieve information about a node (i.e the rule-view will fetch the css rules for a node).
- Markup-view:
- This panel initializes in its iframe, and gets a reference to the WalkerActor. It uses it to know the DOM tree to display. It knows when nodes change (markup-mutations), and knows what root node to start from.
- It only displays the nodes that are supposed to be visible at first (usually html, head, body and direct children of body).
- Then, as you expand nodes, it uses the WalkerActor to get more nodes lazily. It only ever knows data about nodes that have already been expanded once in the UI.
## Server-side structure
Simplified actor hierarchy
InspectorActor
|
+---------------+
| |
WalkerActor PageStyleActor (for rule-view/computed-view)
| |
NodeActor StyleRuleActor
__InspectorActor__
This tab-level actor is the one the inspector-panel connects to. It doesn't do much apart from creating and returning the WalkerActor and PageStyleActor.
__WalkerActor__
- Single most important part of the inspector panel.
- Responsible for walking the DOM on the current page but:
- also walks iframes
- also finds pseudo-elements ::before and ::after
- also finds anonymous content (e.g. in the BrowserToolbox)
- The actor uses an instance of inIDeepTreeWalker to walk the DOM
- Provides a tree of NodeActor objects that reflects the DOM.
- But only has a partial knowledge of the DOM (what is currently displayed/expanded in the MarkupView). It doesn't need to walk the whole tree when you first instantiate it.
- Reflects some of the usual DOM APIs like querySelector.
- Note that methods like querySelector return arbitrarily nested NodeActors, in which case the WalkerActor also sends the list of parents to link the returned nodes to the closest known nodes, so the UI can display the tree correctly.
- Emits events when there are DOM mutations. These events are sent to the front-end and used to, for example refresh the markup-view. This uses an instance of MutationObserver (https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) configured with, in particular, nativeAnonymousChildList set to true, so that mutation events are also sent when pseudo elements are added/removed via css.
__NodeActor__
- Representation of a single DOM node (tagname, namespace, attributes, parent, sibblings, ...), which panels use to display previews of nodes.
- Also provide useful methods to:
- change attributes
- scroll into view
- get event listeners data
- get image data
- get unique css selector
## Highlighters
One of the important aspects of the inspector is the highlighters.
Some documentation already at: https://wiki.mozilla.org/DevTools/Highlighter
We don't just have 1 highlighter, we have a framework for highlighters:
- a (chrome-only) platform API to inject markup in a native-anonymous node in content (that works on all targets)
- a number of specific highlighter implementations (css transform, rect, selector, geometry, rulers, ...)
- a CustomHighlighterActor to get instances of specific highlighters
The entry point is toolbox-highlighter-utils.js:
- get it with toolbox.highlighterUtils
- use this to easily highlight any node in the page (with the usual box model highlighter),
- also use this to instantiate an other specific highlighter

View file

@ -0,0 +1,226 @@
# Memory Tool Architecture
The memory tool is built of three main elements:
1. The live heap graph exists in memory, and is managed by the C++ allocator and
garbage collector. In order to get access to the structure of this graph, a
specialized interface is created to represent its state. The `JS::ubi::Node`
is the basis for this representation. This interface can be created from the
live heap graph, or a serialized, offline snapshot from a previous moment in
time. Our various heap analyses (census, dominator trees, shortest paths,
etc) run on top of `JS::ubi::Node` graphs. The `ubi` in the name stands for
"ubiquitous" and provides a namespace for memory analyses in C++ code.
2. The `HeapAnalysesWorker` runs in a worker thread, performing analyses on
snapshots and translating the results into something the frontend can render
simply and quickly. The `HeapAnalysesClient` is used to communicate between
the worker and the main thread.
3. Finally, the last element is the frontend that renders data received from the
`HeapAnalysesClient` to the DOM and translates user input into requests for
new data with the `HeapAnalysesClient`.
Unlike other tools (such as the JavaScript debugger), the memory tool makes very
little use of the Remote Debugger Server and the actors that reside in it. Use
of the [`MemoryActor`](devtools/server/actors/memory.js) is limited to toggling
allocation stack recording on and off, and transferring heap snapshots from the
debuggee (which is on the server) to the `HeapAnalysesWorker` (which is on the
client). A nice benefit that naturally emerges, is that supporting "legacy"
servers (eg, using Firefox Developer Edition as a client to remote debug a
release Firefox for Android server) is a no-op. As we add new analyses, we can
run them on snapshots taken on old servers no problem. The only requirement is
that changes to the snapshot format itself remain backwards compatible.
## `JS::ubi::Node`
`JS::ubi::Node` is a lightweight serializable interface that can represent the
current state of the heap graph. For a deeper dive into the particulars of how
it works, it is very well documented in the `js/public/UbiNode.h`
A "heap snapshot" is a representation of the heap graph at some particular past
instance in time.
A "heap analysis" is an algorithm that runs on a `JS::ubi::Node` heap graph.
Generally, analyses can run on either the live heap graph or a deserialized
snapshot. Example analyses include "census", which aggregates and counts nodes
into various user-specified buckets; "dominator trees", which compute the
[dominates](https://en.wikipedia.org/wiki/Dominator_%28graph_theory%29) relation
and retained size for all nodes in the heap graph; and "shortest paths" which
finds the shortest paths from the GC roots to some subset of nodes.
### Saving Heap Snapshots
Saving a heap snapshot has a few requirements:
1. The binary format must remain backwards compatible and future extensible.
2. The live heap graph must not mutate while we are in the process of
serializing it.
3. The act of saving a heap snapshot should impose as little memory overhead as
possible. If we are taking a snapshot to debug frequent out-of-memory errors,
we don't want to trigger an OOM ourselves!
To solve (1), we use the [protobuf](https://developers.google.com/protocol-buffers/)
message format. The message definitions themselves are in
`devtools/shared/heapsnapshot/CoreDump.proto`. We always use `optional` fields
so we can change our mind about what fields are required sometime in the future.
Deserialization checks the semantic integrity of deserialized protobuf messages.
For (2), we rely on SpiderMonkey's GC rooting hazard static analysis and the
`AutoCheckCannotGC` dynamic analysis to ensure that neither JS nor GC runs and
modifies objects or moves them from one address in memory to another. There is
no equivalent suppression and static analysis technique for the
[cycle collector](https://developer.mozilla.org/en/docs/Interfacing_with_the_XPCOM_cycle_collector),
so care must be taken not to invoke methods that could start cycle collection or
mutate the heap graph from the cycle collector's perspective. At the time of
writing, we don't yet support saving the cycle collector's portion of the heap
graph in snapshots, but that work is deemed Very Important and Very High
Priority.
Finally, (3) imposes upon us that we do not build the serialized heap snapshot
binary blob in memory, but instead stream it out to disk while generating it.
Once all of that is accounted for, saving snapshots becomes pretty straight
forward. We traverse the live heap graph with `JS::ubi::Node` and
`JS::ubi::BreadthFirst`, create a protobuf message for each node and each node's
edges, and write these messages to disk before continuing the traversal to the
next node.
This functionality is exposed to chrome JavaScript as the
`[ThreadSafe]ChromeUtils.saveHeapSnapshot` function. See
`dom/webidl/ThreadSafeChromeUtils.webidl` for API documentation.
### Reading Heap Snapshots
Reading heap snapshots has less restrictions than saving heap snapshots. The
protobuf messages that make up the core dump are deserialized one by one, stored
as a set of `DeserializedNode`s and a set of `DeserializedEdge`s, and the result
is a `HeapSnapshot` instance.
The `DeserializedNode` and `DeserializedEdge` classes implement the
`JS::ubi::Node` interface. Analyses running on offline heap snapshots rather
than the live heap graph operate on these classes (unknowingly, of course).
For more details, see the
[`mozilla::devtools::HeapSnapshot`](devtools/shared/heapsnapshot/HeapSnapshot.cpp)
and
[`mozilla::devtools::Deserialized{Node,Edge}`](devtools/shared/heapsnapshot/DeserializedNode.h)
classes.
### Heap Analyses
Heap analyses operate on `JS::ubi::Node` graphs without knowledge of whether
that graph is backed by the live heap graph or an offline heap snapshot. They
must make sure never to allocate GC things or modify the live heap graph.
In general, analyses are implemented in their own `js/public/Ubi{AnalysisName}.h`
header (eg `js/public/UbiCensus.h`), and are exposed to chrome JavaScript code
via a method on the [`HeapSnapshot`](dom/webidl/HeapSnapshot.webidl) webidl
interface.
For each analysis we expose to chrome JavaScript on the `HeapSnapshot` webidl
interface, there is a small amount of glue code in Gecko. The
[`mozilla::devtools::HeapSnapshot`](devtools/shared/heapsnapshot/HeapSnapshot.h)
C++ class implements the webidl interface. The analyses methods (eg
`ComputeDominatorTree`) take the deserialized nodes and edges from the heap
snapshot, create `JS::ubi::Node`s from them, call the analyses from
`js/public/Ubi*.h`, and wrap the results in something that can be represented in
JavaScript.
For API documentation on running specific analyses, see the
[`HeapSnapshot`](dom/webidl/HeapSnapshot.webidl) webidl interface.
### Testing `JS::ubi::Node`, Snapshots, and Analyses
The majority of the tests reside within `devtools/shared/heapsnapshot/tests/**`.
For reading and saving heap snapshots, most tests are gtests. The gtests can be
run with the `mach gtest DevTools.*` command. The rest are integration sanity
tests to make sure we can read and save snapshots in various environments, such
as xpcshell or workers. These can be run with the usual `mach test $PATH`
commands.
There are also `JS::ubi::Node` related unit tests in
`js/src/jit-test/tests/heap-analysis/*`, `js/src/jit-test/tests/debug/Memory-*`,
and `js/src/jsapi-tests/testUbiNode.cpp`. See
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Running_Automated_JavaScript_Tests#Running_jit-tests
for running the JIT tests.
## `HeapAnalysesWorker`
The `HeapAnalysesWorker` orchestrates running specific analyses on snapshots and
transforming the results into something that can simply and quickly be rendered
by the frontend. The analyses can take some time to run (sometimes on the order
of seconds), so doing them in a worker thread allows the interface to stay
responsive. The `HeapAnalysisClient` provides the main thread's interface to the
worker.
The `HeapAnalysesWorker` doesn't actually do much itself; mostly just shuffling
data and transforming it from one representation to another or calling C++
utility functions exposed by webidl that do those things. Most of these are
implemented as traversals of the resulting census or dominator trees.
See the following files for details on the various data transformations and
shuffling that the `HeapAnalysesWorker` delegates to.
* `devtools/shared/heapsnapshot/CensusUtils.js`
* `devtools/shared/heapsnapshot/CensusTreeNode.js`
* `devtools/shared/heapsnapshot/DominatorTreeNode.js`
### Testing the `HeapAnalysesWorker` and `HeapAnalysesClient`
Tests for the `HeapAnalysesWorker` and `HeapAnalysesClient` reside in
`devtools/shared/heapsnapshot/tests/**` and can be run with the usual `mach test
$PATH` command.
## Frontend
The frontend of the memory tool is built with React and Redux.
[React has thorough documentation.](https://facebook.github.io/react/)
[Redux has thorough documentation.](http://rackt.org/redux/index.html)
We have React components in `devtools/client/memory/components/*`.
We have Redux reducers in `devtools/client/memory/reducers/*`.
We have Redux actions and action-creating tasks in
`devtools/client/memory/actions/*`.
React components should be pure functions from their props to the rendered
(virtual) DOM. Redux reducers should also be observably pure.
Impurity within the frontend is confined to the tasks that are creating and
dispatching actions. All communication with the outside world (such as the
`HeapAnalysesWorker`, the Remote Debugger Server, or the file system) is
restricted to within these tasks.
### Snapshots State
On the JavaScript side, the snapshots represent a reference to the underlying
heap dump and the various analyses. The following diagram represents a finite
state machine describing the snapshot states. Any of these states may go to the
ERROR state, from which they can never leave.
```
SAVING → SAVED → READING → READ
IMPORTING
```
Each of the report types (census, diffing, tree maps, dominators) have their own states as well, and are documented at `devtools/client/memory/constants.js`.
These report states are updated as the various filtering and selecting options
are updated in the UI.
### Testing the Frontend
Unit tests for React components are in `devtools/client/memory/test/chrome/*`.
Unit tests for actions, reducers, and state changes are in
`devtools/client/memory/test/unit/*`.
Holistic integration tests for the frontend and the whole memory tool are in
`devtools/client/memory/test/browser/*`.
All tests can be run with the usual `mach test $PATH` command.

View file

@ -0,0 +1,6 @@
The devtools is built around a client/server architecture because the
tools run outside of the process it debugs. This protocol is
documented extensively in the [Remote Debugging
Protocol](https://wiki.mozilla.org/Remote_Debugging_Protocol) wiki
page.

View file

@ -0,0 +1,73 @@
# Guidelines for Writing React
These are soft rules for writing react devtools code. Try to stick to
these for consistency, and if you disagree, file a bug to change these
docs and we can talk about it.
**Please also read** the [coding
standards](https://wiki.mozilla.org/DevTools/CodingStandards#React_.26_Redux)
for react and redux code. The guidelines here are more general
patterns not specific to code style.
### Why no JSX?
You probably already noticed we don't use JSX. The answer isn't
complicated: we don't build our JS code, and we write directly for our
JS engine, SpiderMonkey. It already supports much of ES6, but it does
not support JSX (which is not a standard).
This may change if we ever adopt a build step. Even so, the author is
not convinced that JSX helps enough to warrant all the syntax. It is
clearer sometimes, but it can be noisy switching between JSX and JS a
lot.
It's not as bad as you may think! If you are used to JSX it may be an
adjustment, but you won't miss it too much.
### One component per file
Try to only put one component in a file. This helps avoid large files
full of components, but it's also technically required for how we wrap
components with factories. See the next rule.
It also makes it easier to write tests because you might not export
some components, so tests can't access them.
You can include small helper components in the same file if you really
want to, but note that they won't be directly tested and you will have
to use `React.createElement` or immediately wrap them in factories to
use them.
### Export the component directly and create factory on import
Modules are the way components interact. Ideally every component lives
in a separate file and they require whatever they need. This allows
tests to access all components and use module boundaries to wrap
components.
For example, we don't use JSX, so we need to create factories for
components to use them as functions. A simple way to do this is on
import:
```js
const Thing1 = React.createFactory(require('./thing1'));
const Thing2 = React.createFactory(require('./thing2'));
```
It adds a little noise, but then you can do `Thing1({ ... })` instead
of `React.createElement(Thing1, { ... })`. Definitely worth it.
Additionally, make sure to export the component class directly:
```js
const Thing1 = React.createClass({ ... });
module.exports = Thing1;
```
Do not export `{ Thing1 }` or anything like that. This is required for
the factory wrapping as well as hot reloading.
### More to Come
This is just a start. We will add more to this document.

View file

@ -0,0 +1,19 @@
# React Tips
Learn various tips and tricks for working with our React code.
## Hot Reloading
If you followed [this
rule](react-guidelines.md#export-the-component-directly-and-create-factory-on-import)
about exporting components, and are using the BrowserLoader, you can
hot reload any React component. Just turn on the pref
`devtools.loader.hotreload`, re-open the devtools, and you should be
able to save any React component and instantly see changes.
This does not reset the state of your app, so you can quickly se
changes in the same context.
## Profiling
We need information here about how to use React.addons.Perf to profile the app.

157
devtools/docs/react.md Normal file
View file

@ -0,0 +1,157 @@
We use [React](http://facebook.github.io/react/) to write our user
interfaces. In here you can find an explanation of why we chose React
and a short primer on it. Additionally, we list best practices that
all devtools code should adhere to when writing React.
# Quick Intro
This is a very quick introduction on how to *use* React, but does not
explain in-depth the concepts behind it. If you want more in-depth
articles, I recommend the following links:
* http://facebook.github.io/react/docs/tutorial.html - the official tutorial
* https://github.com/petehunt/react-howto - how to learn React
* http://jlongster.com/Removing-User-Interface-Complexity,-or-Why-React-is-Awesome - long read but explains the concepts in depth
React embraces components as a way of thinking about UIs. Components
are the center of everything: they are composable like functions,
testable like JSON data, and provide lifecycle APIs for more complex
scenarios.
A component can represent anything from a single item in a list to a
complete virtualized grid that is made up of sub-components. They can
be used to abstract out "behaviors" instead of UI elements (think of a
`Selectable` component). React's API makes it easy to break up your UI
into whatever abstractions you need.
The core idea of a component is simple: it's something that takes
properties and returns a DOM-like structure.
```js
function Item({ name, iconURL }) {
return div({ className: "item" },
img({ className: "icon", href: iconURL }),
name);
}
```
The `div` and `span` functions refer to `React.DOM.div` and
`React.DOM.span`. React provides constructors for all DOM elements on
`React.DOM`. These conform to the standard API for creating elements:
the first argument takes properties, and the rest are children.
You can see component composition kick in when using `Item`:
```js
const Item = React.createFactory(require('./Item'));
function List({ items }) {
return div({ className: "list" },
items.map(item => Item({ name: item.name, icon: item.iconURL)));
}
```
You can use custom components exactly the same way you use native
ones! The only difference is we wrapped it in a factory when importing
instead of using the React.DOM functions. Factories are just a way of
turning a component into a convenient function. Without factories, you
need to do do `React.createElement(Item, { ... })`, which is exactly
the same as `Item({ ... })` if using a factory.
## Rendering and Updating Components
Now that we have some components, how do we render them? You use
`React.render` for that:
```js
let items = [{ name: "Dubois", iconURL: "dubois.png" },
{ name: "Ivy", iconURL: "ivy.png" }];
React.render(List({ items: items }),
document.querySelector("#mount"));
```
This renders a `List` component, given `items`, to a DOM node with an
id of `mount`. Typically you have a top-level `App` component that is
the root of everything, and you would render it like so.
What about updating? First, let's talk about data. The above
components take data from above and render out DOM structure. If any
user events were involved, the components would call callbacks passed
as props, so events walk back up the hierarchy. The conceptual model
is data goes down, and events come up.
You usually want to change data in response to events, and rerender
the UI with the new data. What does that look like? There are two
places where React will rerender components:
1\. Any additional `React.render` calls. Once a component is mounted,
you can call `React.render` again to the same place and React will see
that it's already mounted and perform an update instead of a full
render. For example, this code adds an item in response to an event
and updates the UI, and will perform optimal incremental updates:
```js
function addItem(item) {
render([...items, item]);
}
function render(items) {
React.render(List({ items: items,
onAddItem: addItem }),
document.querySelector("#mount"));
}
render(items);
```
2\. Changing component local state. This is much more common. React
allows components to have local state, and whenever the state is
changed with the `setState` API it will rerender that specific
component. If you use component local state, you need to create a
component with `createClass`:
```js
const App = React.createClass({
getInitialState: function() {
return { items: [] };
},
handleAddItem: function(item) {
const items = [...this.props.items, item];
this.setState({ items: items });
},
render: function() {
return List({ items: this.state.items,
onAddItem: this.handleAddItem });
}
});
```
If you are using something like Redux to manage state this is handled
automatically for you with the library you use to bind Redux with
React. See more in [Redux](redux.html).
## DOM Diffing
What does it mean when React "updates" a component, and how does it
know which DOM to change? React achieves this with a technique called
DOM diffing. This alleviates the need for the programmer to worry
about how updates are actually applied to the DOM, and components can
render DOM structure declaratively in response to data. In the above
examples, when adding an item, React knows to only add a new DOM node
instead of recreating the whole list each time.
DOM diffing is possible because our components return what's called
"virtual DOM": a lightweight JSON structure that React can use to diff
against previous versions, and generate minimal changes to the real DOM.
This also makes it really east to test components with a real DOM:
just make sure the virtual DOM has what it should.
## Next
Read the [React Guidelines](react-guidelines.md) next to learn how to
write React code specifically for the devtools.

View file

@ -0,0 +1,52 @@
### Getting data from the store
To get data from the store, use `connect()`.
When using connect, you'll break up your component into two parts:
1. The part that displays the data (presentational component)
// todos.js
const Todos = React.createClass({
propTypes: {
todos: PropTypes.array.isRequired
}
render: function() {...}
})
module.exports = Todos;
2. The part that gets the data from the store (container component)
// todos-container.js
const Todos = require("path/to/todos");
function mapStateToProps(state) {
return {
todos: state.todos
};
}
module.exports = connect(mapStateToProps)(Todos);
`connect()` generates the container component. It wraps around the presentational component that was passed in (e.g. Todos).
The `mapStateToProps` is often called a selector. That's because it selects data from the state object. When the container component is rendering, the the selector will be called. It will pick out the data that the presentational component is going to need. Redux will take this object and pass it in to the presentational component as props.
With this setup, a presentational component is easy to share across different apps. It doesn't have any dependencies on the app, or any hardcoded expectations about how to get data. It just gets props that are passed to it and renders them.
For more advanced use cases, you can pass additional parameters into the selector and `connect()` functions. Read about those in the [`connect()`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) docs.
---
Need to answer the following questions:
* How do I do I load asynchronous data?
* How do I do optimistic updates or respond to errors from async work?
* Do I use Immutable.js for my state?
* What file structure should I use?
* How do I test redux code?
And more.

View file

@ -0,0 +1,5 @@
Need to document:
* How to attach various redux loggers
* How to hot reload redux code

160
devtools/docs/redux.md Normal file
View file

@ -0,0 +1,160 @@
We use [Redux](https://github.com/reactjs/redux) to manage application
state. The [docs](http://redux.js.org/) do a good job explaining the
concepts, so go read them.
# Quick Intro
Just like the [React introduction](react.html), this is a quick
introduction to redux, focusing on how it fits into React and why we
chose it.
One of the core problems that React does not address is managing
state. In the React intro, we talked about data flowing down and
events flowing up. Conceptually this is nice, but you quickly run into
awkward situations in large apps.
Let's look at an example. Say you have a page with a tabbed interface.
Here, `Tab1` is managing a list of items, so naturally it uses local
state. `Tab2` renders different stuff.
```js
const Tab1 = React.createClass({
getInitialState: function() {
return { items: [] };
},
handleAddItem: function(item) {
this.setState({ items: [...this.state.items, item]});
},
render: function() {
/* ... Renders the items and button to add new item ... */
}
});
const Tab2 = React.createClass({
render: function() {
/* ... Renders other data ... */
}
});
// Assume `Tab1` and `Tab2` are wrapped with a factory when importing
const Tabs = React.createClass({
render: function() {
return div(
{ className: 'tabs' },
// ... Render the tab buttons ...
Tab1(),
Tab2()
);
}
});
```
What happens when `Tab2` needs the list of items though? This scenario
comes up all time: components that aren't directly related need access
to the same state. A small change would be to move the `items` state
up to the `Tabs` component, and pass it down to both `Tab1` and `Tab2`.
But now `Tabs` has to implement the `handleAddItem` method to add an
item because it's managing that state. This quickly gets ugly as the
end result is the root component ends up with a ton of state and
methods to manage it: a [god
component](https://en.wikipedia.org/wiki/God_object) is born.
Additionally, how do we know what data each tab needs? We end up
passing *all* the state down because we don't know. This is not a
modular solution: one object managing the state and every component
receiving the entire state is like using tons of global variables.
## Evolution of Flux
Facebook addressed this with the
[flux](https://facebook.github.io/flux/) architecture, which takes the
state out of the components and into a "store". Redux is the latest
evolution of this idea and solves a lot of problems previous flux
libraries had (read it's documentation for more info).
Because the state exists outside the component tree, any component can
read from it. Additionally, **state is updated with
[actions](http://redux.js.org/docs/basics/Actions.html)** that any
component can fire. We have [guidelines](redux-guidelines) for where
to read/write state, but it completely solves the problem described
above. Both `Tab1` and `Tab2` would be listening for changes in the
`item` state, and `Tab1` would fire actions to change it.
With redux, **state is managed modularly with
[reducers](http://redux.js.org/docs/basics/Reducers.html)** but tied
together into a single object. This means a single JS object
represents most* of your state. It may sound crazy at first, but think
of it as an object with references to many pieces of state; that's all
it is.
This makes it very easy to test, debug, and generally think about. You
can log your entire state to the console and inspect it. You can even
dump in old states and "replay" to see how the UI changed over time.
I said "most*" because it's perfectly fine to use both component local
state and redux. Be aware that any debugging tools will not see local
state at all though. It should only be used for transient state; we'll
talk more about that in the guidelines.
## Immutability
Another important concept is immutability. In large apps, mutating
state makes it very hard to track what changed when. It's very easy to
run into situations where something changes out from under you, and
the UI is rendered with invalid data.
Redux enforces the state to be updated immutably. That means you
always return new state. It doesn't mean you do a deep copy of the
state each time: when you need to change some part of the tree you
only need to create new objects to replace the ones your changing (and
walk up to the root to create a new root). Unchanged subtrees will
reference the same objects.
This removes a whole class of errors, almost like Rust removing a
whole class of memory errors by enforcing ownership.
## Order of Execution
One of best things about React is that **rendering is synchronous**. That
means when you render a component, given some data, it will fully
render in the same tick. If you want the UI to change over time, you
have to change the *data* and rerender, instead of arbitrary UI
mutations.
The reason this is desired is because if you build the UI around
promises or event emitters, updating the UI becomes very brittle
because anything can happen at any time. The state might be updated in
the middle of rendering it, maybe because you resolved a few promises
which made your rendering code run a few ticks later.
Redux embraces the synchronous execution semantics as well. What this
means is that everything happens in a very controlled way. When
updating state through an action, all reducers are run and a new state
is synchronously generated. At that point, the new state is handed off
to React and synchronously rendered.
Updating and rendering happen in two phases, so the UI will *always*
represent consistent state. The state can never be in the middle of
updating when rendering.
What about asynchronous work? That's where
[middleware](http://redux.js.org/docs/advanced/Middleware.html) come
in. At this point you should probably go study our code, but
middleware allows you to dispatch special actions that indicate
asynchronous work. The middleware will catch these actions and do
something async, dispatching "raw" actions along the way (it's common
to emit a START, DONE, and ERROR action).
**Ultimately there are 3 "phases" or level of abstraction**: the async
layer talks to the network and may dispatch actions, actions are
synchronously pumped through reducers to generate state, and state is
rendered with react.
## Next
Read the [Redux Guidelines](redux-guidelines.md) next to learn how to
write React code specifically for the devtools.

View file

@ -0,0 +1,70 @@
# Responsive Design Mode Architecture
## Context
You have a single browser tab that has visited several pages, and now has a
history that looks like, in oldest to newest order:
1. https://newsblur.com
2. https://mozilla.org (← current page)
3. https://convolv.es
## Opening RDM During Current Firefox Session
When opening RDM, the browser tab's history must preserved. Additionally, we
strive to preserve the exact state of the currently displayed page (effectively
any in-page state, which is important for single page apps where data can be
lost if they are reloaded).
This seems a bit convoluted, but one advantage of this technique is that it
preserves tab state since the same tab is reused. This helps to maintain any
extra state that may be set on tab by add-ons or others.
1. Create a temporary, hidden tab to load the tool UI.
2. Mark the tool tab browser's docshell as active so the viewport frame is
created eagerly and will be ready to swap.
3. Create the initial viewport inside the tool UI.
4. Swap tab content from the regular browser tab to the browser within the
viewport in the tool UI, preserving all state via
`gBrowser._swapBrowserDocShells`.
5. Force the original browser tab to be non-remote since the tool UI must be
loaded in the parent process, and we're about to swap the tool UI into
this tab.
6. Swap the tool UI (with viewport showing the content) into the original
browser tab and close the temporary tab used to load the tool via
`swapBrowsersAndCloseOther`.
7. Start a tunnel from the tool tab's browser to the viewport browser
so that some browser UI functions, like navigation, are connected to
the content in the viewport, instead of the tool page.
## Closing RDM During Current Firefox Session
To close RDM, we follow a similar process to the one from opening RDM so we can
restore the content back to a normal tab.
1. Stop the tunnel between outer and inner browsers.
2. Create a temporary, hidden tab to hold the content.
3. Mark the content tab browser's docshell as active so the frame is created
eagerly and will be ready to swap.
4. Swap tab content from the browser within the viewport in the tool UI to the
regular browser tab, preserving all state via
`gBrowser._swapBrowserDocShells`.
5. Force the original browser tab to be remote since web content is loaded in
the child process, and we're about to swap the content into this tab.
6. Swap the content into the original browser tab and close the temporary tab
used to hold the content via `swapBrowsersAndCloseOther`.
## Session Restore
When restarting Firefox and restoring a user's browsing session, we must
correctly restore the tab history. If the RDM tool was opened when the session
was captured, then it would be acceptable to either:
* A: Restore the tab content without any RDM tool displayed **OR**
* B: Restore the RDM tool the tab content inside, just as before the restart
We currently follow path A (no RDM after session restore), which seems more in
line with how the rest of DevTools currently functions after restore. To do so,
we watch for `beforeunload` events on the tab at shutdown and quickly exit RDM
so that session restore records only the original page content during its final
write at shutdown.

View file

@ -0,0 +1,13 @@
.book .book-summary ul.summary li {
cursor: pointer;
}
.book .book-body .page-wrapper .page-inner section.normal p,
.book .book-body .page-wrapper .page-inner section.normal pre {
margin: .85em 0;
}
.book .book-body .page-wrapper .page-inner section.normal pre {
line-height: 1.25em;
}

42
devtools/docs/svgs.md Normal file
View file

@ -0,0 +1,42 @@
# Panel SVGs
These are the guidelines for creating devtools SVGs to make sure they're as small and neatly formatted as possible. The Mozilla Developer SVG guidelines can be found [here](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/SVG_Guidelines).
## Explanation of Pixel Grid
Since so many of our SVGs appear so small, designing them on the pixel grid will help them not appear fuzzy when they're sized down to 16x16 pixels. There is program-specific documentation in both the [Illustrator](#illustrator) and [Sketch](#sketch) sections.
## Panel Icon Requirements
The devtools panel icons do a couple of things in a specific way; following these guidelines will help stick your patch:
1. **Inline fill colors.** Devtools panel icons all use ```fill="#0b0b0b"``` in the ```<svg>``` tag.
2. **Inline opacities.** Devtools panel icons also inline opacities on their relevant path.
## Illustrator
For Illustrator you'll want the following document settings:
- **Document settings**: ```Units: pixels```, ```Advanced``` > check ```Align New Objects to Pixel Grid```
- **Transform Panel**: for existing artwork not on pixel grid, select shape and then within ```Transform``` > ```Advanced``` > check ```Align to Pixel Grid```
You can get a more detailed breakdown with images [here](http://medialoot.com/blog/3-valuable-pixel-perfect-illustrator-techniques/).
You can download a sample Illustrator file [here](https://www.dropbox.com/home/Mozilla_MobileUX_Share/Internal%20Assets/Templates/Firefox?preview=pixel-grid-illustrator.ai).
### Tips for Object Creation
When you're designing your icons in a graphics editor like Adobe Illustrator, there are a lot of things you can do that will bring down the size of the file and make your SVGs easier for the developers to work with. Here are some of them:
- **Expand paths**: Instead of having multiple shapes overlapping each other, expand shapes using the pathfinder.
![Use pathfinder to expand shapes](./svgs/pathfinder.gif)
- Simplify paths (```Object``` > ```Path``` > ```Simplify```)
- Expand objects so that strokes become objects. This has the added benefit of keeping the stroke size intact as the SVG is resized.
![Expand strokes to make them objects](./svgs/expand-strokes.gif)
## Sketch
Sketch vector work is a little different but the fundamentals (keeping your SVG small, expanding all paths) is the same. Here's what we've found helps to build clean icons:
- **Build your icon at 16x16 with the Pixel Grid turned on.** You can turn the pixel grid on at ```View > Canvas > Show Pixels```
- **Make sure that all x/y coordinates are full pixels for lines/rectangles.** Sub-pixels = not on pixel grid.
![Position in the upper right hand corner of Sketch](./svgs/sketch-position.png)
- **Expand all your paths so strokes expand properly as the SVG gets resized.** You can do this at ```Layer > Paths > Vectorize Stroke```.
- **Align anything that isn't boxy to the pixel grid with item selected then ```Layer > Round to Nearest Pixel Edge```.**

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

2
devtools/docs/tools.md Normal file
View file

@ -0,0 +1,2 @@
These files provide a high-level overview of each tool and how they work.