Bug 1257322 - add more devtools docs r=pbrosset
authorJames Long <longster@gmail.com>
Mon, 21 Mar 2016 10:36:28 -0400
changeset 289580 9e9f949b36c80f6794a22cbd137c1bce5d9e7098
parent 289579 297bb754e23135fefc9683024bd33b7503427ee8
child 289581 af22a2e633c03d496fdb832a0abc9d9253fff148
push id30107
push usercbook@mozilla.com
push dateTue, 22 Mar 2016 10:00:23 +0000
treeherdermozilla-central@3587b25bae30 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbrosset
bugs1257322
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1257322 - add more devtools docs r=pbrosset
devtools/docs/.gitignore
devtools/docs/README.md
devtools/docs/SUMMARY.md
devtools/docs/backend.md
devtools/docs/debugger-api.md
devtools/docs/debugger-panel.md
devtools/docs/frontend.md
devtools/docs/protocol.md
devtools/docs/react-guidelines.md
devtools/docs/react-tips.md
devtools/docs/react.md
devtools/docs/redux-guidelines.md
devtools/docs/redux-tips.md
devtools/docs/redux.md
devtools/docs/styles/website.css
devtools/docs/svgs.md
devtools/docs/tools.md
new file mode 100644
--- /dev/null
+++ b/devtools/docs/.gitignore
@@ -0,0 +1,1 @@
+_book
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/README.md
@@ -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
+```
new file mode 100644
--- /dev/null
+++ b/devtools/docs/SUMMARY.md
@@ -0,0 +1,18 @@
+
+# Summary
+
+* [Tool Architectures](tools.md)
+  * [Inspector](inspector-panel.md)
+  * [Memory](memory-panel.md)
+  * [Debugger](debugger-panel.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)
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/backend.md
@@ -0,0 +1,2 @@
+
+These files provide information about general backend architecture.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/debugger-api.md
@@ -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
+
new file mode 100644
--- /dev/null
+++ b/devtools/docs/debugger-panel.md
@@ -0,0 +1,2 @@
+
+This is just a stub until we write debugger docs.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/frontend.md
@@ -0,0 +1,2 @@
+
+These files provide information about general frontend architecture.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/protocol.md
@@ -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.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/react-guidelines.md
@@ -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.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/react-tips.md
@@ -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.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/react.md
@@ -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.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/redux-guidelines.md
@@ -0,0 +1,10 @@
+
+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.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/redux-tips.md
@@ -0,0 +1,5 @@
+
+Need to document:
+
+* How to attach various redux loggers
+* How to hot reload redux code
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/redux.md
@@ -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.
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/styles/website.css
@@ -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;
+}
\ No newline at end of file
--- a/devtools/docs/svgs.md
+++ b/devtools/docs/svgs.md
@@ -19,24 +19,24 @@ For Illustrator you'll want the followin
 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](./pathfinder.gif)
+![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](./expand-strokes.gif)
+![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](./sketch-position.png)
+![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```.**
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/docs/tools.md
@@ -0,0 +1,2 @@
+
+These files provide a high-level overview of each tool and how they work.
\ No newline at end of file