diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /devtools/docs | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/docs')
758 files changed, 23767 insertions, 0 deletions
diff --git a/devtools/docs/contributor/.gitignore b/devtools/docs/contributor/.gitignore new file mode 100644 index 0000000000..9a8882dd2b --- /dev/null +++ b/devtools/docs/contributor/.gitignore @@ -0,0 +1 @@ +_book
\ No newline at end of file diff --git a/devtools/docs/contributor/README.md b/devtools/docs/contributor/README.md new file mode 100644 index 0000000000..cf3e6d8979 --- /dev/null +++ b/devtools/docs/contributor/README.md @@ -0,0 +1,3 @@ +# Firefox Developer Tools: Contributor Docs + +This is a guide to working on the code for Firefox Developer Tools. If you're looking for help with using the tools, see the [user docs](https://firefox-source-docs.mozilla.org/devtools-user/). For other ways to get involved, check out our [community site](https://firefox-dev.tools). diff --git a/devtools/docs/contributor/backend/actor-best-practices.md b/devtools/docs/contributor/backend/actor-best-practices.md new file mode 100644 index 0000000000..3e29f51f60 --- /dev/null +++ b/devtools/docs/contributor/backend/actor-best-practices.md @@ -0,0 +1,38 @@ +# Actor Best Practices + +Some aspects of front and actor design can be tricky to understand, even for experienced engineers. +The following are several best practices you should keep in mind when adding new actors and fronts. + +## Actor Should Clean Up Itself, Don't Wait For the Client + +In the past, some actors would wait for the client to send a "you are done now" message when the toolbox closes to shutdown the actor. +This seems reasonable at first, but keep in mind that the connection can disappear at any moment. +It may not be possible for the client to send this message. + +A better choice is for the actor to do all clean up itself when it's notified that the connection goes away. +Then there's no need for the client to send any clean up message, and we know the actor will be in a good state no matter what. + +## Actor Destruction + +Ensure that the actor's destroy is really destroying everything that it should. Here's an example from the animation actor: + +```js +destroy: function() { + Actor.prototype.destroy.call(this); + this.targetActor.off("will-navigate", this.onWillNavigate); + this.targetActor.off("navigate", this.onNavigate); + + this.stopAnimationPlayerUpdates(); + this.targetActor = this.observer = this.actors = null; +}, +``` + +## Child Actors + +With protocol.js actors, if your creates child actors for further functionality, in most cases you should call: + +```js +this.manage(child); +``` + +in the parent after constructing the child, so that the child is destroyed when the parent is. diff --git a/devtools/docs/contributor/backend/actor-e10s-handling.md b/devtools/docs/contributor/backend/actor-e10s-handling.md new file mode 100644 index 0000000000..0de44c88a3 --- /dev/null +++ b/devtools/docs/contributor/backend/actor-e10s-handling.md @@ -0,0 +1,106 @@ +# How to handle E10S in actors + +In multi-process environments, most devtools actors are created and initialized in the child content process, to be able to access the resources they are exposing to the toolbox. But sometimes, these actors need to access things in the parent process too. Here's why and how. + +```warning:: + | This documentation page is **deprecated**. setupInParent relies on the message manager which is being deprecated. Furthermore, communications between parent and content processes should be avoided for security reasons. If possible, the client should be responsible for calling actors both on the parent and content process. + | + | This page will be removed when all actors relying on this API are removed. +``` + +## Use case and examples + +Some actors need to exchange messages between the parent and the child process (typically when some components aren't available in the child process). + +To that end, there's a parent/child setup mechanism at `DevToolsServer` level that can be used. + +When the actor is loaded for the first time in the `DevToolsServer` running in the child process, it may decide to run a setup procedure to load a module in the parent process with which to communicate. + +Example code for the actor running in the child process: + +``` + const {DevToolsServer} = require("devtools/server/devtools-server"); + + // Setup the child<->parent communication only if the actor module + // is running in a child process. + if (DevToolsServer.isInChildProcess) { + setupChildProcess(); + } + + function setupChildProcess() { + // `setupInParent` is defined on DevToolsServerConnection, + // your actor receives a reference to one instance in its constructor. + conn.setupInParent({ + module: "devtools/server/actors/module-name", + setupParent: "setupParentProcess" + }); + // ... + } +``` + +The `setupChildProcess` helper defined and used in the previous example uses the `DevToolsServerConnection.setupInParent` to run a given setup function in the parent process DevTools Server. + +With this, the `DevToolsServer` running in the parent process will require the requested module and call its `setupParentProcess` function (which should be exported on the module). + +The `setupParentProcess` function will receive a parameter that contains a reference to the **MessageManager** and a prefix that should be used to send/receive messages between the child and parent processes. + +See below an example implementation of a `setupParent` function in the parent process: + +``` +exports.setupParentProcess = function setupParentProcess({ mm, prefix }) { + // Start listening for messages from the actor in the child process. + setMessageManager(mm); + + function handleChildRequest(msg) { + switch (msg.json.method) { + case "get": + return doGetInParentProcess(msg.json.args[0]); + break; + case "list": + return doListInParentProcess(); + break; + default: + console.error("Unknown method name", msg.json.method); + throw new Error("Unknown method name"); + } + } + + function setMessageManager(newMM) { + if (mm) { + // Remove listener from old message manager + mm.removeMessageListener("debug:some-message-name", handleChildRequest); + } + // Switch to the new message manager for future use + // Note: Make sure that any other functions also use the new reference. + mm = newMM; + if (mm) { + // Add listener to new message manager + mm.addMessageListener("debug:some-message-name", handleChildRequest); + } + } + + return { + onDisconnected: () => setMessageManager(null), + }; +}; +``` + +The server will call the `onDisconnected` method returned by the parent process setup flow to give the actor modules the chance to cleanup their handlers registered on the disconnected message manager. + +## Summary of the setup flow + +In the child process: + +* The `DevToolsServer` loads an actor module, +* the actor module checks `DevToolsServer.isInChildProcess` to know whether it runs in a child process or not, +* the actor module then uses the `DevToolsServerConnection.setupInParent` helper to start setting up a parent-process counterpart, +* the `DevToolsServerConnection.setupInParent` helper asks the parent process to run the required module's setup function, +* the actor module uses the `DevToolsServerConnection.parentMessageManager.sendSyncMessage` and `DevToolsServerConnection.parentMessageManager.addMessageListener` helpers to send or listen to message. + +In the parent process: + +* The DevToolsServer receives the `DevToolsServerConnection.setupInParent` request, +* tries to load the required module, +* tries to call the `module[setupParent]` function with the frame message manager and the prefix as parameters `{ mm, prefix }`, +* the `setupParent` function then uses the mm to subscribe the message manager events, +* the `setupParent` function returns an object with a `onDisconnected` method which the server can use to notify the module of various lifecycle events diff --git a/devtools/docs/contributor/backend/actor-hierarchy.md b/devtools/docs/contributor/backend/actor-hierarchy.md new file mode 100644 index 0000000000..2b8277aa77 --- /dev/null +++ b/devtools/docs/contributor/backend/actor-hierarchy.md @@ -0,0 +1,177 @@ +# How actors are organized + +To start with, actors are living within devtools/server/actors folder. +They are organized in a hierarchy for easier lifecycle and memory management: +once a parent is removed from the pool, its children are removed as well. +(See actor-registration.md for more information about how to implement one) + +The overall hierarchy of actors looks like this: + +``` +RootActor: First one, automatically instantiated when we start connecting. + | Mostly meant to instantiate new actors. + | + |-- Global-scoped actors: + | Actors exposing features related to the main process, that are not + | specific to any particular target (document, tab, add-on, or worker). + | These actors are registered with `global: true` in + | devtools/server/actors/utils/actor-registry.js + | Examples include: + | PreferenceActor (for Firefox prefs) + | + \-- Descriptor Actor's -or- Watcher Actor + | + \ -- Target actors: + Actors that represent the main "thing" being targeted by a given toolbox, + such as a tab, frame, worker, add-on, etc. and track its lifetime. + Generally, there is a target actor for each thing you can point a + toolbox at. + Examples include: + WindowGlobalTargetActor (for a WindowGlobal, such as a tab or a remote iframe) + ProcessTargetActor + WorkerTargetActor (for various kind of workers) + | + \-- Target-scoped actors: + Actors exposing one particular feature set. They are children of a + given target actor and the data they return is filtered to reflect + the target. + These actors are registered with `target: true` in + devtools/server/actors/utils/actor-registry.js + Examples include: + WebConsoleActor + InspectorActor + These actors may extend this hierarchy by having their own children, + like LongStringActor, WalkerActor, etc. +``` + +## RootActor + +The root actor is special. It is automatically created when a client connects. +It has a special `actorID` which is unique and is "root". +All other actors have an `actorID` which is computed dynamically, +so that you need to ask an existing actor to create an Actor +and returns its `actorID`. That's the main role of RootActor. + +``` +RootActor (root.js) + | + |-- TabDescriptorActor (descriptors/tab.js) + | Targets frames (such as a tab) living in the parent or child process. + | Returned by "listTabs" or "getTab" requests. + | + |-- WorkerTargetActor (worker.js) + | Targets a worker (applies to various kinds like web worker, service + | worker, etc.). + | Returned by "listWorkers" request to the root actor to get all workers. + | Returned by "listWorkers" request to a WindowGlobalTargetActor to get + | workers for a specific document/WindowGlobal. + | Returned by "listWorkers" request to a ContentProcessTargetActor to get + | workers for the chrome of the child process. + | + |-- ParentProcessTargetActor (parent-process.js) + | Targets all resources in the parent process of Firefox (chrome documents, + | JSMs, JS XPCOM, etc.). + | Extends the abstract class WindowGlobalTargetActor. + | Extended by WebExtensionTargetActor. + | Returned by "getProcess" request without any argument. + | + |-- ContentProcessTargetActor (content-process.js) + | Targets all resources in a content process of Firefox (chrome sandboxes, + | frame scripts, documents, etc.) + | Returned by "getProcess" request with a id argument, matching the + | targeted process. + | + \-- WebExtensionActor (addon/webextension.js) + Represents a WebExtension add-on in the parent process. This gives some + metadata about the add-on and watches for uninstall events. This uses a + proxy to access the actual WebExtension in the WebExtension process via + the message manager. + Returned by "listAddons" request. + | + \-- WebExtensionTargetActor (targets/webextension.js) + Targets a WebExtension add-on. This runs in the WebExtension process. + The client issues an additional "connect" request to + WebExtensionActor to get this actor, which is different from the + approach used for frame target actors. + Extends ParentProcessTargetActor. + Returned by "connect" request to WebExtensionActor. +``` +All these descriptor actors expose a `getTarget()` method which +returns the target actor for the descriptor's debuggable context +(tab, worker, process or add-on). + +But note that this is now considered as a deprecated codepath. +Ideally, all targets should be retrieved via the new WatcherActor. +For now, the WatcherActor only support tabs and entire browser debugging. +Workers and add-ons still have to go through descriptor's getTarget. + +## Target Actors + +Those are the actors exposed by the watcher actor, or, via descriptor's getTarget methods. +They are meant to track the lifetime of a given target: document, process, add-on, or worker. +It also allows to fetch the target-scoped actors connected to this target, +which are actors like console, inspector, thread (for debugger), style inspector, etc. + +Some target actors inherit from WindowGlobalTargetActor (defined in +window-global.js) which is meant for "window globals" which present +documents to the user. It automatically tracks the lifetime of the targeted +window global, but it also tracks its iframes and allows switching the +target to one of its iframes. + +For historical reasons, target actors also handle creating the ThreadActor, used +to manage breakpoints in the debugger. Actors inheriting from +WindowGlobalTargetActor expose `attach`/`detach` requests, that allows to +start/stop the ThreadActor. + +Target-scoped actors are accessed via the target actor's RDP form which contains +the `actorID` for each target-scoped actor. + +The target-scoped actors expect to find the following properties on the target +actor: + - threadActor: + ThreadActor instance for the given target, + only defined once `attach` request is called, or on construction. + - isRootActor: (historical name) + Always false, except on ParentProcessTargetActor. + Despite the attribute name, it is being used to accept all resources + (like chrome one) instead of limiting only to content resources. + - makeDebugger: + Helper function used to create Debugger object for the target. + (See actors/utils/make-debugger.js for more info) + +In addition to this, the actors inheriting from WindowGlobalTargetActor, +expose many other attributes and events: + - window: + Reference to the window global object currently targeted. + It can change over time if we switch target to an iframe, so it + shouldn't be stored in a variable, but always retrieved from the actor. + - windows: + List of all document globals including the main window object and all + iframes. + - docShell: + Primary docShell reference for the targeted document. + - docShells: + List of all docShells for the targeted document and all its iframes. + - chromeEventHandler: + The chrome event handler for the current target. Allows to listen to events + that can be missing/cancelled on this document itself. + +See WindowGlobalTargetActor documentation for more details. + +## Target-scoped actors + +Each of these actors focuses on providing one particular feature set. They are +children of a given target actor. + +The data they return is filtered to reflect the target. For example, the +InspectorActor that you fetch from a WindowGlobalTargetActor gives you information +about the markup and styles for only that frame. + +These actors may extend this hierarchy by having their own children, like +LongStringActor, WalkerActor, etc. + +To improve performance, target-scoped actors are created lazily. The target +actor lists the actor ID for each one, but the actor modules aren't actually +loaded and instantiated at that point. Once the first request for a given +target-scoped actor is received by the server, that specific actor is +instantiated just in time to service the request. diff --git a/devtools/docs/contributor/backend/actor-registration.md b/devtools/docs/contributor/backend/actor-registration.md new file mode 100644 index 0000000000..c43fef29a7 --- /dev/null +++ b/devtools/docs/contributor/backend/actor-registration.md @@ -0,0 +1,39 @@ +# How to register an actor + +## Target-scoped actors vs. global actors + +Target-scoped actors are the most common types of actors. That's the type of actors you will most probably be adding. + +Target-scoped actors target a document, this could be a tab in Firefox or a remote document in Firefox for Android. + +Global actors however are for the rest, for things not related to any particular document but instead for things global to the whole Firefox/Chrome/Safari instance the toolbox is connected to (e.g. the preference actor). + +## The ActorRegistry.registerModule function + +To register a target-scoped actor: + +``` +ActorRegistry.registerModule("devtools/server/actors/webconsole", { + prefix: "console", + constructor: "WebConsoleActor", + type: { target: true } +}); +``` + +To register a global actor: + +``` +ActorRegistry.registerModule("devtools/server/actors/preference", { + prefix: "preference", + constructor: "PreferenceActor", + type: { global: true } +}); +``` + +If you are adding a new built-in actor, you should be registering it using `ActorRegistry.registerModule` in `addBrowserActors` or `addTargetScopedActors` in `/devtools/server/actors/utils/actor-registry.js`. + +## A note about lazy registration + +The `ActorRegistry` loads and creates all of the actors lazily to keep the initial memory usage down (which is extremely important on lower end devices). + +It becomes especially important when debugging pages with e10s when there are more than one process, because that's when we need to spawn a `DevToolsServer` per process (it may not be immediately obvious that the server in the main process is mostly only here for piping messages to the actors in the child process). diff --git a/devtools/docs/contributor/backend/backward-compatibility.md b/devtools/docs/contributor/backend/backward-compatibility.md new file mode 100644 index 0000000000..1a4cbf77ea --- /dev/null +++ b/devtools/docs/contributor/backend/backward-compatibility.md @@ -0,0 +1,90 @@ +# Backward Compatibility + +## Overview + +When making changes to the DevTools, there are certain backward compatibility requirements that we should keep in mind. + +In general, we should strive to maintain feature support for existing servers as we continue to make changes to the code base. At times, this can be difficult to achieve, however. + +## Specific Guidelines + +The important compatibility scenarios are: + +- Nightly desktop client **MUST** maintain existing compatibility back to release channel servers. + +This is mainly to simplify cross-platform use cases, i.e. desktop Nightly with release Fennec. + +- Servers **MAY** use traits to state a feature is not supported yet. + +This helps us support alternate environments, which does not implement every possible server feature. + +Certainly when a new feature needs a new actor method to function, it won't work with servers that don't support it. But we should still ensure the client doesn't explode when using unrelated, existing features, at least until the above time windows have elapsed. + +## Testing + +The harder part of this currently is that there is no automated testing to ensure the above guidelines have been met. While we hope to have this at some point, for now manual testing is needed here. + +The easiest way to test this is to check your work against a Firefox for Android device on release channel to ensure existing features in the area you are changing continue to function. That doesn't cover every case, but it's a great start. + +Alternatively, you can connect to a Firefox release server. This can be done in multiple steps: + +1. Start Firefox release from the command line, specifying the `--start-debugger-server` with an available port (e.g. `/Applications/Firefox.app/Contents/MacOS/firefox --start-debugger-server 6081`) +2. Navigate to a page where you can check that the part of DevTools which is impacted by the patch still works. +3. Build and run Firefox locally with the patch you want to check +4. In this build, open an `about:debugging` tab +5. On the `Network Location` section, fill in the host with localhost and the debugger server port you started the Firefox release instance with (e.g. `localhost:6081`) and hit Enter (or the `Add` button) +6. A new item will appear in the sidebar, click on its `Connect` button. +7. Accept the `Incoming connection` popup that appears +8. Click on the on sidebar item again. You will now see a list of the tabs and workers running in the Firefox release instance. Click on the `Inspect` button next to them to open a toolbox that is connected to the older server. + +## Feature Detection + +Starting with Firefox 36 (thanks to [bug 1069673](https://bugzilla.mozilla.org/show_bug.cgi?id=1069673)), you can use actor feature detection to determine which actors exist. + +### Target hasActor helper + +Detecting if the server has an actor: all you need is access to the `Toolbox` instance, which all panels do, when they get instantiated. Then you can do: + +```js +let hasSomeActor = toolbox.target.hasActor("actorTypeName"); +``` + +The `hasActor` method returns a boolean synchronously. + +### Traits + +Expose traits on an Actor in order to flag certain features as available or not. For instance if a new method "someMethod" is added to an Actor, expose a "supportsSomeMethod" flag in the traits object for the Actor, set to true. When debugging older servers, the flag will be missing and will default to false. + +Traits need to be forwarded to the client, and stored or used by the corresponding Front. There is no unique way of exposing traits, but there are still a few typical patterns found in the codebase. + +For Actors using a "form()" method, for which the Front is automatically created by protocol.js, the usual pattern is to add a "traits" property to the form, that contains all the traits for the actor. The Front can then read the traits in its corresponding "form()" method. Example: + +- [NodeActor form method](https://searchfox.org/mozilla-central/rev/e75e8e5b980ef18f4596a783fbc8a36621de7d1e/devtools/server/actors/inspector/node.js#209) +- [NodeFront form method](https://searchfox.org/mozilla-central/rev/e75e8e5b980ef18f4596a783fbc8a36621de7d1e/devtools/client/fronts/node.js#145) + +For other Actors, there are two options. First option is to define the trait on the Root actor. Those traits will be available both via TargetMixin::getTrait(), and on DevToolsClient.traits. The second option is to implement a "getTraits()" method on the Actor, which will return the traits for the Actor. Example: + +- [CompatibilityActor getTraits method](https://searchfox.org/mozilla-central/rev/e75e8e5b980ef18f4596a783fbc8a36621de7d1e/devtools/shared/specs/compatibility.js#40) +- [CompatibilitySpec getTraits definition](https://searchfox.org/mozilla-central/rev/e75e8e5b980ef18f4596a783fbc8a36621de7d1e/devtools/shared/specs/compatibility.js#40-43) +- [CompatibilityFront getTraits method](https://searchfox.org/mozilla-central/rev/e75e8e5b980ef18f4596a783fbc8a36621de7d1e/devtools/client/fronts/compatibility.js#41-47) + +Ironically, "getTraits" needs to be handled with backwards compatibility. But there is no way to check that "getTraits" is available on the server other than performing a try catch around the method. See the CompatibilityFront example. + +Whenever traits are added, make sure to add a relevant backward compatibility comment so that we know when the trait can be removed. + +## Maintaining backward compatibility code + +When introducing backward compatibility code, a comment should be added for extra information. +In order to simplify future code cleanups, the comment should follow the following syntax: +`// @backward-compat { version XX } Detailed comment`, where `XX` is the Firefox version this code was added in. + +Below is a made-up example of what it should look like: + +```js +// @backward-compat { version 85 } For older server which don't have the AwesomeActor, +// we have to do this another way. +if (!toolbox.target.hasActor("awesome")) { +``` + +Backward compatibility code can be safely removed when the revision it was added in reaches the release channel. +So if something landed in Firefox Nightly 85, it can be removed when Firefox 85 is released, i.e. when Firefox Nightly is 87. Search for the corresponding `@backward-compat` entries to retrieve all the code that can be removed. diff --git a/devtools/docs/contributor/backend/client-api.md b/devtools/docs/contributor/backend/client-api.md new file mode 100644 index 0000000000..05eb1e5921 --- /dev/null +++ b/devtools/docs/contributor/backend/client-api.md @@ -0,0 +1,245 @@ +# Client API + +DevTools has a client module that allows applications to be written that debug or inspect web pages using the [Remote Debugging Protocol](protocol.md). + +## Starting communication + +In order to communicate, a client and a server instance must be created and a protocol connection must be established. The connection can be either over a TCP socket or an nsIPipe. The `start` function displayed below establishes an nsIPipe-backed connection: + +```javascript +const { DevToolsServer } = require("devtools/server/devtools-server"); +const { DevToolsClient } = require("devtools/client/devtools-client"); + +function start() { + // Start the server. + DevToolsServer.init(); + DevToolsServer.registerAllActors(); + + // Listen to an nsIPipe + let transport = DevToolsServer.connectPipe(); + + // Start the client. + client = new DevToolsClient(transport); + + client.connect((type, traits) => { + // Now the client is connected to the server. + debugTab(); + }); +} +``` + +If a TCP socket is required, the function should be split in two parts, a server-side and a client-side, like this: + +```javascript +const { DevToolsServer } = require("devtools/server/devtools-server"); +const { DevToolsClient } = require("devtools/client/devtools-client"); + +function startServer() { + // Start the server. + DevToolsServer.init(); + DevToolsServer.registerAllActors(); + + // For an nsIServerSocket we do this: + DevToolsServer.openListener(2929); // A connection on port 2929. +} + +async function startClient() { + let transport = await DevToolsClient.socketConnect({ host: "localhost", port: 2929 }); + + // Start the client. + client = new DevToolsClient(transport); + + client.connect((type, traits) => { + // Now the client is connected to the server. + debugTab(); + }); +} +``` + +## Shutting down + +When the application is finished, it has to notify the client to shut down the protocol connection. This makes sure that memory leaks are avoided and the server is terminated in an orderly fashion. Shutting down is as simple as it gets: + +```javascript +function shutdown() { + client.close(); +} +``` + +## Attaching to a browser tab + +Attaching to a browser tab requires enumerating the available tabs and attaching to one: + +```javascript +function attachToTab() { + // Get the list of tabs to find the one to attach to. + client.mainRoot.listTabs().then(tabs => { + // Find the active tab. + let targetFront = tabs.find(tab => tab.selected); + + // Attach listeners for client events. + targetFront.on("tabNavigated", onTab); + }); +} +``` + +The devtools client will send event notifications for a number of events the application may be interested in. These events include state changes in the debugger, like pausing and resuming, stack frames or source scripts being ready for retrieval, etc. + +## Handling location changes + +When the user navigates away from a page, a `tabNavigated` event will be fired. The proper way to handle this event is to detach from the previous thread and tab and attach to the new ones: + +```javascript +async function onTab() { + // Detach from the previous tab. + await targetFront.detach(); + // Start debugging the new tab. + start(); +} +``` + +## Debugging JavaScript running in a browser tab + +Once the application is attached to a tab, it can attach to its thread in order to interact with the JavaScript debugger: + +```javascript +// Assuming the application is already attached to the tab, and response is the first +// argument of the attachTarget callback. + +client.attachThread(response.threadActor).then(function(threadFront) { + if (!threadFront) { + return; + } + + // Attach listeners for thread events. + threadFront.on("paused", onPause); + threadFront.on("resumed", fooListener); + + // Debugger is now ready and debuggee is running. +}); +``` + +## Debugger application example + +Here is the source code for a complete debugger application: + +```javascript +/* + * Debugger API demo. + */ +const { DevToolsServer } = require("devtools/server/devtools-server"); +const { DevToolsClient } = require("devtools/client/devtools-client"); + +let client; +let threadFront; + +function startDebugger() { + // Start the server. + DevToolsServer.init(); + DevToolsServer.registerAllActors(); + // Listen to an nsIPipe + let transport = DevToolsServer.connectPipe(); + // For an nsIServerSocket we do this: + // DevToolsServer.openListener(port); + // ...and this at the client: + // let transport = debuggerSocketConnect(host, port); + + // Start the client. + client = new DevToolsClient(transport); + client.connect((type, traits) => { + // Now the client is connected to the server. + debugTab(); + }); +} + +function shutdownDebugger() { + client.close(); +} + +/** + * Start debugging the current tab. + */ +async function debugTab() { + // Get the list of tabs to find the one to attach to. + const tabs = await client.mainRoot.listTabs(); + // Find the active tab. + let targetFront = tabs.find(tab => tab.selected); + // Attach to the thread (context). + const threadFront = await targetFront.attachThread(); + // Attach listeners for thread events. + threadFront.on("paused", onPause); + threadFront.on("resumed", fooListener); + // Debugger is now ready and debuggee is running. +} + +/** + * Handler for location changes. + */ +function onTab() { + // Detach from the previous tab. + client.detach().then(() => { + // Start debugging the new tab. + debugTab(); + }); +} + +/** + * Helper function to inspect the provided frame. + */ +function inspectFrame(frame) { + // Get the "this" object. + if (frame["this"]) { + getObjectProperties(frame["this"]); + } + + // Add "arguments". + if (frame.arguments && frame.arguments.length > 0) { + // frame.arguments is a regular Array. + dump("frame.arguments: " + frame.arguments.toSource() + "\n"); + + // Add variables for every argument. + let objClient = client.activeThread.pauseGrip(frame.callee); + objClient.getSignature(response => { + for (let i = 0; i < response.parameters.length; i++) { + let name = response.parameters[i]; + let value = frame.arguments[i]; + + if (typeof value == "object" && value.type == "object") { + getObjectProperties(value); + } + } + }); + } +} + +/** + * Helper function that retrieves the specified object's properties. + */ +function getObjectProperties(object) { + let thisClient = client.activeThread.pauseGrip(object); + thisClient.getPrototypeAndProperties(response => { + // Get prototype as a protocol-specified grip. + if (response.prototype.type != "null") { + dump("__proto__: " + response.prototype.toSource() + "\n"); + } + + // Get the rest of the object's own properties as protocol-specified grips. + for (let prop of Object.keys(response.ownProperties)) { + dump(prop + ": " + response.ownProperties[prop].toSource() + "\n"); + } + }); +} + +/** + * Generic event listener. + */ +function fooListener(event) { + dump(event + "\n"); +} + +// Run the program. +startDebugger(); + +// Execute the following line to stop the program. +//shutdownDebugger(); +``` diff --git a/devtools/docs/contributor/backend/debugger-api.md b/devtools/docs/contributor/backend/debugger-api.md new file mode 100644 index 0000000000..c37a405b6d --- /dev/null +++ b/devtools/docs/contributor/backend/debugger-api.md @@ -0,0 +1,5 @@ +# Debugger API + +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 [Debugger API](https://firefox-source-docs.mozilla.org/devtools-user/debugger-api/) page. diff --git a/devtools/docs/contributor/backend/protocol.js.md b/devtools/docs/contributor/backend/protocol.js.md new file mode 100644 index 0000000000..568344209e --- /dev/null +++ b/devtools/docs/contributor/backend/protocol.js.md @@ -0,0 +1,577 @@ +Writing an Actor +================ + +A Simple Hello World +-------------------- + +Here's a simple Hello World actor. It is a global actor (not associated with a given browser tab). +It has two parts: a spec and an implementation. The spec would go somewhere like +`devtools/shared/specs/hello-world.js` and would look like: + + const {Arg, RetVal, generateActorSpec} = require("devtools/shared/protocol"); + + const helloWorldSpec = generateActorSpec({ + typeName: "helloWorld", // I'll explain types later, I promise. + + methods: { + sayHello: { + // The request packet template. There are no arguments, so + // it is empty. The framework will add the "type" and "to" + // request properties. + request: {}, + + // The response packet template. The return value of the function + // will be plugged in where the RetVal() appears in the template. + response: { + greeting: RetVal("string") // "string" is the return value type. + } + }, + }, + }); + + // Expose the spec so it can be imported by the implementation. + exports.helloWorldSpec = helloWorldSpec; + +The actor implementation would go somewhere like +`devtools/server/actors/hello-world.js` and would look like: + + const protocol = require("devtools/shared/protocol"); + const {helloWorldSpec} = require("devtools/shared/specs/hello-world"); + + const HelloActor = protocol.ActorClassWithSpec(helloWorldSpec, { + initialize: function (conn) { + protocol.Actor.prototype.initialize.call(this, conn); // This is the worst part of heritage. + }, + + sayHello: function () { + return "hello"; + }, + }); + + // You also need to export the actor class in your module for discovery. + exports.HelloActor = HelloActor; + +To activate your actor, register it in the `addBrowserActors` method in `server/actors/utils/actor-registry.js`. +The registration code would look something like this: + + this.registerModule("devtools/server/actors/hello-world", { + prefix: "hello", + constructor: "HelloActor", + type: { global: true } + }); + +Your spec allows the actor to support a `sayHello` request. +A request/reply will look like this: + + -> { to: <actorID>, type: "sayHello" } + <- { from: <actorID>, greeting: "hello" } + +Now we can create a client side object. We call these *front* objects and +they typically go in `devtools/client/fronts/`. + +Here's the front for the HelloActor: + + const HelloFront = protocol.FrontClassWithSpec(helloWorldSpec, { + initialize: function (client, form) { + protocol.Front.prototype.initialize.call(this, client, form); + // This call may not be required but it's a good idea. It will + // guarantee that your instance is managed in the pool. + this.manage(this); + } + }); + +Note that there is no `sayHello` method. The FrontClass will generate a method on the Front object that matches the method declaration in the Actor class. + +The generated methods will return a Promise. That promise will resolve to the RetVal of the actor method. + +So if we have a reference to a HelloFront object, we can issue a `sayHello` request: + + hello.sayHello().then(greeting => { + console.log(greeting); + }); + +How do you get an initial reference to the front? That's a bit tricky, but basically there are two ways: + +* Manually +* Magically + +Manually - If you're using a DevToolsClient instance, you can discover the actorID manually and create a Front for it: + + let hello = new HelloFront(this.client, { actor: <hello actorID> }); + +Magically - Once you have an initial reference to a protocol.js object, it can return other protocol.js objects and fronts will automatically be created. + +Arguments +--------- + +`sayHello` has no arguments, so let's add a method that does take arguments. +Here's an adjustment to the spec: + + methods: { + echo: { + request: { echo: Arg(0, "string") }, + response: { echoed: RetVal("string") } + } + } + +Here's an adjustment to the implementation: + + echo: function (str) { + return str + "... " + str + "..."; + } + +This tells the library to place the 0th argument, which should be a string, in the `echo` property of the request packet. + + +This will generate a request handler whose request and response packets look like this: + + { to: <actorID>, type: "echo", echo: <str> } + { from: <actorID>, echoed: <str> } + +The client usage should be predictable: + + hello.echo("hello").then(str => { assert(str === "hello... hello...") }) + +The library tries hard to make using fronts feel like natural javascript (or as natural as you believe promises are, I guess). When building the response it will put the return value of the function where RetVal() is specified in the response template, and on the client side it will use the value in that position when resolving the promise. + +Returning JSON +-------------- + +Maybe your response is an object. Here's an example of a spec: + + methods: { + addOneTwice: { + request: { a: Arg(0, "number"), b: Arg(1, "number") }, + response: { ret: RetVal("json") } + } + } + +Here's an example implementation: + + addOneTwice: function (a, b) { + return { a: a + 1, b: b + 1 }; + } + +This will generate a response packet that looks like: + + { from: <actorID>, ret: { a: <number>, b: <number> } } + +That's probably unnecessary nesting (if you're sure you won't be returning an object with 'from' as a key!), so you can just replace `response` with: + + response: RetVal("json") + +and now your packet will look like: + + { from: <actorID>, a: <number>, b: <number> } + +Types and Marshalling +--------------------- + +Things have been pretty simple up to this point - all the arguments we've passed in have been javascript primitives. But for some types (most importantly Actor types, which I'll get to eventually), we can't just copy them into a JSON packet and expect it to work, we need to marshal things ourselves. + +Again, the protocol lib tries hard to provide a natural API to actors and clients, and sometime that natural API might involve object APIs. I'm going to use a wickedly contrived example, bear with me. Let's say I have a small object that contains a number and has a few methods associated with it: + + let Incrementor = function (i) { + this.value = value; + } + Incrementor.prototype = { + increment: function () { this.value++ }, + decrement: function () { this.value-- } + }; + + +and I want to return it from a backend function: + + // spec: + methods: { + getIncrementor: { + request: { number: Arg(0, "number") }, + response: { value: RetVal("incrementor") } // We'll define "incrementor" below. + } + } + + // implementation: + getIncrementor: function (i) { + return new Incrementor(i) + } + +I want that response to look like `{ from: <actorID>, value: <number> }`, but the client side needs to know to return an Incrementor, not a primitive number. So let's tell the protocol lib about Incrementors: + + protocol.types.addType("incrementor", { + // When writing to a protocol packet, just send the value + write: (v) => v.value, + + // When reading from a protocol packet, wrap with an Incrementor + // object. + read: (v) => new Incrementor(v) + }); + +And now our client can use the API as expected: + + front.getIncrementor(5).then(incrementor => { + incrementor.increment(); + assert(incrementor.value === 6); + }); + +You can do the same thing with arguments: + + // spec: + methods: { + passIncrementor: { + request: { Arg(0, "incrementor") }, + } + } + + // implementation: + passIncrementor: function (inc) { + w.increment(); + assert(incrementor.value === 6); + } + + front.passIncrementor(new Incrementor(5)); + +The library provides primitiive `boolean`, `number`, `string`, and `json` types. + +Moving right along, let's say you want to pass/return an array of Incrementors. You can just prepend `array:` to the type name: + + // spec: + methods: { + incrementAll: { + request: { incrementors: Arg(0, "array:incrementor") }, + response: { incrementors: RetVal("array:incrementor") } + } + } + + // implementation: + incrementAll: function (incrementors) { + incrementors.forEach(incrementor => { + incrementor.increment(); + } + return incrementors; + } + +You can use an iterator in place of an array as an argument or return value, and the library will handle the conversion automatically. + +Or maybe you want to return a dictionary where one item is a incrementor. To do this you need to tell the type system which members of the dictionary need custom marshallers: + + protocol.types.addDictType("contrivedObject", { + incrementor: "incrementor", + incrementorArray: "array:incrementor" + }); + + // spec: + methods: { + reallyContrivedExample: { + response: RetVal("contrivedObject") + } + } + + // implementations: + reallyContrivedExample: function () { + return { + /* a and b are primitives and so don't need to be called out specifically in addDictType */ + a: "hello", b: "world", + incrementor: new Incrementor(1), + incrementorArray: [new Incrementor(2), new Incrementor(3)] + } + } + + front.reallyContrivedExample().then(obj => { + assert(obj.a == "hello"); + assert(obj.b == "world"); + assert(incrementor.i == 1); + assert(incrementorArray[0].i == 2); + assert(incrementorArray[1].i == 3); + }); + +Nullables +--------- + +If an argument, return value, or dict property can be null/undefined, you can prepend `nullable:` to the type name: + + "nullable:incrementor", // Can be null/undefined or an incrementor + "array:nullable:incrementor", // An array of incrementors that can have holes. + "nullable:array:incrementor" // Either null/undefined or an array of incrementors without holes. + + +Actors +------ + +Probably the most common objects that need custom martialing are actors themselves. These are more interesting than the Incrementor object, but by default they're somewhat easy to work with. Let's add a ChildActor implementation that will be returned by the HelloActor (which is rapidly becoming the OverwhelminglyComplexActor): + + // spec: + const childActorSpec = generateActorSpec({ + actorType: "childActor", + methods: { + getGreeting: { + response: { greeting: RetVal("string") }, + } + } + }); + + // implementation: + const ChildActor = protocol.ActorClassWithSpec(childActorSpec, { + initialize: function (conn, id) { + protocol.Actor.prototype.initialize.call(this, conn); + this.greeting = "hello from " + id; + }, + getGreeting: function () { + return this.greeting; + }, + }); + + exports.ChildActor = ChildActor; + + const ChildFront = protocol.FrontClassWithSpec(childActorSpec, { + initialize: function (client, form) { + protocol.Front.prototype.initialize.call(this, client, form); + }, + }); + +The library will register a marshaller for the actor type itself, using typeName as its tag. + +So we can now add the following code to HelloActor: + + // spec: + methods: { + getChild: { + request: { id: Arg(0, "string") }, + response: { child: RetVal("childActor") } + } + } + + // implementation: + getChild: function (id) { + return ChildActor(this.conn, id); + } + + front.getChild("child1").then(childFront => { + return childFront.getGreeting(); + }).then(greeting => { + assert(id === "hello from child1"); + }); + +The conversation will look like this: + + { to: <actorID>, type: "getChild", id: "child1" } + { from: <actorID>, child: { actor: <childActorID> }} + { to: <childActorID>, type: "getGreeting" } + { from: <childActorID>, greeting: "hello from child1" } + +But the ID is the only interesting part of this made-up example. You're never going to want a reference to a ChildActor without checking its ID. Making an extra request just to get that id is wasteful. You really want the first response to look like `{ from: <actorID>, child: { actor: <childActorID>, greeting: "hello from child1" } }` + +You can customize the marshalling of an actor by providing a `form` method in the `ChildActor` class: + + form: function () { + return { + actor: this.actorID, + greeting: this.greeting + } + }, + +And you can demarshal in the `ChildFront` class by implementing a matching `form` method: + + form: function (form) { + this.actorID = form.actor; + this.greeting = form.greeting; + } + +Now you can use the id immediately: + + front.getChild("child1").then(child => { assert(child.greeting === "child1) }); + +You may come across a situation where you want to customize the output of a `form` method depending on the operation being performed. For example, imagine that ChildActor is a bit more complex, with a, b, c, and d members: + + ChildActor: + form: function () { + return { + actor: this.actorID, + greeting: this.greeting, + a: this.a, + b: this.b, + c: this.c, + d: this.d + } + } + ChildFront: + form: function (form) { + this.actorID = form.actorID; + this.id = form.id; + this.a = form.a; + this.b = form.b; + this.c = form.c; + this.d = form.d; + } + +And imagine you want to change 'c' and return the object: + + // Oops! If a type is going to return references to itself or any other + // type that isn't fully registered yet, you need to predeclare the type. + types.addActorType("childActor"); + + ... + + // spec: + methods: { + changeC: { + request: { newC: Arg(0) }, + response: { self: RetVal("childActor") } + } + } + + // implementation: + changeC: function (newC) { + c = newC; + return this; + } + + ... + + childFront.changeC('hello').then(ret => { assert(ret === childFront); assert(childFront.c === "hello") }); + +Now our response will look like: + + { from: <childActorID>, self: { actor: <childActorID>, greeting: <id>, a: <a>, b: <b>, c: "hello", d: <d> } + + +Lifetimes +--------- + +No, I don't want to talk about lifetimes quite yet. + +Events +------ + +Your actor has great news! + +Actors are subclasses of jetpack `EventTarget`, so you can just emit events. +Here's how you'd set it up in a spec: + + events: { + "good-news": { + type: "goodNews", // event target naming and packet naming are at odds, and we want both to be natural! + news: Arg(0) + } + } + + methods: { + giveGoodNews: { + request: { news: Arg(0) } + } + } + +Here's how the implementation would look: + + const EventEmitter = require("devtools/shared/event-emitter"); + + // In your protocol.ActorClassWithSpec definition: + giveGoodNews: function (news) { + EventEmitter.emit(this, "good-news", news); + } + +Now you can listen to events on a front: + + front.on("good-news", news => { + console.log(`Got some good news: ${news}\n`); + }); + front.giveGoodNews().then(() => { console.log("request returned.") }); + +If you want to modify the argument that will be passed to event listeners callbacks, you +can use `before(eventName, fn)` in the front definition. This can only be used once for a +given `eventName`. The `fn` function will be called before emitting the event via +the EventEmitter API on the Front, and its return value will be passed to the event +listener callbacks. If `fn` is async, the event will only be emitted after `fn` call resolves. + + // In front file, most probably in the constructor: + this.before("good-news", function(news) { + return news.join(" - "); + }); + + // In any consumer + front.on("good-news", function(news) { + console.log(news); + }); + +So if the server sent the following array: `[1, 2, 3]`, the console.log in the consumer +would print `1 - 2 - 3`. + +On a somewhat related note, not every method needs to be request/response. Just like an actor can emit a one-way event, a method can be marked as a one-way request. Maybe we don't care about giveGoodNews returning anything: + + // spec: + methods: { + giveGoodNews: { + request: { news: Arg(0, "string") }, + oneway: true + } + } + + // implementation: + giveGoodNews: function (news) { + emit(this, "good-news", news); + } + +Lifetimes +--------- + +No, let's talk about custom front methods instead. + +Custom Front Methods +-------------------- + +You might have some bookkeeping to do before issuing a request. Let's say you're calling `echo`, but you want to count the number of times you issue that request. Just use the `custom` tag in your front implementation: + + echo: custom(function (str) { + this.numEchos++; + return this._echo(str); + }, { + impl: "_echo" + }) + +This puts the generated implementation in `_echo` instead of `echo`, letting you implement `echo` as needed. If you leave out the `impl`, it just won't generate the implementation at all. You're on your own. + +Lifetimes +--------- + +OK, I can't think of any more ways to put this off. The remote debugging protocol has the concept of a *parent* for each actor. This is to make distributed memory management a bit easier. Basically, any descendents of an actor will be destroyed if the actor is destroyed. + +Other than that, the basic protocol makes no guarantees about lifetime. Each interface defined in the protocol will need to discuss and document its approach to lifetime management (although there are a few common patterns). + +The protocol library will maintain the child/parent relationships for you, but it needs some help deciding what the child/parent relationships are. + +The default parent of an object is the first object that returns it after it is created. So to revisit our earlier HelloActor `getChild` implementation: + + // spec: + methods: { + getChild: { + request: { id: Arg(0) }, + response: { child: RetVal("childActor") } + } + } + + // implementation: + getChild: function (id) { + return new ChildActor(this.conn, id); + } + +The ChildActor's parent is the HelloActor, because it's the one that created it. + +You can customize this behavior in two ways. The first is by defining a `marshallPool` property in your actor. Imagine a new ChildActor method: + + // spec: + methods: { + getSibling: { + request: { id: Arg(0) }, + response: { child: RetVal("childActor") } + } + } + + // implementation: + getSibling: function (id) { + return new ChildActor(this.conn, id); + } + +This creates a new child actor owned by the current child actor. But in this example we want all actors created by the child to be owned by the HelloActor. So we can define a `defaultParent` property that makes use of the `parent` property provided by the Actor class: + + get marshallPool() { return this.parent } + +The front needs to provide a matching `defaultParent` property that returns an owning front, to make sure the client and server lifetimes stay synced. diff --git a/devtools/docs/contributor/backend/protocol.md b/devtools/docs/contributor/backend/protocol.md new file mode 100644 index 0000000000..a577d6e722 --- /dev/null +++ b/devtools/docs/contributor/backend/protocol.md @@ -0,0 +1,1599 @@ +# Remote Debugging Protocol + +The Mozilla debugging protocol allows a debugger to connect to a browser, discover what sorts of things are present to debug or inspect, select JavaScript threads to watch, and observe and modify their execution. The protocol provides a unified view of JavaScript, DOM nodes, CSS rules, and the other technologies used in client-side web applications. The protocol ought to be sufficiently general to be extended for use with other sorts of clients (profilers, say) and servers (mail readers; random XULrunner applications). + +All communication between debugger (client) and browser (server) is in the form of JSON objects. This makes the protocol directly readable by humans, capable of graceful evolution, and easy to implement using stock libraries. In particular, it should be easy to create mock implementations for testing and experimentation. + +The protocol operates at the JavaScript level, not at the C++ or machine level, and assumes that the JavaScript implementation itself is healthy and responsive. The JavaScript program being executed may well have gone wrong, but the JavaScript implementation's internal state must not be corrupt. Bugs in the implementation may cause the debugger to fail; bugs in the interpreted program must not. + +## General Conventions + +### Actors + +An **actor** is something on the server that can exchange JSON packets with the client. Every packet from the client specifies the actor to which it is directed, and every packet from the server indicates which actor sent it. + +Each server has a root actor, with which the client first interacts. The root actor can explain what sort of thing the server represents (browser; mail reader; etc.), and enumerate things available to debug: tabs, chrome, and so on. Each of these, in turn, is represented by an actor to which requests can be addressed. Both artifacts of the program being debugged, like JavaScript objects and stack frames, and artifacts of the debugging machinery, like breakpoints and watchpoints, are actors with whom packets can be exchanged. + +For example, a debugger might connect to a browser, ask the root actor to list the browser's tabs, and present this list to the developer. If the developer chooses some tabs to debug, then the debugger can send `attach` requests to the actors representing those tabs, to begin debugging. + +Actor names are JSON strings, containing no spaces or colons. The name of the root actor is `"root"`. + +To allow the server to reuse actor names and the resources they require, actors have limited lifetimes. All actors in a server form a tree, whose root is the root actor. Closing communications with an actor automatically closes communications with its descendants. For example, the actors representing a thread's stack frames are children of the actor representing the thread itself, so that when a debugger detaches from a thread, which closes the thread's actor, the frames' actors are automatically closed. This arrangement allows the protocol to mention actors liberally, without making the client responsible for explicitly closing every actor that has ever been mentioned. + +When we say that some actor *A* is a child of some actor *B*, we mean that *A* is a direct child of *B*, not a grandchild, great-grandchild, or the like. Similarly, **parent** means "direct parent". We use the terms **ancestor** and **descendent** to refer to those looser relationships. + +The root actor has no parent, and lives as long as the underlying connection to the client does; when that connection is closed, all actors are closed. + +Note that the actor hierarchy does not, in general, correspond to any particular hierarchy appearing in the debuggee. For example, although web workers are arranged in a hierarchy, the actors representing web worker threads are all children of the root actor: one might want to detach from a parent worker while continuing to debug one of its children, so it doesn't make sense to close communications with a child worker simply because one has closed communications with its parent. + +*(We are stealing the "actor" terminology from Mozilla's IPDL, to mean, roughly, "things participating in the protocol". However, IPDL does much more with the idea than we do: it treats both client and server as collections of actors, and uses that detail to statically verify properties of the protocol. In contrast, the debugging protocol simply wants a consistent way to indicate the entities to which packets are directed.)* + +### Packets + +The protocol is carried by a reliable, bi-directional byte stream; data sent in both directions consists of JSON objects, called packets. A packet is a top-level JSON object, not contained inside any other value. + +Every packet sent from the client has the form: + +``` +{ "to":actor, "type":type, ... } +``` + +where `actor` is the name of the actor to whom the packet is directed and `type` is a string specifying what sort of packet it is. Additional properties may be present, depending on `type`. + +Every packet sent from the server has the form: + +``` +{ "from":actor, ... } +``` + +where `actor` is the name of the actor that sent it. The packet may have additional properties, depending on the situation. + +If a packet is directed to an actor that no longer exists, the server sends a packet to the client of the following form: + +``` +{ "from":actor, "error":"noSuchActor" } +``` + +where `actor` is the name of the non-existent actor. (It is strange to receive messages from actors that do not exist, but the client evidently believes that actor exists, and this reply allows the client to pair up the error report with the source of the problem.) + +Clients should silently ignore packet properties they do not recognize. We expect that, as the protocol evolves, we will specify new properties that can appear in existing packets, and experimental implementations will do the same. + +### Common Patterns of Actor Communication + +Each type of actor specifies which packets it can receive, which it might send, and when it can do each. Although in principle these interaction rules could be complex, in practice most actors follow one of two simple patterns: + +* **Request/Reply**: Each packet sent to the actor ("request") elicits a single packet in response ("reply"). +* **Request/Reply/Notify**: Like Request/Reply, but the actor may send packets that are not in response to any specific request ("notification"), perhaps announcing events that occur spontaneously in the debuggee. + +These patterns are described in more detail below. + +Some actors require more complicated rules. For example, the set of packets accepted by a [Thread-like actor](#interacting-with-thread-like-actors) depends on which one of four states it occupies. The actor may spontaneously transition from one state to another, and not all state transitions produce notification packets. Actors like this require careful specification. + +#### The Request/Reply Pattern + +In this specification, if we call a packet a **request**, then it is a packet sent by the client, which always elicits a single packet from the actor in return, the **reply**. These terms indicate a simple pattern of communication: the actor processes packets in the order they are received, and the client can trust that the *i*'th reply corresponds to the *i*'th request. + +An [error reply packet](#error-packets) from a request/reply actor constitutes a reply. + +Note that it is correct for a client to send several requests to a request/reply actor without waiting for a reply to each request before sending the next; requests can be pipelined. However, as the pending requests consume memory, the client should ensure that only a bounded number of requests are outstanding at any one time. + +#### The Request/Reply/Notify Pattern + +Some actors follow the request/reply pattern, but may also send the client ***notification*** packets, not in reply to any particular request. For example, if the client sends the root actor a `["listTabs"](#listing-browser-tabs)` request, then the root actor sends a reply. However, since the client has now expressed an interest in the list of open tabs, the root actor may subsequently send the client a `"tabListChanged"` notification packet, indicating that the client should re-fetch the list of tabs if it is interested in the latest state. + +There should be a small upper bound on the number of notification packets any actor may send between packets received from the client, to ensure that the actor does not flood the client. In the example above, the root actor sends at most one `"tabListChanged"` notification after each `"listTabs"` request. + +#### Error Packets + +Any actor can reply to a packet it is unable to process with an **error reply** of the form: + +``` +{ "from":actor, "error":name, "message":message } +``` + +where *name* is a JSON string naming what went wrong, and *message* is an English error message. Error *names* are specified by the protocol; the client can use the name to identify which error condition arose. The *message* may vary from implementation to implementation, and should only be displayed to the user as a last resort, as the server lacks enough information about the user interface context to provide appropriate messages. + +If an actor receives a packet whose type it does not recognize, it sends an error reply of the form: + +``` +{ "from":actor, "error":"unrecognizedPacketType", "message":message } +``` + +where *message* provides details to help debugger developers understand what went wrong: what kind of actor actor is; the packet received; and so on. + +If an actor receives a packet which is missing needed parameters (say, an `"autocomplete"` packet with no `"text"` parameter), it sends an error reply of the form: + +``` +{ "from":actor, "error":"missingParameter", "message":message } +``` + +where *message* provides details to help debugger developers fix the problem. + +If an actor receives a packet with a parameter whose value is inappropriate for the operation, it sends an error reply of the form: + +``` +{ "from":actor, "error":"badParameterType", "message":message } +``` + +where *message* provides details to help debugger developers fix the problem. (Some packets' descriptions specify more specific errors for particular circumstances.) + +### Grips + +A grip is a JSON value that refers to a specific JavaScript value in the debuggee. Grips appear anywhere an arbitrary value from the debuggee needs to be conveyed to the client: stack frames, object property lists, lexical environments, `paused` packets, and so on. + +For mutable values like objects and arrays, grips do not merely convey the value's current state to the client. They also act as references to the original value, by including an actor to which the client can send messages to modify the value in the debuggee. + +A grip has one of the following forms: + +``` +value +``` + +where value is a string, a number, or a boolean value. For these types of values, the grip is simply the JSON form of the value. + +``` +{ "type":"null" } +``` + +This represents the JavaScript `null` value. (The protocol does not represent JavaScript `null` simply by the JSON `null`, for the convenience of clients implemented in JavaScript: this representation allows such clients to use `typeof(grip) == "object"` to decide whether the grip is simple or not.) + +``` +{ "type":"undefined" } +``` + +This represents the JavaScript `undefined` value. (`undefined` has no direct representation in JSON.) + +``` +{ "type":"Infinity" } +``` + +This represents the JavaScript `Infinity` value. (`Infinity` has no direct representation in JSON.) + +``` +{ "type":"-Infinity" } +``` + +This represents the JavaScript `-Infinity` value. (`-Infinity` has no direct representation in JSON.) + +``` +{ "type":"NaN" } +``` + +This represents the JavaScript `NaN` value. (`NaN` has no direct representation in JSON.) + +``` +{ "type":"-0" } +``` + +This represents the JavaScript `-0` value. (`-0` stringifies to JSON as 0.) + +``` +{ "type":"object", "class":className, "actor":actor } +``` + +This represents a JavaScript object whose class is `className`. (Arrays and functions are treated as objects for the sake of forming grips.) Actor can be consulted for the object's contents, as explained below. + +If the class is "Function", the grip may have additional properties: + +``` +{ "type":"object", "class":"Function", "actor":actor, + "name":name, "displayName":displayName, + "userDisplayName":userDisplayName, + "url":url, "line":line, "column":column } +``` + +These additional properties are: + +***Name*** + +The function's name (as given in the source code, following the `function` keyword), as a string. If the function is anonymous, the `name` property is omitted. + +***displayName*** + +A name the system has inferred for the function (say, `"Foo.method"`). If the function has a given name (appearing in the grip as the `"name"` property), or if the system was unable to infer a suitable name for it, the `displayName` property is omitted. + +***userDisplayName*** + +If the function object has a `"displayName"` value property whose value is a string, this is that property's value. (Many JavaScript development tools consult such properties, to give developers a way to provide their own meaningful names for functions.) + +***url*** + +The URL of the function's source location (see [Source Locations](#source-locations)); + +***line*** + +The line number of the function's source location (see [Source Locations](#source-locations)); + +***column*** + +The column number of the function's source location (see [Source Locations](#source-locations)); + +``` +{ "type":"longString", "initial":initial, "length":length, "actor":actor } +``` + +This represents a very long string, where "very long" is defined at the server's discretion. `Initial` is some initial portion of the string, `length` is the string's full length, and actor can be consulted for the rest of the string, as explained below. + +For example, the following table shows some JavaScript expressions and the grips that would represent them in the protocol: + +| JavaScript Expression | Grip | +|:--------------------------------------------------------:|:---------------------------------------------------------------------------------------------:| +| 42 | 42 | +| true | true | +| "nasu" | "nasu" | +| (void 0) | `{ "type":"undefined" }` | +| ({x:1}) | `{ "type":"object", "class":"Object", "actor":"24" }` | +| "Arms and the man I sing, who, *[much, much more text]*" | `{ "type":"longString", "initial":"Arms and the man I sing", "length":606647, "actor":"25" }` | + +Garbage collection will never free objects visible to the client via the protocol. Thus, actors representing JavaScript objects are effectively garbage collection roots. + +#### Objects + +While a thread is paused, the client can send requests to the actors appearing in object grips to examine the objects they represent in more detail. + +##### Property Descriptors + +Protocol requests that describe objects' properties to the client often use **descriptors**, JSON values modeled after ECMAScript 5's property descriptors, to describe individual properties. + +A descriptor has the form: + +``` +{ "enumerable":<enumerable>, "configurable":<configurable>, ... } +``` + +where *enumerable* and *configurable* are boolean values indicating whether the property is enumerable and configurable, and additional properties are present depending on what sort of property it is. + +A descriptor for a data property has the form: + +``` +{ "enumerable":<enumerable>, "configurable":<configurable>, + "value":<value>, "writeable":<writeable> } +``` + +where *value* is a grip on the property's value, and *writeable* is a boolean value indicating whether the property is writeable. + +A descriptor for an accessor property has the form: + +``` +{ "enumerable":<enumerable>, "configurable":<configurable>, + "get":<getter>, "set":<setter> } +``` + +where *getter* and *setter* are grips on the property's getter and setter functions. These may be `{ "type":"undefined" }` if the property lacks the given accessor function. + +A **safe getter value descriptor** provides a value that an inherited accessor returned when applied to an instance. (See [Finding An Object's Prototype And Properties](#finding-an-objects-prototype-and-properties) for an explanation of why and when such descriptors are used.) Such a descriptor has the form: + +``` +{ "getterValue": <value>, "getterPrototypeLevel": <level>, + "enumerable":<enumerable>, "writable":<writable> } +``` + +where *value* is a grip on the value the getter returned, *level* is the number of steps up the object's prototype chain one must take to find the object on which the getter appears as an own property. If the getter appears directly on the object, *level* is zero. The *writable* property is true if the inherited accessor has a setter, and false otherwise. + +For example, if the JavaScript program being debugged evaluates the expression: + +``` +({x:10, y:"kaiju", get a() { return 42; }}) +``` + +then a grip on this value would have the form: + +``` +{ "type":"object", "class":"Object", "actor":<actor> } +``` + +and sending a ["prototypeAndProperties"](#finding-an-objects-prototype-and-properties) request to *actor* would produce the following reply: + +``` +{ "from":<actor>, "prototype":{ "type":"object", "class":"Object", "actor":<objprotoActor> }, + "ownProperties":{ "x":{ "enumerable":true, "configurable":true, "writeable":true, "value":10 }, + "y":{ "enumerable":true, "configurable":true, "writeable":true, "value":"kaiju" }, + "a":{ "enumerable":true, "configurable":true, + "get":{ "type":"object", "class":"Function", "actor":<getterActor> }, + "set":{ "type":"undefined" } + } + } +} +``` + + +Sending a ["prototypeAndProperties"](#finding-an-objects-prototype-and-properties) request to an object actor referring to a DOM mouse event might produce the following reply: + +``` +{ "from":<mouseEventActor>, "prototype":{ "type":"object", "class":"MouseEvent", "actor":<mouseEventProtoActor> }, + "ownProperties":{ } + "safeGetterValues":{ "screenX": { "getterValue": 1000, "getterPrototypeLevel": 1, + "enumerable": true, "writable": false }, + "screenY": { "getterValue": 1000, "getterPrototypeLevel": 1, + "enumerable": true, "writable": false }, + "clientX": { "getterValue": 800, "getterPrototypeLevel": 1, + "enumerable": true, "writable": false }, + "clientY": { "getterValue": 800, "getterPrototypeLevel": 1, + "enumerable": true, "writable": false }, + ... + } +} +``` + +##### Finding An Object's Prototype And Properties + +To examine an object's prototype and properties, a client can send the object's grip's actor a request of the form: + +``` +{ "to":<gripActor>, "type":"prototypeAndProperties" } +``` + +to which the grip actor replies: + +``` +{ "from":<gripActor>, "prototype":<prototype>, "ownProperties":<ownProperties> } +``` + +where *prototype* is a grip on the object's prototype (possibly `{ "type":"null" }`), and *ownProperties* has the form: + +``` +{ name:<descriptor>, ... } +``` + +with a *name*:<descriptor> pair for each of the object's own properties. + +The web makes extensive use of inherited accessor properties; for example, the `clientX` and `clientY`> properties of a mouse click event are actually accessor properties which the event object inherits from its prototype chain. It can be very valuable to display such properties' values directly on the object (taking care to distinguish them from true "own" properties), if the server can determine that the getters can be called without side effects. + +To this end, when possible, the server may provide safe getter value descriptors for an object, as described in [Property Descriptors](#property-descriptors) above, reporting the values that getter functions found on the object's prototype chain return when applied to that object. If the server chooses to provide any, the reply includes a `"safeGetterValues"` property of the form: + +``` +{ name:<descriptor>, ... } +``` + +with a *name*:<descriptor> pair for each safe getter the object inherits from its prototype chain, or that appears directly on the object. Each *descriptor* here is a safe getter value descriptor. + +*TODO: What about objects with many properties?* + +##### Finding an Object's Prototype + + +To find an object's prototype, a client can send the object's grip's actor a request of the form: + +``` +{ "to":<gripActor>, "type":"prototype" } +``` + +to which the grip actor replies: + +``` +{ "from":<gripActor>, "prototype":<prototype> } +``` + +where *prototype* is a grip on the object's prototype (possibly `{ "type":"null" }`). + + +##### Listing an Object's Own Properties' Names + +To list an object's own properties' names, a client can send the object's grip's actor a request of the form: + +``` +{ "to":<gripActor>, "type":"ownPropertyNames" } +``` + +to which the grip actor replies: + +``` +{ "from":<gripActor>, "ownPropertyNames":[ <name>, ... ] } +``` + +where each *name* is a string naming an own property of the object. + +##### Finding Descriptors For Single Properties + +To obtain a descriptor for a particular property of an object, a client can send the object's grip's actor a request of the form: + +``` +{ "to":<gripActor>, "type":"property", "name":<name> } +``` + +to which the grip actor replies: + +``` +{ "from":<gripActor>, "descriptor":<descriptor> } +``` + +where *descriptor* is a descriptor for the own property of the object named *name*, or `null` if the object has no such own property. + +A property descriptor has the form: + +``` +{ "configurable":<configurable>, "enumerable":<enumerable>, ... } +``` + +where *configurable* and *enumerable* are boolean values. *Configurable* is true if the property can be deleted or have its attributes changed. *Enumerable* is true if the property will be enumerated by a `for-in` enumeration. + +Descriptors for value properties have the form: + +``` +{ "configurable":<configurable>, "enumerable":<enumerable>, + "writable":<writable>, "value":<value> } +``` + +where *writable* is `true` if the property's value can be written to; *value* is a grip on the property's value; and *configurable* and *enumerable* are as described above. + +Descriptors for accessor properties have the form: + +``` +{ "configurable":<configurable>, "enumerable":<enumerable>, + "get":<get>, "set":<set> } +``` + +where *get* and *set* are grips on the property's getter and setter functions; either or both are omitted if the property lacks the given accessor function. *Configurable* and *enumerable* are as described above. + +*TODO: assign to value property* + +*TODO: special stuff for arrays* + +*TODO: special stuff for functions* + +*TODO: find function's source position* + +*TODO: get function's named arguments, in order* + +*TODO: descriptors for Harmony proxies* + +##### Functions + +If an object's class as given in the grip is `"Function"`, then the grip's actor responds to the messages given here. + +``` +{ "to":<functionGripActor>, "type":"parameterNames" } +``` + +This requests the names of the parameters of the function represented by *functionGripActor*. The reply has the form: + +``` +{ "from":<functionGripActor>, "parameterNames":[ <parameter>, ... ] } +``` + +where each *parameter* is the name of a formal parameter to the function as a string. If the function takes destructuring arguments, then *parameter* is a structure of JSON array and object forms matching the form of the destructuring arguments. + +``` +{ "to":<functionGripActor>, "type":"scope" } +``` + +Return the lexical environment over which the function has closed. The reply has the form: + +``` +{ "from":<functionGripActor>, "scope":<environment> } +``` + +where *environment* is a [lexical environment](#lexical-environments). Note that the server only returns environments of functions in a context being debugged; if the function's global scope is not the browsing context to which we are attached, the function grip actor sends an error reply of the form: + +``` +{ "from":<functionGripActor>, "error":"notDebuggee", "message":<message> } +``` + +where *message* is text explaining the problem. + +``` +{ "to":<functionGripActor>, "type":"decompile", "pretty":<pretty> } +``` + +Return JavaScript source code for a function equivalent to the one represented by *functionGripActor*. If the optional `pretty` parameter is present and *pretty* is `true`, then produce indented source code with line breaks. The reply has the form: + +``` +{ "from":<functionGripActor>, "decompiledCode":<code> } +``` + +where *code* is a string. + +If *functionGripActor*'s referent is not a function, or is a function proxy, the actor responds to these requests with an error reply of the form: + +``` +{ "from":<functionGripActor>, "error":"objectNotFunction", message:<message> } +``` + +where *message* is a string containing any additional information that would be helpful to debugger developers. + +#### Long Strings + +The client can find the full contents of a long string by sending a request to the long string grip actor of the form: + +``` +{ "to":<gripActor>, "type":"substring", "start":<start>, "end":<end> } +``` + +where *start* and *end* are integers. This requests the substring starting at the *start*'th character, and ending before the *end*'th character. The actor replies as follows: + +``` +{ "from":<gripActor>, "substring":<string> } +``` + +where *string* is the requested portion of the string the actor represents. Values for *start* less than zero are treated as zero; values greater than the length of the string are treated as the length of the string. Values for *end* are treated similarly. If *end* is less than *start*, the two values are swapped. (This is meant to be the same behavior as JavaScript's `String.prototype.substring`.) + +As with any other actor, the client may only send messages to a long string grip actor while it is alive: for [pause-lifetime grips](#grip-lifetimes), until the debuggee is resumed; or for [thread-lifetime grips](#grip-lifetimes), until the thread is detached from or exits. However, unlike object grip actors, the client may communicate with a long string grip actor at any time the actor is alive, regardless of whether the debuggee is paused. (Since strings are immutable values in JavaScript, the responses from a long string grip actor cannot depend on the actions of the debuggee.) + +#### Grip Lifetimes + +Most grips are **pause-lifetime** grips: they last only while the JavaScript thread is paused, and become invalid as soon as the debugger allows the thread to resume execution. (The actors in pause-lifetime grips are children of an actor that is closed when the thread resumes, or is detached from.) This arrangement allows the protocol to use grips freely in responses without requiring the client to remember and close them all. + +However, in some cases the client may wish to retain a reference to an object or long string while the debuggee runs. For example, a panel displaying objects selected by the user must update its view of the objects each time the debuggee pauses. To carry this out, the client can promote a pause-lifetime grip to a **thread-lifetime** grip, which lasts until the thread is detached from or exits. Actors in thread-lifetime grips are children of the thread actor. When the client no longer needs a thread-lifetime grip, it can explicitly release it. + +Both pause-lifetime and thread-lifetime grips are garbage collection roots. + +To promote a pause-lifetime grip to a thread-lifetime grip, the client sends a packet of the form: + +``` +{ "to":<gripActor>, "type":"threadGrip" } +``` + +where *gripActor* is the actor from the existing pause-lifetime grip. The grip actor will reply: + +``` +{ "from":<gripActor>, "threadGrip":<threadGrip> } +``` + +where *threadGrip* is a new grip on the same object, but whose actor is parented by the thread actor, not the pause actor. + +The client can release a thread-lifetime grip by sending the grip actor a request of the form: + +``` +{ "to":<gripActor>, "type":"release" } +``` + +The grip actor will reply, simply: + +``` +{ "from":<gripActor> } +``` + +This closes the grip actor. The `"release"` packet may only be sent to thread-lifetime grip actors; if a pause-lifetime grip actor receives a `"release"` packet, it sends an error reply of the form: + +``` +{ "from":<gripActor>, "error":"notReleasable", "message":<message> } +``` + +where each *gripActor* is the name of a child of *thread* that should be freed. The thread actor will reply, simply: + +``` +{ "from":<thread> } +``` + +Regardless of the lifetime of a grip, the client may only send messages to object grip actors while the thread to which they belong is paused; the client's interaction with mutable values cannot take place concurrently with the thread. + +### Completion Values + +Some packets describe the way a stack frame's execution completed using a **completion value**, which takes one of the following forms: + +``` +{ "return":<grip> } +``` + +This indicates that the frame completed normally, returning the value given by *grip*. + +``` +{ "throw":<grip> } +``` + +This indicates that the frame threw an exception; *grip* is the exception value thrown. + +``` +{ "terminated":true } +``` + +This indicates that the frame's execution was terminated, as by a "slow script" dialog box or running out of memory. + +### Source Locations + +Many packets refer to particular locations in source code: breakpoint requests specify where the breakpoint should be set; stack frames show the current point of execution; and so on. + +Descriptions of source code locations (written as *location* in packet descriptions) can take one of the following forms: + +``` +{ "url":<url>, "line":<line>, "column":<column> } +``` + +This refers to line *line*, column *column* of the source code loaded from *url*. Line and column numbers start with 1. If *column* or *line* are omitted, they default to 1. + +``` +{ "eval":<location>, "id":<id>, "line":<line>, "column":<column> } +``` + +This refers to line *line*, column *column* of the source code passed to the call to eval at *location*. To distinguish the different texts passed to eval, each is assigned a unique integer, *id*. + +``` +{ "function":<location>, "id":<id>, "line":<line>, "column":<column> } +``` + +This refers to line *line*, column *column* of the source code passed to the call to the `Function` constructor at *location*. To distinguish the different texts passed to the `Function` constructor, each is assigned a unique integer, *id*. + +As indicated, locations can be nested. A location like this one: + +``` +{ "eval":{ "eval":{ "url":"file:///home/example/sample.js", "line":20 } + "id":300, "line":30 } + "id":400, "line":40 } +``` + +refers to line 40 of the code passed to the call to eval occurring on line 30 of the code passed to the call to eval on line 20 of `file:///home/example/sample.js`. + +## The Root Actor + +When the connection to the server is opened, the root actor opens the conversation with the following packet: + +``` +{ "from":"root", "applicationType":<appType>, "traits":<traits>, ...} +``` + +The root actor's name is always `"root"`. *appType* is a string indicating what sort of program the server represents. There may be more properties present, depending on *appType*. + +*traits* is an object describing protocol variants this server supports that are not convenient for the client to detect otherwise. The property names present indicate what traits the server has; the properties' values depend on their names. If *traits* would have no properties, the `"traits"` property of the packet may be omitted altogether. This version of the protocol defines no traits, so if the `"traits"` property is present at all, its value must be an object with no properties, `{}`. + +For web browsers, the introductory packet should have the following form: + +``` +{ "from":"root", "applicationType":"browser", "traits":<traits> } +``` + +### Listing Browser Tabs + +To get a list of the tabs currently present in a browser, a client sends the root actor a request of the form: + +``` +{ "to":"root", "type":"listTabs" } +``` + +The root actor replies: + +``` +{ "from":"root", "tabs":[<tab>, ...], "selected":<selected> } +``` + +where each *tab* describes a single open tab, and *selected* is the index in the array of tabs of the currently selected tab. This form may have other properties describing other global actors; for one example, see [Chrome Debugging](#chrome-debugging). + +Each *tab* has the form: + +``` +{ "actor":<targetActor>, "title":<title>, "url":<URL> } +``` + +where *targetActor* is the name of an actor representing the tab, and *title* and *URL* are the title and URL of the web page currently visible in that tab. This form may have other properties describing other tab-specific actors. + +To attach to a *targetActor*, a client sends a message of the form: + +``` +{ "to":<targetActor>, "type":"attach" } +``` + +The target actor replies: + +``` +{ "from":<targetActor>, "threadActor":<tabThreadActor> } +``` + +where *tabThreadActor* is the name of a thread-like actor representing the tab's current content. If the user navigates the tab, *tabThreadActor* switches to the new content; we do not create a separate thread-like actor each page the tab visits. + +If the user closes the tab before the client attaches to it, *targetActor* replies: + +``` +{ "from":<targetActor>, "error":"exited" } +``` + +When the client is no longer interested in interacting with the tab, the client can request: + +``` +{ "to":<targetActor>, "type":"detach" } +``` + +The *targetActor* replies: + +``` +{ "from":<targetActor>, "type":"detached" } +``` + +If the client was not already attached to *targetActor*, *targetActor* sends an error reply of the form: + +``` +{ "from":<targetActor>, "error":"wrongState" } +``` + +While the client is attached, *targetActor* sends notifications to the client whenever the user navigates the tab to a new page. When navigation begins, *targetActor* sends a packet of the form: + +``` +{ "from":<targetActor>, "type":"tabNavigated", "state":"start", + "url":<newURL> } +``` + +This indicates that the tab has begun navigating to *newURL*; JavaScript execution in the tab's prior page is suspended. When navigation is complete, *targetActor* sends a packet of the form: + +``` +{ "from":<targetActor>, "type":"tabNavigated", "state":"stop", + "url":<newURL>, "title":<newTitle> } +``` + +where *newURL* and *newTitle* are the URL and title of the page the tab is now showing. The *tabThreadActor* given in the response to the original `"attach"` packet is now debugging the new page's code. + +### Chrome Debugging + +If the server supports debugging chrome code, the root actor's reply to a `"listTabs"` request includes a property named `"chromeDebugger"`, whose value is the name of a thread-like actor to which the client can attach to debug chrome code. + +## Interacting with Thread-Like Actors + +Actors representing independent threads of JavaScript execution, like browsing contexts and web workers, are collectively known as "threads". Interactions with actors representing threads follow a more complicated communication pattern. + +A thread is always in one of the following states: + +* **Detached**: the thread is running freely, and not presently interacting with the debugger. Detached threads run, encounter errors, and exit without exchanging any sort of messages with the debugger. A debugger can attach to a thread, putting it in the **Paused** state. Or, a detached thread may exit on its own, entering the **Exited** state. + +* **Running**: the thread is running under the debugger's observation, executing JavaScript code or possibly blocked waiting for input. It will report exceptions, breakpoint hits, watchpoint hits, and other interesting events to the client, and enter the **Paused** state. The debugger can also interrupt a running thread; this elicits a response and puts the thread in the **Paused** state. A running thread may also exit, entering the **Exited** state. + +* **Paused**: the thread has reported a pause to the client and is awaiting further instructions. In this state, a thread can accept requests and send replies. If the client asks the thread to continue or step, it returns to the **Running** state. If the client detaches from the thread, it returns to the **Detached** state. + +* **Exited**: the thread has ceased execution, and will disappear. The resources of the underlying thread may have been freed; this state merely indicates that the actor's name is not yet available for reuse. When the actor receives a "release" packet, the name may be reused. + +![Thread states](../resources/thread-states.png) + +These interactions are meant to have certain properties: + +* At no point may either client or server send an unbounded number of packets without receiving a packet from its counterpart. This avoids deadlock without requiring either side to buffer an arbitrary number of packets per actor. +* In states where a transition can be initiated by either the debugger or the thread, it is always clear to the debugger which state the thread actually entered, and for what reason.<br>For example, if the debugger interrupts a running thread, it cannot be sure whether the thread stopped because of the interruption, paused of its own accord (to report a watchpoint hit, say), or exited. However, the next packet the debugger receives will either be "paused", or "exited", resolving the ambiguity.<br>Similarly, when the debugger attaches to a thread, it cannot be sure whether it has succeeded in attaching to the thread, or whether the thread exited before the "attach" packet arrived. However, in either case the debugger can expect a disambiguating response: if the attach succeeded, it receives an "attached" packet; and in the second case, it receives an "exit" packet.<br>To support this property, the thread ignores certain debugger packets in some states (the "interrupt" packet in the **Paused** and **Exited** states, for example). These cases all handle situations where the ignored packet was preempted by some thread action. + +Note that the rules here apply to the client's interactions with each thread actor separately. A client may send an "interrupt" to one thread actor while awaiting a reply to a request sent to a different thread actor. + +*TODO: What about user selecting nodes in displayed content? Should those be eventy things the client can receive in the "paused" state? What does that mean for the "request"/"reply" pattern?* + +### Attaching To a Thread + +To attach to a thread, the client sends a packet of the form: + +``` +{ "to":<thread>, "type":"attach" } +``` + +Here, *thread* is the actor representing the thread, perhaps a browsing context from a "listContexts" reply. This packet causes the thread to pause its execution, if it does not exit of its own accord first. The thread responds in one of two ways: + +``` +{ "from":<thread>, "type":"paused", "why":{ "type":"attached" }, ... } +``` + +The thread is now in the **Paused** state, because the client has attached to it. The actor name *thread* remains valid until the client detaches from the thread or acknowledges a thread exit. This is an ordinary `"paused"` packet, whose form and additional properties are as described in [Thread Pauses](#thread-pauses), below. + +``` +{ "from":<thread>, "type":"exited" } +``` + +This indicates that the thread exited on its own before receiving the "attach" packet. The thread is now in the **Exited** state. The client should follow by sending a "release" packet; see [Exiting Threads](#exiting-threads), below. + +If the client sends an `"attach"` packet to a thread that is not in the **Detached** or **Exited** state, the actor sends an error reply of the form: + +``` +{ "from":<thread>, "error":"wrongState", "message":<message> } +``` + +where *message* details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. + +### Detaching From a Thread + +To detach from a thread, the client sends a packet of the form: + +``` +{ "to":<thread>, "type":"detach" } +``` + +The thread responds in one of three ways: + +``` +{ "from":<thread>, "type":"detached" } +``` + +This indicates that the client has detached from the thread. The thread is now in the **Detached** state: it can run freely, and no longer reports events to the client. Communications with *thread* are closed, and the actor name is available for reuse. If the thread had been in the **Paused** state, the pause actor is closed (because the pause actor is a child of *thread*). + +``` +{ "from":<thread>, "type":"paused", ... } +{ "from":<thread>, "type":"detached" } +``` + +This series of packets indicates that the thread paused of its own accord (for the reason given by the additional properties of the "paused" packet), and only then received the "detach" packet. As above, this indicates that the thread is in the **Detached** state, the just-created pause actor is closed, and the actor name is available for reuse. + +``` +{ "from":<thread>, "type":"exited" } +``` + +This indicates that the thread exited on its own before receiving the "detach" packet. The client should follow by sending a "release" packet; see [Exiting Threads](#exiting-threads), below. + +Detaching from a thread causes all breakpoints, watchpoints, and other debugging-related state to be forgotten. + +If the client sends a `"detach"` packet to a thread that is not in the **Running**, **Paused**, or **Exited** state, the actor sends an error reply of the form: + +``` +{ "from":<thread>, "error":"wrongState", "message":<message> } +``` + +where *message* details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. + +### Running Threads + +Once the client has attached to a thread, it is in the **Running** state. In this state, four things can happen: + +* The thread can hit a breakpoint or watchpoint, or encounter some other condition of interest to the client. +* The thread can exit. +* The client can detach from the thread. +* The client can interrupt the running thread. + +Note that a client action can occur simultaneously with a thread action. The protocol is designed to avoid ambiguities when both client and thread act simultaneously. + +### Thread Pauses + +If the thread pauses to report an interesting event to the client, it sends a packet of the form: + +``` +{ "from":<thread>, "type":"paused", "actor":<pauseActor>, "why":<reason>, + "currentFrame":<frame> } +``` + +This indicates that the thread has entered the **Paused** state, and explains where and why. + +*PauseActor* is a "pause actor", representing this specific pause of the thread; it lives until the thread next leaves the **Paused** state. The pause actor parents actors referring to values and other entities uncovered during this pause; when the thread resumes, those actors are automatically closed. This relieves the client from the responsibility to explicitly close every actor mentioned during the pause. + +Since actors in value grips are parented by the pause actor, this means that those grips become invalid when the thread resumes, or is detached from; it is not possible to take a grip from one pause and use it in the next. To create a grip that remains valid between pauses, see [Grip Lifetimes](#grip-lifetimes). + +The *currentFrame* value describes the top frame on the JavaScript stack; see [Listing Stack Frames](#listing-stack-frames), below. + +The *reason* value describes why the thread paused. It has one of the following forms: + +``` +{ "type":"attached" } +``` + +The thread paused because the client attached to it. + +``` +{ "type":"interrupted" } +``` + +The thread stopped because it received an "interrupt" packet from the client. + +``` +{ "type":"resumeLimit" } +``` + +The client resumed the thread with a `"resume"` packet that included a `resumeLimit` property, and the thread paused because the given *limit* was met. Execution remains in the frame the thread was resumed in, and that frame is not about to be popped. + +``` +{ "type":"resumeLimit", "frameFinished":<completion> } +``` + +The client resumed the thread with a `"resume"` packet that included a `resumeLimit` property, and the thread paused because the frame is about to be popped. *Completion* is a [completion value](#completion-values) describing how the frame's execution ended. The frame being popped is still the top frame on the stack, but subsequent `"resume"` operations will run in the calling frame. + +``` +{ "type":"debuggerStatement" } +``` + +The thread stopped because it executed a JavaScript "debugger" statement. + +``` +{ "type":"breakpoint", "actors":[<breakpointActor>...] } +``` + +The thread stopped at the breakpoints represented by the given actors. + +``` +{ "type":"watchpoint", "actors":[<watchpointActor>...] } +``` + +The thread stopped at the watchpoints represented by the given actors. + +*TODO: This should provide more details about the watchpoint in the packet, instead of incurring another round-trip before we can display anything helpful.* + +``` +{ "type":"clientEvaluated", "frameFinished":<completion> } +``` + +The expression given in the client's prior `clientEvaluate` command has completed execution; *completion* is a [completion value](#completion-values) describing how it completed. The frame created for the `clientEvaluate` resumption has been popped from the stack. See [Evaluating Source-Language Expressions](#evaluating-source-language-expressions) for details. + +### Resuming a Thread + +If a thread is in the **Paused** state, the client can resume it by sending a packet of the following form: + +``` +{ "to":<thread>, "type":"resume" } +``` + +This puts the thread in the **Running** state. The thread will pause again for breakpoint hits, watchpoint hits, throw watches, frame pop watches, and other standing pause requests. + +To step a thread's execution, the client can send a packet of the form: + +``` +{ "to":<thread>, "type":"resume", "resumeLimit":<limit> } +``` + +*Limit* must have one of the following forms: + +``` +{ "type":"next" } +``` + +The thread should pause: + +* just before the current frame is popped, whether by throwing an exception or returning a value; or +* when control in the current frame reaches a different statement than the one it is currently at. + +Note that execution in frames younger than the current frame never meets these conditions, so a `"next"` limit steps over calls, generator-iterator invocations, and so on. + +``` +{ "type":"step" } +``` + +The thread should pause: + +* just before the current frame is popped, whether by throwing an exception or returning a value; or +* just after a new frame is pushed; or +* when control in the current frame reaches a different statement than the one it is currently at. + +This is the same as `"next"`, except that it steps into calls. + +To resume the thread but have it stop when the current frame is about to be popped, the client can send a packet of the form: + +``` +{ "to":<thread>, "type":"resume", "resumeLimit":{ "type":"finish" } } +``` + +Here, the thread should pause just before the current frame is popped, whether by throwing an exception, returning a value, or being terminated. + +When a thread pauses because a limit was reached, the "paused" packet's *reason* will have a type of `"resumeLimit"`. + +A resume limit applies only to the current resumption; once the thread pauses, whether because the limit was reached or some other event occurred—a breakpoint hit, for example—the resume limit is no longer in effect. + +If no `"resumeLimit"` property appears in the `"resume"` packet, then the thread should run until some standing pause condition is met (a breakpoint is hit; a watchpoint triggers; or the like). + +To force the current frame to end execution immediately, the client can send a packet of the form: + +``` +{ "to":<thread>, "type":"resume", "forceCompletion":<completion> } +``` + +where *completion* is a [completion value](#completion-values) indicating whether the frame should return a value, throw an exception, or be terminated. Execution resumes in the current frame's caller, in the manner appropriate for *completion*. + +To request that execution pause when an exception is thrown, the client may send a request of the form: + +``` +{ "to":<thread>, "type":"resume", "pauseOnExceptions": true } +``` + +If `pauseOnExceptions` has the value `false` or is omitted, execution will continue in the face of thrown exceptions. When a thread pauses because an exception was thrown, the "paused" packet's *reason* will have the following form: + +``` +{ "type":"exception", "exception":<exception> } +``` + +where *exception* is a grip on the exception object. + +To request that execution pause on a DOM event, the client may send a request of the form: + +If a `"forceCompletion"` property is present in a `"resume"` packet, along with `"resumeLimit"`, or `"pauseOnExceptions"`, the thread will respond with an error: + +``` +{ "from":<thread>, "error":"badParameterType", "message":<message> } +``` + +A `"resume"` packet closes the pause actor the client provided in the "paused" packet that began the pause. + +If the client sends a `"resume"` packet to a thread that is not in the **Paused** state, the actor sends an error reply of the form: + +``` +{ "from":<thread>, "error":"wrongState", "message":<message> } +``` + +where *message* details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. + +### Interrupting a Thread + +If a thread is in the **Running** state, the client can cause it to pause where it is by sending a packet of the following form: + +``` +{ "to":<thread>, "type":"interrupt" } +``` + +The thread responds in one of two ways: + +``` +{ "from":<thread>, "type":"paused", "why":<reason>, ... } +``` + +This indicates that the thread stopped, and is now in the **Paused** state. If *reason* is `{ "type":"interrupted" }`, then the thread paused due to the client's *interrupt* packet. Otherwise, the thread paused of its own accord before receiving the *interrupt* packet, and will ignore the *interrupt* packet when it receives it. In either case, this is an ordinary `"paused"` packet, whose form and additional properties are as described in [Thread Pauses](#thread-pauses), above. + +``` +{ "from":<thread>, "type":"exited" } +``` + +This indicates that the thread exited before receiving the client's *interrupt* packet, and is now in the **Exited** state. See [Exiting Threads](#exiting-threads), below. + +If the client sends an `"interrupt"` packet to a thread that is not in the **Running**, **Paused**, or **Exited** state, the actor sends an error reply of the form: + +``` +{ "from":<thread>, "error":"wrongState", "message":<message> } +``` + +where *message* details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. + +### Exiting Threads + +When a thread in the **Running** state exits, it sends a packet of the following form: + +``` +{ "from":<thread>, "type":"exited" } +``` + +At this point, the thread can no longer be manipulated by the client, and most of the thread's resources may be freed; however, the thread actor name must remain alive, to handle stray `interrupt` and `detach` packets. To allow the last trace of the thread to be freed, the client should send a packet of the following form: + +``` +{ "to":<thread>, "type":"release" } +``` + +This acknowledges the exit and allows the thread actor name, *thread*, to be reused for other actors. + +## Inspecting Paused Threads + +When a thread is in the **Paused** state, the debugger can make requests to inspect its stack, lexical environment, and values. + +Only those packets explicitly defined to do so can cause the thread to resume execution. JavaScript features like getters, setters, and proxies, which could normally lead inspection operations like enumerating properties and examining their values to run arbitrary JavaScript code, are disabled while the thread is paused. If a given protocol request is not defined to let the thread run, but carrying out the requested operation would normally cause it to do so—say, fetching the value of a getter property—the actor sends an error reply of the form: + +``` +{ "from":<actor>, "error":"threadWouldRun", "message":<message>, "cause":<cause> } +``` + +where *message* is text that could be displayed to users explaining why the operation could not be carried out. *Cause* is one of the following strings: + +| *cause* value | meaning | +|:-------------:|:------------------------------------------------------------------------:| +| "proxy" | Carrying out the operation would cause a proxy handler to run. | +| "getter" | Carrying out the operation would cause an object property getter to run. | +| "setter" | Carrying out the operation would cause an object property setter to run. | + +(Taken together, the `"threadWouldRun"` error name and the *cause* value should allow the debugger to present an appropriately localized error message.) + +### Loading Script Sources + +To get a snapshot of all sources currently loaded by the thread actor, the client can send the following packet: + +``` +{ to: <threadActorID>, type: "sources" } +``` + +The response packet has the form: + +``` +{ from: <threadActorID>, sources: [<sourceForm1>, <sourceForm2>, ..., <sourceFormN>] } +``` + +Where each *sourceForm* has the following form: + +``` +{ actor: <sourceActorID>, + url: <sourceURL>, + isBlackBoxed: <isBlackBoxed> } +``` + +* *sourceActorID* is the source actor's id +* *sourceURL* is the URL of the source represented by the source actor +* *isBlackBoxed* is a boolean specifying whether the source actor's 'black-boxed' flag is set. See [Black Boxing Sources](#black-boxing-sources). + +Each source actor exists throughout the thread's whole lifetime. + +To get the contents of a source, send the corresponding source actor the following packet: + +``` +{ to: <sourceActorID>, type: "source" } +``` + +And the source actor replies with a packet of the following form: + +``` +{ from: <sourceActorID>, source: <contentsOfSource> } +``` + +where *contentsOfSource* is a grip representing the string of source code: either a JSON string, or a long string grip. (See [Grips](#grips) for a description of long string grips.) + +#### Black-Boxing Sources + +When debugging a web application that uses large off-the-shelf JavaScript libraries, it may help the developer focus on their own code to treat such libraries as "black boxes", whose internal details are omitted or simplified in the user interface. For example, the user interface could display a sub-chain of stack frames within a black-boxed library as a single element; breakpoints set in a black-boxed library could be disabled; and so on. + +Each source actor has a 'black-boxed' flag, and understands requests to set and clear the flag. When a source actor is black-boxed, the debugger does not pause when it hits breakpoints or `debugger` statements inside that source. If pausing on exceptions is enabled and an exception is thrown inside a black-boxed source, the debugger does not pause until the stack has unwound to a frame in a source that is not black-boxed. + +Thread actors still list black-boxed source actors in `"sources"` replies; and include stack frames running black-boxed code in `"frames"` requests. However, each *sourceForm* includes an `"isBlackBoxed"` property, giving the client all the information it needs to implement the black-boxing behavior in the user interface. + +To set a source actor's 'black-boxed' flag: + +``` +{ "to": <sourceActor>, "type": "blackbox" } +``` + +The *sourceActor* responds with a blank response on success: + +``` +{ "from": <sourceActor> } +``` + +Or an error response on failure: + +``` +{ "from": <sourceActor>, "error": <reason> } +``` + +To clear a source actor's 'black-boxed' flag: + +``` +{ "to": <sourceActor>, "type": "unblackbox" } +``` + +And once again, the *sourceActor* responds with a blank response on success: + +``` +{ "from": <sourceActor> } +``` + +Or an error response on failure: + +``` +{ "from": <sourceActor>, "error": <reason> } +``` + +### Listing Stack Frames + +To inspect the thread's JavaScript stack, the client can send the following request: + +``` +{ "to":<thread>, "type":"frames", "start":<start>, "count":<count> } +``` + +The `start` and `count` properties are optional. If present, *start* gives the number of the youngest stack frame the reply should describe, where the youngest frame on the stack is frame number zero; if absent, *start* is taken to be zero. If present, *count* specifies the maximum number of frames the reply should describe; if absent, it is taken to be infinity. (Clients should probably avoid sending `frames` requests with no *count*, to avoid being flooded by frames from unbounded recursion.) + +The thread replies as follows: + +``` +{ "from":<thread>, "frames":[<frame> ...] } +``` + +where each *frame* has the form: + +``` +{ "actor": <actor>, + "depth": <depth>, + "type": <type>, + "this": <this>, + ... } +``` + +where: + +* *actor* is the name of an actor representing this frame; +* *depth* is the number of this frame, starting with zero for the youngest frame on the stack; +* *type* is a string indicating what sort of frame this is; and +* *this* is a grip on the value of `this` for this call. + +The frame may have other properties, depending on *type*. + +All actors mentioned in the frame or grips appearing in the frame (*actor*, *callee*, *environment*, and so on) are parented by the thread actor. + +#### Global Code Frames + +A frame for global code has the form: + +``` +{ "actor":<actor>, + "depth":<depth>, + "type":"global", + "this":<this>, + "where":<location>, + "source":<source>, + "environment":<environment> } +``` + +where: + +* *location* is the source location of the current point of execution in the global code (see [Source Locations](#source-locations)); +* *environment* is a value representing the lexical environment of the current point of execution (see [Lexical Environments](#lexical-environments)); +* *source* is a source form as described in [Loading Script Sources](#loading-script-sources) + +and other properties are as above. + +#### Function Call Frames + +A frame for an ordinary JavaScript function call has the form: + +``` +{ "actor":<actor>, "depth":<depth>, "type":"call", "this":<this>, + "where":<location>, "environment":<environment>, + "callee":<callee>, "arguments":<arguments> } +``` + +where: + +* *callee* is a grip on the function value being called; +* *arguments* is an array of grips on the actual values passed to the function; + +and other properties are as above. + +If the callee is a host function, or a function scoped to some global other than the one to which we are attached, the `"where"` and `"environment"` properties are absent. + +The argument list may be incomplete or inaccurate, for various reasons. If the program has assigned to its formal parameters, the original values passed may have been lost, and compiler optimizations may drop some argument values. + +#### Eval Frames + +A frame for a call to `eval` has the form: + +``` +{ "actor":<actor>, "depth":<depth>, "type":"eval", "this":<this>, + "where":<location>, "environment":<environment> } +``` + +where the properties are as defined above. + +#### Client Evaluation Frames + +When the client evaluates an expression with an `clientEvaluate` packet, the evaluation appears on the stack as a special kind of frame, of the form: + +``` +{ "actor":<actor>, "depth":<depth>, "type":"clientEvaluate", "this":<this>, + "where":<location>, "environment":<environment> } +``` + +where the properties are as defined above. In this case, *where* will be a location inside the expression provided by the debugger. + +### Popping Stack Frames + +The client can remove frames from the stack by sending a request of the form: + +``` +{ "to":<frameActor>, "type":"pop", "completionValue":<completion> } +``` + +where *frameActor* is the actor representing the stack frame to pop, and *completion* is a [completion value](#completion-values) describing how the frame should appear to have finished execution. All younger stack frames are also popped. The thread remains paused. The frame actor will reply: + +``` +{ "from":<frameActor>, "watches":[<watchActor> ...] } +``` + +where each *watchActor* is the name of a frame pop watch actor that has been triggered in the process of popping the given frame. If no frame pop watches are triggered, the `watches` property may be omitted. + +*TODO: specify the error to return if the frame cannot be popped --- can host (C++) function frames be popped?* + +### Evaluating Source-Language Expressions + +To evaluate a source-language expression in a thread, the client sends a specialized `"resume"` packet of the form: + +``` +{ "to":<thread>, "type":"clientEvaluate", "expression":<expr>, "frame":<frame> } +``` + +This resumes the thread just as an ordinary `"resume"` packet does, but, rather than continuing execution where the pause took place, has the thread begin evaluation of the source-language expression given by *expr*, a string. The evaluation takes place in a new [Client Evaluation Frame](#client-evaluation-frames), pushed on top of *thread*'s current stack, using the environment of *frame*. *Frame* must be a live actor for one of *thread*'s frames, and the given frame must be one from which we can retrieve a lexical environment; that is, it must not be the frame for a call to a non-debuggee function. When evaluation of *expr* completes, the client will report a `clientEvaluate` pause containing the expression's value. + +If evaluating *expr* completes abruptly, this outcome is still reported via an `clientEvaluated` pause, so it is not necessary for the client to take explicit steps to catch exceptions thrown by the expression. + +If *frame* is not the name of an actor for a frame currently on *thread*'s stack, the thread actor sends a reply of the form: + +``` +{ "from":<thread>, "error":"unknownFrame", "message":<message> } +``` + +where *message* provides any details that would be helpful to the debugger developers. In this case, the thread's state is unaffected. + +If *frame* is not a frame whose environment we can access, the thread actor sends an error reply of the form: + +``` +{ "from":<thread>, "error":"notDebuggee", "message":<message> } +``` + +where *message* provides further appropriate details. + +If the client sends a `"clientEvaluate"` packet to a thread that is not in the **Paused** state, the actor sends an error reply of the form: + +``` +{ "from":<thread>, "error":"wrongState", "message":<message> } +``` + +where *message* details which state the thread was in instead (to make debugging debuggers easier). In this case, the thread's state is unaffected. + +*TODO: evaluate with given grips bound to given identifiers* + +## Lexical Environments + +A lexical environment (written as *environment* in packet descriptions) records the identifier bindings visible at a particular point in the program. An environment has one of the following forms: + +``` +{ "type":"object", "actor":<actor>, "object":<object>, "parent":<parentEnvironment> } +``` + +This represents a scope chain element whose identifier bindings reflect the properties of *object* (a grip). This could be the global object (`window` in a browser), or a DOM element (for event handler content attributes, which have the input element, form, and document on their scope chain along with the `window`). + +*Actor* is the name of an actor representing this lexical environment. The requests it can answer are described below. + +*ParentEnvironment* is a lexical environment describing the next enclosing environment; the `parent` property is omitted on the outermost environment. + +``` +{ "type":"function", "actor":<actor>, "function":<function>, + "bindings":<bindings>, "parent":<parentEnvironment> } +``` + +This represents the variable environment created by a call to *function* (a grip). *Bindings* describes the bindings in scope, including the function's arguments, the `arguments` object, and local `var` and function bindings; its form is described in detail below. The other properties are as described above. + +``` +{ "type":"with", "actor":<actor>, "object":<object>, "parent":<parentEnvironment> } +``` + +This represents an environment introduced by a `with` statement whose operand is *object* (a grip). The other properties are as described above. + +``` +{ "type":"block", "actor":<actor>, "bindings":<bindings>, "parent":<parentEnvironment> } +``` + +This represents an environment introduced by a `let` block, `for-in` statement, `catch` block, or the like. The properties are as described above. + +A *bindings* value has the form: + +``` +{ "arguments":[ { name:<descriptor> }, ... ], + "variables":{ name:<descriptor>, ... } } +``` + +Each *name* is the name of a bound identifier, as a string. Each *descriptor* is a [property descriptor](#property-descriptors) for the variable, presenting the variable's value as the descriptor's `"value"` property, and the variable's mutability as the descriptor's `"writable"` property. The descriptor's `"configurable"` property reflects whether the environment supports deleting and adding variables. Each descriptor's `"enumerable"` property is `true`. + +The `"arguments"` list appears only in bindings for `"function"` environments. It lists the arguments in the order they appear in the function's definition. (The same name may appear several times in the list, as permitted by JavaScript; the name's last appearance is the one in scope in the function.) + +Note that language implementations may omit some environment records from a function's scope if it can determine that the function would not use them. This means that it may be impossible for a debugger to find all the variables that ought to be in scope. + +To fully enumerate the bindings introduced by any lexical environment, the client can send a request of the following form to the environment's actor: + +``` +{ "to":<envActor>, "type":"bindings" } +``` + +The actor will reply as follows: + +``` +{ "from":<envActor>, "bindings":<bindings> } +``` + +Note that this request elicits a `"threadWouldRun"` error reply when *envActor* refers to an object environment whose object is a proxy. + +To change the value of a variable bound in a particular lexical environment, the client can send a request to the environment's actor: + +``` +{ "to":<envActor>, "type":"assign", "name":<name>, "value":<value> } +``` + +This changes the value of the identifier whose name is *name* (a string) to that represented by *value* (a grip). The actor will reply as follows, simply: + +``` +{ "from":<envActor> } +``` + +If the named identifier is immutable, the actor will send an error reply of the form: + +``` +{ "from":<envActor>, "error":"immutableBinding", "message":<message> } +``` + +If *envActor* refers to an object environment whose object is a proxy, or whose property named *name* has a setter function, this request elicits a `"threadWouldRun"` error reply. + +### Lexical Environment Examples + +For example, if we have the following JavaScript code: + +``` +function f(x) { + function g(y) { + var z = "value of z"; + alert(x + y); + } +} +``` + +we set a breakpoint on the line containing the call to `alert`, and then evaluate the expression: + +``` +f("argument to f")("argument to g") +``` + +then we would hit that breakpoint, eliciting a packet like the following: + +``` +{ "from":<thread>, "type":"paused", "actor":<pauseActor>, + "why":{ "type":"breakpoint", "actors":[<breakpointActor>] }, + "frame":{ "actor":<frameActor>, "depth":1, + "type":"call", "where":{ "url":"sample.js", "line":3 }, + "environment":{ "type":"function", "actor":<gFrameActor>, + "function":{ "type":"object", "class":"Function", "actor":<gActor> }, + "functionName":"g", + "bindings":{ arguments: [ { "y": { "value":"argument to g", "configurable":"false", + "writable":true, "enumerable":true } } ] }, + "parent":{ "type":"function", "actor":<fFrameActor>, + "function":{ "type":"object", "class":"Function", "actor":<fActor> }, + "functionName":"f", + "bindings": { arguments: [ { "x": { "value":"argument to f", "configurable":"false", + "writable":true, "enumerable":true } } ], + variables: { "z": { "value":"value of z", "configurable":"false", + "writable":true, "enumerable":true } } }, + "parent":{ "type":"object", "actor":<globalCodeActor>, + "object":{ "type":"object", "class":"Global", + "actor":<globalObjectActor> } + } + } + }, + "callee":<gActor>, "calleeName":"g", + "this":{ "type":"object", "class":"Function", "actor":<gActor> }, + "arguments":["argument to g"] + } +} +``` + +You can see here the three nested environment forms, starting with the `environment` property of the top stack frame, reported in the pause: + +* The first environment form shows the environment record created by the call to `g`, with the string `"argument to g"` passed as the value of `y`. +* Because `g` is nested within `f`, each function object generated for `g` captures the environment of a call to the enclosing function `f`. Thus, the next thing on `g`'s scope chain is an environment form for the call to `f`, where `"argument to f"` was passed as the vale of `x`. +* Because `f` is a top-level function, the (only) function object for `f` closes over the global object. This is the "type":"object" environment shown as the parent of `f`'s environment record. +* Because the global object is at the end of the scope chain, its environment form has no `parent` property. + +## Breakpoints + +While a thread is paused, a client can set breakpoints in the thread's code by sending requests of the form: + +``` +{ "to":<thread>, "type":"setBreakpoint", "location":<location> } +``` + +where *location* is a [source location](#source-locations). If the thread is able to establish a breakpoint at the given location, it replies: + +``` +{ "from":<thread>, "actor":<actor>, "actualLocation":<actualLocation> } +``` + +where *actor* is an actor representing the breakpoint (a child of the thread actor), and *actualLocation* is the location at which the breakpoint was really set. If *location* and *actualLocation* are the same, then the `actualLocation` property can be omitted. + +If the thread cannot find the script referred to in *location*, it sends an error reply of the form: + +``` +{ "from":<thread>, "error":"noScript" } +``` + +If *location* refers to a line and column at which the given script has no program code, and no reasonable alternative location can be chosen (say, by skipping forward), then the thread sends an error reply of the form: + +``` +{ "from":<thread>, "error":"noCodeAtLineColumn" } +``` + +To delete a breakpoint, the client can send the breakpoint's actor a message of the form: + +``` +{ "to":<breakpointActor>, "type":"delete" } +``` + +to which the breakpoint actor will reply, simply: + +``` +{ "from":<breakpointActor> } +``` + +This closes communications with *breakpointActor*. + +## Event Listeners + +To request a list of all the event listeners and event handlers (see [DOM Event Handlers](https://developer.mozilla.org/docs/Web/Guide/DOM/Events/Event_handlers#Definitions) for definitions of the two terms) attached to the page, the client sends a request of the form: + +``` +{ "to":<thread>, "type":"eventListeners" } +``` + +The thread replies with a response of the form: + +``` +{ "from":<thread>, "listeners":[ <listener>, ... ] } +``` + +Such requests can be sent when the thread is either paused or running. A *listener* value has the form: + +``` +{ "node":{ "selector":<node-selector>, "object":<node> }, + "type":<type>, + "capturing":<capturing>, + "allowsUntrusted":<allowsUntrusted>, + "inSystemEventGroup":<inSystemEventGroup>, + "isEventHandler":<isEventHandler>, + "function":<function> } +``` + +The values for these properties are: + +***node-selector*** + +A unique CSS selector of the DOM element on which the event handler is attached, or `"window"` if the handler is attached on the window. + +***node*** + +A grip on the DOM element on which the event handler is attached. + +***type*** + +The type of the DOM event as specified in the DOM specification (see [nsIEventListenerInfo](https://developer.mozilla.org/docs/XPCOM_Interface_Reference/nsIEventListenerInfo#Attributes)). + +***capturing*** + +A boolean flag indicating whether the event listener is in capture mode (see [nsIEventListenerInfo](https://developer.mozilla.org/docs/XPCOM_Interface_Reference/nsIEventListenerInfo#Attributes)). + +***allowsUntrusted*** + +A boolean flag that indicates whether the listener allows untrusted events (see [nsIEventListenerInfo](https://developer.mozilla.org/docs/XPCOM_Interface_Reference/nsIEventListenerInfo#Attributes)). + +***inSystemEventGroup*** + +A boolean flag that indicates whether or not the event listener is in the system event group (see [nsIEventListenerInfo](https://developer.mozilla.org/docs/XPCOM_Interface_Reference/nsIEventListenerInfo#Attributes)). + +***isEventHandler*** + +A boolean flag indicating whether this is an event handler or an event listener (see [DOM Event Handlers](https://developer.mozilla.org/docs/Web/Guide/DOM/Events/Event_handlers#Definitions) for definitions of the two terms). For HTML attribute handlers or assignments to WebIDL properties this flag would be true. + +***function*** + +A grip on the function object. + +## Stream Transport + +The debugging protocol is specified in terms of packets exchanged between a client and server, where each packet is either a JSON text or a block of bytes (a "bulk data" packet). The protocol does not specify any particular mechanism for carrying packets from one party to the other. Implementations may choose whatever transport they like, as long as packets arrive reliably, undamaged, and in order. + +This section describes the Mozilla Remote Debugging Protocol Stream Transport, a transport layer suitable for carrying Mozilla debugging protocol packets over a reliable, ordered byte stream, like a TCP/IP stream or a pipe. Debugger user interfaces can use it to exchange packets with debuggees in other processes (say, for debugging Firefox chrome code), or on other machines (say, for debugging Firefox OS apps running on a phone or tablet). + +(The Stream Transport is not the only transport used by Mozilla. For example, when using Firefox's built-in script debugger, the client and server are in the same process, so for efficiency they use a transport that simply exchanges the JavaScript objects corresponding to the JSON texts specified by the protocol, and avoid serializing packets altogether.) + +### Packets + +Once the underlying byte stream is established, transport participants may immediately begin sending packets, using the forms described here. The transport requires no initial handshake or setup, and no shutdown exchange: the first bytes on the stream in each direction are those of the first packet, if any; the last bytes on the stream in each direction are the final bytes of the last packet sent, if any. + +The transport defines two types of packets: JSON and bulk data. + +#### JSON Packets + +A JSON packet has the form: + +``` +length:JSON +``` + +where *length* is a series of decimal ASCII digits, *JSON* is a well-formed JSON text (as defined in [RFC 4627](http://www.ietf.org/rfc/rfc4627.txt)) encoded in UTF-8, and *length*, interpreted as a number, is the length of *JSON* in bytes. + +#### Bulk Data Packets + +A bulk data packet has the form: + +``` +bulk actor type length:data +``` + +where: + +* The keyword `bulk` is encoded in ASCII, and the spaces are always exactly one ASCII space +* *actor* is a sequence of Unicode characters, encoded in UTF-8, containing no spaces or colons +* *type* is a sequence of Unicode characters, encoded in UTF-8, containing no spaces or colons +* *length* is a sequence of decimal ASCII digits +* *data* is a sequence of bytes whose length is *length* interpreted as a number + +The *actor* field is the name of the actor sending or receiving the packet. (Actors are server-side entities, so if the packet was sent by the client, *actor* names the recipient; and if the packet was sent by the server, *actor* names the sender.) The protocol imposes the same syntactic restrictions on actor names that we require here. + +Which actor names are valid at any given point in an exchange is established by the remote debugging protocol. + +The *type* field defines the type of the packet, which may be used with the actor name to route the packet to its destination properly. The protocol provides more detail about the type, which remains in effect here. + +The content of a bulk data packet is exactly the sequence of bytes appearing as *data*. Data is not UTF-8 text. + +### Stream Requirements + +The Stream Transport requires the underlying stream to have the following properties: + +* It must be **transparent**: each transmitted byte is carried to the recipient without modification. Bytes whose values are ASCII control characters or fall outside the range of ASCII altogether must be carried unchanged; line terminators are left alone. +* It must be **reliable**: every transmitted byte makes it to the recipient, or else the connection is dropped altogether. Errors introduced by hardware, say, must be detected and corrected, or at least reported (and the connection dropped). The Stream Transport includes no checksums of its own; those are the stream's responsibility. (So, for example, a plain serial line is not suitable for use as an underlying stream.) +* It must be **ordered**: bytes are received in the same order they are transmitted, and bytes are not duplicated. (UDP packets, for example, may be duplicated or arrive out of order.) + +TCP/IP streams and USB streams meet these requirements. + +### Implementation Notes + +#### Constant-Overhead Bulk Data + +Mozilla added bulk data packets to the protocol to let devices with limited memory upload performance profiling and other large data sets more efficiently. Profiling data sets need to be as large as possible, as larger data sets can cover a longer period of time or more frequent samples. However, converting a large data set to a JavaScript object, converting that object to a JSON text, and sending the text over the connection entails making several temporary complete copies of the data; on small devices, this limits how much data the profiler can collect. Avoiding these temporary copies would allow small devices to collect and transmit larger profile data sets. Since it seemed likely that other sorts of tools would need to exchange large binary blocks efficiently as well, we wanted a solution usable by all protocol participants, rather than one tailored to the profiler's specific case. + +In our implementation of this Stream Transport, when a participant wishes to transmit a bulk data packet, it provides the actor name, the type, the data's length in bytes, and a callback function. When the underlying stream is ready to send more data, the transport writes the packet's `bulk actor type length:` header, and then passes the underlying `nsIOutputStream` to the callback, which then writes the packet's data portion directly to the stream. Similarly, when a participant receives a bulk data packet, the transport parses the header, and then passes the actor name, type, and the transport's underlying `nsIInputStream` to a callback function, which consumes the data directly. Thus, while the callback functions may well use fixed-size buffers to send and receive data, the transport imposes no overhead proportional to the full size of the data. diff --git a/devtools/docs/contributor/book.json b/devtools/docs/contributor/book.json new file mode 100644 index 0000000000..a0a738e0b7 --- /dev/null +++ b/devtools/docs/contributor/book.json @@ -0,0 +1,6 @@ +{ + "plugins": ["hints"], + "structure": { + "summary": "index.md" + } +} diff --git a/devtools/docs/contributor/bugs-issues.md b/devtools/docs/contributor/bugs-issues.md new file mode 100644 index 0000000000..254e1fc81e --- /dev/null +++ b/devtools/docs/contributor/bugs-issues.md @@ -0,0 +1,6 @@ +# Bugs and issue trackers + +Since we have code in two different places, issues and bugs are to be found in two different places: + +* For code in `m-c`: [http://firefox-dev.tools/](http://firefox-dev.tools/) which also lets you filter by good bugs for beginners. +* For code in `devtools-html`: [this page](https://github.com/search?l=&q=org%3Adevtools-html+state%3Aopen&type=Issues) lists all the issues across the organisation and these are [available issues](https://github.com/search?l=&q=org%3Adevtools-html+state%3Aopen+label%3Aavailable&type=Issues) i.e. ready to be worked on. diff --git a/devtools/docs/contributor/contributing.md b/devtools/docs/contributor/contributing.md new file mode 100644 index 0000000000..51c5ec3744 --- /dev/null +++ b/devtools/docs/contributor/contributing.md @@ -0,0 +1,47 @@ +# Contributing + +Thank you for taking the time to contribute! There are several areas where you can help: code, UX, bugs, talking about the DevTools, etc... + +--- + +## 👉 Code of conduct 👈 + +We strive for collaboration with [mutual respect](https://searchfox.org/mozilla-central/source/devtools/CODE_OF_CONDUCT.md) for each other. Mozilla also has a set of [participation guidelines](https://www.mozilla.org/en-US/about/governance/policies/participation/) which goes into greater detail specific to Mozilla employees and contributors. + +Please read the two links above before getting involved. **Contributions that don't abide by the code of conduct and participation guidelines will be rejected**. + +--- + +## Help with code + +Whether you're an external contributor or a Mozilla employee, the process to get your code into the repository is essentially the same: + +* You [find a bug to work on](./contributing/find-bugs.md) (*note: we use bugs to track 'broken' things, new features and even discussions*). +* [Work on the bug](./contributing/fixing-bugs.md). +* [Request a review](./contributing/making-prs.md) for your code. +* Land the code in the repository. +* And you've contributed—well done 😀 + +## Help with design and UX + +If you're more interested in user experience (think: wireframes, workflows, navigations... and not necessarily implementation details), please have a look at the [UX](https://github.com/firefox-devtools/ux) repository—our friendly designers will be more than happy to welcome you onboard. You can also have a look at the [issues](https://github.com/firefox-devtools/ux/issues) they are considering right now, to get an idea of how it works. + +## Help with BUGS! 🐛🐞 <!--TODO: we might want to split this out to another page with more detail, in addition to this introductory section--> + +Not less importantly, we also love **when people file bugs**. They help us a lot and are very valuable (specially when they come with reproducible steps, e.g. in the case of crashes or malfunctions). Here is a short [guide on how to file good bugs](./contributing/filing-good-bugs.md) if you've never done it before (or if you need a reminder). + +Another thing that is super valuable is **reproducing** bugs (to validate they're happening in more than one environment), and also **completing** bugs, i.e. ensuring the bug has steps to reproduce, a test case, etc, as [mentioned on the guide](./contributing/filing-good-bugs.md). This saves time for the person(s) who will work on the bug, as then they can jump straight to fixing or implementing whatever is needed, instead of doing research work. If you can do any of these for a given bug, add a comment with the additional data that you found out. + +Likewise, if you think that a bug is solved, because you can't reproduce it and doesn't happen any more, this is also useful to know. We can always do with closing more bugs, so please leave a comment detailing as much information as you can provide 😀 + +## Talking about the tools <!--TODO: same as above, might want a separate page on talking and maybe collecting talks?--> + +We really love when people talk about our work. Be it in a blog post, or in your favourite social media network, or user group, conference, you name it! We'd love to read/watch your article/talk, so please get in touch if you do. + +Answering other people's questions in [our Discourse forum](https://discourse.mozilla.org/c/devtools) or [developer mailing list](https://groups.google.com/forum/#!forum/mozilla.dev.developer-tools) is also helpful. + +## Other areas + +The above is not a comprehensive list; if you think you can help some other way that is not here, feel free to do it! + +**Whatever you choose to do, thank you so much for helping us** ❤️ diff --git a/devtools/docs/contributor/contributing/code-reviews-checklist.md b/devtools/docs/contributor/contributing/code-reviews-checklist.md new file mode 100644 index 0000000000..fb4959d3a1 --- /dev/null +++ b/devtools/docs/contributor/contributing/code-reviews-checklist.md @@ -0,0 +1,58 @@ +# Code reviews checklist + +This checklist is primarily aimed at reviewers, as it lists important points to check while reviewing a patch. + +It can also be useful for patch authors: if the changes comply with these guidelines, then it's more likely the review will be approved. + +## Bug status and patch file + +* Bug status is assigned, and assignee is correctly set. +* Commit title and message follow [the conventions](https://firefox-source-docs.mozilla.org/mobile/android/geckoview/contributor/contributing-to-mc.html). +* Commit message says [what is being changed and why](http://mozilla-version-control-tools.readthedocs.org/en/latest/mozreview/commits.html#write-detailed-commit-messages). +* Patch applies locally to current sources with no merge required. +* Check that every new file introduced by the patch has the proper Mozilla license header: https://www.mozilla.org/en-US/MPL/headers/ + +## Manual testing + +* Verify: + * if it's a new feature, the patch implements it. + * if it's a fix, the patch fixes the bug it addresses. +* Report any problems you find in the global review comment. +* Decide if any of those problems should block landing the change, or if they can be filed as follow-up bugs instead, to be fixed later. + +## Automated testing + +* Run new/modified tests, [with and without e10s](../tests/writing-tests.md#electrolysis). +* Watch out for tests that pass but log exceptions or end before protocol requests are handled. +* Watch out for slow/long tests: suggest many small tests rather than single long tests. +* Watch out for new tests written as integration tests instead of as unit tests: unit tests should be the preferred option, when possible. + +## Code review + +* Code changes: + * Review only what was changed by the contributor. + * Code formatting follows [our ESLint rules](eslint.md) and [coding standards](./coding-standards.md). + * Code is properly commented, JSDoc is updated, new "public" methods all have JSDoc, see the [comment guidelines](./javascript.md#comments). + * If Promise code was added/modified, the right promise syntax is used and rejections are handled. See [asynchronous code](./javascript.md#asynchronous-code). + * If a CSS file is added/modified, it follows [the CSS guidelines](./css.md). + * If a React or Redux module is added/modified, it follows the [React/Redux guidelines](./javascript.md#react--redux). + * If DevTools server code that should run in a worker is added/modified then it shouldn't use Services +* Test changes: + * The feature or bug is [tested by new tests, or a modification of existing tests](../tests/writing-tests.md). + * [Test logging](../tests/writing-tests.md#logs-and-comments) is sufficient to help investigating test failures/timeouts. + * [Test is e10s compliant](../tests/writing-tests.md#e10s-electrolysis) (doesn't try to access web content from the parent process, etc…). + * Tests are [clean and maintainable](../tests/writing-tests.md#writing-clean-maintainable-test-code). + * A try push has started (or even better, is green already). +* User facing changes: + * If any user-facing interfaces are added/modified, double-check the changes with the UX mockups or specs, if available. If there's any confusion, need-info the UX designer.<!--TODO this needs updating with the new process--> + * If a user facing string has been added, it is localized and follows [the localization guidelines](../files/adding-files.html#localization-l10n). + * If a user-facing string has changed meaning, [the key has been updated](https://mozilla-l10n.github.io/documentation/localization/making_string_changes.html). + * If a new image is added, it is a SVG image or there is a reason for not using a SVG. + * If a SVG is added/modified, it follows [the SVG guidelines](../frontend/svgs.md). + * If a documented feature has been modified, the keyword `dev-doc-needed` is present on the bug. + +## Finalize the review + +* R+: the code should land as soon as possible. +* R+ with comments: there are some comments, but they are minor enough, or don't require a new review once addressed, trust the author. +* R cancel / R- / F+: there is something wrong with the code, and a new review is required. diff --git a/devtools/docs/contributor/contributing/code-reviews-find-reviewer.md b/devtools/docs/contributor/contributing/code-reviews-find-reviewer.md new file mode 100644 index 0000000000..67548b5c30 --- /dev/null +++ b/devtools/docs/contributor/contributing/code-reviews-find-reviewer.md @@ -0,0 +1,5 @@ +# Finding suitable reviewers + +There are several options to find a good reviewer for a patch. If the bug you are working on is mentored, assign the review to the mentor. Otherwise, assign it to the triage owner (visible in the "People" section of a Bug in Bugzilla). + +Finally, an easy option is to use the #devtools-reviewers group in Phabricator. diff --git a/devtools/docs/contributor/contributing/code-reviews-setup.md b/devtools/docs/contributor/contributing/code-reviews-setup.md new file mode 100644 index 0000000000..a55f114c1a --- /dev/null +++ b/devtools/docs/contributor/contributing/code-reviews-setup.md @@ -0,0 +1,27 @@ +# Set up for code reviews + +There are two things you need to do before you can get a code review, although you only need to do this once 😃 + +## Set up to get code reviews in Phabricator + +We use an online tool called Phabricator for code reviews. To create an account in Phabricator, you first need the Bugzilla account that you created earlier. If you don't have one, [create it now](../getting-started/bugzilla.md). + +--- + +⚠️ *IMPORTANT:* ⚠️️️ + +It's helpful to have the same user name in both Bugzilla and Phabricator, so that people always know how to find you. + +Bugzilla's `Real name` field can be edited after the fact, but you cannot change Phabricator's username once the account has been created. + +If you added an `:ircnickname` in your Bugzilla's `Real name`, Phabricator will use that to pre-fill the username field when you create the account. **Please double check you like the proposed username, and make any corrections before you register**. + +--- + +Once you understand the above, please [create a Phabricator account](https://moz-conduit.readthedocs.io/en/latest/phabricator-user.html#creating-an-account). + + + +## Set up to send code for review + +In order to push your commit to Phabricator, you need to install [moz-phab](https://moz-conduit.readthedocs.io/en/latest/phabricator-user.html#using-moz-phab). diff --git a/devtools/docs/contributor/contributing/code-reviews.md b/devtools/docs/contributor/contributing/code-reviews.md new file mode 100644 index 0000000000..9ddb03b5bb --- /dev/null +++ b/devtools/docs/contributor/contributing/code-reviews.md @@ -0,0 +1,104 @@ +# Code reviews + +A review is required before code is added to Mozilla's repository. In addition, contrary to what you might have seen in other open source projects, code is not pushed directly to the repository. Instead, once the code is approved by reviewers, they will _request_ the code to be _landed_ on the repository. + +All this can be done somehow manually, but we have infrastructure in place to help us with reviews and landing code. + +Learn about how to get started with getting your code reviewed and landed in our [setting up](./code-reviews-setup.md) guide. + +And read on to learn about why and how we do code reviews. + +## Why do we do code reviews? + +### Quality + +Doing code reviews helps with **correctness**. It gives us a chance to check that the code: + +- fixes the problem, +- doesn't have bugs, +- doesn't regress other things, +- covers edge cases. + +A review is also a chance for the reviewer to check that the right **test coverage** is here, that the change doesn't have **performance** problems, that it **simplifies hard to understand code** and that it comes with **code comments** and **adequate documentation**. + +### Learning and sharing knowledge + +While going through the process of doing a code review, **both the reviewer and the reviewee** will be learning something (a new part of the code, a new efficient way to write code, tests, localization, etc.). + +Making the code easy to understand by the reviewer helps everybody. + +Doing reviews also helps people working on DevTools feel more comfortable and learn about new parts of the codebase. + +It helps build consensus around ideas and practices. + +### Maintenance + +Doing reviews gives us an opportunity to check we are willing to maintain and support the new code that being introduced for a feature or bug fix. + +### Code consistency + +It is also a great opportunity for us to check that whatever new code is being written is consistent with what already exists in the code base. + +### Shared responsibility + +Approving a code review means that you are the second person who thinks this change is correct and a good idea. Doing this makes you responsible for the code change just as much as the author. + +It is the entire DevTools group who owns the code, not just the author. We write code as a group, not as individuals, because on the long-term it's the group that maintains it. + +Having a review discussion shares the ownership of the code, because authors and reviewers all put a little bit of themselves in it, and the code that results isn't just the result of one person's work + +## What should be avoided in code reviews? + +- Style nits that a linter should ideally handle. +- Requests that are outside of the scope of the bug. + +## What are some good practices for code reviews? + +### Use empathetic language. + +More reading: + +- [Mindful Communication in Code Reviews](http://amyciavolino.com/assets/MindfulCommunicationInCodeReviews.pdf) +- [How to Do Code Reviews Like a Human](https://mtlynch.io/human-code-reviews-1/) + +### Prefer smaller commits over large monolithic commits. + +It makes it easier for the reviewer to provide a quality review. It's also easier to get consensus on the change if that change is small. Finally, it's easier to understand the risk, and find regressions. + +### Be explicit + +Specifically, be explicit about required versus optional changes. + +Reviews are conversations, and the reviewee should feel comfortable on discussing and pushing back on changes before making them. + +### Be quick + +As a reviewer, strive to answer in a couple of days, or at least under a week. + +If that is not possible, please update the bug to let the requester know when you'll get to the review. Or forward it to someone else who has more time. + +### Ask for help + +Know when you're out of your comfort zone as a reviewer, and don't hesitate to ask for an extra review to someone else. + +It's fine, you can't know everything, and the more people participate, the more everybody learns, and the more bugs we avoid. + +## How do we communicate? + +First and foremost, like in any Mozilla-run platforms or events, please abide by [the Community Participation Guidelines](https://www.mozilla.org/en-US/about/governance/policies/participation/). + +Maintainers should **lead by example through their tone and language**. We want to build an inclusive and safe environment for working together. + +As a reviewer, **double-check your comments**. Just because you're a reviewer and think you have a better solution doesn't mean that's true. Assume **the code author has spent more time thinking about this part of the code than you have** (if you're the reviewer) and might actually be right, even if you originally thought something was wrong. It doesn't take long to look up the code and double-check. + +**Being inclusive** is highly important. We shouldn't make any assumptions about the person requesting a review, or about the person you're asking a review from, and always provide as much information as required, in a way that is as inclusive as possible. + +The bug will live forever online, and many people will read it long after the author and reviewers are done. + +Talking over video/IRC/Slack is a great way to get buy-in and share responsibility over a solution. It is also helpful to resolve disagreement and work through issues in a more relaxed environment. + +**Do not criticize** the reviewee, talk about the quality of the code on its own, and not directly how the reviewee wrote the code. + +Take the time to thank and point out good code changes. + +**Using "please" and “what do you think?”** goes a long way in making others feel like colleagues, and not subordinates. diff --git a/devtools/docs/contributor/contributing/coding-standards.md b/devtools/docs/contributor/contributing/coding-standards.md new file mode 100644 index 0000000000..43d6a496e9 --- /dev/null +++ b/devtools/docs/contributor/contributing/coding-standards.md @@ -0,0 +1,9 @@ +# Coding standards + +Our code base is quite large, and a lot of different people contribute to it all the time. Therefore, it's important to share standards to keep the code consistent and written in a predictable style. This also helps avoid common mistakes. + +We have pages defining standards, best practices and tips for various languages used in our tools: + +* [JavaScript](./javascript.md) +* [CSS](./css.md) +* [SVG](../frontend/svgs.md) diff --git a/devtools/docs/contributor/contributing/css.md b/devtools/docs/contributor/contributing/css.md new file mode 100644 index 0000000000..ed54c2dfcf --- /dev/null +++ b/devtools/docs/contributor/contributing/css.md @@ -0,0 +1,130 @@ +# CSS + +This page is for information about CSS used by DevTools. Wondering about the Dev Edition theme? See this page for more information about the [Developer Edition theme](https://wiki.mozilla.org/DevTools/Developer_Edition_Theme). + +## Basics + +The CSS code is in `devtools/client/themes`. + +Here are some basic tips that can optimize reviews if you are changing CSS: + +* Avoid `!important` but if you have to use it, make sure it's obvious why you're using it (maybe with a comment). +* Avoid magic numbers, prefer automatic sizing. +* Avoid platforms specific styles, put everything in the `shared` directory. +* Avoid preprocessor variables, use CSS variables instead. +* Avoid setting styles in JavaScript. It's generally better to set a class and then specify the styles in CSS +* `classList` is generally better than `className`. There's less chance of over-writing an existing class. + +### Boilerplate + +Make sure each file starts with the standard copyright header (see [License Boilerplate](https://www.mozilla.org/MPL/headers/)). + +### Testing + +CSS changes should generally be similar across platforms since they used a shared implementation, but there can still be differences worth checking out. Check major changes on Windows, OS X and Ubuntu. + +## Formatting + +We use 2-spaces indentation for the CSS. + +In general the formatting looks like this: + +```css +selector, +alternate-selector { + property: value; + other-property: other-value; +} +``` +<!--TODO: add examples for long shorthand properties, and multi-valued properties (background, font-family, ...)--> +Also: + +* Omit units on 0 values. + * Example: Use `margin: 0;`, not `margin: 0px;`. +* Add a space after each comma, **except** within color functions. + * Example: `linear-gradient(to bottom, black 1px, rgba(255,255,255,0.2) 1px)`. +* Always add a space before ` !important`. +* Assume `="true"` in attribute selectors. + * Example: Use `option[checked]`, not `option[checked="true"]`. +* Use longhand versions of properties so it's clear what you're changing. + * Example: Use `border-color: red`, not `border: red;`. + +Naming standards for class names: + +* `lower-case-with-dashes` is the most common. +* But `camelCase` is also used sometimes. Try to follow the style of existing or related code. + +## Light and Dark theme support + +DevTools supports 2 different themes: the dark theme and the light theme. In order to support them, there are 2 class names available (`theme-dark` and `theme-light`). + +* Use [pre-defined CSS variables](https://searchfox.org/mozilla-central/source/devtools/client/themes/variables.css) instead of hardcoding colors when possible. +* If you need to support themes and the pre-defined variables don't fit, define a variable with your custom colors at the beginning of the CSS file. This avoids selector duplication in the code. + +Example: + +```css +.theme-light { + --some-variable-name: <color-for-light-theme>; +} +.theme-dark { + --some-variable-name: <color-for-dark-theme>; +} +#myElement { + background-color: var(--some-variable-name); +} +``` + +## HDPI support + +It's recommended to use SVG since it keeps the CSS clean when supporting multiple resolutions. However, if only 1x and 2x PNG assets are available, you can use this `@media` query to target higher density displays (HDPI): `@media (min-resolution: 1.1dppx)`. <!--TODO an example would be good here--> + +## Performance + +* Use an iframe where possible so your rules are scoped to the smallest possible set of nodes.<!--TODO: is this still true? and also refine exactly when it is appropriate to use an iframe. Examples might help--> +* If your CSS is used in `browser.xhtml`, you need to take special care with performance: + * Descendent selectors should be avoided. + * If possible, find ways to use **only** id selectors, class selectors and selector groups. + +## Localization + +### Text Direction +* For margins, padding and borders, use `inline-start`/`inline-end` rather than `left`/`right`. + * Example: Use `margin-inline-start: 3px;` not `margin-left: 3px`. +* For RTL-aware positioning (left/right), use `inset-inline-start/end`. +* When there is no special RTL-aware property (eg. `float: left|right`) available, use the pseudo `:-moz-locale-dir(ltr|rtl)` (for XUL files) or `:dir(ltr|rtl)` (for HTML files). +* Remember that while a tab content's scrollbar still shows on the right in RTL, an overflow scrollbar will show on the left. +* Write `padding: 0 3px 4px;` instead of `padding: 0 3px 4px 3px;`. This makes it more obvious that the padding is symmetrical (so RTL won't be an issue). + +### RTL support for html modules + +By default, new HTML modules support only left-to-right (LTR) and do not reuse the current direction of the browser. + +To enable right-to-left (RTL) support in a module, set the `[dir]` attribute on the document element of the module: +* Example: `<html xmlns="http://www.w3.org/1999/xhtml" dir="">`. + +The appropriate value for the `dir` attribute will then be set when the toolbox loads this module. + +### Testing + +The recommended workflow to test RTL on DevTools is to use the [Force RTL extension](https://addons.mozilla.org/en-US/firefox/addon/force-rtl/). After changing the direction using Force RTL, you should restart DevTools to make sure all modules apply the new direction. A future version of Force RTL will be able to update dynamically all DevTools documents.<!--TODO: update when the fate of this addon/webextension is known--> + +## Toggles + +Sometimes you have a style that you want to turn on and off. For example a tree twisty (a expand-collapse arrow), a tab background, etc. + +The Mozilla way is to perform the toggle using an attribute rather than a class: + +```css +.tree-node { + background-image: url(right-arrow.svg); +} +.tree-node[open] { + background-image: url(down-arrow.svg); +} +``` + +## Tips + +* Use `:empty` to match a node that doesn't have children. +* Usually, if `margin` or `padding` has 4 values, something is wrong. If the left and right values are asymmetrical, you're supposed to use `-start` and `-end`. If the values are symmetrical, use only 3 values (see localization section). diff --git a/devtools/docs/contributor/contributing/eslint-atom-settings.png b/devtools/docs/contributor/contributing/eslint-atom-settings.png Binary files differnew file mode 100644 index 0000000000..1108c206dc --- /dev/null +++ b/devtools/docs/contributor/contributing/eslint-atom-settings.png diff --git a/devtools/docs/contributor/contributing/eslint-atom.png b/devtools/docs/contributor/contributing/eslint-atom.png Binary files differnew file mode 100644 index 0000000000..52350a31ec --- /dev/null +++ b/devtools/docs/contributor/contributing/eslint-atom.png diff --git a/devtools/docs/contributor/contributing/eslint-sublimetext3.png b/devtools/docs/contributor/contributing/eslint-sublimetext3.png Binary files differnew file mode 100644 index 0000000000..8a76f06cda --- /dev/null +++ b/devtools/docs/contributor/contributing/eslint-sublimetext3.png diff --git a/devtools/docs/contributor/contributing/eslint-vscode.png b/devtools/docs/contributor/contributing/eslint-vscode.png Binary files differnew file mode 100644 index 0000000000..41a58fe9d4 --- /dev/null +++ b/devtools/docs/contributor/contributing/eslint-vscode.png diff --git a/devtools/docs/contributor/contributing/eslint.md b/devtools/docs/contributor/contributing/eslint.md new file mode 100644 index 0000000000..88da959255 --- /dev/null +++ b/devtools/docs/contributor/contributing/eslint.md @@ -0,0 +1,157 @@ +# Using ESLint in DevTools +<!--TODO paths, executables and everything here should be reviewed when we go to GitHub--> + +The main rule set is in `devtools/.eslintrc`. It is meant to be used with ESLint 3.5.0. + +Note that the file `.eslintignore` at the root of the repository contains a list of paths to ignore. This is because a lot of the code isn't ESLint compliant yet. We're in the process of making code free of ESLint warnings and errors, but this takes time. In the meantime, make sure the file or folder you are running ESLint on isn't ignored. + +## Installing + +From the root of the project type: + +`./mach eslint --setup` + +ESLint, `eslint-plugin-html`, `eslint-plugin-mozilla` and `eslint-plugin-react` will be automatically downloaded and installed. + +## Running ESLint + +### From the command line + +The preferred way of running ESLint from the command line is by using `mach` like this: + +```bash +./mach eslint path/to/directory +``` + +This ensures that ESLint runs with the same configuration that our CI environment (see the next section). + +### In continuous integration + +Relying only on people to run ESLint isn't enough to guarantee new warnings or errors aren't introduced in the code. Therefore, ESLint also runs automatically in our Continuous Integration environment. + +This means that every time a commit is pushed to one of the repositories, a job runs ESLint on the whole code. + +If you are pushing a patch to the [`try` repository](https://wiki.mozilla.org/ReleaseEngineering/TryServer) to run the tests, then you can also tell it to run the ESLint job and therefore verify that you did not introduce new errors. + +If you build on all platforms, then the ESLint job will run by default, but if you selected a few platforms only in your [trysyntax](https://wiki.mozilla.org/Build:TryChooser), then you need to also add `eslint-gecko` as a target platform for ESLint to run. + +### Running ESLint in SublimeText + +SublimeText is a popular code editor and it supports ESLint via a couple of plugins. Here are some pointers to get started: + +* make sure you have [SublimeText 3](http://www.sublimetext.com/3), the linter plugin doesn't work with ST2, +* install [SublimeLinter 3](http://www.sublimelinter.com/en/latest/installation.html), this is a framework for linters that supports, among others, ESLint. Installing SublimeLinter via [Package Control](https://packagecontrol.io/installation) is the easiest way) +* with SublimeLinter installed, you can now [install the specific ESLint plugin](https://github.com/roadhump/SublimeLinter-eslint#linter-installation). The installation instructions provide details about how to install node, npm, eslint which are required). +* make sure to configure SublimeLinter with the `--no-ignore` option so that errors are also shown for source files that are ignored. To do this, open the SublimeLinter user configuration at: Preferences / Package Settings / SublimeLinter / Settings - User, and add `"args": "--no-ignore"` to the eslint linter object. + +You will also need to point SublimeLinter at the local eslint installation by setting the path to whatever `./mach eslint --setup` gives you when you run it (include a trailing slash but remove the eslint binary filename) e.g. + +NOTE: Your local eslint binary is at /some-project-path/tools/lint/eslint/node_modules/.bin/eslint + +``` + "paths": { + "linux": [], + "osx": [ + "/some-project-path/tools/lint/eslint/node_modules/.bin" + ], + "windows": [ + "C:\\some-project-path\\tools\\lint\\eslint\\node_modules\\.bin" + ] + }, +``` + +Once done, open the mozilla project in SublimeText and open any JS file in the `/devtools` directory. You can then trigger the linter via the contextual menu (right click on the file itself) or with a keyboard shortcut (ctrl+option+L on Mac). + +You should see errors and warnings in the gutter as shown in the screenshot below. You can also see all errors listed with ctrl+option+A, and individual errors by clicking in the gutter marker. + +![ESLint in SublimeText 3](./eslint-sublimetext3.png) + +### Running ESLint in Emacs + +* First, install the flycheck package (flymake doesn't support ESLint yet). You can get flycheck from the [marmalade](https://marmalade-repo.org/) or [melpa-stable](http://stable.melpa.org/#/) repositories. + +* Tell flycheck to disable jslint, and enable flycheck in your javascript mode. Some care is needed to find the eslint installed in the source tree. This snippet assumes the built-in javascript mode, but with minor changes (just the name of the hook) should work fine with js2-mode as well: +```lisp +(defun my-js-mode-hacks () + (setq-local mode-name "JS") + ;; Set this locally so that the head.js rule continues to work + ;; properly. In particular for a mochitest we want to preserve the + ;; "browser_" prefix. + (when (buffer-file-name) + (let ((base (file-name-nondirectory (buffer-file-name)))) + (when (string-match "^\\([a-z]+_\\)" base) + (setq-local flycheck-temp-prefix (match-string 1 base)))) + (let ((base-dir (locate-dominating-file (buffer-file-name) + ".eslintignore"))) + (when base-dir + (let ((eslint (expand-file-name + "tools/lint/eslint/node_modules/.bin/eslint" base-dir))) + (when (file-exists-p eslint) + (setq-local flycheck-javascript-eslint-executable eslint)))))) + (flycheck-mode 1)) +(require 'flycheck) +(setq-default flycheck-disabled-checkers + (append flycheck-disabled-checkers + '(javascript-jshint))) +(add-hook 'js-mode-hook #'my-js-mode-hacks) +``` + +* flycheck puts its bindings on `C-c !` by default, so use `C-c ! C-h` to see what is available. There are key bindings to list all the errors and to move through the errors, among other things. +* To make sure flycheck is finding eslint, open a .js file and run `M-x flycheck-verify-setup`. It should show the path to your eslint installation. + +### Running ESLint in Atom + +From the root of the project type: + +`./mach eslint --setup` + +Install the [linter-eslint](https://atom.io/packages/linter-eslint) package v.8.00 or above. Then go to the package settings and enable the following options: + +![linter-eslint settings in Atom](eslint-atom-settings.png) + +Once done, you should see errors and warnings as shown in the screenshot below. + +![ESLint in Atom](eslint-atom.png) + +### Running ESLint in ViM + +If you don't use Syntastic yet, the instructions here should get you started: https://wiki.mozilla.org/WebExtensions/Hacking#Vim + +Alternatively, if you do use Syntastic already, add this to your `.vimrc` to get ESLint working where the path contains `mozilla-central` (adjust the path to reflect the one in your computer): + +```vim + autocmd FileType javascript,html + \ if stridx(expand("%:p"), "/mozilla-central/") != -1 | + \ let b:syntastic_checkers = ['eslint'] | + \ let b:syntastic_eslint_exec = '/path/to/mozilla-central/tools/lint/eslint/node_modules/.bin/eslint' | + \ let b:syntastic_html_eslint_args = ['--plugin', 'html'] | + \ endif +``` + +You probably need to close and reopen ViM for the changes to take effect. Then, open any file and try to edit it to cause an error, then save it. If all goes well, you will get some distinctive arrows pointing to the error. Hovering with the mouse will produce a sort of tooltip with more information about the error. + +### Running ESLint in Visual Studio Code + +From the root of the project type: + +`./mach eslint --setup` + +Install the [dbaeumer.vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) package. Then go to the package settings and set the following option: + +`"eslint.nodePath": "tools/lint/eslint/node_modules/.bin"` + +Once done, you should see errors and warnings as shown in the screenshot below: + +![ESLint in VS Code](eslint-vscode.png) + +### Fixing ESLint Errors + +This should help you write eslint-clean code: + +* When moving or refactoring a piece of code, consider this as an opportunity to remove all ESlint errors from this piece of code. In fact, it may even be a good opportunity to remove all ESLint errors from the entire file. +* When doing ESLint-only changes, please do them in a separate patch from the actual functionality changes or bug fix. This helps make the review easier, and isolate the actual changes when looking at the source history. +* When cleaning an entire file or folder from ESLint errors, do not forget to remove the corresponding entry from the `.eslintignore` file. +* When writing new code, from scratch, please make it ESLint compliant from the start. This is a lot easier than having to revisit it later. +* ESLint also runs on `<script>` tags in HTML files, so if you create new HTML test files for mochitests for example, make sure that JavaScript code in those files is free of ESLint errors. +* Depending on how a dependency is loaded into a file, the symbols this dependency exports might not be considered as defined by ESLint. For instance, using `Cu.import("some.jsm")` doesn't explicitly say which symbols are now available in the scope of the file, and so using those symbols will be consider by ESLint as using undefined variables. When this happens, please avoid using the `/* globals ... */` ESLint comment (which tells it that these variables are defined). Instead, please use `/* import-globals-from relative/path/to/file.js */`. This way, you won't have a list of variables to maintain manually, the globals are going to be imported dynamically instead. +* In test files (xpcshell and mochitest), all globals from the corresponding `head.js` file are imported automatically, so you don't need to define them using a `/* globals ... */` comment or a `/* import-globals-from head.js */` comment. diff --git a/devtools/docs/contributor/contributing/filing-good-bugs.md b/devtools/docs/contributor/contributing/filing-good-bugs.md new file mode 100644 index 0000000000..9046b3fc10 --- /dev/null +++ b/devtools/docs/contributor/contributing/filing-good-bugs.md @@ -0,0 +1,11 @@ +# Filing good bugs + +Getting started working on a bug can be hard, specially if you lack context. + +This guide is meant to provide a list of steps to provide the necessary information to resolve a bug. + +* Use a descriptive title. Avoid jargon and abbreviations where possible, they make it hard for other people to find existing bugs, and to understand them. +* Explain the problem in depth and provide the steps to reproduce. Be as specific as possible, and include things such as operating system and version if reporting a bug. +* If you can, list files and lines of code that may need to be modified. Ideally provide a patch for getting started. +* If applicable, provide a test case or document that can be used to test the bug is solved. For example, if the bug title was "HTML inspector fails when inspecting a page with one million of nodes", you would provide an HTML document with one million of nodes, and we could use it to test the implementation, and make sure you're looking at the same thing we're looking at. You could use services like jsfiddle, codepen or jsbin to share your test cases. Other people use GitHub, or their own web server. +* If it's a bug that new contributors can work on, add the keyword `good-first-bug`. diff --git a/devtools/docs/contributor/contributing/find-bugs.md b/devtools/docs/contributor/contributing/find-bugs.md new file mode 100644 index 0000000000..16dbd322af --- /dev/null +++ b/devtools/docs/contributor/contributing/find-bugs.md @@ -0,0 +1,6 @@ +# Find bugs to work on + +* Choose something from [the list of existing bugs](https://codetribute.mozilla.org/projects/devtools). You can filter by tools (e.g. only `Console` bugs), and also by good bugs for beginners. +* Or if you would like to work on something that is not listed there, [file a bug in Bugzilla](https://bugzilla.mozilla.org/enter_bug.cgi?product=DevTools) (you'll need the Bugzilla account [you created earlier](../getting-started/bugzilla.md)) and ask for it to be assigned to you. Please also try to initiate a conversation in the bug first, to ensure that you don't work on something that will not be accepted (for example, if you think you found a bug, but the feature worked that way by design). + +<!-- TODO: mention finding potential work that is captured as a TODO or FIXME comments, but doesn't have an associated filed bug --> diff --git a/devtools/docs/contributor/contributing/fixing-bugs.md b/devtools/docs/contributor/contributing/fixing-bugs.md new file mode 100644 index 0000000000..9d20a3929b --- /dev/null +++ b/devtools/docs/contributor/contributing/fixing-bugs.md @@ -0,0 +1,177 @@ +# How to fix a bug + +## Make sure you understand what needs to be done + +If you're not quite sure of this, please add a comment requesting more information in the bug itself. It is absolutely fine to also talk to someone via other means (e.g. irc, email, in the office kitchen...), but it is good to come back to the bug and add the extra information, just in case you stop working on the bug at some point and someone else has to pick work where you left it. + +## Find out where are the files that need changing + +In an ideal world, the bug has this information from the start, but very often it doesn't. + +If you're not yet familiar with the codebase, the [files and directories overview](../files/README.md) might help you find where things are. + +If you know what you're looking for (e.g. a string that has a typo and needs correcting, or a function name that needs modifying), you can use a source code search engine: + +* [Searchfox](http://searchfox.org/mozilla-central/source) + +It is a good idea to [add smart keyword searches](https://support.mozilla.org/en-US/kb/how-search-from-address-bar) for DXR to search faster. + +You can also use your operating system's command line. For example, let's search for occurrences of `TODO` in the code base. + +Within your command line prompt, `cd` to the `devtools` directory: + +```bash +cd ~/mozilla-central/devtools # use your actual folder name! +grep -r 'TODO' . +``` + +This will list all the places in the DevTools code that contain the `TODO` string. If there are too many instances, you can combine the above with `less`, to scroll and paginate the output of `grep`: + +```bash +grep -r 'TODO' . | less +``` + +Press `q` to exit. + +If after all of this you still can't find your bearings, add a comment in the bug asking for more information. + +## How do you know that you have found what you were looking for? + +There are a few options to confirm that you found the right files: + +### If this is about changing a string... + +Edit the file(s), and change the string (e.g. fix a typo). Rebuild, and run again: + +```bash +./mach build +./mach run +``` +Then go to the panel that displays the string you wanted to change. + +Does the typo still occur? Or is the string being displayed the correct one now? + +### If this is about changing JavaScript code... + +If you think you found the right file to edit, add some logging statement on a place of the code which is likely to be executed (for example, on a class constructor): + +```javascript +// For front-end code +console.log('hello friends\n'); + +// Sometimes it's hard to spot your output. Emojis can help here really well. +console.log('👗👗👗', 'This is your logged output!'); +``` + +Or... + +```javascript +// For server code +dump('hello friends\n'); +``` + +TIP: Whether to use one or another depends on the type of bug you're working on, but if you've just started in DevTools, it's highly likely you'll take a front-end bug first. + +Then rebuild and run again: + +```bash +./mach build +./mach run +``` + +Go to the panel or initiate the action that is likely to result on the code being executed, and pay close attention to the output in your console. + +Can you see `hello friends`? Then you found the file that you were looking for. + +It's possible that you'll get a lot of other messages you don't care about now, but we can use `grep` to filter: + +```bash +./mach run | grep hello friends +``` + +This will only show lines that contain `hello friends`. If you get an empty output after trying to get the code to execute, maybe this isn't the right file, or maybe you didn't trigger the action. + +And that means it's time to ask for more information in the bug or from your colleagues. Tell them what you tried, so they don't have to figure that out themselves (saves everyone some time!). + +### If this is about changing CSS code... + +If you think you have found the right file and the right CSS selector, you could try to edit the file to insert some outrageously colourful rule (for example, a really thick bright blue border). + +```css +border: 4px solid blue; +``` + +Check if the changes show up by rebuilding your local changes. + +```bash +./mach build faster +./mach run +``` + +## NEXT: do whatever needs doing + +This will always depend on the specific bug you're working on, so it's hard to provide guidance here. + +The key aspect here is that if you have questions, you should not hesitate to ask. Ask your mentor, your manager, or [get in touch](https://firefox-dev.tools/#getting-in-touch). **You should just not get stuck**. + +Some people find it difficult to recognise or even admit they're in such a situation, so some ways to describe 'stuck' could be: + +* you've tried everything you could think of, nothing works, and do not know what else to do. +* you have various ideas for things that can be done, and do not know which one to go for. +* you have not learned anything new about the problem in the last one or two days. +* you're starting to think about abandoning this bug and doing something else instead. +* you don't know what to do, but are afraid of asking for help. + +If you think *any* of the above describes you, ask for help! + +Another tip you can use if you're afraid that you're wasting people's time is to timebox. For example, give yourself 2 hours to investigate. If you can't figure anything after that time has elapsed, stop and ask for help. It might be that you needed a key piece of information that was missing in the bug description, or you misunderstood something, or maybe even that you found a bug and that's why things didn't work even if they should! This is why it's important to call for help sooner rather than later. + +### Useful references + +#### Coding standards + +If it's your first time contributing, the documentation on [coding standards](./coding-standards.md) might have answers to questions such as what style of code to use, how to name new files (if you have to add any), tools we use to run automated checks, etc. + +#### Specialised guides + +We also have a few guides explaining how to work on specific types of problems, for example: [investigating performance issues](./performance.md), or [writing efficient React code](./react-performance-tips.md). Please have a look at the sidebar or use the search box on top of the sidebar to see if there's something written about the type of bug you're working on. + +If not, maybe you'll be able to contribute with one by the time you fix your bug! + +#### MDN + +[MDN Web Docs](http://developer.mozilla.org/) (also known as *MDN*) has a lot of information about HTML, CSS, JS, DOM, Web APIs, Gecko-specific APIs, and more. + +## Run tests + +We have several types of automated tests to help us when developing. + +Some, like the linting tests, address coding style; others address functionality, such as unit and integration tests. This page has more [details on types of tests and how to run them](../tests/writing-tests.md). + +You might want to run the unit and integration types of tests quite frequently, to confirm you're not breaking anything else. Depending on what you're doing, it might be even possible to run just one test file which addresses the specific change you're implementing: + +```bash +./mach test devtools/path/to/test.js +``` + +Sometimes you might want to run a number of tests which are related to the bug you're fixing: + +```bash +./mach test devtools/path/to/test-thing-*.js +``` + +At the beginning, it is entirely possible that you have no idea of where the tests are for the thing you're working on. Please ask for help! You will eventually learn your way around. + +It is good etiquette to ensure the tests pass locally before asking for a code review. This includes linting tests. To run them, please [configure your system to run ESlint](./eslint.md), and then you can execute: + +```bash +./mach eslint devtools/path/to/files/you/changed +``` + +Our tool for code review will run the linter automatically as well, but if you run this locally you'll get instant feedback, and avoid having to send an updated commit again. + +## Time for a review + +When you think you have fixed the bug, first let's celebrate a bit! Yay! Well done 😀 + +And now it's time to [get your code reviewed](./code-reviews.md). diff --git a/devtools/docs/contributor/contributing/javascript.md b/devtools/docs/contributor/contributing/javascript.md new file mode 100644 index 0000000000..25d1f1c251 --- /dev/null +++ b/devtools/docs/contributor/contributing/javascript.md @@ -0,0 +1,87 @@ +# JavaScript coding standards + +Probably the best piece of advice is **to be consistent with the rest of the code in the file**. + +We use [ESLint](http://eslint.org/) to analyse JavaScript files automatically, either from within a code editor or from the command line. Here's [our guide to install and configure it](./eslint.md). + +For quick reference, here are some of the main code style rules: + +* file references to browser globals such as `window` and `document` need `/* eslint-env browser */` at the top of the file, +* lines should be 90 characters maximum, +* indent with 2 spaces (no tabs!), +* `camelCasePlease`, +* don't open braces on the next line, +* don't name function expressions: `let o = { doSomething: function doSomething() {} };`, +* use a space before opening paren for anonymous functions, but don't use one for named functions: + * anonymous functions: `function () {}` + * named functions: `function foo() {}` + * anonymous generators: `function* () {}` + * named generators: `function* foo() {}` +* aim for short functions, 24 lines max (ESLint has a rule that checks for function complexity too), +* `aArguments aAre the aDevil` (don't use them please), +* `"use strict";` globally per module, +* `semicolons; // use them`, +* no comma-first, +* consider using async/await for nice-looking asynchronous code instead of formatting endless `.then` chains, +* use ES6 syntax: + * `function setBreakpoint({url, line, column}) { ... }`, + * `(...args) => { }` rest args are awesome, no need for `arguments`, + * `for..of` loops, +* don't use non-standard SpiderMonkey-only syntax: + * no `for each` loops, + * no `function () implicitReturnVal`, + * getters / setters require { }, +* only import specific, explicitly-declared symbols into your namespace: + * `const { foo, bar } = require("foo/bar");`, + * `const { foo, bar } = ChromeUtils.import("...");`, +* use Maps, Sets, WeakMaps when possible, +* use [template strings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) whenever possible to avoid concatenation, allow multi-line strings, and interpolation. + + +## Comments + +Commenting code is important, but bad comments can hurt too, so it's important to have a few rules in mind when commenting: + +* If the code can be rewritten to be made more readable, then that should be preferred over writing an explanation as a comment. +* Instead of writing a comment to explain the meaning of a poorly chosen variable name, then rename that variable. +* Avoid long separator comments like `// ****************** another section below **********`. They are often a sign that you should split a file in multiple files. +* Line comments go above the code they are commenting, not at the end of the line. +* Sentences in comments start with a capital letter and end with a period. +* Watch out for typos. +* Obsolete copy/pasted code hurts, make sure you update comments inside copy/pasted code blocks. +* A global comment at the very top of a file explaining what the file is about and the major types/classes/functions it contains is a good idea for quickly browsing code. +* If you are forced to employ some kind of hack in your code, and there's no way around it, then add a comment that explains the hack and why it is needed. The reviewer is going to ask for one anyway. +* Bullet points in comments should use stars aligned with the first comment to format each point +```javascript +// headline comment +// * bullet point 1 +// * bullet point 2 +``` + +## Asynchronous code + +A lot of code in DevTools is asynchronous, because a lot of it relies on connecting to the DevTools server and getting information from there in an asynchronous fashion. + +It's easy to make mistakes with asynchronous code, so here are a few guidelines that should help: + +* Prefer [promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) over callbacks. +* Use the `new Promise(() => {})` syntax. +* Don't forget to catch rejections by defining a rejection handler: `promise.then(() => console.log("resolved"), () => console.log("rejected"));` or `promise.catch(() => console.log("rejected"));`. +* Make use of [async and await](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function). + +## React & Redux + +There are React-specific code style rules in the .eslintrc file. + +### Components + +* Default to creating components as [stateless function components](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions). +* If you need local state or lifecycle methods, use `React.createClass` instead of functions. +* Use React.DOM to create native elements. Assign it to a variable named `dom`, and use it like `dom.div({}, dom.span({}))`. You may also destructure specific elements directly: `const { div, ul } = React.DOM`. + +### PropTypes + +* Use [PropTypes](https://facebook.github.io/react/docs/reusable-components.html#prop-validation) to define the expected properties of your component. Each directly accessed property (or child of a property) should have a corresponding PropType. +* Use `isRequired` for any required properties. +* Place the propTypes definition at the top of the component. If using a stateless function component, place it above the declaration of the function. +* Where the children property is used, consider [validating the children](http://www.mattzabriskie.com/blog/react-validating-children). diff --git a/devtools/docs/contributor/contributing/landing-code.md b/devtools/docs/contributor/contributing/landing-code.md new file mode 100644 index 0000000000..7cbb8764ef --- /dev/null +++ b/devtools/docs/contributor/contributing/landing-code.md @@ -0,0 +1,18 @@ +# Landing code (i.e. getting code into Mozilla's repository) + +Code changes (patches) in Mozilla are not 'merged' in a sequential way, as it's the fashion in other popular projects. Here, the patches will be *applied* on top of the latest code, and will stay there if + +1. the patch applies cleanly, without conflicts +2. the patch doesn't cause 'bustage' (i.e. breaks the build) + +Therefore, it's good to try and do smaller changes rather than bigger, specially if you're modifying files that many other people are working on simultaneously, to avoid conflicts and your patch being rejected. Otherwise you might need to rebase from the latest changes, try to write your changes on top of it, and submit this new diff. + +Leaving potential conflicts aside, a patch can make its way into the repository in two ways: + +## From Phabricator + +Once a review has been approved, someone with enough privileges can request the code be merged, using the [Lando](https://moz-conduit.readthedocs.io/en/latest/lando-user.html) interface. These 'privileges' are "commit level access 3". You get these once you have successfully contributed with a number of patches. See [levelling up](./levelling-up.md) for more details. + +If you don't have the privileges, you can also ask your mentor to land the code. In fact, they might even initiate that for you once the code review is approved. + +To request the landing, ask your reviewer to land the patch. diff --git a/devtools/docs/contributor/contributing/levelling-up.md b/devtools/docs/contributor/contributing/levelling-up.md new file mode 100644 index 0000000000..fc0c8bab01 --- /dev/null +++ b/devtools/docs/contributor/contributing/levelling-up.md @@ -0,0 +1,23 @@ +# Levelling up + +Once you've fixed a few bugs, it's quite likely that we'll ask you to complete one or more of the following steps. They will grant you access to useful Mozilla infrastructure for testing or landing code automatically, so you can be more autonomous and able to contribute with less supervision. + +If you're curious, the [Mozilla Commit Access Policy](https://www.mozilla.org/en-US/about/governance/policies/commit/access-policy/) page explains what does each level mean. + +## Request commit access level 1 + +Once this is granted, you can use [the `Try` server](https://wiki.mozilla.org/ReleaseEngineering/TryServer) for running automated tests of your code. + +To request this, [follow the instructions here](https://www.mozilla.org/en-US/about/governance/policies/commit/) to file a bug with the title "Commit Access (Level 1) for ${your name}", and continue with steps such as adding the public SSH key, etc (it's not enough with just filing the bug!). + +The person that asked you to request this can also vouch for you. + +## Request commit access level 3 + +After you landed a few bugs, that are more advanced than the `good-first-bugs` and you feel confident about your contributions as well as the process, you can request level 3 access. Once this is granted, you will get access to Lando, the autoland feature. + +To request this, [follow the instructions here](https://www.mozilla.org/en-US/about/governance/policies/commit/) to file a bug with the title "Commit Access (Level 3) for ${your name}". + +The person that asked you to request this can also vouch for you, and at this point you might already know a few other people who will vouch for you. You need two vouchers. + +If you reach this level, well done! That's amazing, and we're glad to have you on board 😄 diff --git a/devtools/docs/contributor/contributing/making-prs.md b/devtools/docs/contributor/contributing/making-prs.md new file mode 100644 index 0000000000..fdaf827d15 --- /dev/null +++ b/devtools/docs/contributor/contributing/making-prs.md @@ -0,0 +1,82 @@ +# Sending your code for review (also known as "sending patches") + +First, commit your changes. For example: + +```bash +hg add /path/to/file/changed +hg commit -m "Bug 1234567 - [devtools] Implement feature XYZ. r=name,name2!" +``` + + The commit message explained in detail: + - `Bug 1234567` - The number of the bug in bugzilla. + - `- [devtools] Implement feature XYZ.` - The commit message, with a "devtools" prefix to quickly identify DevTools changesets. + - `r=name` - The short form to request a review. Enter the name you found using the + instructions in the [previous step](./code-reviews-find-reviewer.md). + - `,name2!` - You can have more than one reviewer. The `!` makes the review a *blocking* review (Patch can not land without accepted review). + +Then create a revision in Phabricator using `moz-phab`: + +```bash +moz-phab submit +``` + +A revision will be created including that information and the difference in code between your changes and the point in the repository where you based your work (this difference is sometimes called "a patch", as it's what you'd need to apply on the repository to get to the final state). + +If you click on the provided URL for the revision, it'll bring you to Phabricator's interface, which the reviewer will visit as well in order to review the code. They will look at your changes and decide if they need any additional work, based on whether the changes do fix what the bug describes or not. To get a better idea of the types of things they will look at and verify, read the [code reviews checklist](./code-reviews-checklist.md). + +For more information on using moz-phab, you can run: + +```bash +moz-phab -h +``` + +or to get information on a specific command (here `submit`): + +```bash +moz-phab submit -h +``` + +The reviewer might suggest you do additional changes. For example, they might recommend using a helper that already exists (but you were not aware of), or they might recommend renaming things to make things clearer. Or they might recommend you do *less* things (e.g. if you changed other things that are out of scope for the bug). Or they might simply ask questions if things aren't clear. You can also ask questions if the comments are unclear or if you're unsure about parts of the code you're interacting with. Something that looks very obvious to one person might confuse others. + +Hence, you might need to go back to the code and do some edits to address the issues and recommendations. Once you have done this, you must update the existing commit: + +```bash +hg commit --amend +``` + +And submit the change again: + +```bash +moz-phab submit +``` + +You might have to go through this cycle of submitting changes and getting it reviewed several times, depending on the complexity of the bug. + +Once your code fixes the bug, and there are no more blocking issues, the reviewer will approve the changes, and the code can be landed in the repository now. + + +# Squashing commits + +Sometimes you may be asked to squash your commits. Squashing means merging multiple commits into one in case you created multiple commits while working on a bug. Squashing bugs is easy! + +We will use the histedit extension for squashing commits in Mercurial. You can check if this extension is enabled in your Mercurial installation following these steps: + +* Open `.hgrc` (Linux/OSX) or `Mercurial.ini` (Windows) –this is the default configuration file of Mercurial– located in your home directory, using your favourite editor. +* Then add `histedit= ` under the `[extensions]` list present in file, if not present already. + +Then, run the following command: + +`hg histedit` + +You will see something like this on your terminal: + +``` +pick 3bd22d1cc59a 0 "First-Commit-Message" +pick 81c4d40e57d3 1 "Second-Commit-Message" +``` + +These lines represent your commits. Suppose we want to merge `81c4d40e57d3` to `3bd22d1cc59a`. Then replace **pick** in front of `81c4d40e57d3` with **fold** (or simply 'f'). Save the changes. + +You will see that `81c4d40e57d3` has been combined with `3bd22d1cc59a`. You can verify this using the `hg log` command. + +You can fold as many commits you want, and they will be combined with the first commit above them which does not use fold. diff --git a/devtools/docs/contributor/contributing/performance.md b/devtools/docs/contributor/contributing/performance.md new file mode 100644 index 0000000000..4c6728d326 --- /dev/null +++ b/devtools/docs/contributor/contributing/performance.md @@ -0,0 +1,152 @@ +# Writing efficient code + +When debugging a page, tools get to slow down the website because of the added instrumentation. +While working on Developer Tools we should strive to be the less impactful. +First, because it is painful to work with laggy UI, but also because some tools record timings. +For example, the network monitor records HTTP request timings. +If the tools are slowing down Firefox significantly, it will make these measurements be wrong. + +To be efficient while working on performance, you should always focus on one precise user scenario. +It could be: +* a bug report where someone reports a precise interaction being slow, +* or you could be trying to improve overall tools performance by looking at the most common usages. +The important point here is to have some steps to reproduce, that you can redo manually in order to record a profile. +And also, it is even better if you can replay via a test script. Test script that you can save as a new performance test. + +## Don't guess — profile. + +The very first thing to do is to record a profile while reproducing the scenario. + +Here's the Firefox documentation for [how to install the profiler and record a profile](https://developer.mozilla.org/docs/Mozilla/Performance/Reporting_a_Performance_Problem) and also [how to interpret the profiles](https://developer.mozilla.org/docs/Mozilla/Performance/Profiling_with_the_Built-in_Profiler#Understanding_Profiles) + +There are some peculiarities about DevTools architecture that are worth knowing about when looking at a profile: + +### Tweak profiler default settings + +The default buffer size (9MB) is too small. If you don't increase it, you may easily miss data and only see last couple of seconds of your recording. +To increase the buffer size, click on the profiler add-on icon, in the Firefox toolbar, and set it to 360MB, like this: + + <img src="performance/profiler-buffer-size.png" alt="Profiler buffer size" style="width: 300px" /> + +The other setting worth mentioning for DevTools debugging is the `Interval` +The profiler records only samples, based on this `Interval`. +If you want to see more fine-grained stack traces, you may reduce this interval to 0.1ms, +but do that only if you really need it, as it will make Firefox much even slower when recording, +and the measured times will be even slower. + +### The DevTools UI runs on the parent process + +When you are debugging tool front-ends (e.g. panels), always ensure you select the `Main Thread` line. +It should have a light blue background like this: + + <img src="performance/profiler-main-thread.png" alt="Select main process" style="width: 300px" /> + +Otherwise, the vast majority of DevTools backend (DevToolsServer, actors, ...) lives in content processes. +So if you are debugging them, you should select one of the `Content` lines. + +### Most of the DevTools codebase is in Javascript + +In the call tree, it is easier to filter by `JS`, via this menu list: + <img src="performance/profiler-filter-js.png" alt="JS Filtering" style="width: 200px" /> + +But note that you may have to switch back to `Combined` in order to understand why some particular Javascript method is slow. + +### Handy filter strings for DevTools: + + * `require` + Helps highlighting the cost of module loading + ![modules](performance/profiler-filter-require.png) + * DevTools uses two kind of URLs: + * `chrome://devtools/` for all panel documents. Filter with this to see the cost of all panel documents: + ![panels documents](performance/profiler-chrome-url.png) + * `resource://devtools/` for all javascript modules. Filter with this to see the cost of all modules: + ![modules](performance/profiler-resource-url.png) + +### Record durations manually + +Sometimes it is handy to focus on a very precise piece of code and record its time manually. +For example when you identified one slow running method and think you can speed it up. +It saves your from having to: record the profile, wait for the profiler to display and search for the precise method durations. + +#### Print durations in your Terminal and in the Browser Console + +You can use the [`Performance`](https://developer.mozilla.org/docs/Web/API/Performance) API, like this: +``` +let start = window.performance.now(); + +// Run the code you want to measure + +// Once it is done, do: +console.log("my function took", window.performance.now() - start, "ms"); +``` + +#### Use markers + +The Performance API also allows recording markers, like this: +``` +window.performance.mark("my-function-start"); + +// Run the code you want to measure + +// Once it is done, do: +window.performance.measure("my-function", "my-function-start"); +``` + +This marker will appear in the `Marker Chart` section in [profiler.firefox.com](https://profiler.firefox.com), in the `UserTiming` lines: + ![custom markers](performance/profiler-custom-markers.png) + +You can double click on it to make [profiler.firefox.com](https://profiler.firefox.com) display the record during this precise moment in time, +and the call tree will only display what was executed during this measurement. + +### Prototype quickly + +Sometimes the best way to find what is slow is to comment blocks of code out +and uncomment them one by one until you identify the culprit. And then focus on it. + +There are few things worse than spending a long time refactoring the piece of code that was not slow to begin with! + +## Assess your improvement. + +Once you have a patch that you think improves the performance, you have to assess whether it actually improves it. + +### Record another profile + +Compare the two profiles, without and with your patch. +Then see if the call tree reports a significant difference: +* A function call completely disappears in the new profile, with your fix. + For example you were loading a big module, and you got a frame for `require("my/big/module")` call, and no longer see it. +* The same function call takes xxx ms less with your patch. + +This [lazy loading of modules in netmonitor](https://bugzilla.mozilla.org/show_bug.cgi?id=1420289) is a good example. +Without this patch, App.js was loading in 91ms and was loading MonitorPanel.js and StatisticsPanel.js as dependencies: + ![netmonitor without patch](performance/profiler-netmonitor-open.png) + +With the patch, App.js loads in 47ms and only loads MonitorPanel.js: + ![netmonitor with patch](performance/profiler-netmon-open-fixed.png) + +It highlights that: + * we no longer load StatisticsPanel, + * App is faster to load. + +### Run performance tests + +See if any subtest reports a improvement. Ensure that the improvement makes any sense. +For example, if the test is 50% faster, maybe you broke the performance test. +This might happen if the test no longer waits for all the operations to finish executing before completing. + +To push your current patch to try, execute: +```bash +./mach try fuzzy --query "'linux 'damp" --rebuild 5 +``` +It will print in your Terminal a link to perfherder like this one: +[https://treeherder.mozilla.org/perf.html#/comparechooser?newProject=try&newRevision=9bef6cb13c43bbce21d40ffaea595e082a4c28db](https://treeherder.mozilla.org/perf.html#/comparechooser?newProject=try&newRevision=9bef6cb13c43bbce21d40ffaea595e082a4c28db) +Running performance tests takes time, so you should open it 30 minutes up to 2 hours later to see your results. +See [DAMP Performance tests](../tests/performance-tests-damp.md) for more information about PerfHerder/try. + +Let's look at how to interpret an actual real-life [set of perfherder results](https://treeherder.mozilla.org/perf.html#/comparesubtest?originalProject=mozilla-central&newProject=try&newRevision=9bef6cb13c43bbce21d40ffaea595e082a4c28db&originalSignature=edaec66500db21d37602c99daa61ac983f21a6ac&newSignature=edaec66500db21d37602c99daa61ac983f21a6ac&showOnlyImportant=1&framework=1&selectedTimeRange=172800): + +![perfherder results](performance/perfherder-results.png) + +These results are related to [lazy loading of modules in netmonitor](https://bugzilla.mozilla.org/show_bug.cgi?id=1420289). +It is interesting to see that this patch is a trade-off. It makes netmonitor opening significantly faster, by preventing loading many modules during its opening. +But it makes the page reload a bit slower as some modules that used to be loaded during netmonitor open, now have to be loaded during page reload. diff --git a/devtools/docs/contributor/contributing/performance/perfherder-results.png b/devtools/docs/contributor/contributing/performance/perfherder-results.png Binary files differnew file mode 100644 index 0000000000..8f35a47c8c --- /dev/null +++ b/devtools/docs/contributor/contributing/performance/perfherder-results.png diff --git a/devtools/docs/contributor/contributing/performance/profiler-buffer-size.png b/devtools/docs/contributor/contributing/performance/profiler-buffer-size.png Binary files differnew file mode 100644 index 0000000000..626fcc5c3f --- /dev/null +++ b/devtools/docs/contributor/contributing/performance/profiler-buffer-size.png diff --git a/devtools/docs/contributor/contributing/performance/profiler-chrome-url.png b/devtools/docs/contributor/contributing/performance/profiler-chrome-url.png Binary files differnew file mode 100644 index 0000000000..fa6209f2fd --- /dev/null +++ b/devtools/docs/contributor/contributing/performance/profiler-chrome-url.png diff --git a/devtools/docs/contributor/contributing/performance/profiler-custom-markers.png b/devtools/docs/contributor/contributing/performance/profiler-custom-markers.png Binary files differnew file mode 100644 index 0000000000..be40060a8d --- /dev/null +++ b/devtools/docs/contributor/contributing/performance/profiler-custom-markers.png diff --git a/devtools/docs/contributor/contributing/performance/profiler-filter-js.png b/devtools/docs/contributor/contributing/performance/profiler-filter-js.png Binary files differnew file mode 100644 index 0000000000..e59242aea8 --- /dev/null +++ b/devtools/docs/contributor/contributing/performance/profiler-filter-js.png diff --git a/devtools/docs/contributor/contributing/performance/profiler-filter-require.png b/devtools/docs/contributor/contributing/performance/profiler-filter-require.png Binary files differnew file mode 100644 index 0000000000..44c89b0907 --- /dev/null +++ b/devtools/docs/contributor/contributing/performance/profiler-filter-require.png diff --git a/devtools/docs/contributor/contributing/performance/profiler-main-thread.png b/devtools/docs/contributor/contributing/performance/profiler-main-thread.png Binary files differnew file mode 100644 index 0000000000..f7e8f5541f --- /dev/null +++ b/devtools/docs/contributor/contributing/performance/profiler-main-thread.png diff --git a/devtools/docs/contributor/contributing/performance/profiler-netmon-open-fixed.png b/devtools/docs/contributor/contributing/performance/profiler-netmon-open-fixed.png Binary files differnew file mode 100644 index 0000000000..007a923713 --- /dev/null +++ b/devtools/docs/contributor/contributing/performance/profiler-netmon-open-fixed.png diff --git a/devtools/docs/contributor/contributing/performance/profiler-netmonitor-open.png b/devtools/docs/contributor/contributing/performance/profiler-netmonitor-open.png Binary files differnew file mode 100644 index 0000000000..31c78845f5 --- /dev/null +++ b/devtools/docs/contributor/contributing/performance/profiler-netmonitor-open.png diff --git a/devtools/docs/contributor/contributing/performance/profiler-resource-url.png b/devtools/docs/contributor/contributing/performance/profiler-resource-url.png Binary files differnew file mode 100644 index 0000000000..d4631f5317 --- /dev/null +++ b/devtools/docs/contributor/contributing/performance/profiler-resource-url.png diff --git a/devtools/docs/contributor/contributing/react-performance-tips.md b/devtools/docs/contributor/contributing/react-performance-tips.md new file mode 100644 index 0000000000..2bd55d61ad --- /dev/null +++ b/devtools/docs/contributor/contributing/react-performance-tips.md @@ -0,0 +1,527 @@ +# Writing efficient React code + +In this article we'll discuss about the various component types we can use, as +well as discuss some tips to make your React application faster. + +## TL;DR tips + +* Prefer props and state immutability and use `PureComponent` components as a default +* As a convention, the object reference should change **if and only if** the inner data + changes. + * Be careful to never use new instance of functions as props to a Component (it's fine to use + them as props to a DOM element). + * Be careful to not update a reference if the inner data doesn't change. +* [Always measure before optimizing](./performance.md) to have a real impact on + performance. And always measure _after_ optimizing too, to prove your change + had a real impact. + +## How React renders normal components + +### What's a normal component? +As a start let's discuss about how React renders normal plain components, that +don't use `shouldComponentUpdate`. What we call plain components here are either: +* classes that extend [`Component`](https://reactjs.org/docs/react-component.html) + ```javascript + class Application extends React.Component { + render() { + return <div>{this.props.content}</div>; + } + } + ``` +* normal functions that take some `props` as parameter and return some JSX. We + call these functions either Stateless Components or Functional Components. + This is important to understand that these Stateless Components are _not_ + especially optimized in React. + ```javascript + function Application(props) { + return <div>{props.content}</div>; + } + ``` + These functions are equivalent to classes extending `Component`. In + the rest of the article we'll especially focus on the latter. Unless otherwise + stated everything about classes extending `Component` is also true for + Stateless/Functional Components. + +#### Notes on the use of JSX +Because we don't use a build step in mozilla-central yet, some of our +tools don't use JSX and use [factories](https://reactjs.org/docs/react-api.html#createfactory) +instead: +```javascript +class Application extends React.Component { + render() { + return dom.div(null, this.props.content); + } +} +``` + +We'll use JSX in this documentation for more clarity but this is strictly +equivalent. You can read more on [React documentation](https://reactjs.org/docs/react-without-jsx.html). + +### The first render +There's only one way to start a React application and trigger a first render: +calling `ReactDOM.render`: + +```jsx +ReactDOM.render( + <Application content='Hello World!'/>, + document.getElementById('root') +); +``` + +React will call that component's `render` method, and then recursively call +every child's `render` method, generating a rendering tree and then a virtual +DOM tree. It will then render actual DOM elements to the specified container. + +### Subsequent rerenders + +There are several ways to trigger a rerender: +1. We call `ReactDOM.render` again with the same component. + ```javascript + ReactDOM.render( + <Application content='Good Bye, Cruel World!'/>, + document.getElementById('root') + ); + ``` +2. One component's state changes, through the use of [`setState`](https://reactjs.org/docs/react-component.html#setstate). + If the application is using Redux, this is how Redux-connected components + trigger updates too. +3. One component's props change. But note that this can't happen by itself, this + is always a consequence of the case 1 or 2 in one of its parents. So we'll + ignore this case for this chapter. + +When one of these happens, just like the initial render, React will call that +component's `render` method, and then recursively call every child's `render` +method, but this time possibly with changed props compared to the previous render. + +These recursive calls produce a new rendering tree. That's where React uses an +algorithm called _virtual diffing_ or +[_reconciliation_](https://reactjs.org/docs/reconciliation.html) to find the +minimal set of updates to apply to the DOM. This is good because the less +updates to the DOM the less work the browser has to do to reflow and repaint the +application. + +### Main sources of performance issues + +From this explanation we can gather that the main performance issues can +come from: +1. triggering the render process **too frequently**, +2. **expensive** render methods, +3. the reconciliation algorithm itself. The algorithm is O(n) according to React + authors, which means the processing duration increases linearly with **the number + of elements in the tree** we compare. So a larger tree means a longer time to + process. + +Let's dive more into each one of these issues. + +#### Do not render too often + +A rerender will happen after calling `setState` to change the +local state. + +Everything that's in the state should be used in `render`. +Anything in the state that's not used in `render` shouldn't be in the state, but +rather in an instance variable. This way you won't trigger an update if you +change some internal state that you don't want to reflect in the UI. + +If you call `setState` from an event handler you may call it too often. +This is usually not a problem because React is smart enough to merge close +setState calls and trigger a rerender only once per frame. Yet if your `render` +is expensive (see below as well) this could lead to problems and you may want to +use `setTimeout` or other similar techniques to throttle the renders. + +#### Keep `render` methods as lean as possible + +When rendering a list, it's very common that we'll map this list to a list of +components. This can be costly and we might want to cut this list in several +chunks of items or to +[virtualize this list](https://reactjs.org/docs/optimizing-performance.html#virtualize-long-lists). +Although this is not always possible or easy. + +Do not do heavy computations in your `render` methods. Rather do them before +setting the state, and set the state to the result of these computations. +Ideally `render` should be a direct mirror of the component's props and state. + +Note that this rule also applies to the other methods called as part of the +rendering process: `componentWillUpdate` and `componentDidUpdate`. In +`componentDidUpdate` especially avoid synchronous reflows by getting DOM +measurements, and do not call `setState` as this would trigger yet another +update. + +#### Help the reconciliation algorithm be efficient + +The smaller the tree is, the faster the algorithm is. So it's +useful to limit the changes to a subtree of the full tree. Note that the use of +`shouldComponentUpdate` or `PureComponent` alleviates this issue by cutting off +entire branches from the rendering tree, [we discuss this in more details +below](#shouldcomponentupdate-and-purecomponent-avoiding-renders-altogether). + +Try to change the state as close as possible to where your UI +should change (close in the components tree). + +Do not forget to [set `key` attributes when rendering a list of +things](https://reactjs.org/docs/lists-and-keys.html), which shouldn't be the +array's indices but something that identifies the item in a predictable, unique +and stable way. This helps the algorithm +a lot by skipping parts that likely haven't changed. + +### More documentation + +The React documentation has [a very well documented page](https://reactjs.org/docs/implementation-notes.html#mounting-as-a-recursive-process) +explaining the whole render and rerender process. + +## `shouldComponentUpdate` and `PureComponent`: avoiding renders altogether + +React has an optimized algorithm to apply changes. But the fastest algorithm is +an algorithm that isn't executed at all. + +[React's own documentation about performance](https://reactjs.org/docs/optimizing-performance.html#shouldcomponentupdate-in-action) +is quite complete on this subject. + +### Avoiding rerenders with `shouldComponentUpdate` + +As the first step of a rerender process, React calls your component's +[`shouldComponentUpdate`](https://reactjs.org/docs/react-component.html#shouldcomponentupdate) +method with 2 parameters: the new props, and the new +state. If this method returns false, then React will skip the render process for this +component, **and its whole subtree**. + +```jsx +class ComplexPanel extends React.Component { + // Note: this syntax, new but supported by Babel, automatically binds the + // method with the object instance. + onClick = () => { + this.setState({ detailsOpen: true }); + } + + // Return false to avoid a render + shouldComponentUpdate(nextProps, nextState) { + // Note: this works only if `summary` and `content` are primitive data + // (eg: string, number) or immutable data + // (keep reading to know more about this) + return nextProps.summary !== this.props.summary + || nextProps.content !== this.props.content + || nextState.detailsOpen !== this.state.detailsOpen; + } + + render() { + return ( + <div> + <ComplexSummary summary={this.props.summary} onClick={this.onClick}/> + {this.state.detailsOpen + ? <ComplexContent content={this.props.content} /> + : null} + </div> + ); + } +} +``` + +__This is a very efficient way to improve your application speed__, because this +avoids everything: both calling render methods for this component _and_ the +whole subtree, and the reconciliation phase for this subtree. + +Note that just like the `render` method, `shouldComponentUpdate` is called once +per render cycle, so it needs to be very lean and return as fast as possible. So +it should execute some cheap comparisons only. + +### `PureComponent` and immutability + +A very common implementation of `shouldComponentUpdate` is provided by React's +[`PureComponent`](https://reactjs.org/docs/react-api.html#reactpurecomponent): +it will shallowly check the new props and states for reference equality. + +```jsx +class ComplexPanel extends React.PureComponent { + // Note: this syntax, new but supported by Babel, automatically binds the + // method with the object instance. + onClick = () => { + // Running this repeatidly won't render more than once. + this.setState({ detailsOpen: true }); + } + + render() { + return ( + <div> + <ComplexSummary summary={this.props.summary} onClick={this.onClick}/> + {this.state.detailsOpen + ? <ComplexContent content={this.props.content} /> + : null} + </div> + ); + } +} +``` + +This has a very important consequence: for non-primitive props and states, that is +objects and arrays that can be mutated without changing the reference itself, +PureComponent's inherited `shouldComponentUpdate` will yield wrong results and will +skip renders where it shouldn't. + +So you're left with one of these two options: +* either implement your own `shouldComponentUpdate` in a `Component` +* or (__preferred__) decide to make all your data structure immutable. + +The latter is recommended because: +* It's much simpler to think about. +* It's much faster to check for equality in `shouldComponentUpdate` and in other + places (like Redux' selectors). + +Note you could technically implement your own `shouldComponentUpdate` in a +`PureComponent` but this is quite useless because `PureComponent` is nothing +more than `Component` with a default implementation for `shouldComponentUpdate`. + +### About immutability +#### What it doesn't mean +It doesn't mean you need to enforce the immutability using a library like +[Immutable](https://github.com/facebook/immutable-js). + +#### What it means +It means that once a structure exists, you don't mutate it. + +**Every time some data changes, the object reference must change as well**. This +means a new object or a new array needs to be created. This gives the nice +reverse guarantee: if the object reference has changed, the data has changed. + +It's good to go one step further to get a **strict equivalence**: if the data +doesn't change, the object reference mustn't change. This isn't necessary for +your app to work, but this is a lot better for performance as this avoids +spurious rerenders. + +Keep reading to learn how to proceed. + +#### Keep your state objects simple + +Updating your immutable state objects can be difficult if the objects used are +complex. That's why it's a good idea to keep the objects simple, especially keep +them not nested, so that you don't need to use a library like +[immutability-helper](https://github.com/kolodny/immutability-helper), +[updeep](https://github.com/substantial/updeep), or even +[Immutable](https://github.com/facebook/immutable-js). Be especially careful +with Immutable as it's easy to create performance problems by misusing +its API. + +If you're using Redux ([see below as well](#a-few-words-about-redux)) this +advice applies to your individual reducers as well, even if Redux tools make +it easy to have a nested/combined state. + +#### How to update an object + +Updating an object is quite easy. + +You must not change/add/delete inner properties directly: + +```javascript +// Note that in the following examples we use the callback version +// of `setState` everywhere, because we build the new state from +// the current state. + +// Please don't do this as this will likely induce bugs. +this.setState(state => { + state.stateObject.details = details; + return state; +}); + +// This is wrong too: `stateObject` is still mutated. +this.setState(({ stateObject }) => { + stateObject.details = details; + return { stateObject }; +}); +``` + +Instead **you must create a new object** for this property. In this example +we'll use the object spread operator, already implemented in Firefox, Chrome and Babel. + +However here we take care to return the same object if it doesn't need an update. The +comparison happens inside the callback because it depends on the state as +well. This is a good thing to do so that the shallow equality check doesn't +return false if nothing changes. + +```javascript +// Updating one property in the state +this.setState(({ stateObject }) => ({ + stateObject: stateObject.content === newContent + ? stateObject + : { ...stateObject, content: newContent }, +}); + +// This is very similar if 2 properties need an update: +this.setState(({ stateObject1, stateObject2 }) => ({ + stateObject1: stateObject1.content === newContent + ? stateObject1 + : { ...stateObject1, content: newContent }, + stateObject2: stateObject2.details === newDetails + ? stateObject2 + : { ...stateObject2, details: newDetails }, +}); + +// Or if one of the properties needs to update 2 of it's own properties: +this.setState(({ stateObject }) => ({ + stateObject: stateObject.content === newContent && stateObject.details === newDetails + ? stateObject + : { ...stateObject, content: newContent, details: newDetails }, +}); +``` + +Note that this isn't about the returned `state` object, but its properties. +The returned object is always merged into the current state, and React creates +a new component's state object at each update cycle. + +#### How to update an array +Updating an array is easy too. + +You must avoid methods that mutate the array like push/splice/pop/shift and you +must not change directly an item. + +```javascript +// Please don't do this as this will likely induce bugs. +this.setState(({ stateArray }) => { + stateArray.push(newItem); // This is wrong + stateArray[1] = newItem; // This is wrong too + return { stateArray }; +}); +``` + +Instead here again you need to **create a new array instance**. + +```javascript +// Adding an element is easy. +this.setState(({ stateArray }) => ({ + stateArray: [...stateArray, newElement], +})); + +this.setState(({ stateArray }) => { + // Removing an element is more involved. + const newArray = stateArray.filter(element => element !== removeElement); + // or + const newArray = [...stateArray.slice(0, index), ...stateArray.slice(index + 1)]; + // or do what you want on a new clone: + const newArray = stateArray.slice(); + return { + // Because we want to keep the old array if removeElement isn't in the + // filtered array, we compare the lengths. + // We still start a render phase because we call `setState`, but thanks to + // PureComponent's shouldComponentUpdate implementation we won't actually render. + stateArray: newArray.length === stateArray.length ? stateArray : newArray, + }; + + // You can also return a falsy value to avoid the render cycle at all: + return newArray.length === stateArray.length + ? null + : { stateArray: newArray }; +}); +``` + +#### How to update Maps and Sets +The process is very similar for Maps and Sets. Here is a quick example: + +```javascript +// For a Set +this.setState(({ stateSet }) => { + if (!stateSet.has(value)) { + stateSet = new Set(stateSet); + stateSet.add(value); + } + return { stateSet }; +}); + +// For a Map +this.setState(({ stateMap }) => { + if (stateMap.get(key) !== value) { + stateMap = new Map(stateMap); + stateMap.set(key, value); + } + return { stateMap }; +})); +``` + +#### How to update primitive values + +Obviously, with primitive types like boolean, number or string, that are +comparable with the operator `===`, it's much easier: + +```javascript +this.setState({ + stateString: "new string", + stateNumber: 42, + stateBool: false, +}); +``` + +Note that we don't use the callback version of `setState` here. That's because +for primitive values we don't need to use the previous state to generate a new +state. + +#### A few words about Redux + +When working with Redux, the rules stay the same, except all of this +happens in your reducers instead of in your components. With Redux comes the +function [`combineReducers`](https://redux.js.org/docs/api/combineReducers.html) +that obeys all the rules we outlined before while making it possible to have a +nested state. + +### `shouldComponentUpdate` or `PureComponent`? + +It is highly recommended to go the full **PureComponent + immutability** route, +instead of writing custom `shouldComponentUpdate` implementations for +components. This is more generic, more maintainable, less error-prone, faster. + +Of course all rules have exceptions and you're free to implement a +`shouldComponentUpdate` method if you have specific cases to take care of. + +### Some gotchas with `PureComponent` + +Because `PureComponent` shallowly checks props and state, you need to take care +to not create a new reference for something that's otherwise identical. Some +common cases are: + +* Using a new instance for a prop at each render cycle. Especially, do not use + a bound function or an anonymous function (both classic functions or + arrow functions) as a prop: + + ```javascript + render() { + return <MyComponent onUpdate={() => this.update()} />; + } + ``` + + Each time the `render` method runs, a new function will be created, and in + `MyComponent`'s `shouldComponentUpdate` the shallow check will always fail + defeating its purpose. + +* Using another reference for the same data. One very common example is the empty + array: if you use a new `[]` for each render, you won't skip render. A solution + is to reuse a common instance. Be careful as this can very well be hidden + within some complicated Redux reducers. + +* A similar issue can arise if you use sets or maps. If you add an element in a + `Set` that's already in there, you don't need to return a new `Set` as it will be + identical. + +* Be careful with array's methods, especially `map` or `filter`, as they always + return a new array. So even with the same inputs (same input array, same + function), you'll get a new output, even if it contains the same data. If + you're using Redux, [reselect](https://github.com/reactjs/reselect) is + recommended. + [memoize-immutable](https://github.com/memoize-immutable/memoize-immutable) + can be useful in some cases too. + +## Diagnosing performance issues with some tooling + +[You can read about it in the dedicated +page](./performance.md#diagnosing-performance-issues-in-react-based-applications). + +## Breaking the rules: always measure first + +You should generally follow these rules because they bring a consistent +performance in most cases. + +However you may have specific cases that will need that you break the rules. In +that case the first thing to do is to **measure** using a profiler so that you +know where your problem are. + +Then and only then you can decide to break the rules by using some mutable state +and/or custom `shouldComponentUpdate` implementation. + +And remember to measure again after you did your changes, to check and prove +that your changes actually made an impact. Ideally you should always give links +to profiles when requesting a review for a performance patch. diff --git a/devtools/docs/contributor/devtools-alert-example.png b/devtools/docs/contributor/devtools-alert-example.png Binary files differnew file mode 100644 index 0000000000..21335e7db5 --- /dev/null +++ b/devtools/docs/contributor/devtools-alert-example.png diff --git a/devtools/docs/contributor/devtools-alert-improvement.png b/devtools/docs/contributor/devtools-alert-improvement.png Binary files differnew file mode 100644 index 0000000000..a220f79407 --- /dev/null +++ b/devtools/docs/contributor/devtools-alert-improvement.png diff --git a/devtools/docs/contributor/devtools-alert-invalid.png b/devtools/docs/contributor/devtools-alert-invalid.png Binary files differnew file mode 100644 index 0000000000..cc33bf9909 --- /dev/null +++ b/devtools/docs/contributor/devtools-alert-invalid.png diff --git a/devtools/docs/contributor/devtools-alert-investigate.png b/devtools/docs/contributor/devtools-alert-investigate.png Binary files differnew file mode 100644 index 0000000000..01b8bf5a40 --- /dev/null +++ b/devtools/docs/contributor/devtools-alert-investigate.png diff --git a/devtools/docs/contributor/devtools-alert-notes-owner.png b/devtools/docs/contributor/devtools-alert-notes-owner.png Binary files differnew file mode 100644 index 0000000000..9e2bb3bc87 --- /dev/null +++ b/devtools/docs/contributor/devtools-alert-notes-owner.png diff --git a/devtools/docs/contributor/devtools-alert-regroup.png b/devtools/docs/contributor/devtools-alert-regroup.png Binary files differnew file mode 100644 index 0000000000..625217497a --- /dev/null +++ b/devtools/docs/contributor/devtools-alert-regroup.png diff --git a/devtools/docs/contributor/devtools-performance-dashboard.png b/devtools/docs/contributor/devtools-performance-dashboard.png Binary files differnew file mode 100644 index 0000000000..49cbd26cd0 --- /dev/null +++ b/devtools/docs/contributor/devtools-performance-dashboard.png diff --git a/devtools/docs/contributor/files/README.md b/devtools/docs/contributor/files/README.md new file mode 100644 index 0000000000..8d85916b12 --- /dev/null +++ b/devtools/docs/contributor/files/README.md @@ -0,0 +1,12 @@ +# Directories Overview + +This page provides a very top level overview of what is on each directory in the DevTools source code: + +* `devtools/shared`: Code shared by both the client (front-end UI) and server. If we are using any third party libraries, or importing external repositories into our tree, those libraries generally live here (eg, `devtools/shared/jsbeautify`), assuming they are used by both client and server. + * `devtools/shared/client`: Code for the [Remote Debugging Protocol](../backend/protocol.md) (RDP) client. You may wonder why this is not in `devtools/client` below: it's mainly because tests in server also need access to the RDP client. + * `devtools/shared/locales`: Strings used in either the server only, or shared with both the client and server. +* `devtools/server`: Code for the [RDP](../backend/protocol.md) server and transport layer. + * `devtools/server/actors`: [RDP Actors](../backend/protocol.md#actors). Note that if you're modifying actors, you may need to worry about [backwards compatibility](../backend/backward-compatibility.md) with older clients. +* `devtools/client`: Code for the front-end side of our tools. In theory, each directory corresponds to a panel, but this is not always the case. This directory is only shipped with desktop Firefox, as opposed to other directories above, which are shipped with all Gecko products (Firefox for Android, etc.) + * `devtools/client/locales`: Strings used in the client front-end. + * `devtools/client/themes`: CSS and images used in the client front-end. diff --git a/devtools/docs/contributor/files/adding-files.md b/devtools/docs/contributor/files/adding-files.md new file mode 100644 index 0000000000..5d1fca4b1e --- /dev/null +++ b/devtools/docs/contributor/files/adding-files.md @@ -0,0 +1,163 @@ +# Adding New Files - Various DevTools Resource Types + +This page lists the various DevTools resource types and how they can be created and loaded. + +## JavaScript Modules + +### Build Configuration + +JavaScript modules are installed by our build system using `moz.build` files. If you add a new JavaScript module, you'll need to update (or add) one of these files to make the build system aware of your new module. See the example below. + +A `moz.build` file must live in the same directory as the files to be installed. Don't list files from a subdirectory in a `moz.build` from a parent directory. + +Following these steps ensures that `require()` and `resource://` paths map directly to locations in the source tree, instead of being totally arbitrary. + +Example: + +* File: `/devtools/server/actors/layout.js` +* In `/devtools/server/actors/moz.build`: + +``` +DevToolsModules( + 'layout.js' +) +``` + +### `require()` + +Most DevTools JS code is in the form of CommonJS modules that are loaded with `require()`. + +To `require()` a file, the module ID is exactly its source tree path. + +Example: + +* File: `/devtools/server/actors/layout.js` +* Usage (prefer lazy in most cases): + * `loader.lazyRequireGetter(this, "layout", "devtools/server/actors/layout")` + * `require("devtools/server/actors/layout")` + +### `ChromeUtils.import()` + +Some older DevTools JS modules use the Gecko JavaScript code module format with the file extension `.jsm`. We are trying to move away from this format, so it's unlikely you would add a new one, but you might need to import an existing one in your code. + +These modules are loaded using `ChromeUtils.import()`. To `import()` a file, you provide a `resource://` URL, which is exactly the source tree path. + +In more detail: + +* `/devtools/client/<X>: resource://devtools/client/<X>` +* `/devtools/server/<X>: resource://devtools/server/<X>` +* `/devtools/shared/<X>: resource://devtools/shared/<X>` + +Example: + +* File: `/devtools/shared/loader/Loader.sys.mjs` +* Usage: + * `const { loader } = ChromeUtils.importESModule("resource://devtools/shared/loader/Loader.sys.mjs")` + +Example: + +* File: `/toolkit/mozapps/extensions/AddonManager.jsm` +* Usage (prefer lazy in most cases): + * `const lazy = {}; ChromeUtils.defineModuleGetter(lazy, "AddonManager", "resource://gre/modules/AddonManager.jsm")` + * `const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm")` + +## Chrome Content + +Much of the DevTools front-end / UI is currently loaded using `chrome://` URLs, which allow those files to have privileged access to platform internals. + +This is typically used to load XUL, HTML, and JS files in the UI. + +Note: "Chrome" here means "browser chrome", as in the UI, and bears no relation to "Chrome" as in the browser. We'd like to move away from this on DevTools and be more like regular web sites, but most tools are using `chrome://` URLs for now. + +### Packaging + +If you add a new file that should be loaded via a `chrome://` URL, you need to update a manifest file at `/devtools/client/jar.mn` so that it's packaged correctly. + +Please ensure that any new files are added so their entire source tree path is part of the URL. To do so, the `jar.mn` entry should look like: + +``` +content/<X> (<X>) +``` + +where `<X>` is the path to your file after removing the `/devtools/client/` prefix. + +Example: + +* File: `/devtools/client/webaudioeditor/models.js` +* Entry: `content/webaudioeditor/models.js (webaudioeditor/models.js)` + +### Usage + +Chrome content URLs almost match their source tree path, with one difference: the segment `client` is replaced by `content`. This is a requirement of the `chrome://` protocol handler. + +Example: + +* File: `/devtools/client/webaudioeditor/models.js` +Usage: `chrome://devtools/content/webaudioeditor/models.js` + +For files within a single tool, consider relative URLs. They're shorter! + +## Chrome Themes + +Similar to the chrome content section above, we also use chrome themes (or `skin` URLs) in the DevTools UI. These are typically used to load CSS and images. + +### Packaging + +If you add a new file that should be loaded via `chrome://` (such as a new CSS file for a tool UI), you need to update a manifest file at `/devtools/client/jar.mn` so that it's packaged correctly. + +Please ensure that any new files are added so their entire source tree path is part of the URL. To do so, the `jar.mn` entry should look like: + +``` +skin/<X> (themes/<X>) +``` + +where `<X>` is the path to your file after removing the `/devtools/client/themes/` prefix. + +Example: + +* File: `/devtools/client/themes/images/add.svg` +* Entry: `skin/images/add.svg (themes/images/add.svg)` + +### Usage + +Chrome theme URLs almost match their source tree path, with one difference: the segment `client/themes` is replaced by `skin`. This is a requirement of the `chrome://` protocol handler. + +Example: + +* File: `/devtools/client/themes/images/add.svg` +* Usage: `chrome://devtools/skin/images/add.svg` + +## Localization (l10n) + +Similar to the other chrome sections above, we also use `locale` URLs in the DevTools UI to load localized strings. This section applies to `*.dtd` (for use as entities within XUL / XHTML files) and `*.properties` (for use via runtime APIs) files. + +We currently have two sets of localized files: + +* `devtools/client/locales`: Strings used in the DevTools client (front-end UI) +* `devtools/shared/locales`: Strings used in either the DevTools server only, or shared with both the client and server + +### Packaging + +If you add a new l10n file (such as a new `*.dtd` or `*.properties` file), there should not be any additional packaging steps to perform, assuming the new file is placed in either of the 2 directories mentioned above. Each one contains a `jar.mn` which uses wildcards to package all files in the directory by default. + +### Usage + +Locale URLs differ somewhat based on whether they are in `client` or `shared`. While we would have preferred them to match the source tree path, the requirements of the `chrome://` protocol don't make that easy to do. + +Example: + +* File: `/devtools/client/locales/en-US/debugger.dtd` +* Usage: `chrome://devtools/locale/debugger.dtd` + +Example: + +* File: `/devtools/shared/locales/en-US/screenshot.properties` +* Usage: `chrome://devtools-shared/locale/screenshot.properties` + +### Guidelines + +Localization files should follow a set of guidelines aimed at making it easier for people to translate the labels in these files in many languages. + +[Read best practices for developers](https://mozilla-l10n.github.io/documentation/localization/dev_best_practices.html). + +In particular, it's important to write self-explanatory comments for new keys, deleting unused keys, changing the key name when changing the meaning of a string, and more. So make sure you read through these guidelines should you have to modify a localization file in your patch. diff --git a/devtools/docs/contributor/frontend/csp.md b/devtools/docs/contributor/frontend/csp.md new file mode 100644 index 0000000000..7357a13ba2 --- /dev/null +++ b/devtools/docs/contributor/frontend/csp.md @@ -0,0 +1,56 @@ + +The DevTools toolbox is loaded in an iframe pointing to about:devtools-toolbox. This iframe has a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) (CSP) applied, which will mitigate potential attacks. However this may limit the resources that can be loaded in the toolbox documenth. + +# Current DevTools CSP + +The current policy for about:devtools-toolbox is: +``` +default-src chrome: resource:; img-src chrome: resource: data:; object-src 'none' +``` + +This means: +- `chrome://` and `resource://` are allowed for any resource +- `chrome://` and `resource://` and `data://` are allowed for images + +For more information about which resources and requests are in scope of the CSP, you can read the [default-src documentation on MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src). + +# Scope of the DevTools CSP + +This content security policy only applies to the toolbox document for now. If you are working within the document of a panel or if you are working on the server, those limitations should not apply. + +Note that even when working in the document of a panel, we are sometimes interacting with the toolbox document, for instance to show tooltips. So typically any resource created for a tooltip will be subject to the CSP limitations. + +# Recognizing CSP issues + +Open the Browser Toolbox, if you see errors such as + +``` +JavaScript Error: "Content Security Policy: The page’s settings blocked the loading of a resource [...]" +``` + +it means you are trying to load a resource with a forbidden scheme. + +# Fixing CSP issues + +If your implementation hits a CSP issue, the first suggestion is to try to use a supported scheme. If this is not an option, check if you can perform your request or load your resource outside of the toolbox document. For instance if the resource you are loading is related to the debugged target, the request can (and probably should) be made from an actor in the DevTools server and then forwarded from the server to the client. Requests made by the server will not be impacted by the CSP. + +If it seems like the only solution is to update the CSP, get in touch with security peers in order to discuss about your use case. You can [file a bug in Core/DOM: security](https://bugzilla.mozilla.org/enter_bug.cgi?product=Core&component=DOM%3A%20Security). + +# Fixing CSP issues in tests + +If the issue comes from test code, it should be possible to update the test to use a supported scheme. A typical issue might be trying to load an iframe inside of the toolbox with a data-uri. Instead, you can create an HTML support file and load it from either a chrome:// or a resource:// URL. + +In general once a support file is added you can access it via: +- `https://example.com/browser/[path_to_file]` +- or `chrome://mochitests/content/browser/[path_to_file]` + +For instance [devtools/client/aboutdebugging/test/browser/resources/service-workers/controlled-sw.html](https://searchfox.org/mozilla-central/source/devtools/client/aboutdebugging/test/browser/resources/service-workers/controlled-sw.html) is accessed in tests via `http://example.com/browser/devtools/client/aboutdebugging/test/browser/resources/service-workers/controlled-sw.html`. + +If you absolutely have to use an unsupported scheme, you can turn off CSPs for the test only. To do so, you need to temporarily update two preferences: + +``` +await pushPref("security.csp.enable", false); +await pushPref("dom.security.skip_about_page_has_csp_assert", true); +``` + +The `pushPref` helper will ensure the preferences come back to their initial value at the end of the test. diff --git a/devtools/docs/contributor/frontend/react-guidelines.md b/devtools/docs/contributor/frontend/react-guidelines.md new file mode 100644 index 0000000000..0622daf2ce --- /dev/null +++ b/devtools/docs/contributor/frontend/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. diff --git a/devtools/docs/contributor/frontend/react.md b/devtools/docs/contributor/frontend/react.md new file mode 100644 index 0000000000..770320f570 --- /dev/null +++ b/devtools/docs/contributor/frontend/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.md). + +## 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. diff --git a/devtools/docs/contributor/frontend/redux-guidelines.md b/devtools/docs/contributor/frontend/redux-guidelines.md new file mode 100644 index 0000000000..1782a6de47 --- /dev/null +++ b/devtools/docs/contributor/frontend/redux-guidelines.md @@ -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. diff --git a/devtools/docs/contributor/frontend/redux.md b/devtools/docs/contributor/frontend/redux.md new file mode 100644 index 0000000000..e090060f5d --- /dev/null +++ b/devtools/docs/contributor/frontend/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.md), 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. diff --git a/devtools/docs/contributor/frontend/svgs.md b/devtools/docs/contributor/frontend/svgs.md new file mode 100644 index 0000000000..d0de19a2e1 --- /dev/null +++ b/devtools/docs/contributor/frontend/svgs.md @@ -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/Web/SVG). + +## 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](../resources/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](../resources/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](../resources/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```.** diff --git a/devtools/docs/contributor/frontend/telemetry.md b/devtools/docs/contributor/frontend/telemetry.md new file mode 100644 index 0000000000..26193a4184 --- /dev/null +++ b/devtools/docs/contributor/frontend/telemetry.md @@ -0,0 +1,447 @@ +# Telemetry + +We use telemetry to get metrics of usage of the different features and panels in DevTools. This will help us take better, informed decisions when prioritising our work. + +## Adding metrics to a tool + +The process to add metrics to a tool roughly consists in: + +1. Adding the probe to Firefox +2. Using Histograms.json probes in DevTools code +3. Using Scalars.yaml probes in DevTools code +4. Using Events.yaml probes in DevTools code for analysis in Amplitude. +5. Getting approval from the data team + +### 1. Adding the probe to Firefox + +The first step involves creating entries for the probe in one of the files that contain declarations for all data that Firefox might report to Mozilla. + +These files are: + +- `toolkit/components/telemetry/Histograms.json` +- `toolkit/components/telemetry/Scalars.yaml` +- `toolkit/components/telemetry/Events.yaml` + +Scalars allow collection of simple values, like counts, booleans and strings and are to be used whenever possible instead of histograms. + +Histograms allow collection of multiple different values, but aggregate them into a number of buckets. Each bucket has a value range and a count of how many values we recorded. + +Events allow collection of a number of properties keyed to a category, method, object and value. Event telemetry helps us tell a story about how a user is interacting with the browser. + +Both scalars & histograms allow recording by keys. This allows for more flexible, two-level data collection. + +#### The different file formats + +The data team chose YAML for `Scalars.yaml` and `Events.yaml` because it is easy to write and provides a number of features not available in JSON including comments, extensible data types, relational anchors, strings without quotation marks, and mapping types preserving key order. + +While we previously used JSON for similar purposes in histograms.json, we have used YAML here because it allows for comments and is generally easier to write. + +The data team are considering moving the histograms over to YAML format at some point. + +If it's the first time you add one of these, it's advised to follow the style of existing entries. + +New data types have been added over the years, so it's quite feasible that some of our probes are not the most suitable nowadays. + +There's more information about types (and telemetry in general) on [this page](https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/telemetry/start/adding-a-new-probe.html) and [this other page](https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/telemetry/collection/index.html). + +And of course, in case of doubt, ask! + +### Adding probes to `Histograms.json` + +Our entries are prefixed with `DEVTOOLS_`. For example: + +``` + "DEVTOOLS_DOM_OPENED_COUNT": { + "alert_emails": ["dev-developer-tools@lists.mozilla.org"], + "expires_in_version": "never", + "kind": "count", + "bug_numbers": [1343501], + "description": "Number of times the DevTools DOM Inspector has been opened.", + "releaseChannelCollection": "opt-out" + }, + "DEVTOOLS_DOM_TIME_ACTIVE_SECONDS": { + "alert_emails": ["dev-developer-tools@lists.mozilla.org"], + "expires_in_version": "never", + "kind": "exponential", + "bug_numbers": [1343501], + "high": 10000000, + "n_buckets": 100, + "description": "How long has the DOM inspector been active (seconds)" + }, +``` + +There are different types of probes you can use. These are specified by the `kind` field. Normally we use `count` for counting how many times the tools are opened, and `exponential` for how many times a panel is active. + +### Adding probes to `Scalars.yaml` + +Our entries are prefixed with `devtools.`. For example: + +```yaml +devtools.toolbar.eyedropper: + opened: + bug_numbers: + - 1247985 + - 1352115 + description: Number of times the DevTools Eyedropper has been opened via the inspector toolbar. + expires: never + kind: uint + notification_emails: + - dev-developer-tools@lists.mozilla.org + release_channel_collection: opt-out + record_in_processes: + - 'main' + +devtools.copy.unique.css.selector: + opened: + bug_numbers: + - 1323700 + - 1352115 + description: Number of times the DevTools copy unique CSS selector has been used. + expires: "57" + kind: uint + notification_emails: + - dev-developer-tools@lists.mozilla.org + release_channel_collection: opt-out + record_in_processes: + - 'main' +``` + +### Adding probes to `Events.yaml` + +Our entries are prefixed with `devtools.`. For example: + +```yaml +devtools.main: + open: + objects: ["tools"] + bug_numbers: [1416024] + notification_emails: ["dev-developer-tools@lists.mozilla.org", "hkirschner@mozilla.com"] + record_in_processes: ["main"] + description: User opens devtools toolbox. + release_channel_collection: opt-out + expiry_version: never + extra_keys: + entrypoint: How was the toolbox opened? CommandLine, ContextMenu, HamburgerMenu, KeyShortcut, SessionRestore or SystemMenu + first_panel: The name of the first panel opened. + host: "Toolbox host (positioning): bottom, side, window or other." + splitconsole: Indicates whether the split console was open. + width: Toolbox width (px). +``` + +### 2. Using Histograms.json probes in DevTools code + +Once the probe has been declared in the `Histograms.json` file, you'll need to actually use it in our code. + +First, you need to give it an id in `devtools/client/shared/telemetry.js`. Similarly to the `Histograms.json` case, you'll want to follow the style of existing entries. For example: + +```js +dom: { + histogram: "DEVTOOLS_DOM_OPENED_COUNT", + timerHistogram: "DEVTOOLS_DOM_TIME_ACTIVE_SECONDS" +}, +``` + +... would correspond to the probes we declared in the previous section. + +Then, include that module on each tool that requires telemetry: + +```js +let Telemetry = require("devtools/client/shared/telemetry"); +``` + +Create a telemetry instance on the tool constructor: + +```js +this._telemetry = new Telemetry({ useSessionId: true }); +``` +`useSessionId` allows to aggregate all records behind a randomly unique "session_id" +extra attribute. For example, this helps aggregate all data recorded for one precise +toolbox instance. + +And use the instance to report e.g. tool opening... + +```js +this._telemetry.toolOpened("mytoolname", this); +``` + +... or closing: + +```js +this._telemetry.toolClosed("mytoolname", this); +``` + +Note that `mytoolname` is the id we declared in the `telemetry.js` module. + +### 3. Using Scalars.yaml probes in DevTools code + +Once the probe has been declared in the `Scalars.yaml` file, you'll need to actually use it in our code. + +First, you need to give it an id in `devtools/client/shared/telemetry.js`. You will want to follow the style of existing lowercase histogram entries. For example: + +```js +toolbareyedropper: { + scalar: "devtools.toolbar.eyedropper.opened", // Note that the scalar is lowercase +}, +copyuniquecssselector: { + scalar: "devtools.copy.unique.css.selector.opened", +}, +``` + +... would correspond to the probes we declared in the previous section. + +Then, include that module on each tool that requires telemetry: + +```js +let Telemetry = require("devtools/client/shared/telemetry"); +``` + +Create a telemetry instance on the tool constructor: + +```js +this._telemetry = new Telemetry(); +``` + +And use the instance to report e.g. tool opening... + +```js +this._telemetry.toolOpened("mytoolname", this); +``` + +Notes: + +- `mytoolname` is the id we declared in the `Scalars.yaml` module. +- Because we are not logging tool's time opened in `Scalars.yaml` we don't care + about toolClosed. Of course, if there was an accompanying `timerHistogram` + field defined in `telemetry.js` and `histograms.json` then `toolClosed` should + also be added. + +### 4. Using Events.yaml probes in DevTools code + +Once the probe has been declared in the `Events.yaml` file, you'll need to actually use it in our code. + +It is crucial to understand that event telemetry have a string identifier which is constructed from the `category`, `method`, `object` (name) and `value` on which the event occurred. This key points to an "extra" object that contains further information about the event (we will give examples later in this section). + +Because these "extra" objects can be from completely independent code paths we +can send events and leave them in a pending state until all of the expected extra properties have been received. + +First, include the telemetry module in each tool that requires telemetry: + +```js +let Telemetry = require("devtools/client/shared/telemetry"); +``` + +Create a telemetry instance on the tool constructor: + +```js +this._telemetry = new Telemetry(); +``` + +And use the instance to report e.g. tool opening... + +```js +// Event telemetry is disabled by default so enable it for your category. +this._telemetry.setEventRecordingEnabled(true); + +// If you already have all the properties for the event you can send the +// telemetry event using: +// this._telemetry.recordEvent(method, object, value, extra) e.g. +this._telemetry.recordEvent("open", "tools", null, { + "entrypoint": "ContextMenu", + "first_panel": "Inspector", + "host": "bottom", + "splitconsole": false, + "width": 1024, +}); + +// If your "extra" properties are in different code paths you will need to +// create a "pending event." These events contain a list of expected properties +// that can be populated before or after creating the pending event. + +// Use the category, method, object, value combinations above to add a +// property... we do this before creating the pending event simply to +// demonstrate that properties can be sent before the pending event is created. +this._telemetry.addEventProperty( + this, "open", "tools", null, "entrypoint", "ContextMenu"); + +// In this example `"open", "tools", null` make up the +// signature of the event and needs to be sent with all properties. + +// Create the pending event using +// this._telemetry.preparePendingEvent(this, method, object, value, +// expectedPropertyNames) e.g. +this._telemetry.preparePendingEvent(this, "open", "tools", null, + ["entrypoint", "first_panel", "host", "splitconsole", "width", "session_id"] +); + +// Use the category, method, object, value combinations above to add each +// property. +this._telemetry.addEventProperty( + this, "open", "tools", null, "first_panel", "inspector"); +this._telemetry.addEventProperty( + this, "open", "tools", null, "host", "bottom"); +this._telemetry.addEventProperty( + this, "open", "tools", null, "splitconsole", false); +this._telemetry.addEventProperty( + this, "open", "tools", null, "width", 1024); + +// You can also add properties in batches using e.g.: +this._telemetry.addEventProperties(this, "open", "tools", null, { + "first_panel": "inspector", + "host": "bottom", + "splitconsole": false, + "width": 1024 +}); + +``` + +Notes: + +- `mytoolname` is the id we declared in the `Scalars.yaml` module. +- Because we are not logging tool's time opened in `Scalars.yaml` we don't care + about toolClosed. Of course, if there was an accompanying `timerHistogram` + field defined in `telemetry.js` and `histograms.json` then `toolClosed` should + also be added. + +#### Note on top level panels + +The code for the tabs uses their ids to automatically report telemetry when you switch between panels, so you don't need to explicitly call `toolOpened` and `toolClosed` on top level panels. + +You will still need to call those functions on subpanels, or tools such as `about:debugging` which are not opened as tabs. + +#### Testing + +The telemetry module will print warnings to stdout if there are missing ids. It is strongly advisable to ensure this is working correctly, as the module will attribute usage for undeclared ids to a generic `custom` bucket. This is not good for accurate results! + +To see these warnings, you need to have the `browser.dom.window.dump.enabled` browser preference set to `true` in `about:config` (and restart the browser). + +Then, try doing things that trigger telemetry calls (e.g. opening a tool). Imagine we had a typo when reporting the tool was opened: + +```js +this._telemetry.toolOpened('mytoolnmae', this); + ^^^^ typo, should be *mytoolname* +``` + +Would report an error to stdout: + +```text +Warning: An attempt was made to write to the mytoolnmae histogram, which is not defined in Histograms.json +``` + +So watch out for errors. + +#### Testing Event Telemetry + +This is best shown via an example: + +```js +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { Toolbox } = require("devtools/client/framework/toolbox"); +const { TelemetryTestUtils } = ChromeUtils.import("resource://testing-common/TelemetryTestUtils.jsm"); + +const URL = "data:text/html;charset=utf8,browser_toolbox_telemetry_close.js"; +const { RIGHT, BOTTOM } = Toolbox.HostType; +const DATA = [ + { + category: "devtools.main", + method: "close", + object: "tools", + value: null, + extra: { + host: "right", + width: w => w > 0, + } + }, + { + category: "devtools.main", + method: "close", + object: "tools", + value: null, + extra: { + host: "bottom", + width: w => w > 0, + } + } +]; + +add_task(async function() { + // Let's reset the counts. + Services.telemetry.clearEvents(); + + // Ensure no events have been logged + TelemetryTestUtils.assertNumberOfEvents(0); + + await openAndCloseToolbox("webconsole", SIDE); + await openAndCloseToolbox("webconsole", BOTTOM); + + checkResults(); +}); + +async function openAndCloseToolbox(toolId, host) { + const tab = await addTab(URL); + const toolbox = await gDevTools.showToolboxForTab(tab, { toolId }); + + await toolbox.switchHost(host); + await toolbox.destroy(); +} + +function checkResults() { + TelemetryTestUtils.assertEvents(DATA, {category: "devtools.main", method: "close", object: "tools"}); +} +``` + +#### Compile it + +You need to do a full Firefox build if you have edited either `Histograms.json` or `Events.yaml`, as they are processed at build time, and various checks will be run on them to guarantee they are valid. + +```bash +./mach build +``` + +If you use `mach build faster` or artifact builds, the checks will not be performed, and your try builds will fail ("bust") when the checks are run there. + +Save yourself some time and run the checks locally. + +NOTE: Changes to `Scalars.yaml` *are* processed when doing an artifact build. + +### 4. Getting approval from the data team + +This is required before the changes make their way into `mozilla-central`. + +To get approval, attach your patch to the bug in Bugzilla, and set two flags: + +- a `review?` flag for a data steward. +- a `needinfo?` flag to hkirschner (our product manager, so he vouches that we're using the data) + +Be sure to explain very clearly what is the new probe for. E.g. "We're seeking approval for tracking opens of a new panel for debugging Web API ABCD" is much better than just asking for feedback without background info. + +This review shouldn't take too long: if there's something wrong, they should tell you what to fix. If you see no signs of activity after a few days, you can ask in `#developers`. + +Note that this review is *in addition* to normal colleague reviews. + +Click [here](https://wiki.mozilla.org/Firefox/Data_Collection#Requesting_Data_Collection) for more details. + +## Accessing existing data + +### Local data + +Go to `about:telemetry` to see stats relating to your local instance. + +### Global data + +Data aggregated from large groups of Firefox users is available at [telemetry.mozilla.org](https://telemetry.mozilla.org). + +Reports are written with SQL. For example, here's one comparing [usage of some DevTools panels](https://sql.telemetry.mozilla.org/queries/1000#table). + +If you want to get better understanding of how people are using the tools, you are encouraged to explore this data set by writing your own reports. + +The easiest way to get started is to *fork* an existing report and modify it to get used to the syntax, as SQL for massive data tables is very different from SQL for a humble blog engine, and you'll find some new operators that might look unfamiliar. + +It's also recommended to take small steps and run the queries often to detect errors before they're too complicated to solve, particularly if you're not experienced with this (yet). + +Slow queries will be interrupted by the system, so don't worry about "fetching too much data" or "using too many resources". There's built-in protection to avoid your code eating up the Telemetry database. + +Funnily, if you're based in Europe, you might be in luck, as the website tends to be more responsive during European working hours than it is at Pacific working hours, as seemingly there's less people in Europe interacting with it. diff --git a/devtools/docs/contributor/getting-started/README.md b/devtools/docs/contributor/getting-started/README.md new file mode 100644 index 0000000000..fe21ecbe8d --- /dev/null +++ b/devtools/docs/contributor/getting-started/README.md @@ -0,0 +1,29 @@ +# Getting started + +Hello, and thanks for your interest in contributing to Firefox DevTools! + +DevTools is a complex web app, but if you're familiar with either HTML/CSS or JavaScript, you can contribute! The process goes like this: + +- Claim a bug +- Set up your dev environment +- Write the patch and get it reviewed + +Feel free to ask questions at any point on [Matrix](https://chat.mozilla.org/#/room/#devtools:mozilla.org). + +## Claim a bug + +Visit the [Codetribute](https://codetribute.mozilla.org/projects/devtools) bug tracker and find a bug you like. Anything labeled **good-first-bug** is perfect for a newcomer. Many of these tasks will make a visible impact to the DevTools UI. + +Claim the bug by creating a Bugzilla account and posting a comment on the bug’s page to say that you’d like to work on it. Ask questions if you have any uncertainty about what the bug means. + +## Set up your dev environment + +Follow the steps of Firefox’s [contributor guide](https://firefox-source-docs.mozilla.org/contributing/how_to_contribute_firefox.html) to install and run Firefox locally. During installation, follow the steps for “Artifact Mode.” + +If you run into errors about missing libraries, search the web to learn how to install whatever is missing. If you get stuck, ask for help on [Matrix](https://chat.mozilla.org/#/room/#devtools:mozilla.org). + +## Write the patch and get it reviewed + +The rest of this documentation has info on coding standards as well as specifics of DevTools architecture. + +When you’re ready to commit your changes, you can ask the bug’s mentor to review, or refer to the list of [DevTools team members](https://firefox-dev.tools/#about-devtools). diff --git a/devtools/docs/contributor/getting-started/architecture-overview.md b/devtools/docs/contributor/getting-started/architecture-overview.md new file mode 100644 index 0000000000..c7631fdca3 --- /dev/null +++ b/devtools/docs/contributor/getting-started/architecture-overview.md @@ -0,0 +1,11 @@ +# Architecture overview + +Broadly speaking, the tools are divided in two parts: the server and the client. A **server** is anything that can be debugged: for example, your browser, but it could also be Firefox for Android, running on another device. The **client** is the front-end side of the tools, and it is what developers interact with when using the tools. + +Since these two parts are decoupled, we can connect to any server using the same client. This enables us to debug multiple types of servers, using the same protocol to communicate. + +You will often hear about `actors`. Each feature that can be debugged (for example, network) is exposed via an `actor`, which provides data about that specific feature. It's up to each server to implement some or all actors; the client needs to find out and decide what it can render on the front-side when it connects to the server. So when we want to debug a new feature, we might need to do work in two parts of the code: the server (perhaps implementing a new actor, or extending existing ones) and the client (to display the debugging data returned by the actor). + +Often, an actor will correspond to a panel. But a panel might want to get data from multiple actors. + +You might also hear about `the toolbox`. The toolbox is what everyone else calls `developer tools` i.e. the front-end that you see when you open the tools in your browser. diff --git a/devtools/docs/contributor/getting-started/bugzilla.md b/devtools/docs/contributor/getting-started/bugzilla.md new file mode 100644 index 0000000000..fe68491477 --- /dev/null +++ b/devtools/docs/contributor/getting-started/bugzilla.md @@ -0,0 +1,9 @@ +# Get a Bugzilla account + +Mozilla's bug tracker is at [https://bugzilla.mozilla.org/](https://bugzilla.mozilla.org/), which is often abbreviated as `BMO`. + +You don't need an account if you simply want to build the code and modify it, but you will need an account in Bugzilla if you want to file or comment on bugs, send patches, get assigned to bugs (so you can 'claim' them), etc. + +**Note**: if you are a Mozilla employee, don’t use an email alias to sign up, use your full LDAP account. + +To make yourself easier to find by other colleagues (for example when they're trying to set a reviewer for a patch), you can [edit the *real name* field](https://bugzilla.mozilla.org/userprefs.cgi?tab=account) to add your alias or any other word they might use to search for you there. The convention is to use something like `Your Name :alias :ldap/:ircnick`. For example: `Mary Smith :mary :msmith` diff --git a/devtools/docs/contributor/getting-started/development-profiles.md b/devtools/docs/contributor/getting-started/development-profiles.md new file mode 100644 index 0000000000..99dd370733 --- /dev/null +++ b/devtools/docs/contributor/getting-started/development-profiles.md @@ -0,0 +1,105 @@ +# Setting up a development profile + +You can have various [Firefox profiles](https://developer.mozilla.org/en-US/Firefox/Multiple_profiles) (think of something like "user accounts"), each one with different settings, addons, appearance, etc. + +This page will guide you through configuring a new profile to enable development features such as additional logging, dumping of network packets, remote debugging, etc. which will help when working in DevTools. + +Many of these changes are achieved by modifying preferences in `about:config`, a special page you can access by typing in `about:config` in Firefox's URL bar. The first time, it will show you a warning page. Click through or disable the warning for the future, and then you can start searching for preferences to modify. + +(If you're curious, here's more information about [about:config](https://support.mozilla.org/en-US/kb/about-config-editor-firefox)) + +## Default profile + +The following command line expression will run Firefox using a default profile. It'll create the default profile if there isn't one already. + + +``` +./mach run +``` + +## Using temporary profile + +The following command line expression will run Firefox using a temporary profile which is discarded when you close the browser. It also means that any preferences we set will not persist. + +``` +./mach run --temp-profile +``` + +## Create a permanent profile + +Create a permanent profile can be done as follows: + +``` +./mach run -P development +``` + +If this profile doesn't exist yet (quite likely), a window will open offering you options to create a new profile, and asking you for the name you want to use. + +Create a new profile, and name it `development`. Then start Firefox by clicking on `Start Nightly`. + +Next time you start Firefox with `./mach run -P development`, the new profile will be automatically used, and settings will persist between browser launches. + +It's now time to [start contributing](../contributing.md)! 😃 + +--- + +## Advanced settings + +The following section describes how to enable additional development features; don't worry if you don't understand what some of these are or what they're for. Feel free to skip these if you're new; you probably don't need them yet. + +### Enable additional logging + +You can change the value of these preferences by going to `about:config`: + +| Preference name | Value | Comments | +| --------------- | --------------- | -------- | +| `browser.dom.window.dump.enabled` | `true` | Adds global `dump` function to log strings to `stdout` | +| `devtools.console.stdout.chrome` | `true` | Allows console API to write to `stdout` when used by chrome content | +| `devtools.console.stdout.content` | `true` | Allows console API to write to `stdout` when used by content | +| `devtools.debugger.log` (*) | `true` | Dump packets sent over remote debugging protocol to `stdout`.<!-- TODO: I think this is outdated and there isn't a compatible addon anymore <br /><br />The [remote protocol inspector add-on](https://github.com/firebug/rdp-inspector/wiki) might be useful too.--> | +| `devtools.dump.emit` (*) | `true` | Log event notifications from the EventEmitter class<br />(found at `devtools/shared/event-emitter.js`). | + +Preferences marked with a (`*`) also require `browser.dom.window.dump.enabled` in order to work. You might not want to enable *all* of those all the time, as they can cause the output to be way too verbose, but they might be useful if you're working on a server actor, for example<!--TODO link to actors doc-->. + +Restart the browser to apply configuration changes. + +### Enable remote debugging and the Browser Toolbox + +<!--TODO: aren't some of these preferences enabled by default now in local builds? --> + +These settings allow you to use the [browser toolbox](https://firefox-source-docs.mozilla.org/devtools-user/browser_toolbox/) to inspect the DevTools themselves, set breakpoints inside of DevTools code in the *Browser* environment. + +Open DevTools, and click the "Toolbox Options" gear icon in the top right (the image underneath is outdated). <!--TODO update image--> + +Make sure the following two options are checked: + +- Enable browser chrome and add-on debugging toolboxes +- Enable remote debugging + +![Settings for developer tools - "Enable Chrome Debugging" and "Enable Remote Debugging"](../resources/DevToolsDeveloperSettings.png) + +In `about:config`, set `devtools.debugger.prompt-connection` to `false`. + +This will get rid of the prompt displayed every time you open the browser toolbox. + +### Enable DevTools assertions + +When assertions are enabled, assertion failures are fatal, log console warnings, and throw errors. + +When assertions are not enabled, the `assert` function is a no-op. + +It also enables the "debug" builds of certain third party libraries, such as React. + +To enable assertions, add this to your `mozconfig` file: + +``` +ac_add_options --enable-debug-js-modules +``` + +And assert your own invariants like this: + +``` +const { assert } = require("devtools/shared/DevToolsUtils"); +// ... +assert(1 + 1 === 2, "I really hope this is true..."); +``` diff --git a/devtools/docs/contributor/getting-started/restart.png b/devtools/docs/contributor/getting-started/restart.png Binary files differnew file mode 100644 index 0000000000..a4611e116a --- /dev/null +++ b/devtools/docs/contributor/getting-started/restart.png diff --git a/devtools/docs/contributor/getting-started/where-is-the-code.md b/devtools/docs/contributor/getting-started/where-is-the-code.md new file mode 100644 index 0000000000..3412bff6a4 --- /dev/null +++ b/devtools/docs/contributor/getting-started/where-is-the-code.md @@ -0,0 +1,13 @@ +# Where is the code? (or: `mozilla-central` vs `devtools-html`) + +Most of the code is hosted in the Firefox repository (we call it `mozilla-central`, often abbreviated as `m-c`), in the [devtools](https://searchfox.org/mozilla-central/source/devtools) folder. Development of some pieces of the tools is happening in GitHub, on the [firefox-devtools](https://github.com/firefox-devtools/) organisation. + +<!--TODO: table listing components and locations (m-c vs github)--> + +Code in `m-c` takes longer to obtain and build, as it involves checking out Firefox's repository and installing tools such as a compiler to build a version of Firefox in your machine. + +On the other hand, the repositories in `devtools-html` are more straightforward if you're used to *the GitHub workflow*: you clone them, and then run `npm install && npm run` or similar. Roughly, you can work with each repository individually, and we periodically generate JavaScript bundles that are then copied into `m-c`. + +Even if you only want to work on a tool whose code is on `devtools-html`, you might still need to go through the step of getting and compiling the code from `mozilla-central` in order to do integration work (such as updating a tool bundle). + +From now on, this guide will focus on building the full DevTools within Firefox. Please refer to individual project instructions for tools hosted in `devtools-html`. diff --git a/devtools/docs/contributor/index.md b/devtools/docs/contributor/index.md new file mode 100644 index 0000000000..1c57cbb5e0 --- /dev/null +++ b/devtools/docs/contributor/index.md @@ -0,0 +1,66 @@ +# Devtools + +* [Getting started](getting-started/README.md) + * [Get a Bugzilla account](getting-started/bugzilla.md) + * [Create a development profile](getting-started/development-profiles.md) +* [Contributing](./contributing.md) + * [Find bugs to work on](./contributing/find-bugs.md) + * [How to fix a bug](./contributing/fixing-bugs.md) + * [Code reviews](./contributing/code-reviews.md) + * [Set up for code reviews](./contributing/code-reviews-setup.md) + * [Find a reviewer](./contributing/code-reviews-find-reviewer.md) + * [Send your code for review](./contributing/making-prs.md) + * [Reviews checklist](./contributing/code-reviews-checklist.md) + * [Landing code](./contributing/landing-code.md) + * [Levelling up](./contributing/levelling-up.md) + * [Coding standards](./contributing/coding-standards.md) + * [JavaScript](./contributing/javascript.md) + * [ESLint](./contributing/eslint.md) + * [CSS](./contributing/css.md) + * [Filing good bugs](./contributing/filing-good-bugs.md) + * [Investigating performance issues](./contributing/performance.md) + * [Writing efficient React code](./contributing/react-performance-tips.md) +* [Automated tests](tests/README.md) + * Running tests + * [`xpcshell`](tests/xpcshell.md) + * [Chrome mochitests](tests/mochitest-chrome.md) + * [DevTools mochitests](tests/mochitest-devtools.md) + * [Node tests](tests/node-tests.md) + * [Writing tests](tests/writing-tests.md) + * [Debugging intermittent failures](tests/debugging-intermittents.md) + * [Performance tests overview](tests/performance-tests-overview.md) + * [DAMP Performance tests](tests/performance-tests-damp.md) + * [Writing a new test](tests/writing-perf-tests.md) + * [Example](tests/writing-perf-tests-example.md) + * [Advanced tips](tests/writing-perf-tests-tips.md) +* [Files and directories](files/README.md) + * [Adding New Files](files/adding-files.md) +* [Tool Architectures](tools/tools.md) + * [Inspector](tools/inspector.md) + * [Panel Architecture](tools/inspector-panel.md) + * [Highlighters](tools/highlighters.md) + * [Memory](tools/memory-panel.md) + * [Debugger](tools/debugger-panel.md) + * [Responsive Design Mode](tools/responsive-design-mode.md) + * [Console](tools/console-panel.md) + * [Storage](tools/storage.md) +* [Frontend](frontend/frontend.md) + * [Panel SVGs](frontend/svgs.md) + * [React](frontend/react.md) + * [Guidelines](frontend/react-guidelines.md) + * [Redux](frontend/redux.md) + * [Guidelines](frontend/redux-guidelines.md) + * [Telemetry](frontend/telemetry.md) + * [Content Security Policy](frontend/csp.md) +* [Backend](backend/backend.md) + * [Remote Debugging Protocol](backend/protocol.md) + * [Client API](backend/client-api.md) + * [Debugger API](backend/debugger-api.md) + * [Backward Compatibility](backend/backward-compatibility.md) + * Actors + * [Actors Organization](backend/actor-hierarchy.md) + * [Handling Multi-Processes in Actors](backend/actor-e10s-handling.md) + * [Writing Actors With protocol.js](backend/protocol.js.md) + * [Registering A New Actor](backend/actor-registration.md) + * [Actor Best Practices](backend/actor-best-practices.md) +* [Preferences](preferences.md) diff --git a/devtools/docs/contributor/index.rst b/devtools/docs/contributor/index.rst new file mode 100644 index 0000000000..babb5cb767 --- /dev/null +++ b/devtools/docs/contributor/index.rst @@ -0,0 +1,124 @@ +.. toctree:: + :name: devtools-contributor-doc + +================================= +Firefox DevTools Contributor Docs +================================= + +This is a guide to working on the code for Firefox Developer Tools. If you're looking for help with using the tools, see the `user docs </devtools-user>`_. For other ways to get involved, check out our `community site <https://firefox-dev.tools/>`__. + + +Getting Started +=============== +.. toctree:: + :maxdepth: 1 + + Getting Started <getting-started/README.md> + Get a Bugzilla account <getting-started/bugzilla.md> + Create a development profile <getting-started/development-profiles.md> + + +Contributing +============ +.. toctree:: + :maxdepth: 1 + + Contributing <contributing.md> + Find bugs to work on <contributing/find-bugs.md> + How to fix a bug <contributing/fixing-bugs.md> + Code reviews <contributing/code-reviews.md> + Landing code <contributing/landing-code.md> + Leveling up <contributing/levelling-up.md> + Coding standards <contributing/coding-standards.md> + Filing good bugs <contributing/filing-good-bugs.md> + Investigating performance issues <contributing/performance.md> + Writing efficient React code <contributing/react-performance-tips.md> + + +Recurring tasks +=============== +.. toctree:: + :maxdepth: 1 + + Release tasks<release.md> + Performance sheriffing<performance-sheriffing.md> + + +Automated tests +=============== +.. toctree:: + :maxdepth: 1 + + Automated tests <tests/README.md> + xpcshell <tests/xpcshell.md> + Chrome mochitests <tests/mochitest-chrome.md> + DevTools mochitests <tests/mochitest-devtools.md> + Node tests <tests/node-tests.md> + Memory Allocation tests </devtools/tests/memory/index.md> + Writing tests <tests/writing-tests.md> + Debugging intermittent failures <tests/debugging-intermittents.md> + Performance tests overview<tests/performance-tests-overview.md> + DAMP Performance tests <tests/performance-tests-damp.md> + Writing a new test <tests/writing-perf-tests.md> + Example <tests/writing-perf-tests-example.md> + Advanced tips <tests/writing-perf-tests-tips.md> + +Files and directories +===================== +.. toctree:: + :maxdepth: 1 + + Files and directories <files/README.md> + Adding New Files <files/adding-files.md> + + +Tool Architectures +================== +.. toctree:: + :maxdepth: 1 + + Inspector Panel Architecture <tools/inspector-panel.md> + Inspector Highlighters <tools/highlighters.md> + Memory <tools/memory-panel.md> + Debugger <tools/debugger-panel.md> + Responsive Design Mode <tools/responsive-design-mode.md> + Console <tools/console-panel.md> + Network </devtools/netmonitor/architecture.md> + Storage <tools/storage.md> + + +Frontend +======== +.. toctree:: + :maxdepth: 1 + + Panel SVGs <frontend/svgs.md> + React <frontend/react.md> + React Guidelines <frontend/react-guidelines.md> + Redux <frontend/redux.md> + Redux Guidelines <frontend/redux-guidelines.md> + Telemetry <frontend/telemetry.md> + Content Security Policy <frontend/csp.md> + + +Backend +======= +.. toctree:: + :maxdepth: 1 + + Remote Debugging Protocol <backend/protocol.md> + Client API <backend/client-api.md> + Debugger API <backend/debugger-api.md> + Backward Compatibility <backend/backward-compatibility.md> + Actors Organization <backend/actor-hierarchy.md> + Handling Multi-Processes in Actors <backend/actor-e10s-handling.md> + Writing Actors With protocol.js <backend/protocol.js.md> + Registering A New Actor <backend/actor-registration.md> + Actor Best Practices <backend/actor-best-practices.md> + +Preferences +=========== +.. toctree:: + :maxdepth: 1 + + Preferences <preferences.md> diff --git a/devtools/docs/contributor/performance-sheriffing.md b/devtools/docs/contributor/performance-sheriffing.md new file mode 100644 index 0000000000..7a9d3e5c0f --- /dev/null +++ b/devtools/docs/contributor/performance-sheriffing.md @@ -0,0 +1,113 @@ +# DevTools performance sheriffing + +On a weekly basis, we should review: +- [DevTools performance alerts from PerfHerder](https://treeherder.mozilla.org/perfherder/alerts?status=0&framework=12&hideDwnToInv=1&page=1) +- [DevTools performance dashboard](https://firefox-dev.tools/performance-dashboard/) + +## PerfHerder alerts + +### Setup + +First of all keep in mind this DevTools documentation only highlights specifics of the DevTools workflow. DevTools team is triaging its own performance alerts and follows a slightly simplified workflow, but in general the whole Performance Sheriffing documentation also applies here. + +Please take a look at the [Performance Sheriffing documentation](https://wiki.mozilla.org/TestEngineering/Performance/Sheriffing) and the [workflow documentation](https://wiki.mozilla.org/TestEngineering/Performance/Sheriffing/Workflow). You should also join the Performance Sheriffing room at [https://chat.mozilla.org/#/room/#perfsheriffs:mozilla.org](https://chat.mozilla.org/#/room/#perfsheriffs:mozilla.org) + +The DevTools documentation will not explain how to use PerfHerder or TreeHerder, but we will try to link to the relevant documentation when possible. + +In order to sheriff DevTools alerts, your Treeherder user needs to belong to the performance sheriffing group, otherwise you will be unable to update the alerts. Take a look at the [documentation to request access](https://www.google.com/url?q=https://wiki.mozilla.org/TestEngineering/Performance/Onboarding%23Sheriffing&sa=D&source=docs&ust=1634550314641000&usg=AOvVaw2aHrCFQ-wyyc-wrJgkfK4q) and you can refer to a [previous Bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1723906) as an example. + +### DevTools alerts + +![DevTools alert example](devtools-alert-example.png) + +Performance tests are a special kind of test which output a set of metrics (timings, memory) which are collected over time by Perfherder. When a significant variance is detected for one of those metrics, an alert will be automatically created by the system + +The list of DevTools performance tests is documented on the [DevTools Performance Tests overview](./tests/performance-tests-overview.md). From this list, most of the alerts will be either related to DAMP or to Memory Leak tests. + +### Weekly sheriffing + +The untriaged DevTools alerts are listed at [https://treeherder.mozilla.org/perfherder/alerts?status=0&framework=12&hideDwnToInv=1&page=1](https://treeherder.mozilla.org/perfherder/alerts?status=0&framework=12&hideDwnToInv=1&page=1). The goal of the weekly sheriffing task will be to triage all those alerts. + +Triaging an alert doesn't mean solving the performance issue but simply acknowledging the alert and either: +- filing a bug +- or marking as won't fix +- or mark as improvement + +As follow up, the most significant and actionable alerts should be presented to the team and you should decide next steps. It is perfectly ok to say that a performance regression is not worth investigating or fixing and close the bug as a consequence. + +### Workflow suggestion + +There is no right or wrong way to sheriff alerts, and a lot of this will depend on your own judgement and knowledge of the recent changes in the codebase. Nevertheless, below are some steps which can help to breakdown the task. + +#### Ignore minor or invalid changes + +![DevTools alert invalid](devtools-alert-invalid.png) + +DevTools tests can be impacted by many platform changes, which will sometimes update the baseline of a subtest of a few percents. If a change is minor (for instance less than 5%) and the pushlog does not contain any DevTools change, you may Acknowledge the test regressions and mark the alert as "Won't Fix". + +We also usually avoid paying too much attention to the "close" tests, which measure the time needed to close the toolbox after using a panel (eg "complicated.styleeditor.close.DAMP"). Those tests are usually noisy and not an area where we put much effort. Unless there was a huge regression, as long as those tests run in a few dozen ms you may skip those. + +#### Regroup alerts + +![DevTools alert regroup](devtools-alert-regroup.png) + +I suggest to then try to regroup alerts which are likely to be caused by the same change. You can identify those by several criteria: +- the datetime of the alerts should be close (for instance same day, just a few hours apart) +- the area of the alerts should be similar (for instance only debugger tests are impacted, only memory tests are impacted) +- the different alerts are about different platforms + +What happens very often, given how noisy the DevTools DAMP tests are, is that an alert will be generated for a different build on Linux and on Windows for instance. Based on your overall knowledge of what landed last week, you can take a look at the pushlog for similar alerts and see if you spot any DevTools change present for all similar alerts + +Once you identified 2 alerts to regroup, use the Reassign feature to merge the alerts. + +#### Investigate an alert + +![DevTools alert investigate](devtools-alert-investigate.png) + +If an alert is valid and should be investigated, you should acknowledge the alert and file a bug, using the "File Bug" feature. + +After the Bug was created, link the bug to the alert on Perfherder using the "Link to Bug" feature. Then we can also perform some early investigation. + +1. You can look at the pushlog of the alert to see if any patch that landed seems directly related. +1. Review which platforms regressed to see if the regression impacted all platforms (windows, linux, macos). Usually a regression from a DevTools change will impact all platforms, so an alert only touching macos might indicate that the regression is rather coming from a platform change. +1. Make sense of which tests have regressed. In case several platforms have reported the same issue, it can be hard to parse the list visually. Use the "filter" input to only see tests for one platform, for instance "linux1804-64-qr". You should then be able to tell which area of our tests have regressed. + +Summarize this information in the Bug filed for the alert, and add it to the weekly DevTools Tools Check-in agenda. You might also add some information in the notes of the alert. + +Unless you already identified the regressing changeset, you should also start backfills starting from the job of the alert. Click on the job link from Perfherder, which should lead you to a DAMP job from autoland. Start a backfill using a Custom Action for the 10 previous autoland jobs, retriggered 5 times. + +#### Note about improvements + +![DevTools alert improvement](devtools-alert-improvement.png) + +If an alert only contains improvements, you should still check if the improvement is expected, especially if it is an important change. Otherwise it could indicate that the test is no longer testing what it should test and it should be investigated. Unless an improvement was expected, we should follow the exact same workflow as for any alert. + +#### Alert notes and alert owner + +![DevTools alert notes and owner](devtools-alert-notes-owner.png) + +This applies to all alerts you are triaging. Try to add a Note for alerts which are not associated with a Bug. The Note can just be a quick explanation saying why an alert is being ignored, or where a given improvement might come from. And assign yourself to the alert using the "Take" button. + +### Perfherder tips + +Again please refer to the [main documentation for Perfherder](https://wiki.mozilla.org/TestEngineering/Performance/Sheriffing/Workflow). But there are some UX issues you might run into with the DevTools workflow and worth pointing out. + +#### Unable to change the state of an alert + +Each alert lists several tests which might have improved or regressed. Both the alert and tests have a state, for instance "untriaged", "invalid", … The state for the alert seems partly derived from the state of the individual tests. And sometimes it can be difficult to either update the state of the tests or the state of the alert correctly. + +A common issue occurs after regrouping alerts. At that point the tests which were moved to another alert get the "reassigned" state. Imagine you want to move all the tests from this alert to "acknowledged". If you select all tests using the checkbox on the left of the alert title, you will not see the button to "Acknowledge" the tests. This is because you can't move tests from the "reassigned" state to the "acknowledged" state. Instead here, you have to select only the "untriaged" tests. You can easily do that by selecting "untriaged" in the dropdown next to the checkbox. Once you only have untriaged tests selected, you should be able to change their state as expected. And an alert with only "reassigned" and "acknowledged" tests will be considered as acknowledged. + +From time to time, alerts also don't seem to offer the expected action in the top right dropdown. In that case it's probably best to raise this problem on [https://chat.mozilla.org/#/room/#perfsheriffs:mozilla.org](https://chat.mozilla.org/#/room/#perfsheriffs:mozilla.org) , but you might also try resetting the tests in the alert to see if you manage to unblock the state. + +#### Already triaged alert coming back + +Sometimes an alert which was already triaged will be assigned a new test change (regression or improvement). This new test will most likely have a status of "untriaged", which will move back the alert in the untriaged category. If an alert seems familiar, pay attention to the date and check the status of individual tests, there might just be a few new tests added, which need to be assigned the same state as the others. + +## DevTools performance dashboard + +We used to rely mostly on the DevTools performance dashboard for monitoring, but alerts are now the main way we detect regressions or improvements. But the DevTools dashboard still offer a nice way to visualize DevTools performance over time, in a centralized place. + +The DevTools dashboard relies on the same perfherder data as the alerts, so we should not expect to gather too much new information from this tool. The data used comes from tests running on mozilla-central, on windows platforms for DAMP and on linux for metrics or memory tests. Whereas the alerts use mostly data from autoland and check all platforms. Weekly review of this information should be done very quickly. + +The homepage [https://firefox-dev.tools/performance-dashboard/](https://firefox-dev.tools/performance-dashboard/) offers links to individual pages presenting charts grouped by panel or by test domain (eg DAMP, metrics test, ...). I suggest to quickly open each link in a different tab and then scroll through to see if any significant regression pops up. diff --git a/devtools/docs/contributor/preferences.md b/devtools/docs/contributor/preferences.md new file mode 100644 index 0000000000..c0dd98d82e --- /dev/null +++ b/devtools/docs/contributor/preferences.md @@ -0,0 +1,82 @@ +# Preferences + +This documentation aims at giving an overview of the preferences API used in DevTools, it +is not an actual documentation about the list of preferences available in DevTools. + +## Overview + +Preferences allows you to save and read strings, numbers, booleans to the preferences +store, which is tied to a profile. A preference can also have a default value. + +The technical solution for handling preferences differs depending whether you are +testing DevTools as Firefox panel, or a standalone tool running with Launchpad. + +## Preference types + +DevTools relies on nsIPrefBranch for preferences, which supports different types of +preferences: +* `Int` +* `Boolean` +* `Char` +* `String` + +Choose the appropriate type depending on the data you need to store. If you need to store +a JavaScript object or array, the recommended way is to: +* use a `String` type preference +* use JSON.stringify to save +* use JSON.parse to read + +Note that nsIPrefBranch also supports a `Complex` type, but this type is not supported +when running in Launchpad. + +## Reading and updating preferences + +### API docs for nsIPrefBranch and nsIPrefService + +DevTools relies on Services.pref to handle preferences. You can access the API docs for +this service at: +* [Source for nsIPrefBranch](https://searchfox.org/mozilla-central/source/modules/libpref/nsIPrefBranch.idl) +* [Source for nsIPrefService](https://searchfox.org/mozilla-central/source/modules/libpref/nsIPrefService.idl) + +If you are using Launchpad, note that only a subset of nsIPrefService methods are +implemented (addObserver and removeObserver). Launchpad relies on a Services shim file +provided by devtools-module ([code on GitHub](https://github.com/firefox-devtools/devtools-core/blob/master/packages/devtools-modules/src/Services.js)). + +### Services.pref.get* and Services.pref.set* + +The main APIs you will have to know and use are getters and setters. +* `Services.pref.getIntPref(prefName, defaultValue);` This method will throw if the +preference cannot be found and you didn't pass a default value! +* `Services.pref.setIntPref(prefName, prefValue)` This method will throw if the provided +value does not match the preference type! + +These APIs are very similar for each preference type. + +## Create a new preference + +Debugger-specific preferences should go in +devtools/client/preferences/debugger.js. Beyond that, most new preferences +should go in browser/app/profile/firefox.js, which is for desktop Firefox only. +If a preference should be available even when the client for DevTools is not +shipped (for instance on Fennec) it should go in modules/libpref/init/all.js, +which is for preferences that go in all products. + +### Projects using Launchpad + +At the time of writing this doc, projects using Launchpad have to duplicate the default +definition of a preference. +* debugger.html: update [src/utils/prefs.js](https://github.com/firefox-devtools/debugger.html/blob/master/src/utils/prefs.js) +* netmonitor: update [index.js](http://searchfox.org/mozilla-central/source/devtools/client/netmonitor/index.js) +* webconsole: update [local-dev/index.js](http://searchfox.org/mozilla-central/source/devtools/client/webconsole/local-dev/index.js) + +## Inspect preferences + +Depending on the project you are working on, preferences are stored differently but can +always be inspected. + +In Firefox, you can open a tab to about:config and search by preference name. + +In Launchpad, preferences are actually saved to localStorage. Open DevTools on your +Launchpad application and inspect the local storage content. You should see entries +prefixed by `Services.prefs:`. You will only see preferences where a user-specific value +has overridden the default value. diff --git a/devtools/docs/contributor/release.md b/devtools/docs/contributor/release.md new file mode 100644 index 0000000000..c88124513c --- /dev/null +++ b/devtools/docs/contributor/release.md @@ -0,0 +1,147 @@ +# Recurring DevTools tasks + +There are a few things we should do on each Nightly cycle to keep our code clean and up-to-date. + +## Update MDN data for the Compatibility panel + +Follow instructions from [devtools/client/inspector/compatibility/README.md](https://searchfox.org/mozilla-central/source/devtools/client/inspector/compatibility/README.md). + +## Generate webidl-pure-allowlist.js and webidl-deprecated-list.js + +The `webidl-pure-allowlist.js` file is used by the console instant evaluation, in order to know +if a given method does not have side effects. The file might be updated if new APIs are added, +or if methods are tagged as pure (or untagged). + +The `webidl-deprecated-list.js` file will be used to avoid calling deprecated getters from devtools code. + +1. Generating those files requires a non-artifact build. If you're mostly working with artifact builds, you might want to run `./mach bootstrap` in order to have a proper build environment. +2. Once the build is over, you should be able to follow instructions at the top of [GenerateDataFromWebIdls.py](https://searchfox.org/mozilla-central/source/devtools/shared/webconsole/GenerateDataFromWebIdls.py), which should be: + 2.1. Run the script with `./mach python devtools/shared/webconsole/GenerateDataFromWebIdls.py` + +## Remove backwards compatibility code + +In order to accommodate connecting to older server, we sometimes need to introduce specific branches in the code. At the moment, we only support connecting to server 2 versions older than the client (e.g. if the client is 87, we support connecting to 86 and 85). +This means that on each release there's an opportunity to cleanup backward compatibility code that was introduced for server we don't have to support anymore. If I go back to my example with the 87 client, we can remove any backward-compatibility code that was added in 85. + +Luckily, when adding compatibility code, we also add comments that follow a specific pattern: `@backward-compat { version XX }`, where `XX` is the version number the code is supporting. + +Back to our example where the current version is 87, we need to list all the comments added for 85. This can be done by doing a search with the following expression: `@backward-compat { version 85 }` (here's the searchfox equivalent: [searchfox query](https://searchfox.org/mozilla-central/search?q=%40backward-compat%5Cs*%7B%5Cs*version+85%5Cs*%7D&path=&case=false®exp=true)). + +Try to file a specific bug for each backward compatibility code you are removing (you can have broader bugs though, for example if you are removing a trait). Those bugs should block a META bug that will reference all the cleanups. You can check if a bug already exists in the main cleanup META bug ([Bug 1677944](https://bugzilla.mozilla.org/show_bug.cgi?id=1677944)), and if not, you can create it by visiting [this bugzilla link](https://bugzilla.mozilla.org/enter_bug.cgi?format=__default__&blocked=1677944&product=DevTools&component=General&short_desc=[META]%20Cleanup%20backward%20compatibility%20code%20added%20in%20YY&comment=YY%20is%20now%20in%20release,%20so%20we%20can%20remove%20any%20backward%20compatibility%20code%20that%20was%20added%20to%20support%20older%20servers&keywords=meta&bug_type=task) (make sure to replace `YY` with a version number that is equal to the current number minus 2; so if current release is 87, YY is 87 - 2 = 85). + +## Smoke test remote debugging + +### Setup + +We will run the remote debugging smoke tests twice. Once to exercise backward compatibility, and once without backward compatibility (same version). The tests to run are the same in both cases (see Tests section). + +You can use either desktop or mobile versions of Firefox as the server. Mobile is preferable as some codepaths are specific to Firefox mobile, but if you don't have access to an Android device, using a Desktop server is a decent alternative. + +- [Instructions](https://firefox-source-docs.mozilla.org/devtools-user/about_colon_debugging/index.html#connecting-to-a-remote-device) to setup remote debugging for Firefox mobile. +- [Instructions](https://gist.github.com/juliandescottes/b0d3d83154d9ea8a84db5d32aa35d2c1) to setup remote debugging for Firefox desktop. + +#### Backward compatibility test + +- Start the current Nightly (release XX) as Client +- Prepare Firefox (release XX -1) as the Server. Either + [https://play.google.com/store/apps/details?id=org.mozilla.firefox_beta](https://play.google.com/store/apps/details?id=org.mozilla.firefox_beta) (mobile beta) or + Desktop Beta or DevEdition + +#### Same version test + +- Start the current Nightly (release XX) as Client +- Prepare Firefox (also for release XX) as the Server. Either + [https://play.google.com/store/apps/details?id=org.mozilla.fenix](https://play.google.com/store/apps/details?id=org.mozilla.fenix) (mobile nightly) + or Desktop Nightly + +### Tests + +#### Basic connection: + +- On the Client Firefox Nightly, open about:debugging +- Connect to the Server (either via network or USB) +- Open the corresponding Runtime Page + +#### Debug targets: + +- On the Server Firefox, open a tab to [https://mdn.github.io/dom-examples/service-worker/simple-service-worker/](https://mdn.github.io/dom-examples/service-worker/simple-service-worker/) +- On the Client Firefox, check in the Runtime Page for the Server Firefox that you can see the new tab as well as the corresponding service worker +- On the Client Firefox, open the Profiler by clicking the Profile Performance button and record a short profile by clicking the Start, then the Stop button. Verify that the profiler opens a new tab with the recording. +- On the Server Firefox, close the tab you just opened +- On the Client Firefox, check that the corresponding tab is removed +- On the Client Firefox, unregister the service worker, check that the corresponding SW is removed from the list + +#### Inspect a remote target: + +- On the Server Firefox, open a tab to [https://juliandescottes.github.io/webcomponents-playground/debugger-example/](https://juliandescottes.github.io/webcomponents-playground/debugger-example/) +- On the Client Firefox, click on Inspect for this tab. Check that toolbox opens. Now we will verify that the toolbox is working. +- Open Inspector, check that no panel is blank. Check that selecting another element in the markup-view updates the computed view. +- Open Console, check that you see the "script loaded" message. Type "1+1" in the console, check you get "2". +- Open Debugger, check that you can see the script.js source. Open it, put a breakpoint inside the clickMe() method (line 6). On the Server Firefox, click on the button in the page, check that you hit the breakpoint. +- Open the Network tab. If it is empty and tells you to "perform a request…", reload the page on the Server Firefox. Check that requests are displayed. + +#### Inspect a remote extension: + +- On the Server Firefox, install any extension (for instance [https://addons.mozilla.org/en-US/firefox/addon/devtools-highlighter/](https://addons.mozilla.org/en-US/firefox/addon/devtools-highlighter/)) +- On the Client Firefox, check the extension is displayed in the Extensions category +- Click on Inspect, check the toolbox opens. +- Check the Inspector, Console, Debugger and Netmonitor UIs for empty panels. + +## Remove expired or renew telemetry probes + +Bugs are automatically filed for all expired probes. You can find them using [this bugzilla query](https://bugzilla.mozilla.org/buglist.cgi?quicksearch=[probe-expiry-alert]%20devtools). + +We should review the list of bugs and make sure each of them block the following [META bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1566383). + +Reviewing a probe means either to update the expiration field because we are still monitoring the data, or to remove the probe and all the related code for recording it. This discussion can happen on the bug. + +## Check if third-party library should be updated + +This is not a mandatory task to do on each cycle, but having up-to-date libraries can help us getting new features and most importantly bug fixes that will improve the user experience of DevTools users. + +### WasmDis and WasmParser + +These modules are used by the debugger to be able to parse and debug WASM sources. + +Follow the [upgrade documentation](https://searchfox.org/mozilla-central/source/devtools/client/shared/vendor/WASMPARSER_UPGRADING) + +### jsbeautify + +This module is used by the inspector and the webconsole to pretty print user input. + +Follow the [upgrade documentation](https://searchfox.org/mozilla-central/source/devtools/shared/jsbeautify/UPGRADING.md) + +### CodeMirror should be updated + +CodeMirror is used by our source editor component, which is used all over DevTools. + +Follow the [upgrade section in the documentation](https://searchfox.org/mozilla-central/source/devtools/client/shared/sourceeditor/README) + +### fluent-react + +This module is used in several panels to manage localization in React applications. + +Follow the [upgrade documentation](https://searchfox.org/mozilla-central/source/devtools/client/shared/vendor/FLUENT_REACT_UPGRADING) + +### reselect + +Follow the [upgrade documentation](https://searchfox.org/mozilla-central/source/devtools/client/shared/vendor/RESELECT_UPGRADING) + +### pretty-fast + +**TODO** + +## Check xpcshell debugging + +Check that xpcshell tests can be debugged using the `--jsdebugger` option. + +1. Run Firefox from `mozilla-central` using `./mach run` +1. Open an `about:debugging` tab +1. If you don't have a `localhost:6000` item on the left sidebar, click on `Setup` +1. In setup, add a new `localhost:6000` item under `Network Location` +1. From your `mozilla-central` folder, run an xpcshell test with the `--jsdebugger` option (e.g. `./mach test devtools/server/tests/xpcshell/test_front_destroy.js --jsdebugger`) +1. In the terminal, you should see the following message: `Waiting for the debugger to connect on port 6000` +1. Go back to the `about:debugging` tab, click on the `Connect` button next to `localhost:6000`, and click again on the `localhost:6000` item. +1. In the new screen, under the `Processes` section, there should be a `Multiprocess Toolbox` item with an `Inspect` button next to it. Click on the button. +1. This should open an `about:devtools-toolbox` tab, showing the test file, paused at the first breakable line. +1. Make sure you can add breakpoints in the test and that you can step/resume diff --git a/devtools/docs/contributor/resources/DevToolsDeveloperSettings.png b/devtools/docs/contributor/resources/DevToolsDeveloperSettings.png Binary files differnew file mode 100644 index 0000000000..4ed135e626 --- /dev/null +++ b/devtools/docs/contributor/resources/DevToolsDeveloperSettings.png diff --git a/devtools/docs/contributor/resources/box-model-highlighter-screenshot.png b/devtools/docs/contributor/resources/box-model-highlighter-screenshot.png Binary files differnew file mode 100644 index 0000000000..1c64b38768 --- /dev/null +++ b/devtools/docs/contributor/resources/box-model-highlighter-screenshot.png diff --git a/devtools/docs/contributor/resources/expand-strokes.gif b/devtools/docs/contributor/resources/expand-strokes.gif Binary files differnew file mode 100644 index 0000000000..9e15767c5f --- /dev/null +++ b/devtools/docs/contributor/resources/expand-strokes.gif diff --git a/devtools/docs/contributor/resources/pathfinder.gif b/devtools/docs/contributor/resources/pathfinder.gif Binary files differnew file mode 100644 index 0000000000..73d376b103 --- /dev/null +++ b/devtools/docs/contributor/resources/pathfinder.gif diff --git a/devtools/docs/contributor/resources/sketch-position.png b/devtools/docs/contributor/resources/sketch-position.png Binary files differnew file mode 100644 index 0000000000..bcc9ac6689 --- /dev/null +++ b/devtools/docs/contributor/resources/sketch-position.png diff --git a/devtools/docs/contributor/resources/thread-states.png b/devtools/docs/contributor/resources/thread-states.png Binary files differnew file mode 100644 index 0000000000..1808345270 --- /dev/null +++ b/devtools/docs/contributor/resources/thread-states.png diff --git a/devtools/docs/contributor/styles/website.css b/devtools/docs/contributor/styles/website.css new file mode 100644 index 0000000000..eeab68a9aa --- /dev/null +++ b/devtools/docs/contributor/styles/website.css @@ -0,0 +1,67 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +.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: 0.85em 0; +} + +.book .book-body .page-wrapper .page-inner section.normal pre { + line-height: 1.25em; +} + +/* Resets default style on the main page container */ +.page-inner { + max-width: unset !important; + margin: 0 10px !important; +} + +/* + * Sections are displayed on the grid. By default everything goes into the + * second column, and we use left and right columns to expand content when needed, + * for example for images, diagrams, code example, … + */ +.normal.markdown-section { + display: grid; + grid-template-columns: 1fr minmax(min-content, 800px) 1fr; + margin: 0 50px; +} + +.normal.markdown-section > * { + grid-column: 2 / 3; +} + +/* Hero element take the whole row */ +.normal.markdown-section > .hero { + grid-column: 1 / -1; + width: max-content; + max-width: 100%; + justify-self: center; +} + +.diagram, +pre.diagram { + width: max-content; + max-width: 100%; + background-color: #f9f9fa; + border: 3px solid #d7d7db; + margin: 0 auto !important; + padding: 2em; + overflow-x: auto; + white-space: pre; + font-size: 11px; +} + +figcaption { + max-width: 800px; + font-size: 0.85em !important; + text-align: center; + font-style: italic; + margin: 10px auto 0; + line-height: 1.25; +} diff --git a/devtools/docs/contributor/tests/README.md b/devtools/docs/contributor/tests/README.md new file mode 100644 index 0000000000..d981361b28 --- /dev/null +++ b/devtools/docs/contributor/tests/README.md @@ -0,0 +1,22 @@ +# Automated tests + +When working on a patch for DevTools, there's almost never a reason not to add a new test. If you are fixing a bug, you probably should write a new test to prevent this bug from occurring again. If you're implementing a new feature, you should write new tests to cover the aspects of this new feature. + +Ask yourself: +* Are there enough tests for my patch? +* Are they the right types of tests? + +We use three suites of tests: + +* [`xpcshell`](xpcshell.md): Unit-test style of tests. No browser window, only a JavaScript shell. Mostly testing APIs directly. +* [Chrome mochitests](mochitest-chrome.md): Unit-test style of tests, but with a browser window. Mostly testing APIs that interact with the DOM. +* [DevTools mochitests](mochitest-devtools.md): Integration style of tests. Fires up a whole browser window with every test and you can test clicking on buttons, etc. + + +To run all DevTools tests, regardless of suite type: + +```bash +./mach test devtools/* +``` + +Have a look at the child pages for more specific commands for running only a single suite or single test in a suite. diff --git a/devtools/docs/contributor/tests/debugging-intermittents.md b/devtools/docs/contributor/tests/debugging-intermittents.md new file mode 100644 index 0000000000..d82c35bdb6 --- /dev/null +++ b/devtools/docs/contributor/tests/debugging-intermittents.md @@ -0,0 +1,84 @@ +# Debugging Intermittent Test Failures + +## What are Intermittents (aka Oranges)? + +Intermittents are test failures which happen intermittently, in a seemingly random way. Often you'll write a test that passes fine locally on your computer, but when ran thousands of times on various CI environments (some of them under heavy load) it may start to fail randomly. + +Intermittents are also known as Oranges, because the corresponding test jobs are rendered orange on [treeherder](http://treeherder.mozilla.org/). + +These intermittent failures are tracked in Bugzilla. When a test starts being intermittent a bug is filed in Bugzilla (usually by a Mozilla code sheriff). + +Once the bug exists for a given test failure, all further similar failures of that test will be reported as comments within that bug. +These reports are usually posted weekly and look like this: + +> 5 failures in 2740 pushes (0.002 failures/push) were associated with this bug in the last 7 days. + +See [an example here](https://bugzilla.mozilla.org/show_bug.cgi?id=1250523#c4). + +Sometimes, tests start failing more frequently and these reports are then posted daily. + +To help with the (unfortunately) ever-growing list of intermittents, the Stockwell project was initiated a while ago (read more about the goals of that project on [their wiki](https://wiki.mozilla.org/Auto-tools/Projects/Stockwell)). + +This project defines a scenario where very frequently failing tests get disabled. +Ideally, we should try to avoid this, because this means reducing our test coverage, but sometimes we do not have time to investigate the failure, and disabling it is the only remaining option. + +## Finding Intermittents + +You will have no trouble finding out that a particular test is intermittent, because a bug for it will be filed and you will see it in Bugzilla ([watching the Bugzilla component of your choice](https://bugzilla.mozilla.org/userprefs.cgi?tab=component_watch) is a good way to avoid missing the failure reports). + +However, it can still be useful to see intermittents in context. The [Intermittent Failures View on Treeherder](https://treeherder.mozilla.org/intermittent-failures.html) shows intermittents ranked by frequency. + +You can also see intermittents in Bugzilla. Go to [the settings page](https://bugzilla.mozilla.org/userprefs.cgi?tab=settings) and enable "When viewing a bug, show its corresponding Orange Factor page". + +## Reproducing Test Failures locally + +The first step to fix an intermittent is to reproduce it. + +Sometimes reproducing the failure can only be done in automation, but it's worth trying locally, because this makes it much simpler to debug. + +First, try running the test in isolation. You can use the `--repeat` and `--run-until-failure` flags to `mach mochitest` to automate this a bit. It's nice to do this sort of thing in headless mode (`--headless`) or in a VM (or using Xnest on Linux) to avoid locking up your machine. + +Sometimes, though, a test will only fail if it is run in conjunction with one or more other tests. You can use the `--start-at` and `--end-at` flags with `mach mochitest` to run a group of tests together. + +For some jobs, but not all, you can get an [interactive shell from TaskCluster](https://jonasfj.dk/2016/03/one-click-loaners-with-taskcluster/). + +There's also a [handy page of e10s test debugging tips](https://wiki.mozilla.org/Electrolysis/e10s_test_tips) that is worth a read. + +Because intermittents are often caused by race conditions, it's sometimes useful to enable Chaos Mode. This changes timings and event orderings a bit. The simplest way to do this is to enable it in a specific test, by +calling `SimpleTest.testInChaosMode`. You can also set the `MOZ_CHAOSMODE` environment variable, or even edit `mfbt/ChaosMode.cpp` directly. + +Some tests leak intermittently. Use `ac_add_options --enable-logrefcnt` in your mozconfig to potentially find them.<!--TODO: how? add more detail about this --> + +The `rr` tool has [its own chaos mode](http://robert.ocallahan.org/2016/02/introducing-rr-chaos-mode.html). This can also sometimes reproduce a failure that isn't ordinarily reproducible. While it's difficult to debug JS bugs using `rr`, often if you can reliably reproduce the failure you can at least experiment (see below) to attempt a fix. + +## That Didn't Work + +If you couldn't reproduce locally, there are other options. + +One useful approach is to add additional logging to the test, then push again. Sometimes log buffering makes the output weird; you can add a call to `SimpleTest.requestCompleteLog()` to fix this. + +You can run a single directory of tests on try using `mach try DIR`. You can also use the `--rebuild` flag to retrigger test jobs multiple times; or you can also do this easily from treeherder.<!--TODO: how? and why is it easy?--> + +## Solving + +If a test fails at different places for each failure it might be a timeout. The current mochitest timeout is 45 seconds, so if successful runs of an intermittent are ~40 seconds, it might just be a +real timeout. This is particularly true if the failure is most often seen on the slower builds, for example Linux 32 debug. In this case you can either split the test or call `requestLongerTimeout` somewhere at the beginning of the test (here's [an example](https://searchfox.org/mozilla-central/rev/c56977420df7a1b692ce0f7e499ddb364d9fd7b2/devtools/client/framework/test/browser_toolbox_tool_remote_reopen.js#12)). + +Sometimes the problem is a race at a specific spot in the test. You can test this theory by adding a short wait to see if the failure goes away, like: +```javascript +yield new Promise(r => setTimeout(r, 100)); +``` + +See the `waitForTick` and `waitForTime` functions in `DevToolsUtils` for similar functionality. + +You can use a similar trick to "pause" the test at a certain point. This is useful when debugging locally because it will leave Firefox open and responsive, at the specific spot you've chosen. Do this +using `yield new Promise(r => r);`. + +`shared-head.js` also has some helpers, like `once`, to bind to events with additional logging. + +You can also binary search the test by either commenting out chunks of it, or hacking in early `return`s. You can do a bunch of these experiments in parallel without waiting for the first to complete. + +## Verifying + +It's difficult to verify that an intermittent has truly been fixed. +One thing you can do is push to try, and then retrigger the job many times in treeherder. Exactly how many times you should retrigger depends on the frequency of the failure. diff --git a/devtools/docs/contributor/tests/mochitest-chrome.md b/devtools/docs/contributor/tests/mochitest-chrome.md new file mode 100644 index 0000000000..5417999baa --- /dev/null +++ b/devtools/docs/contributor/tests/mochitest-chrome.md @@ -0,0 +1,13 @@ +# Automated tests: chrome mochitests + +To run the whole suite of chrome mochitests: + +```bash +./mach mochitest -f chrome --tag devtools +``` + +To run a specific chrome mochitest: + +```bash +./mach mochitest devtools/path/to/the/test_you_want_to_run.html +``` diff --git a/devtools/docs/contributor/tests/mochitest-devtools.md b/devtools/docs/contributor/tests/mochitest-devtools.md new file mode 100644 index 0000000000..e5f44ba1d6 --- /dev/null +++ b/devtools/docs/contributor/tests/mochitest-devtools.md @@ -0,0 +1,36 @@ +# Automated tests: DevTools mochitests + +To run the whole suite of browser mochitests for DevTools (sit back and relax): + +```bash +./mach mochitest --subsuite devtools --tag devtools +``` +To run a specific tool's suite of browser mochitests: + +```bash +./mach mochitest devtools/client/<tool> +``` + +For example, run all of the debugger browser mochitests: + +```bash +./mach mochitest devtools/client/debugger +``` +To run a specific DevTools mochitest: + +```bash +./mach mochitest devtools/client/path/to/the/test_you_want_to_run.js +``` +Note that the mochitests *must* have focus while running. The tests run in the browser which looks like someone is magically testing your code by hand. If the browser loses focus, the tests will stop and fail after some time. (Again, sit back and relax) + +In case you'd like to run the mochitests without having to care about focus and be able to touch your computer while running: + +```bash +./mach mochitest --headless devtools/client/<tool> +``` + +You can also run just a single test: + +```bash +./mach mochitest --headless devtools/client/path/to/the/test_you_want_to_run.js +``` diff --git a/devtools/docs/contributor/tests/node-tests.md b/devtools/docs/contributor/tests/node-tests.md new file mode 100644 index 0000000000..2e2f205a30 --- /dev/null +++ b/devtools/docs/contributor/tests/node-tests.md @@ -0,0 +1,97 @@ +# DevTools node tests + +In addition to mochitests and xpcshell tests, some panels in DevTools are using node test libraries to run unit tests. For instance, several panels are using [Jest](https://jestjs.io/) to run React component unit tests. + +## Find the node tests on Try + +The DevTools node tests are split in two different test suites on try: +- `node(devtools)`: all the DevTools node tests, except the ones for the debugger +- `node(debugger)`: only the Debugger node tests + +They are running on the `Linux 64 opt` platform. They are both tier 1 jobs, which means that any failure will lead to a backout. + +## Run Tests On Try + +To run the DevTools node tests on try, you can use `./mach try fuzzy` and look for the jobs named `source-test-node-debugger-tests` and `source-test-node-devtools-tests`. + +They are also run when using the "devtools" preset: `./mach try --preset devtools`. + +### Node tests try job definition + +The definition of those try jobs can be found at [taskcluster/ci/source-test/node.yml](https://searchfox.org/mozilla-central/source/taskcluster/ci/source-test/node.yml). + +The definition also contains the list of files that will trigger the node test jobs. Currently the debugger tests run when any file is modified under `devtools/client/debugger`, the devtools tests run when any file is modified under `devtools/client` or `devtools/shared`. + +## Run Tests Locally + +### Prerequisite: yarn + +You will need yarn to be installed in order to run both the debugger and the DevTools tests. See [https://yarnpkg.com/getting-started](https://yarnpkg.com/getting-started). + +### Debugger + +To run the debugger node tests: +``` +> cd devtools/client/debugger/ +> yarn && node bin/try-runner.js +``` + +Note that the debugger is running other tasks than just unit tests: `flow`, `eslint`, `stylelint` etc... +Using `yarn && yarn test` would only run the Jest tests, while `node bin/try-runner` will run the same tests and scripts as the ones used on try. + +### DevTools + +To run the other (non-debugger) DevTools tests, the easiest is to rely on the same script as the one used to run the tests on try: +``` +> node devtools/client/bin/devtools-node-test-runner.js --suite={suitename} +``` + +At the moment of writing, the supported suites for this script are: +- `aboutdebugging` +- `accessibility` +- `application` +- `compatibility` +- `framework` +- `netmonitor` +- `performance` +- `shared_components` +- `webconsole` + +(You can see the full list and the associated configuration in devtools/client/bin/devtools-node-test-runner.js) + +Alternatively, you can also locate the `package.json` corresponding to a given suite, and run `yarn && yarn test`. + +## Updating snapshots + +Some of the node tests are snapshot tests, which means they compare the output of a given component to a previous text snapshot. They might break if you are legitimately modifying a component and it means the snapshots need to be updated. + +A snapshot failure will show up as follows: +``` +› 1 snapshot failed from 1 test suite +``` + +It should also mention the command you can run to update the snapshots: +``` +Inspect your code changes or run `yarn run test-ci -u` to update them. +``` + +For example, if you need to update snapshots in a specific panel, first locate the package.json corresponding to the node test folder of the panel. In theory it should be under `devtools/client/{panelname}/test/node/` but it might be slightly different depending on each panel. Then run `yarn run test-ci -u` in this folder and add the snapshot changes to your commit. + +## TypeScript + +The "performance" suite performs TypeScript checks. The TypeScript usage in the performance panel is documented at [devtools/client/performance-new/typescript.md](https://searchfox.org/mozilla-central/source/devtools/client/performance-new/typescript.md) ([see rendered version on GitHub](https://github.com/mozilla/gecko-dev/blob/master/devtools/client/performance-new/typescript.md)). + +## devtools-bundle + +The devtools-bundle job is a tier2 job which checks if DevTools bundles are outdated. DevTools bundles are generated JavaScript files built from other dependencies in tree in order to run in specific environments (typically a worker). + +All the bundles used by DevTools are generated by devtools/client/debugger/bin/bundle.js. The devtools-bundle job is simply running this script and fails if any versioned file is updated. + +In order to fix a failure, you should run the script: + +``` +> cd devtools/client/debugger/ +> yarn && node bin/bundle.js +``` + +And commit the changes, either in the commit which updated the bundle dependencies, or in a separate commit in order to keep things separated. diff --git a/devtools/docs/contributor/tests/perfherder-compare-link.png b/devtools/docs/contributor/tests/perfherder-compare-link.png Binary files differnew file mode 100644 index 0000000000..8e253bf363 --- /dev/null +++ b/devtools/docs/contributor/tests/perfherder-compare-link.png diff --git a/devtools/docs/contributor/tests/perfherder-compare.png b/devtools/docs/contributor/tests/perfherder-compare.png Binary files differnew file mode 100644 index 0000000000..55ba3b3c5d --- /dev/null +++ b/devtools/docs/contributor/tests/perfherder-compare.png diff --git a/devtools/docs/contributor/tests/perfherder-create-gecko-profile.png b/devtools/docs/contributor/tests/perfherder-create-gecko-profile.png Binary files differnew file mode 100644 index 0000000000..a7526bb25f --- /dev/null +++ b/devtools/docs/contributor/tests/perfherder-create-gecko-profile.png diff --git a/devtools/docs/contributor/tests/perfherder-damp.png b/devtools/docs/contributor/tests/perfherder-damp.png Binary files differnew file mode 100644 index 0000000000..e8b853adb7 --- /dev/null +++ b/devtools/docs/contributor/tests/perfherder-damp.png diff --git a/devtools/docs/contributor/tests/perfherder-filter-subtests.png b/devtools/docs/contributor/tests/perfherder-filter-subtests.png Binary files differnew file mode 100644 index 0000000000..c33187d556 --- /dev/null +++ b/devtools/docs/contributor/tests/perfherder-filter-subtests.png diff --git a/devtools/docs/contributor/tests/perfherder-subtests.png b/devtools/docs/contributor/tests/perfherder-subtests.png Binary files differnew file mode 100644 index 0000000000..fbe90299ac --- /dev/null +++ b/devtools/docs/contributor/tests/perfherder-subtests.png diff --git a/devtools/docs/contributor/tests/performance-tests-damp.md b/devtools/docs/contributor/tests/performance-tests-damp.md new file mode 100644 index 0000000000..c2cdb3a2e8 --- /dev/null +++ b/devtools/docs/contributor/tests/performance-tests-damp.md @@ -0,0 +1,193 @@ +# Performance Tests: DAMP + +DAMP (DevTools At Maximum Performance) is our test suite to track performance. + +## How to run it locally? + +```bash +./mach talos-test --suite damp +``` +Note that the first run is slower as it pulls a large tarball with various website copies. +This will run all DAMP tests, you can filter by test name with: +```bash +./mach talos-test --suite damp --subtests console +``` +This command will run all tests which contains "console" in their name. + +Note that in continuous integration, DAMP tests are split in smaller tests suites: `damp-inspector`, `damp-other` and `damp-webconsole`. Actually `--suite damp` is only used locally because it contains all possible tests and makes it easier to use. But if needed you can substitute `damp` with any of the other test suites if you want to only run tests associated to a given test suite. You can find the mapping between tests and test suites in [damp-tests.js](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/damp-tests.js). +### Command line options + +#### Running tests only once + +```bash +./mach talos-test --suite damp --cycles 1 --tppagecycles 1 +``` +`--cycles` will limit the number of Firefox restart to only one, while +`--tppagecycles` will limit the number of test re-run in each firefox start to one. +This is often helpful when debugging one particular subtest. + +#### Taking screenshots + +```bash +DEBUG_DEVTOOLS_SCREENSHOTS=1 ./mach talos-test --suite damp +``` +When passing `DEBUG_DEVTOOLS_SCREENSHOTS` env variable, screenshots will be taken after each subtest +was run. The screenshot will be opened in new tabs and their title +includes the subtest label. Firefox won't automatically close so that you can view the screenshots. + +#### Recording a profile + +```bash +./mach talos-test --suite damp --gecko-profile --gecko-profile-entries 100000000 +``` +This will automatically record the tests and open the profile. You may use the following command in order +to focus on just one subtest run: +```bash +./mach talos-test --suite damp --subtests custom.webconsole --cycles 1 --tppagecycles 1 --gecko-profile --gecko-profile-entries 100000000 +``` + +## How to run it on try? + +```bash +./mach try fuzzy --query "'test-linux1804-64-shippable-qr/ 'damp" --rebuild 6 +``` +* Linux appears to build and run quickly, and offers quite stable results over the other OSes. +The vast majority of performance issues for DevTools are OS agnostic, so it doesn't really matter which one you run them on. +* "damp" is the talos bucket in which we run DAMP. +* And 6 is the number of times we run DAMP tests. That's to do averages between all the 6 runs and helps filtering out the noise. + +## How to get performance profiles on try? + +Once you have a successful try job for `damp`: +* select this job in treeherder +* click on the `...` menu in the bottom left +* select "Create Gecko Profile" + +![PerfHerder Create Gecko Profile menu](perfherder-create-gecko-profile.png) + +This should start a new damp job called `damp-p`. Once `damp-p` is finished: +* select the `damp-p` job +* click on `Job Details` tab +* click on `open in Firefox Profiler` + +## What does it do? + +DAMP measures three important operations: +* Open a toolbox +* Reload the web page +* Close the toolbox +It measures the time it takes to do each of these operations for the following panels: + +inspector, console, netmonitor debugger, memory, performance. + +It runs all these three tests two times. Each time against a different web page: +* "simple": an empty webpage. This test highlights the performance of all tools against the simplest possible page. +* "complicated": a copy of bild.de website. This is supposed to represent a typical website to debug via DevTools. + +Then, there are a couple of extra tests: +* "cold": we run the three operations (open toolbox, page reload and close toolbox) first with the inspector. +This is run first after Firefox's startup, before any other test. +This test allows to measure a "cold startup". When a user first interacts with DevTools, many resources are loaded and cached, +so that all next interactions will be significantly faster. +* and many other smaller tests, focused on one particular feature or possible slowness for each panel. + +## How to see the results from try? + +First, open TreeHerder. A link is displayed in your console when executing `./mach try`. +You should also receive a mail with a link to it. + +Look for "T-e10s(+6)", click on "+6", then click on "damp": +![TreeHerder jobs](perfherder-damp.png) + +On the bottom panel that just opened, click on "Compare result against another revision". +![TreeHerder panel](perfherder-compare-link.png) + +You are now on PerfHerder, click on "Compare", +![PerfHerder compare](perfherder-compare.png) + +Next to "Talos" select menu, in the filter textbox, type "damp". +Under "damp opt e10s" item, mouse over the "linux64" line, click on "subtests" link. +![PerfHerder filter](perfherder-filter-subtests.png) + +And here you get the results for each DAMP test: +![PerfHerder subtests](perfherder-subtests.png) + +On this page, you can filter by test name with the filter box on top of the result table. +This table has the following columns: +* Base: + Average time it took to run the test on the base build (by default, the last 2 days of DAMP runs on mozilla-central revisions) +* New: + Average time it took to run the test on the new build, the one with your patches. + Both "Base" and "New" have a "± x.xx%" suffix which tells you the variance of the timings. + i.e. the average difference in percent between the median timing and both the slowest and the fastest. +* Delta: + Difference in percent between the base and new runs. + The color of this can be red, orange or green: + * Red means "certainly regressing" + * Orange means "possibly regressing" + * Green means "certainly improving" + * No colored background means "nothing to conclude" + The difference between certainly and possibly is explained by the next column. +* Confidence: + If there is a significant difference between the two runs, tells if the results is trustworthy. + * "low" either means there isn't a significant difference between the two runs, or the difference is smaller than the typical variance of the given test. + If the test is known to have an execution time varying by 2% between two runs of the same build, and you get a 1% difference between your base and new builds, + the confidence will be low. You really can't make any conclusion. + * "med" means medium confidence and the delta is around the size of the variance. It may highlight a regression, but it can still be justified by the test noise. + * "high" means that this is a high confidence difference. The delta is significantly higher than the typical test variance. A regression is most likely detected. + +There is also "Show only important changes" checkbox, which helps seeing if there is any significant regression. +It will only display regressions and improvements with a medium or high confidence. + +## How to contribute to DAMP? + +DAMP is based on top of a more generic test suite called [Talos](https://wiki.mozilla.org/Buildbot/Talos). +Talos is a Mozilla test suite to follow all Firefox components performance. +It is written in Python and here are [the sources](https://searchfox.org/mozilla-central/source/testing/talos/) in mozilla-central. +Compared to the other test suites, it isn't run on the cloud, but on dedicated hardware. +This is to ensure performance numbers are stable over time and between two runs. +Talos runs various types of tests. More specifically, DAMP is a [Page loader test](https://wiki.mozilla.org/Buildbot/Talos/Tests#Page_Load_Tests). +The [source code](http://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/) for DAMP is also in mozilla-central. +See [Writing new performance test](./writing-perf-tests.md) for more information about the implementation of DAMP tests. + +## How to see the performance trends? + +You can find the dedicated performance dashboard for DevTools at http://firefox-dev.tools/performance-dashboard. You will find links to trend charts for various tools: +* [Inspector dashboard](http://firefox-dev.tools/performance-dashboard/tools/inspector.html?days=60&filterstddev=true) +* [Console dashboard](http://firefox-dev.tools/performance-dashboard/tools/console.html?days=60&filterstddev=true) +* [Netmonitor dashboard](http://firefox-dev.tools/performance-dashboard/tools/netmonitor.html?days=60&filterstddev=true) +* [Debugger dashboard](http://firefox-dev.tools/performance-dashboard/tools/debugger.html?days=60&filterstddev=true) + +Each tool page displays charts for all the subtests relevant for a given panel. + +Each circle on the chart is a push to mozilla-central. You can hover on a circle to see some additional information about the push, such as the date, the performance impact for the subtest, and the push id. Clicking on a circle will take you to the pushlog. + +Colored circles indicate that the push contains a change that was identified as having a performance impact. Those can be categorized as: +- hardware: hardware change for the machines used to run Talos +- platform: non-DevTools change that impacts DevTools performance +- damp: test change in DAMP that impacts test results +- devtools: identified DevTools change that introduced an improvement or regression + +This data is synchronized from a [shared Google doc](https://docs.google.com/spreadsheets/d/12Goo3vq-0X0_Ay-J6gfV56pUB8GC0Nl62I4p8G-UsEA/edit#gid=0). + +There is a PerfHerder link on each chart that will take you to the PerfHerder page corresponding to this subtest. + +## How to use PerfHerder charts + +On PerfHerder charts, each circle is a push on mozilla-central. +When you see a spike or a drop, you can try to identify the patch that relates to it by clicking the circles. +It will show a black popup. Then click on the changeset hash like "cb717386aec8" and you will get a mercurial changelog. +Then it is up to you to read the changelog and see which changeset may have hit the performance. + +For example, open [this page](https://treeherder.mozilla.org/perf.html#/graphs?timerange=31536000&series=mozilla-central,1417969,1,1&series=mozilla-central,1417971,1,1&series=mozilla-central,1417966,1,1&highlightedRevisions=a06f92099a5d&zoom=1482734645161.3916,1483610598216.4773,594.756508587898,969.2883437938906). +This is tracking inspector opening performance against the "Simple" page. +![Perfherder graphs](regression-graph.png) + +See the regression on Dec 31th? +Now, click on the first yellow circle of this spike. +You will get a black popup like this one: +![Perfherder changeset popup](regression-popup.png) + +Click on the [changelog link](https://hg.mozilla.org/mozilla-central/pushloghtml?fromchange=9104708cc3ac0ccfe4cf5d518e13736773c565d7&tochange=a06f92099a5d8edeb05e5971967fe8d6cd4c593c) to see which changesets were added during this run. Here, you will see that the regression comes from these patches: + * Bug 1245921 - Turn toolbox toolbar into a React component + * Bug 1245921 - Monkey patch ReactDOM event system for XUL diff --git a/devtools/docs/contributor/tests/performance-tests-overview.md b/devtools/docs/contributor/tests/performance-tests-overview.md new file mode 100644 index 0000000000..20a9716a24 --- /dev/null +++ b/devtools/docs/contributor/tests/performance-tests-overview.md @@ -0,0 +1,103 @@ +# DevTools Performance Tests overview + +This page provides a short overview of the various DevTools performance tests. + +## damp + +DAMP (short for DevTools At Maximum Performance) is the main DevTools performance test suite, based on the talos framework. It mostly runs end to end scenarios, opening the toolbox, various panels and interacting with the UI. It might regress for a wide variety of reasons: DevTools frontend changes, DevTools server changes, platform changes etc. To investigate DAMP regressions or improvements, it is usually necessary to analyze DAMP subtests individually. + +See [DAMP Performance tests](performance-tests-damp.md) for more details on how to run DAMP, analyze results or add new tests. + +## debugger-metrics + +debugger-metrics measures the number of modules and the overall size of modules loaded when opening the Debugger in DevTools. This test is a mochitest which can be executed locally with: + +```bash +./mach test devtools/client/framework/test/metrics/browser_metrics_debugger.js --headless +``` + +At the end of the test, logs should contain a `PERFHERDER_DATA` entry containing 4 measures. `debugger-modules` is the number of debugger-specific modules loaded, `debugger-chars` is the number of characters in said modules. `all-modules` is the number of modules loaded including shared modules, `all-chars` is the number of characters in said modules. + +A significant regression or improvement to this test can indicate that modules are no longer lazy loaded, or a new part of the UI is now loaded upfront. + +## inspector-metrics + +See the description for debugger-metrics. This test is exactly the same but applied to the inspector panel. It can be executed locally with: + +```bash +./mach test devtools/client/framework/test/metrics/browser_metrics_inspector.js --headless +``` + +## netmonitor-metrics + +See the description for debugger-metrics. This test is exactly the same but applied to the netmonitor panel. It can be executed locally with: + +```bash +./mach test devtools/client/framework/test/metrics/browser_metrics_netmonitor.js --headless +``` + +## webconsole-metrics + +See the description for debugger-metrics. This test is exactly the same but applied to the webconsole panel. It can be executed locally with: + +```bash +./mach test devtools/client/framework/test/metrics/browser_metrics_webconsole.js --headless +``` + +## server.pool + +server.pool measures the performance of the DevTools `Pool` [class](https://searchfox.org/mozilla-central/source/devtools/shared/protocol/Pool.js) which is intensively used by the DevTools server. This test is a mochitest which can be executed with: + +```bash +./mach test devtools/client/framework/test/metrics/browser_metrics_pool.js --headless +``` + +At the end of the test, logs should contain a `PERFHERDER_DATA` entry which contain values corresponding to various APIs of the `Pool` class. + +A regression or improvement in this test is most likely linked to a change in a file from devtools/shared/protocol. + +## toolbox:parent-process + +toolbox:parent-process measures the number of objects allocated by DevTools after opening and closing a DevTools toolbox. This test is a mochitest which can be executed with: + +```bash +./mach test devtools/client/framework/test/allocations/browser_allocations_toolbox.js --headless +``` + +The test will record allocations while opening and closing the Toolbox several times. The `PERFHERDER_DATA` entry in the logs will contain 3 measures. objects-with-stacks is the number of allocated objects for which the allocation site is known and should be easy to fix for developers. objects-with-no-stacks is for objects for which there is no stack. memory attempts to record the memory, but the results are very noisy. You can refer to devtools/client/framework/test/allocations/README.md for a more detailed description of this test and how to use it to investigate and fix memory issues. + +A regression here may indicate a leak, for instance a module which no longer cleans its dependencies. It can also indicate that DevTools is loading more singletons or other objects which are not tied to the lifecycle of the DevTools objects. + +## target:parent-process + +target:parent-process measures the number of objects created by DevTools to create a tab target. It does not involve DevTools frontend. This test is a mochitest which can be executed with: + +```bash +./mach test devtools/client/framework/test/allocations/browser_allocations_target.js --headless +``` + +See the description for toolbox:parent-process for more information. + +## reload:parent-process + +target:parent-process measures the number of objects created by DevTools when reloading a page inspected by a DevTools Toolbox. This test is a mochitest which can be executed with: + +```bash +./mach test devtools/client/framework/test/allocations/browser_allocations_reload.js --headless +``` + +See the description for toolbox:parent-process for more information. Note that this test also records another suite, reload:content-process. + +## reload:content-process + +See the description for reload:parent-process. + +## browser-console:parent-process + +browser-console:parent-process measures the number of objects created by DevTools when opening and closing the Browser Console. This test is a mochitest which can be executed with: + +```bash +./mach test devtools/client/framework/test/allocations/browser_allocations_browser_console.js --headless +``` + +See the description for toolbox:parent-process for more information. diff --git a/devtools/docs/contributor/tests/regression-graph.png b/devtools/docs/contributor/tests/regression-graph.png Binary files differnew file mode 100644 index 0000000000..3212324012 --- /dev/null +++ b/devtools/docs/contributor/tests/regression-graph.png diff --git a/devtools/docs/contributor/tests/regression-popup.png b/devtools/docs/contributor/tests/regression-popup.png Binary files differnew file mode 100644 index 0000000000..e64d55df0d --- /dev/null +++ b/devtools/docs/contributor/tests/regression-popup.png diff --git a/devtools/docs/contributor/tests/writing-perf-tests-example.md b/devtools/docs/contributor/tests/writing-perf-tests-example.md new file mode 100644 index 0000000000..5f68d46406 --- /dev/null +++ b/devtools/docs/contributor/tests/writing-perf-tests-example.md @@ -0,0 +1,68 @@ +# Performance test example: performance of click event in the inspector + +Let's look at a trivial but practical example and add a simple test to measure the performance of a click in the inspector. + +First we create a file under [tests/inspector](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/tests/inspector) since we are writing an inspector test. We call the file `click.js`. + +We will use a dummy test document here: `data:text/html,click test document`. + +We prepare the imports needed to write the test, from head.js and inspector-helper.js: +- `testSetup`, `testTeardown`, `openToolbox` and `runTest` from head.js +- `reloadInspectorAndLog` from inspector-helper.js + +The full code for the test looks as follows: +``` +const { + reloadInspectorAndLog, +} = require("devtools/docs/tests/inspector-helpers"); + +const { + openToolbox, + runTest, + testSetup, + testTeardown, +} = require("devtools/docs/head"); + +module.exports = async function() { + // Define here your custom document via a data URI: + const url = "data:text/html,click test document"; + + await testSetup(url); + const toolbox = await openToolbox("inspector"); + + const inspector = toolbox.getPanel("inspector"); + const window = inspector.panelWin; // Get inspector's panel window object + const body = window.document.body; + + await new Promise(resolve => { + const test = runTest("inspector.click"); + body.addEventListener("click", function () { + test.done(); + resolve(); + }, { once: true }); + body.click(); + }); + + // Check if the inspector reload is impacted by click + await reloadInspectorAndLog("click", toolbox); + + await testTeardown(); +} +``` + +Finally we add an entry in [damp-tests.js](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/damp-tests.js): +``` + { + name: "inspector.click", + path: "inspector/click.js", + description: + "Measure the time to click in the inspector, and reload the inspector", + }, +``` + +Since this is an inspector test, we add it under `TEST_SUITES.INSPECTOR`, which contains all the tests which will run with the `damp-inspector` test suite in continuous integration. The test is still part of the overall `damp` suite by default, there is no action needed to ensure that. + +Then we can run our test with: +``` +./mach talos-test --suite damp --subtest inspector.click +``` diff --git a/devtools/docs/contributor/tests/writing-perf-tests-tips.md b/devtools/docs/contributor/tests/writing-perf-tests-tips.md new file mode 100644 index 0000000000..9cb300805d --- /dev/null +++ b/devtools/docs/contributor/tests/writing-perf-tests-tips.md @@ -0,0 +1,41 @@ +# How to write a good performance test? + +## Verify that you wait for all asynchronous code + +If your test involves asynchronous code, which is very likely given the DevTools codebase, please review carefully your test script. +You should ensure that _any_ code ran directly or indirectly by your test is completed. +You should not only wait for the functions related to the very precise feature you are trying to measure. + +This is to prevent introducing noise in the test run after yours. If any asynchronous code is pending, +it is likely to run in parallel with the next test and increase its variance. +Noise in the tests makes it hard to detect small regressions. + +You should typically wait for: +* All RDP requests to finish, +* All DOM Events to fire, +* Redux action to be dispatched, +* React updates, +* ... + + +## Ensure that its results change when regressing/fixing the code or feature you want to watch. + +If you are writing the new test to cover a recent regression and you have a patch to fix it, push your test to try without _and_ with the regression fix. +Look at the try push and confirm that your fix actually reduces the duration of your perf test significantly. +If you are introducing a test without any patch to improve the performance, try slowing down the code you are trying to cover with a fake slowness like `setTimeout` for asynchronous code, or very slow `for` loop for synchronous code. This is to ensure your test would catch a significant regression. + +For our click performance test, we could do this from the inspector codebase: +``` +window.addEventListener("click", function () { + + // This for loop will fake a hang and should slow down the duration of our test + for (let i = 0; i < 100000000; i++) {} + +}, true); // pass `true` in order to execute before the test click listener +``` + + +## Keep your test execution short. + +Running performance tests is expensive. We are currently running them 25 times for each changeset landed in Firefox. +Aim to run tests in less than a second on try. diff --git a/devtools/docs/contributor/tests/writing-perf-tests.md b/devtools/docs/contributor/tests/writing-perf-tests.md new file mode 100644 index 0000000000..9f027d8ee8 --- /dev/null +++ b/devtools/docs/contributor/tests/writing-perf-tests.md @@ -0,0 +1,144 @@ +# Writing new DAMP performance tests + +See [DAMP Performance tests](performance-tests-damp.md) for an overall description of our performance tests. +Here, we will describe how to write a new test and register it to run in DAMP. + +```note:: + | **Reuse existing tests if possible!** + | + | If a `custom` page already exists for the tool you are testing, try to modify the existing `custom` test rather than adding a new individual test. + | + | New individual tests run separately, in new tabs, and make DAMP slower than just modifying existing tests. Complexifying `custom` test pages should also help cover more scenarios and catch more regressions. For those reasons, modifying existing tests should be the preferred way of extending DAMP coverage. + | + | `custom` tests are using complex documents that should stress a particular tool in various ways. They are all named `custom.${tool}` (for instance `custom.inspector`). The test pages for those tests can be found in [pages/custom](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/pages/custom). + | + | If your test case requires a dedicated document or can't run next to the other tests in the current `custom` test, follow the instructions below to add a new individual test. +``` + +This page contains the general documentation for writing DAMP tests. See also: +- [Performance test writing example](writing-perf-tests-example.md) for a practical example of creating a new test +- [Performance test writing tips](writing-perf-tests-tips.md) for detailed tips on how to write a good and efficient test + +## Test location + +Tests are located in [testing/talos/talos/tests/devtools/addon/content/tests](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/tests). You will find subfolders for panels already tested in DAMP (debugger, inspector, …) as well as other subfolders for tests not specific to a given panel (server, toolbox). + +Tests are isolated in dedicated files. Some examples of tests: +- [tests/netmonitor/simple.js](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/simple.js) +- [tests/inspector/mutations.js](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/tests/inspector/mutations.js) + +## Basic test + +The basic skeleton of a test is: + +``` +const { + testSetup, + testTeardown, + SIMPLE_URL, +} = require("devtools/docs/head"); + +module.exports = async function() { + await testSetup(SIMPLE_URL); + + // Run some measures here + + await testTeardown(); +}; +``` + +* always start the test by calling `testSetup(url)`, with the `url` of the document to use +* always end the test with `testTeardown()` + + +## Test documents + +DevTools performance heavily depends on the document against which DevTools are opened. There are two "historical" documents you can use for tests for any panel: +* "Simple", an empty webpage. This one helps highlighting the load time of panels, +* "Complicated", a copy of bild.be, a German newspaper website. This allows us to examine the performance of the tools when inspecting complicated, big websites. + +The URL of those documents are exposed by [tests/head.js](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/tests/head.js). The Simple page can be found at [testing/talos/talos/tests/devtools/addon/content/pages/simple.html](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/pages/simple.html). The Complicated page is downloaded via [tooltool](https://wiki.mozilla.org/ReleaseEngineering/Applications/Tooltool) automatically the first time you run the DAMP tests. + +You can create also new test documents under [testing/talos/talos/tests/devtools/addon/content/pages](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/pages). See the pages in the `custom` subfolder for instance. If you create a document in `pages/custom/mypanel/index.html`, the URL of the document in your tests should be `PAGES_BASE_URL + "custom/mypanel/index.html"`. The constant `PAGES_BASE_URL` is exposed by head.js. + +Note that modifying any existing test document will most likely impact the baseline for existing tests. + +Finally you can also create very simple test documents using data urls. Test documents don't have to contain any specific markup or script to be valid DAMP test documents, so something as simple as `testSetup("data:text/html,my test document");` is valid. + + +## Test helpers + +Helper methods have been extracted in shared modules: +* [tests/head.js](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/tests/head.js) for the most common ones +* tests/{subfolder}/{subfolder}-helpers.js for folder-specific helpers ([example](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/tests/inspector/inspector-helpers.js)) + +To measure something which is not covered by an existing helper, you should use `runTest`, exposed by head.js. + +``` +module.exports = async function() { + await testSetup(SIMPLE_URL); + + // Calling `runTest` will immediately start recording your action duration. + // You can execute any necessary setup action you don't want to record before calling it. + const test = runTest(`mypanel.mytest.mymeasure`); + + await doSomeThings(); // <== Do an action you want to record here + + // Once your action is completed, call `runTest` returned object's `done` method. + // It will automatically record the action duration and appear in PerfHerder as a new subtest. + // It also creates markers in the profiler so that you can better inspect this action in + // profiler.firefox.com. + test.done(); + + await testTeardown(); +}; +``` + +If your measure is not simply the time spent by an asynchronous call (for instance computing an average, counting things…) there is a lower level helper called `logTestResult` which will directly log a value. See [this example](https://searchfox.org/mozilla-central/rev/325c1a707819602feff736f129cb36055ba6d94f/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/streamlog.js#62). + + +## Test runner + +If you need to dive into the internals of the DAMP runner, most of the logic is in [testing/talos/talos/tests/devtools/addon/content/damp.js](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/damp.js). + + +# How to name your test and register it? + +If a new test file was created, it needs to be registered in the test suite. To register the new test, add it in [damp-tests.js](https://searchfox.org/mozilla-central/source/testing/talos/talos/tests/devtools/addon/content/damp-tests.js). This file acts as the manifest for the DAMP test suite. + +If your are writing a test executing against Simple and Complicated documents, your test name will look like: `(simple|complicated).${tool-name}.${test-name}`. +So for our example, it would be `simple.inspector.click` and `complicated.inspector.click`. +For independent tests that don't use the Simple or Complicated documents, the test name only needs to start with the tool name, if the test is specific to that tool +For the example, it would be `inspector.click`. + +In general, the test name should try to match the path of the test file. As you can see in damp-tests.js this naming convention is not consistently followed. We have discrepancies for simple/complicated/custom tests, as well as for webconsole tests. This is largely for historical reasons. + +You will see that tests are split across different subsuites: damp-inspector, damp-other and damp-webconsole. The goal of this split is to run DAMP tests in parallel in CI, so we aim to keep them balanced in terms of number of tests, and mostly running time. Add your test in the suite which makes the most sense. We can add more suites and rearrange tests in the future if needed. + + +# How to run your new test? + +You can run any performance test with this command: +``` +./mach talos-test --suite damp --subtest ${your-test-name} +``` + +By default, it will run the test 25 times. In order to run it just once, do: +``` +./mach talos-test --suite damp --subtest ${your-test-name} --cycles 1 --tppagecycles 1 +``` +`--cycles` controls the number of times Firefox is restarted +`--tppagecycles` defines the number of times we repeat the test after each Firefox start + +Also, you can record a profile while running the test. To do that, execute: +``` +./mach talos-test --suite damp --subtest ${your-test-name} --cycles 1 --tppagecycles 1 --gecko-profile --gecko-profile-entries 100000000 +``` +`--gecko-profile` enables the profiler +`--gecko-profile-entries` defines the profiler buffer size, which needs to be large while recording performance tests + +Once it is done executing, the profile lives in a zip file you have to uncompress like this: +``` +unzip testing/mozharness/build/blobber_upload_dir/profile_damp.zip +``` +Then you have to open [https://profiler.firefox.com/](https://profiler.firefox.com/) and manually load the profile file that lives here: `profile_damp/page_0_pagecycle_1/cycle_0.profile` diff --git a/devtools/docs/contributor/tests/writing-tests.md b/devtools/docs/contributor/tests/writing-tests.md new file mode 100644 index 0000000000..39dac2a99e --- /dev/null +++ b/devtools/docs/contributor/tests/writing-tests.md @@ -0,0 +1,237 @@ +# Automated tests: writing tests + +<!--TODO this file might benefit from being split in other various files. For now it's just taken from the wiki with some edits--> + +## Adding a new browser chrome test + +It's almost always a better idea to create a new test file rather than to add new test cases to an existing one. + +This prevents test files from growing up to the point where they timeout for running too long. Test systems may be under lots of stress at time and run a lot slower than your regular local environment. + +It also helps with making tests more maintainable: with many small files, it's easier to track a problem rather than in one huge file. + +### Creating the new file + +The first thing you need to do is create a file. This file should go next to the code it's testing, in the `tests` directory. For example, an inspector test would go into `devtools/inspector/test/`. + +### Naming the new file + +Naming your file is pretty important to help other people get a feeling of what it is supposed to test. +Having said that, the name shouldn't be too long either. + +A good naming convention is `browser_<panel>_<short-description>[_N].js` + +where: + +* `<panel>` is one of `debugger`, `markupview`, `inspector`, `ruleview`, etc. +* `<short-description>` should be about 3 to 4 words, separated by hyphens (-) +* and optionally add a number at the end if you have several files testing the same thing + +For example: `browser_ruleview_completion-existing-property_01.js` + +Note that not all existing tests are consistently named. So the rule we try to follow is to **be consistent with how other tests in the same test folder are named**. + +### Basic structure of a test + +```javascript +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// A detailed description of what the test is supposed to test + +const TEST_URL = TEST_URL_ROOT + "doc_some_test_page.html"; + +add_task(async function() { + await addTab(TEST_URL_ROOT); + let {toolbox, inspector, view} = await openRuleView(); + await selectNode("#testNode", inspector); + await checkSomethingFirst(view); + await checkSomethingElse(view); +}); + +async function checkSomethingFirst(view) { +/* ... do something ... this function can await */ +} + +async function checkSomethingElse(view) { +/* ... do something ... this function can await */ +} +``` + +### Referencing the new file + +For your test to be run, it needs to be referenced in the `browser.ini` file that you'll find in the same directory. For example: `browser/devtools/debugger/test/browser.ini` + +Add a line with your file name between square brackets, and make sure that the list of files **is always sorted by alphabetical order** (some lists can be really long, so the alphabetical order helps in finding and reasoning about things). + +For example, if you were to add the test from the previous section, you'd add this to `browser.ini`: + +```ini +[browser_ruleview_completion-existing-property_01.js] +``` + +### Adding support files + +Sometimes your test may need to open an HTML file in a tab, and it may also need to load CSS or JavaScript. For this to work, you'll need to... + +1. place these files in the same directory, and also +2. reference them in the `browser.ini` file. + +There's a naming convention for support files: `doc_<support-some-test>.html` + +But again, often names do not follow this convention, so try to follow the style of the other support files currently in the same test directory. + +To reference your new support file, add its filename in the `support-files` section of `browser.ini`, also making sure this section is in alphabetical order. + +Support files can be accessed via a local server that is started while tests are running. This server is accessible at [http://example.com/browser/](http://example.com/browser/). See the `head.js section` below for more information. + +## Leveraging helpers in `head.js` + +`head.js` is a special support file that is loaded in the scope the test runs in, before the test starts. It contains global helpers that are useful for most tests. Read through the head.js file in your test directory to see what functions are there and therefore avoid duplicating code. + +Each panel in DevTools has its own test directory with its own `head.js`, so you'll find different things in each panel's `head.js` file. + +For example, the head.js files in the `markupview` and `styleinspector` test folders contain these useful functions and constants: + +* Base URLs for support files: `TEST_URL_ROOT`. This avoids having to duplicate the http://example.com/browser/browser/devtools/styleinspector/ URL fragment in all tests, +* `waitForExplicitFinish()` is called in `head.js` once and for all<!--TODO: what does this even mean?-->. All tests are asynchronous, so there's no need to call it again in each and every test, +* `auto-cleanup`: the toolbox is closed automatically and all tabs are closed, +* `tab addTab(url)` +* `{toolbox, inspector} openInspector()` +* `{toolbox, inspector, view} openRuleView()` +* `selectNode(selectorOrNode, inspector)` +* `node getNode(selectorOrNode)` +* ... + +## Shared head.js file + +A [shared-head.js](https://searchfox.org/mozilla-central/source/devtools/client/shared/test/shared-head.js) file has been introduced to avoid duplicating code in various `head.js` files. + +It's important to know whether or not the `shared.js` in your test directory already imports `shared-head.js` (look for a <code>Services.scriptloader.loadSubScript</code> call), as common helpers in `shared-head.js` might be useful for your test. + +If you're planning to work on a lot of new tests, it might be worth the time actually importing `shared-head.js` in your `head.js` if it isn't here already. + +## Electrolysis + +E10S is the codename for Firefox multi-process, and what that means for us is that the process in which the test runs isn't the same as the one in which the test content page runs. + +You can learn more about E10S [from this blog post](https://timtaubert.de/blog/2011/08/firefox-electrolysis-101/), [the Electrolysis wiki page](https://wiki.mozilla.org/Electrolysis) and the page on [tests and E10s](https://wiki.mozilla.org/Electrolysis/e10s_test_tips). + +One of the direct consequences of E10S on tests is that you cannot retrieve and manipulate objects from the content page as you'd do without E10S. + +So when creating a new test, if this test needs to access the content page in any way, you can use [the message manager or JSActors](https://firefox-source-docs.mozilla.org/dom/ipc/jsactors.html) to communicate with a script loaded in the content process to do things for you instead of accessing objects in the page directly. + +You can use the helper `ContentTask.spawn()` for this. See [this list of DevTools tests that use that helper for examples](https://searchfox.org/mozilla-central/search?q=ContentTask.spawn%28&path=devtools%2Fclient). + +Note that a lot of tests only need to access the DevTools UI anyway, and don't need to interact with the content process at all. Since the UI lives in the same process as the test, you won't need to use the message manager to access it. + +## Asynchronous tests + +Most browser chrome DevTools tests are asynchronous. One of the reasons why they are asynchronous is that the code needs to register event handlers for various user interactions in the tools and then simulate these interactions. Another reason is that most DevTools operations are done asynchronously via the debugger protocol. + +Here are a few things to keep in mind with regards to asynchronous testing: + +* `head.js` already calls `waitForExplicitFinish()` so there's no need for your new test to do it too. +* Using `add_task` with an async function means that you can await calls to functions that return promises. It also means your main test function can be written to almost look like synchronous code, by adding `await` before calls to asynchronous functions. For example: + +```javascript +for (let test of testData) { + await testCompletion(test, editor, view); +} +``` + +Each call to `testCompletion` is asynchronous, but the code doesn't need to rely on nested callbacks and maintain an index, a standard for loop can be used. + +## Writing clean, maintainable test code + +Test code is as important as feature code itself, it helps avoiding regressions of course, but it also helps understanding complex parts of the code that would be otherwise hard to grasp. + +Since we find ourselves working with test code a large portion of our time, we should spend the time and energy it takes to make this time enjoyable. + +### Logs and comments + +Reading test output logs isn't exactly fun and it takes time but is needed at times. Make sure your test generates enough logs by using: + +``` +info("doing something now") +``` + +it helps a lot knowing around which lines the test fails, if it fails. + +One good rule of thumb is if you're about to add a JS line comment in your test to explain what the code below is about to test, write the same comment in an `info()` instead. + +Also add a description at the top of the file to help understand what this test is about. The file name is often not long enough to convey everything you need to know about the test. Understanding a test often teaches you about the feature itself. + +Not really a comment, but don't forget to "use strict"; + +### Callbacks and promises + +Avoid multiple nested callbacks or chained promises. They make it hard to read the code. Use async/await instead. + +### Clean up after yourself + +Do not expose global variables in your test file, they may end up causing bugs that are hard to track. Most functions in `head.js` return useful instances of the DevTools panels, and you can pass these as arguments to your sub functions, no need to store them in the global scope. +This avoids having to remember nullifying them at the end. + +If your test needs to toggle user preferences, make sure you reset these preferences when the test ends. Do not reset them at the end of the test function though because if your test fails, the preferences will never be reset. Use the `registerCleanupFunction` helper instead. + +It may be a good idea to do the reset in `head.js`. + +### Write small, maintainable code + +Split your main test function into smaller test functions with self explanatory names. + +Make sure your test files are small. If you are working on a new feature, you can create a new test each time you add a new functionality, a new button to the UI for instance. This helps having small, incremental tests and can also help writing test while coding. + +If your test is just a sequence of functions being called to do the same thing over and over again, it may be better to describe the test steps in an array instead and just have one function that runs each item of the array. See the following example + +```javascript +const TESTS = [ + {desc: "add a class", cssSelector: "#id1", makeChanges: async function() {...}}, + {desc: "change href", cssSelector: "a.the-link", makeChanges: async function() {...}}, + ... +]; + +add_task(async function() { + await addTab("..."); + let {toolbox, inspector} = await openInspector(); + for (let step of TESTS) { + info("Testing step: " + step.desc); + await selectNode(step.cssSelector, inspector); + await step.makeChanges(); + } +}); +``` + +As shown in this code example, you can add as many test cases as you want in the TESTS array and the actual test code will remain very short, and easy to understand and maintain (note that when looping through test arrays, it's always a good idea to add a "desc" property that will be used in an info() log output). + +### Avoid exceptions + +Even when they're not failing the test, exceptions are bad because they pollute the logs and make them harder to read. +They're also bad because when your test is run as part of a test suite and if an other, unrelated, test fails then the exceptions may give wrong information to the person fixing the unrelated test. + +After your test has run locally, just make sure it doesn't output exceptions by scrolling through the logs. + +Often, non-blocking exceptions may be caused by hanging protocol requests that haven't been responded to yet when the tools get closed at the end of the test. Make sure you register to the right events and give time to the tools to update themselves before moving on. + +### Avoid test timeouts + +<!--TODO: this recommendation is conflicting with the above recommendation. What? --> +When tests fail, it's far better to have them fail and end immediately with an exception that will help fix it rather than have them hang until they hit the timeout and get killed. + +## Adding new helpers + +In some cases, you may want to extract some common code from your test to use it another another test. + +* If this is very common code that all tests could use, then add it to `devtools/client/shared/test/shared-head.js`. +* If this is common code specific to a given tool, then add it to the corresponding `head.js` file. +* If it isn't common enough to live in `head.js`, then it may be a good idea to create a helper file to avoid duplication anyway. Here's how to create a helper file: + * Create a new file in your test director. The naming convention should be `helper_<description_of_the_helper>.js` + * Add it to the browser.ini support-files section, making sure it is sorted alphabetically + * Load the helper file in the tests + * `browser/devtools/markupview/test/head.js` has a handy `loadHelperScript(fileName)` function that you can use. + * The file will be loaded in the test global scope, so any global function or variables it defines will be available (just like `head.js`). + * Use the special ESLint comment `/* import-globals-from helper_file.js */` to prevent ESLint errors for undefined variables. + +In all cases, new helper functions should be properly commented with an jsdoc comment block. diff --git a/devtools/docs/contributor/tests/xpcshell.md b/devtools/docs/contributor/tests/xpcshell.md new file mode 100644 index 0000000000..e98c9deef7 --- /dev/null +++ b/devtools/docs/contributor/tests/xpcshell.md @@ -0,0 +1,13 @@ +# Automated tests: `xpcshell` tests + +To run all of the xpcshell tests: + +```bash +./mach xpcshell-test --tag devtools +``` + +To run a specific xpcshell test: + +```bash +./mach xpcshell-test devtools/path/to/the/test_you_want_to_run.js +``` diff --git a/devtools/docs/contributor/tools/console-panel.md b/devtools/docs/contributor/tools/console-panel.md new file mode 100644 index 0000000000..68fa24e349 --- /dev/null +++ b/devtools/docs/contributor/tools/console-panel.md @@ -0,0 +1,158 @@ +# Console Tool Architecture + +The Console panel is responsible for rendering all logs coming from the current page. + +## Architecture + +Internal architecture of the Console panel (the client side) is described +on the following diagram. + +<figure class="hero"> + <pre class="diagram"> +┌──────────────────────────────┐ ┌────────────────────────┐ +│ DevTools │ │ WebConsolePanel │ +│[client/framework/devtools.js]│ │ [panel.js] │ +└──────────────────────────────┘ └────────────────────────┘ + │ │ + │ + openBrowserConsole() or │ + toggleBrowserConsole() │ + │ │ + ▼ │ +┌──────────────────────────────┐ {hud} +│ BrowserConsoleManager │ │ +│ [browser-console-manager.js] │ │ +└──────────────────────────────┘ │ + │ │ + │ │ + {_browserConsole} │ + │ │ + ▼ 0..1 ▼ 1 +┌──────────────────────────────┐ ┌────────────────────────┐ +│ BrowserConsole │ │ WebConsole │ +│ [browser-console.js] │─ ─ extends ─ ▶│ [webconsole.js] │ +└──────────────────────────────┘ └──────────────1─────────┘ + │ + {ui} + │ + ▼ 1 + ┌────────────────────────┐ + │ WebConsoleUI │ + │ [webconsole-ui.js] │ + └────────────────────────┘ + │ + {wrapper} + │ + │ + ▼ 1 + ┌────────────────────────┐ + │ WebConsoleWrapper │ + │[webconsole-wrapper.js] │ + └────────────────────────┘ + │ + <renders> + │ + ▼ + ┌────────────────────────┐ + │ App │ + └────────────────────────┘ + </pre> + <figcaption>Elements between curly bracket on arrows represent the property name of the reference (for example, the WebConsolePanel as a `hud` property that is a reference to the WebConsole instance)</figcaption> +</figure> + +## Components + +The Console panel UI is built on top of [React](../frontend/react.md). It defines set of React components in `components` directory +The React architecture is described on the following diagram. + +<figure class="hero"> + <pre class="diagram"> +┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ + WebConsole React components │ +│ [/components] ┌────────────────────────┐ + │ App │ │ +│ └────────────────────────┘ + │ │ +│ │ + ┌───────────────────┬──────────────────────┬───────────────────┬───────────┴─────────┬───────────────────────┬────────────────────┬─────────────────┐ │ +│ │ │ │ │ │ │ │ │ + ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ │ ┌────────────────────────────────────────┐ +│ ┌──────────┐ ┌────────────────┐ ┌────────────────┐ ┌───────────┐ ┌────────────────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────┐ │ Editor │ + │ SideBar │ │NotificationBox │ │ ConfirmDialog │ │ FilterBar │ │ ReverseSearchInput │ │ConsoleOutput │ │EditorToolbar │ │ JSTerm │──.editor───▶│ <CodeMirror> │ +│ └──────────┘ └────────────────┘ │ <portal> │ └───────────┘ └────────────────────┘ └──────────────┘ └──────────────┘ └─────────┘ │ [client/shared/sourceeditor/editor.js] │ + │ └────────────────┘ │ │ │ └────────────────────────────────────────┘ +│ │ ┌─────────┴─────────────┐ │ + │ │ │ │ │ +│ │ ▼ ▼ ▼ + │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ +│ │ │ FilterButton │ │ FilterCheckbox │ │ MessageContainer │ + │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ +│ │ │ + │ │ │ +│ │ │ + │ ▼ │ +│ │ ┌──────────────────┐ + │ │ Message │ │ +│ │ └──────────────────┘ + │ │ │ ┌─────────────────────────────────────┐ +│ │ │ │ Frame │ + │ ┌─────────────────────┬─────────────────────┬─────────────────────┬───────┴─────────────┬─────────────────────┬─────────────┼─────┬──▶│ [client/shared/components/Frame.js] │ +│ │ │ │ │ │ │ │ │ └─────────────────────────────────────┘ + │ ▼ ▼ ▼ ▼ ▼ ▼ │ │ +│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ ┌────────────────────────────────────────┐ + │ │ MessageIndent │ │ MessageIcon │ │ CollapseButton │ │ GripMessageBody │ │ ConsoleTable │ │ MessageRepeat │ │ │ │ SmartTrace │ +│ │ └──────────────────┘ └──────────────────┘ └──────────────────┘ └──────────────────┘ └──────────────────┘ └──────────────────┘ ├──▶│[client/shared/components/SmartTrace.js]│ + │ │ │ │ │ └────────────────────────────────────────┘ +└ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ + │ │ │ │ ┌──────────────────────────────────────────────────┐ + │ │ │ │ │ TabboxPanel │ + │ ├─────────────────────┘ └──▶│[client/netmonitor/src/components/TabboxPanel.js] │ + │ │ └──────────────────────────────────────────────────┘ + │ │ + │ │ + │ ▼ + │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ + │ Reps ┌──────────────────────┐ │ + │ │ [client/shared/components/reps/reps.js] │ ObjectInspector │ + │ └──────────────────────┘ │ + │ │ │ + │ ▼ │ + │ │ ┌──────────────────────┐ + │ │ ObjectInspectorItem │ │ + │ │ └──────────────────────┘ + └───────────────────────────────────────────────────────────────▶ │ │ + │ ▼ + ┌──────────────────────┐ │ + │ ┌─▶│ Rep │ + │ └──────────────────────┘ │ + │ │ │ + │ │ │ + │ └──────────────┘ + ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ + </pre> +</figure> + +There are several external components we use from the WebConsole: +- ObjectInspector/Rep: Used to display a variable in the console output and handle expanding the variable when it's not a primitive. +- Frame: Used to display the location of messages. +- SmartTrace: Used to display the stack trace of messages and errors +- TabboxPanel: Used to render a network message detail. This is directly using the component from the Netmonitor so we are consistent in how we display a request internals. + +## Actions + +The Console panel implements a set of actions divided into several groups. + +- **Filters** Actions related to content filtering. +- **Messages** Actions related to list of messages rendered in the panel. +- **UI** Actions related to the UI state. + +### State + +The Console panel manages the app state via [Redux](../frontend/redux.md). + +There are following reducers defining the panel state: + +- `reducers/filters.js` state for panel filters. These filters can be set from within the panel's toolbar (e.g. error, info, log, css, etc.) +- `reducers/messages.js` state of all messages rendered within the panel. +- `reducers/prefs.js` Preferences associated with the Console panel (e.g. logLimit) +- `reducers/ui.js` UI related state (sometimes also called a presentation state). This state contains state of the filter bar (visible/hidden), state of the time-stamp (visible/hidden), etc. diff --git a/devtools/docs/contributor/tools/debugger-panel.md b/devtools/docs/contributor/tools/debugger-panel.md new file mode 100644 index 0000000000..0e1faa380c --- /dev/null +++ b/devtools/docs/contributor/tools/debugger-panel.md @@ -0,0 +1,3 @@ +# Debugger Tool Architecture + +Documentation for the Debugger tool in currently under construction. diff --git a/devtools/docs/contributor/tools/highlighters.md b/devtools/docs/contributor/tools/highlighters.md new file mode 100644 index 0000000000..3f377e3843 --- /dev/null +++ b/devtools/docs/contributor/tools/highlighters.md @@ -0,0 +1,184 @@ +# Highlighters + +This article provides technical documentation about DevTools highlighters. + +By highlighter, we mean anything that DevTools displays on top of the content page, in order to highlight an element, a set of elements or shapes to users. + +The most obvious form of highlighter is the box-model highlighter, whose job is to display the 4 box-model regions on top of a given element in the content page, as illustrated in the following screen capture: + +![Box-model highlighter](../resources/box-model-highlighter-screenshot.png) + +But there can be a wide variety of highlighters. In particular, highlighters are a pretty good way to give detailed information about: + +* the exact form of a css shape, +* how a css transform applied to an element, +* which are all the elements that match a given selector, +* ... + +## Using highlighters + +Highlighters run on the debuggee side, not on the toolbox side. This is so that it's possible to highlight elements on a remote device for instance. This means you need to go through the [Remote Debugging Protocol](../backend/protocol.md) to use a highlighter. + +The InspectorFront provides the following method: + +| Method | Description | +|----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `getHighlighterByType(typeName)` | Instantiate a new highlighter, given its type (as a String). At the time of writing, the available types of highlighters are: `CssGridHighlighter`, `BoxModelHighlighter`, `CssTransformHighlighter`, `FlexboxHighlighter`, `FontsHighlighter`, `GeometryEditorHighlighter`, `MeasuringToolHighlighter`, `PausedDebuggerOverlay`, `RulersHighlighter`, `SelectorHighlighter` and `ShapesHighlighter`. This returns a promise that resolves to the new instance of [protocol.js](https://wiki.mozilla.org/DevTools/protocol.js) actor. | + +### The highlighter API + +When getting a highlighter via `InspectorFront.getHighlighterByType(typeName)`, the right type of highlighter will be instantiated on the server-side and will be wrapped into a `CustomHighlighterActor` and that's what will be returned to the caller. This means that all types of highlighters share the same following API: + +| Method | Description | +|------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `show(NodeActor node[, Object options])` | Highlighters are hidden by default. Calling this method is what makes them visible. The first, mandatory, parameter should be a NodeActor. NodeActors are what the WalkerActor return. It's easy to get a NodeActor for an existing DOM node. For example `toolbox.walker.querySelector(toolbox.walker.rootNode, "css selector")` resolves to a NodeFront (the client-side version of the NodeActor) which can be used as the first parameter. The second, optional, parameter depends on the type of highlighter being used. | +| `hide()` | Hides the highlighter. | +| `finalize()` | Destroys the highlighter. | + +## Creating new highlighters + +Before digging into how one goes about adding a new type of highlighter to the DevTools, it is worth understanding how are highlighters displayed in the page. + +### Inserting content in the page + +Highlighters use web technology themselves to display the required information on screen. For instance, the box-model highlighter uses SVG to draw the margin, border, padding and content regions over the highlighted node. + +This means the highlighter content needs to be inserted in the page, but in a non-intrusive way. Indeed, the DevTools should never alter the page unless the alteration was done by the user (like changing the DOM using the inspector or a CSS rule via the style-editor for example). So simply appending the highlighter's markup in the content document is not an option. + +Furthermore, highlighters not only need to work with Firefox Desktop, but they should work just as well on Firefox OS, Firefox for Android, and more generally anything that runs the Gecko rendering engine. Therefore appending the highlighter's markup to the browser chrome XUL structure isn't an option either. + +To this end, DevTools highlighters make use of a (chrome-only) API: + +``` + /** + * Chrome document anonymous content management. + * This is a Chrome-only API that allows inserting fixed positioned anonymous + * content on top of the current page displayed in the document. + * The supplied content is cloned and inserted into the document's CanvasFrame. + * Note that this only works for HTML documents. + */ + partial interface Document { + /** + * Deep-clones the provided element and inserts it into the CanvasFrame. + * Returns an AnonymousContent instance that can be used to manipulate the + * inserted element. + */ + [ChromeOnly, NewObject, Throws] + AnonymousContent insertAnonymousContent(Element aElement); + + /** + * Removes the element inserted into the CanvasFrame given an AnonymousContent + * instance. + */ + [ChromeOnly, Throws] + void removeAnonymousContent(AnonymousContent aContent); + }; +``` + +Using this API, it is possible for chrome-privileged JS to insert arbitrary DOM elements on top of the content page. + +Technically, the DOM element is inserted into the `CanvasFrame` of the document. The `CanvasFrame` is part of the rendered frame tree and the DOM element is part of the native anonymous elements of the `CanvasFrame`. + +Consider the following simple example: + +```js + let el = document.createElement("div"); + el.textContent = "My test element"; + let insertedEl = document.insertAnonymousContent(el); +``` + +In this example, the test DIV will be inserted in the page, and will be displayed on top of everything else, in a way that doesn't impact the current layout. + +### The AnonymousContent API + +In the previous example, the returned `insertedEl` object isn't a DOM node, and it certainly is not `el`. It is a new object, whose type is `AnonymousContent` ([see the WebIDL here](https://searchfox.org/mozilla-central/source/dom/webidl/AnonymousContent.webidl)). + +Because of the way content is inserted into the page, it isn't wanted to give consumers a direct reference to the inserted DOM node. This is why `document.insertAnonymousContent(el)` actually **clones** `el` and returns a new object whose API lets consumers make changes to the inserted element in a way that never gives back a reference to the inserted DOM node. + +### CanvasFrameAnonymousContentHelper + +In order to help with the API described in the previous section, the `CanvasFrameAnonymousContentHelper` class was introduced. + +Its goal is to provide a simple way for highlighters to insert their content into the page and modify it dynamically later. One of its goal is also to re-insert the highlighters' content on page navigation. Indeed, the frame tree is destroyed when the page is navigated away from since it represents the document element. One thing to note is that highlighter content insertion is asynchronous and `CanvasFrameAnonymousContentHelper` users must call and wait for its `initialize` method to resolve. + +Using this helper is quite simple: + +```js +let helper = new CanvasFrameAnonymousContentHelper(targetActor, this.buildMarkup.bind(this)); +await helper.initialize(); +``` + +It only requires a `targetActor`, which highlighters get when they are instantiated, and a callback function that will be used to create and insert the content the first time the highlighter is shown, and every time there's a page navigation. + +The returned object provides the following API: + +| Method | Description | +|-------------------------------------------|------------------------------------------------------------| +| `getTextContentForElement(id)` | Get the textContent of an element given its ID. | +| `setTextContentForElement(id, text)` | Set the textContent of an element given its ID. | +| `setAttributeForElement(id, name, value)` | Set an attribute value of an element given its ID. | +| `getAttributeForElement(id, name)` | Get an attribute value of an element given its ID. | +| `removeAttributeForElement(id, name)` | Remove an attribute of an element given its ID. | +| `content` | This property returns the wrapped AnonymousContent object. | +| `destroy()` | Destroy the helper instance. | + + ### Creating a new highlighter class + +A good way to get started is by taking a look at [existing highlighters here](https://searchfox.org/mozilla-central/rev/1a973762afcbc5066f73f1508b0c846872fe3952/devtools/server/actors/highlighters.js#519-530). + +Here is some boilerplate code for a new highlighter class: + +```js + function MyNewHighlighter(targetActor) { + this.doc = targetActor.window.document; + this.markup = new CanvasFrameAnonymousContentHelper(targetActor, this._buildMarkup.bind(this)); + this.markup.initialize(); + } + + MyNewHighlighter.prototype = { + destroy: function() { + this.doc = null; + this.markup.destroy(); + }, + + _buildMarkup: function() { + let container = this.markup.anonymousContentDocument.createElement("div"); + container.innerHTML = '<div id="new-highlighted-" style="display:none;">'; + return container; + }, + + show: function(node, options) { + this.markup.removeAttributeForElement("new-highlighted-el", "style"); + }, + + hide: function() { + this.markup.setAttributeForElement("new-highlighted-el", "style", "display:none;"); + } + }; +``` + +In most situations, the `container` returned by `_buildMarkup` will be absolutely positioned, and will need to contain elements with IDs, so that these can then later be moved, resized, hidden or shown in `show` and `hide` using the AnonymousContent API. + +### The AutoRefreshHighlighter parent class + +It is worth mentioning this class as it may be a useful parent class to inherit a new highlighter from in some situations. + +If the new highlighter's job is to highlight an element in the DOM, then it most likely should inherit from `AutoRefreshHighlighter`. + +The `AutoRefreshHighlighter` class updates itself in a loop, checking if the currently highlighted node's geometry has changed since the last iteration. This is useful to make sure the highlighter **follows** the highlighted node around, in case the layout around it changes, or in case it is an animated node. + +Sub classes must implement the following methods: + +| Method | Description | +|-------------|-------------------------------------------------------------------------------------| +| `_show()` | Called when the highlighter should be shown. | +| `_update()` | Called while the highlighter is shown and the geometry of the current node changes. | +| `_hide()` | Called when the highlighter should be hidden. | + +Sub classes will have access to the following properties: + +| Property | Description | +|---------------------|-------------------------------------------| +| `this.currentNode` | The node to be shown. | +| `this.currentQuads` | All of the node's box model region quads. | +| `this.win` | The current window | diff --git a/devtools/docs/contributor/tools/inspector-panel.md b/devtools/docs/contributor/tools/inspector-panel.md new file mode 100644 index 0000000000..ee8abca83b --- /dev/null +++ b/devtools/docs/contributor/tools/inspector-panel.md @@ -0,0 +1,93 @@ +# 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. +You can find a lot more [documentation about highlighters here](highlighters.md). + +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 diff --git a/devtools/docs/contributor/tools/inspector.md b/devtools/docs/contributor/tools/inspector.md new file mode 100644 index 0000000000..6a3a223da5 --- /dev/null +++ b/devtools/docs/contributor/tools/inspector.md @@ -0,0 +1,2 @@ + +These files provide information about the Inspector panel architecture. diff --git a/devtools/docs/contributor/tools/memory-panel.md b/devtools/docs/contributor/tools/memory-panel.md new file mode 100644 index 0000000000..d04ffecfc2 --- /dev/null +++ b/devtools/docs/contributor/tools/memory-panel.md @@ -0,0 +1,230 @@ +# 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 DevTools Server and the actors that reside in it. Use +of the +[`MemoryActor`](https://searchfox.org/mozilla-central/source/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 +`ChromeUtils.saveHeapSnapshot` function. See `dom/webidl/ChromeUtils.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`](https://searchfox.org/mozilla-central/source/devtools/shared/heapsnapshot/HeapSnapshot.cpp) +and +[`mozilla::devtools::Deserialized{Node,Edge}`](https://searchfox.org/mozilla-central/source/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`](https://searchfox.org/mozilla-central/source/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`](https://searchfox.org/mozilla-central/source/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`](https://searchfox.org/mozilla-central/source/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://firefox-source-docs.mozilla.org/js/test.html#running-jit-tests-locally +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 DevTools 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/xpcshell/*`. + +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. diff --git a/devtools/docs/contributor/tools/responsive-design-mode.md b/devtools/docs/contributor/tools/responsive-design-mode.md new file mode 100644 index 0000000000..8b355c95d7 --- /dev/null +++ b/devtools/docs/contributor/tools/responsive-design-mode.md @@ -0,0 +1,75 @@ +# 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. + +## List of Devices + +RDM is maintaining a list of popular mobile devices that can be used to quickly simulate a particular environment (screen resolution, pixel ratio, user agent, etc.) +[Learn more](/devtools/responsive/devices) about how this list is maintained and how it should be properly updated. diff --git a/devtools/docs/contributor/tools/storage.md b/devtools/docs/contributor/tools/storage.md new file mode 100644 index 0000000000..7a16eb2c65 --- /dev/null +++ b/devtools/docs/contributor/tools/storage.md @@ -0,0 +1,90 @@ +# Storage Panel Architecture + +## Actor structure + +### Legacy + +This is currently only used by the browser toolbox and when inspecting Web Extensions. + +![Class structure architecture (legacy)](storage/legacy.svg) + +- We have an actor per storage type. +- These actors are contained in a pool managed by a global `Storage` actor. See [source code of the actor](https://searchfox.org/mozilla-central/rev/2c991232499e826e46f9d976eb653817340ba389/devtools/server/actors/storage.js#3435) and [source code of the pool](https://searchfox.org/mozilla-central/rev/2c991232499e826e46f9d976eb653817340ba389/devtools/server/actors/storage.js#3477-3489) +- Each specific storage type actor has a reference back to this global `Storage` actor. + +### Resource-based + +This is the new architecture that is being implemented to support Fission. It's currently used when inspecting tabs. + +![Class structure architecture (resource-based)](storage/resources.svg) + +- We no longer have a global `Storage` actor. +- The specific actors for each storage type are spawned by watchers instead. +- The reference to a global `Storage` actor that each actor has now points to a mock instead. +- Some watchers require to be run in the parent process, while others can be run in the content process. + - Parent process: Cookies, IndexedDB, Web Extension[^web-extension-not-implemented]. + - Content process: LocalStorage, SessionStorage, Cache. + +[^web-extension-not-implemented]: Web Extension has not yet been implemented in this new architecture. + +## Flow + +Some considerations to keep in mind: + +- In the Storage Panel, **resources are fronts**. +- These fronts contain a `hosts` object, which is populated with the host name, and the actual storage data it contains. +- In the client, we get as part of the `onAvailable` callback of `ResourceCommand.watchResources`: + - Content process storage types: multiple resources, one per target + - Parent process storage types: a single resource + +### Initial load + +Web page loaded, open toolbox. Later on, we see what happens if a new remote target is added (for instance, an iframe is created that points to a different host). + +#### Fission OFF + +![Initial load diagram, fission off](storage/flow-fission-off.svg) + +- We get all the storage fronts as new resources sent in the `onAvailable` callback for `watchResources`. +- After a remote target has been added, we get new additions as `"single-store-update"` events. + +#### Fission ON + +![Initial load diagram, fission on](storage/flow-fission-on.svg) + +Similar to the previous scenario (fission off), but now when a new remote target is added: + +- We get content process storage resources in a new `onAvailable` callback, instead of `"single-store-update"`. +- Parent process storage resources keep using the `"single-store-update"` method. This is possible due to their `StorageMock` actors emitting a fake `"window-ready"` event after a `"window-global-created"`. + +### Navigation + +#### Fission ON, target switching OFF + +![Navigation diagram, fission on, target switching off](storage/navigation-fission-on-target-switching-off.svg) + +- Deletion of content process storage hosts is handled within the `onTargetDestroyed` callback. +- Deletion of parent process storage hosts is handled with `"single-store-update"` events, fired when the `StorageMock` detects a `"window-global-destroyed"` event. +- When the new target is available, new storage actors are spawned from their watchers' `watch` method and are sent as resources in the `onAvailable` callback. + +#### Fission ON, target switching ON + +![Navigation diagram, fission on, target switching off](storage/navigation-fission-on-target-switching-on.svg) + +Similar to the previous scenario (fission on, target switching off), but parent process storage resources are handled differently, since their watchers remain instantiated. + +- New actors for parent process resources are not spawned by their watchers `watch`, but as a callback of `"window-global-created"`. +- Some times there's a race condition between a target being available and firing `"window-global-created"`. There is a delay to send the resource to the client, to ensure that any `onTargetAvailable` callback is processed first. + - The new actor/resource is sent after a `"target-available-form"` event. + +### CRUD operations + +#### Add a new cookie + +Other CRUD operations work very similar to this one. + +![CRUD operation diagram, add a new cookie](storage/crud-cookie.svg) + +- We call `StorageMock.getWindowFromHost` so we can get the storage principal. Since this is a parent process resource, it doesn't have access to an actual window, so it returns a mock instead (but with a real principal). +- To detect changes in storage, we subscribe to different events that platform provides via `Services.obs.addObserver`. +- To manipulate storage data, we use different methods depending on the storage type. For cookies, we use the API provided by `Services.cookies`. diff --git a/devtools/docs/contributor/tools/storage/crud-cookie.svg b/devtools/docs/contributor/tools/storage/crud-cookie.svg new file mode 100644 index 0000000000..5a4671a0df --- /dev/null +++ b/devtools/docs/contributor/tools/storage/crud-cookie.svg @@ -0,0 +1,6 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1052px" height="527px" viewBox="-0.5 -0.5 1052 527" content="<mxfile host="Electron" modified="2021-06-09T15:53:37.452Z" agent="5.0 (Macintosh; Intel Mac OS X 11_4_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" version="14.6.13" etag="EwVAYTYE5uQF3ssIPhho" type="device"><diagram id="Dm-Yc-wTLX_n6glO8A4T">5Vtbb6M4FP41kboPjTDmEh6bdGZnpB1ptNVodh5dcAKqwVlwmmZ//dpgErBJahLSTNq+JD6QA3znO1fTEZylL3/maBl/oxEmI9uKXkbwfmTbAFge/xCSTSUJbKcSLPIkkiftBA/Jf1gKLSldJREuWicySglLlm1hSLMMh6wlQ3lO1+3T5pS0r7pEC6wJHkJEdOnPJGJx/VxesDvwBSeLWF56YvvVgRTVJ8snKWIU0XVDBD+N4CynlFXf0pcZJgK8Gpfqd5/3HN3eWI4zZvIDu/rBMyIr+WwPjOb89u9C/vmNhk8jmxsLcuTh9Ebq5RcY/yHvn21qUPijLMXXVUr+SuaYJBlfTZc4T1LMcM6PECn+vpNNuYkY4jJxHJRrQtCySB5LteKqOQ5XeZE8479xUTGhlNJVFuFIrrYwlguW06etYYRS+ZA4Z/hlL1BgCz/nLab8BvMNP0X+wPGlxTZbElfrdYMAlpTFDdtDVwqRJN1iq3tnF/5FmqbbTFAzkwb/kiYZKy/hTkfuvQI9zVlMFzRDpAn+8CDah0F0WxhuvaCFoQ6hPQCCzj6i//j6wZgMgksy2b0WJjt9mOz7Y/fNqOxpEK4KDgJPdyQJnwqRDGPcCNv8678rkVCmKIrKrEifEryTctHjijGaaZYQECU87d2RZJFx0SPlp6UlpihndyKTCovwO+EynEW15JGIxCFPk+l7YmoXHGnJ19Qq1thya3hyTBDjvtZO/h2wS23fBet2qm4n1h4L1zoKuspDLH/WTLGKpr1UqRVxkBaYHVBUn0jn8wIzjSpbHIzY42vsmZV8KJqJHons/9FSvDu5ZGCcXEtg9PsExsAsw3sDABi8SwDB5O0QrPVqseEnYmEsUsyHigfQOzYewCGMAa6Fz8A6CKNa9LtmhPYPpGxjDPX29nfF8HBmAm0Mt8x8BUM4BIZ670kz0Tvh4scyQgxriMYsJfLZ9xaQHcXinIcFWSuWRQjHJt/8I2AdB269/iVhLhf3L63VprlqmKsSviRMKLsVNaJc/2oc2+kSi01joWoadVepVTnY5l1V2bX92cDqDavW0f/EanYvgXoXswCOPd/a/gFV7RiaVbdHVK5A7+FZnHNGEe6KKgm5s4nepsHFyjNnlFBhyYyWuWqeEKKIkORqyM1UertK4jSJInGZ6TpOGH5YotLs6xwttdDQ4PRkoDmUY3VbskEa90yNJ7ia5v0whp7d9gbHmhi2746tNm9H4ah38DhN2M2uJS+SbEHwbSHC7O1Khtn6oN6XDR1xeZAErYg7thz/lKhbh/A66I69wD9L4K13A5qB171g4HXstrs6QFFhGng98IqiAeOsPiK4Sje3oeLmwNTN4TBurrf0snI6c+G0K3VA2+t8F/b2umYZ1o4Jnm8fExNM3NjtcGO/r/2tsePUNcPwnu0rnu2pDYupZ2/Tda3I0LPPNB8E+hxF1EXcYs9y76+cNa+6Kfxbll0EPWIyReHTojytvvDIhvPyb6DKTBkfdlcVwZlqM1uf3aAo+spweoYwExxM7SeEmJPbPIPg0hFbJhesEGxlymRbR7Zm0HlF0XAVQv2U114hqG2x6Yb0EPO9GrIGhtw6P5OMP/PnnKZfaMHO5br7UvpJJX4rCEDb7R0GDHx3ovtuD6ufYa6i5G4tlBuX9/AVRQM6rz7QW5es47K0fOHnNNLRJeaHphEq4tKVq04yauTjfXQEcjTXKDHdUygJlFGfVDc0KXsw8FS6qSxxjqWbMgGxYWC4K30M4TrevcH5cxKKPWeFa+97R8m32vZ72x3m63n3pt/LN8Axy9pgCAz10d37wNA3w3CQVkWfL1WvJBXj8gWlt21XJqD/JLI1KD19b+rIwueSc01PbTbUAGWaifzXFA2Yh7rmcdULDrMYZYuOXaRhuaftPY771yOD1u4mxY2nMy+4IPF8tQQKji2B1NJdVTQg8fSRWjU+u2m9min493Z7PFqxfUqlrY+aPe8sGzyBzkZ4yTCo1NGwvsG+bHQ1RZOB2MiXu38nqU7f/VMO/PQ/</diagram></mxfile>" style="background-color: rgb(255, 255, 255);"><defs/><g><rect x="555" y="0" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 630 60 L 630 525" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="629.5" y="26.5">StorageActorMock</text><text x="629.5" y="47.5">(parent p.)</text></g><rect x="622.5" y="330" width="15" height="30" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="135" y="0" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 210 60 L 210 525" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="209.5" y="37">StorageUI</text></g><rect x="202.5" y="116.25" width="15" height="30" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 21 116.25 L 190.32 116.25" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="15" cy="116.25" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 200.82 116.25 L 190.32 121.5 L 190.32 111 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="16.5px"><rect fill="#ffffff" stroke="none" x="43" y="74" width="145" height="41" stroke-width="0"/><text x="113.5" y="88.75">user clicks the</text><text x="113.5" y="108.25">"add cookie" button</text></g><rect x="720" y="0" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 795 60 L 795 525" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="794.5" y="26.5">Cookies</text><text x="794.5" y="47.5">(actor)</text></g><rect x="787.5" y="135" width="15" height="90" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="787.5" y="270" width="15" height="90" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="390" y="0" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 465 60 L 465 495" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="464.5" y="37">CookieWatcher</text></g><rect x="457.5" y="375" width="15" height="106.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="622.5" y="375" width="15" height="46.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 621.75 375 L 483.93 375" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 473.43 375 L 483.93 369.75 L 483.93 380.25 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 247px; margin-left: 365px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoresUpdate</div></div></div></foreignObject><text x="365" y="247" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoresUpdate</text></switch></g><rect x="510" y="375" width="75" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 48px; height: 1px; padding-top: 260px; margin-left: 341px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">throttled</div></div></div></foreignObject><text x="365" y="262" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">throttled</text></switch></g><rect x="787.5" y="447.75" width="15" height="63.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 471.75 449.23 L 773.82 450.69" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 784.32 450.74 L 773.8 455.94 L 773.85 445.44 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 297px; margin-left: 419px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">emit("single-store-update")</div></div></div></foreignObject><text x="419" y="297" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">emit("single-sto...</text></switch></g><rect x="202.5" y="462.75" width="15" height="48.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 785.25 495.75 L 228.18 495.52" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 217.68 495.51 L 228.18 490.27 L 228.17 500.77 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 327px; margin-left: 250px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoreUpdate</div></div></div></foreignObject><text x="250" y="327" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoreUpdate</text></switch></g><rect x="570" y="447.75" width="135" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 309px; margin-left: 381px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: normal; word-wrap: normal; ">one event per update</div></div></div></foreignObject><text x="425" y="311" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">one event per update</text></switch></g><path d="M 216.75 134.25 L 775.32 134.98" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 785.82 135 L 775.32 140.23 L 775.33 129.73 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 87px; margin-left: 334px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">addItem</div></div></div></foreignObject><text x="334" y="87" fill="#000000" font-family="Helvetica" font-size="9px" text-anchor="middle">addItem</text></switch></g><rect x="622.5" y="165" width="15" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 787.5 164.25 L 648.18 164.94" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 637.68 164.99 L 648.15 159.69 L 648.2 170.19 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 107px; margin-left: 474px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">getWindowFromHost</div></div></div></foreignObject><text x="474" y="107" fill="#000000" font-family="Helvetica" font-size="9px" text-anchor="middle">getWindowFromHost</text></switch></g><path d="M 638.25 194.25 L 784.15 194.25" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="stroke"/><path d="M 772.32 201 L 785.82 194.25 L 772.32 187.5" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 127px; margin-left: 476px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">window mock</div></div></div></foreignObject><text x="476" y="127" fill="#000000" font-family="Helvetica" font-size="9px" text-anchor="middle">window mock</text></switch></g><rect x="900" y="0" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 975 60 L 975 525" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="974.5" y="37">Services</text></g><rect x="967.5" y="210" width="15" height="15" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="967.5" y="255" width="15" height="30" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 801.75 208.53 L 953.07 209.89" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 963.57 209.98 L 953.03 215.14 L 953.12 204.64 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 137px; margin-left: 589px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">cookies.add</div></div></div></foreignObject><text x="589" y="137" fill="#000000" font-family="Helvetica" font-size="9px" text-anchor="middle">cookies.add</text></switch></g><path d="M 966.75 270 L 813.18 270" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 802.68 270 L 813.18 264.75 L 813.18 275.25 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 177px; margin-left: 590px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 9px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onCookieChanged</div></div></div></foreignObject><text x="590" y="177" fill="#000000" font-family="Helvetica" font-size="9px" text-anchor="middle">onCookieChanged</text></switch></g><path d="M 785.25 330.03 L 650.43 330" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 639.93 330 L 650.43 324.75 L 650.43 335.25 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 217px; margin-left: 475px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">update("added")</div></div></div></foreignObject><text x="475" y="217" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">update("added")</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
\ No newline at end of file diff --git a/devtools/docs/contributor/tools/storage/flow-fission-off.svg b/devtools/docs/contributor/tools/storage/flow-fission-off.svg new file mode 100644 index 0000000000..3f1c2475d8 --- /dev/null +++ b/devtools/docs/contributor/tools/storage/flow-fission-off.svg @@ -0,0 +1,6 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1697px" height="932px" viewBox="-0.5 -0.5 1697 932" style="background-color: rgb(255, 255, 255);"><defs/><g><rect x="885" y="90" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 960 150 L 960 630" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="959.5" y="116.5">StorageActorMock</text><text x="959.5" y="137.5">(parent p.)</text></g><rect x="120" y="90" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 195 150 L 195 735" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="194.5" y="127">StorageUI</text></g><rect x="187.5" y="195" width="15" height="75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 21 195 L 175.32 195" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="15" cy="195" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 185.82 195 L 175.32 200.25 L 175.32 189.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="16.5px"><rect fill="#ffffff" stroke="none" x="55" y="173" width="97" height="21" stroke-width="0"/><text x="101.5" y="187">open toolbox</text></g><rect x="375" y="90" width="165" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 457.5 150 L 457.5 465" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="457" y="116.5">Resource</text><text x="457" y="137.5">Command</text></g><rect x="450" y="240" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="450" y="390" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 202.5 209.25 L 610.32 209.24" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 620.82 209.24 L 610.32 214.49 L 610.32 203.99 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="15px"><rect fill="#ffffff" stroke="none" x="248" y="188" width="91" height="20" stroke-width="0"/><text x="292" y="201.25">watchTargets</text></g><rect x="202.5" y="240" width="210" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 170px; margin-left: 136px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">(we only listen to onAvailable)</div></div></div></foreignObject><text x="205" y="172" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">(we only listen to onAvailable)</text></switch></g><rect x="0" y="0" width="90" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 10px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><b>Fission</b> OFF</div></div></div></foreignObject><text x="30" y="12" fill="#333333" font-family="Helvetica" font-size="8px" text-anchor="middle">Fission OFF</text></switch></g><path d="M 203.25 238.5 L 436.32 239.24" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 446.82 239.27 L 436.31 244.49 L 436.34 233.99 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 156px; margin-left: 190px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">watchResources</div></div></div></foreignObject><text x="190" y="156" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">watchResources</text></switch></g><rect x="555" y="90" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 630 150 L 630 375" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="629.5" y="116.5">Target</text><text x="629.5" y="137.5">Command</text></g><rect x="622.5" y="210" width="15" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 621.75 340.17 L 214.68 340.49" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 204.18 340.5 L 214.67 335.24 L 214.68 345.74 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 224px; margin-left: 275px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onTargetAvailable</div></div></div></foreignObject><text x="275" y="224" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onTargetAvailable</text></switch></g><rect x="622.5" y="340.5" width="15" height="19.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="187.5" y="340.5" width="15" height="19.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="187.5" y="390" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 447.75 390 L 211.68 390" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 201.18 390 L 211.68 384.75 L 211.68 395.25 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 257px; margin-left: 216px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onResourceListAvailable</div></div></div></foreignObject><text x="216" y="257" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onResourceListAv...</text></switch></g><rect x="202.5" y="390" width="225" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 270px; margin-left: 136px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">parent storage types: 1 front<br />content storage types: 1 front per target</div></div></div></foreignObject><text x="210" y="272" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">parent storage types: 1 front...</text></switch></g><rect x="1050" y="90" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 1125 150 L 1125 720" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="1124.5" y="116.5">Cookies</text><text x="1124.5" y="137.5">(actor)</text></g><rect x="1117.5" y="491.25" width="15" height="63.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 966.75 491.25 L 1104.57 491.25" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 1115.07 491.25 L 1104.57 496.5 L 1104.57 486 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 325px; margin-left: 695px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onWindowReady</div></div></div></foreignObject><text x="695" y="325" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onWindowReady</text></switch></g><rect x="952.5" y="480" width="15" height="75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 1117.5 523.5 L 977.68 523.98" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 967.18 524.02 L 977.66 518.73 L 977.7 529.23 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 346px; margin-left: 694px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">update("added")</div></div></div></foreignObject><text x="694" y="346" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">update("added")</text></switch></g><rect x="720" y="90" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 795 150 L 795 705" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="794.5" y="127">CookieWatcher</text></g><rect x="787.5" y="568.5" width="15" height="106.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="952.5" y="568.5" width="15" height="46.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 951.75 568.5 L 813.93 568.5" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 803.43 568.5 L 813.93 563.25 L 813.93 573.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 376px; margin-left: 585px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoresUpdate</div></div></div></foreignObject><text x="585" y="376" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoresUpdate</text></switch></g><rect x="840" y="568.5" width="75" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 48px; height: 1px; padding-top: 389px; margin-left: 561px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">throttled</div></div></div></foreignObject><text x="585" y="391" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">throttled</text></switch></g><path d="M 696 480 L 940.32 480" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="690" cy="480" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 950.82 480 L 940.32 485.25 L 940.32 474.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="16.5px"><rect fill="#ffffff" stroke="none" x="698" y="458" width="136" height="21" stroke-width="0"/><text x="764.5" y="472">new remote target</text></g><rect x="1117.5" y="641.25" width="15" height="63.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 801.75 642.73 L 1103.82 644.19" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 1114.32 644.24 L 1103.8 649.44 L 1103.85 638.94 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 426px; margin-left: 639px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">emit("single-store-update")</div></div></div></foreignObject><text x="639" y="426" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">emit("single-sto...</text></switch></g><rect x="187.5" y="660" width="15" height="90" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 1115.25 689.25 L 213.18 689.96" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 202.68 689.97 L 213.17 684.71 L 213.18 695.21 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 457px; margin-left: 439px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoreUpdate</div></div></div></foreignObject><text x="439" y="457" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoreUpdate</text></switch></g><rect x="900" y="641.25" width="135" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 438px; margin-left: 601px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: normal; word-wrap: normal; ">one event per update</div></div></div></foreignObject><text x="645" y="440" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">one event per update</text></switch></g><rect x="720" y="45" width="435" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 288px; height: 1px; padding-top: 40px; margin-left: 482px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: normal; word-wrap: normal; ">Note: IndexedDB does the same things.</div></div></div></foreignObject><text x="482" y="43" fill="#000000" font-family="Helvetica" font-size="10px">Note: IndexedDB does the same things.</text></switch></g><rect x="187.5" y="780" width="480" height="150" fill="#f5f5f5" stroke="#666666" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 312px; height: 1px; padding-top: 510px; margin-left: 130px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; max-height: 110px; overflow: hidden; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><h1>Notes:</h1><p></p><ul><li>Storage resources are actually <b>fronts</b>.</li><li><b>Content process resources</b> are: localStorage, sessionStorage and Cache</li><li><b>Parent process resources</b> are: cookies and IndexedDB</li></ul><p></p></div></div></div></foreignObject><text x="130" y="520" fill="#333333" font-family="Helvetica" font-size="10px">Notes:...</text></switch></g><rect x="1380" y="90" width="150" height="60" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><path d="M 1455 150 L 1455 720" fill="none" stroke="#d6b656" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="1454.5" y="116.5">StorageActorMock</text><text x="1454.5" y="137.5">(content p.)</text></g><rect x="1545" y="90" width="150" height="60" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><path d="M 1620 150 L 1620 765" fill="none" stroke="#d6b656" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="1619.5" y="116.5">LocalStorage</text><text x="1619.5" y="137.5">(actor)</text></g><rect x="1612.5" y="491.25" width="15" height="63.75" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><path d="M 1461.75 491.25 L 1599.57 491.25" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 1610.07 491.25 L 1599.57 496.5 L 1599.57 486 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 325px; margin-left: 1025px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onWindowReady</div></div></div></foreignObject><text x="1025" y="325" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onWindowReady</text></switch></g><rect x="1447.5" y="480" width="15" height="75" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><path d="M 1612.5 523.5 L 1472.68 523.98" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 1462.18 524.02 L 1472.66 518.73 L 1472.7 529.23 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 346px; margin-left: 1024px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">update("added")</div></div></div></foreignObject><text x="1024" y="346" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">update("added")</text></switch></g><rect x="1215" y="90" width="150" height="60" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><path d="M 1290 150 L 1290 705" fill="none" stroke="#d6b656" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="1289.5" y="116.5">LocalStorage</text><text x="1289.5" y="137.5">Watcher</text></g><rect x="1282.5" y="568.5" width="15" height="106.5" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><rect x="1447.5" y="568.5" width="15" height="46.5" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><path d="M 1446.75 568.5 L 1308.93 568.5" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 1298.43 568.5 L 1308.93 563.25 L 1308.93 573.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 376px; margin-left: 915px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoresUpdate</div></div></div></foreignObject><text x="915" y="376" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoresUpdate</text></switch></g><rect x="1335" y="568.5" width="75" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 48px; height: 1px; padding-top: 389px; margin-left: 891px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">throttled</div></div></div></foreignObject><text x="915" y="391" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">throttled</text></switch></g><rect x="1612.5" y="641.25" width="15" height="93.75" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><path d="M 1296.75 642.73 L 1598.82 645.54" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 1609.32 645.64 L 1598.77 650.79 L 1598.87 640.29 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 426px; margin-left: 969px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">emit("single-store-update")</div></div></div></foreignObject><text x="969" y="426" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">emit("single-sto...</text></switch></g><rect x="1395" y="641.25" width="135" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 438px; margin-left: 931px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: normal; word-wrap: normal; ">one event per update</div></div></div></foreignObject><text x="975" y="440" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">one event per update</text></switch></g><path d="M 1191 480 L 1435.32 480" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="1185" cy="480" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 1445.82 480 L 1435.32 485.25 L 1435.32 474.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="16.5px"><rect fill="#ffffff" stroke="none" x="1193" y="458" width="136" height="21" stroke-width="0"/><text x="1259.5" y="472">new remote target</text></g><rect x="1215" y="45" width="435" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 288px; height: 1px; padding-top: 40px; margin-left: 812px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: normal; word-wrap: normal; ">Note: sessionStorage and Cache do the same things.</div></div></div></foreignObject><text x="812" y="43" fill="#000000" font-family="Helvetica" font-size="10px">Note: sessionStorage and Cache do the same things.</text></switch></g><path d="M 1609.99 718.97 L 214.18 716.99" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 203.68 716.97 L 214.19 711.74 L 214.17 722.24 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 475px; margin-left: 440px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoreUpdate</div></div></div></foreignObject><text x="440" y="475" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoreUpdate</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
\ No newline at end of file diff --git a/devtools/docs/contributor/tools/storage/flow-fission-on.svg b/devtools/docs/contributor/tools/storage/flow-fission-on.svg new file mode 100644 index 0000000000..24cd56aaf1 --- /dev/null +++ b/devtools/docs/contributor/tools/storage/flow-fission-on.svg @@ -0,0 +1,6 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1202px" height="932px" viewBox="-0.5 -0.5 1202 932" content="<mxfile host="Electron" modified="2021-09-01T14:01:03.647Z" agent="5.0 (Macintosh; Intel Mac OS X 11_5_2) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" etag="Vd28oLcwDLHYtGPCu13_" version="14.6.13" type="device"><diagram id="kgpKYQtTHZ0yAKxKKP6v" name="Page-1">5Vxtc5s4EP41mel9iIc38fIxdtpeZtI207TT3kdiZJuJjDjAiX2//iQQGCTZljFgJ/XNXNECAnZXz7O7knJlTpbrz4kfL77gAKIrQwvWV+btlWHoumaTf6hkU0g8wyoE8yQM2EVbwWP4H2RCjUlXYQDTxoUZxigL46ZwiqMITrOGzE8S/Nq8bIZR86mxP4eC4HHqI1H6KwyyRfldtrc98TcM5wv2aNdwihNLv7yYfUm68AP8WhOZH6/MSYJxVhwt1xOIqPJKvRT3fdpxtnqxBEaZyg2z5Gt2H0f23Z2Jnz8vfoxvvk+ugVl08+KjFfvih7zHxwwnVDUGsZ9JjGGOv+DpM/uSbFOqh3xUTA9XS3QfziAKI9IaxzAJlzCDCTmDmPhhKxsTY2U+kdHzet5GyI/T8Cnvlj4sgdNVkoYv8DtMC5/IpXgVBTBgrUqheSNL8HNlItqpqB+msheYZHBdEzF9fYaYvGCyIZews47GbLep3Llov9ZcobxmUfMCqzS6z9xvXvW9tRA5YEaSG8yMVuNP68Sbbu61OPr264fhf77WBXsxS/28+8OMo3ttjWP2ZRxDMI5gkxiHUZY/F4yvwC1nD5xkCzzHkY/qFjlRs3v9SFndFmho25EpW9Q16EvVIm7hGEaMHZ7wWlA8/dKQ4PoNCucRET3hLMPLXIV+kt1QqqAGIB0SGYyCUvKEKO6xyxg/uV2bgfQ9h/uuY44EgwZbicZKIPIzMjKbPChRPrv1gbrj1sjXjia1ctlDilfJFLKbOAtWb9HeqEAwKkGY/JE1Hprg5dKPgreEdrMQoQlGOMnf1JzNZsZ0Wl1ZOxPYTzawu8FH01bAR9mYNXvDR/vt4CM4Vt2A07amBpBWB7qWRna6iJDvVtnGuZVdhntnVXYXKNOPeRxvMPNIP8AVrPPqZ9PFj5x1U/VQQRIWEM0lm9/MJnnjH9K41kaa7hw2WX7D7bp+++2m3qo5QyFch9nv8mZyTB+mjXSPNbdd0cam1uA7mhHeY9FMzqo7CYfx78HQpB7CSMeIoxjClKGINgKOzooFJ4Y1fPCqu2pxjdAR8A50VGhhT0flhXg2S+GpQZQciwRn//BK4icNR4iqF4VpxgLlXHjz4ofIJ+HOX8I4IKOdGHS8yJaIuVwTSiKch1h13GEin42cKfGmHMj4IbUMg4A+Zvy6CDP4GPu5i70mfiygXs1T3W4iI94dDFlkZEkAqousXmozGVfbiCr/iRzM6cGnME1DHJVy8pjqFLnz29ejrUeIwM5/EuoA9L9+7UibtUcWv44qA3qTfyzRvPaQ1hXTmpx+ytxGJKCazY7hIh7TK24iWFqxTUVPxkn8M9JAg4KAezQFdUo5+/KNIygnd0LeWUqQP5WFuISsgp1jWcjk4yvecy+AhcRUr4i23nYi3wE4Wa5CWi4rW1b1zs7xyZGwz/kzxb1u1bZs6aolil1UQKTv7wmqxlExMKo4rDc2YNBNwb+O3VWuchx6V+TSYJYtjRxBKkpEIC9yaCITyC/UFamge9i3uEkkU3fawb6YVttKsN8VpOuXUOToAIGBwYX/jqMECmVFrftSnTip9yYVSwKTC1OsGHm/C8Wa0phBVGx/xWUxvMNRmc/ch+kAbFbV3UbuyQRUo0a9kdb0k9PsrdcfpjJwNiozuOlBYaaobQYjdNQ3lYkhL7MZ9VW28kXLNjFdenRDDomqtFmC6QWsLpOU1ReaeKjdqRFk2Vr42HrNe6m2ScELDFmPMUS+HRq9GOCwukxruLmIINxQDcJL9j0DcpmeNuLcsO0cAF/GMV1vWOwSc8gIvuYqWuIM7gaYs6+AUV3sokpw27KdDSyzEz/Ry1tK4yqugJFMOXEd2d4IAG/7A0pOM+jEkSE4luBDb2IS+/T4uhzSZ1tPIFneVkzf7Y42/ph4go8dXUmWOWg8YYqFkQnGzyE1SlXt/uBPicHESdb3Xe12uam4YVdQy631TqotLuBAy3HKEOcQbBkj0JdyZcD1K4yILr5DP9gImu5hqrOobndSENBGXnOasyoPDFESKOuUBwPr0qXPEFg7fEnA4Yp5qgGTy8M631HPYbXxTmqFDp9uD7hSX+6comJXceBn8ANhx39XdPvT2A+oiqqmyJI9VA1tpwETI+A6baBip8kOp8PnK+QJ3OFxOazqqHWEjtxBR60pJsNF6PWLrrCh5bY/KtwCrfdE8WtROhv+1mWGW/u9qe3yAlNXSxydPSP2NG2/kzlansUsQ02xZm+KtQTF4ohuvYTpz5zNBqAsr7vAll8EMuRMl6VaL7bOGNbu8r9jCdK2zJHtaNVP57sdmWqVwK4Y0xLDsWyREIdENAL7Q2pINpe1yOBl0BKSJU6ov62i/hHFestw7U4GKRByz5bF+p050wVV563LXKV5ctnKuoSylSXuEIPLMKtlqGkYzRG8pkV3eL1ijD9cvsqv4Cf0bbVKV3dWtmzv+DWgJ8QAqkmypbptrPsYwOYqUlbbGWPXOtBR35QvJslvEjr4aTrLUytt9TZNB8RsiyUFPecEu9arjRxgHj2Kt4tIWG+1mpgB2oDMCbjgqOKCdzZccIzmcAZtww5XO9BRz7gADIn70lgTvsDaerWV3JcvMjdA/hNEY3/6PM8va64kIL+OqhP8BkNpDOMNmUAAsT7xlSYO+RrEO6KwNQxu6U7RANNpaS1bUEOn/jLPLRYkuuljBQGCs+w0+yoYVHkre/s6qmTb0MDmlUX/xUrUhV6uRKUGL1adVnuEtydLYcwLyFWCbIV4CQpLSfWX4QiOVhtYNaJt+v9ptvJRvlBF2MCcL4JNZfuXRzXh9jnik4UuJ9VimDjB5D3S5ktJd0rTF80HBeFWH1UfM6GuD/Mt1tsPzHcnahN/SgdLu1d8KJcVt3jDabmIhL1IbRjveRkiFM13wOpHDnyVreMpGdUEVYi01vqBYxq2GNqOsY/JEJ2hPPZZEIyAkSoelFDEQQGHQz3uOedjU2CLkGEaEsiopmOOwAzS3P7dyCIa2P71TfPj/w==</diagram></mxfile>" style="background-color: rgb(255, 255, 255);"><defs/><g><rect x="885" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 960 165 L 960 735" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="959.5" y="131.5">ParentStorage</text><text x="959.5" y="152.5">Mock</text></g><rect x="120" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 195 165 L 195 750" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="194.5" y="142">StorageUI</text></g><rect x="187.5" y="210" width="15" height="75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 21 210 L 175.32 210" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="15" cy="210" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 185.82 210 L 175.32 215.25 L 175.32 204.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="16.5px"><rect fill="#ffffff" stroke="none" x="55" y="188" width="97" height="21" stroke-width="0"/><text x="101.5" y="202">open toolbox</text></g><rect x="375" y="105" width="165" height="60" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><path d="M 457.5 165 L 457.5 600" fill="none" stroke="#d6b656" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="457" y="131.5">Resource</text><text x="457" y="152.5">Command</text></g><rect x="450" y="255" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="450" y="405" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="450" y="523.5" width="15" height="60" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><path d="M 202.5 224.25 L 610.32 224.24" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 620.82 224.24 L 610.32 229.49 L 610.32 218.99 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="15px"><rect fill="#ffffff" stroke="none" x="248" y="203" width="91" height="20" stroke-width="0"/><text x="292" y="216.25">watchTargets</text></g><rect x="202.5" y="255" width="210" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 180px; margin-left: 136px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">(we only listen to onAvailable)</div></div></div></foreignObject><text x="205" y="182" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">(we only listen to onAvailable)</text></switch></g><rect x="0" y="0" width="90" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 10px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><b>Fission</b> ON</div></div></div></foreignObject><text x="30" y="12" fill="#333333" font-family="Helvetica" font-size="8px" text-anchor="middle">Fission ON</text></switch></g><path d="M 203.25 253.5 L 436.32 254.24" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 446.82 254.27 L 436.31 259.49 L 436.34 248.99 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 166px; margin-left: 190px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">watchResources</div></div></div></foreignObject><text x="190" y="166" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">watchResources</text></switch></g><rect x="555" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 630 165 L 630 390" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="629.5" y="131.5">Target</text><text x="629.5" y="152.5">Command</text></g><rect x="622.5" y="225" width="15" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 621.75 355.17 L 214.68 355.49" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 204.18 355.5 L 214.67 350.24 L 214.68 360.74 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 234px; margin-left: 275px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onTargetAvailable</div></div></div></foreignObject><text x="275" y="234" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onTargetAvailable</text></switch></g><rect x="622.5" y="355.5" width="15" height="19.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="187.5" y="355.5" width="15" height="19.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="187.5" y="405" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 447.75 405 L 211.68 405" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 201.18 405 L 211.68 399.75 L 211.68 410.25 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 267px; margin-left: 216px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onResourceListAvailable</div></div></div></foreignObject><text x="216" y="267" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onResourceListAv...</text></switch></g><rect x="202.5" y="405" width="225" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 280px; margin-left: 136px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">parent storage types: 1 front<br />content storage types: 1 front per target</div></div></div></foreignObject><text x="210" y="282" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">parent storage types: 1 front...</text></switch></g><path d="M 451.5 523.5 L 214.68 523.5" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 204.18 523.5 L 214.68 518.25 L 214.68 528.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 346px; margin-left: 218px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onResourceListAvailable</div></div></div></foreignObject><text x="218" y="346" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onResourceListAv...</text></switch></g><path d="M 22.5 495 L 444.57 495" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="16.5" cy="495" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 455.07 495 L 444.57 500.25 L 444.57 489.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="16.5px"><rect fill="#ffffff" stroke="none" x="26" y="473" width="136" height="21" stroke-width="0"/><text x="92.5" y="487">new remote target</text></g><rect x="187.5" y="523.5" width="15" height="60" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><rect x="210" y="520.5" width="225" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 357px; margin-left: 141px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">only content storage types</div></div></div></foreignObject><text x="215" y="359" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">only content storage types</text></switch></g><rect x="1050" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 1125 165 L 1125 735" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="1124.5" y="131.5">Cookies</text><text x="1124.5" y="152.5">(actor)</text></g><rect x="1117.5" y="506.25" width="15" height="63.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 966.75 506.25 L 1104.57 506.25" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 1115.07 506.25 L 1104.57 511.5 L 1104.57 501 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 335px; margin-left: 695px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onWindowReady</div></div></div></foreignObject><text x="695" y="335" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onWindowReady</text></switch></g><rect x="952.5" y="495" width="15" height="75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 1117.5 538.5 L 977.68 538.98" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 967.18 539.02 L 977.66 533.73 L 977.7 544.23 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 356px; margin-left: 694px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">update("added")</div></div></div></foreignObject><text x="694" y="356" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">update("added")</text></switch></g><rect x="720" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 795 165 L 795 720" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="794.5" y="142">CookieWatcher</text></g><rect x="787.5" y="583.5" width="15" height="106.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="952.5" y="583.5" width="15" height="46.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 951.75 583.5 L 813.93 583.5" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 803.43 583.5 L 813.93 578.25 L 813.93 588.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 386px; margin-left: 585px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoresUpdate</div></div></div></foreignObject><text x="585" y="386" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoresUpdate</text></switch></g><rect x="840" y="583.5" width="75" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 48px; height: 1px; padding-top: 399px; margin-left: 561px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">throttled</div></div></div></foreignObject><text x="585" y="401" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">throttled</text></switch></g><path d="M 696 495 L 940.32 495" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="690" cy="495" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 950.82 495 L 940.32 500.25 L 940.32 489.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="16.5px"><rect fill="#ffffff" stroke="none" x="698" y="473" width="136" height="21" stroke-width="0"/><text x="764.5" y="487">new remote target</text></g><rect x="1117.5" y="656.25" width="15" height="63.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 801.75 657.73 L 1103.82 659.19" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 1114.32 659.24 L 1103.8 664.44 L 1103.85 653.94 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 436px; margin-left: 639px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">emit("single-store-update")</div></div></div></foreignObject><text x="639" y="436" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">emit("single-sto...</text></switch></g><rect x="187.5" y="675" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 1115.25 704.25 L 216.93 706.47" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 206.43 706.5 L 216.91 701.22 L 216.94 711.72 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 467px; margin-left: 440px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoreUpdate</div></div></div></foreignObject><text x="440" y="467" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoreUpdate</text></switch></g><rect x="900" y="656.25" width="135" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 448px; margin-left: 601px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: normal; word-wrap: normal; ">one event per update</div></div></div></foreignObject><text x="645" y="450" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">one event per update</text></switch></g><rect x="720" y="60" width="435" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 288px; height: 1px; padding-top: 50px; margin-left: 482px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: normal; word-wrap: normal; ">Note: IndexedDB does the same things</div></div></div></foreignObject><text x="482" y="53" fill="#000000" font-family="Helvetica" font-size="10px">Note: IndexedDB does the same things</text></switch></g><rect x="187.5" y="780" width="480" height="150" fill="#f5f5f5" stroke="#666666" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 312px; height: 1px; padding-top: 510px; margin-left: 130px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; max-height: 110px; overflow: hidden; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><h1>Notes:</h1><p></p><ul><li>Storage resources are actually <b>fronts</b>.</li><li><b>Content process resources</b> are: localStorage, sessionStorage and Cache</li><li><b>Parent process resources</b> are: cookies and IndexedDB</li></ul><p></p></div></div></div></foreignObject><text x="130" y="520" fill="#333333" font-family="Helvetica" font-size="10px">Notes:...</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
\ No newline at end of file diff --git a/devtools/docs/contributor/tools/storage/legacy.svg b/devtools/docs/contributor/tools/storage/legacy.svg new file mode 100644 index 0000000000..99df5ee72c --- /dev/null +++ b/devtools/docs/contributor/tools/storage/legacy.svg @@ -0,0 +1,6 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="722px" height="564px" viewBox="-0.5 -0.5 722 564" content="<mxfile host="Electron" modified="2021-06-09T15:02:41.953Z" agent="5.0 (Macintosh; Intel Mac OS X 11_4_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" version="14.6.13" etag="5bLNuVU7haQv3XDUxh68" type="device"><diagram id="8hjxK8QrEu9bYOSHH7Pt">zVpLc9o6FP41XjaD5EdgGSBtF+1MZ+jMvVkqtsCaCIuRnQL99ZWw/JBEbAM2cTaxjo8e/s53HpJw3MX28I2jXfyTRZg6cBIdHHfpQAjAJBD/pOSYS2bQywUbTiKlVAlW5C9WwomSvpMIp5pixhjNyE4XhixJcJhpMsQ52+tqa0b1WXdogy3BKkTUlv5HoizOpVP4WMm/Y7KJi5lBMMvfbFGhrL4kjVHE9jWR++y4C85Ylj9tDwtMJXgFLnm/rx+8LRfGcZJ16QDzDn8QfVffptaVHYuP5ew9ibDUnzjufB+TDK92KJRv98K8QhZnWypaQDyuCaULRhkX7YQlQmkeoTQ+dZfv1XSYZ/jw4ZJBCYRgEGZbnPGjUCk6uAo7RZ4Sy31lCjhRsrhmBjhVQqTMvynHrhASDwqk84C57YDhJHqSJKsgqAPEkkwReiqa+ECy/9Ur+fwiYX7wVWt5UKifGseikYg1l51ko9ZLNqtup1bRTzdkvmwcGUxP2TsPlUiRNkN8gxWG087WqlnDP2OMQsYxRRn5oy/inIHUDL8YEROXZHAnOhn8wLBx/j2qV90RzIF8YyBgDJSjYA0kLI2ONbWdVEgbFgz1ebzAb16XqQ80ffGQr6Aib2mDTnz2Pp/POi+vZXcHPvs2n4Ox8dkwtxWzOvPZGMh9vA+f4YV8dnvms2/xmTKRuFcZ4zKp35Tc+khejwZcvp28ADzDL7eH3BVY2KQ4TQlLxopOGd3vgc6jhc4ChfEIQbknZaYWKER8/AFHy/nogPHuyZaZBUzI2BvB6fhguSdfiqqphstTKKLL56PS7kTToTABFiYWHLUa61UkrLdTeVPsfEGQN78SOccJsBo4AxZMKiTWC6YCx8sqJlCU5TeWR2JJD75mSDi7skACZiGiCpe2AumKmgR02GU3FtmliSeXmdiorFVtXjx3qc07kOTMLtG/hiMDVtXA2Nx5ZjHclTTBzBgIGgP1VFVbC26pqk19owq/uaoGtx57XMvgD/g35i2caYtih3Mx2cqEZFbDA0SoW48BBrTv2I6cLN/szb7+cPa1t8Vj2fO55vbGU2ttK1e9Pkoze0vcxPuQIrFfDoc70e3He9pKuLGlZ9czste1NZ1rpGeLIz16lH1ckOYedX6/I430A71iqpMHUbJJJLOEJTAXAulLJET0Sb3YkiiSY8w5Tslf9HoaT9pelRBicH/u+EudiECOTeV0c8YjzI0rmVQ4NEk2v5lw5yUsVVH4tjm5f6HuQHd9+mtydXXJppbmlNcxGr2CxhjwZfIANcOBXng11cYM9O5svU7xzSywz0cuzJufdv+j7yc7RJKpHUlGF0jMo49rA4ln3rp0DCQ33waBy07Pe6/zzxxrxYRGp5j2izHaxO7awYViFjDILui1Ul0Zz2K2YQmiz5W0PRxmMmRZka7c2Zae81J3nJacWznOS/1dX7eo4My1E7y0ShIR0g982I+XGHVnYJK7q5f4XstAPXmJZ9z4g9mkcV2wWV/3kqp3r5mhcNSBDv1MD7Ac57xLgKtcAtzDJT73YNHMHIF5/d/ZJ9yWgQbyicIXP1xXo/qliUM0q98o5erVL73c538=</diagram></mxfile>" style="background-color: rgb(255, 255, 255);"><defs/><g><rect x="180" y="55.5" width="300" height="420" fill="none" stroke="#000000" stroke-width="1.5" stroke-dasharray="4.5 4.5" pointer-events="all"/><path d="M 420 423 L 465 423 L 465 348 L 420 348" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 420 123 L 465 123 L 465 198 L 420 198" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><rect x="240" y="100.5" width="180" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 82px; margin-left: 161px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">localStorage</div></div></div></foreignObject><text x="220" y="86" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">localStorage</text></switch></g><rect x="240" y="175.5" width="180" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 132px; margin-left: 161px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">sessionStorage</div></div></div></foreignObject><text x="220" y="136" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">sessionStorage</text></switch></g><rect x="240" y="250.5" width="180" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 182px; margin-left: 161px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Cache</div></div></div></foreignObject><text x="220" y="186" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Cache</text></switch></g><rect x="240" y="325.5" width="180" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 232px; margin-left: 161px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">indexedDB</div></div></div></foreignObject><text x="220" y="236" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">indexedDB</text></switch></g><rect x="240" y="400.5" width="180" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 282px; margin-left: 161px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">cookies</div></div></div></foreignObject><text x="220" y="286" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">cookies</text></switch></g><rect x="0" y="250.5" width="120" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 182px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Actor</div></div></div></foreignObject><text x="40" y="186" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Actor</text></switch></g><path d="M 240 273 L 147.18 273" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 121.68 273 L 147.18 260.25 L 147.18 285.75 Z" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><path d="M 240 423 L 210 423 L 210 123 L 240 123" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 210 198 L 240 198" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 210 348 L 240 348" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><rect x="540" y="243" width="180" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 182px; margin-left: 361px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Storage</div></div></div></foreignObject><text x="420" y="186" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Storage</text></switch></g><path d="M 420 273 L 530.45 273" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 538.32 273 L 527.82 278.25 L 530.45 273 L 527.82 267.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 189px; margin-left: 321px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">storageActor</div></div></div></foreignObject><text x="321" y="192" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">storageActor</text></switch></g><path d="M 420 348 L 465 348 L 465 123 L 420 123" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 630 243 L 630 10.5 L 330 10.5 L 330 43.32" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 330 53.82 L 324.75 43.32 L 335.25 43.32 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 85px; margin-left: 420px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">childActorPool</div></div></div></foreignObject><text x="420" y="96" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">childActorPool</text></switch></g><path d="M 630 303 L 630 535.5 L 60 535.5 L 60 322.68" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 60 297.18 L 72.75 322.68 L 47.25 322.68 Z" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
\ No newline at end of file diff --git a/devtools/docs/contributor/tools/storage/navigation-fission-on-target-switching-off.svg b/devtools/docs/contributor/tools/storage/navigation-fission-on-target-switching-off.svg new file mode 100644 index 0000000000..6899f5d8b4 --- /dev/null +++ b/devtools/docs/contributor/tools/storage/navigation-fission-on-target-switching-off.svg @@ -0,0 +1,6 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1082px" height="902px" viewBox="-0.5 -0.5 1082 902" content="<mxfile host="Electron" modified="2021-06-09T15:36:10.658Z" agent="5.0 (Macintosh; Intel Mac OS X 11_4_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" version="14.6.13" etag="y1Qj4akUHhTtIlbDpFi7" type="device"><diagram id="P-9pQ54hQDAgbKCWHi1S">7Vxbc5s4FP41nrYP9QDiYj8mTrPb2d5m0053HwnIhq2MvCAn8f76PRLiJskxdiFpkzoziXUQQpz7+Thkghbru9/ycJO8pzEmE8eK7yboYuI4tm358IdTdiVl7rglYZWnsZzUEK7S/7AkWpK6TWNcdCYySglLN11iRLMMR6xDC/Oc3nanLSnpXnUTrrBGuIpColO/pjFLqvvy582B33G6SuSlZ05QHliH1WR5J0USxvS2RUJvJmiRU8rKb+u7BSaceRVfyvMu9xytN5bjjPU5wSlPuAnJVt7bFaM5bP8sgr/vafRt4oCwEHAenb+U68IFpq/k/tmuYgrcyoZ/3a7Ju3SJSZrB6HyD83SNGc7hCJHkTw3tHETEQqDx47YYExJuivRaLMuvmuNomxfpDf4TF6UmCCrdZjGO5ahmoxiwnH6rBcMXlTeJc4bv9jLKrtkPeospbDDfwRR5QmBJie1qJS7Hty0FqOYkLdk7c0kMpdKt6rUbucAXKRqzmNA+MX15+8zkYM9PlAMKBpCDq8kBmEG3eYRbZrKg63WYxc9MMMjvIRh7LMF4mmBAIASucH4NX1b8y2VaFCnNKjqsWB+CMz9+0OQFvOAHE7Ymkk0l4xaU0FxMQb74wJFlSkiLvvT4D9BDkq4yoEXARSFOzuMUIsmZPLBO45hf8Pw2SRm+2oQRv/otxE1NhkvQBBkLZ3LYumT5GcfIXF2UvsnVDSBIXxPk5zBfYfbs7cudPabjCwz2pbB/Q9OMiUt45xPvQmE9zVlCVzQLSZv5D81EB3kdJtYq22HiOJo901i4oPRbCnlsK8MKedr13HKrmf2YudX8aaj2zFNU2wqmXi/ldp1q4vewsVq5xUeafU0zuO0LzO94h3WX3YqtamC8pozRNRzAWXzGazZOI7wg6URCYTbAonz3F4xeW1PLqwh/S3aLwcVdZ7Rrj1piK4l3KeOrWdO5J4diraldDZvV+GDXGqhrlbeMY6WglEmjVPeSxspA11bKHuJvCbeKEDkmIQOj7lzRJFu52ieu3q1SJ7AUTULdJcrNy7PaxaWy0Mw7sFB5x9pCQtXqW+ynffbTsOLAVa24X4DyBnCEtg4GbDdxyPBLiFD/bjkkcR5jApyJG4Ieq4a2aWs684OOTU+9WXCKXe+xxbbd2f0lNrzhaS68cgPHGl6gLTQbzfB0aKJMa76GLEqA9c8rlfFOhScGSWVsHZ/4Ub0gupeNihe03XkvLxjcY3K9eWiCEn5MHh4XSbx+PERD8FCv4mnGEUtcfBER5QGCxny4PLDOKqtEcJQs0DNEI/cRo9FeBTo2GvkumvqBVX9sddkpUuqPAcOTDlywJAeNIoZq5BDgl1ERq9pYnySNCPMN4Ap8NaE3uAJvJNTD1mGPLLxJVyYnsNfyC1AIVtk+haXM3kBMa7Gul5PVjLI/X8EvuM7MH8TaPK3oUpjfO/fbV0EcMK5moWoiXS4L/N0G+EThFfTA8Iqjwyt4nbJWaVak2Yrg1wUPs6+3Msw+XJkGxmB3qzTLPalK24u++PNglMDrGgLvY+IvvgKbIPtEVzBzDyw0XJx1ngj+oj4gQHZfM0fDmLmOwcjMeeTEuUl17a7VBR462uraaXjXJ/iBMyBy0zHjuW7GR8h/hPzZ6Vqfpz6H62vGjuIPvBHNWEdzeIILrL+RjT8c0bG2Zl38IfNnEl5jch5G31ZiWvshvfgMVG0rj7DM6cF8pCTb0JfygYKA0BkQ3wJv7nB8wbscYsofN1os4TItwjUWA0gciukI0iR4yb5Plj2Ep3qyAYSpgngzHcMbT5T7O1kSu+pX4cIthHTrXpbmYEXcqASYpdG2RKWQtKLIFjPhCUtfxXUHuMh/R2wbEsKZpjXaLHPK47yhz2baIjbX0a+sLbmAFaUDyinso+huytjRwzcqDABCXkjqm1lwLceiFai5QdFOYi3CiBvGaVv8VPdGHr/DqGoEkBtpmew9mwGiLr4DUj/Sxvu0OBVgwOBBgNoafaYbnk041h4zp2CgSyJSkgTcAc76mn7ldRSrV1zOiL1RaoboGRB+5Bi8g60W4ie5Bx1Z1TSxapiyituURdy7m/XPD9dcFNl1wf98vLwcRT1+zg44KMI6UjZ0wM29kSKADln+QswMabGLBkLMPOeHQswMjWI0K2364RpprOlMmNJQjTR2p6Z0Z8fXlHt0r10COoYSMOitosNXgK6Cv2juob+GHlhowArwieC1e436EFo7xNsaOlb7JHhYdxI/BA+fKqBoKCbH6jhGJjCxjCRnN2EKGTYZHVC0phZCHefvnOD867hkV8u1HjP4QzaDteNJZcfteIIeE1JEykNCV2376xtQkAJXueNBikiHFHVA4w/MGVbZ8/24xn2QhQknKOoqn+02oshOoMrmmm+JbkbxTpCV8l8lLsbfqbMikpYLgf6T65C/B2ktKQc/J76l52O+1RfbMOAE+g5jmr1gYqc3Ap5olBB2weEViWNQrmZ8RoZ5UmgxTiiwABFgT2W35oS/qyMx26LvNj/g24mAeWjeoD5RjkPR/SlZAQ4hmYjXbCXXS/hi0+cWXwKzU77Ibdko+KIo9yyG5ZZByxLKF0wLKQgcv+JnJJhLS1ZFIL/7b+oXTvIYOIkXnIqToCFCn46I11qp6qKA4OzGsiScklfa0Kj3oTOt8vFIZao/w+ORIVIc5c1P1/RS1Vj9RVV61eIzj+lXctgkgW8aqsKRZs47KsyRi+YfzNhO8incgl/tCK7JbtoPS6sj5rzG/KDi/gTE0JpQ0XrIp3e20ZvZJvTxJ0zKXTX96fka4CCFzRN9lbIvDwcxehNEVr2J/y4tHqC8qQuSwUAyrfViDIissuCOR3lMjAxZe0zx6JJmdmChk0saGDb/kKWc3vxbG/Tmfw==</diagram></mxfile>" style="background-color: rgb(255, 255, 255);"><defs/><g><rect x="765" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 840 165 L 840 540" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="839.5" y="131.5">StorageActorMock</text><text x="839.5" y="152.5">(parent p.)</text></g><rect x="0" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 75 165 L 75 660" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="74.5" y="142">StorageUI</text></g><rect x="255" y="105" width="165" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 337.5 165 L 337.5 660" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="337" y="131.5">Resource</text><text x="337" y="152.5">Command</text></g><rect x="0" y="0" width="90" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 10px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><b>Fission</b> ON</div></div></div></foreignObject><text x="30" y="12" fill="#333333" font-family="Helvetica" font-size="8px" text-anchor="middle">Fission ON</text></switch></g><rect x="435" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 510 165 L 510 660" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="509.5" y="131.5">Target</text><text x="509.5" y="152.5">Command</text></g><rect x="67.5" y="270" width="15" height="30" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="930" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 1005 165 L 1005 540" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="1004.5" y="131.5">Cookies</text><text x="1004.5" y="152.5">(actor)</text></g><rect x="997.5" y="251.25" width="15" height="63.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 846.75 251.25 L 984.57 251.25" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 995.07 251.25 L 984.57 256.5 L 984.57 246 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 165px; margin-left: 615px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onWindowDestroyed</div></div></div></foreignObject><text x="615" y="165" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onWindowDestroyed</text></switch></g><rect x="832.5" y="240" width="15" height="75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 997.5 283.5 L 857.68 283.98" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 847.18 284.02 L 857.66 278.73 L 857.7 289.23 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 186px; margin-left: 614px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">update("deleted")</div></div></div></foreignObject><text x="614" y="186" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">update("deleted")</text></switch></g><rect x="600" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 675 165 L 675 540" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="674.5" y="142">CookieWatcher</text></g><rect x="667.5" y="328.5" width="15" height="106.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="832.5" y="328.5" width="15" height="46.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 831.75 328.5 L 693.93 328.5" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 683.43 328.5 L 693.93 323.25 L 693.93 333.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 216px; margin-left: 505px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoresUpdate</div></div></div></foreignObject><text x="505" y="216" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoresUpdate</text></switch></g><rect x="720" y="328.5" width="75" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 48px; height: 1px; padding-top: 229px; margin-left: 481px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">throttled</div></div></div></foreignObject><text x="505" y="231" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">throttled</text></switch></g><path d="M 576 240 L 820.32 240" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="570" cy="240" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 830.82 240 L 820.32 245.25 L 820.32 234.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="16.5px"><rect fill="#ffffff" stroke="none" x="614" y="218" width="65" height="21" stroke-width="0"/><text x="644.5" y="232">navigate</text></g><rect x="997.5" y="401.25" width="15" height="63.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 681.75 402.73 L 983.82 404.19" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 994.32 404.24 L 983.8 409.44 L 983.85 398.94 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 266px; margin-left: 559px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">emit("single-store-update")</div></div></div></foreignObject><text x="559" y="266" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">emit("single-sto...</text></switch></g><rect x="67.5" y="416.25" width="15" height="48.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 995.25 449.25 L 93.18 449.01" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 82.68 449.01 L 93.18 443.76 L 93.18 454.26 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 296px; margin-left: 359px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoreUpdate</div></div></div></foreignObject><text x="359" y="296" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoreUpdate</text></switch></g><rect x="780" y="401.25" width="135" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 278px; margin-left: 521px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: normal; word-wrap: normal; ">one event per update</div></div></div></foreignObject><text x="565" y="280" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">one event per update</text></switch></g><rect x="600" y="60" width="435" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 288px; height: 1px; padding-top: 50px; margin-left: 402px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: normal; word-wrap: normal; ">Note: IndexedDB does the same things.</div></div></div></foreignObject><text x="402" y="53" fill="#000000" font-family="Helvetica" font-size="10px">Note: IndexedDB does the same things.</text></switch></g><rect x="67.5" y="705" width="480" height="150" fill="#f5f5f5" stroke="#666666" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 312px; height: 1px; padding-top: 460px; margin-left: 50px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; max-height: 110px; overflow: hidden; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><h1>Notes:</h1><p></p><ul><li>Storage resources are actually <b>fronts</b>.</li><li><b>Content process resources</b> are: localStorage, sessionStorage and Cache</li><li><b>Parent process resources</b> are: cookies and IndexedDB</li></ul><p></p></div></div></div></foreignObject><text x="50" y="470" fill="#333333" font-family="Helvetica" font-size="10px">Notes:...</text></switch></g><rect x="112.5" y="0" width="142.5" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 93px; height: 1px; padding-top: 10px; margin-left: 76px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><b>Target switching</b> OFF</div></div></div></foreignObject><text x="123" y="12" fill="#333333" font-family="Helvetica" font-size="8px" text-anchor="middle">Target switching OFF</text></switch></g><path d="M 366 240 L 490.32 240" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="360" cy="240" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 500.82 240 L 490.32 245.25 L 490.32 234.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="16.5px"><rect fill="#ffffff" stroke="none" x="369" y="218" width="65" height="21" stroke-width="0"/><text x="400" y="232">navigate</text></g><path d="M 504 268.98 L 92.17 269.97" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 81.67 270 L 92.16 264.72 L 92.18 275.22 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 177px; margin-left: 195px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onTargetDestroyed</div></div></div></foreignObject><text x="195" y="177" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onTargetDestroyed</text></switch></g><rect x="502.5" y="240" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="502.5" y="495" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="67.5" y="510" width="15" height="30" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 502.99 511.98 L 95.17 512.01" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 84.67 512.01 L 95.17 506.76 L 95.17 517.26 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 338px; margin-left: 195px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onTargetAvailable</div></div></div></foreignObject><text x="195" y="338" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onTargetAvailable</text></switch></g><rect x="577.5" y="705" width="480" height="195" fill="#f5f5f5" stroke="#666666" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 312px; height: 1px; padding-top: 460px; margin-left: 390px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; max-height: 140px; overflow: hidden; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><h1>Key points:</h1><ul><li>Content process storage types handle deletion in the UI client callback for `onTargetDestroyed`.</li><li>Parent process storage types don't have a target front, so we need to send `delete` updates.</li><li>New actors are created for both content and parent process storage types (their watcher's `watch` method is called) when navigation.</li></ul><p></p></div></div></div></foreignObject><text x="390" y="470" fill="#333333" font-family="Helvetica" font-size="10px">Key points:...</text></switch></g><rect x="105" y="570" width="225" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 390px; margin-left: 71px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">parent storage types: 1 front<br />content storage types: 1 front per target</div></div></div></foreignObject><text x="145" y="392" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">parent storage types: 1 front...</text></switch></g><rect x="330" y="570" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="67.5" y="570" width="15" height="30" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 327.75 570 L 94.68 570" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 84.18 570 L 94.68 564.75 L 94.68 575.25 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 377px; margin-left: 137px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onResourceListAvailable</div></div></div></foreignObject><text x="137" y="377" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onResourceListAv...</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
\ No newline at end of file diff --git a/devtools/docs/contributor/tools/storage/navigation-fission-on-target-switching-on.svg b/devtools/docs/contributor/tools/storage/navigation-fission-on-target-switching-on.svg new file mode 100644 index 0000000000..be0381741d --- /dev/null +++ b/devtools/docs/contributor/tools/storage/navigation-fission-on-target-switching-on.svg @@ -0,0 +1,6 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1082px" height="1127px" viewBox="-0.5 -0.5 1082 1127" style="background-color: rgb(255, 255, 255);"><defs/><g><rect x="765" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 840 165 L 840 480" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="839.5" y="131.5">StorageActorMock</text><text x="839.5" y="152.5">(parent p.)</text></g><rect x="0" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 75 165 L 75 780" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="74.5" y="142">StorageUI</text></g><rect x="195" y="105" width="165" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 277.5 165 L 277.5 780" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="277" y="131.5">Resource</text><text x="277" y="152.5">Command</text></g><rect x="0" y="0" width="90" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 10px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><b>Fission</b> ON</div></div></div></foreignObject><text x="30" y="12" fill="#333333" font-family="Helvetica" font-size="8px" text-anchor="middle">Fission ON</text></switch></g><rect x="390" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 465 165 L 465 660" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="464.5" y="131.5">Target</text><text x="464.5" y="152.5">Command</text></g><rect x="67.5" y="270" width="15" height="30" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="930" y="105" width="150" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 1005 165 L 1005 480" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="1004.5" y="131.5">Cookies</text><text x="1004.5" y="152.5">(actor)</text></g><rect x="997.5" y="251.25" width="15" height="63.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 846.75 251.25 L 984.57 251.25" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 995.07 251.25 L 984.57 256.5 L 984.57 246 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 165px; margin-left: 615px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onWindowDestroyed</div></div></div></foreignObject><text x="615" y="165" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onWindowDestroyed</text></switch></g><rect x="832.5" y="240" width="15" height="75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 997.5 283.5 L 857.68 283.98" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 847.18 284.02 L 857.66 278.73 L 857.7 289.23 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 186px; margin-left: 614px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">update("deleted")</div></div></div></foreignObject><text x="614" y="186" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">update("deleted")</text></switch></g><rect x="600" y="105" width="150" height="60" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><path d="M 675 165 L 675 600" fill="none" stroke="#d6b656" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="18px"><text x="674.5" y="142">CookieWatcher</text></g><rect x="667.5" y="328.5" width="15" height="106.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="667.5" y="493.5" width="15" height="61.5" fill="#fff2cc" stroke="#d6b656" stroke-width="1.5" pointer-events="all"/><rect x="832.5" y="328.5" width="15" height="46.5" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 831.75 328.5 L 693.93 328.5" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 683.43 328.5 L 693.93 323.25 L 693.93 333.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 216px; margin-left: 505px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoresUpdate</div></div></div></foreignObject><text x="505" y="216" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoresUpdate</text></switch></g><rect x="720" y="328.5" width="75" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 48px; height: 1px; padding-top: 229px; margin-left: 481px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">throttled</div></div></div></foreignObject><text x="505" y="231" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">throttled</text></switch></g><path d="M 576 240 L 820.32 240" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="570" cy="240" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 830.82 240 L 820.32 245.25 L 820.32 234.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="16.5px"><rect fill="#ffffff" stroke="none" x="614" y="218" width="65" height="21" stroke-width="0"/><text x="644.5" y="232">navigate</text></g><rect x="997.5" y="401.25" width="15" height="63.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 681.75 402.73 L 983.82 404.19" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 994.32 404.24 L 983.8 409.44 L 983.85 398.94 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 266px; margin-left: 559px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">emit("single-store-update")</div></div></div></foreignObject><text x="559" y="266" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">emit("single-sto...</text></switch></g><rect x="67.5" y="416.25" width="15" height="48.75" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 995.25 449.25 L 93.18 449.01" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 82.68 449.01 L 93.18 443.76 L 93.18 454.26 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 296px; margin-left: 359px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onStoreUpdate</div></div></div></foreignObject><text x="359" y="296" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onStoreUpdate</text></switch></g><rect x="780" y="401.25" width="135" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 278px; margin-left: 521px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: normal; word-wrap: normal; ">one event per update</div></div></div></foreignObject><text x="565" y="280" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">one event per update</text></switch></g><rect x="600" y="60" width="435" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 288px; height: 1px; padding-top: 50px; margin-left: 402px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: normal; word-wrap: normal; ">Note: IndexedDB does the same things.</div></div></div></foreignObject><text x="402" y="53" fill="#000000" font-family="Helvetica" font-size="10px">Note: IndexedDB does the same things.</text></switch></g><rect x="67.5" y="855" width="480" height="150" fill="#f5f5f5" stroke="#666666" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 312px; height: 1px; padding-top: 560px; margin-left: 50px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; max-height: 110px; overflow: hidden; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><h1>Notes:</h1><p></p><ul><li>Storage resources are actually <b>fronts</b>.</li><li><b>Content process resources</b> are: localStorage, sessionStorage and Cache</li><li><b>Parent process resources</b> are: cookies and IndexedDB</li></ul><p></p></div></div></div></foreignObject><text x="50" y="570" fill="#333333" font-family="Helvetica" font-size="10px">Notes:...</text></switch></g><rect x="112.5" y="0" width="142.5" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 93px; height: 1px; padding-top: 10px; margin-left: 76px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><b>Target switching</b> ON</div></div></div></foreignObject><text x="123" y="12" fill="#333333" font-family="Helvetica" font-size="8px" text-anchor="middle">Target switching ON</text></switch></g><path d="M 321 240 L 445.32 240" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="315" cy="240" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 455.82 240 L 445.32 245.25 L 445.32 234.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="16.5px"><rect fill="#ffffff" stroke="none" x="324" y="218" width="65" height="21" stroke-width="0"/><text x="355" y="232">navigate</text></g><path d="M 459 268.98 L 92.17 269.97" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 81.67 270 L 92.16 264.72 L 92.19 275.22 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 177px; margin-left: 180px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onTargetDestroyed</div></div></div></foreignObject><text x="180" y="177" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onTargetDestroyed</text></switch></g><rect x="457.5" y="240" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="457.5" y="630" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="67.5" y="645" width="15" height="30" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 457.99 646.98 L 95.17 647.01" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 84.67 647.01 L 95.17 641.76 L 95.17 652.26 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 428px; margin-left: 180px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onTargetAvailable</div></div></div></foreignObject><text x="180" y="428" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onTargetAvailable</text></switch></g><rect x="577.5" y="855" width="480" height="270" fill="#f5f5f5" stroke="#666666" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 312px; height: 1px; padding-top: 560px; margin-left: 390px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; max-height: 190px; overflow: hidden; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><h1>Key points:</h1><ul><li>Content process storage types handle deletion in the UI client callback for `onTargetDestroyed`.</li><li>Parent process storage types don't have a target front, so we need to send `delete` updates.</li><li>New actors are created for content process storage types (their watcher's `watch` method is called) when navigation.</li><li>New actors are created for parent process storage types after observing the event `window-global-created`. We delay sending these as resources until `target-available-form` has been triggered and the client has then processed onTargetAvailable.</li></ul><p></p></div></div></div></foreignObject><text x="390" y="570" fill="#333333" font-family="Helvetica" font-size="10px">Key points:...</text></switch></g><rect x="88.5" y="714" width="172.5" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 113px; height: 1px; padding-top: 486px; margin-left: 60px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">parent storage types: 1 front<br />content storage types: 1 front per target</div></div></div></foreignObject><text x="117" y="488" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">parent storage types: 1 fron...</text></switch></g><rect x="270" y="705" width="15" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><rect x="67.5" y="705" width="15" height="30" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 267.75 705 L 94.68 705" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 84.18 705 L 94.68 699.75 L 94.68 710.25 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 467px; margin-left: 117px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">onResourceListAvailable</div></div></div></foreignObject><text x="117" y="467" fill="#000000" font-family="Helvetica" font-size="10px" text-anchor="middle">onResourceListAv...</text></switch></g><path d="M 501 495 L 655.32 495" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="495" cy="495" rx="6" ry="6" fill="#000000" stroke="#000000" stroke-width="1.5" pointer-events="all"/><path d="M 665.82 495 L 655.32 500.25 L 655.32 489.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="13.5px"><rect fill="#ffffff" stroke="none" x="503" y="477" width="137" height="17" stroke-width="0"/><text x="569.5" y="487">window-global-created</text></g><rect x="697.5" y="525" width="120" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 360px; margin-left: 466px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">schedule spawning a new actor as a resource and send onAvailable after onTargetAvailable has been sent</div></div></div></foreignObject><text x="505" y="362" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">schedule spawning a...</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
\ No newline at end of file diff --git a/devtools/docs/contributor/tools/storage/resources.svg b/devtools/docs/contributor/tools/storage/resources.svg new file mode 100644 index 0000000000..5be2517e08 --- /dev/null +++ b/devtools/docs/contributor/tools/storage/resources.svg @@ -0,0 +1,6 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1142px" height="429px" viewBox="-0.5 -0.5 1142 429" content="<mxfile host="Electron" modified="2021-06-09T15:08:00.737Z" agent="5.0 (Macintosh; Intel Mac OS X 11_4_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" version="14.6.13" etag="Z0MbjdkrAFKkCsY56XtO" type="device"><diagram id="xBC6-tSpt6A_KGkmK59l">7VvRcps4FP0az+w+JIMkwOYxcdrdzrQzmcnOdPOogGwzwcgLuHb69SuBhJGEbYzBpkn7UiRLF3Huke65kjJC0+X2rwSvFt9oQKIRtILtCD2MIATActl/vOatqPGgXVTMkzAQjXYVT+FPIiotUbsOA5IqDTNKoyxcqZU+jWPiZ0odThK6UZvNaKS+dYXnxKh48nFk1n4Pg2xR1E7geFf/NwnnC/lm4HrFL0ssG4svSRc4oJtKFfo0QtOE0qx4Wm6nJOLgSVyKfp/3/FoOLCFx1qQDLDr8wNFafJsYV/YmP5bEwR3HjJViGrPK+0W2jFgJsMcZjTPhnwkrkm2Y/St+4s/P7Nm6dUTpgRPAkoU3WYiz5K3sxAuVXry465aXZL+EruOABKJUDJsEmuNSuk58UTURTMHJnAhwxiZeoPQCoy+hS8LeyZpsdn52hO8WFRfLuoREOAt/qIPAgm7z0lz5hkcashdDS0wNOBZ2xMRwXEs1UXyP6FX1q2YIQc0Q0AwVKBiGmKfxW6XZijdIDwzYU99ju87BcRntgdKePRQjkKWKD3ZVOYfr+Yyuz2eVl23Z3YDPtslnd2h81twNJy35rBtC48vwGZ7IZ+R2y2fb4HNEWRx6ymjCY5TObZV7m0WYkacVzgmzYaFY5bowTZKMbBX/NqCP7GBrcEneVOgFYA2/kOa+KpUq4B3ExjGwSUmahjQeKjrIuiA6roHOFPuLAYJyScqMDVBC9vFbEjzcDw4Y+5JsmRjA+JS+hiQdHiyX5ItnwHLns8Xl+qAcn0OTniCRr24osV5YvHrN1Y3M44BbFD+H/B05YAY4B3WPWNmqusdrDGCVNW43Igd441tH8UepCk6VOR7ULCE9Adijc1pICwBOcqSplUtpa50mbTWBLCS2fG4isRuQpCbZs9uQpEdxDLQczdY1bVPWuK5mCDYjzani2BjwEXGst4cdi2MAr8TgPfyrks0ZONmQSLxPz8QczZDV3wp1bjbfo3+HtnNkzM3O/Ov0518zuxWpW66yvnHlAN2IAXX/wjSXO+dPf/g0zkg+4lVCfZbx/WmQ4tJyDCFtofME+Mc0qt2FIDPz4AYoio8bMIhlFLsIiGa6/CVm5YjMsc8sTbl9XoH9bI15z3KTgVfxlMAKWei01imDTIeSgZKpeKVZQl/JlEasZ7lyzZgi1qpwFM5jVvQZiITV33OIQx9Hd+KHZRgE/DW1DlJdqO1tduIyXfkiVMN8yzGdpkuUVk4z0/lD8cGPcJqGfn8HGN1EmaqKdczAA4YmYxHQF7+2ZxaajDUmdoeRx9zvSCtrpski5qSv+IVEKnmaz86EpOFP/JLb474XUpsZd+5HzsMJs1OcaQpbo3IiKXwYH5y2N9YtVJAGnRBBtemp3elslpKz3Wbux/xKE/7kE8txzfQfWmJRbjSdm8Xujf99H1mCE48sbeVI/ewsVn7me12LvINrkXVr29Jcp6vRjcwepVXNQicLEjT30KZFevJYyOpSJe4EeMWX7n9rftEjx/smzQG/Yw0AWG1z2OXvpWrf4MxfkORMsV5xL+hIvLvOFTMgGQUqXnjMR/7BnXDRDKrm6sXXylH19wK0wWFm6wf4lzx1qznen+bHbr8KWjZsOM07Qcvc6DCDY/uTJx0uNUUfcWbwf2fovzZac3RcJdZdBJLQXOdEzLFUmjj6HnHbkw3DUHcpYc2FiQ/LruuwxtVPtYF9O265i62HwjpbHXKn242oYwSRJ6alc5+rvj2Ssu549KzQqKOUVcqAIR+8Ovqttba3Eh39tqAe5jpKWR1dXSPr4LigdbD9+SmruX2G30OuCo/um00cd3IeB7vNP80Nsd/HFvuXl73HFrZbo/r7OraQi0SHu5inBgvQKliAzoNFjXKdDC1Y6FpWX+MbBws9O9ejTk/BQn5A02ChtT//TzLMPbJ3ESwKHhwKFp6F3AEFC2Rukz0pF+bLXQcWM1g0sPIL45XKKKV8Rm/DNPswscI2Lnde9IgbmbtqX+SN9XrPmDe2P45raq/M9+Yac/Pun0UupjalY8gy5A6xZgnN12z+Qr4aFDHj47jKda6ruMydw9+uauyqPhc8Vtz9sXMR03Z/Mo4+/Q8=</diagram></mxfile>" style="background-color: rgb(255, 255, 255);"><defs/><g><path d="M 375 363 L 420 363 L 420 288 L 375 288" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 375 63 L 420 63 L 420 213 L 375 213" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><rect x="195" y="40.5" width="180" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 42px; margin-left: 131px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">localStorage</div></div></div></foreignObject><text x="190" y="46" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">localStorage</text></switch></g><rect x="195" y="115.5" width="180" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 92px; margin-left: 131px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">sessionStorage</div></div></div></foreignObject><text x="190" y="96" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">sessionStorage</text></switch></g><rect x="195" y="190.5" width="180" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 142px; margin-left: 131px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Cache</div></div></div></foreignObject><text x="190" y="146" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Cache</text></switch></g><rect x="195" y="265.5" width="180" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 192px; margin-left: 131px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">indexedDB</div></div></div></foreignObject><text x="190" y="196" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">indexedDB</text></switch></g><rect x="195" y="340.5" width="180" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 242px; margin-left: 131px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">cookies</div></div></div></foreignObject><text x="190" y="246" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">cookies</text></switch></g><rect x="0" y="190.5" width="120" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 142px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Actor</div></div></div></foreignObject><text x="40" y="146" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Actor</text></switch></g><path d="M 195 213 L 147.18 213" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 121.68 213 L 147.18 200.25 L 147.18 225.75 Z" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><path d="M 195 363 L 165 363 L 165 63 L 195 63" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 165 138 L 195 138" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 165 288 L 195 288" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><rect x="480" y="108" width="180" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 92px; margin-left: 321px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">StorageActorMock<br />(content process)</div></div></div></foreignObject><text x="380" y="96" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">StorageActorMock...</text></switch></g><rect x="480" y="295.5" width="180" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 217px; margin-left: 321px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">StorageActorMock<br />(parent process)</div></div></div></foreignObject><text x="380" y="221" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">StorageActorMock...</text></switch></g><rect x="491.25" y="168" width="157.5" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 122px; margin-left: 329px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">In legacy, an actual Storage actor is used</div></div></div></foreignObject><text x="380" y="124" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">In legacy, an actual Stora...</text></switch></g><path d="M 375 138 L 470.45 138" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 478.32 138 L 467.82 143.25 L 470.45 138 L 467.82 132.75 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 101px; margin-left: 281px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">storageActor</div></div></div></foreignObject><text x="281" y="103" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">storageActor</text></switch></g><path d="M 375 288 L 420 288 L 420 325.5 L 470.45 325.5" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 478.32 325.5 L 467.82 330.75 L 470.45 325.5 L 467.82 320.25 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 228px; margin-left: 281px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">storageActor</div></div></div></foreignObject><text x="281" y="230" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">storageActor</text></switch></g><rect x="960" y="108" width="180" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 92px; margin-left: 641px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">ContentProcessStorage<br style="font-size: 11px" />(watcher)</div></div></div></foreignObject><text x="700" y="95" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">ContentProcessStorage...</text></switch></g><rect x="960" y="295.5" width="180" height="60" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 217px; margin-left: 641px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">ParentProcessStorage<br style="font-size: 11px" />(watcher)</div></div></div></foreignObject><text x="700" y="220" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">ParentProcessStorage...</text></switch></g><rect x="720" y="115.5" width="180" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 92px; margin-left: 481px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">LocalStorageWatcher</div></div></div></foreignObject><text x="540" y="95" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">LocalStorageWatcher</text></switch></g><rect x="720" y="303" width="180" height="45" fill="#ffffff" stroke="#000000" stroke-width="1.5" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 217px; margin-left: 481px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">CookieWatcher</div></div></div></foreignObject><text x="540" y="220" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">CookieWatcher</text></switch></g><path d="M 900 325.5 L 932.82 325.5" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 958.32 325.5 L 932.82 338.25 L 932.82 312.75 Z" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><path d="M 900 137.63 L 932.82 137.63" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 958.32 137.63 L 932.82 150.38 L 932.82 124.88 Z" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><path d="M 810 115.5 L 810 10.5 L 285 10.5 L 285 30.95" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 285 38.82 L 279.75 28.32 L 285 30.95 L 290.25 28.32 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 46px; margin-left: 541px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">actor</div></div></div></foreignObject><text x="541" y="48" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">actor</text></switch></g><rect x="491.25" y="355.5" width="157.5" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 247px; margin-left: 329px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">In legacy, an actual Storage actor is used</div></div></div></foreignObject><text x="380" y="249" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">In legacy, an actual Stora...</text></switch></g><path d="M 810 348 L 810 415.5 L 285 415.5 L 285 395.05" fill="none" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 285 387.18 L 290.25 397.68 L 285 395.05 L 279.75 397.68 Z" fill="#000000" stroke="#000000" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 253px; margin-left: 541px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">actor</div></div></div></foreignObject><text x="541" y="255" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">actor</text></switch></g><rect x="731.25" y="168" width="157.5" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 122px; margin-left: 489px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">SessionStorageWatcher and CacheWatcher also exist</div></div></div></foreignObject><text x="540" y="124" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">SessionStorageWatcher and...</text></switch></g><rect x="731.25" y="265.5" width="157.5" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 187px; margin-left: 489px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">IndexedDBWatcher also exists</div></div></div></foreignObject><text x="540" y="189" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">IndexedDBWatcher also exis...</text></switch></g><rect x="971.25" y="355.5" width="157.5" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 247px; margin-left: 649px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">This watcher emits fronts as resources</div></div></div></foreignObject><text x="700" y="249" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">This watcher emits fronts...</text></switch></g><rect x="971.25" y="168" width="157.5" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)scale(1.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 122px; margin-left: 649px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">This watcher emits fronts as resources</div></div></div></foreignObject><text x="700" y="124" fill="#000000" font-family="Helvetica" font-size="8px" text-anchor="middle">This watcher emits fronts...</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
\ No newline at end of file diff --git a/devtools/docs/user/3d_view/3dview.png b/devtools/docs/user/3d_view/3dview.png Binary files differnew file mode 100644 index 0000000000..5cce08c8f5 --- /dev/null +++ b/devtools/docs/user/3d_view/3dview.png diff --git a/devtools/docs/user/3d_view/index.rst b/devtools/docs/user/3d_view/index.rst new file mode 100644 index 0000000000..dfe0bb83a3 --- /dev/null +++ b/devtools/docs/user/3d_view/index.rst @@ -0,0 +1,88 @@ +======= +3D view +======= + +.. warning:: + + From Firefox 47 onwards, 3D view is no longer available. + +When you click on the 3D view button, the page goes into 3D view mode; in this mode, you can see your page presented in a 3D view in which nested blocks of HTML are increasingly "tall," projecting outward from the bottom of the page. This view makes it easy to visualize the nesting of your content. + +.. image:: 3dview.png + :class: center + +By clicking and dragging the view, you can rotate and re-orient the 3D presentation of the DOM hierarchy of your page to see it from different angles, to better examine its structure. Off-screen elements become visible, so that you can see where your elements are located in relation to the visible content. You can click on elements to see their HTML in the :doc:`HTML panel <page_inspector_ui_tour_html_pane>` or the :doc:`Style panel <page_inspector_ui_tour_rules_view>`. Conversely, you can click on elements in the breadcrumb bar to change which element is selected in the 3D view. + +If you do not see the 3D button in the page inspector, it is possible that your graphics driver needs to be updated. See the `blocklisted drivers page <https://wiki.mozilla.org/Blocklisting/Blocked_Graphics_Drivers>`_ for more information. + + +Controlling the 3D view +*********************** + +There are keyboard shortcuts and mouse controls available for the 3D view. + +.. list-table:: + :widths: 20 20 60 + :header-rows: 1 + + * - Function + - Keyboard + - Mouse + + * - Zoom in/out + - :kbd:`+` / :kbd:`-` + - Scroll wheel up/down + + * - Rotate left/right + - :kbd:`a` / :kbd:`d` + - Mouse left/right + + * - Rotate up/down + - :kbd:`w` / :kbd:`s` + - Mouse up/down + + * - Pan left/right + - :kbd:`←` / :kbd:`→` + - Mouse left/right + + * - Pan up/down + - :kbd:`↑` / :kbd:`↓` + - Mouse up/down + + * - Reset zoom level + - :kbd:`0` + - Resets the zoom level to the default + + * - Focus on selected node + - :kbd:`f` + - Makes sure the currently selected node is visible + + * - Reset view + - :kbd:`r` + - Resets zoom, rotation, and panning to the default + + * - Hide current node + - :kbd:`x` + - Makes the currently selected node invisible; this can be helpful if you need to get at a node that's obscured + + +Use cases for the 3D view +************************* + +There are a variety of ways the 3D view is useful: + + +- If you have broken HTML causing layout problems, looking at the 3D view can help find where you've gone wrong. Often, layout problems are caused by improper nesting of content. This can become much more obvious when looking at the 3D view and seeing where your elements are nested wrong. +- If content isn't displaying, you may be able to figure out why; since the 3D view lets you zoom out to see elements that are rendering outside the visible area of the page, you can find stray content this way. +- You can get a look at how your page is structured to see if there may be ways to optimize your layout. +- And, of course, it looks **awesome**. + + +See also +******** + +- :doc:`Page Inspector <../page_inspector/index>` +- :ref:`HTML panel <page_inspector_ui_tour_html_pane>` +- :ref:`Style panel <page_inspector_ui_tour_rules_view>` +- :doc:`Tools <../index>` +- `New Developer Tools in Firefox 11 Aurora <https://hacks.mozilla.org/2011/12/new-developer-tools-in-firefox-11-aurora>`_ (blog post) diff --git a/devtools/docs/user/about_colon_debugging/about_debugging_setup.png b/devtools/docs/user/about_colon_debugging/about_debugging_setup.png Binary files differnew file mode 100644 index 0000000000..f3f7df8960 --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/about_debugging_setup.png diff --git a/devtools/docs/user/about_colon_debugging/about_debugging_this_firefox.png b/devtools/docs/user/about_colon_debugging/about_debugging_this_firefox.png Binary files differnew file mode 100644 index 0000000000..31de51f77b --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/about_debugging_this_firefox.png diff --git a/devtools/docs/user/about_colon_debugging/about_debugging_workers.png b/devtools/docs/user/about_colon_debugging/about_debugging_workers.png Binary files differnew file mode 100644 index 0000000000..fbcd7f133f --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/about_debugging_workers.png diff --git a/devtools/docs/user/about_colon_debugging/always_use_private_browsing_mode.png b/devtools/docs/user/about_colon_debugging/always_use_private_browsing_mode.png Binary files differnew file mode 100644 index 0000000000..55690bbac1 --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/always_use_private_browsing_mode.png diff --git a/devtools/docs/user/about_colon_debugging/connect_network_location.png b/devtools/docs/user/about_colon_debugging/connect_network_location.png Binary files differnew file mode 100644 index 0000000000..dae6c25dda --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/connect_network_location.png diff --git a/devtools/docs/user/about_colon_debugging/device_information.png b/devtools/docs/user/about_colon_debugging/device_information.png Binary files differnew file mode 100644 index 0000000000..b4f81f3d7e --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/device_information.png diff --git a/devtools/docs/user/about_colon_debugging/fxand-68-error.png b/devtools/docs/user/about_colon_debugging/fxand-68-error.png Binary files differnew file mode 100644 index 0000000000..9e61e87e3d --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/fxand-68-error.png diff --git a/devtools/docs/user/about_colon_debugging/index.rst b/devtools/docs/user/about_colon_debugging/index.rst new file mode 100644 index 0000000000..5e07791d97 --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/index.rst @@ -0,0 +1,326 @@ +=============== +about:debugging +=============== + +The ``about:debugging`` page provides a single place from which you can attach the Firefox Developer Tools to a number of debugging targets. At the moment it supports three main sorts of targets: restartless add-ons, tabs, and workers. + +This is also the main entry point to remotely debug Firefox, in particular Firefox for Android. + +Opening the about:debugging page +******************************** + +There are two ways to open ``about:debugging``: + +- Type ``about:debugging`` in the Firefox URL bar. +- In the **Tools** > **Browser Tools** menu, click **Remote Debugging**. + + +When about:debugging opens, on the left-hand side, you'll see a sidebar with two options and information about your remote debugging setup: + + +Setup + Use the Setup tab to configure the connection to your remote device. +This Firefox + Provides information about temporary extensions you have loaded for debugging, extensions that are installed in Firefox, the tabs that you currently have open, and service workers running on Firefox. + +.. image:: about_debugging_setup.png + :class: border + + +If your ``about:debugging`` page is different from the one displayed here, go to ``about:config``, find and set the option ``devtools.aboutdebugging.new-enabled`` to **true**. + + +Setup tab +********* + +.. _about-colon-debugging-connecting-to-a-remote-device: + +Connecting to a remote device +----------------------------- + +Firefox supports debugging over USB with Android devices, using the about:debugging page. + +Before you connect: + +1. Enable Developer settings on your Android device. +2. Enable USB debugging in the Android Developer settings. +3. Enable **Remote Debugging via USB** in the Advanced Settings in Firefox on the Android device. +4. Connect the Android device to your computer using a USB cable. + + +If your device doesn't appear in the lefthand side of the about:debugging page, try clicking the **Refresh devices** button. + +**If it still doesn't appear**, it may be because the link between your Android device and your computer is not authorized yet. First make sure you have installed `Android Debug Bridge <https://developer.android.com/studio/command-line/adb.html>`_ from Android Tools on your computer in order for it to be able to connect to your device. Next, disable every debugging setting already activated and repeat the steps described before. Your device should show a popup to authorize your computer to connect to it — accept this and then click the **Refresh devices** button again. The device should appear. + +.. note:: + + You do not need to install the full Android Studio SDK. Only adb is needed. + + +To start a debugging session, first open the page that you wish to debug and then click **Connect** next to the device name to open a connection to it. If the connection was successful, you can now click the name of the device to switch to a tab with information about the device. + +.. image:: device_information.png + :alt: Screenshot of the debugging page for an Android device + :class: border + + +The information on this page is the same as the information on the **This Firefox** tab, but instead of displaying information for your computer, it displays the information for the remote device with the addition of a **Tabs** section with an entry for each of the tabs open on the remote device. + +Note: If the version of Firefox on your remote device is more than one major version older than the version running on your computer, you may see a message like the following: + +.. image:: version_warning.png + :alt: The connected browser has an old version (68.2.0). The minimum supported version (69.0a1). This is an unsupported setup and may cause DevTools to fail. Please update the connected browser. + :class: center + + +In Firefox 76 and above, the message can look like the following: + +.. image:: fxand-68-error.png + :alt: This version of Firefox cannot debug Firefox for Android (68). We recommend installing Firefox for Android Nightly on your phone for testing. More details + :class: center + +See Connection for Firefox for Android 68 for more information. + +In the image above, there are three tabs open: **Network or cache Recipe**, **Nightly Home**, and **About Nightly**. To debug the contents of one of these tabs, click the **Inspect** button next to its title. When you do, the Developer Tools open in a new tab. + + +.. image:: remote-debugger-w-url-buttons.png + :class: border + :alt: Screenshot showing the remote debugging window, with the editable URL bar + + +Above the usual list of tools, you can see information about the device you are connected to, including the fact that you are connected (in this example) via USB, to Firefox Preview, on a Pixel 2, as well as the title of the page that you are debugging, and the address of the page. + +Starting in Firefox 78, the URL bar is editable, so that you can change the URL used by the browser on the remote device, by typing in Firefox for Desktop. You can also reload the page by clicking the **Reload** button next to the URL bar, and (starting 79), navigate backward or forward in the browsing history with the **Back** and **Forward** buttons. + + +Connecting over the Network +--------------------------- + +You can connect to a Firefox Debug server on your network, or on your debugging machine using the **Network Location** settings of the about:debugging page. + +.. image:: network_location.png + :class: center + + +Enter the location and port on which the debugger server is running. When you do, it is added to the Network locations list along with the devices, as shown below: + +.. image:: connect_network_location.png + :class: center + + +This Firefox +************ + +The **This Firefox** tab combines the features of Extensions, Tabs, and Workers into a single tab with the following sections: + + +Temporary Extensions + Displays a list of the extensions that you have loaded using the **Load Temporary Add-on** button. +Extensions + This section lists information about the extensions that you have installed on your system. +Service Workers, Shared Workers, and Other Workers + There are three sections on this page that deal with Service Workers, Shared Workers, and Other Workers. + + +.. image:: about_debugging_this_firefox.png + :class: border + + +Whether internal extensions appear in the list on this page depends on the setting of the ``devtools.aboutdebugging.showHiddenAddons`` preference. If you need to see these extensions, navigate to ``about:config`` and make sure that the preference is set to ``true``. + + +Extensions +********** + +Loading a temporary extension +----------------------------- + +With the **Load Temporary Add-on** button you can temporarily load a web extension from a directory on disk. Click the button, navigate to the directory containing the add-on and select its manifest file. The temporary extension is then displayed under the **Temporary Extensions** header. + +You don't have to package or sign the extension before loading it, and it stays installed until you restart Firefox. + +The major advantages of this method, compared with installing an add-on from an XPI, are: + + +- You don't have to rebuild an XPI and reinstall when you change the add-on's code; +- You can load an add-on without signing it and without needing to disable signing. + + +Once you have loaded a temporary extension, you can see information about it and perform operations on it. + +.. image:: temporary_extension.png + :alt: Screenshot of the debugging information panel for a temporary extension + :class: center + + +You can use the following buttons: + + +Inspect + Loads the extension in the debugger. +Reload + Reloads the temporary extension. This is handy when you have made changes to the extension. +Remove + Unloads the temporary extension. + + +Other information about the extension is displayed: + + +Location + The location of the extension's source code on your local system. +Extension ID + The temporary ID assigned to the extension. +Internal UUID + The internal UUID assigned to the extension. +Manifest URL + If you click the link, the manifest for this extension is loaded in a new tab. + + +Updating a temporary extension +------------------------------ + +If you install an extension in this way, what happens when you update the extension? + + +- If you change files that are loaded on demand, like `content scripts <https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts>`_ or `popups <https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Anatomy_of_a_WebExtension#browser_actions_2>`_, then changes you make are picked up automatically, and you'll see them the next time the content script is loaded or the popup is shown. + +- For other changes, click the **Reload** button. This does what it says: + + - Reloads any persistent scripts, such as `background scripts <https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Anatomy_of_a_WebExtension#background_scripts>`_ + - Parses the ``manifest.json`` file again, so changes to `permissions <https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions>`_, `content_scripts <https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_scripts>`_, `browser_action <https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/browser_action>`_ or any other keys take effect + + +Installed Extensions +-------------------- + +The permanently installed extensions are listed in the next section, **Extensions**. For each one, you see something like the following: + +.. image:: installed_extension.png + :alt: Screenshot of the debugging information panel for an installed extension + :class: center + + +The **Inspect** button, and the **Extension ID** and **Internal UUID** fields are the same as for temporary extensions. + +Just as it does with temporarily loaded extensions, the link next to **Manifest URL** opens the loaded manifest in a new tab. + +.. note:: + + It's recommended that you use the Browser Toolbox, not the Add-on Debugger, for debugging WebExtensions. See `Debugging WebExtensions <https://extensionworkshop.com/documentation/develop/debugging/>`_ for all the details. + + +The Add-ons section in about:debugging lists all web extensions that are currently installed. Next to each entry is a button labeled **Inspect**. + +.. note:: + + This list may include add-ons that came preinstalled with Firefox. + + +If you click **Inspect**, the Add-on Debugger will start in a new tab. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/efCpDNuNg_c" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +Workers +******* + +The Workers section shows all the workers you've got registered on your Firefox, categorized as follows: + + +- All registered `Service Workers <https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API>`_ +- All registered `Shared Workers <https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker>`_ +- Other workers, including Chrome Workers and `Dedicated Workers <https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#dedicated_workers>`_ + + +You can connect the developer tools to each worker, and send push notifications to service workers. + +.. image:: about_debugging_workers.png + :class: border + + +Service worker state +-------------------- + +The list of service workers shows the state of the service worker in its `lifecycle <https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle>`_. Three states are possible: + + +- *Registering*: this covers all states between the service worker's initial registration, and its assuming control of pages. That is, it subsumes the *installing*, *activating*, and *waiting* states. +- *Running*: the service worker is currently running. It's installed and activated, and is currently handling events. +- *Stopped*: the service worker is installed and activated, but has been terminated after being idle. + + +.. image:: sample_service_worker.png + :alt: Screenshot of the debugging panel for a service worker that is in the Running state + :class: center + + +This section uses a simple ServiceWorker demo, hosted at https://serviceworke.rs/push-simple/. + +.. note:: + + From Firefox 79 onwards, you can access similar information on the Service Workers registered on a particular domain by going to the Firefox DevTools :doc:`Application panel <../application/index>`. + + +Unregistering service workers +----------------------------- + +Click the **Unregister** button to unregister the service worker. + + +Sending push events to service workers +-------------------------------------- + +To debug push notifications, you can set a breakpoint in the `push event <https://developer.mozilla.org/en-US/docs/Web/API/PushEvent>`_ listener. However, you can also debug push notifications locally, without needing the server. Click the **Push** button to send a push event to the service worker. + + +Service workers not compatible +------------------------------ + +A warning message is displayed at the top of the **This Firefox** tab if service workers are incompatible with the current browser configuration, and therefore cannot be used or debugged. + +.. image:: worker_warning.png + :class: center + + +Service workers can be unavailable if: + +- ``dom.serviceWorkers.enable`` preference is set to false in ``about:config``. +- ``browser.privatebrowsing.autostart`` preference is set to true in ``about:config`` or through Firefox preferences UI. + + +The ``browser.privatebrowsing.autostart`` preference is set to true if the user selects **Never remember history** option or enables **Always use private browsing mode** in preferences UI, see about:preferences#privacy + + +Always use private browsing mode: + +.. image:: always_use_private_browsing_mode.png + :class: center + + +Never remember history: + +.. image:: never_remember_history.png + :class: center + + +Connection to Firefox for Android 68 +************************************ + +Releases of Firefox for Android that are based on version 68 cannot be debugged from desktop Firefox versions 69 or later, because of the difference in release versions. Until such time as Firefox for Android is updated to a newer major release, in synch with desktop Firefox, you should use one of the following Firefox for Android versions: + + +- `Firefox Preview <https://play.google.com/store/apps/details?id=org.mozilla.fenix>`_, if your desktop Firefox is the main release or Developer Edition +- `Firefox for Android Nightly <https://play.google.com/store/apps/details?id=org.mozilla.fenix>`_ + + +If you prefer to test with the main release of Firefox for Android (i.e., based on release 68), you can do so with the desktop `Firefox Extended Support Release (ESR) <https://support.mozilla.org/en-US/kb/switch-to-firefox-extended-support-release-esr>`_, which is also based on version 68. + +Note that ``about:debugging`` is not enabled by default in Firefox ESR. To enable it, open the `Configuration Editor <https://support.mozilla.org/en-US/kb/about-config-editor-firefox>`_ (``about:config``) and set ``devtools.aboutdebugging.new-enabled`` to **true**. + +If you used a higher version of Firefox prior to installing Firefox ESR, you will be prompted to create a new user profile, in order to protect your user data. For more information, see `What happens to my profile if I downgrade to a previous version of Firefox? <https://support.mozilla.org/en-US/kb/dedicated-profiles-firefox-installation#w_what-happens-to-my-profile-if-i-downgrade-to-a-previous-version-of-firefox>`_ diff --git a/devtools/docs/user/about_colon_debugging/installed_extension.png b/devtools/docs/user/about_colon_debugging/installed_extension.png Binary files differnew file mode 100644 index 0000000000..f2c5b068c1 --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/installed_extension.png diff --git a/devtools/docs/user/about_colon_debugging/network_location.png b/devtools/docs/user/about_colon_debugging/network_location.png Binary files differnew file mode 100644 index 0000000000..a80abb4d69 --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/network_location.png diff --git a/devtools/docs/user/about_colon_debugging/never_remember_history.png b/devtools/docs/user/about_colon_debugging/never_remember_history.png Binary files differnew file mode 100644 index 0000000000..cfc7c8f07f --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/never_remember_history.png diff --git a/devtools/docs/user/about_colon_debugging/remote-debugger-w-url-buttons.png b/devtools/docs/user/about_colon_debugging/remote-debugger-w-url-buttons.png Binary files differnew file mode 100644 index 0000000000..6cc3ce1a15 --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/remote-debugger-w-url-buttons.png diff --git a/devtools/docs/user/about_colon_debugging/sample_service_worker.png b/devtools/docs/user/about_colon_debugging/sample_service_worker.png Binary files differnew file mode 100644 index 0000000000..cda75521b1 --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/sample_service_worker.png diff --git a/devtools/docs/user/about_colon_debugging/temporary_extension.png b/devtools/docs/user/about_colon_debugging/temporary_extension.png Binary files differnew file mode 100644 index 0000000000..218f33f4a3 --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/temporary_extension.png diff --git a/devtools/docs/user/about_colon_debugging/version_warning.png b/devtools/docs/user/about_colon_debugging/version_warning.png Binary files differnew file mode 100644 index 0000000000..6602624fe6 --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/version_warning.png diff --git a/devtools/docs/user/about_colon_debugging/worker_warning.png b/devtools/docs/user/about_colon_debugging/worker_warning.png Binary files differnew file mode 100644 index 0000000000..c85a3a9d31 --- /dev/null +++ b/devtools/docs/user/about_colon_debugging/worker_warning.png diff --git a/devtools/docs/user/accessibility_inspector/accessibility-inspector-check_for_issues.png b/devtools/docs/user/accessibility_inspector/accessibility-inspector-check_for_issues.png Binary files differnew file mode 100644 index 0000000000..5ab84fa847 --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/accessibility-inspector-check_for_issues.png diff --git a/devtools/docs/user/accessibility_inspector/accessibility-inspector-hidden_item_revealed.png b/devtools/docs/user/accessibility_inspector/accessibility-inspector-hidden_item_revealed.png Binary files differnew file mode 100644 index 0000000000..799406052b --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/accessibility-inspector-hidden_item_revealed.png diff --git a/devtools/docs/user/accessibility_inspector/accessibility-inspector-hidden_items.png b/devtools/docs/user/accessibility_inspector/accessibility-inspector-hidden_items.png Binary files differnew file mode 100644 index 0000000000..41d88d9552 --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/accessibility-inspector-hidden_items.png diff --git a/devtools/docs/user/accessibility_inspector/accessibility-inspector-picker.png b/devtools/docs/user/accessibility_inspector/accessibility-inspector-picker.png Binary files differnew file mode 100644 index 0000000000..18ddda6078 --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/accessibility-inspector-picker.png diff --git a/devtools/docs/user/accessibility_inspector/accessibility-inspector-print_tree_to_json.png b/devtools/docs/user/accessibility_inspector/accessibility-inspector-print_tree_to_json.png Binary files differnew file mode 100644 index 0000000000..58c557a9f8 --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/accessibility-inspector-print_tree_to_json.png diff --git a/devtools/docs/user/accessibility_inspector/accessibility-inspector-show_tab_order.png b/devtools/docs/user/accessibility_inspector/accessibility-inspector-show_tab_order.png Binary files differnew file mode 100644 index 0000000000..7cf96d8cb7 --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/accessibility-inspector-show_tab_order.png diff --git a/devtools/docs/user/accessibility_inspector/accessibility-inspector-tabbing_order.png b/devtools/docs/user/accessibility_inspector/accessibility-inspector-tabbing_order.png Binary files differnew file mode 100644 index 0000000000..147eaa1b6c --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/accessibility-inspector-tabbing_order.png diff --git a/devtools/docs/user/accessibility_inspector/accessibility_json.png b/devtools/docs/user/accessibility_inspector/accessibility_json.png Binary files differnew file mode 100644 index 0000000000..3115c47ce8 --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/accessibility_json.png diff --git a/devtools/docs/user/accessibility_inspector/dom-inspector-context-menu.png b/devtools/docs/user/accessibility_inspector/dom-inspector-context-menu.png Binary files differnew file mode 100644 index 0000000000..54806b806f --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/dom-inspector-context-menu.png diff --git a/devtools/docs/user/accessibility_inspector/dom-inspector-picker.png b/devtools/docs/user/accessibility_inspector/dom-inspector-picker.png Binary files differnew file mode 100644 index 0000000000..dab01116af --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/dom-inspector-picker.png diff --git a/devtools/docs/user/accessibility_inspector/dom-node-target-icon.png b/devtools/docs/user/accessibility_inspector/dom-node-target-icon.png Binary files differnew file mode 100644 index 0000000000..0079299ff2 --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/dom-node-target-icon.png diff --git a/devtools/docs/user/accessibility_inspector/image_accessibility.png b/devtools/docs/user/accessibility_inspector/image_accessibility.png Binary files differnew file mode 100644 index 0000000000..ecb9611393 --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/image_accessibility.png diff --git a/devtools/docs/user/accessibility_inspector/index.rst b/devtools/docs/user/accessibility_inspector/index.rst new file mode 100644 index 0000000000..6574f893de --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/index.rst @@ -0,0 +1,288 @@ +======================= +Accessibility Inspector +======================= + +The Accessibility Inspector provides a means to access important information exposed to assistive technologies on the current page via the accessibility tree, allowing you to check what's missing or otherwise needs attention. This article takes you through the main features of the Accessibility Inspector and how to use it. + +A (very) brief guide to accessibility +************************************* + +Accessibility is the practice of making your websites usable by as many people as possible. This means trying your best to not lock anyone out of accessing information because of any disability they may have, or any other personal circumstances such as the device they are using, the speed of their network connection, or their geographic location or locale. You can find more extensive information in the `Accessibility <https://developer.mozilla.org/en-US/docs/Web/Accessibility>`_ section of MDN Web Docs. + +Here we are mainly talking about exposing information to people with visual disabilities — this is done via the `accessibility APIs <https://www.smashingmagazine.com/2015/03/web-accessibility-with-accessibility-api/>`_ available inside web browsers, which expose information on what roles the different elements on your page play (e.g., are they just text, or are they buttons, links, form elements, etc.?). + +Semantic DOM elements have roles assigned to them by default that hint at what their purpose is. Sometimes, however, you need to use some non-semantic markup (e.g., `div <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div>`_ s) to build a complex custom control, and the control won't have a default role that reflects its purpose. In such a situation, you can use `WAI-ARIA <https://developer.mozilla.org/en-US/docs/Learn/Accessibility/WAI-ARIA_basics>`_ role attributes to provide your own roles. + +Roles and other information exposed by browser accessibility APIs are presented in a hierarchical structure called the accessibility tree. This is a bit like the DOM tree, except that it contains a more limited set of elements and slightly different information about them. + +Assistive technologies like screenreaders use this information to find out what's on a web page, tell their users what's there, and enable them to interact with the page. The Accessibility Inspector also uses this information to provide valuable accessibility debugging capabilities in the DevTools. + + +Accessing the Accessibility Inspector +************************************* + +When you first open any of the other Developer Tools, the accessibility features are turned off (unless you've already got them turned on in another browser tab, or got the Firefox accessibility engine started already, e.g., you might be a screenreader user or tester). + +The accessibility inspector is automatically enabled when you do one of the following(beforeFirefox 79, it had to be explicitly enabled): + + +- Choose **Accessibility** in the **Tools > Browser Tools** menu. +- Select the **Accessibility** tab in the Developer Tools toolbox. +- Right-click in the main browser window, and choose **Inspect Accessibility Properties** in the context menu. +- Right-click an item in the HTML pane of the :doc:`Page Inspector <../page_inspector/index>`, and choose **Show Accessibility Properties** in the context menu. + + +Once activated, the accessibility engine remains running until you close the Developer Tools toolbox. + +.. note:: + + The accessibility engine runs in the background when the accessibility features are turned on. When enabled it may affect the metrics from other panels such as :doc:`Memory <../memory/index>` and :doc:`Performance <../performance/index>`, and have some impact onoverall browser performance. + + +If you don't wish to allow the accessibility features to be automatically enabled, you can use the `Configuration Editor <https://support.mozilla.org/en-US/kb/about-config-editor-firefox>`__ (also known as ``about:config``) to define the preference ``devtools.accessibility.auto-init.enabled``, and set it to ``False``. + +If you don't wish to use the accessibility features at all, you can use the `Configuration Editor <https://support.mozilla.org/en-US/kb/about-config-editor-firefox>`__ to set the preference ``devtools.accessibility.enabled`` to ``False``. If you do this, the methods listed above for activating the Accessibility Inspector do nothing. + + +Features of the Accessibility panel +*********************************** + +The enabled accessibility panel looks like so: + +.. image:: accessibility-inspector-tabbing_order.png + :class: border + :alt: Shows issue checker toolbar with "contrast" and "text label" options + + +On the left-hand side, there is a tree diagram representing all the items in the accessibility tree for the current page. Items with nested children have arrows that can be clicked to reveal the children, so you can move deeper into the hierarchy. Each item has two properties listed: + + +- *Role* — the role this item has on the page (e.g., ``pushbutton``, or ``footer``). This can be either a default role provided by the browser, or a role given to it via a WAI-ARIA ``role`` attribute. +- *Name* — the name this item has on the page. The name depends on the element; for example, the name of most text elements is their ``textContent``, whereas form elements' names are the contents of their associated `label <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label>`_. + + +On the right-hand side, you can see further information about the currently selected item. The listed properties are as follows: + + +- *name* — the item's name, as described above. +- *role* — the item's role, as described above. +- *actions* — a list of actions that can be performed on the item, for example, a pushbutton would have "Press" listed, while a link would have "Jump" listed. +- *value* — the value of the item. This can mean different things depending on the type of the item; for example, a form input (role: entry) would have a value of whatever is entered in the input, whereas a link's value would be the URL in the corresponding ``<a>`` element's ``href``. +- *DOMNode* — the type of DOM node that the item in the accessibility tree represents. You can click on the "target" icon that comes after it to select the node in the :doc:`Page Inspector <../page_inspector/index>`. Hovering over the "target" icon highlights the DOM node in the page content. + + .. image:: dom-node-target-icon.png + :alt: DOMNode property in accessibility inspector with target icon highlighted + +- *description* — any further description provided on the element, usually by the content of a title attribute. +- *keyboardShortcut* — any keyboard shortcut that is available to activate the element, as specified in an ``accessKey`` attribute. Note that this works correctly as of Firefox 62 `bug 1467381 <https://bugzilla.mozilla.org/show_bug.cgi?id="1467381>`_. +- *childCount* — the number of child items the current item has in the accessibility tree hierarchy. +- *indexInParent* — an index value indicating what number child the item is, inside its parent. If the item is the first item inside its parent, it has a value of 0. If it is the second, it has a value of 1. And so on. +- *states* — a list of the different accessibility-relevant states that can apply to the current item. For example, one of the links in one demo has states of focusable, linked, selectable text, opaque, enabled, and sensitive. For a full list of internal states, see Gecko states. +- *relations* — a list of the accessibility-relevant relationships between this item and other items. For example, in a form, an entry item could have a "labelled by" relation with a label item, which in turn has a "label for" relation to the entry item. +- *attributes* — a list of all the accessibility-relevant attributes that are applied to the item. This can include style-related attributes such as margin-left and text-indent, and other useful states for accessibility information, such as draggable and level (e.g., what heading level is it, in the case of headings). For a full list of possible attributes, see Gecko object attributes. + + +.. note:: + The exposed information is the same across all platforms — the inspector exposes Gecko's accessibility tree, rather than information from the platform accessibility layer. + + +Keyboard controls +----------------- + +The *Accessibility* tab is fully keyboard-accessible: + +- You can tab between *Check for Issues*, *Simulate*, *Show tabbing order*, and left and right panels. +- When one of the panels is focused, you can move the focus up and down items using the up and down arrow keys, and use the left and right arrow keys to expand and collapse expandable rows (e.g., different hierarchy levels of the accessibility tree). + + +Print accessibility tree to JSON +-------------------------------- + +You can print the contents of the accessibility tree to JSON by right-clicking on an entry in the Accessibility tab and selecting **Print to JSON:** + +.. image:: accessibility-inspector-print_tree_to_json.png + :alt: Print to JSON right-click menu in left panel + :class: border + +When you do, you will get a new tab with the selected accessibility tree loaded into the JSON viewer: + +.. image:: accessibility_json.png + :alt: Accessibility tree loaded in new tab JSON viewer + :class: center + +Once opened, you can save or copy the data as necessary. The JSON viewer can also show you the raw JSON data on a separate tab in the viewer. + + +Show web page tabbing order +--------------------------- + +People who are unable to navigate a page with the mouse or a trackpad can use the :kbd:`tab` key to toggle through focusable items on the page (i.e. buttons, links, form controls).The order that items are focused is one of the most important aspects of web accessibility, as it allows keyboard users to properly navigate a web page — if the tab order is incorrect, the page may be confusing! + +Firefox 84 and later can enable a visual overlay showing the tabbing order. This provides a high-level overview of how the page will be navigated using the :kbd:`tab` key, which may highlight problems more effectively than tabbing through the elements. The overlay is toggled on/off using the**Show Tabbing Order** checkbox. + +.. image:: accessibility-inspector-show_tab_order.png + :alt: Accessibility inspector and page with checkbox Show tab order selected. + :class: border + + +All focusable items have a numbered marker and the currently focused item is highlighted in a different color. In some cases the marker may be hidden by other elements, as is true for items 1 and 2 in the page below. + +.. image:: accessibility-inspector-hidden_items.png + :alt: A page where some of the markers for selection items are hidden + :class: center + +These become visible in the overlay when the item is the current item. + +.. image:: accessibility-inspector-hidden_item_revealed.png + :alt: Shows a hidden selection item in the tabbing order overlay when it is selected. + :class: center + + +.. note:: + + The overlay reflects the tab order at the time that the checkbox is selected (i.e. it is not dynamic). If a user does anything that adds items to the tab order (e.g. opens a visual element that contains more links), these new items will not be reflected in the overlay until the Accessibility Inspector is re-launched. + + +Check for accessibility issues +------------------------------ + +You can check for accessibility issues by clicking the drop-down menu next to: **Check for issues**. The available menu items include: + + +- **None** — Don't show the possible list of issues. +- **All Issues** — Check for all types of issues. +- **Contrast** — Check for `issues with visual contrast. <https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable/Color_contrast>`_ +- **Keyboard** — Check for `issues with navigating via a keyboard. <https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Keyboard>`_ +- **Text Labels** — Check for `issues with missing text labels. <https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Text_labels_and_names>`_ + + +When you select one of the menu items, Firefox scans your document for the type of issues you selected. Depending on the size and complexity of your document, this may take a few seconds. When the scan is complete, the left side of the Accessibility Inspector panel displays only the items that have that type of issue. In the right side of the panel, the *Checks* subpanel lists the specific issue with the selected node. For each type of issue, there is a **Learn more** link to further information on *MDN Web Docs* about the issue. + + +.. image:: accessibility-inspector-check_for_issues.png + :alt: Accessibility Inspector - Showing the options when you select the Check for Issues button + :class: border + + +The menu items act as toggles. Select the item to view that type of issue; select the item again to clear the display of issues of that type. + +Issues with a particular item are always displayed in the *Checks* subpanel as you browse the tree. The **Check for issues** menuitems are a quick way to view all and only those items that have issues. + + +Simulate +-------- + +The Accessibility Inspector offers (as of Firefox 70), a :doc:`simulator <simulation/index>` that lets you see what a web page would look like to users with various forms of *color vision deficiency* (better known as "color blindness"), as well as *contrast sensitivity loss*. + + +Notable related features +************************ + +When the accessibility features are turned on, there are a number of useful additional features available in the DevTools, which are detailed below: + +Context menu options +-------------------- + +An extra context menu option is added, both for the general context menu on the web page when right-clicking a UI feature, and the HTML pane of the page inspector when right-clicking a DOM element: + +.. image:: web-page-context-menu.png + :alt: context menu in the browser viewport, with a highlighted option: Inspect Accessibility Properties + :class: border + + +.. image:: dom-inspector-context-menu.png + :alt: context menu in the DOM inspector, with a highlighted option: Show Accessibility Properties + :class: border + +When you choose the *Inspect Accessibility Properties*/*Show Accessibility Properties* context menu options, the *Accessibility* tab is immediately opened to show the corresponding accessibility tree item and its properties. + +.. note:: + + Some DOM elements do not have accessibility properties — in that case, the *Inspect Accessibility Properties*/*Show Accessibility Properties* context menu item is grayed out. + + +Highlighting of UI items +------------------------ + +In the Accessibility tab, when the mouse hovers over accessibility items, you can see a semi-transparent highlight appear over the UI items they relate to, if appropriate. The role and name of the item will be shown in a small information bar along with color contrast information if appropriate. This is useful for determining how the items in the accessibility tree relate to the UI items on the actual page. + +In the following example, you can see that the image has been highlighted and its role, graphic, name, "Road, Asphalt, Sky, Clouds, Fall", and the color contrast ratio, 3.46, appears in the information bar above it. + +.. image:: image_accessibility.png + :alt: image has been highlighted and graphic, "Road, Asphalt, Sky, Clouds, Fall", and Contrast:3.46 warning sign, appears in the information bar above it + :class: border + + +Color contrast +~~~~~~~~~~~~~~ + +Contrast ratio information is particularly useful when you are designing the color palette for your website because if the contrast is not sufficient, readers with visual impairments such as low vision or color blindness will be unable to read the text. See `Color contrast <https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable/Color_contrast>`_ for details about recommended contrast ratios. + +For example: + +.. image:: screen_shot_2019-01-29_at_10.11.13.png + :alt: A screenshot of color contrast highlighter with warning sign where text contrast if below the AA WCAG threshold. + :class: center + +The color contrast in the image above is 2.86, so potentially not enough contrast to make it easy to read. Notice the warning symbol that indicates that the contrast fails to meet the acceptable contrast ratio. + +As of Firefox 65, viewing this information for some foreground text that has a complex background image (e.g. a gradient) gives you a range of color contrast values. For example: + +.. image:: screen_shot_2019-01-29_at_10.21.07.png + :alt: A screenshot of color contrast highlighter with checked sign where for text over gradient background with contrast satisfying the AAA WCAG guidelines. + :class: center + + +In this example, the contrast ranges from 4.72 to 5.98. The numbers are followed by AAA and a checkmark in green, indicating that the large text has a contrast ratio of 4.5:1 or more, meeting the criteria for enhanced contrast, or Level AAA. + +See `Color contrast <https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable/Color_contrast>`_ for more information on color contrast. + + +Accessibility picker +-------------------- + +Like the element picker button on the :ref:`Page Inspector <page-inspector-how-to-select-an-element-with-the-node-picker>`, the *Accessibility* tab's element picker button allows you to hover and select UI items on the current pageto highlight objects in the accessibility tree. + +The accessibility tab element picker looks slightly different from the Page Inspector HTML pane picker, as shown below: + +.. image:: dom-inspector-picker.png + :alt: highlighted DOM inspector picker button, with a tooltip saying Pick an element from the page + :class: border + + +.. image:: accessibility-inspector-picker.png + :alt: highlighted accessibility inspector button, with a tooltip saying Pick accessible object from the page + :class: border + + +When you "perform a pick", you see the accessibility object highlighted in the accessibility tree, and the picker is then deactivated. Note, however, that if you hold the :kbd:`Shift` key down when "performing a pick", you can "preview" the accessibility object in the tree (and its properties in the right-hand pane), but then continue picking as many times as you like(the picker does not get cancelled) until you release the :kbd:`Shift` key. + +When the picker is activated, you can also deactivate it by pressing the picker button a second time, or pressing the :kbd:`Esc` key. + + +Typical use cases +***************** + +The Accessibility Inspector is very useful for spotting accessibility problems at a glance. For a start, you can investigate items that don't have a proper text equivalent — images without ``alt`` text and form elements without proper labels have a ``name`` property of ``null``, for example. + +.. image:: use-case-no-label.png + :alt: A form input highlighted in the UI, with information about it shown in the accessibility inspector to reveal that it has no label — it has a name property of null + :class: border + + +It is also very handy for verifying semantics — you can use the *Inspect Accessibility Properties* context menu option to quickly see whether an item has the correct role set on it (e.g., whether a button is really a button, or a link is really a link). + +.. image:: use-case-fake-button.png + :alt: A UI element that looks like a button, with information about it shown in the accessibility inspector to reveal that it isn't a button, it is a section element. It has a name property of null + :class: border + + +See also +******** + +- `Accessibility tutorials <https://developer.mozilla.org/en-US/docs/Learn/Accessibility>`_ +- `Web accessibility overview <https://developer.mozilla.org/en-US/docs/Web/Accessibility>`_ +- `Practical debugging information <https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility>`_ +- `Understanding WCAG <https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG>`_ +- `WAI-ARIA basics <https://developer.mozilla.org/en-US/docs/Learn/Accessibility/WAI-ARIA_basics>`_ +- `Accessibility APIs: A Key To Web Accessibility <https://www.smashingmagazine.com/2015/03/web-accessibility-with-accessibility-api/>`_ by Léonie Watson diff --git a/devtools/docs/user/accessibility_inspector/screen_shot_2019-01-29_at_10.11.13.png b/devtools/docs/user/accessibility_inspector/screen_shot_2019-01-29_at_10.11.13.png Binary files differnew file mode 100644 index 0000000000..acdc675ceb --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/screen_shot_2019-01-29_at_10.11.13.png diff --git a/devtools/docs/user/accessibility_inspector/screen_shot_2019-01-29_at_10.21.07.png b/devtools/docs/user/accessibility_inspector/screen_shot_2019-01-29_at_10.21.07.png Binary files differnew file mode 100644 index 0000000000..edadcac9fb --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/screen_shot_2019-01-29_at_10.21.07.png diff --git a/devtools/docs/user/accessibility_inspector/simulation/28369550088_617db0d6f2_m.jpg b/devtools/docs/user/accessibility_inspector/simulation/28369550088_617db0d6f2_m.jpg Binary files differnew file mode 100644 index 0000000000..2b0243ab10 --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/simulation/28369550088_617db0d6f2_m.jpg diff --git a/devtools/docs/user/accessibility_inspector/simulation/accessibily_color_simulation_menu.jpg b/devtools/docs/user/accessibility_inspector/simulation/accessibily_color_simulation_menu.jpg Binary files differnew file mode 100644 index 0000000000..8b68f4c026 --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/simulation/accessibily_color_simulation_menu.jpg diff --git a/devtools/docs/user/accessibility_inspector/simulation/colorcat_achromatopsia.png b/devtools/docs/user/accessibility_inspector/simulation/colorcat_achromatopsia.png Binary files differnew file mode 100644 index 0000000000..6a3f960682 --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/simulation/colorcat_achromatopsia.png diff --git a/devtools/docs/user/accessibility_inspector/simulation/colorcat_contrastloss.png b/devtools/docs/user/accessibility_inspector/simulation/colorcat_contrastloss.png Binary files differnew file mode 100644 index 0000000000..01f0df8e4f --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/simulation/colorcat_contrastloss.png diff --git a/devtools/docs/user/accessibility_inspector/simulation/colorcat_deuteranopia.png b/devtools/docs/user/accessibility_inspector/simulation/colorcat_deuteranopia.png Binary files differnew file mode 100644 index 0000000000..a784aa09bc --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/simulation/colorcat_deuteranopia.png diff --git a/devtools/docs/user/accessibility_inspector/simulation/colorcat_protanopia.png b/devtools/docs/user/accessibility_inspector/simulation/colorcat_protanopia.png Binary files differnew file mode 100644 index 0000000000..1bfe43b40f --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/simulation/colorcat_protanopia.png diff --git a/devtools/docs/user/accessibility_inspector/simulation/colorcat_tritanopia.png b/devtools/docs/user/accessibility_inspector/simulation/colorcat_tritanopia.png Binary files differnew file mode 100644 index 0000000000..891ddeebcc --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/simulation/colorcat_tritanopia.png diff --git a/devtools/docs/user/accessibility_inspector/simulation/index.rst b/devtools/docs/user/accessibility_inspector/simulation/index.rst new file mode 100644 index 0000000000..2265a44ebf --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/simulation/index.rst @@ -0,0 +1,89 @@ +======================= +Color vision simulation +======================= + +The simulator in the :doc:`Accessibility Inspector <../index>` in Firefox Developer Tools lets you see what a web page would look like to users with various forms of *color vision deficiency* (better known as "color blindness"), as well as *contrast sensitivity loss*. + +"Color blindness" is a bit of a misnomer, since most people with these disorders can see colors, but do not see all of the distinctions that people with normal color vision can see; color vision deficiencies affect perception across the color spectrum, not only of specific colors like red or green. Color vision deficiencies affect about 8% of men, and 0.5% of women. The most common forms of color blindness (commonly lumped together as "red-green color blindness") affect more men than women, because they are due to a mutation in a gene in the X chromosome, which men usually have only one copy of. + +Contrast sensitivity loss can be caused by cataracts, glaucoma, diabetic retinopathy, and other disorders of the retina; it can be age-related, congenital, or due to an injury. + +.. note:: + + This feature depends on webrender, an experimental featurethat is not enabled by default on all platforms. You can force-enablewebrender by settingthe preference ``gfx.webrender.all`` to ``true`` using the `Firefox Configuration Editor <https://support.mozilla.org/en-US/kb/about-config-editor-firefox>`_ (note that if webrender is enabled by default on your platform, the setting has no effect. + + +The current color simulation option may be selected from the **Simulate** menu as shown. + +.. image:: accessibily_color_simulation_menu.jpg + :alt: Simulate menu in Accessibility panel. Used for selecting the simulation mode: None, Protanopia (no red), Deuteranopia (no green), Tritanopia (no blue), Achromatopsia (no color), Contrast loss + :class: center + + +The following table shows a colorful image of a cat's face, and what it looks like in the each of the simulations. + +.. |image1| image:: 28369550088_617db0d6f2_m.jpg + :alt: Colorful image of a cat's face, without modification + +.. |image2| image:: colorcat_protanopia.png + :alt: Colorful image of a cat's face, with protanopia simulation + +.. |image3| image:: colorcat_deuteranopia.png + :alt: Colorful image of a cat's face, with deuteranopia simulation + +.. |image4| image:: colorcat_tritanopia.png + :alt: Colorful image of a cat's face, with tritanopia simulation + +.. |image5| image:: colorcat_achromatopsia.png + :alt: Colorful image of a cat's face, with achromatopsia simulation + +.. |image6| image:: colorcat_contrastloss.png + :alt: Colorful image of a cat's face, with contrast loss simulation + + +.. list-table:: + :widths: 50 50 + :header-rows: 1 + + * - Simulation + - Image displayed + + * - None (Choose this to return to normal display) + - |image1| + + * - Protanopia (no red) + - |image2| + + * - Deuteranopia (no green) + - |image3| + + * - Tritanopia (no blue) + - |image4| + + * - Achromatopsia (no color) + - |image5| + + * - Contrast loss + - |image6| + + +.. note:: + + The simulation transformation matrices are based on the paper: `A Physiologically-based Model for Simulation of Color Vision Deficiency <https://www.inf.ufrgs.br/~oliveira/pubs_files/CVD_Simulation/CVD_Simulation.html>`_, Gustavo M. Machado, Manuel M. OliveiraLeandro A. F. Fernandes, IEEE Transactions on Visualization and Computer Graphics, Volume 15 (2009). + + +.. note:: + Firefox 81 removed unnecessary simulations for *protanomaly*, *deuteranomaly*, and *tritanomaly*, and added a simulation for *achromatopsia* (no color). + + +See also +******** + + +- `Types of color blindness <https://www.color-blindness.com/types-of-color-blindness/>`_ +- `Color blindness simulator <https://www.color-blindness.com/coblis-color-blindness-simulator/>`_ +- `Contrast sensitivity <http://www.vision-and-eye-health.com/contrast-sensitivity.html>`_ +- `Color palettes for color blindness <http://mkweb.bcgsc.ca/colorblind/>`_ +- `Color universal design <https://jfly.uni-koeln.de/color/>`_ +- `WCAG success criterion 1.4.1: Use of color <https://www.w3.org/TR/WCAG21/#use-of-color>`_ +- `WCAG success criterion 1.4.11: Non-text contrast <https://www.w3.org/TR/WCAG21/#non-text-contrast>`_ diff --git a/devtools/docs/user/accessibility_inspector/use-case-fake-button.png b/devtools/docs/user/accessibility_inspector/use-case-fake-button.png Binary files differnew file mode 100644 index 0000000000..1e39c78f4c --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/use-case-fake-button.png diff --git a/devtools/docs/user/accessibility_inspector/use-case-no-label.png b/devtools/docs/user/accessibility_inspector/use-case-no-label.png Binary files differnew file mode 100644 index 0000000000..255aaa35ee --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/use-case-no-label.png diff --git a/devtools/docs/user/accessibility_inspector/web-page-context-menu.png b/devtools/docs/user/accessibility_inspector/web-page-context-menu.png Binary files differnew file mode 100644 index 0000000000..aa7b242b72 --- /dev/null +++ b/devtools/docs/user/accessibility_inspector/web-page-context-menu.png diff --git a/devtools/docs/user/application/index.rst b/devtools/docs/user/application/index.rst new file mode 100644 index 0000000000..d07d4b02b6 --- /dev/null +++ b/devtools/docs/user/application/index.rst @@ -0,0 +1,32 @@ +=========== +Application +=========== + +The **Application panel** provides tools for inspecting and debugging modern web apps (also known as `Progressive Web Apps <https://developer.mozilla.org/en-US/docs/Glossary/Progressive_web_apps>`_). This includes inspection of `service workers <https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API>`_ and `web app manifests <https://developer.mozilla.org/en-US/docs/Web/Manifest>`_. + +.. image:: sw-registered.jpg + :alt: the firefox application panel zoomed out view showing a picture of two arctic foxes + :class: border + + +Accessing the Application panel +******************************* + +The Application panel is available on the standard DevTools tab menu under *Application*, in Firefox 79+. If you can’t see it there, you can enable it by going to the "three dot" menu and selecting *Settings* (also accessible by pressing F1), then checking the *Application* checkbox under *Default Developer Tools*. + + +Finding an example +****************** + +If you want to test this functionality and you don't have a handy PWA available, you can grab one of our simple examples to use: + +- Add to homescreen demo: Shows pictures of foxes (`source code <https://github.com/mdn/pwa-examples/tree/master/a2hs>`__ | `live version <https://mdn.github.io/pwa-examples/a2hs/>`__) + +- Js13kpwa demo: Show information on entries to the JS13K annual competition (`source code <https://github.com/mdn/pwa-examples/tree/master/js13kpwa>`__ | `live version <https://mdn.github.io/pwa-examples/js13kpwa/>`__) + + +How to +****** + +- :doc:`Debug service workers <service_workers/index>` +- :doc:`Inspect web app manifests <manifests/index>` diff --git a/devtools/docs/user/application/manifests/index.rst b/devtools/docs/user/application/manifests/index.rst new file mode 100644 index 0000000000..2a17057cd0 --- /dev/null +++ b/devtools/docs/user/application/manifests/index.rst @@ -0,0 +1,47 @@ +============================ +Inspecting web app manifests +============================ + +In this article we will look at inspecting `app manifests <https://developer.mozilla.org/en-US/docs/Web/Manifest>`_ using the Firefox DevTools :doc:`Application panel <../index>`. + +When you open the Application panel’s *Manifest* view on a page that doesn't have an app manifest successfully deployed, you'll get the following output shown: + +.. image:: no-manifest.jpg + :alt: default manifest view saying that you need to add a manifest to inspect it. + :class: border + + +Deploying a manifest +******************** + +To get a manifest deployed successfully, you need to include a `<link> <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link>`_ element in the `<head> <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head>`_ of your document that points to your ``.webmanifest`` file: + + +.. code-block:: html + + <link rel="manifest" href="/manifest.webmanifest"> + + +The ``.webmanifest`` extension is recommended in the spec, and should be served with an ``application/manifest+json`` mime type, although browsers generally tend to support manifests with other appropriate extensions like ``.json`` (mime type: ``application/json``). + +You also need to make sure the JSON inside the file is of the correct format. You can test this using the `Web Manifest Validator <https://manifest-validator.appspot.com/>`_. + + +Inspecting your manifest +************************ + +If your manifest is deployed successfully, you should end up with a display like the following on the *Manifest* view: + +.. image:: manifest-deployed.jpg + :alt: manifest view showing a deployed manifest, with identity, presentation, and icon information shown + :class: border + +From here, you can inspect all the information in the manifest in an easy-to-digest way, making sure that it is all correct. It provides a link to the manifest file at the top which when clicked on brings up the JSON in a new tab. + +It also loads all the icon files into the view, so you can see the relative size of them all, and any other information associated with them. + + +List of manifest members +************************ + +We won't provide an exhaustive list of all the members that can appear in a web manifest here; you can already find that in our `web manifest documentation <https://developer.mozilla.org/en-US/docs/Web/Manifest#members>`_. diff --git a/devtools/docs/user/application/manifests/manifest-deployed.jpg b/devtools/docs/user/application/manifests/manifest-deployed.jpg Binary files differnew file mode 100644 index 0000000000..382160b121 --- /dev/null +++ b/devtools/docs/user/application/manifests/manifest-deployed.jpg diff --git a/devtools/docs/user/application/manifests/no-manifest.jpg b/devtools/docs/user/application/manifests/no-manifest.jpg Binary files differnew file mode 100644 index 0000000000..36426b94c6 --- /dev/null +++ b/devtools/docs/user/application/manifests/no-manifest.jpg diff --git a/devtools/docs/user/application/service_workers/cache-network.jpg b/devtools/docs/user/application/service_workers/cache-network.jpg Binary files differnew file mode 100644 index 0000000000..0498335155 --- /dev/null +++ b/devtools/docs/user/application/service_workers/cache-network.jpg diff --git a/devtools/docs/user/application/service_workers/index.rst b/devtools/docs/user/application/service_workers/index.rst new file mode 100644 index 0000000000..11178d741b --- /dev/null +++ b/devtools/docs/user/application/service_workers/index.rst @@ -0,0 +1,116 @@ +========================= +Debugging service workers +========================= + +In this article we will look at debugging `service workers <https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API>`_ using the Firefox DevTools :doc:`Application Panel <../index>`. + +When you open the Application panel’s *Service Workers* view on a page that doesn't have a service worker registered, you'll get the following output shown: + +.. image:: no-service-worker.jpg + :alt: the application panel with a message stating that you need to register a service worker to inspect it here + :class: border + +This gives you some advice on what to do if you don't have a service worker registered, and were perhaps expecting there to be one registered! Let's start by troubleshooting this. + + +Getting your service worker to register +*************************************** + +Before you can look at your service worker in action in the *Applications* panel, you need to successfully register it. Registration is done with a block of code along these lines, using the `register() <https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register>`_ method: + +.. code-block:: JavaScript + + if('serviceWorker' in navigator) { + navigator.serviceWorker + .register('sw.js') + .then(function() { console.log('Service Worker Registered'); }); + } + +If you get the path wrong, for example, you'll get an error in the :doc:`Web Console <../../web_console/index>` giving you a hint as to what's wrong, which depends on what exactly is wrong with the code. If this is not enough to help you figure out the problem, you could also try going to the :doc:`JavaScript Debugger <../../debugger/index>` and stepping through your code to pinpoint exactly where it is going wrong. + + +Debugging your service worker +***************************** + +In any case, when the service worker is successfully registered, you'll see information about it displayed in the *Application* > *Service Workers* view (along with any other service workers registered on the same domain): + +.. image:: sw-registered.jpg + :alt: the application panel showing a registered service worker + :class: border + +This gives you a variety of information about your service worker: + + +- The URL that the service worker is registered on. +- That last time the service worker was updated (if the service has not been updated, this is when it was first installed). +- The path to the service worker source file. +- The server worker’s status, which can be one of the following: + + - *Stopped*: The service worker is installed, but not currently running. When the service worker is stopped, a *Start* button is provided to start it running, allowing you to trigger the service worker lifecycle. + - *Running*: The service worker is running. + +There are a couple of other useful controls on this view, too. + + +Unregister +********** + +On the right-hand side of the *Service Workers* view there is an *Unregister* button, which when pressed unregisters the service worker. This is very useful because, once registered, the service worker will not necessarily update to the new version immediately, which can make debugging a pain. From `Updating your service worker <https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#updating_your_service_worker>`_: + +*If your service worker has previously been installed, but then a new version of the worker is available on refresh or page load, the new version is installed in the background, but not yet activated. It is only activated when there are no longer any pages loaded that are still using the old service worker. As soon as there are no more such pages still loaded, the new service worker activates.* + + +Debug +***** + +.. warning:: + + **Important**: The *debugging* feature is currently enabled only in Firefox Nightly via the experimental feature "Developer Tools: Service Worker debugging". Enabling this can degrade the performance of the Debugger. See about:preferences#experimental. + + +When your service worker is running, the *Arrow* button available next to the Source information (it is disabled when the service worker is stopped). When pressed (and if service worker debugging is enabled), this takes you straight to the :doc:`JavaScript debugger <../../debugger/index>` view of your service worker code, and you can use the full power of the debugger to debug it — stepping through code, etc. + +.. image:: sw-debugger.jpg + :alt: the firefox JS debugger show the code for a service worker + :class: border + + +Testing a service worker cache +****************************** + +If you are using your service worker to store your site assets in Cache Storage (using the `Cache API <https://developer.mozilla.org/en-US/docs/Web/API/Cache>`_), which is essential for creating offline apps, it can be annoying to test the cache. When you fill up the cache with assets, but then want to alter the behavior slightly, you previously had to manually write code to empty the cache, and then fill it again. + +The :doc:`Firefox DevTools Storage tab <../../storage_inspector/index>` has a Cache Storage section that lists all the different Caches you have stored under each different origin. + +.. image:: sw-storage.jpg + :alt: the firefox storage inspector showing cache storage items + :class: border + + +Right/:kbd:`Ctrl` clicking on one of the caches gives you two options: + +- *Delete All* — delete all caches under this origin. +- *Delete "name of cache"* — delete only the highlighted cache. + +You can also click on one of the individual items stored in the cache, then Right/:kbd:`Ctrl` click on it to get options for deleting just that item, or every item in the cache. + +These options make it much easier to remove a cache if it is required for testing a code update. + +It is also worth knowing that if you are testing an app's offline capabilities, you'll be able to see whether requests are being retrieved from a service worker-initiated cache rather than from the network by looking at :doc:`Network Monitor <../../network_monitor/index>`. + +.. image:: cache-network.jpg + :alt: the network monitor, showing that requests that come from a cache are marked with service worker + :class: border + +Resources retrieved from the cache are indicated with *service worker* in the *Transferred* column. + + +.. note:: + + There is currently a bug whereby the Network Monitor cannot show network requests from a service worker running in a different process to the application `bug 1432311 <https://bugzilla.mozilla.org/show_bug.cgi?id=1432311>`_. + + +Finding registered service workers on other domains +*************************************************** + +As mentioned above, the *Service Worker* view of the *Application* panel shows all the service workers registered on the current domain. If you want to see a list of information concerning all the service workers registered on your browser, you can visit ``about:debugging#/runtime/this-firefox``. Below the list of installed extensions you'll find a list of all the service workers you have registered. diff --git a/devtools/docs/user/application/service_workers/no-service-worker.jpg b/devtools/docs/user/application/service_workers/no-service-worker.jpg Binary files differnew file mode 100644 index 0000000000..b10e3a0521 --- /dev/null +++ b/devtools/docs/user/application/service_workers/no-service-worker.jpg diff --git a/devtools/docs/user/application/service_workers/sw-debugger.jpg b/devtools/docs/user/application/service_workers/sw-debugger.jpg Binary files differnew file mode 100644 index 0000000000..6580d1b60a --- /dev/null +++ b/devtools/docs/user/application/service_workers/sw-debugger.jpg diff --git a/devtools/docs/user/application/service_workers/sw-registered.jpg b/devtools/docs/user/application/service_workers/sw-registered.jpg Binary files differnew file mode 100644 index 0000000000..d2982d5c51 --- /dev/null +++ b/devtools/docs/user/application/service_workers/sw-registered.jpg diff --git a/devtools/docs/user/application/service_workers/sw-storage.jpg b/devtools/docs/user/application/service_workers/sw-storage.jpg Binary files differnew file mode 100644 index 0000000000..b122977f19 --- /dev/null +++ b/devtools/docs/user/application/service_workers/sw-storage.jpg diff --git a/devtools/docs/user/application/sw-registered.jpg b/devtools/docs/user/application/sw-registered.jpg Binary files differnew file mode 100644 index 0000000000..e3c0aa2bb9 --- /dev/null +++ b/devtools/docs/user/application/sw-registered.jpg diff --git a/devtools/docs/user/browser_console/browser-console-addon-output.png b/devtools/docs/user/browser_console/browser-console-addon-output.png Binary files differnew file mode 100644 index 0000000000..a07d472db3 --- /dev/null +++ b/devtools/docs/user/browser_console/browser-console-addon-output.png diff --git a/devtools/docs/user/browser_console/browser-console-addon.png b/devtools/docs/user/browser_console/browser-console-addon.png Binary files differnew file mode 100644 index 0000000000..4aa274bab8 --- /dev/null +++ b/devtools/docs/user/browser_console/browser-console-addon.png diff --git a/devtools/docs/user/browser_console/browser-console-chromewindow.png b/devtools/docs/user/browser_console/browser-console-chromewindow.png Binary files differnew file mode 100644 index 0000000000..54047a7287 --- /dev/null +++ b/devtools/docs/user/browser_console/browser-console-chromewindow.png diff --git a/devtools/docs/user/browser_console/browser-console-commandline.png b/devtools/docs/user/browser_console/browser-console-commandline.png Binary files differnew file mode 100644 index 0000000000..1dd34bddb1 --- /dev/null +++ b/devtools/docs/user/browser_console/browser-console-commandline.png diff --git a/devtools/docs/user/browser_console/browser-console-modify-ui-osx.png b/devtools/docs/user/browser_console/browser-console-modify-ui-osx.png Binary files differnew file mode 100644 index 0000000000..e73b555a22 --- /dev/null +++ b/devtools/docs/user/browser_console/browser-console-modify-ui-osx.png diff --git a/devtools/docs/user/browser_console/browser-console-modify-ui-windows.png b/devtools/docs/user/browser_console/browser-console-modify-ui-windows.png Binary files differnew file mode 100644 index 0000000000..23c19bbae4 --- /dev/null +++ b/devtools/docs/user/browser_console/browser-console-modify-ui-windows.png diff --git a/devtools/docs/user/browser_console/browser_console_68.png b/devtools/docs/user/browser_console/browser_console_68.png Binary files differnew file mode 100644 index 0000000000..b1646e297b --- /dev/null +++ b/devtools/docs/user/browser_console/browser_console_68.png diff --git a/devtools/docs/user/browser_console/browser_console_68_02.png b/devtools/docs/user/browser_console/browser_console_68_02.png Binary files differnew file mode 100644 index 0000000000..bba33d4a57 --- /dev/null +++ b/devtools/docs/user/browser_console/browser_console_68_02.png diff --git a/devtools/docs/user/browser_console/index.rst b/devtools/docs/user/browser_console/index.rst new file mode 100644 index 0000000000..a22a43feb2 --- /dev/null +++ b/devtools/docs/user/browser_console/index.rst @@ -0,0 +1,159 @@ +=============== +Browser Console +=============== + +The Browser Console is like the :doc:`Web Console <../web_console/index>`, but applied to the whole browser rather than a single content tab. + +So it logs the same sorts of information as the Web Console - network requests, JavaScript, CSS, and security errors and warnings, and messages explicitly logged by JavaScript code. However, rather than logging this information for a single content tab, it logs information for all content tabs, for add-ons, and for the browser's own code. + +If you also want to use the other web developer tools in the regular Web :doc:`Toolbox <../tools_toolbox/index>` with add-on or browser code, consider using the :doc:`Browser Toolbox<../browser_toolbox/index>`. + +Similarly, you can execute JavaScript expressions using the Browser Console. But while the Web Console executes code in the page window scope, the Browser Console executes them in the scope of the browser's chrome window. This means you can interact with all the browser's tabs using the ``gBrowser`` global, and even with the XUL used to specify the browser's user interface. + +.. container:: block_quote + + NB: The Browser Console command line (to execute JavaScript expressions) is disabled by default. To enable it set the ``devtools.chrome.enabled`` preference to ``true`` in about:config, or set the "Enable browser `chrome <https://developer.mozilla.org/en-US/docs/Glossary/Chrome>`_ and add-on debugging toolboxes" (Firefox 40 and later) option in the :doc:`developer tool settings <../settings/index>`. + + +Opening the Browser Console +*************************** + +You can open the Browser Console in one of two ways: + +1. from the menu: select "Browser Console" from the Browser Tools submenu in the Firefox Menu (or Tools menu if you display the menu bar or are on macOS). + +2. from the keyboard: press :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`J` (or :kbd:`Cmd` + :kbd:`Shift` + :kbd:`J` on a Mac). + + +You can also start the Browser Console by launching Firefox from the command line and passing the ``-jsconsole`` argument: + +.. code-block:: + + /Applications/FirefoxAurora.app/Contents/MacOS/firefox-bin -jsconsole + +The Browser Console looks like this: + +.. image:: browser_console_68.png + + +You can see that the Browser Console looks and behaves very much like the :doc:`Web Console <../web_console/index>`: + +- most of the window is occupied by a :doc:`pane that display messages <../web_console/console_messages/index>` + +- at the top, a :ref:`toolbar <web_console_ui_tour_toolbar>` enables you to filter the messages that appear. + +- at the bottom, a :doc:`command line interpreter <../web_console/the_command_line_interpreter/index>` enables you to evaluate JavaScript expressions. + + +Beginning with Firefox 68, the Browser Console allows you to show or hide messages from the content process (i.e. the messages from scripts in all the opened pages) by setting or clearing the checkbox labeled **Show Content Messages**. The following image shows the browser console focused on the same page as above after clicking on the **Show Content Messages** checkbox. + +.. image:: browser_console_68_02.png + + +Browser Console logging +*********************** + +The Browser console logs the same sorts of messages as the Web Console: + +- :ref:`HTTP requests <web_console_console_messages>` + +- :doc:`Warnings and errors <../web_console/console_messages/index>` (including JavaScript, CSS, security warnings and errors, and messages explicitly logged by JavaScript code using the `Console API <https://developer.mozilla.org/en-US/docs/Web/API/console>`_ + +- :ref:`Input/output messages <web_console_console_messages_interpreter_io>` commands send to the browser via the command line, and the result of executing them + + +However, it displays such messages from: + +- web content hosted by all browser tabs +- the browser's own code +- add-ons + +Messages from add-ons +--------------------- + +The Browser Console displays messages logged by all Firefox add-ons. + +Console.sys.mjs +~~~~~~~~~~~~~~~ + +To use the console API from a traditional or bootstrapped add-on, get it from the Console module. + +One exported symbol from ``Console.sys.mjs`` is ``console``. Below is an example of how to access it, which adds a message to the Browser Console. + +.. code-block:: JavaScript + + const { console } = ChromeUtils.importESModule("resource://gre/modules/Console.sys.mjs"); + console.log("Hello from Firefox code"); //output messages to the console + +Learn more: + +- `Console API reference <https://developer.mozilla.org/en-US/docs/Web/API/console>`_ +- :searchfox:`Console.sys.mjs source code <toolkit/modules/Console.sys.mjs>` + + +Browser Console command line +**************************** + +.. container:: block_quote + + The Browser Console command line is disabled by default. To enable it set the ``devtools.chrome.enabled`` preference to ``true`` in ``about:config``, or set the "Enable chrome debugging" option in the :doc:`developer tool settings <../settings/index>`. + + +Like the Web Console, the command line interpreter enables you to evaluate JavaScript expressions in real time: + +.. image:: browser-console-commandline.png + +Also like the Web Console's command line interpreter, this command line supports autocomplete, history, and various :ref:`keyboard shortcuts <keyboard-shortcuts-web-console>` and :doc:`helper commands <../web_console/helpers/index>`. If the result of a command is an object, you can click on the object to see its details. + +But while the Web Console executes code in the scope of the content window it's attached to, the browser console executes code in the scope of the chrome window of the browser. You can confirm this by evaluating ``window``: + +.. image:: browser-console-chromewindow.png + +This means you can control the browser: opening, closing tabs and windows and changing the content that they host, and modify the browser's UI by creating, changing and removing XUL elements. + + +Controlling the browser +----------------------- + +The command line interpreter gets access to the ``tabbrowser`` object, through the ``gBrowser`` global, and that enables you to control the browser through the command line. Try running this code in the Browser Console's command line (remember that to send multiple lines to the Browser Console, use :kbd:`Shift` + :kbd:`Enter`): + +.. code-block:: JavaScript + + var newTabBrowser = gBrowser.getBrowserForTab(gBrowser.selectedTab); + newTabBrowser.addEventListener("load", function() { + newTabBrowser.contentDocument.body.innerHTML = "<h1>this page has been eaten</h1>"; + }, true); + newTabBrowser.contentDocument.location.href = "https://mozilla.org/"; + + +It adds a listener to the currently selected tab's ``load`` event that will eat the new page, then loads a new page. + +.. note:: + + You can restart the browser with the command :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`R` (Windows, Linux) or :kbd:`Cmd` + :kbd:`Alt` + :kbd:`R` (Mac) This command restarts the browser with the same tabs open as before the restart. + + +Modifying the browser UI +------------------------ + +Since the global ``window`` object is the browser's chrome window, you can also modify the browser's user interface. On Windows, the following code will add a new item to the browser's main menu: + +.. code-block:: JavaScript + + var parent = window.document.getElementById("appmenuPrimaryPane"); + var makeTheTea = gBrowser.ownerDocument.defaultView.document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem"); + makeTheTea.setAttribute("label", "A nice cup of tea?"); + parent.appendChild(makeTheTea); + +.. image:: browser-console-modify-ui-windows.png + +On macOS, this similar code will add a new item to the **Tools** menu: + +.. code-block:: JavaScript + + var parent = window.document.getElementById("menu_ToolsPopup"); + var makeTheTea = gBrowser.ownerDocument.defaultView.document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem"); + makeTheTea.setAttribute("label", "A nice cup of tea?"); + parent.appendChild(makeTheTea); + +.. image:: browser-console-modify-ui-osx.png diff --git a/devtools/docs/user/browser_toolbox/2014-01-10-13-08-08-f52b8c.png b/devtools/docs/user/browser_toolbox/2014-01-10-13-08-08-f52b8c.png Binary files differnew file mode 100644 index 0000000000..8a1cb2b691 --- /dev/null +++ b/devtools/docs/user/browser_toolbox/2014-01-10-13-08-08-f52b8c.png diff --git a/devtools/docs/user/browser_toolbox/browser-toolbox-iframes.png b/devtools/docs/user/browser_toolbox/browser-toolbox-iframes.png Binary files differnew file mode 100644 index 0000000000..8994e023bf --- /dev/null +++ b/devtools/docs/user/browser_toolbox/browser-toolbox-iframes.png diff --git a/devtools/docs/user/browser_toolbox/browser-toolbox-warning.png b/devtools/docs/user/browser_toolbox/browser-toolbox-warning.png Binary files differnew file mode 100644 index 0000000000..9fea742a25 --- /dev/null +++ b/devtools/docs/user/browser_toolbox/browser-toolbox-warning.png diff --git a/devtools/docs/user/browser_toolbox/browser-toolbox.png b/devtools/docs/user/browser_toolbox/browser-toolbox.png Binary files differnew file mode 100644 index 0000000000..e0881c1e1d --- /dev/null +++ b/devtools/docs/user/browser_toolbox/browser-toolbox.png diff --git a/devtools/docs/user/browser_toolbox/index.rst b/devtools/docs/user/browser_toolbox/index.rst new file mode 100644 index 0000000000..cf3fbdefba --- /dev/null +++ b/devtools/docs/user/browser_toolbox/index.rst @@ -0,0 +1,74 @@ +=============== +Browser Toolbox +=============== + +The Browser Toolbox enables you to debug add-ons and the browser's own JavaScript code rather than just web pages like the normal :doc:`Toolbox <../tools_toolbox/index>`. The Browser Toolbox's context is the whole browser rather than justsingle page on a single tab. + +Enabling the Browser Toolbox +**************************** + +The Browser Toolbox is not enabled by default. To enable it you need to check the settings "Enable chrome and addon debugging" and "Enable remote debugging". + +To do this, open the Developer Tools :doc:`Settings <../settings/index>`, go to the section :ref:`Advanced Settings <settings_advanced_settings>`, and check the settings "Enable browser chrome and add-on debugging toolboxes" and "Enable remote debugging". + +.. image:: settings_for_browser_debugger.png + :alt: Developer Tools Settings + :class: border + + +Opening the Browser Toolbox +*************************** + +.. |image1| image:: 2014-01-10-13-08-08-f52b8c.png + :alt: new fx menu + +Open the Browser Toolbox through the menu button |image1| and the menu items "Developer" then "Browser Toolbox". + +You can also open it with the :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`Shift` + :kbd:`I` key combination (:kbd:`Cmd` + :kbd:`Opt` + :kbd:`Shift` + :kbd:`I` on a Mac). + +You will be presented with a dialog like this (it can be removed by setting the ``devtools.debugger.prompt-connection`` property to false): + +.. image:: browser-toolbox-warning.png + +Click OK, and the Browser Toolbox will open in its own window: + +.. image:: browser-toolbox.png + +You'll be able to inspect the browser's chrome windows and see, and be able to debug, all the JavaScript files loaded by the browser itself and by any add-ons that are running. Altogether you will have access to the following developer tools: + + +- :doc:`Debugger <../debugger/index>` +- :doc:`Console <../browser_console/index>` +- :doc:`Style Editor <../style_editor/index>` +- :doc:`Performance <../performance/index>` +- :doc:`Network Monitor <../network_monitor/index>` +- :doc:`Page Inspector <../page_inspector/index>` +- :doc:`Accessibility Inspector <../accessibility_inspector/index>` + +You can debug chrome: and about: pages using the normal :doc:`Debugger <../debugger/index>`, just as if they were ordinary content pages. + + +Targeting a document +******************** + +In the normal toolbox, there's a :doc:`button in the toolbar enabling you to target specific iframes in the document <../working_with_iframes/index>`. The same button appears in the browser toolbox where it lists all the top-level chrome and content windows as well as any iframes they contain. This enables you to inspect documents in individual chrome windows and popups, as well as in content tabs. + +For example, here's what the frame selection popup lists when there are two browser windows open, one with one content tab, and one with two: + +.. image:: browser-toolbox-iframes.png + + +Debugging popups +**************** + +It's hard to debug popups, because the browser hides them as soon as you click outside them. There is a way to disable this behavior. Click the toolbox menu and select **Disable popup auto-hide**. + +.. image:: popup_auto-hide.png + :class: center + +Now when you open any popup, it will stay open until you press the :kbd:`Esc` key. You can use the Inspector's node picker to select that panel, and examine and modify its content. + +You can use the same technique to debug `popups created by add-ons <https://extensionworkshop.com/documentation/develop/debugging/#debugging_popups>`_. + +.. note:: + This change is not persistent across browser restarts. When you close the browser toolbox, the setting will be cleared. diff --git a/devtools/docs/user/browser_toolbox/popup_auto-hide.png b/devtools/docs/user/browser_toolbox/popup_auto-hide.png Binary files differnew file mode 100644 index 0000000000..ccdbeae32b --- /dev/null +++ b/devtools/docs/user/browser_toolbox/popup_auto-hide.png diff --git a/devtools/docs/user/browser_toolbox/settings_for_browser_debugger.png b/devtools/docs/user/browser_toolbox/settings_for_browser_debugger.png Binary files differnew file mode 100644 index 0000000000..a11b54b375 --- /dev/null +++ b/devtools/docs/user/browser_toolbox/settings_for_browser_debugger.png diff --git a/devtools/docs/user/camera_button.png b/devtools/docs/user/camera_button.png Binary files differnew file mode 100644 index 0000000000..330dea7056 --- /dev/null +++ b/devtools/docs/user/camera_button.png diff --git a/devtools/docs/user/close_button.png b/devtools/docs/user/close_button.png Binary files differnew file mode 100644 index 0000000000..85a1593767 --- /dev/null +++ b/devtools/docs/user/close_button.png diff --git a/devtools/docs/user/debugger-api/debugger.environment/index.rst b/devtools/docs/user/debugger-api/debugger.environment/index.rst new file mode 100644 index 0000000000..43baa06f25 --- /dev/null +++ b/devtools/docs/user/debugger-api/debugger.environment/index.rst @@ -0,0 +1,98 @@ +==================== +Debugger.Environment +==================== + +A ``Debugger.Environment`` instance represents a lexical environment, associating names with variables. Each :doc:`Debugger.Frame <../debugger.frame/index>` instance representing a debuggee frame has an associated environment object describing the variables in scope in that frame; and each :doc:`Debugger.Object <../debugger.object/index>` instance representing a debuggee function has an environment object representing the environment the function has closed over. + +ECMAScript environments form a tree, in which each local environment is parented by its enclosing environment (in ECMAScript terms, its ‘outer’ environment). We say an environment *binds* an identifier if that environment itself associates the identifier with a variable, independently of its outer environments. We say an identifier is *in scope* in an environment if the identifier is bound in that environment or any enclosing environment. + +SpiderMonkey creates ``Debugger.Environment`` instances as needed as the debugger inspects stack frames and function objects; calling ``Debugger.Environment`` as a function or constructor raises a ``TypeError`` exception. + +SpiderMonkey creates exactly one ``Debugger.Environment`` instance for each environment it presents via a given :doc:`Debugger <../debugger/index>` instance: if the debugger encounters the same environment through two different routes (perhaps two functions have closed over the same environment), SpiderMonkey presents the same ``Debugger.Environment`` instance to the debugger each time. This means that the debugger can use the ``==`` operator to recognize when two ``Debugger.Environment`` instances refer to the same environment in the debuggee, and place its own properties on a ``Debugger.Environment`` instance to store metadata about particular environments. + +(If more than one :doc:`Debugger <../debugger/index>` instance is debugging the same code, each :doc:`Debugger <../debugger/index>` gets a separate ``Debugger.Environment`` instance for a given environment. This allows the code using each :doc:`Debugger <../debugger/index>` instance to place whatever properties it likes on its own :doc:`Debugger.Object <../debugger.object/index>` instances, without worrying about interfering with other debuggers.) + +If a ``Debugger.Environment`` instance’s referent is not a debuggee environment, then attempting to access its properties (other than ``inspectable``) or call any its methods throws an instance of ``Error``. + +``Debugger.Environment`` instances protect their referents from the garbage collector; as long as the ``Debugger.Environment`` instance is live, the referent remains live. Garbage collection has no visible effect on ``Debugger.Environment`` instances. + + +Accessor Properties of the Debugger.Environment Prototype Object +**************************************************************** + +A ``Debugger.Environment`` instance inherits the following accessor properties from its prototype: + + +``inspectable`` + + True if this environment is a debuggee environment, and can therefore be inspected. False otherwise. All other properties and methods of ``Debugger.Environment`` instances throw if applied to a non-inspectable environment. + +``type`` + + The type of this environment object, one of the following values: + + - “declarative”, indicating that the environment is a declarative environment record. Function calls, calls to ``eval``, ``let`` blocks, ``catch`` blocks, and the like create declarative environment records. + + - “object”, indicating that the environment’s bindings are the properties of an object. The global object and DOM elements appear in the chain of environments via object environments. (Note that ``with`` statements have their own environment type.) + + - “with”, indicating that the environment was introduced by a ``with`` statement. + +``parent`` + + The environment that encloses this one (the “outer” environment, in ECMAScript terminology), or ``null`` if this is the outermost environment. + +``object`` + + A :doc:`Debugger.Object <../debugger.object/index>` instance referring to the object whose properties this environment reflects. If this is a declarative environment record, this accessor throws a ``TypeError`` (since declarative environment records have no such object). Both ``"object"`` and ``"with"`` environments have ``object`` properties that provide the object whose properties they reflect as variable bindings. + +``callee`` + + If this environment represents the variable environment (the top-level environment within the function, which receives ``var`` definitions) for a call to a function *f*, then this property’s value is a :doc:`Debugger.Object <../debugger.object/index>` instance referring to *f*. Otherwise, this property’s value is ``null``. + +``optimizedOut`` + + True if this environment is optimized out. False otherwise. For example, functions whose locals are never aliased may present optimized-out environments. When true, ``getVariable`` returns an ordinary JavaScript object whose ``optimizedOut`` property is true on all bindings, and ``setVariable`` throws a ``ReferenceError``. + + +Function Properties of the Debugger.Environment Prototype Object +**************************************************************** + +The methods described below may only be called with a ``this`` value referring to a ``Debugger.Environment`` instance; they may not be used as methods of other kinds of objects. + + +``names()`` + + Return an array of strings giving the names of the identifiers bound by this environment. The result does not include the names of identifiers bound by enclosing environments. + +``getVariable(*name*)`` + + Return the value of the variable bound to *name* in this environment, or ``undefined`` if this environment does not bind *name*. *Name* must be a string that is a valid ECMAScript identifier name. The result is a debuggee value. + + JavaScript engines often omit variables from environments, to save space and reduce execution time. If the given variable should be in scope, but ``getVariable`` is unable to produce its value, it returns an ordinary JavaScript object (not a :doc:`Debugger.Object <../debugger.object/index>` instance) whose ``optimizedOut`` property is ``true``. + + This is not an :ref:`invocation function <debugger-api-debugger-frame-invf>`; if this call would cause debuggee code to run (say, because the environment is a ``"with"`` environment, and *name* refers to an accessor property of the ``with`` statement’s operand), this call throws a ``Debugger.DebuggeeWouldRun`` exception. + +``setVariable(*name*,*value*)`` + + Store *value* as the value of the variable bound to *name* in this environment. *Name* must be a string that is a valid ECMAScript identifier name; *value* must be a debuggee value. + + If this environment binds no variable named *name*, throw a ``ReferenceError``. + + This is not an :ref:`invocation function <debugger-api-debugger-frame-invf>`; if this call would cause debuggee code to run, this call throws a ``Debugger.DebuggeeWouldRun`` exception. + +``find(*name*)`` + + Return a reference to the innermost environment, starting with this environment, that binds *name*. If *name* is not in scope in this environment, return ``null``. *Name* must be a string whose value is a valid ECMAScript identifier name. + + +Source Metadata +--------------- + +Generated from file: + js/src/doc/Debugger/Debugger.Environment.md + +Watermark: + sha256:3d6f67939e351803d5d7fe201ed38c4aaf766caf032f255e168df1f1c6fe73cb + +Changeset: + `7ae377917236 <https://hg.mozilla.org/mozilla-central/rev/7ae377917236>`_ diff --git a/devtools/docs/user/debugger-api/debugger.frame/index.rst b/devtools/docs/user/debugger-api/debugger.frame/index.rst new file mode 100644 index 0000000000..b1c53f19e4 --- /dev/null +++ b/devtools/docs/user/debugger-api/debugger.frame/index.rst @@ -0,0 +1,202 @@ +============== +Debugger.Frame +============== + +A ``Debugger.Frame`` instance represents a :ref:`visible stack frame <debugger-api-debugger-frame-visible-frames>`. Given a ``Debugger.Frame`` instance, you can find the script the frame is executing, walk the stack to older frames, find the lexical environment in which the execution is taking place, and so on. + +For a given :doc:`Debugger <../debugger/index>` instance, SpiderMonkey creates only one ``Debugger.Frame`` instance for a given visible frame. Every handler method called while the debuggee is running in a given frame is given the same frame object. Similarly, walking the stack back to a previously accessed frame yields the same frame object as before. Debugger code can add its own properties to a frame object and expect to find them later, use ``==`` to decide whether two expressions refer to the same frame, and so on. + +(If more than one :doc:`Debugger <../debugger/index>` instance is debugging the same code, each :doc:`Debugger <../debugger/index>` gets a separate ``Debugger.Frame`` instance for a given frame. This allows the code using each :doc:`Debugger <../debugger/index>` instance to place whatever properties it likes on its ``Debugger.Frame`` instances, without worrying about interfering with other debuggers.) + +When the debuggee pops a stack frame (say, because a function call has returned or an exception has been thrown from it), the ``Debugger.Frame`` instance referring to that frame becomes inactive: its ``live`` property becomes ``false``, and accessing its other properties or calling its methods throws an exception. Note that frames only become inactive at times that are predictable for the debugger: when the debuggee runs, or when the debugger removes frames from the stack itself. + + +.. _debugger-api-debugger-frame-visible-frames: + +Visible frames +************** + +When inspecting the call stack, :doc:`Debugger <../debugger/index>` does not reveal all the frames that are actually present on the stack: while it does reveal all frames running debuggee code, it omits frames running the debugger’s own code, and omits most frames running non-debuggee code. We call those stack frames a :doc:`Debugger <../debugger/index>` does reveal *visible frames*. + +A frame is a visible frame if any of the following are true: + + +- it is running debuggee code; + +- its immediate caller is a frame running debuggee code; or + +- it is a :ref:`"debugger" <debugger-api-debugger-frame-invf>`, representing the continuation of debuggee code invoked by the debugger. + + +The “immediate caller” rule means that, when debuggee code calls a non-debuggee funct`on, it looks like a call to a primitive: you see a frame for the non-debuggee function that was accessible to the debuggee, but any further calls that function makes are treated as internal details, and omitted from the stack trace. If the non-debuggee function eventually calls back into debuggee code, then those frames are visible. + +(Note that the debuggee is not considered an “immediate caller” of handler methods it triggers. Even though the debuggee and debugger share the same JavaScript stack, frames pushed for SpiderMonkey’s calls to handler methods to report events in the debuggee are never considered visible frames.) + + +.. _debugger-api-debugger-frame-invf: + +Invocation functions and Debugger frames +**************************************** + +An *invocation function* is any function in this interface that allows the debugger to invoke code in the debuggee: ``Debugger.Object.prototype.call``, ``Debugger.Frame.prototype.eval``, and so on. + +While invocation functions differ in the code to be run and how to pass values to it, they all follow this general procedure: + +1. Let *older* be the youngest visible frame on the stack, or ``null`` if there is no such frame. (This is never one of the debugger’s own frames; those never appear as ``Debugger.Frame`` instances.) + +2. Push a ``"debugger"`` frame on the stack, with *older* as its ``older`` property. + +3. Invoke the debuggee code as appropriate for the given invocation function, with the ``"debugger"`` frame as its continuation. For example, ``Debugger.Frame.prototype.eval`` pushes an ``"eval"`` frame for code it runs, whereas ``Debugger.Object.prototype.call`` pushes a ``"call"`` frame. + +4. When the debuggee code completes, whether by returning, throwing an exception or being terminated, pop the ``"debugger"`` frame, and return an appropriate completion value from the invocation function to the debugger. + + +When a debugger calls an invocation function to run debuggee code, that code’s continuation is the debugger, not the next debuggee code frame. Pushing a ``"debugger"`` frame makes this continuation explicit, and makes it easier to find the extent of the stack created for the invocation. + + +Accessor properties of the Debugger.Frame prototype object +********************************************************** + +A ``Debugger.Frame`` instance inherits the following accessor properties from its prototype: + + +``type`` + + A string describing what sort of frame this is: + + - ``"call"``: a frame running a function call. (We may not be able to obtain frames for calls to host functions.) + + - ``"eval"``: a frame running code passed to ``eval``. + - ``"global"``: a frame running global code (JavaScript that is neither of the above). + - ``"module"``: a frame running code at the top level of a module. + - ``"wasmcall"``: a frame running a WebAssembly function call. + - ``"debugger"``: a frame for a call to user code invoked by the debugger (see the ``eval`` method below). + +``implementation`` + + A string describing which tier of the JavaScript engine this frame is executing in: + + - ``"interpreter"``: a frame running in the interpreter. + - ``"baseline"``: a frame running in the unoptimizing, baseline JIT. + - ``"ion"``: a frame running in the optimizing JIT. + - ``"wasm"``: a frame running in WebAssembly baseline JIT. + +``this`` + + The value of ``this`` for this frame (a debuggee value). For a ``wasmcall`` frame, this property throws a ``TypeError``. + +``older`` + + The next-older visible frame, in which control will resume when this frame completes. If there is no older frame, this is ``null``. + +``depth`` + The depth of this frame, counting from oldest to youngest; the oldest frame has a depth of zero. + +``live`` + True if the frame this ``Debugger.Frame`` instance refers to is still on the stack; false if it has completed execution or been popped in some other way. + +``script`` + The script being executed in this frame (a :doc:`Debugger.Script <../debugger.script/index>` instance), or ``null`` on frames that do not represent calls to debuggee code. On frames whose ``callee`` property is not null, this is equal to ``callee.script``. + +``offset`` + The offset of the bytecode instruction currently being executed in ``script``, or ``undefined`` if the frame’s ``script`` property is ``null``. For a ``wasmcall`` frame, this property throws a ``TypeError``. + +``environment`` + The lexical environment within which evaluation is taking place (a :doc:`Debugger.Environment <../debugger.environment/index>` instance), or ``null`` on frames that do not represent the evaluation of debuggee code, like calls non-debuggee functions, host functions or ``"debugger"`` frames. + +``callee`` + The function whose application created this frame, as a debuggee value, or ``null`` if this is not a ``"call"`` frame. + +``generator`` + True if this frame is a generator frame, false otherwise. + +``constructing`` + True if this frame is for a function called as a constructor, false otherwise. + +``arguments`` + The arguments passed to the current frame, or ``null`` if this is not a ``"call"`` frame. When non-``null``, this is an object, allocated in the same global as the debugger, with ``Array.prototype`` on its prototype chain, a non-writable ``length`` property, and properties whose names are array indices. Each property is a read-only accessor property whose getter returns the current value of the corresponding parameter. When the referent frame is popped, the argument value’s properties’ getters throw an error. + + +Handler methods of Debugger.Frame instances +******************************************* + +Each ``Debugger.Frame`` instance inherits accessor properties holding handler functions for SpiderMonkey to call when given events occur in the frame. + +Calls to frames’ handler methods are cross-compartment, intra-thread calls: the call takes place in the thread to which the frame belongs, and runs in the compartment to which the handler method belongs. + +``Debugger.Frame`` instances inherit the following handler method properties: + + +``onStep`` + + This property must be either ``undefined`` or a function. If it is a function, SpiderMonkey calls it when execution in this frame makes a small amount of progress, passing no arguments and providing this ``Debugger.Frame`` instance as the ``this`` value. The function should return a resumption value specifying how the debuggee’s execution should proceed. + + What constitutes “a small amount of progress” varies depending on the implementation, but it is fine-grained enough to implement useful “step” and “next” behavior. + + If multiple :doc:`Debugger <../debugger/index>` instances each have ``Debugger.Frame`` instances for a given stack frame with ``onStep`` handlers set, their handlers are run in an unspecified order. If any ``onStep`` handler forces the frame to return early (by returning a resumption value other than ``undefined``), any remaining debuggers’ ``onStep`` handlers do not run. + + This property is ignored on frames that are not executing debuggee code, like ``"call"`` frames for calls to host functions and ``"debugger"`` frames. + +``onPop`` + + This property must be either ``undefined`` or a function. If it is a function, SpiderMonkey calls it just before this frame is popped, passing a completion value indicating how this frame’s execution completed, and providing this ``Debugger.Frame`` instance as the ``this`` value. The function should return a resumption value indicating how execution should proceed. On newly created frames, this property’s value is ``undefined``. + + When this handler is called, this frame’s current execution location, as reflected in its ``offset`` and ``environment`` properties, is the operation which caused it to be unwound. In frames returning or throwing an exception, the location is often a return or a throw statement. In frames propagating exceptions, the location is a call. + + When an ``onPop`` call reports the completion of a construction call (that is, a function called via the ``new`` operator), the completion value passed to the handler describes the value returned by the function body. If this value is not an object, it may be different from the value produced by the ``new`` expression, which will be the value of the frame’s ``this`` property. (In ECMAScript terms, the ``onPop`` handler receives the value returned by the ``[[Call]]`` method, not the value returned by the ``[[Construct]]`` method.) + + When a debugger handler function forces a frame to complete early, by returning a ``{ return:... }``, ``{ throw:... }``, or ``null`` resumption value, SpiderMonkey calls the frame’s ``onPop`` handler, if any. The completion value passed in this case reflects the resumption value that caused the frame to complete. + + When SpiderMonkey calls an ``onPop`` handler for a frame that is throwing an exception or being terminated, and the handler returns ``undefined``, then SpiderMonkey proceeds with the exception or termination. That is, an ``undefined`` resumption value leaves the frame’s throwing and termination process undisturbed. + + If multiple :doc:`Debugger <../debugger/index>` instances each have ``Debugger.Frame`` instances for a given stack frame with ``onPop`` handlers set, their handlers are run in an unspecified order. The resumption value each handler returns establishes the completion value reported to the next handler. + + This handler is not called on ``"debugger"`` frames. It is also not called when unwinding a frame due to an over-recursion or out-of-memory exception. + + +Function properties of the Debugger.Frame prototype object +********************************************************** + +The functions described below may only be called with a ``this`` value referring to a ``Debugger.Frame`` instance; they may not be used as methods of other kinds of objects. + +.. _debugger-api-debugger-frame-eval: + +``eval(code, [options])`` + + Evaluate *code* in the execution context of this frame, and return a completion value describing how it completed. *Code* is a string. If this frame’s ``environment`` property is ``null`` or ``type`` property is ``wasmcall``, throw a ``TypeError``. All extant handler methods, breakpoints, and so on remain active during the call. This function follows the :ref:`invocation function convention <debugger-api-debugger-frame-invf>`. + + *Code* is interpreted as strict mode code when it contains a Use Strict Directive, or the code executing in this frame is strict mode code. + + If *code* is not strict mode code, then variable declarations in *code* affect the environment of this frame. (In the terms used by the ECMAScript specification, the ``VariableEnvironment`` of the execution context for the eval code is the ``VariableEnvironment`` of the execution context that this frame represents.) If implementation restrictions prevent SpiderMonkey from extending this frame’s environment as requested, this call throws an Error exception. + + If given, *options* should be an object whose properties specify details of how the evaluation should occur. The ``eval`` method recognizes the following properties: + + + ``url`` + The filename or URL to which we should attribute *code*. If this property is omitted, the URL defaults to ``"debugger eval code"``. + ``lineNumber`` + The line number at which the evaluated code should be claimed to begin within *url*. + + +``evalWithBindings(*code*,*bindings*, [*options*])`` + + Like ``eval``, but evaluate *code* in the environment of this frame, extended with bindings from the object *bindings*. For each own enumerable property of *bindings* named *name* whose value is *value*, include a variable in the environment in which *code* is evaluated named *name*, whose value is *value*. Each *value* must be a debuggee value. (This is not like a ``with`` statement: *code* may access, assign to, and delete the introduced bindings without having any effect on the *bindings* object.) + + This method allows debugger code to introduce temporary bindings that are visible to the given debuggee code and which refer to debugger-held debuggee values, and do so without mutating any existing debuggee environment. + + Note that, like ``eval``, declarations in the *code* passed to ``evalWithBindings`` affect the environment of this frame, even as that environment is extended by bindings visible within *code*. (In the terms used by the ECMAScript specification, the ``VariableEnvironment`` of the execution context for the eval code is the ``VariableEnvironment`` of the execution context that this frame represents, and the *bindings* appear in a new declarative environment, which is the eval code’s ``LexicalEnvironment``.) If implementation restrictions prevent SpiderMonkey from extending this frame’s environment as requested, this call throws an ``Error`` exception. + + The *options* argument is as for :ref:`Debugger.Frame.prototype.eval <debugger-api-debugger-frame-eval>`, described above. Also like ``eval``, if this frame’s ``environment`` property is ``null`` or ``type`` property is ``wasmcall``, throw a ``TypeError``. + + +Source Metadata +--------------- + +Generated from file: + js/src/doc/Debugger/Debugger.Frame.md + +Watermark: + sha256:b1894f88b76b7afd661f3044a05690d76d1498c54c596ca729c6ee0d150d2da1 + +Changeset: + `e91b2c85aacd <https://hg.mozilla.org/mozilla-central/rev/e91b2c85aacd>`_ diff --git a/devtools/docs/user/debugger-api/debugger.memory/index.rst b/devtools/docs/user/debugger-api/debugger.memory/index.rst new file mode 100644 index 0000000000..17f659bba8 --- /dev/null +++ b/devtools/docs/user/debugger-api/debugger.memory/index.rst @@ -0,0 +1,240 @@ +=============== +Debugger.Memory +=============== + +The :doc:`Debugger API <../index>` can help tools observe the debuggee’s memory use in various ways: + + +- It can mark each new object with the JavaScript call stack at which it was allocated. + +- It can log all object allocations, yielding a stream of JavaScript call stacks at which allocations have occurred. + +- It can compute a *census* of items belonging to the debuggee, categorizing items in various ways, and yielding item counts. + + +If *dbg* is a :doc:`Debugger <../debugger/index>` instance, then the methods and accessor properties of ``dbg.memory`` control how *dbg* observes its debuggees’ memory use. The ``dbg.memory`` object is an instance of ``Debugger.Memory``; its inherited accessors and methods are described below. + + +Allocation Site Tracking +************************ + +The JavaScript engine marks each new object with the call stack at which it was allocated, if: + + +- the object is allocated in the scope of a global object that is a debuggee of some :doc:`Debugger <../debugger/index>` instance *dbg*; and + +- :ref:`dbg.memory.trackingAllocationSites <debugger-api-debugger-memory-tracking-allocation-sites>` is set to ``true``. + +- A `Bernoulli trial <https://en.wikipedia.org/wiki/Bernoulli_trial>`_ succeeds, with probability equal to the maximum of :ref:`d.memory.allocationSamplingProbability <debugger-api-debugger-memory-alloc-sampling-probability>` of all ``Debugger`` instances ``d`` that are observing the global that this object is allocated within the scope of. + + +Given a :doc:`Debugger.Object <../debugger.object/index>` instance *dobj* referring to some object, :ref:`dobj.allocationSite <debugger-api-debugger-object-allocation-site>` returns a saved call stack indicating where *dobj*’s referent was allocated. + + +Allocation Logging +****************** + +If *dbg* is a :doc:`Debugger <../debugger/index>` instance, and :ref:`dbg.memory.trackingAllocationSites <debugger-api-debugger-memory-tracking-allocation-sites>` is set to ``true``, then the JavaScript engine logs each object allocated by *dbg*’s debuggee code. You can retrieve the current log by calling *dbg.memory.drainAllocationsLog*. You can control the limit on the log’s size by setting :ref:`dbg.memory.maxAllocationsLogLength <debugger-api-debugger-memory-max-alloc-log>`. + + +Censuses +******** + +A *census* is a complete traversal of the graph of all reachable memory items belonging to a particular ``Debugger``‘s debuggees. It produces a count of those items, broken down by various criteria. If *dbg* is a :doc:`Debugger <../debugger/index>` instance, you can call *dbg.memory.takeCensus* to conduct a census of its debuggees’ possessions. + + +Accessor Properties of the ``Debugger.Memory.prototype`` Object +*************************************************************** + +If *dbg* is a :doc:`Debugger <../debugger/index>` instance, then ``dbg.memory`` is a ``Debugger.Memory`` instance, which inherits the following accessor properties from its prototype: + + +.. _debugger-api-debugger-memory-tracking-allocation-sites: + +``trackingAllocationSites`` + + A boolean value indicating whether this ``Debugger.Memory`` instance is capturing the JavaScript execution stack when each Object is allocated. This accessor property has both a getter and setter: assigning to it enables or disables the allocation site tracking. Reading the accessor produces ``true`` if the Debugger is capturing stacks for Object allocations, and ``false`` otherwise. Allocation site tracking is initially disabled in a new Debugger. + + Assignment is fallible: if the Debugger cannot track allocation sites, it throws an ``Error`` instance. + + You can retrieve the allocation site for a given object with the :ref:`Debugger.Object.prototype.allocationSite <debugger-api-debugger-object-allocation-site>` accessor property. + + +.. _debugger-api-debugger-memory-alloc-sampling-probability: + +``allocationSamplingProbability`` + + A number between 0 and 1 that indicates the probability with which each new allocation should be entered into the allocations log. 0 is equivalent to “never”, 1 is “always”, and .05 would be “one out of twenty”. + + The default is 1, or logging every allocation. + + Note that in the presence of multiple ``Debugger`` instances observing the same allocations within a global’s scope, the maximum ``allocationSamplingProbability`` of all the ``Debugger`` is used. + + +.. _debugger-api-debugger-memory-max-alloc-log: + +``maxAllocationsLogLength`` + + The maximum number of allocation sites to accumulate in the allocations log at a time. This accessor can be both fetched and stored to. Its default value is ``5000``. + + +.. _debugger-api-debugger-memory-alloc-log-overflowed: + +``allocationsLogOverflowed`` + + Returns ``true`` if there have been more than [``maxAllocationsLogLength``][#max-alloc-log] allocations since the last time [``drainAllocationsLog``][#drain-alloc-log] was called and some data has been lost. Returns ``false`` otherwise. + + +Debugger.Memory Handler Functions +********************************* + +Similar to :doc:`Debugger's handler functions <../index>`, ``Debugger.Memory`` inherits accessor properties that store handler functions for SpiderMonkey to call when given events occur in debuggee code. + +Unlike ``Debugger``‘s hooks, ``Debugger.Memory``’s handlers’ return values are not significant, and are ignored. The handler functions receive the ``Debugger.Memory``’s owning ``Debugger`` instance as their ``this`` value. The owning ``Debugger``’s ``uncaughtExceptionHandler`` is still fired for errors thrown in ``Debugger.Memory`` hooks. + +On a new ``Debugger.Memory`` instance, each of these properties is initially ``undefined``. Any value assigned to a debugging handler must be either a function or ``undefined``; otherwise a ``TypeError`` is thrown. + +Handler functions run in the same thread in which the event occurred. They run in the compartment to which they belong, not in a debuggee compartment. + + +``onGarbageCollection(statistics)`` + + A garbage collection cycle spanning one or more debuggees has just been completed. + + The *statistics* parameter is an object containing information about the GC cycle. It has the following properties: + + +``collections`` + + The ``collections`` property’s value is an array. Because SpiderMonkey’s collector is incremental, a full collection cycle may consist of multiple discrete collection slices with the JS mutator running interleaved. For each collection slice that occurred, there is an entry in the ``collections`` array with the following form: + + .. code-block:: javascript + + { + "startTimestamp": timestamp, + "endTimestamp": timestamp, + } + + Here the *timestamp* values are timestamps of the GC slice’s start and end events. + +``reason`` + + A very short string describing the reason why the collection was triggered. Known values include the following: + + - “API” + - “EAGER_ALLOC_TRIGGER” + - “DESTROY_RUNTIME” + - “LAST_DITCH” + - “TOO_MUCH_MALLOC” + - “ALLOC_TRIGGER” + - “DEBUG_GC” + - “COMPARTMENT_REVIVED” + - “RESET” + - “OUT_OF_NURSERY” + - “EVICT_NURSERY” + - “FULL_STORE_BUFFER” + - “SHARED_MEMORY_LIMIT” + - “PERIODIC_FULL_GC” + - “INCREMENTAL_TOO_SLOW” + - “DOM_WINDOW_UTILS” + - “COMPONENT_UTILS” + - “MEM_PRESSURE” + - “CC_WAITING” + - “CC_FORCED” + - “LOAD_END” + - “PAGE_HIDE” + - “NSJSCONTEXT_DESTROY” + - “SET_NEW_DOCUMENT” + - “SET_DOC_SHELL” + - “DOM_UTILS” + - “DOM_IPC” + - “DOM_WORKER” + - “INTER_SLICE_GC” + - “REFRESH_FRAME” + - “FULL_GC_TIMER” + - “SHUTDOWN_CC” + - “USER_INACTIVE” + + +``nonincrementalReason`` + + If SpiderMonkey’s collector determined it could not incrementally collect garbage, and had to do a full GC all at once, this is a short string describing the reason it determined the full GC was necessary. Otherwise, ``null`` is returned. Known values include the following: + + - “GC mode” + - “malloc bytes trigger” + - “allocation trigger” + - “requested” + + +``gcCycleNumber`` + + The GC cycle’s “number”. Does not correspond to the number of GC cycles that have run, but is guaranteed to be monotonically increasing. + + + +Function Properties of the ``Debugger.Memory.prototype`` Object +*************************************************************** + +Memory Use Analysis Exposes Implementation Details + +Memory analysis may yield surprising results, because browser implementation details that are transparent to content JavaScript often have visible effects on memory consumption. Web developers need to know their pages’ actual memory consumption on real browsers, so it is correct for the tool to expose these behaviors, as long as it is done in a way that helps developers make decisions about their own code. + +This section covers some areas where Firefox’s actual behavior deviates from what one might expect from the specified behavior of the web platform. + +Objects +------- + +SpiderMonkey objects usually use less memory than the naïve “table of properties with attributes” model would suggest. For example, it is typical for many objects to have identical sets of properties, with only the properties’ values varying from one object to the next. To take advantage of this regularity, SpiderMonkey objects with identical sets of properties may share their property metadata; only property values are stored directly in the object. + +Array objects may also be optimized, if the set of live indices is dense. + + +Strings +------- + +SpiderMonkey has three representations of strings: + + +- Normal: the string’s text is counted in its size. + +- Substring: the string is a substring of some other string, and points to that string for its storage. This representation may result in a small string retaining a very large string. However, the memory consumed by the string itself is a small constant independent of its size, since it is a reference to the larger string, a start position, and a length. + +- Concatenations: When asked to concatenate two strings, SpiderMonkey may elect to delay copying the strings’ data, and represent the result as a pointer to the two original strings. Again, such a string retains other strings, but the memory consumed by the string itself is a small constant independent of its size, since it is a pair of pointers. + + +SpiderMonkey converts strings from the more complex representations to the simpler ones when it pleases. Such conversions usually increase memory consumption. + +SpiderMonkey shares some strings amongst all web pages and browser JS. These shared strings, called *atoms*, are not included in censuses’ string counts. + + +Scripts +------- + +SpiderMonkey has a complex, hybrid representation of JavaScript code. There are four representations kept in memory: + + +- *Source code*. SpiderMonkey retains a copy of most JavaScript source code. + +- *Compressed source code*. SpiderMonkey compresses JavaScript source code, and de-compresses it on demand. Heuristics determine how long to retain the uncompressed code. + +- *Bytecode*. This is SpiderMonkey’s parsed representation of JavaScript. Bytecode can be interpreted directly, or used as input to a just-in-time compiler. Source is parsed into bytecode on demand; functions that are never called are never parsed. + +- *Machine code*. SpiderMonkey includes several just-in-time compilers, each of which translates JavaScript source or bytecode to machine code. Heuristics determine which code to compile, and which compiler to use. Machine code may be dropped in response to memory pressure, and regenerated as needed. + + +Furthermore, SpiderMonkey tracks which types of values have appeared in variables and object properties. This type information can be large. + +In a census, all the various forms of JavaScript code are placed in the ``"script"`` category. Type information is accounted to the ``"types"`` category. + + +Source Metadata +--------------- + +Generated from file: + js/src/doc/Debugger/Debugger.Memory.md + +Watermark: + sha256:2c1529d6932efec8c624a6f1f366b09cb7fce625a6468657fab81788240bc7ae + +Changeset: + `e91b2c85aacd <https://hg.mozilla.org/mozilla-central/rev/e91b2c85aacd>`_ diff --git a/devtools/docs/user/debugger-api/debugger.object/index.rst b/devtools/docs/user/debugger-api/debugger.object/index.rst new file mode 100644 index 0000000000..e989195b25 --- /dev/null +++ b/devtools/docs/user/debugger-api/debugger.object/index.rst @@ -0,0 +1,310 @@ +=============== +Debugger.Object +=============== + +A ``Debugger.Object`` instance represents an object in the debuggee, providing reflection-oriented methods to inspect and modify its referent. The referent’s properties do not appear directly as properties of the ``Debugger.Object`` instance; the debugger can access them only through methods like ``Debugger.Object.prototype.getOwnPropertyDescriptor`` and ``Debugger.Object.prototype.defineProperty``, ensuring that the debugger will not inadvertently invoke the referent’s getters and setters. + +SpiderMonkey creates exactly one ``Debugger.Object`` instance for each debuggee object it presents to a given :doc:`Debugger <../debugger/index>` instance: if the debugger encounters the same object through two different routes (perhaps two functions are called on the same object), SpiderMonkey presents the same ``Debugger.Object`` instance to the debugger each time. This means that the debugger can use the ``==`` operator to recognize when two ``Debugger.Object`` instances refer to the same debuggee object, and place its own properties on a ``Debugger.Object`` instance to store metadata about particular debuggee objects. + +JavaScript code in different compartments can have different views of the same object. For example, in Firefox, code in privileged compartments sees content DOM element objects without redefinitions or extensions made to that object’s properties by content code. (In Firefox terminology, privileged code sees the element through an “xray wrapper”.) To ensure that debugger code sees each object just as the debuggee would, each ``Debugger.Object`` instance presents its referent as it would be seen from a particular compartment. This “viewing compartment” is chosen to match the way the debugger came across the referent. As a consequence, a single :doc:`Debugger <../debugger/index>` instance may actually have several ``Debugger.Object`` instances: one for each compartment from which the referent is viewed. + +If more than one :doc:`Debugger <../debugger/index>` instance is debugging the same code, each :doc:`Debugger <../debugger/index>` gets a separate ``Debugger.Object`` instance for a given object. This allows the code using each :doc:`Debugger <../debugger/index>` instance to place whatever properties it likes on its own ``Debugger.Object`` instances, without worrying about interfering with other debuggers. + +While most ``Debugger.Object`` instances are created by SpiderMonkey in the process of exposing debuggee’s behavior and state to the debugger, the debugger can use ``Debugger.Object.prototype.makeDebuggeeValue`` to create ``Debugger.Object`` instances for given debuggee objects, or use ``Debugger.Object.prototype.copy`` and ``Debugger.Object.prototype.create`` to create new objects in debuggee compartments, allocated as if by particular debuggee globals. + +``Debugger.Object`` instances protect their referents from the garbage collector; as long as the ``Debugger.Object`` instance is live, the referent remains live. This means that garbage collection has no visible effect on ``Debugger.Object`` instances. + + +Accessor Properties of the Debugger.Object prototype +**************************************************** + +A ``Debugger.Object`` instance inherits the following accessor properties from its prototype: + + +``proto`` + The referent’s prototype (as a new ``Debugger.Object`` instance), or ``null`` if it has no prototype. This accessor may throw if the referent is a scripted proxy or some other sort of exotic object (an opaque wrapper, for example). + +``class`` + A string naming the ECMAScript ``[[Class]]`` of the referent. + +``callable`` + ``true`` if the referent is a callable object (such as a function or a function proxy); false otherwise. + +``name`` + The name of the referent, if it is a named function. If the referent is an anonymous function, or not a function at all, this is ``undefined``. + + This accessor returns whatever name appeared after the ``function`` keyword in the source code, regardless of whether the function is the result of instantiating a function declaration (which binds the function to its name in the enclosing scope) or evaluating a function expression (which binds the function to its name only within the function’s body). + +``displayName`` + + The referent’s display name, if the referent is a function with a display name. If the referent is not a function, or if it has no display name, this is ``undefined``. + + If a function has a given name, its display name is the same as its given name. In this case, the ``displayName`` and ``name`` properties are equal. + + If a function has no name, SpiderMonkey attempts to infer an appropriate name for it given its context. For example: + + .. code-block:: javascript + + function f() {} // display name: f (the given name) + var g = function () {}; // display name: g + o.p = function () {}; // display name: o.p + var q = { + r: function () {} // display name: q.r + }; + + Note that the display name may not be a proper JavaScript identifier, or even a proper expression: we attempt to find helpful names even when the function is not immediately assigned as the value of some variable or property. Thus, we use ``a/b`` to refer to the *b* defined within *a*, and ``a<`` to refer to a function that occurs somewhere within an expression that is assigned to *a*. For example: + + .. code-block:: javascript + + function h() { + var i = function() {}; // display name: h/i + f(function () {}); // display name: h/< + } + var s = f(function () {}); // display name: s<``</pre> + +``parameterNames`` + If the referent is a debuggee function, the names of the its parameters, as an array of strings. If the referent is not a debuggee function, or not a function at all, this is ``undefined``. + + If the referent is a host function for which parameter names are not available, return an array with one element per parameter, each of which is ``undefined``. + + If the referent is a function proxy, return an empty array. + + If the referent uses destructuring parameters, then the array’s elements reflect the structure of the parameters. For example, if the referent is a function declared in this way: + + .. code-block:: javascript + + function f(a, [b, c], {d, e:f}) { ... } + + then this ``Debugger.Object`` instance’s ``parameterNames`` property would have the value: + + .. code-block:: javascript + + ["a", ["b", "c"], {d:"d", e:"f"}] + +``script`` + If the referent is a function that is debuggee code, this is that function’s script, as a :doc:`Debugger.Script <../debugger.script/index>` instance. If the referent is a function proxy or not debuggee code, this is ``undefined``. + +``environment`` + If the referent is a function that is debuggee code, a :doc:`Debugger.Environment <../debugger.environment/index>` instance representing the lexical environment enclosing the function when it was created. If the referent is a function proxy or not debuggee code, this is ``undefined``. + +``errorMessageName`` + If the referent is an error created with an engine internal message template this is a string which is the name of the template; ``undefined`` otherwise. + +``errorLineNumber`` + If the referent is an Error object, this is the line number at which the referent was created; ``undefined`` otherwise. + +``errorColumnNumber`` + If the referent is an Error object, this is the column number at which the referent was created; ``undefined`` otherwise. + +``isBoundFunction`` + If the referent is a debuggee function, returns ``true`` if the referent is a bound function; ``false`` otherwise. If the referent is not a debuggee function, or not a function at all, returns ``undefined`` instead. + +``isArrowFunction`` + If the referent is a debuggee function, returns ``true`` if the referent is an arrow function; ``false`` otherwise. If the referent is not a debuggee function, or not a function at all, returns ``undefined`` instead. + +``isGeneratorFunction`` + If the referent is a debuggee function, returns ``true`` if the referent was created with a ``function*`` expression or statement, or false if it is some other sort of function. If the referent is not a debuggee function, or not a function at all, this is ``undefined``. (This is always equal to ``obj.script.isGeneratorFunction``, assuming ``obj.script`` is a ``Debugger.Script``.) + +``isAsyncFunction`` + If the referent is a debuggee function, returns ``true`` if the referent is an async function, defined with an ``async function`` expression or statement, or false if it is some other sort of function. If the referent is not a debuggee function, or not a function at all, this is ``undefined``. (This is always equal to ``obj.script.isAsyncFunction``, assuming ``obj.script`` is a ``Debugger.Script``.) + +``isPromise`` + ``true`` if the referent is a Promise; ``false`` otherwise. + +``boundTargetFunction`` + If the referent is a bound debuggee function, this is its target function— the function that was bound to a particular ``this`` object. If the referent is either not a bound function, not a debuggee function, or not a function at all, this is ``undefined``. + +``boundThis`` + If the referent is a bound debuggee function, this is the ``this`` value it was bound to. If the referent is either not a bound function, not a debuggee function, or not a function at all, this is ``undefined``. + +``boundArguments`` + If the referent is a bound debuggee function, this is an array (in the Debugger object’s compartment) that contains the debuggee values of the ``arguments`` object it was bound to. If the referent is either not a bound function, not a debuggee function, or not a function at all, this is ``undefined``. + +``isProxy`` + If the referent is a (scripted) proxy, either revoked or not, return ``true``. If the referent is not a (scripted) proxy, return ``false``. + +``proxyTarget`` + If the referent is a non-revoked (scripted) proxy, return a ``Debugger.Object`` instance referring to the ECMAScript ``[[ProxyTarget]]`` of the referent. If the referent is a revoked (scripted) proxy, return ``null``. If the referent is not a (scripted) proxy, return ``undefined``. + +``proxyHandler`` + If the referent is a non-revoked (scripted) proxy, return a ``Debugger.Object`` instance referring to the ECMAScript ``[[ProxyHandler]]`` of the referent. If the referent is a revoked (scripted) proxy, return ``null``. If the referent is not a (scripted) proxy, return ``undefined``. + +``promiseState`` + If the referent is a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, return a string indicating whether the `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ is pending, or has been fulfilled or rejected. This string takes one of the following values: + + - ``"pending"``, if the `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ is pending. + - ``"fulfilled"``, if the `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ has been fulfilled. + - ``"rejected"``, if the `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ has been rejected. + + + If the referent is not a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, throw a ``TypeError``. + +``promiseValue`` + Return a debuggee value representing the value the `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ has been fulfilled with. + + If the referent is not a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, or the `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ has not been fulfilled, throw a ``TypeError``. + +``promiseReason`` + Return a debuggee value representing the value the `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ has been rejected with. + + If the referent is not a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, or the `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ has not been rejected, throw a ``TypeError``. + +``promiseAllocationSite`` + If the referent is a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, this is the JavaScript execution stack captured at the time of the promise’s allocation. This can return null if the promise was not created from script. If the referent is not a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, throw a ``TypeError`` exception. + +``promiseResolutionSite`` + If the referent is a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, this is the JavaScript execution stack captured at the time of the promise’s resolution. This can return null if the promise was not resolved by calling its ``resolve`` or ``reject`` resolving functions from script. If the referent is not a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, throw a ``TypeError`` exception. + +``promiseID`` + If the referent is a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, this is a process-unique identifier for the `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_. With e10s, the same id can potentially be assigned to multiple `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ instances, if those instances were created in different processes. If the referent is not a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, throw a ``TypeError`` exception. + +``promiseDependentPromises`` + If the referent is a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, this is an ``Array`` of ``Debugger.Objects`` referring to the promises directly depending on the referent `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_. These are: + + + 1. Return values of ``then()`` calls on the promise. + 2. Return values of ``Promise.all()`` if the referent `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ was passed in as one of the arguments. + 3. Return values of ``Promise.race()`` if the referent `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ was passed in as one of the arguments. + + + Once a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ is settled, it will generally notify its dependent promises and forget about them, so this is most useful on *pending* promises. + + Note that the ``Array`` only contains the promises that directly depend on the referent `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_. It does not contain promises that depend on promises that depend on the referent `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_. + + If the referent is not a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, throw a ``TypeError`` exception. + + +``promiseLifetime`` + If the referent is a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, this is the number of milliseconds elapsed since the `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ was created. If the referent is not a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, throw a ``TypeError`` exception. + +``promiseTimeToResolution`` + If the referent is a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, this is the number of milliseconds elapsed between when the `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ was created and when it was resolved. If the referent hasn’t been resolved or is not a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, throw a ``TypeError`` exception. + +``global`` + A ``Debugger.Object`` instance referring to the global object in whose scope the referent was allocated. This does not unwrap cross-compartment wrappers: if the referent is a wrapper, the result refers to the wrapper’s global, not the wrapped object’s global. The result refers to the global directly, not via a wrapper. + +.. _debugger-api-debugger-object-allocation-site: + +``allocationSite`` + If :ref:`object allocation site tracking <debugger-api-debugger-memory-tracking-allocation-sites>` was enabled when this ``Debugger.Object``’s referent was allocated, return the JavaScript execution stack captured at the time of the allocation. Otherwise, return ``null``. + + +Function Properties of the Debugger.Object prototype +**************************************************** + +The functions described below may only be called with a ``this`` value referring to a ``Debugger.Object`` instance; they may not be used as methods of other kinds of objects. The descriptions use “referent” to mean “the referent of this ``Debugger.Object`` instance”. + +Unless otherwise specified, these methods are not :ref:`invocation functions <debugger-api-debugger-frame-invf>`; if a call would cause debuggee code to run (say, because it gets or sets an accessor property whose handler is debuggee code, or because the referent is a proxy whose traps are debuggee code), the call throws a ``Debugger.DebuggeeWouldRun`` exception. + +These methods may throw if the referent is not a native object. Even simple accessors like ``isExtensible`` may throw if the referent is a proxy or some sort of exotic object like an opaque wrapper. + + +``getProperty(key, [receiver])`` + Return a completion value with "return" being the value of the referent's property named *key*, or ``undefined`` if it has no such property. *key* must be a string or symbol; *receiver* must be a debuggee value. If the property is a getter, it will be evaluated with *receiver* as the receiver, defaulting to the ``Debugger.Object`` if omitted. + +``setProperty(key, value, [receiver])`` + Store *value* as the value of the referent’s property named *key*, creating the property if it does not exist. *key* must be a string or symbol; *value* and *receiver* must be debuggee values. If the property is a setter, it will be evaluated with *receiver* as the receiver, defaulting to the ``Debugger.Object`` if omitted. + +``getOwnPropertyDescriptor(name)`` + Return a property descriptor for the property named *name* of the referent. If the referent has no such property, return ``undefined``. (This function behaves like the standard ``Object.getOwnPropertyDescriptor`` function, except that the object being inspected is implicit; the property descriptor returned is allocated as if by code scoped to the debugger’s global object (and is thus in the debugger’s compartment); and its ``value``, ``get``, and ``set`` properties, if present, are debuggee values.) + +``getOwnPropertyNames()`` + Return an array of strings naming all the referent’s own properties, as if ``Object.getOwnPropertyNames(referent)`` had been called in the debuggee, and the result copied in the scope of the debugger’s global object. + +``getOwnPropertySymbols()`` + Return an array of strings naming all the referent’s own symbols, as if ``Object.getOwnPropertySymbols(referent)`` had been called in the debuggee, and the result copied in the scope of the debugger’s global object. + +``defineProperty(name, attributes)`` + Define a property on the referent named *name*, as described by the property descriptor *descriptor*. Any ``value``, ``get``, and ``set`` properties of *attributes* must be debuggee values. (This function behaves like ``Object.defineProperty``, except that the target object is implicit, and in a different compartment from the function and descriptor.) + +``defineProperties(properties)`` + Add the properties given by *properties* to the referent. (This function behaves like ``Object.defineProperties``, except that the target object is implicit, and in a different compartment from the *properties* argument.) + +``deleteProperty(name)`` + Remove the referent’s property named *name*. Return true if the property was successfully removed, or if the referent has no such property. Return false if the property is non-configurable. + +``seal()`` + Prevent properties from being added to or deleted from the referent. Return this ``Debugger.Object`` instance. (This function behaves like the standard ``Object.seal`` function, except that the object to be sealed is implicit and in a different compartment from the caller.) + +``freeze()`` + Prevent properties from being added to or deleted from the referent, and mark each property as non-writable. Return this ``Debugger.Object`` instance. (This function behaves like the standard ``Object.freeze`` function, except that the object to be sealed is implicit and in a different compartment from the caller.) + +``preventExtensions()`` + Prevent properties from being added to the referent. (This function behaves like the standard ``Object.preventExtensions`` function, except that the object to operate on is implicit and in a different compartment from the caller.) + +``isSealed()`` + Return true if the referent is sealed—that is, if it is not extensible, and all its properties have been marked as non-configurable. (This function behaves like the standard ``Object.isSealed`` function, except that the object inspected is implicit and in a different compartment from the caller.) + +``isFrozen()`` + Return true if the referent is frozen—that is, if it is not extensible, and all its properties have been marked as non-configurable and read-only. (This function behaves like the standard ``Object.isFrozen`` function, except that the object inspected is implicit and in a different compartment from the caller.) + +``isExtensible()`` + Return true if the referent is extensible—that is, if it can have new properties defined on it. (This function behaves like the standard ``Object.isExtensible`` function, except that the object inspected is implicit and in a different compartment from the caller.) + +``copy(value)`` + Apply the HTML5 “structured cloning” algorithm to create a copy of *value* in the referent’s global object (and thus in the referent’s compartment), and return a ``Debugger.Object`` instance referring to the copy. + + Note that this returns primitive values unchanged. This means you can use ``Debugger.Object.prototype.copy`` as a generic “debugger value to debuggee value” conversion function—within the limitations of the “structured cloning” algorithm. + +``create(prototype, [properties])`` + Create a new object in the referent’s global (and thus in the referent’s compartment), and return a ``Debugger.Object`` referring to it. The new object’s prototype is *prototype*, which must be an ``Debugger.Object`` instance. The new object’s properties are as given by *properties*, as if *properties* were passed to ``Debugger.Object.prototype.defineProperties``, with the new ``Debugger.Object`` instance as the ``this`` value. + +``makeDebuggeeValue(value)`` + Return the debuggee value that represents *value* in the debuggee. If *value* is a primitive, we return it unchanged; if *value* is an object, we return the ``Debugger.Object`` instance representing that object, wrapped appropriately for use in this ``Debugger.Object``’s referent’s compartment. + + Note that, if *value* is an object, it need not be one allocated in a debuggee global, nor even a debuggee compartment; it can be any object the debugger wishes to use as a debuggee value. + + As described above, each ``Debugger.Object`` instance presents its referent as viewed from a particular compartment. Given a ``Debugger.Object`` instance *d* and an object *o*, the call ``d.makeDebuggeeValue(o)`` returns a ``Debugger.Object`` instance that presents *o* as it would be seen by code in *d*’s compartment. + +``call(this, argument, …)`` + If the referent is callable, call it with the given *this* value and *argument* values, and return a completion value describing how the call completed. *This* should be a debuggee value, or ``{ asConstructor: true }`` to invoke the referent as a constructor, in which case SpiderMonkey provides an appropriate ``this`` value itself. Each *argument* must be a debuggee value. All extant handler methods, breakpoints, and so on remain active during the call. If the referent is not callable, throw a ``TypeError``. This function follows the invocation function conventions. + +``apply(this, arguments)`` + If the referent is callable, call it with the given *this* value and the argument values in *arguments*, and return a completion value describing how the call completed. *This* should be a debuggee value, or ``{ asConstructor: true }`` to invoke *function* as a constructor, in which case SpiderMonkey provides an appropriate ``this`` value itself. *Arguments* must either be an array (in the debugger) of debuggee values, or ``null`` or ``undefined``, which are treated as an empty array. All extant handler methods, breakpoints, and so on remain active during the call. If the referent is not callable, throw a ``TypeError``. This function follows the :ref:`invocation function conventions <debugger-api-debugger-frame-invf>`. + +``executeInGlobal(code, [options])`` + If the referent is a global object, evaluate *code* in that global environment, and return a completion value describing how it completed. *Code* is a string. All extant handler methods, breakpoints, and so on remain active during the call. This function follows the :ref:`invocation function conventions <debugger-api-debugger-frame-invf>`. If the referent is not a global object, throw a ``TypeError`` exception. + + *Code* is interpreted as strict mode code when it contains a Use Strict Directive. + + This evaluation is semantically equivalent to executing statements at the global level, not an indirect eval. Regardless of *code* being strict mode code, variable declarations in *code* affect the referent global object. + + The *options* argument is as for :ref:`Debugger.Frame.eval <debugger-api-debugger-frame-eval>`. + +``executeInGlobalWithBindings(code, bindings, [options])`` + + Like ``executeInGlobal``, but evaluate *code* using the referent as the variable object, but with a lexical environment extended with bindings from the object *bindings*. For each own enumerable property of *bindings* named *name* whose value is value, include a variable in the lexical environment in which *code* is evaluated named *name*, whose value is *value*. Each *value* must be a debuggee value. (This is not like a ``with`` statement: *code* may access, assign to, and delete the introduced bindings without having any effect on the *bindings* object.) + + This method allows debugger code to introduce temporary bindings that are visible to the given debuggee code and which refer to debugger-held debuggee values, and do so without mutating any existing debuggee environment. + + Note that, like ``executeInGlobal``, any declarations it contains affect the referent global object, even as *code* is evaluated in an environment extended according to *bindings*. (In the terms used by the ECMAScript specification, the ``VariableEnvironment`` of the execution context for *code* is the referent, and the *bindings* appear in a new declarative environment, which is the eval code’s ``LexicalEnvironment``.) + + The *options* argument is as for :ref:`Debugger.Frame.eval <debugger-api-debugger-frame-eval>`. + +``asEnvironment()`` + If the referent is a global object, return the :doc:`Debugger.Environment <../debugger.environment/index>` instance representing the referent’s global lexical scope. The global lexical scope’s enclosing scope is the global object. If the referent is not a global object, throw a ``TypeError``. + +``unwrap()`` + If the referent is a wrapper that this ``Debugger.Object``’s compartment is permitted to unwrap, return a ``Debugger.Object`` instance referring to the wrapped object. If we are not permitted to unwrap the referent, return ``null``. If the referent is not a wrapper, return this ``Debugger.Object`` instance unchanged. + +``unsafeDereference()`` + Return the referent of this ``Debugger.Object`` instance. + + If the referent is an inner object (say, an HTML5 ``Window`` object), return the corresponding outer object (say, the HTML5 ``WindowProxy`` object). This makes ``unsafeDereference`` more useful in producing values appropriate for direct use by debuggee code, without using :ref:`invocation functions <debugger-api-debugger-frame-invf>`. + + This method pierces the membrane of ``Debugger.Object`` instances meant to protect debugger code from debuggee code, and allows debugger code to access debuggee objects through the standard cross-compartment wrappers, rather than via ``Debugger.Object``’s reflection-oriented interfaces. This method makes it easier to gradually adapt large code bases to this Debugger API: adapted portions of the code can use ``Debugger.Object`` instances, but use this method to pass direct object references to code that has not yet been updated. + +``forceLexicalInitializationByName(binding)`` + If *binding* is in an uninitialized state initialize it to undefined and return true, otherwise do nothing and return false. + + +Source Metadata +*************** + +Generated from file: + js/src/doc/Debugger/Debugger.Object.md + +Watermark: + sha256:7ae16a834e0883a95b4e0d227193293f6b6e4e4dd812c2570372a39c4c04897b +Changeset: + `5572465c08a9+ <https://hg.mozilla.org/mozilla-central/rev/5572465c08a9>`_ diff --git a/devtools/docs/user/debugger-api/debugger.script/index.rst b/devtools/docs/user/debugger-api/debugger.script/index.rst new file mode 100644 index 0000000000..7df26208af --- /dev/null +++ b/devtools/docs/user/debugger-api/debugger.script/index.rst @@ -0,0 +1,272 @@ +=============== +Debugger.Script +=============== + +A ``Debugger.Script`` instance may refer to a sequence of bytecode in the debuggee or to a block of WebAssembly code. For the former, it is the :doc:`Debugger <../debugger/index>` API’s presentation of a JSAPI ``JSScript`` object. The two cases are distinguished by their ``format`` property being ``"js"`` or ``"wasm"``. + + +Debugger.Script for JSScripts +***************************** + +For ``Debugger.Script`` instances referring to a ``JSScript``, they are distinguished by their ``format`` property being ``"js"``. + +Each of the following is represented by a single ``JSScript`` object: + + +- The body of a function—that is, all the code in the function that is not contained within some nested function. + +- The code passed to a single call to ``eval``, excluding the bodies of any functions that code defines. + +- The contents of a ``<script>`` element. + +- A DOM event handler, whether embedded in HTML or attached to the element by other JavaScript code. + +- Code appearing in a ``javascript:`` URL. + + +The :doc:`Debugger <../debugger/index>` interface constructs ``Debugger.Script`` objects as scripts of debuggee code are uncovered by the debugger: via the ``onNewScript`` handler method; via :doc:`Debugger.Frame <../debugger.frame/index>`’s ``script`` properties; via the ``functionScript`` method of :doc:`Debugger.Object <../debugger.object/index>` instances; and so on. For a given :doc:`Debugger <../debugger/index>` instance, SpiderMonkey constructs exactly one ``Debugger.Script`` instance for each underlying script object; debugger code can add its own properties to a script object and expect to find them later, use ``==`` to decide whether two expressions refer to the same script, and so on. + +(If more than one :doc:`Debugger <../debugger/index>` instance is debugging the same code, each :doc:`Debugger <../debugger/index>` gets a separate ``Debugger.Script`` instance for a given script. This allows the code using each :doc:`Debugger <../debugger/index>` instance to place whatever properties it likes on its ``Debugger.Script`` instances, without worrying about interfering with other debuggers.) + +A ``Debugger.Script`` instance is a strong reference to a JSScript object; it protects the script it refers to from being garbage collected. + +Note that SpiderMonkey may use the same ``Debugger.Script`` instances for equivalent functions or evaluated code—that is, scripts representing the same source code, at the same position in the same source file, evaluated in the same lexical environment. + + +Debugger.Script for WebAssembly +******************************* + +For ``Debugger.Script`` instances referring to a block of WebAssembly code, they are distinguished by their ``format`` property being ``"wasm"``. + +Currently only entire modules evaluated via ``new WebAssembly.Module`` are represented. + +``Debugger.Script`` objects for WebAssembly are uncovered via ``onNewScript`` when a new WebAssembly module is instantiated and via the ``findScripts`` method on :doc:`Debugger <../debugger/index>` instances. SpiderMonkey constructs exactly one ``Debugger.Script`` for each underlying WebAssembly module per :doc:`Debugger <../debugger/index>` instance. + +A ``Debugger.Script`` instance is a strong reference to the underlying WebAssembly module; it protects the module it refers to from being garbage collected. + +Please note at the time of this writing, support for WebAssembly is very preliminary. Many properties and methods below throw. + + +Convention +********** + +For descriptions of properties and methods below, if the behavior of the property or method differs between the instance referring to a ``JSScript`` or to a block of WebAssembly code, the text will be split into two sections, headed by “**if the instance refers to a JSScript**” and “**if the instance refers to WebAssembly code**”, respectively. If the behavior does not differ, no such emphasized headings will appear. + + +Accessor Properties of the Debugger.Script Prototype Object +*********************************************************** + +A ``Debugger.Script`` instance inherits the following accessor properties from its prototype: + + +``isGeneratorFunction`` + True if this instance refers to a ``JSScript`` for a function defined with a ``function*`` expression or statement. False otherwise. + +``isAsyncFunction`` + True if this instance refers to a ``JSScript`` for an async function, defined with an ``async function`` expression or statement. False otherwise. + +``displayName`` + **If the instance refers to a JSScript**, this is the script’s display name, if it has one. If the script has no display name — for example, if it is a top-level ``eval`` script — this is ``undefined``. + + If the script’s function has a given name, its display name is the same as its function’s given name. + + If the script’s function has no name, SpiderMonkey attempts to infer an appropriate name for it given its context. For example: + + .. code-block:: javascript + + function f() {} // display name: f (the given name) + var g = function () {}; // display name: g + o.p = function () {}; // display name: o.p + var q = { + r: function () {} // display name: q.r + }; + + + Note that the display name may not be a proper JavaScript identifier, or even a proper expression: we attempt to find helpful names even when the function is not immediately assigned as the value of some variable or property. Thus, we use ``a/b`` to refer to the *b* defined within *a*, and ``a<`` to refer to a function that occurs somewhere within an expression that is assigned to *a*. For example: + + .. code-block:: javascript + + function h() { + var i = function() {}; // display name: h/i + f(function () {}); // display name: h/< + } + var s = f(function () {}); // display name: s<``</pre> + + **If the instance refers to WebAssembly code**, throw a ``TypeError``. + +``url`` + + **If the instance refers to a JSScript**, the filename or URL from which this script’s code was loaded. For scripts created by ``eval`` or the ``Function`` constructor, this may be a synthesized filename, starting with a valid URL and followed by information tracking how the code was introduced into the system; the entire string is not a valid URL. For ``Function.prototype``’s script, this is ``null``. If this ``Debugger.Script``’s ``source`` property is non-``null``, then this is equal to ``source.url``. + + **If the instance refers to WebAssembly code**, throw a ``TypeError``. + +``startLine`` + **If the instance refers to a JSScript**, the number of the line at which this script’s code starts, within the file or document named by ``url``. + +``lineCount`` + **If the instance refers to a JSScript**, the number of lines this script’s code occupies, within the file or document named by ``url``. + +``source`` + **If the instance refers to a JSScript**, the :doc:`Debugger.Source <../debugger.source/index>` instance representing the source code from which this script was produced. This is ``null`` if the source code was not retained. + + **If the instance refers to WebAssembly code**, the :doc:`Debugger.Source <../debugger.source/index>` instance representing the serialized text format of the WebAssembly code. + +``sourceStart`` + **If the instance refers to a JSScript**, the character within the :doc:`Debugger.Source <../debugger.source/index>` instance given by ``source`` at which this script’s code starts; zero-based. If this is a function’s script, this is the index of the start of the ``function`` token in the source code. + + **If the instance refers to WebAssembly code**, throw a ``TypeError``. + +``sourceLength`` + **If the instance refers to a JSScript**, the length, in characters, of this script’s code within the :doc:`Debugger.Source <../debugger.source/index>` instance given by ``source``. + + **If the instance refers to WebAssembly code**, throw a ``TypeError``. + +``global`` + + **If the instance refers to a JSScript**, a :doc:`Debugger.Object <../debugger.object/index>` instance referring to the global object in whose scope this script runs. The result refers to the global directly, not via a wrapper or a ``WindowProxy`` (“outer window”, in Firefox). + + **If the instance refers to WebAssembly code**, throw a ``TypeError``. + +``format`` + **If the instance refers to a JSScript**, ``"js"``. + + **If the instance refers to WebAssembly code**, ``"wasm"``. + + + +Function Properties of the Debugger.Script Prototype Object +*********************************************************** + +The functions described below may only be called with a ``this`` value referring to a ``Debugger.Script`` instance; they may not be used as methods of other kinds of objects. + + +``getAllOffsets()`` + **If the instance refers to a JSScript**, return an array *L* describing the relationship between bytecode instruction offsets and source code positions in this script. *L* is sparse, and indexed by source line number. If a source line number *line* has no code, then *L* has no *line* property. If there is code for *line*, then ``L[line]`` is an array of offsets of byte code instructions that are entry points to that line. + + For example, suppose we have a script for the following source code: + + .. code-block:: javascript + + a=[] + for (i=1; i < 10; i++) + // It's hip to be square. + a[i] = i*i; + + Calling ``getAllOffsets()`` on that code might yield an array like this: + + .. code-block:: javascript + + [[0], [5, 20], , [10]] + + This array indicates that: + + - the first line’s code starts at offset 0 in the script; + - the ``for`` statement head has two entry points at offsets 5 and 20 (for the initialization, which is performed only once, and the loop test, which is performed at the start of each iteration); + - the third line has no code; + - and the fourth line begins at offset 10. + + **If the instance refers to WebAssembly code**, throw a ``TypeError``. + + +``getAllColumnOffsets()``: + **If the instance refers to a JSScript**, return an array describing the relationship between bytecode instruction offsets and source code positions in this script. Unlike getAllOffsets(), which returns all offsets that are entry points for each line, getAllColumnOffsets() returns all offsets that are entry points for each (line, column) pair. + + The elements of the array are objects, each of which describes a single entry point, and contains the following properties: + + - lineNumber: the line number for which offset is an entry point + - columnNumber: the column number for which offset is an entry point + - offset: the bytecode instruction offset of the entry point + + + For example, suppose we have a script for the following source code: + + .. code-block:: javascript + + a=[] + for (i=1; i < 10; i++) + // It's hip to be square. + a[i] = i*i; + + Calling ``getAllColumnOffsets()`` on that code might yield an array like this: + + .. code-block:: javascript + + [{ lineNumber: 0, columnNumber: 0, offset: 0 }, + { lineNumber: 1, columnNumber: 5, offset: 5 }, + { lineNumber: 1, columnNumber: 10, offset: 20 }, + { lineNumber: 3, columnNumber: 4, offset: 10 }] + + **If the instance refers to WebAssembly code**, throw a ``TypeError``. + +``getLineOffsets(line)`` + **If the instance refers to a JSScript**, return an array of bytecode instruction offsets representing the entry points to source line *line*. If the script contains no executable code at that line, the array returned is empty. + +``getOffsetLocation(offset)`` + **If the instance refers to a JSScript**, return an object describing the source code location responsible for the bytecode at *offset* in this script. The object has the following properties: + + - lineNumber: the line number for which offset is an entry point + - columnNumber: the column number for which offset is an entry point + - isEntryPoint: true if the offset is a column entry point, as would be reported by getAllColumnOffsets(); otherwise false. + + +``getOffsetsCoverage()``: + **If the instance refers to a JSScript**, return ``null`` or an array which contains information about the coverage of all opcodes. The elements of the array are objects, each of which describes a single opcode, and contains the following properties: + + - lineNumber: the line number of the current opcode. + - columnNumber: the column number of the current opcode. + - offset: the bytecode instruction offset of the current opcode. + - count: the number of times the current opcode got executed. + + + If this script has no coverage, or if it is not instrumented, then this function will return ``null``. To ensure that the debuggee is instrumented, the flag ``Debugger.collectCoverageInfo`` should be set to ``true``. + + **If the instance refers to WebAssembly code**, throw a ``TypeError``. + +``getChildScripts()`` + **If the instance refers to a JSScript**, return a new array whose elements are Debugger.Script objects for each function in this script. Only direct children are included; nested children can be reached by walking the tree. + + **If the instance refers to WebAssembly code**, throw a ``TypeError``. + +``setBreakpoint(offset, handler)`` + **If the instance refers to a JSScript**, set a breakpoint at the bytecode instruction at *offset* in this script, reporting hits to the ``hit`` method of *handler*. If *offset* is not a valid offset in this script, throw an error. + + When execution reaches the given instruction, SpiderMonkey calls the ``hit`` method of *handler*, passing a :doc:`Debugger.Frame <../debugger.frame/index>` instance representing the currently executing stack frame. The ``hit`` method’s return value should be a resumption value, determining how execution should continue. + + Any number of breakpoints may be set at a single location; when control reaches that point, SpiderMonkey calls their handlers in an unspecified order. + + Any number of breakpoints may use the same *handler* object. + + Breakpoint handler method calls are cross-compartment, intra-thread calls: the call takes place in the same thread that hit the breakpoint, and in the compartment containing the handler function (typically the debugger’s compartment). + + The new breakpoint belongs to the :doc:`Debugger <../debugger/index>` instance to which this script belongs. Disabling the :doc:`Debugger <../debugger/index>` instance disables this breakpoint; and removing a global from the :doc:`Debugger <../debugger/index>` instance’s set of debuggees clears all the breakpoints belonging to that :doc:`Debugger <../debugger/index>` instance in that global’s scripts. + +``getBreakpoints([offset])`` + **If the instance refers to a JSScript**, return an array containing the handler objects for all the breakpoints set at *offset* in this script. If *offset* is omitted, return the handlers of all breakpoints set anywhere in this script. If *offset* is present, but not a valid offset in this script, throw an error. + + **If the instance refers to WebAssembly code**, throw a ``TypeError``. + +``clearBreakpoint(handler, [offset])`` + **If the instance refers to a JSScript**, remove all breakpoints set in this :doc:`Debugger <../debugger/index>` instance that use *handler* as their handler. If *offset* is given, remove only those breakpoints set at *offset* that use *handler*; if *offset* is not a valid offset in this script, throw an error. + + Note that, if breakpoints using other handler objects are set at the same location(s) as *handler*, they remain in place. + +``clearAllBreakpoints([offset])`` + **If the instance refers to a JSScript**, remove all breakpoints set in this script. If *offset* is present, remove all breakpoints set at that offset in this script; if *offset* is not a valid bytecode offset in this script, throw an error. + +``isInCatchScope([offset])`` + **If the instance refers to a JSScript**, this is ``true`` if this offset falls within the scope of a try block, and ``false`` otherwise. + + **If the instance refers to WebAssembly code**, throw a ``TypeError``. + + +Source Metadata +*************** + +Generated from file: + js/src/doc/Debugger/Debugger.Script.md + +Watermark: + sha256:8816a4e8617be32c4ce7f3ae54970fe9c8a7d248175d215a8990ccff23e6efa9 + +Changeset: + `5572465c08a9+ <https://hg.mozilla.org/mozilla-central/rev/5572465c08a9>`_ diff --git a/devtools/docs/user/debugger-api/debugger.source/index.rst b/devtools/docs/user/debugger-api/debugger.source/index.rst new file mode 100644 index 0000000000..3e4e73c1ee --- /dev/null +++ b/devtools/docs/user/debugger-api/debugger.source/index.rst @@ -0,0 +1,130 @@ +=============== +Debugger.Source +=============== + +A ``Debugger.Source`` instance represents either a piece of JavaScript source code or the serialized text of a block of WebAssembly code. The two cases are distinguished by the latter having its ``introductionType`` property always being ``"wasm"`` and the former having its ``introductionType`` property never being ``"wasm"``. + +Each :doc:`Debugger <../debugger/index>` instance has a separate collection of ``Debugger.Source`` instances representing the source code that has been presented to the system. + +A debugger may place its own properties on ``Debugger.Source`` instances, to store metadata about particular pieces of source code. + + +Debugger.Source for JavaScript +****************************** + +For a ``Debugger.Source`` instance representing a piece of JavaScript source code, its properties provide the source code itself as a string, and describe where it came from. Each :doc:`Debugger.Script <../debugger.script/index>` instance refers to the ``Debugger.Source`` instance holding the source code from which it was produced. + +If a single piece of source code contains both top-level code and function definitions, perhaps with nested functions, then the :doc:`Debugger.Script <../debugger.script/index>` instances for those all refer to the same ``Debugger.Source`` instance. Each script indicates the substring of the overall source to which it corresponds. + +A ``Debugger.Source`` instance may represent only a portion of a larger source document. For example, an HTML document can contain JavaScript in multiple ``<script>`` elements and event handler content attributes. In this case, there may be either a single ``Debugger.Source`` instance for the entire HTML document, with each :doc:`Debugger.Script <../debugger.script/index>` referring to its substring of the document; or there may be a separate ``Debugger.Source`` instance for each ``<script>`` element and attribute. The choice is left up to the implementation. + +If a given piece of source code is presented to the JavaScript implementation more than once, with the same origin metadata, the JavaScript implementation may generate a fresh ``Debugger.Source`` instance to represent each presentation, or it may use a single ``Debugger.Source`` instance to represent them all. + + +Debugger.Source for WebAssembly +******************************* + +For a ``Debugger.Source`` instance representing the serialized text of a block of WebAssembly code, its properties provide the serialized text as a string. + +Currently only entire modules evaluated via ``new WebAssembly.Module`` are represented. SpiderMonkey constructs exactly one ``Debugger.Source`` for each underlying WebAssembly module per :doc:`Debugger <../debugger/index>` instance. + +Please note at the time of this writing, support for WebAssembly is very preliminary. Many properties below return placeholder values. + + +Convention +********** + +For descriptions of properties and methods below, if the behavior of the property or method differs between the instance referring to JavaScript source or to a block of WebAssembly code, the text will be split into two sections, headed by “**if the instance refers to JavaScript source**” and “**if the instance refers to WebAssembly code**”, respectively. If the behavior does not differ, no such emphasized headings will appear. + + +Accessor Properties of the Debugger.Source Prototype Object +*********************************************************** + +A ``Debugger.Source`` instance inherits the following accessor properties from its prototype: + + +``text`` + **If the instance refers to JavaScript source**, the JavaScript source code, as a string. The value satisfies the ``Program``, ``FunctionDeclaration``, or ``FunctionExpression`` productions in the ECMAScript standard. + + **If the instance refers to WebAssembly code**, the serialized text representation. The format is yet to be specified in the WebAssembly standard. Currently, the text is an s-expression based syntax. The text generation is disabled if the Debugger has the ``allowWasmBinarySource`` property set, the ``"[wasm]"`` value will be returned in this case. + +``binary`` + **If the instance refers to WebAssembly code** and the Debugger has the ``allowWasmBinarySource`` property set, a Uint8Array that contains the WebAssembly bytecode. + +``url`` + **If the instance refers to JavaScript source**, the filename or URL from which this script’s code was loaded. For scripts created by ``eval`` or the ``Function`` constructor, this may be a synthesized filename, starting with a valid URL and followed by information tracking how the code was introduced into the system; the entire string is not a valid URL. For ``Function.prototype``’s script, this is ``null``. Source may be loaded from a URL in the following ways: + + - The URL may appear as the ``src`` attribute of a ``<script>`` element in markup text. + - The URL may be passed to the ``Worker`` web worker constructor, or the web worker ``importScripts`` function. + - The URL may be the name of a XPCOM JavaScript module or subscript. + + + (Note that code passed to ``eval``, the ``Function`` constructor, or a similar function is*not* considered to be loaded from a URL; the ``url`` accessor on ``Debugger.Source`` instances for such sources should return ``undefined``.) + + **If the instance refers to WebAssembly code**, the URL of the script that called ``new WebAssembly.Module`` with the string ``"> wasm"`` appended. + +``sourceMapURL`` + **If the instance refers to JavaScript source**, if this source was produced by a minimizer or translated from some other language, and we know the URL of a **source map** document relating the source positions in this source to the corresponding source positions in the original source, then this property’s value is that URL. Otherwise, this is ``null``. + + (On the web, the translator may provide the source map URL in a specially formatted comment in the JavaScript source code, or via a header in the HTTP reply that carried the generated JavaScript.) + + This property is writable, so you can change the source map URL by setting it. All Debugger.Source objects referencing the same source will see the change. Setting an empty string has no effect and will not change existing value. + + **If the instance refers to WebAssembly code**, ``null``. Attempts to write to this property throw a ``TypeError``. + +``displayURL`` + If the script had a special ``//# sourceURL`` comment, as described in the source maps specification, then this property’s value holds the string that was given. Otherwise, this is ``null``. + +``element`` + The <a href="Debugger.Object">``Debugger.Object``</a> instance referring to the DOM element to which this source code belongs, if any, or ``undefined`` if it belongs to no DOM element. Source belongs to a DOM element in the following cases: + + - Source belongs to a ``<script>`` element if it is the element’s text content (that is, it is written out as the body of the ``<script>`` element in the markup text), or is the source document referenced by its ``src`` attribute. + - Source belongs to a DOM element if it is an event handler content attribute (that is, if it is written out in the markup text as an attribute value). + - Source belongs to a DOM element if it was assigned to one of the element’s event handler IDL attributes as a string. (Note that one may assign both strings and functions to DOM elements’ event handler IDL attributes. If one assigns a function, that function’s script’s source does*not* belong to the DOM element; the function’s definition must appear elsewhere.) + + (If the sources attached to a DOM element change, the ``Debugger.Source`` instances representing superseded code still refer to the DOM element; this accessor only reflects origins, not current relationships.) + +``elementAttributeName`` + If this source belongs to a DOM element because it is an event handler content attribute or an event handler IDL attribute, this is the name of that attribute, a string. Otherwise, this is ``undefined``. + +``introductionType`` + **If the instance refers to JavaScript source**, a string indicating how this source code was introduced into the system. This accessor returns one of the following values: + + - ``"eval"``, for code passed to ``eval``. + - ``"Function"``, for code passed to the ``Function`` constructor. + - ``"Function.prototype"``, for ``Function.prototype`` internally generated code. + - ``"Worker"``, for code loaded by calling the Web worker constructor—the worker’s main script. + - ``"importScripts"``, for code by calling ``importScripts`` in a web worker. + - ``"eventHandler"``, for code assigned to DOM elements’ event handler IDL attributes as a string. + - ``"scriptElement"``, for code belonging to ``<script>`` elements. + - ``"javascriptURL"``, for code presented in ``javascript:`` URLs. + - ``"setTimeout"``, for code passed to ``setTimeout`` as a string. + - ``"setInterval"``, for code passed to ``setInterval`` as a string. + - ``undefined``, if the implementation doesn’t know how the code was introduced. + + + **If the instance refers to WebAssembly code**, ``"wasm"``. + +``introductionScript``, ``introductionOffset`` + **If the instance refers to JavaScript source**, and if this source was introduced by calling a function from debuggee code, then ``introductionScript`` is the <a href="Debugger.Script">``Debugger.Script``</a> instance referring to the script containing that call, and ``introductionOffset`` is the call’s bytecode offset within that script. Otherwise, these are both ``undefined``. Taken together, these properties indicate the location of the introducing call. + + For the purposes of these accessors, assignments to accessor properties are treated as function calls. Thus, setting a DOM element’s event handler IDL attribute by assigning to the corresponding JavaScript property creates a source whose ``introductionScript`` and ``introductionOffset`` refer to the property assignment. + + Since a ``<script>`` element parsed from a web page’s original HTML was not introduced by any scripted call, its source’s ``introductionScript`` and ``introductionOffset`` accessors both return ``undefined``. + + If a ``<script>`` element was dynamically inserted into a document, then these accessors refer to the call that actually caused the script to run—usually the call that made the element part of the document. Thus, they do*not* refer to the call that created the element; stored the source as the element’s text child; made the element a child of some uninserted parent node that was later inserted; or the like. + + Although the main script of a worker thread is introduced by a call to ``Worker`` or ``SharedWorker``, these accessors always return ``undefined`` on such script’s sources. A worker’s main script source and the call that created the worker are always in separate threads, but <a href="Debugger" title="The Debugger object">``Debugger``</a> is an inherently single-threaded facility: its debuggees must all run in the same thread. Since the global that created the worker is in a different thread, it is guaranteed not to be a debuggee of the <a href="Debugger" title="The Debugger object">``Debugger``</a> instance that owns this source; and thus the creating call is never “in debuggee code”. Relating a worker to its creator, and other multi-threaded debugging concerns, are out of scope for <a href="Debugger" title="The Debugger object">``Debugger``</a>. + + **If the instance refers to WebAssembly code**, ``introductionScript`` is the <a href="Debugger.Script">``Debugger.Script``</a> instance referring to the same underlying WebAssembly module. ``introductionOffset`` is ``undefined``. + + +Source Metadata +--------------- + +Generated from file: + js/src/doc/Debugger/Debugger.Source.md +Watermark: + sha256:5ca245813db96628aab1c78b803355eb2aa8c575839c67eb7d7bde177898df88 +Changeset: + `e91b2c85aacd <https://hg.mozilla.org/mozilla-central/rev/e91b2c85aacd>`_ diff --git a/devtools/docs/user/debugger-api/debugger/index.rst b/devtools/docs/user/debugger-api/debugger/index.rst new file mode 100644 index 0000000000..b26716a78a --- /dev/null +++ b/devtools/docs/user/debugger-api/debugger/index.rst @@ -0,0 +1,314 @@ +======== +Debugger +======== + +The Debugger Object +******************* + +When called as a constructor, the ``Debugger`` object creates a new ``Debugger`` instance. + + +``new Debugger([global, …])`` + Create a debugger object, and apply its :ref:`addDebuggee <debugger-api-debugger-add-debuggee>` method to each of the given *global* objects to add them as the initial debuggees. + + +Accessor Properties of the Debugger Prototype Object +---------------------------------------------------- + +A ``Debugger`` instance inherits the following accessor properties from its prototype: + + +``enabled`` + + A boolean value indicating whether this ``Debugger`` instance’s handlers, breakpoints, and the like are currently enabled. It is an accessor property with a getter and setter: assigning to it enables or disables this ``Debugger`` instance; reading it produces true if the instance is enabled, or false otherwise. This property is initially ``true`` in a freshly created ``Debugger`` instance. + + This property gives debugger code a single point of control for disentangling itself from the debuggee, regardless of what sort of events or handlers or “points” we add to the interface. + +``allowUnobservedAsmJS`` + + A boolean value indicating whether asm.js code running inside this ``Debugger`` instance’s debuggee globals is invisible to Debugger API handlers and breakpoints. Setting this to ``false`` inhibits the ahead-of-time asm.js compiler and forces asm.js code to run as normal JavaScript. This is an accessor property with a getter and setter. It is initially ``false`` in a freshly created ``Debugger`` instance. + + Setting this flag to ``true`` is intended for uses of subsystems of the Debugger API (e.g, :doc:`Debugger.Source <../debugger.source/index>`) for purposes other than step debugging a target JavaScript program. + +``allowWasmBinarySource`` + + A boolean value indicating whether WebAssembly sources will be available in binary form. The WebAssembly text generation will be disabled. + +``collectCoverageInfo`` + + A boolean value indicating whether code coverage should be enabled inside each debuggee of this ``Debugger`` instance. Changing this flag value will recompile all JIT code to add or remove code coverage instrumentation. Changing this flag when any frame of the debuggee is currently active on the stack will produce an exception. + + Setting this to ``true`` enables code coverage instrumentation, which can be accessed via the :doc:`Debugger.Script <../debugger.script/index>` ``getOffsetsCoverage`` function. In some cases, the code coverage might expose information which pre-date the modification of this flag. Code coverage reports are monotone, thus one can take a snapshot when the Debugger is enabled, and output the difference. + + Setting this to ``false`` prevents this ``Debugger`` instance from requiring any code coverage instrumentation, but it does not guarantee that the instrumentation is not present. + +``uncaughtExceptionHook`` + + Either ``null`` or a function that SpiderMonkey calls when a call to a debug event handler, breakpoint handler, or similar function throws some exception, which we refer to as *debugger-exception* here. Exceptions thrown in the debugger are not propagated to debuggee code; instead, SpiderMonkey calls this function, passing *debugger-exception* as its sole argument and the ``Debugger`` instance as the ``this`` value. This function should return a resumption value, which determines how the debuggee should continue. + + If the uncaught exception hook itself throws an exception, *uncaught-hook-exception*, SpiderMonkey throws a new error object, *confess-to-debuggee-exception*, to the debuggee whose message blames the debugger, and includes textual descriptions of *uncaught-hook-exception* and the original *debugger-exception*. + + If ``uncaughtExceptionHook``’s value is ``null``, SpiderMonkey throws an exception to the debuggee whose message blames the debugger, and includes a textual description of *debugger-exception*. + + Assigning anything other than a callable value or ``null`` to this property throws a ``TypeError`` exception. + + (This is not an ideal way to handle debugger bugs, but the hope here is that some sort of backstop, even if imperfect, will make life easier for debugger developers. For example, an uncaught exception hook may have access to browser-level features like the ``alert`` function, which this API’s implementation does not, making it possible to present debugger errors to the developer in a way suited to the context.) + + +Debugger Handler Functions +************************** + +Each ``Debugger`` instance inherits accessor properties with which you can store handler functions for SpiderMonkey to call when given events occur in debuggee code. + +When one of the events described below occurs in debuggee code, the engine pauses the debuggee and calls the corresponding debugging handler on each ``Debugger`` instance that is observing the debuggee. The handler functions receive the ``Debugger`` instance as their ``this`` value. Most handler functions can return a resumption value indicating how the debuggee’s execution should proceed. + +On a new ``Debugger`` instance, each of these properties is initially ``undefined``. Any value assigned to a debugging handler must be either a function or ``undefined``; otherwise a ``TypeError`` is thrown. + +Handler functions run in the same thread in which the event occurred. They run in the compartment to which they belong, not in a debuggee compartment. + + +``onNewScript(script, global)`` + + New code, represented by the :doc:`Debugger.Script <../debugger.script/index>` instance *script*, has been loaded in the scope of the debuggees. + + This method’s return value is ignored. + +``onNewPromise(promise)`` + + A new Promise object, referenced by the :doc:`Debugger.Object <../debugger.object/index>` instance *promise*, has been allocated in the scope of the debuggees. The Promise’s allocation stack can be obtained using the *promiseAllocationStack* accessor property of the :doc:`Debugger.Object <../debugger.object/index>` instance *promise*. + + This handler method should return a resumption value specifying how the debuggee’s execution should proceed. However, note that a ``{ return: value }`` resumption value is treated like ``undefined`` (“continue normally”); *value* is ignored. + +``onPromiseSettled(promise)`` + + A Promise object, referenced by the :doc:`Debugger.Object <../debugger.object/index>` instance *promise* that was allocated within a debuggee scope, has settled (either fulfilled or rejected). The Promise’s state, fulfillment or rejection value, and the allocation and resolution stacks can be obtained using the Promise-related accessor properties of the :doc:`Debugger.Object <../debugger.object/index>` instance *promise*. + + This handler method should return a resumption value specifying how the debuggee’s execution should proceed. However, note that a ``{ return: value }`` resumption value is treated like ``undefined`` (“continue normally”); *value* is ignored. + +``onDebuggerStatement(frame)`` + + Debuggee code has executed a *debugger* statement in *frame*. This method should return a resumption value specifying how the debuggee’s execution should proceed. + +``onEnterFrame(frame)`` + + The stack *frame* is about to begin executing code. (Naturally, *frame* is currently the youngest :doc:`visible frame <../debugger.frame/index>`.) This method should return a resumption value specifying how the debuggee’s execution should proceed. + + SpiderMonkey only calls ``onEnterFrame`` to report :ref:`visible <debugger-api-debugger-frame-visible-frames>`, non-``"debugger"`` frames. + +``onExceptionUnwind(frame, value)`` + + The exception *value* has been thrown, and has propagated to *frame*; *frame* is the youngest remaining stack frame, and is a debuggee frame. This method should return a resumption value specifying how the debuggee’s execution should proceed. If it returns ``undefined``, the exception continues to propagate as normal: if control in ``frame`` is in a ``try`` block, control jumps to the corresponding ``catch`` or ``finally`` block; otherwise, *frame* is popped, and the exception propagates to *frame* caller. + + When an exception’s propagation causes control to enter a ``finally`` block, the exception is temporarily set aside. If the ``finally`` block finishes normally, the exception resumes propagation, and the debugger’s ``onExceptionUnwind`` handler is called again, in the same frame. (The other possibility is for the ``finally`` block to exit due to a ``return``, ``continue``, or ``break`` statement, or a new exception. In those cases the old exception does not continue to propagate; it is discarded.) + + This handler is not called when unwinding a frame due to an over-recursion or out-of-memory exception. + +``sourceHandler(ASuffusionOfYellow)`` + + This method is never called. If it is ever called, a contradiction has been proven, and the debugger is free to assume that everything is true. + +``onError(frame, report)`` + + SpiderMonkey is about to report an error in *frame*. *Report* is an object describing the error, with the following properties: + + + ``message`` + The fully formatted error message. + ``file`` + If present, the source file name, URL, etc. (If this property is present, the *line* property will be too, and vice versa.) + ``line`` + If present, the source line number at which the error occurred. + ``lineText`` + If present, this is the source code of the offending line. + ``offset`` + The index of the character within lineText at which the error occurred. + ``warning`` + Present and true if this is a warning; absent otherwise. + ``strict`` + Present and true if this error or warning is due to the strict option (not to be confused with ES strict mode) + ``exception`` + Present and true if an exception will be thrown; absent otherwise. + ``arguments`` + An array of strings, representing the arguments substituted into the error message. + + + This method’s return value is ignored. + +``onNewGlobalObject(global)`` + + A new global object, *global*, has been created. + + This handler method should return a resumption value specifying how the debuggee’s execution should proceed. However, note that a ``{ return: value }`` resumption value is treated like ``undefined`` (“continue normally”); *value* is ignored. (Allowing the handler to substitute its own value for the new global object doesn’t seem useful.) + + This handler method is only available to debuggers running in privileged code (“chrome”, in Firefox). Most functions provided by this ``Debugger`` API observe activity in only those globals that are reachable by the API’s user, thus imposing capability-based restrictions on a ``Debugger``’s reach. However, the ``onNewGlobalObject`` method allows the API user to monitor all global object creation that occurs anywhere within the JavaScript system (the “JSRuntime”, in SpiderMonkey terms), thereby escaping the capability-based limits. For this reason, ``onNewGlobalObject`` is only available to privileged code. + + +Function Properties of the Debugger Prototype Object +**************************************************** + +The functions described below may only be called with a ``this`` value referring to a ``Debugger`` instance; they may not be used as methods of other kinds of objects. + +.. _debugger-api-debugger-add-debuggee: + +``addDebuggee(global)`` + + Add the global object designated by *global* to the set of global objects this ``Debugger`` instance is debugging. If the designated global is already a debuggee, this has no effect. Return this ``Debugger`` :doc:`Debugger.Object <../debugger.object/index>` instance referring to the designated global. + + The value *global* may be any of the following: + + - A global object. + + - An HTML5 ``WindowProxy`` object (an “outer window”, in Firefox terminology), which is treated as if the ``Window`` object of the browsing context’s active document (the “inner window”) were passed. + + - A cross-compartment wrapper of an object; we apply the prior rules to the wrapped object. + + - A :doc:`Debugger.Object <../debugger.object/index>` instance belonging to this ``Debugger`` instance; we apply the prior rules to the referent. + + - Any other sort of value is treated as a ``TypeError``. (Note that each rule is only applied once in the process of resolving a given *global* argument. Thus, for example, a :doc:`Debugger.Object <../debugger.object/index>` referring to a second :doc:`Debugger.Object <../debugger.object/index>` which refers to a global does not designate that global for the purposes of this function.) + + + The global designated by *global* must be in a different compartment than this ``Debugger`` instance itself. If adding the designated global’s compartment would create a cycle of debugger and debuggee compartments, this method throws an error. + + This method returns the :doc:`Debugger.Object <../debugger.object/index>` instance whose referent is the designated global object. + + The ``Debugger`` instance does not hold a strong reference to its debuggee globals: if a debuggee global is not otherwise reachable, then it is dropped from the ``Debugger`` set of debuggees. (Naturally, the :doc:`Debugger.Object <../debugger.object/index>` instance this method returns does hold a strong reference to the added global.) + + If this debugger is :ref:`tracking allocation sites <debugger-api-debugger-memory-tracking-allocation-sites>` and cannot track allocation sites for *global*, this method throws an ``Error``. + +``addAllGlobalsAsDebuggees()`` + + This method is like :ref:`addDebuggee <debugger-api-debugger-add-debuggee>`, but adds all the global objects from all compartments to this ``Debugger`` instance’s set of debuggees. Note that it skips this debugger’s compartment. + + If this debugger is :ref:`tracking allocation sites <debugger-api-debugger-memory-tracking-allocation-sites>` and cannot track allocation sites for some global, this method throws an ``Error``. Otherwise this method returns ``undefined``. + + This method is only available to debuggers running in privileged code (“chrome”, in Firefox). Most functions provided by this ``Debugger`` API observe activity in only those globals that are reachable by the API’s user, thus imposing capability-based restrictions on a ``Debugger``’s reach. However, the ``addAllGlobalsAsDebuggees`` method allows the API user to monitor all global object creation that occurs anywhere within the JavaScript system (the “JSRuntime”, in SpiderMonkey terms), thereby escaping the capability-based limits. For this reason, ``addAllGlobalsAsDebuggees`` is only available to privileged code. + +``removeDebuggee(global)`` + + Remove the global object designated by *global* from this ``Debugger`` instance’s set of debuggees. Return ``undefined``. + + This method interprets *global* using the same rules that :ref:`addDebuggee <debugger-api-debugger-add-debuggee>` does. + + Removing a global as a debuggee from this ``Debugger`` clears all breakpoints that belong to that ``Debugger`` in that global. + +``removeAllDebuggees()`` + + Remove all the global objects from this ``Debugger`` instance’s set of debuggees. Return ``undefined``. + +``hasDebuggee(global)`` + + Return ``true`` if the global object designated by *global* is a debuggee of this ``Debugger`` instance. + + This method interprets *global* using the same rules that :ref:`addDebuggee <debugger-api-debugger-add-debuggee>` does. + +``getDebuggees()`` + + Return an array of distinct :doc:`Debugger.Object <../debugger.object/index>` instances whose referents are all the global objects this ``Debugger`` instance is debugging. + + Since ``Debugger`` instances don’t hold strong references to their debuggee globals, if a debuggee global is otherwise unreachable, it may be dropped at any moment from the array this method returns. + +``getNewestFrame()`` + + Return a :doc:`Debugger.Frame <../debugger.frame/index>` instance referring to the youngest :doc:`visible frame <../debugger.frame/index>` currently on the calling thread’s stack, or ``null`` if there are no visible frames on the stack. + +``findSources([query]) (not yet implemented)`` + + Return an array of all :doc:`Debugger.Source <../debugger.source/index>` instances matching *query*. Each source appears only once in the array. *Query* is an object whose properties restrict which sources are returned; a source must meet all the criteria given by *query* to be returned. If *query* is omitted, we return all sources of all debuggee scripts. + + *Query* may have the following properties: + + ``url`` + The source’s ``url`` property must be equal to this value. + + ``global`` + The source must have been evaluated in the scope of the given global object. If this property’s value is a :doc:`Debugger.Object <../debugger.object/index>` instance belonging to this ``Debugger`` instance, then its referent is used. If the object is not a global object, then the global in whose scope it was allocated is used. + + Note that the result may include sources that can no longer ever be used by the debuggee: say, eval code that has finished running, or source for unreachable functions. Whether such sources appear can be affected by the garbage collector’s behavior, so this function’s result is not entirely deterministic. + +``findScripts([query])`` + + Return an array of :doc:`Debugger.Script <../debugger.script/index>` instances for all debuggee scripts matching *query*. Each instance appears only once in the array. *Query* is an object whose properties restrict which scripts are returned; a script must meet all the criteria given by *query* to be returned. If *query* is omitted, we return the :doc:`Debugger.Script <../debugger.script/index>` instances for all debuggee scripts. + + *Query* may have the following properties: + + + ``url`` + The script’s ``url`` property must be equal to this value. + ``source`` + The script’s ``source`` property must be equal to this value. + ``line`` + The script must at least partially cover the given source line. If this property is present, the ``url`` property must be present as well. + ``column`` + The script must include given column on the line given by the ``line`` property. If this property is present, the ``url`` and ``line`` properties must both be present as well. + ``innermost`` + If this property is present and true, the script must be the innermost script covering the given source location; scripts of enclosing code are omitted. + ``global`` + The script must be in the scope of the given global object. If this property’s value is a :doc:`Debugger.Object <../debugger.object/index>` instance belonging to this ``Debugger`` instance, then its referent is used. If the object is not a global object, then the global in whose scope it was allocated is used. + + + All properties of *query* are optional. Passing an empty object returns all debuggee code scripts. + + Note that the result may include :doc:`Debugger.Script <../debugger.script/index>` instances for scripts that can no longer ever be used by the debuggee, say, those for eval code that has finished running, or unreachable functions. Whether such scripts appear can be affected by the garbage collector’s behavior, so this function’s behavior is not entirely deterministic. + +``findObjects([query])`` + + Return an array of :doc:`Debugger.Object <../debugger.object/index>` instances referring to each live object allocated in the scope of the debuggee globals that matches *query*. Each instance appears only once in the array. *Query* is an object whose properties restrict which objects are returned; an object must meet all the criteria given by *query* to be returned. If *query* is omitted, we return the :doc:`Debugger.Object <../debugger.object/index>` instances for all objects allocated in the scope of debuggee globals. + + The *query* object may have the following properties: + + + ``class`` + If present, only return objects whose internal ``[[Class]]``’s name matches the given string. Note that in some cases, the prototype object for a given constructor has the same ``[[Class]]`` as the instances that refer to it, but cannot itself be used as a valid instance of the class. Code gathering objects by class name may need to examine them further before trying to use them. + + + All properties of *query* are optional. Passing an empty object returns all objects in debuggee globals. + + Unlike ``findScripts``, this function is deterministic and will never return <a href="Debugger.Object">``Debugger.Object``s</a> referring to previously unreachable objects that had not been collected yet. + +``clearBreakpoint(handler)`` + + Remove all breakpoints set in this ``Debugger`` instance that use *handler* as their handler. Note that, if breakpoints using other handler objects are set at the same location(s) as *handler*, they remain in place. + +``clearAllBreakpoints()`` + + Remove all breakpoints set using this ``Debugger`` instance. + +``findAllGlobals()`` + + Return an array of :doc:`Debugger.Object <../debugger.object/index>` instances referring to all the global objects present in this JavaScript instance. + + The results of this call can be affected in non-deterministic ways by the details of the JavaScript implementation. The array may include :doc:`Debugger.Object <../debugger.object/index>` instances referring to global objects that are not actually reachable by the debuggee or any other code in the system. (Naturally, once the function has returned, the array’s :doc:`Debugger.Object <../debugger.object/index>` instances strongly reference the globals they refer to.) + + This handler method is only available to debuggers running in privileged code (“chrome”, in Firefox). Most functions provided by this ``Debugger`` API observe activity in only those globals that are reachable by the API’s user, thus imposing capability-based restrictions on a ``Debugger``’s reach. However, ``findAllGlobals`` allows the API user to find all global objects anywhere within the JavaScript system (the “JSRuntime”, in SpiderMonkey terms), thereby escaping the capability-based limits. For this reason, ``findAllGlobals`` is only available to privileged code. + +``makeGlobalObjectReference(global)`` + + Return the :doc:`Debugger.Object <../debugger.object/index>` whose referent is the global object designated by *global*, without adding the designated global as a debuggee. If *global* does not designate a global object, throw a ``TypeError``. Determine which global is designated by *global* using the same rules as <a href="Debugger#addDebuggee" title="The Debugger object: addDebuggee">``Debugger.prototype.addDebuggee``</a>. + +``adoptDebuggeeValue(value)`` + + Given a debuggee value ``value`` owned by an arbitrary ``Debugger``, return an equivalent debuggee value owned by this ``Debugger``. + + If ``value`` is a primitive value, return it unchanged. If ``value`` is a ``Debugger.Object`` owned by an arbitrary ``Debugger``, return an equivalent ``Debugger.Object`` owned by this ``Debugger``. Otherwise, if ``value`` is some other kind of object, and hence not a proper debuggee value, throw a TypeError instead. + + +Static methods of the Debugger Object +************************************* + +The functions described below are not called with a ``this`` value. + +``isCompilableUnit(source)`` + Given a string of source code, designated by *source*, return false if the string might become a valid JavaScript statement with the addition of more lines. Otherwise return true. The intent is to support interactive compilation - accumulate lines in a buffer until isCompilableUnit is true, then pass it to the compiler. + + +Source Metadata +--------------- + +Generated from file: + js/src/doc/Debugger/Debugger.md + +Watermark: + sha256:03b36132885e046a5f213130ba22b1139b473770f7324b842483c09ab7665f7c + +Changeset: + `e91b2c85aacd <https://hg.mozilla.org/mozilla-central/rev/e91b2c85aacd>`_ diff --git a/devtools/docs/user/debugger-api/index.rst b/devtools/docs/user/debugger-api/index.rst new file mode 100644 index 0000000000..158b70213a --- /dev/null +++ b/devtools/docs/user/debugger-api/index.rst @@ -0,0 +1,92 @@ +============ +Debugger-API +============ + +The ``Debugger`` Interface +************************** + +Mozilla’s JavaScript engine, SpiderMonkey, provides a debugging interface named ``Debugger`` which lets JavaScript code observe and manipulate the execution of other JavaScript code. Both Firefox’s built-in developer tools and the Firebug add-on use ``Debugger`` to implement their JavaScript debuggers. However, ``Debugger`` is quite general, and can be used to implement other kinds of tools like tracers, coverage analysis, patch-and-continue, and so on. + +``Debugger`` has three essential qualities: + + +- It is a *source level* interface: it operates in terms of the JavaScript language, not machine language. It operates on JavaScript objects, stack frames, environments, and code, and presents a consistent interface regardless of whether the debuggee is interpreted, compiled, or optimized. If you have a strong command of the JavaScript language, you should have all the background you need to use ``Debugger`` successfully, even if you have never looked into the language’s implementation. + +- It is for use *by JavaScript code*. JavaScript is both the debuggee language and the tool implementation language, so the qualities that make JavaScript effective on the web can be brought to bear in crafting tools for developers. As is expected of JavaScript APIs, ``Debugger`` is a *sound* interface: using (or even misusing) ``Debugger`` should never cause Gecko to crash. Errors throw proper JavaScript exceptions. + +- It is an *intra-thread* debugging API. Both the debuggee and the code using ``Debugger`` to observe it must run in the same thread. Cross-thread, cross-process, and cross-device tools must use ``Debugger`` to observe the debuggee from within the same thread, and then handle any needed communication themselves. (Firefox’s builtin tools have a `protocol <https://wiki.mozilla.org/Remote_Debugging_Protocol>`_ defined for this purpose.) + + +In Gecko, the ``Debugger`` API is available to chrome code only. By design, it ought not to introduce security holes, so in principle it could be made available to content as well; but it is hard to justify the security risks of the additional attack surface. + +The ``Debugger`` API cannot currently observe self-hosted JavaScript. This is not inherent in the API’s design, but that the self-hosting infrastructure isn’t prepared for the kind of invasions the ``Debugger`` API can perform. + + +Debugger Instances and Shadow Objects +************************************* + +``Debugger`` reflects every aspect of the debuggee’s state as a JavaScript value—not just actual JavaScript values like objects and primitives, but also stack frames, environments, scripts, and compilation units, which are not normally accessible as objects in their own right. + +Here is a JavaScript program in the process of running a timer callback function: + +.. image:: shadows.svg + :alt: A running JavaScript program and its Debugger shadows + :class: center + +A running JavaScript program and its Debugger shadows + +This diagram shows the various types of shadow objects that make up the Debugger API (which all follow some general conventions): + + +- A :doc:`Debugger.Object <debugger.object/index>` represents a debuggee object, offering a reflection-oriented API that protects the debugger from accidentally invoking getters, setters, proxy traps, and so on. + +- A :doc:`Debugger.Script <debugger.script/index>` represents a block of JavaScript code—either a function body or a top-level script. Given a ``Debugger.Script``, one can set breakpoints, translate between source positions and bytecode offsets (a deviation from the “source level” design principle), and find other static characteristics of the code. + +- A :doc:`Debugger.Frame <debugger.frame/index>` represents a running stack frame. You can use these to walk the stack and find each frame’s script and environment. You can also set ``onStep`` and ``onPop`` handlers on frames. + +- A :doc:`Debugger.Environment <debugger.environment/index>` represents an environment, associating variable names with storage locations. Environments may belong to a running stack frame, captured by a function closure, or reflect some global object’s properties as variables. + + +The :doc:`Debugger <debugger/index>` instance itself is not really a shadow of anything in the debuggee; rather, it maintains the set of global objects which are to be considered debuggees. A ``Debugger`` observes only execution taking place in the scope of these global objects. You can set functions to be called when new stack frames are pushed; when new code is loaded; and so on. + +Omitted from this picture are :doc:`Debugger.Source <debugger.source/index.>` instances, which represent JavaScript compilation units. A ``Debugger.Source`` can furnish a full copy of its source code, and explain how the code entered the system, whether via a call to ``eval``, a ``<script>`` element, or otherwise. A ``Debugger.Script`` points to the ``Debugger.Source`` from which it is derived. + +Also omitted is the ``Debugger``’s :doc:`Debugger.Memory <debugger.memory/index>` instance, which holds methods and accessors for observing the debuggee’s memory use. + +All these types follow some general conventions, which you should look through before drilling down into any particular type’s specification. + +All shadow objects are unique per ``Debugger`` and per referent. For a given ``Debugger``, there is exactly one ``Debugger.Object`` that refers to a particular debuggee object; exactly one ``Debugger.Frame`` for a particular stack frame; and so on. Thus, a tool can store metadata about a shadow’s referent as a property on the shadow itself, and count on finding that metadata again if it comes across the same referent. And since shadows are per-``Debugger``, tools can do so without worrying about interfering with other tools that use their own ``Debugger`` instances. + + +Examples +******** + +Here are some things you can try out yourself that show off some of ``Debugger``’s features: + + +- :doc:`Setting a breakpoint <tutorial-breakpoint/index>` in a page, running a handler function when it is hit that evaluates an expression in the page’s context. + +- :doc:`Showing how many objects different call paths allocate. <tutorial-allocation-log-tree/index>` + + +Gecko-specific features +*********************** + +While the ``Debugger`` core API deals only with concepts common to any JavaScript implementation, it also includes some Gecko-specific features: + + +- [Global tracking][global] supports debugging all the code running in a Gecko instance at once—the ‘chrome debugging’ model. +- [Object wrapper][wrapper] functions help manipulate object references that cross privilege boundaries. + + +Source Metadata +--------------- + +Generated from file: + js/src/doc/Debugger/Debugger-API.md + +Watermark: + sha256:6ee2381145a0d2e53d2f798f3f682e82dd7ab0caa0ac4dd5e56601c2e49913a7 + +Changeset: + `ffa775dd5bd4 <https://hg.mozilla.org/mozilla-central/rev/ffa775dd5bd4>`_ diff --git a/devtools/docs/user/debugger-api/shadows.svg b/devtools/docs/user/debugger-api/shadows.svg new file mode 100644 index 0000000000..03db057223 --- /dev/null +++ b/devtools/docs/user/debugger-api/shadows.svg @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" width="650" height="400"><defs><marker orient="auto" refY="0" refX="0" id="c" overflow="visible"><path d="M.98 0a1 1 0 11-2 0 1 1 0 012 0z" fill-rule="evenodd" stroke="#000" stroke-width=".2pt"/></marker><marker orient="auto" refY="0" refX="0" id="d" overflow="visible"><path d="M-1.2 0l-1 1 3.5-1-3.5-1 1 1z" fill-rule="evenodd" stroke="#000" stroke-width=".2pt"/></marker><marker orient="auto" refY="0" refX="0" id="a" overflow="visible"><path d="M.98 0a1 1 0 11-2 0 1 1 0 012 0z" fill-rule="evenodd" stroke="#000" stroke-width=".2pt"/></marker><marker orient="auto" refY="0" refX="0" id="b" overflow="visible"><path d="M-1.2 0l-1 1 3.5-1-3.5-1 1 1z" fill-rule="evenodd" stroke="#000" stroke-width=".2pt"/></marker></defs><g class="flip"><rect width="171.317" height="150.685" x=".11" y="248.55" ry="4.814" rx="4.035" fill="#c8c0c0" stroke="#000" stroke-width="2.182" stroke-dasharray="4.36452564,4.36452564"/><text style="line-height:125%" x="9.875" y="268.844" font-size="14.12" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="9.875" y="268.844">Debugger</tspan></text></g><g class="flip"><rect width="119.421" height="42.398" x="25.236" y="160.02" ry="11.065" rx="11.065" fill="#969696"/><rect width="177.655" height="46.615" x="209.361" y="336.42" ry="4.039" rx="4.039" fill="#969696"/><rect width="149.122" height="46.615" x="237.894" y="287.164" ry="4.039" rx="4.039" fill="#969696"/><rect width="119.421" height="42.398" x="192.177" y="161.099" ry="11.065" rx="11.065" fill="#969696"/><rect width="136.405" height="46.615" x="26.006" y="336.42" ry="11.065" rx="11.065" fill="#969696"/><rect width="242.772" height="75.739" x="183.318" y="47.055" ry="6.125" rx="6.125" fill="#969696"/><rect width="186.276" height="18.675" x="285.598" y="66.984" ry="6.125" rx="6.125" fill="#969696" stroke="#fff" stroke-width="2.929"/><rect width="194.03" height="46.615" x="413.202" y="336.42" ry="4.039" rx="4.039" fill="#969696"/><rect width="90.035" height="46.615" x="296.98" y="237.91" ry="4.039" rx="4.039" fill="#969696"/><text style="line-height:125%" x="33.258" y="378.201" font-size="12.935" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="33.258" y="378.201">Debugger.Object</tspan></text><text style="line-height:125%" x="218.723" y="377.522" font-size="12.935" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="218.723" y="377.522">Debugger.Environment</tspan></text><text style="line-height:125%" x="421.849" y="377.522" font-size="12.935" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="421.849" y="377.522">Debugger.Frame</tspan></text><text style="line-height:125%" x="29.181" y="196.813" font-size="12.935" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="29.181" y="196.813">Debugger.Object</tspan></text><text style="line-height:125%" x="199.222" y="197.892" font-size="12.935" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="199.222" y="197.892">Debugger.Object</tspan></text><text style="line-height:125%" x="190.189" y="118.008" font-size="12.935" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="190.189" y="118.008">Debugger.Script</tspan></text><rect width="194.03" height="46.615" x="413.202" y="287.164" ry="4.039" rx="4.039" fill="#969696"/></g><g transform="translate(0 -344.094)" class="flip"><rect width="177.927" height="46.615" x="201.82" y="664.159" ry="4.039" rx="4.039" class="nonvalue" fill="#ffd257"/><text style="line-height:125%" x="210.634" y="691.887" transform="scale(1.00076 .99924)" font-size="14.122" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="210.634" y="691.887">global environment</tspan></text><rect width="136.614" height="46.615" x="18.811" y="664.159" ry="11.065" rx="11.065" fill="#83d4ff"/><path d="M204.053 687.466h-46.267" fill="none" stroke="#000" stroke-width="3" marker-start="url(#a)" marker-end="url(#b)"/><text style="line-height:125%" x="3.02" y="659.444" transform="scale(1.00076 .99924)" font-size="14.122" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="3.02" y="659.444">global object:</tspan></text><text style="text-align:center;line-height:125%" x="87.051" y="698.414" transform="scale(1.00076 .99924)" font-size="14.122" font-weight="400" letter-spacing="0" word-spacing="0" text-anchor="middle" font-family="Sans"><tspan x="87.051" y="698.414">Date; Math; ...</tspan></text></g><g class="flip"><rect transform="translate(0 -652.362)" width="243.144" height="75.74" x="176.363" y="683.061" ry="6.125" rx="6.125" class="nonvalue" fill="#83ff9a"/><rect transform="translate(0 -652.362)" width="186.561" height="18.675" x="278.799" y="702.99" ry="6.125" rx="6.125" class="nonvalue" fill="#83ff9a" stroke="#e6e4dd" stroke-width="2.929"/></g><text style="line-height:125%" x="183" y="699.527" transform="matrix(1.00076 0 0 .99924 0 -652.362)" font-size="14.122" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="183" y="699.527">function alertLater(msg, delay) {</tspan><tspan x="183" y="717.179"> setTimeout( function () { alert(msg); },</tspan><tspan x="183" y="734.832"> delay);</tspan><tspan x="183" y="752.485">}</tspan></text><g class="flip"><rect transform="translate(0 -652.362)" width="119.604" height="42.398" x="18.04" y="796.026" ry="11.065" rx="11.065" fill="#83d4ff"/><text style="text-align:end;line-height:125%" x="98.428" y="811.158" transform="matrix(1.00076 0 0 .99924 0 -652.362)" font-size="13.919" font-weight="400" letter-spacing="0" word-spacing="0" text-anchor="end" font-family="Sans"><tspan x="98.428" y="811.158">[[Code]]:</tspan></text><text style="text-align:end;line-height:125%" x="98.428" y="831.649" transform="matrix(1.00076 0 0 .99924 0 -652.362)" font-size="13.919" font-weight="400" letter-spacing="0" word-spacing="0" text-anchor="end" font-family="Sans"><tspan x="98.428" y="831.649">[[Scope]]:</tspan></text><path d="M18.04 165.532h119.602" fill="none" stroke="#e6e4dd" stroke-width="2.957"/><text style="line-height:125%" x="2.407" y="791.508" transform="matrix(1.00076 0 0 .99924 0 -652.362)" font-size="14.122" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="2.407" y="791.508">alertLater:</tspan></text><path transform="translate(0 -652.362)" d="M113.607 806.633c0-45.024 23.466-83.543 59.207-83.543m-59.207 104.131c51.733 0 90.506 99.848 90.506 141.325" fill="none" stroke="#000" stroke-width="3" marker-start="url(#c)" marker-end="url(#d)"/><text style="text-align:center;line-height:125%" x="87.051" y="991.618" transform="matrix(1.00076 0 0 .99924 0 -652.362)" font-size="14.122" font-weight="400" letter-spacing="0" word-spacing="0" text-anchor="middle" font-family="Sans"><tspan x="89.299" y="991.618">alertLater;</tspan></text></g><g class="flip"><rect width="149.35" height="46.615" x="229.789" y="270.808" ry="4.039" rx="4.039" class="nonvalue" fill="#ffd257"/><g font-size="22" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><text transform="matrix(.58945 0 0 .58739 4.526 -176.544)" y="795.716" x="499.133" style="text-align:end;line-height:125%" text-anchor="end"><tspan y="795.716" x="499.133">msg:</tspan><tspan y="823.216" x="499.133">delay:</tspan></text><text y="794.926" x="507" style="line-height:125%" transform="matrix(.58887 0 0 .58797 4.526 -176.544)"><tspan y="794.926" x="507">'xlerb'</tspan><tspan y="822.426" x="507">1000</tspan></text></g></g><g class="flip"><rect width="119.604" height="42.398" x="185.843" y="143.664" ry="11.065" rx="11.065" fill="#83d4ff"/><text style="text-align:end;line-height:125%" x="266.103" y="158.298" transform="scale(1.00076 .99924)" font-size="13.919" font-weight="400" letter-spacing="0" word-spacing="0" text-anchor="end" font-family="Sans"><tspan x="266.103" y="158.298">[[Code]]:</tspan></text><text style="text-align:end;line-height:125%" x="266.103" y="178.789" transform="scale(1.00076 .99924)" font-size="13.919" font-weight="400" letter-spacing="0" word-spacing="0" text-anchor="end" font-family="Sans"><tspan x="266.103" y="178.789">[[Scope]]:</tspan></text><path d="M185.843 165.532h119.602" fill="none" stroke="#e6e4dd" stroke-width="2.957"/><path d="M285.445 176.59c0 31.26-38.089 59.767-38.089 91.053m38.594-113.165c165.685 0 170.772-32.93 158.206-82.184" fill="none" stroke="#000" stroke-width="3" marker-start="url(#c)" marker-end="url(#d)"/></g><g class="flip"><rect width="194.03" height="46.615" x="406.385" y="320.064" ry="4.039" rx="4.039" class="nonvalue" fill="#ff9457"/><text style="line-height:125%" x="434.11" y="346.934" font-size="12.935" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="434.11" y="346.934"><tspan style="-inkscape-font-specification:Sans Italic" font-style="italic">anonymous</tspan>()</tspan></text><rect width="90.035" height="46.615" x="289.103" y="221.555" ry="4.039" rx="4.039" class="nonvalue" fill="#ffd257"/><text style="line-height:125%;-inkscape-font-specification:Sans Italic" x="312.997" y="248.058" font-size="12.935" font-style="italic" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="312.997" y="248.058">empty</tspan></text><path d="M415.86 342.35c-51.097 0 11.106-95.595-39.03-95.595m211.854 96.578c66.197 0 27.6-283.142-117.224-283.142" fill="none" stroke="#000" stroke-width="3" marker-start="url(#a)" marker-end="url(#b)"/></g><g class="flip"><rect width="194.03" height="46.615" x="406.385" y="270.808" ry="4.039" rx="4.039" class="nonvalue" fill="#ff9457"/><text style="line-height:125%" x="433.863" y="298.871" font-size="12.935" font-weight="400" letter-spacing="0" word-spacing="0" font-family="Sans"><tspan x="433.863" y="298.871">alert('xlerb')</tspan></text></g></svg>
\ No newline at end of file diff --git a/devtools/docs/user/debugger-api/tutorial-allocation-log-tree/alloc-plot-console.png b/devtools/docs/user/debugger-api/tutorial-allocation-log-tree/alloc-plot-console.png Binary files differnew file mode 100644 index 0000000000..e41a5449b7 --- /dev/null +++ b/devtools/docs/user/debugger-api/tutorial-allocation-log-tree/alloc-plot-console.png diff --git a/devtools/docs/user/debugger-api/tutorial-allocation-log-tree/enable-chrome-devtools.png b/devtools/docs/user/debugger-api/tutorial-allocation-log-tree/enable-chrome-devtools.png Binary files differnew file mode 100644 index 0000000000..17e3b30aa6 --- /dev/null +++ b/devtools/docs/user/debugger-api/tutorial-allocation-log-tree/enable-chrome-devtools.png diff --git a/devtools/docs/user/debugger-api/tutorial-allocation-log-tree/index.rst b/devtools/docs/user/debugger-api/tutorial-allocation-log-tree/index.rst new file mode 100644 index 0000000000..711e6f245b --- /dev/null +++ b/devtools/docs/user/debugger-api/tutorial-allocation-log-tree/index.rst @@ -0,0 +1,235 @@ +======================================== +Tutorial: Show Allocations Per Call Path +======================================== + +.. |br| raw:: html + + <br/> + +This page shows how to use the :doc:`Debugger API <../index>` to show how many objects a web page allocates, sorted by the function call path that allocated them. + +1. Visit the URL ``about:config``, and set the ``devtools.chrome.enabled`` preference to ``true``: + + .. image:: enable-chrome-devtools.png + :alt: Setting the devtools.chrome.enabled preference + :class: center + + Setting the ``devtools.chrome.enabled`` preference + +|br| + +2. Open a developer Scratchpad (Menu button > Developer > Scratchpad), and select "Browser" from the "Environment" menu. (This menu will not be present unless you have changed the preference as explained above.) + + .. image:: scratchpad-browser-environment.png + :alt: Selecting the browser context in the Scratchpad + :class: center + + Selecting the 'browser' context in the Scratchpad + +|br| + +3. Enter the following code in the Scratchpad: + + .. code-block:: javascript + + // This defines the 'Debugger' constructor in this + // Scratchpad; it doesn't actually start debugging anything. + const { addDebuggerToGlobal } = ChromeUtils.importESModule( + 'resource://gre/modules/jsdebugger.sys.mjs' + ); + addDebuggerToGlobal(window); + + (function () { + // The debugger we'll use to observe a tab's allocation. + var dbg; + + // Start measuring the selected tab's main window's memory + // consumption. This function is available in the browser + // console. + window.demoTrackAllocations = function() { + dbg = new Debugger; + + // This makes hacking on the demo *much* more + // pleasant. + dbg.uncaughtExceptionHook = handleUncaughtException; + + // Find the current tab's main content window. + var w = gBrowser.selectedBrowser.contentWindow; + console.log("Tracking allocations in page: " + + w.location.href); + + // Make that window a debuggee of our Debugger. + dbg.addDebuggee(w.wrappedJSObject); + + // Enable allocation tracking in dbg's debuggees. + dbg.memory.trackingAllocationSites = true; + } + + window.demoPlotAllocations = function() { + // Grab the allocation log. + var log = dbg.memory.drainAllocationsLog(); + + // Neutralize the Debugger, and drop it on the floor + // for the GC to collect. + console.log("Stopping allocation tracking."); + dbg.removeAllDebuggees(); + dbg = undefined; + + // Analyze and display the allocation log. + plot(log); + } + + function handleUncaughtException(ex) { + console.log('Debugger hook threw:'); + console.log(ex.toString()); + console.log('Stack:'); + console.log(ex.stack); + }; + + function plot(log) { + // Given the log, compute a map from allocation sites to + // allocation counts. Note that stack entries are '===' if + // they represent the same site with the same callers. + var counts = new Map; + for (let site of log) { + // This is a kludge, necessary for now. The saved stacks + // are new, and Firefox doesn't yet understand that they + // are safe for chrome code to use, so we must tell it + // so explicitly. + site = Components.utils.waiveXrays(site.frame); + + if (!counts.has(site)) + counts.set(site, 0); + counts.set(site, counts.get(site) + 1); + } + + // Walk from each site that allocated something up to the + // root, computing allocation totals that include + // children. Remember that 'null' is a valid site, + // representing the root. + var totals = new Map; + for (let [site, count] of counts) { + for(;;) { + if (!totals.has(site)) + totals.set(site, 0); + totals.set(site, totals.get(site) + count); + if (!site) + break; + site = site.parent; + } + } + + // Compute parent-to-child links, since saved stack frames + // have only parent links. + var rootChildren = new Map; + function childMapFor(site) { + if (!site) + return rootChildren; + + let parentMap = childMapFor(site.parent); + if (parentMap.has(site)) + return parentMap.get(site); + + var m = new Map; + parentMap.set(site, m); + return m; + } + for (let [site, total] of totals) { + childMapFor(site); + } + + // Print the allocation count for |site|. Print + // |children|'s entries as |site|'s child nodes. Indent + // the whole thing by |indent|. + function walk(site, children, indent) { + var name, place; + if (site) { + name = site.functionDisplayName; + place = ' ' + site.source + ':' + site.line + ':' + site.column; + } else { + name = '(root)'; + place = ''; + } + console.log(indent + totals.get(site) + ': ' + name + place); + for (let [child, grandchildren] of children) + walk(child, grandchildren, indent + ' '); + } + walk(null, rootChildren, ''); + } + })(); + +|br| + +4. In the Scratchpad, ensure that no text is selected, and press the "Run" button. (If you get an error complaining that ``Components.utils`` is not defined, be sure you've selected ``Browser`` from the scratchpad's ``Environment`` menu, as described in step 2.) + +|br| + +5. Save the following HTML text to a file, and visit the file in your browser. Make sure the current browser tab is displaying this page. + +.. code-block:: html + + <div onclick="doDivsAndSpans()"> + Click here to make the page do some allocations. + </div> + + <script> + function makeFactory(type) { + return function factory(content) { + var elt = document.createElement(type); + elt.textContent = content; + return elt; + }; + } + + var divFactory = makeFactory('div'); + var spanFactory = makeFactory('span'); + + function divsAndSpans() { + for (i = 0; i < 10; i++) { + var div = divFactory('div #' + i); + div.appendChild(spanFactory('span #' + i)); + document.body.appendChild(div); + } + } + + function doDivsAndSpans() { divsAndSpans(); } + </script> + +|br| + +6. Open the browser console (Menu Button > Developer > Browser Console), and then evaluate the expression ``demoTrackAllocations()`` in the browser console. This begins logging allocations in the current browser tab. + +|br| + +7. In the browser tab, click on the text that says "Click here…". The event handler should add some text to the end of the page. + +|br| + +8. Back in the browser console, evaluate the expression ``demoPlotAllocations()``. This stops logging allocations, and displays a tree of allocations: + + .. image:: alloc-plot-console.png + :alt: An allocation plot, displayed in the console + :class: center + + An allocation plot, displayed in the console + + The numbers at the left edge of each line show the total number of objects allocated at that site or at sites called from there. After the count, we see the function name, and the source code location of the call site or allocation. + + The ``(root)`` node's count includes objects allocated in the content page by the web browser, like DOM events. Indeed, this display shows that ``popup.xml`` and ``content.js``, which are internal components of Firefox, allocated more objects in the page's compartment than the page itself. (We will probably revise the allocation log to present such allocations in a way that is more informative, and that exposes less of Firefox's internal structure.) + + As expected, the ``onclick`` handler is responsible for all allocation done by the page's own code. (The line number for the onclick handler is ``1``, indicating that the allocating call is located on line one of the handler text itself. We will probably change this to be the line number within ``page.html``, not the line number within the handler code.) + + The ``onclick`` handler calls ``doDivsAndSpans``, which calls ``divsAndSpans``, which invokes closures of ``factory`` to do all the actual allocation. (It is unclear why ``spanFactory`` allocated thirteen objects, despite being called only ten times.) + + + +Source Metadata +--------------- + +Generated from file: + js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md + +Watermark: + sha256:b56f6df61c39dbe19ca1f49752aea42207c804355513f4fea8249bdeb4cb056d +Changeset: + `251fccc1f62b <https://hg.mozilla.org/mozilla-central/rev/251fccc1f62b>`_ diff --git a/devtools/docs/user/debugger-api/tutorial-allocation-log-tree/scratchpad-browser-environment.png b/devtools/docs/user/debugger-api/tutorial-allocation-log-tree/scratchpad-browser-environment.png Binary files differnew file mode 100644 index 0000000000..1e2e6cc51e --- /dev/null +++ b/devtools/docs/user/debugger-api/tutorial-allocation-log-tree/scratchpad-browser-environment.png diff --git a/devtools/docs/user/debugger-api/tutorial-breakpoint/console.png b/devtools/docs/user/debugger-api/tutorial-breakpoint/console.png Binary files differnew file mode 100644 index 0000000000..5016838502 --- /dev/null +++ b/devtools/docs/user/debugger-api/tutorial-breakpoint/console.png diff --git a/devtools/docs/user/debugger-api/tutorial-breakpoint/index.rst b/devtools/docs/user/debugger-api/tutorial-breakpoint/index.rst new file mode 100644 index 0000000000..188d6d3056 --- /dev/null +++ b/devtools/docs/user/debugger-api/tutorial-breakpoint/index.rst @@ -0,0 +1,112 @@ +========================== +Tutorial: Set a breakpoint +========================== + +.. |br| raw:: html + + <br/> + +This page shows how you can try out the :doc:`Debugger API <../index>` yourself using Firefox’s Scratchpad. We use ``Debugger`` to set a breakpoint in a function, and then evaluate an expression whenever it is hit. + +This tutorial was tested against Firefox 58 Beta and Nightly. It does not work in Firefox 57. + +1. Since the ``Debugger`` API is only available to privileged JavaScript code, you’ll need to use the Browser Content Toolbox to try it out. To do this, open the Firefox developer tools, click on the options gear at the upper right of the toolbox, and make sure that both “Enable browser chrome and add-on debugging toolboxes” and “Enable remote debugging” are checked. These are located at the bottom right of the options panel; you may need to scroll to see them. Once they’re checked, you can close the developer tools. + +|br| + +2. Save the following text to an HTML file: + + .. code-block:: html + + <div onclick="report('the best div');">Click me!</div> + <div onclick="report('another great div');">Or me!</div> + <script> + function report(what) { + console.log('clicked: ' + what); + } + </script> + +|br| + +3. Visit the HTML file in your browser, and open the Browser Content Toolbox by opening the Firefox menu, choosing “Browser Tools”, and then “Browser Content Toolbox”. If that item doesn’t appear in the “Web Developer” menu, make sure you checked both boxes to enable the Browser Content Toolbox as explained in Step 1. + +|br| + +4. Our example code is long enough that the best way to run it is to use the Scratchpad panel, which is not enabled by default. To enable it, click on the options gear at the upper right of the Browser Content Toolbox, and make sure the “Scratchpad” box in the “Default Developer Tools” section the left is checked. The Scratchpad panel should appear at the top of the Toolbox alongside the Console, Debugger, and Memory panels. + +|br| + +5. Click on the Scratchpad panel and enter the following code: + + .. code-block:: javascript + + const { addDebuggerToGlobal } = ChromeUtils.importESModule( + "resource://gre/modules/jsdebugger.sys.mjs" + ); + const { console } = ChromeUtils.importESModule( + "resource://gre/modules/Console.sys.mjs" + ); + + // This defines 'Debugger' in this Scratchpad; + // it doesn't actually start debugging anything. + addDebuggerToGlobal(globalThis); + + // Create a 'Debugger' instance. + var dbg = new Debugger; + + // Make the tab's top window a debuggee, and get a + // Debugger.Object referring to the window. + var windowDO = dbg.addDebuggee(tabs[0].content); + + // Get a Debugger.Object referring to the window's `report` + // function. + var reportDO = windowDO.getOwnPropertyDescriptor('report').value; + + // Set a breakpoint at the entry point of `report`. + reportDO.script.setBreakpoint(0, { + hit: function (frame) { + console.log('hit breakpoint in ' + frame.callee.name); + console.log('what = ' + frame.eval('what').return); + } + }); + + console.log('Finished setting breakpoint!'); + +|br| + +6. In the Scratchpad, ensure that no text is selected, and press the “Run” button. + + Now, click on the text that says “Click me!” in the web page. This runs the ``div`` element’s ``onclick`` handler. When control reaches the start of the ``report`` function, ``Debugger`` calls the breakpoint handler’s ``hit`` method, passing a ``Debugger.Frame`` instance. The ``hit`` method logs the breakpoint hit to the browser content toolbox’s console. Then it evaluates the expression ``what`` in the given stack frame, and logs its result. The toolbox’s console now looks like this: + + .. image:: console.png + :alt: The breakpoint handler’s console output + :class: center + + You can also click on the text that says “Or me!”, to see ``report`` called from a different handler. + + If ``Debugger`` is unable to find the ``report`` function, or the console output does not appear, evaluate the expression ``tabs[0].content.document.location`` in the console to make sure that ``tabs[0]`` indeed refers to the HTML file you visited. If you have more than one tab visiting a ``file:`` URL, they all share a single content process, so you may need to use a different element of the array as the debuggee. + +|br| + +7. Press “Run” in the Scratchpad again. Now, clicking on “Click me!” causes the breakpoint hit to be logged twice—one for each ``Debugger`` instance. + + Multiple ``Debugger`` instances can observe the same debuggee. Re-running the code in the Scratchpad creates a fresh ``Debugger`` instance, adds the same web page as its debuggee, and then sets a new breakpoint. When you click on the ``div`` element, both ``Debugger``s breakpoints are hit, and both handlers run. + + This shows how any number of ``Debugger``-based tools can observe a single web page simultaneously. In fact, you can use the Browser Content Toolbox’s Debugger panel to set its own breakpoint in ``report``, and it will trigger along with the first two. Keep in mind, however, that when multiple Debuggers share a debuggee, the order in which their handlers run is not specified. If more than one tool tries to influence the debuggee’s behavior, their combined behavior could be unpredictable. + +|br| + +8. Close the web page and the Browser Content Toolbox. + + Since both the Scratchpad’s global object and the debuggee window are now gone, the ``Debugger`` instances will be garbage collected, since they can no longer have any visible effect on Firefox’s behavior. The ``Debugger`` API tries to interact with garbage collection as transparently as possible; for example, if both a ``Debugger.Object`` instance and its referent are not reachable, they will both be collected, even while the ``Debugger`` instance to which the shadow belonged continues to exist. + + +Source Metadata +--------------- + +Generated from file: + js/src/doc/Debugger/Tutorial-Breakpoint.md +Watermark: + sha256:c8dd4bb69972b58e59fcbe6870499206463a5e330fda25f1214893595a1c01d0 +Changeset: + `ffa775dd5bd4 <https://hg.mozilla.org/mozilla-central/rev/ffa775dd5bd4>`_ diff --git a/devtools/docs/user/debugger/break_on_dom_mutation/dom_breakpoint_context.png b/devtools/docs/user/debugger/break_on_dom_mutation/dom_breakpoint_context.png Binary files differnew file mode 100644 index 0000000000..c8d8a78d0d --- /dev/null +++ b/devtools/docs/user/debugger/break_on_dom_mutation/dom_breakpoint_context.png diff --git a/devtools/docs/user/debugger/break_on_dom_mutation/dom_mutation_breakpoint.png b/devtools/docs/user/debugger/break_on_dom_mutation/dom_mutation_breakpoint.png Binary files differnew file mode 100644 index 0000000000..a36526b8ed --- /dev/null +++ b/devtools/docs/user/debugger/break_on_dom_mutation/dom_mutation_breakpoint.png diff --git a/devtools/docs/user/debugger/break_on_dom_mutation/dom_mutation_paused.png b/devtools/docs/user/debugger/break_on_dom_mutation/dom_mutation_paused.png Binary files differnew file mode 100644 index 0000000000..f193b9878a --- /dev/null +++ b/devtools/docs/user/debugger/break_on_dom_mutation/dom_mutation_paused.png diff --git a/devtools/docs/user/debugger/break_on_dom_mutation/index.rst b/devtools/docs/user/debugger/break_on_dom_mutation/index.rst new file mode 100644 index 0000000000..a16f3a0c0c --- /dev/null +++ b/devtools/docs/user/debugger/break_on_dom_mutation/index.rst @@ -0,0 +1,52 @@ +===================== +Break on DOM mutation +===================== + +A DOM Mutation Breakpoint pauses the code when the DOM node on which you have set the breakpoint is modified. + +You set a DOM Mutation Breakpoint in the :doc:`Page Inspector <../../page_inspector/index>`. Navigate to the DOM node in which you are interested and use the context menu to set the breakpoint. + + +.. image:: dom_breakpoint_context.png + :class: center + +There are three choices: + +**Subtree Modification** + + Execution pauses if any of the element's descendant nodes are modified.<br> + That means, the script execution is stopped whenever a child node or descendant node deeper in the DOM structure is added to or removed from the element the option is set on. + + Examples for when this breakpoint is triggered are calling `Node.appendChild() <https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild>`_ and `Node.removeChild() <https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild>`_, calling `Element.remove() <https://developer.mozilla.org/en-US/docs/Web/API/Element/remove>`_ or setting `Element.innerHTML <https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML>`_ on one of the subnodes. + +**Attribute Modification** + + Execution pauses when any of the elements' attributes are modified.<br> + That means, the script execution is stopped whenever an attribute is added to or removed from the element the option is set on or the value of one of its attributes is changed. + + Examples for when this breakpoint is triggered are calling `Element.setAttribute() <https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute>`_, `Element.removeAttribute() <https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute>`_, and `Element.classList.add() <https://developer.mozilla.org/en-US/docs/Web/API/Element/classList#methods>`_, or setting `Element.id <https://developer.mozilla.org/en-US/docs/Web/API/Element/id>`_. + +**Node Removal** + Execution pauses if the element the option is set on is removed. + + Examples for when this breakpoint is triggered are calling `Element.remove() <https://developer.mozilla.org/en-US/docs/Web/API/Element/remove>`_ or `Node.removeChild() <https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild>`_ on its parent node. + + +Once you set the breakpoint, go to the Debugger. You can see the breakpoint listed in the right-most panel under **DOM Mutation Breakpoints**. + +.. image:: dom_mutation_breakpoint.png + :class: border + +Click on the icon following the node name to go back to the Page Inspector with the node selected. + +When you execute the code, the debugger will pause execution when the DOM mutation occurs. In the following example, the selected node (the unordered list) is modified by adding a new child node. + +.. image:: dom_mutation_paused.png + :class: border + +The panel on the right shows that execution is "Paused on DOM mutation" and you, as with any other breakpoint, you can see the call stack and view any Watch expressions you may have set up. + +Inline variable preview +*********************** + +New in Firefox 71, the :ref:`source pane <debugger_ui_tour_source_pane>` now gives you an instant preview of the variables on each line of code you've stepped through. See :ref:`Set a breakpoint > Inline variable preview <debugger-how-to-set-a-breakpoint-variable-preview>` for more information. diff --git a/devtools/docs/user/debugger/how_to/access_debugging_in_add-ons/index.rst b/devtools/docs/user/debugger/how_to/access_debugging_in_add-ons/index.rst new file mode 100644 index 0000000000..d6aac55b0f --- /dev/null +++ b/devtools/docs/user/debugger/how_to/access_debugging_in_add-ons/index.rst @@ -0,0 +1,24 @@ +=========================== +Access debugging in add-ons +=========================== + +.. warning:: + We are planning to deprecate the use by Firefox add-ons of the techniques described in this document. Don't write new add-ons that use these techniques. + +The following items are accessible in the context of chrome://browser/content/debugger.xul (or, in version 23 beta, chrome://browser/content/devtools/debugger.xul): + + +- window.addEventListener("Debugger:EditorLoaded") - called when the read-only script panel loaded. +- window.addEventListener("Debugger:EditorUnloaded") + + +Relevant files: + + +- chrome://browser/content/devtools/debugger-controller.js +- chrome://browser/content/devtools/debugger-toolbar.js +- chrome://browser/content/devtools/debugger-view.js +- chrome://browser/content/devtools/debugger-panes.js + + +Unfortunately there is not yet any API to evaluate watches/expressions within the debugged scope, or highlight elements on the page that are referenced as variables in the debugged scope. (currently a work in progress, see bug `653545 <https://bugzilla.mozilla.org/show_bug.cgi?id=653545>`_.) diff --git a/devtools/docs/user/debugger/how_to/breaking_on_exceptions/exception-tooltip-stacktrace.png b/devtools/docs/user/debugger/how_to/breaking_on_exceptions/exception-tooltip-stacktrace.png Binary files differnew file mode 100644 index 0000000000..9aabff2c97 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/breaking_on_exceptions/exception-tooltip-stacktrace.png diff --git a/devtools/docs/user/debugger/how_to/breaking_on_exceptions/index.rst b/devtools/docs/user/debugger/how_to/breaking_on_exceptions/index.rst new file mode 100644 index 0000000000..56a5de4335 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/breaking_on_exceptions/index.rst @@ -0,0 +1,20 @@ +====================== +Breaking on exceptions +====================== + +To instruct the debugger to pause on an `exception <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error>`_, tick these checkboxes in the :ref:`Breakpoints list <debugger-ui-tour-breakpoints-list>`: + + +- Pause on exceptions +- Pause on caught exceptions + + +.. image:: version64ui.png + :alt: Screen shot showing "Pause on exceptions + :class: center + +When an exception occurs, the line where it occurs is highlighted in the source pane, with a squiggly red line under the problematic code. A tooltip describes the exception. Starting in Firefox 80, a disclosure triangle within the tooltip reveals a stack trace. + +.. image:: exception-tooltip-stacktrace.png + :alt: Screenshot of a tooltip for an exception + :class: center diff --git a/devtools/docs/user/debugger/how_to/breaking_on_exceptions/version64ui.png b/devtools/docs/user/debugger/how_to/breaking_on_exceptions/version64ui.png Binary files differnew file mode 100644 index 0000000000..98c04d8795 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/breaking_on_exceptions/version64ui.png diff --git a/devtools/docs/user/debugger/how_to/debug_eval_sources/index.rst b/devtools/docs/user/debugger/how_to/debug_eval_sources/index.rst new file mode 100644 index 0000000000..a43dc580a2 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/debug_eval_sources/index.rst @@ -0,0 +1,32 @@ +================== +Debug eval sources +================== + +You can debug JavaScript code that is evaluated dynamically, either as a string passed to `eval() <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval>`_ or as a string passed to the `Function <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function>`_ constructor. + +In the video below, we load a page containing a source like this: + +.. code-block:: javascript + + var script = `function foo() { + console.log('called foo'); + } + //# sourceURL=my-foo.js`; + + eval(script); + + var button = document.getElementById("foo"); + button.addEventListener("click", foo, false); + + +The evaluated string is given the name "my-foo.js" using the ``//# sourceURL`` directive. This source is then listed in the :ref:`source list pane <debugger-ui-tour-source-list-pane>`, and can be opened and debugged like any other source. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/AkvN40-y1NE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +The name of the source will also appear in stack traces appearing in the :doc:`Web Console <../../../web_console/index>`. + +The debugger will also stop at `debugger; <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger>`_ statements in unnamed eval sources. diff --git a/devtools/docs/user/debugger/how_to/disable_breakpoints/disable_breakpoints.png b/devtools/docs/user/debugger/how_to/disable_breakpoints/disable_breakpoints.png Binary files differnew file mode 100644 index 0000000000..d80e3b4b85 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/disable_breakpoints/disable_breakpoints.png diff --git a/devtools/docs/user/debugger/how_to/disable_breakpoints/index.rst b/devtools/docs/user/debugger/how_to/disable_breakpoints/index.rst new file mode 100644 index 0000000000..72a2dbd955 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/disable_breakpoints/index.rst @@ -0,0 +1,15 @@ +=================== +Disable breakpoints +=================== + +To disable a single breakpoint, uncheck the checkbox next to it in the :ref:`breakpoints list <debugger-ui-tour-breakpoints-list>`. + +.. |image1| image:: toggle-all.png + :width: 20 + +To disable all breakpoints, click this icon: |image1| in the :ref:`toolbar <debugger-ui-tour-toolbar>`. + +After you click the icon to disable breakpoints, the appearance of the breakpoints will change to a lighter color with a dark-colored border. For example: + +.. image:: disable_breakpoints.png + :class: border diff --git a/devtools/docs/user/debugger/how_to/disable_breakpoints/toggle-all.png b/devtools/docs/user/debugger/how_to/disable_breakpoints/toggle-all.png Binary files differnew file mode 100644 index 0000000000..719589f4a8 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/disable_breakpoints/toggle-all.png diff --git a/devtools/docs/user/debugger/how_to/highlight_and_inspect_dom_nodes/highlight_dom_node.png b/devtools/docs/user/debugger/how_to/highlight_and_inspect_dom_nodes/highlight_dom_node.png Binary files differnew file mode 100644 index 0000000000..5a1131a377 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/highlight_and_inspect_dom_nodes/highlight_dom_node.png diff --git a/devtools/docs/user/debugger/how_to/highlight_and_inspect_dom_nodes/index.rst b/devtools/docs/user/debugger/how_to/highlight_and_inspect_dom_nodes/index.rst new file mode 100644 index 0000000000..18cd1d5116 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/highlight_and_inspect_dom_nodes/index.rst @@ -0,0 +1,18 @@ +=============================== +Highlight and inspect DOM nodes +=============================== + +If you hover over a DOM node in the Watch Expressions, it will be highlighted in the page. + +When you are working with DOM nodes in the debugger, you can easily highlight the node on the page or view it in the Page Inspector. A DOM object in the Watch Expressions area, for example, includes a target. Hover over the target to highlight the item on the page, Click on the target to switch to the Page Inspector with the item highlighted. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/G8KUW87zkK8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +Also, when you view the details of a DOM node in the code panel, objects that you can highlight in the list will also have a target next to them. You can click any one of these targets to switch to the Page Inspector with this item highlighted. + +.. image:: highlight_dom_node.png + :class: border diff --git a/devtools/docs/user/debugger/how_to/ignore_a_source/debugger-ignore.png b/devtools/docs/user/debugger/how_to/ignore_a_source/debugger-ignore.png Binary files differnew file mode 100644 index 0000000000..76e154469c --- /dev/null +++ b/devtools/docs/user/debugger/how_to/ignore_a_source/debugger-ignore.png diff --git a/devtools/docs/user/debugger/how_to/ignore_a_source/index.rst b/devtools/docs/user/debugger/how_to/ignore_a_source/index.rst new file mode 100644 index 0000000000..ae9f230441 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/ignore_a_source/index.rst @@ -0,0 +1,22 @@ +=============== +Ignore a source +=============== + +In modern web development, we often rely on libraries like `jQuery <https://jquery.com/>`_, `Ember <https://emberjs.com/>`_, or `Angular <https://angularjs.org/>`_, and 99% of the time we can safely assume that they “just work”. We don’t care about the internal implementation of these libraries. However, a library’s abstraction leaks during debugging sessions when you are forced to step through its stack frames in order to reach your own code. However, you can tell the debugger to ignore the details of selected sources. + +You can enable or disable ignoring a source file in a couple of ways: + + +- In the :ref:`source list pane <debugger-ui-tour-source-list-pane>`, right-click the filename and choose **Ignore source** (or **Unignore source**). +- If the source file is displayed in the :ref:`source pane <debugger_ui_tour_source_pane>`, click the "eye" icon at the bottom. + +.. image:: debugger-ignore.png + :alt: Screenshot showing the context menu item to ignore a source file, and highlighting the "ignore" icon. + :class: border + +When a source file is ignored: + + +- Any breakpoints it may have defined are disabled. +- When “Pause on Exceptions” is enabled in the :ref:`Debugger settings <settings-debugger>`, the debugger won’t pause when an exception is thrown in the ignored source; instead it waits until (and if) the stack unwinds to a frame in a source that isn’t ignored. +- The debugger skips through ignored sources when stepping. diff --git a/devtools/docs/user/debugger/how_to/index.rst b/devtools/docs/user/debugger/how_to/index.rst new file mode 100644 index 0000000000..0bd290243a --- /dev/null +++ b/devtools/docs/user/debugger/how_to/index.rst @@ -0,0 +1,21 @@ +====== +How to +====== + +These articles describe how to use the debugger. + +- :doc:`Access debugging in add-ons <access_debugging_in_add-ons/index>` +- :doc:`Breaking on exceptions <breaking_on_exceptions/index>` +- :doc:`Debug eval sources <debug_eval_sources/index>` +- :doc:`Disable breakpoints <disable_breakpoints/index>` +- :doc:`Highlight and inspect DOM nodes <highlight_and_inspect_dom_nodes/index>` +- :doc:`Ignore a source <ignore_a_source/index>` +- :doc:`Open the debugger <open_the_debugger/index>` +- :doc:`Pretty-print a minified file <pretty-print_a_minified_file/index>` +- :doc:`Search <search/index>` +- :doc:`Set a breakpoint <set_a_breakpoint/index>` +- :doc:`Set a conditional breakpoint <set_a_conditional_breakpoint/index>` +- :doc:`Set watch expressions <set_watch_expressions/index>` +- :doc:`Step through code <step_through_code/index>` +- :doc:`Use a source map <use_a_source_map/index>` +- :doc:`Use watchpoints <use_watchpoints/index>` diff --git a/devtools/docs/user/debugger/how_to/open_the_debugger/hamburger.png b/devtools/docs/user/debugger/how_to/open_the_debugger/hamburger.png Binary files differnew file mode 100644 index 0000000000..0a86806250 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/open_the_debugger/hamburger.png diff --git a/devtools/docs/user/debugger/how_to/open_the_debugger/index.rst b/devtools/docs/user/debugger/how_to/open_the_debugger/index.rst new file mode 100644 index 0000000000..157a95c75f --- /dev/null +++ b/devtools/docs/user/debugger/how_to/open_the_debugger/index.rst @@ -0,0 +1,20 @@ +================= +Open the debugger +================= + +There are three ways to open the debugger: + +- Select the *Debugger* panel in the Web Developer Tools, accessible from the Browser Tools submenu + +- Press :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`Z` on Windows and Linux, or :kbd:`Cmd` + :kbd:`Opt` + :kbd:`Z` on macOS (starting in Firefox 71; prior to Firefox 66, the letter in this shortcut was :kbd:`S`). + +- Press the menu button |image1|, select "Developer", then "Debugger". + +.. |image1| image:: hamburger.png + :width: 20 + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/yI5SlVQiZtI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> diff --git a/devtools/docs/user/debugger/how_to/pretty-print_a_minified_file/index.rst b/devtools/docs/user/debugger/how_to/pretty-print_a_minified_file/index.rst new file mode 100644 index 0000000000..b44ac3c48f --- /dev/null +++ b/devtools/docs/user/debugger/how_to/pretty-print_a_minified_file/index.rst @@ -0,0 +1,22 @@ +============================ +Pretty-print a minified file +============================ + +To prettify a minified file, click the **Pretty print source** icon |image1| at the bottom of the :ref:`source pane <debugger_ui_tour_source_pane>`. The debugger formats the source and displays it as a new file with a name like: "{ } [original-name]". + +.. |image1| image:: pretty_print_icon.png + :width: 20 + +.. image:: pretty_print_source.png + :class: border + +After you click the icon, the source code looks like this: + +.. image:: pretty_print_after.png + :class: border + +The **Pretty print source** icon is available only if the source file is minified (i.e., not an original file), and is not already "prettified". + +.. note:: + + Currently Firefox `does not support <https://bugzilla.mozilla.org/show_bug.cgi?id=1010150>`_ pretty printing inline Javascript. diff --git a/devtools/docs/user/debugger/how_to/pretty-print_a_minified_file/pretty_print_after.png b/devtools/docs/user/debugger/how_to/pretty-print_a_minified_file/pretty_print_after.png Binary files differnew file mode 100644 index 0000000000..fe65a39d40 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/pretty-print_a_minified_file/pretty_print_after.png diff --git a/devtools/docs/user/debugger/how_to/pretty-print_a_minified_file/pretty_print_icon.png b/devtools/docs/user/debugger/how_to/pretty-print_a_minified_file/pretty_print_icon.png Binary files differnew file mode 100644 index 0000000000..5fa79884e6 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/pretty-print_a_minified_file/pretty_print_icon.png diff --git a/devtools/docs/user/debugger/how_to/pretty-print_a_minified_file/pretty_print_source.png b/devtools/docs/user/debugger/how_to/pretty-print_a_minified_file/pretty_print_source.png Binary files differnew file mode 100644 index 0000000000..116183c87c --- /dev/null +++ b/devtools/docs/user/debugger/how_to/pretty-print_a_minified_file/pretty_print_source.png diff --git a/devtools/docs/user/debugger/how_to/search/ctrlshiftf.png b/devtools/docs/user/debugger/how_to/search/ctrlshiftf.png Binary files differnew file mode 100644 index 0000000000..31edbbea22 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/search/ctrlshiftf.png diff --git a/devtools/docs/user/debugger/how_to/search/index.rst b/devtools/docs/user/debugger/how_to/search/index.rst new file mode 100644 index 0000000000..2b1868b7d1 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/search/index.rst @@ -0,0 +1,61 @@ +====== +Search +====== + +.. _debugger-how-to-search-searching-for-files: + +Searching for files +******************* + +To search for a particular file, press :kbd:`Control` + :kbd:`P` (or :kbd:`Command` + :kbd:`P` on a Mac) and type the search term. The :ref:`source pane <debugger_ui_tour_source_pane>` will display a list of all matching files as you type. You can use the up and down arrows to move through the list, and :kbd:`Return` to open the file you want: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/xXsfYx0THWg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +.. _debugger-how-to-search-searching-within-a-file: + +Searching within a file +*********************** + +To search for a particular substring in the file currently loaded into the :ref:`source pane <debugger_ui_tour_source_pane>`, press :kbd:`Control` + :kbd:`F` (or :kbd:`Command` + :kbd:`F` on a Mac) while the source pane is focused. Press :kbd:`Return` to search. The debugger will display the number of matches in the code and highlight each result: + +.. image:: search_code.png + :class: border + + +Using the Outline tab +--------------------- + +If you are searching for a specific function within the current JavaScript file, you can use the Outline tab in the debugger to find it quickly. The Outline tab lists the functions in the current file. The default sort order is by the order in the file but you can simplify the search by click on "Sort by name" at the bottom of the tab. + +.. image:: outline_sorted.png + :class: center + +You can further simplify the search by filtering the list. Enter text into the text input above the list to limit the results in the Outline. For example, if I enter "load" when viewing the above list, I get the following: + +.. image:: outline_filtered.png + :class: center + +Only the functions with load in their name are shown. + +This feature may not seem terribly useful when searching a file with a handful of functions in it but when you are searching through a file with dozens of functions, it comes in handy. + + +Searching in all files +********************** + +You can also search for a string in all of the files included in the currently opened project. Press :kbd:`Shift` + :kbd:`Ctrl` + :kbd:`F` (Windows and Linux) or :kbd:`Shift` + :kbd:`Cmd` + :kbd:`F` (macOS) and then enter the string you are trying to find. + +.. image:: searchinallfiles.png + :class: border + +If the string exists in any of the files in the project, the search will return a list showing a list by file and line number. + +.. image:: ctrlshiftf.png + :class: border + +Click on any entry in the list to go directly to the line in the file where the string occurs. diff --git a/devtools/docs/user/debugger/how_to/search/outline_filtered.png b/devtools/docs/user/debugger/how_to/search/outline_filtered.png Binary files differnew file mode 100644 index 0000000000..598c1446a2 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/search/outline_filtered.png diff --git a/devtools/docs/user/debugger/how_to/search/outline_sorted.png b/devtools/docs/user/debugger/how_to/search/outline_sorted.png Binary files differnew file mode 100644 index 0000000000..bee1b25e9f --- /dev/null +++ b/devtools/docs/user/debugger/how_to/search/outline_sorted.png diff --git a/devtools/docs/user/debugger/how_to/search/search_code.png b/devtools/docs/user/debugger/how_to/search/search_code.png Binary files differnew file mode 100644 index 0000000000..2ff118890e --- /dev/null +++ b/devtools/docs/user/debugger/how_to/search/search_code.png diff --git a/devtools/docs/user/debugger/how_to/search/searchinallfiles.png b/devtools/docs/user/debugger/how_to/search/searchinallfiles.png Binary files differnew file mode 100644 index 0000000000..7b9954d0c9 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/search/searchinallfiles.png diff --git a/devtools/docs/user/debugger/how_to/set_a_breakpoint/add-breakpoint-context.png b/devtools/docs/user/debugger/how_to/set_a_breakpoint/add-breakpoint-context.png Binary files differnew file mode 100644 index 0000000000..3ca54d6995 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/set_a_breakpoint/add-breakpoint-context.png diff --git a/devtools/docs/user/debugger/how_to/set_a_breakpoint/breakpoints-list.png b/devtools/docs/user/debugger/how_to/set_a_breakpoint/breakpoints-list.png Binary files differnew file mode 100644 index 0000000000..6f4eaca3cf --- /dev/null +++ b/devtools/docs/user/debugger/how_to/set_a_breakpoint/breakpoints-list.png diff --git a/devtools/docs/user/debugger/how_to/set_a_breakpoint/breakpoints-on-line.png b/devtools/docs/user/debugger/how_to/set_a_breakpoint/breakpoints-on-line.png Binary files differnew file mode 100644 index 0000000000..17a929eb63 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/set_a_breakpoint/breakpoints-on-line.png diff --git a/devtools/docs/user/debugger/how_to/set_a_breakpoint/conditional-set.png b/devtools/docs/user/debugger/how_to/set_a_breakpoint/conditional-set.png Binary files differnew file mode 100644 index 0000000000..5abc6a258e --- /dev/null +++ b/devtools/docs/user/debugger/how_to/set_a_breakpoint/conditional-set.png diff --git a/devtools/docs/user/debugger/how_to/set_a_breakpoint/index.rst b/devtools/docs/user/debugger/how_to/set_a_breakpoint/index.rst new file mode 100644 index 0000000000..e92deb573d --- /dev/null +++ b/devtools/docs/user/debugger/how_to/set_a_breakpoint/index.rst @@ -0,0 +1,108 @@ +================ +Set a breakpoint +================ + +There are many different types of breakpoint that can be set in the debugger; this article covers standard (unconditional) breakpoints and conditional breakpoints. + +Breakpoints in brief +******************** + +Breakpoints are very useful when debugging JavaScript — you basically set a point in your code where you would like execution of the code to pause. At this point you can do useful things like studying the value of different variables at that point, allowing you to work out why a problem is occurring. + + +The source pane context menu +**************************** + +In the :ref:`source pane <debugger_ui_tour_source_pane>`, you can handle setting breakpoints by bringing up the context menu over a line number. + +.. image:: add-breakpoint-context.png + :class: border + +There are a few options available here: + +- *Add breakpoint*: Add a standard **unconditional breakpoint** at this line number (see below). + +- *Add condition*: Add a condition and create a **conditional breakpoint** (see below). + +- *Add log*: Add a :doc:`log point <../../set_a_logpoint/index>`, which logs a value to your console rather than pausing execution as a breakpoint does. + +- *Continue to here*: When :doc:`stepping through code <../step_through_code/index>`, this option tells the debugging to continue execution through to this point. + + +Unconditional breakpoints +************************* + +An unconditional breakpoint is one where the code will always pause execution when it is reached. You can set an unconditional breakpoint using the context menu (see above), or by: + +- Clicking on the line number for the line you want to break at in the source pane. +- Highlighting the line you want to break at in the source pane and pressing :kbd:`Ctrl` + :kbd:`B` (Windows/Linux) or :kbd:`Cmd` + :kbd:`B` (macOS). + +The line number is highlighted in blue: + +.. image:: breakpoints-on-line.png + :class: border + +In addition, if the line contains multiple function calls, each one will be given a small blue arrow icon to the left of it. These are called **column breakpoints**, and allow you to set the breakpoint to happen exactly on any one of the function calls in the line (or multiple calls), by clicking on each relevant one. + + +Conditional breakpoints +*********************** + +A conditional breakpoint is one where the code will pause execution when it is reached, only if a certain condition is met, such a variable having a certain value at the time. You can set a conditional breakpoint using the context menu (see above), or by highlighting the line you want to break at in the source pane and pressing :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`B` (Windows/Linux) or :kbd:`Cmd` + :kbd:`Shift` + :kbd:`B` (macOS). + +When you first choose to set a conditional breakpoint, a text entry line will appear into which you add the condition you want it to break on: + +.. image:: updated-conditional.png + :class: border + +Once you've entered your condition and pressed :kbd:`Enter`/:kbd:`Return`, the line number will be highlighted in orange: + +.. image:: conditional-set.png + :class: border + + +Breakpoints list +**************** + +Once you've set some breakpoints, the :ref:`breakpoints list <debugger-ui-tour-breakpoints-list>` in the right-hand column shows the filename and line number for each one: + +.. image:: breakpoints-list.png + :class: border + + +Unsetting a breakpoint +********************** + +Once a breakpoint has been set, you can unset it again in various ways: + +- Click on the line number highlight. +- Highlight the line of code the breakpoint is set on and pressing :kbd:`Ctrl` + :kbd:`B` (Windows/Linux) or :kbd:`Cmd` + :kbd:`B` (macOS). +- Bring up the context menu over the line highlight and choose the *Remove Breakpoint* option. + +.. image:: remove-breakpoint-context.png + :class: border + +Other context menu options worth mentioning are: + + +- *Disable Breakpoint:* turn it off, but don't remove it completely. +- *Disable breakpoints on line* and *Remove breakpoints on line*: Remove or disable column breakpoints. +- If the breakpoint is an unconditional breakpoint, you'll get an option *Add condition*, which allows you to turn it into a conditional breakpoint. +- If the breakpoint is a conditional breakpoint, you'll get an option *Edit condition*, which allows you to change the previously set condition. + + +.. _debugger-how-to-set-a-breakpoint-variable-preview: + +Inline variable preview +*********************** + +New in Firefox 71, the :ref:`source pane <debugger_ui_tour_source_pane>` now gives you an inline preview of the variables on each line of code you've stepped through: + +.. image:: inline-variables.png + :class: border + +This is a very useful timesaver when stepping through your code. Previously you’d have to scroll through the Scopes panel to find variable values, or hover over a variable in the source pane. Now when execution pauses, you can view relevant variables immediately. + +.. note:: + + There is also a new option in the context menu for the actual code in the source pane — *Hide inline preview*/*Show inline preview* — which allows you to turn the inline variables on/off. diff --git a/devtools/docs/user/debugger/how_to/set_a_breakpoint/inline-variables.png b/devtools/docs/user/debugger/how_to/set_a_breakpoint/inline-variables.png Binary files differnew file mode 100644 index 0000000000..fe3913356d --- /dev/null +++ b/devtools/docs/user/debugger/how_to/set_a_breakpoint/inline-variables.png diff --git a/devtools/docs/user/debugger/how_to/set_a_breakpoint/remove-breakpoint-context.png b/devtools/docs/user/debugger/how_to/set_a_breakpoint/remove-breakpoint-context.png Binary files differnew file mode 100644 index 0000000000..99132dd05e --- /dev/null +++ b/devtools/docs/user/debugger/how_to/set_a_breakpoint/remove-breakpoint-context.png diff --git a/devtools/docs/user/debugger/how_to/set_a_breakpoint/updated-conditional.png b/devtools/docs/user/debugger/how_to/set_a_breakpoint/updated-conditional.png Binary files differnew file mode 100644 index 0000000000..70c86c6adc --- /dev/null +++ b/devtools/docs/user/debugger/how_to/set_a_breakpoint/updated-conditional.png diff --git a/devtools/docs/user/debugger/how_to/set_a_conditional_breakpoint/index.rst b/devtools/docs/user/debugger/how_to/set_a_conditional_breakpoint/index.rst new file mode 100644 index 0000000000..7226f08049 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/set_a_conditional_breakpoint/index.rst @@ -0,0 +1,14 @@ +============================ +Set a conditional breakpoint +============================ + +A normal breakpoint is just associated with a line: when the program reaches that line, the debugger pauses. A conditional breakpoint also has a condition associated with it, which is represented as an `expression <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#expressions>`_. When the program reaches the line, the debugger pauses only if the breakpoint's specified expression evaluates to ``true``. + +This makes it possible to debug specific scenarios, such as bugs that only happen on odd entries in a list, or errors that occur the last time through a loop. + + +To set a conditional breakpoint, activate the context menu in the :ref:`source pane <debugger_ui_tour_source_pane>`, on the line where you want the breakpoint, and select "Add Conditional Breakpoint". You'll then see a textbox where you can enter the expression. Press :kbd:`Return` to finish. + +Conditional breakpoints are shown as orange arrows laid over the line number. + +If you context-click on any breakpoint, you'll see a menu item "Edit Breakpoint". You can use this to modify an existing condition or to add a condition to a normal breakpoint. diff --git a/devtools/docs/user/debugger/how_to/set_watch_expressions/ff_watch_expressions_add.png b/devtools/docs/user/debugger/how_to/set_watch_expressions/ff_watch_expressions_add.png Binary files differnew file mode 100644 index 0000000000..77b918e50b --- /dev/null +++ b/devtools/docs/user/debugger/how_to/set_watch_expressions/ff_watch_expressions_add.png diff --git a/devtools/docs/user/debugger/how_to/set_watch_expressions/ff_watch_expressions_remove.png b/devtools/docs/user/debugger/how_to/set_watch_expressions/ff_watch_expressions_remove.png Binary files differnew file mode 100644 index 0000000000..36a2ce6204 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/set_watch_expressions/ff_watch_expressions_remove.png diff --git a/devtools/docs/user/debugger/how_to/set_watch_expressions/index.rst b/devtools/docs/user/debugger/how_to/set_watch_expressions/index.rst new file mode 100644 index 0000000000..e70de5391a --- /dev/null +++ b/devtools/docs/user/debugger/how_to/set_watch_expressions/index.rst @@ -0,0 +1,25 @@ +===================== +Set watch expressions +===================== + +The Debugger *Watch expressions* pane allows you to specify JavaScript expressions that will be reevaluated and displayed every time the debugger pauses. As you step through code, the debugger will watch the expression and return any results.Watches are most commonly used to group individual*variables* of interest for easier observation. Watching more complicated expressions can sometimes also be useful:for example, to check that variables are within certain limits or values. + +The screenshot below shows the *Watch expressions* panel with a number of expressions already defined. Each line shows the expression and its value at the current step,separated by a colon. Expressions that evaluate to an object can be expanded using the caret symbol to the left. + +.. image:: ff_watch_expressions_add.png + :alt: Screenshot showing the Watch expressions dialog after the + button has been pressed (for entry of a new watch expression) + :class: border + +To add a watch expression click the **+** button in the top right corner of the panel. Then type the expression into the text entry field that appears at the bottom of the panel, and press :kbd:`Enter` to save it. The expression will be evaluated when you save, when you step through the code, or when you select the *Refresh* icon (next to **+**). + +You can enter any valid expression into the watch, and even declare new "watch" variables and re-use them. For example, ``mywatchvar1 = 3`` and ``mywatchvar2 = mywatchvar1 + 2`` will evaluate ``mywatchvar2`` as 5. You can also declare an expression that modifies a variable value in the code, and this will be re-evaluated whenever you step through the code or refresh the watch expression. + +.. warning:: + + **Important**: Changing values in the code using a watch expression may affect normal code execution. + +To remove a watch expression, select the **X** button that appears when you hover over a line. + +.. image:: ff_watch_expressions_remove.png + :alt: Hover over watch expression to get X that can be clicked to remove an expression. + :class: border diff --git a/devtools/docs/user/debugger/how_to/step_through_code/breakpoint_toolbar.png b/devtools/docs/user/debugger/how_to/step_through_code/breakpoint_toolbar.png Binary files differnew file mode 100644 index 0000000000..df6feec180 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/step_through_code/breakpoint_toolbar.png diff --git a/devtools/docs/user/debugger/how_to/step_through_code/debugger-overlay.png b/devtools/docs/user/debugger/how_to/step_through_code/debugger-overlay.png Binary files differnew file mode 100644 index 0000000000..1ee92845d8 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/step_through_code/debugger-overlay.png diff --git a/devtools/docs/user/debugger/how_to/step_through_code/index.rst b/devtools/docs/user/debugger/how_to/step_through_code/index.rst new file mode 100644 index 0000000000..aaca3dab15 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/step_through_code/index.rst @@ -0,0 +1,43 @@ +================= +Step through code +================= + +When the debugger is stopped at a breakpoint, you can step through it using four buttons in the :ref:`toolbar <debugger-ui-tour-toolbar>`: + +.. image:: breakpoint_toolbar.png + +In order, the buttons are: + + +- Play: run to the next breakpoint +- Step over: advance to the next line in the same function. +- Step in: advance to the next line in the function, unless on a function call, in which case enter the function being called +- Step out: run to the end of the current function, in which case, the debugger will skip the return value from a function, returning execution to the caller + + +Split console +************* + +When paused, you can press the Esc key to open and close the split console to gain more insight into errors and variables: + +.. image:: split_console.png + :class: border + + +Pause on breakpoints overlay +**************************** + +Since Firefox 70, when your code is paused on a breakpoint an overlay appears on the viewport of the tab you are debugging. + +.. image:: debugger-overlay.png + :alt: border + + +This lets you know what kind of breakpoint the code is paused on (breakpoint, event breakpoint, etc.), and also provides a step button and a play button. The thinking here is that if you've got your DevTools open in a separate window, as many people do, it can be easier to have the buttons available right there to move the code forward while you are looking at the result. + +If you want to turn this feature off, since Firefox 71 you can do so by going to ``about:config`` in a new tab, setting the ``devtools.debugger.features.overlay`` pref to ``false``, and restarting the browser. + +Inline variable preview +*********************** + +New in Firefox 71, the :ref:`source pane <debugger_ui_tour_source_pane>` now gives you an instant preview of the variables on each line of code you've stepped through. See :ref:`Set a breakpoint > Inline variable preview <debugger-how-to-set-a-breakpoint-variable-preview>` for more information. diff --git a/devtools/docs/user/debugger/how_to/step_through_code/split_console.png b/devtools/docs/user/debugger/how_to/step_through_code/split_console.png Binary files differnew file mode 100644 index 0000000000..46a5ecd141 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/step_through_code/split_console.png diff --git a/devtools/docs/user/debugger/how_to/use_a_source_map/index.rst b/devtools/docs/user/debugger/how_to/use_a_source_map/index.rst new file mode 100644 index 0000000000..e78bdc905b --- /dev/null +++ b/devtools/docs/user/debugger/how_to/use_a_source_map/index.rst @@ -0,0 +1,34 @@ +================ +Use a source map +================ + +The JavaScript sources executed by the browser are often transformed in some way from the original sources created by a developer. For example: + +- sources are often combined and `minified <https://en.wikipedia.org/wiki/Minification_(programming)>`_ to make delivering them from the server more efficient. + +- JavaScript running in a page is often machine-generated, as when compiled from a language like `CoffeeScript <https://coffeescript.org/>`_ or `TypeScript <https://www.typescriptlang.org/>`_ + +In these situations, it's much easier to debug the original source, rather than the source in the transformed state that the browser has downloaded. A `source map <https://www.html5rocks.com/en/tutorials/developertools/sourcemaps/>`_ is a file that maps from the transformed source to the original source, enabling the browser to reconstruct the original source and present the reconstructed original in the debugger. + +To enable the debugger to work with a source map, you must: + +- generate the source map +- include a comment in the transformed file, that points to the source map. The comment's syntax is like this: + +.. code-block:: JavaScript + + //# sourceMappingURL=http://example.com/path/to/your/sourcemap.map + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/Fqd15gHC4Pg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +In the video above we load https://firefox-devtools.github.io/devtools-examples/sourcemaps-in-console/index.html. This page loads a source called "main.js" that was originally written in CoffeeScript and compiled to JavaScript. The compiled source contains a comment like this, that points to a source map: + +.. code-block:: JavaScript + + //# sourceMappingURL=main.js.map + +In the Debugger's :ref:`source list pane <debugger-ui-tour-source-list-pane>`, the original CoffeeScript source now appears as "main.coffee", and we can debug it just like any other source. diff --git a/devtools/docs/user/debugger/how_to/use_watchpoints/index.rst b/devtools/docs/user/debugger/how_to/use_watchpoints/index.rst new file mode 100644 index 0000000000..eb4c803882 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/use_watchpoints/index.rst @@ -0,0 +1,53 @@ +=============== +Use watchpoints +=============== + +When debugging JavaScript code, it can be useful to know when properties on objects are read or modified. In a large, complex codebase, it's not always easy to know where in the code a given property is accessed. In the Firefox Debugger, this information can be provided by *watchpoints*. By setting a watchpoint on the property, rather than a breakpoint at a particular line, you can discover where that access occurs. + +There are three types of watchpoints: *get*, *set*, and *get or set*. A *get* watchpoint pauses whenever a property is read; a *set* watchpoint pauses whenever a property value changes; a *get or set* watchpoint pauses whenever a property value is accessed in either way. + +Set a watchpoint +**************** + +1. Run and then pause the debugger. +2. In the Scopes pane on the right side of the Debugger user interface, find an object you want to watch, and right-click it to open its context menu. +3. Choose **Break on**, and then one of + + - **Property set** + - **Property get** + - **Property get or set** + + .. image:: watchpoint-get-or-set.png + :alt: Screenshot showing the context menu for setting a watchpoint on an object + :class: border + + A watchpoint icon appears to the right of the property in the Scopes pane. *Set* watchpoint icons are blue, *get* watchpoint icons are reddish, and *get or set* watchpoint icons are dark yellow. + + + .. image:: watchpoint-icons.png + :alt: Screenshot highlighting the 3 types of watchpoint icons + :class: border + +4. Click **Play** or press :kbd:`F8` to resume execution. + + +View a watchpoint +***************** + +When the watched property is accessed in the way specified by the watchpoint type (get or set), the debugger pauses, enabling you to see line of code responsible, and to inspect anything else you wish at that time. + +In the following screenshot, the debugger pauses at line 7, where ``obj.a`` is set. The message panel in the upper right corner indicates that the debugger is "Paused on property set". + +.. image:: watchpoint_pause.png + :class: border + +Delete a watchpoint +******************* + +- Locate the watched property in the Scopes pane. +- Click the watchpoint icon, or right-click and choose **Remove watchpoint**. The watchpoint is removed. + +See also +******** + +- `Debugging variables with watchpoints in Firefox 72 <https://hacks.mozilla.org/2019/12/debugging-variables-with-watchpoints-in-firefox-72/>`_ diff --git a/devtools/docs/user/debugger/how_to/use_watchpoints/watchpoint-get-or-set.png b/devtools/docs/user/debugger/how_to/use_watchpoints/watchpoint-get-or-set.png Binary files differnew file mode 100644 index 0000000000..ddd349b393 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/use_watchpoints/watchpoint-get-or-set.png diff --git a/devtools/docs/user/debugger/how_to/use_watchpoints/watchpoint-icons.png b/devtools/docs/user/debugger/how_to/use_watchpoints/watchpoint-icons.png Binary files differnew file mode 100644 index 0000000000..f6e97c04df --- /dev/null +++ b/devtools/docs/user/debugger/how_to/use_watchpoints/watchpoint-icons.png diff --git a/devtools/docs/user/debugger/how_to/use_watchpoints/watchpoint_pause.png b/devtools/docs/user/debugger/how_to/use_watchpoints/watchpoint_pause.png Binary files differnew file mode 100644 index 0000000000..df9e39a217 --- /dev/null +++ b/devtools/docs/user/debugger/how_to/use_watchpoints/watchpoint_pause.png diff --git a/devtools/docs/user/debugger/index.rst b/devtools/docs/user/debugger/index.rst new file mode 100644 index 0000000000..6db1e386ff --- /dev/null +++ b/devtools/docs/user/debugger/index.rst @@ -0,0 +1,70 @@ +=============================== +The Firefox JavaScript Debugger +=============================== + +The JavaScript Debugger enables you to step through JavaScript code and examine or modify its state to help track down bugs. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/QK4hKWmJVLo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +You can use it to debug code running locally in Firefox or running remotely, for example on an Android device running Firefox for Android. See :doc:`about debugging <../about_colon_debugging/index>` to learn how to connect the debugger to a remote target. + +To find your way around the debugger, here's a :doc:`quick tour of the UI<ui_tour/index>`. + + +How to +****** + +To find out what you can do with the debugger, refer to the following how-to guides. + +- :doc:`Open the debugger <how_to/open_the_debugger/index>` +- :doc:`Pretty-print a minified file <how_to/pretty-print_a_minified_file/index>` +- :doc:`Search <how_to/search/index>` +- :doc:`Use a source map <how_to/use_a_source_map/index>` +- `Debug worker threads <https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#debugging_worker_threads>`_ + + +Pause execution +--------------- + +You probably want to pause the execution of your code, in order to see what is going on at various points. There are multiple ways to tell the Debugger how and when to pause: + + +- :doc:`Set a breakpoint <how_to/set_a_breakpoint/index>` +- :doc:`Set a conditional breakpoint <how_to/set_a_conditional_breakpoint/index>` +- :doc:`Set an XHR breakpoint <set_an_xhr_breakpoint/index>` +- :doc:`Set event listener <set_event_listener_breakpoints/index>` +- :doc:`Break on exceptions <how_to/breaking_on_exceptions/index>` +- :doc:`Use watchpoints for property reads and writes <how_to/use_watchpoints/index>` +- :doc:`Break on DOM Mutation <break_on_dom_mutation/index>` +- :doc:`Disable breakpoints <how_to/disable_breakpoints/index>` + + +Control execution +----------------- + +What can you do after execution pauses? + +- :doc:`Step through code <how_to/step_through_code/index>` +- :doc:`Black box a source <how_to/ignore_a_source/index>` +- `Debug worker threads <https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#debugging_worker_threads>`_ +- :doc:`Debug eval sources <how_to/debug_eval_sources/index>` + + +Look at values +-------------- + +You probably want to see the value of variables or expressions, either during execution or when it is paused. + +- :doc:`Set a logpoint <set_a_logpoint/index>` +- :doc:`Set watch expressions <how_to/set_watch_expressions/index>` + +Reference +********* + +- :ref:`Keyboard shortcuts <keyboard-shortcuts-debugger>` +- :doc:`Source map errors <source_map_errors/index>` diff --git a/devtools/docs/user/debugger/set_a_logpoint/add_logpoint.png b/devtools/docs/user/debugger/set_a_logpoint/add_logpoint.png Binary files differnew file mode 100644 index 0000000000..35d4630f4d --- /dev/null +++ b/devtools/docs/user/debugger/set_a_logpoint/add_logpoint.png diff --git a/devtools/docs/user/debugger/set_a_logpoint/creating_the_log_point.png b/devtools/docs/user/debugger/set_a_logpoint/creating_the_log_point.png Binary files differnew file mode 100644 index 0000000000..42ab3a6bcf --- /dev/null +++ b/devtools/docs/user/debugger/set_a_logpoint/creating_the_log_point.png diff --git a/devtools/docs/user/debugger/set_a_logpoint/index.rst b/devtools/docs/user/debugger/set_a_logpoint/index.rst new file mode 100644 index 0000000000..b8ecd4d56a --- /dev/null +++ b/devtools/docs/user/debugger/set_a_logpoint/index.rst @@ -0,0 +1,52 @@ +============== +Set a logpoint +============== + +Sometimes you want to view a value in your code but you don't want to pause execution. Rather than sprinkle `console.log() <https://developer.mozilla.org/en-US/docs/Web/API/console/log>`_ statements throughout your code, you can use a special type of breakpoint, the logpoint. Logpoints print a message to the Console panel instead of pausing code execution. + +The logpoint is especially useful in cases where breaking the execution breaks testing procedures, such as when you are debugging popup windows, or executing focus-related logic. + +To create a logpoint: + +1. Right click on a line number in the Debugger panel and pick **Add log** action from the context menu. + + .. image:: add_logpoint.png + :class: center + +2. Create an expression inline. The result is logged in the console panel every time the line with the logpoint executes. You can use any variable or function available in the current scope. + + .. image:: creating_the_log_point.png + :class: center + +Working with logpoints +********************** + +When you set a logpoint, the indicator is purple, rather than the blue of an unconditional breakpoint or the orange of a conditional breakpoint. + +You can view the list of logpoints in the Breakpoints side panel. + +.. image:: list_logpoints.png + :class: border + +When execution hits a logpoint, the message you have defined is logged to the console. You can make it easier to see the message by opening a split console under the debugger. (Either press :kbd:`Esc` or select the ellipsis menu (**...**) and then click **Show Split Console**.) + +.. image:: logpoints.png + :class: border + + +When should you use logpoints? +------------------------------ + +- When you want to know that a specific line in your code has executed, but you don’t want to break code execution, set a logpoint. +- Logpoints are useful to log the value of a variable at a specific point in the code. It’s faster than changing the underlying source code itself to add calls to the ``console.log`` method. +- If you need to execute additional logic related to the source code and you can’t change the source code itself, you can use a logpoint. +- Note that you can use logpoints with :doc:`source-mapped code <../using_the_debugger_map_scopes_feature/index>`, as well as with your unmapped source files. + + +See also +******** + +- :doc:`Set a breakpoint <../how_to/set_a_breakpoint/index>` +- :doc:`Set a conditional breakpoint <../how_to/set_a_conditional_breakpoint/index>` +- :doc:`Set an XHR breakpoint <../set_an_xhr_breakpoint/index>` +- :doc:`Disable breakpoints <../how_to/disable_breakpoints/index>` diff --git a/devtools/docs/user/debugger/set_a_logpoint/list_logpoints.png b/devtools/docs/user/debugger/set_a_logpoint/list_logpoints.png Binary files differnew file mode 100644 index 0000000000..bb7834fe09 --- /dev/null +++ b/devtools/docs/user/debugger/set_a_logpoint/list_logpoints.png diff --git a/devtools/docs/user/debugger/set_a_logpoint/logpoints.png b/devtools/docs/user/debugger/set_a_logpoint/logpoints.png Binary files differnew file mode 100644 index 0000000000..dc4891df1c --- /dev/null +++ b/devtools/docs/user/debugger/set_a_logpoint/logpoints.png diff --git a/devtools/docs/user/debugger/set_an_xhr_breakpoint/index.rst b/devtools/docs/user/debugger/set_an_xhr_breakpoint/index.rst new file mode 100644 index 0000000000..e831df49f1 --- /dev/null +++ b/devtools/docs/user/debugger/set_an_xhr_breakpoint/index.rst @@ -0,0 +1,33 @@ +===================== +Set an XHR breakpoint +===================== + +An XHR (XMLHttpRequest) breakpoint breaks code execution when an XHR request is dispatched so that you can examine the current state of the program. You can break on all requests or on those that include a specific URL. To turn on the feature: + +1. Open the debugger +2. Click on "Pause on any URL" which acts as a wild card, causing the code to pause on any call, or, +3. Click the plus sign next to the XHR breakpoints header, enter the URL in which you are interested, and press :kbd:`Enter`. + +.. note:: + + If you enter a key word instead of a URL, code execution will pause on any call to a URL that contains that keyword. + +.. image:: xhr_breakpoint.png + :class: border + +When your code breaks on an XHR request, the right hand pane will have two additional sections: + +**Call stack** + The list of functions that were executed in order to get to the currently executing code. Click on an item in the call stack to jump to the associated line in the code display. + +**Scopes** + A list of the values that are in scope in the function, method, or event handler where the break occurred. + +.. image:: xhr_break.png + :class: border + + +Inline variable preview +*********************** + +New in Firefox 71, the :ref:`source pane <debugger_ui_tour_source_pane>` now gives you an instant preview of the variables on each line of code you've stepped through. See :ref:`Set a breakpoint > Inline variable preview <debugger-how-to-set-a-breakpoint-variable-preview>` for more information. diff --git a/devtools/docs/user/debugger/set_an_xhr_breakpoint/xhr_break.png b/devtools/docs/user/debugger/set_an_xhr_breakpoint/xhr_break.png Binary files differnew file mode 100644 index 0000000000..3da3ad0a82 --- /dev/null +++ b/devtools/docs/user/debugger/set_an_xhr_breakpoint/xhr_break.png diff --git a/devtools/docs/user/debugger/set_an_xhr_breakpoint/xhr_breakpoint.png b/devtools/docs/user/debugger/set_an_xhr_breakpoint/xhr_breakpoint.png Binary files differnew file mode 100644 index 0000000000..ebdd338d39 --- /dev/null +++ b/devtools/docs/user/debugger/set_an_xhr_breakpoint/xhr_breakpoint.png diff --git a/devtools/docs/user/debugger/set_event_listener_breakpoints/event-listener-breakpoints.png b/devtools/docs/user/debugger/set_event_listener_breakpoints/event-listener-breakpoints.png Binary files differnew file mode 100644 index 0000000000..31775c92e0 --- /dev/null +++ b/devtools/docs/user/debugger/set_event_listener_breakpoints/event-listener-breakpoints.png diff --git a/devtools/docs/user/debugger/set_event_listener_breakpoints/filter-event-breakpoints.png b/devtools/docs/user/debugger/set_event_listener_breakpoints/filter-event-breakpoints.png Binary files differnew file mode 100644 index 0000000000..5a0a14a332 --- /dev/null +++ b/devtools/docs/user/debugger/set_event_listener_breakpoints/filter-event-breakpoints.png diff --git a/devtools/docs/user/debugger/set_event_listener_breakpoints/index.rst b/devtools/docs/user/debugger/set_event_listener_breakpoints/index.rst new file mode 100644 index 0000000000..b087ba903a --- /dev/null +++ b/devtools/docs/user/debugger/set_event_listener_breakpoints/index.rst @@ -0,0 +1,72 @@ +============================== +Set event listener breakpoints +============================== + +Starting with Firefox 69, debugging an application that includes event handlers is simplified because the debugger now includes the ability to automatically break when the code hits an event handler. This article explains how to use it. + +Using a standard event breakpoint +********************************* + +To use an event breakpoint, you open up the JavaScript debugger, and find and expand the Event Listener Breakpoints section in the right hand column. + +.. image:: event-listener-breakpoints.png + :alt: The list of event listener breakpoints in the right hand column + :class: border + +To break when event listeners are hit, check the boxes next the events you are interested in. All of the standard events supported in your version of Firefox are listed, arranged by which API or API area they're part of. + +Now when a `keydown <https://developer.mozilla.org/en-US/docs/Web/API/Document/keydown_event>`_, `keyup <https://developer.mozilla.org/en-US/docs/Web/API/Document/keyup_event>`_, `keypress <https://developer.mozilla.org/en-US/docs/Web/API/Document/keypress_event>`_, or `input <https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event>`_ event occurs, execution will pause as soon as it enters the listener code. + +.. image:: paused-on-breakpoint.png + :alt: execution is paused on an event breakpoint and the source panel shows what line it is paused on + :class: border + +When execution pauses, the :ref:`source pane <debugger_ui_tour_source_pane>` displays the highlighted line of the JavaScript code that is next to be executed, along with the surrounding code for context. In addition, you get a box overlaid on the viewport saying "Paused on event breakpoint", with buttons to step over that line of code or resume execution. + +You *could* add regular breakpoints at the entry point of the listener to achieve the same effect. If however you have multiple elements, all of which have event listeners attached that you want to break on, this becomes an enormous time saver. + +This is also useful when debugging code that you're unfamiliar with, to save time over hunting down your event handler within your code, or when trying to understand why a web site isn't working as expected in your browser. Use event breakpoints to stop execution at the triggering event for the action that fails, then step through the code or watch the console to see what happens. + +Logging on events +***************** + +In Firefox 71 onwards, the “Log” checkbox is available in the *Event Listener Breakpoints* list. Selecting this and then choosing some events to break on will mean that when you step through code, information about events fired will be logged to the console instead of the DevTools breaking on each one. + +So if we choose to log keyboard events, for example, the code no longer pauses as each event is fired: + +.. image:: log-events-1.png + :alt: log on events checkbox + :class: border + + +Instead, we can then switch to the console, and whenever we press a key we are given a log of where related events were fired. + +.. image:: log-events-2.png + :alt: events logged in console + + +There's an issue here — the console is showing that the `keypress event <https://developer.mozilla.org/en-US/docs/Web/API/Document/keypress_event>`_ is being fired somewhere inside jQuery. Instead, it’d be far more useful if we showed where in our own app code is calling the jQuery that fired the event. This can be done by finding ``jquery.js`` in the Sources panel, and choosing the **Ignore source option** from its context menu. + +.. image:: log-events-3.png + :alt: blackbox jquery source + :class: border + +Now the logs will show where in your app jQuery was called, rather than where in jQuery the event was fired: + +.. image:: log-events-4.png + :alt: events logged in console with lines shown where our source calls jQuery. + :class: border + +Filter by event type +******************** + +Also added to Firefox 71 is a new *Filter by event type...* text input, which can also be found at the top of the *Event Listener Breakpoints* list. When you click in this input and type a search term, the list of event listener types will filter by that term allowing you to find the events you want to break on more easily. + +.. image:: filter-event-breakpoints.png + :class: border + + +Inline variable preview +*********************** + +New in Firefox 71, the :ref:`source pane <debugger_ui_tour_source_pane>` now gives you an instant preview of the variables on each line of code you've stepped through. See :ref:`Set a breakpoint > Inline variable preview <debugger-how-to-set-a-breakpoint-variable-preview>` for more information. diff --git a/devtools/docs/user/debugger/set_event_listener_breakpoints/log-events-1.png b/devtools/docs/user/debugger/set_event_listener_breakpoints/log-events-1.png Binary files differnew file mode 100644 index 0000000000..60cf92cd78 --- /dev/null +++ b/devtools/docs/user/debugger/set_event_listener_breakpoints/log-events-1.png diff --git a/devtools/docs/user/debugger/set_event_listener_breakpoints/log-events-2.png b/devtools/docs/user/debugger/set_event_listener_breakpoints/log-events-2.png Binary files differnew file mode 100644 index 0000000000..323847b740 --- /dev/null +++ b/devtools/docs/user/debugger/set_event_listener_breakpoints/log-events-2.png diff --git a/devtools/docs/user/debugger/set_event_listener_breakpoints/log-events-3.png b/devtools/docs/user/debugger/set_event_listener_breakpoints/log-events-3.png Binary files differnew file mode 100644 index 0000000000..de94a8d9ee --- /dev/null +++ b/devtools/docs/user/debugger/set_event_listener_breakpoints/log-events-3.png diff --git a/devtools/docs/user/debugger/set_event_listener_breakpoints/log-events-4.png b/devtools/docs/user/debugger/set_event_listener_breakpoints/log-events-4.png Binary files differnew file mode 100644 index 0000000000..26c69aa3e4 --- /dev/null +++ b/devtools/docs/user/debugger/set_event_listener_breakpoints/log-events-4.png diff --git a/devtools/docs/user/debugger/set_event_listener_breakpoints/paused-on-breakpoint.png b/devtools/docs/user/debugger/set_event_listener_breakpoints/paused-on-breakpoint.png Binary files differnew file mode 100644 index 0000000000..ea42ec5dbe --- /dev/null +++ b/devtools/docs/user/debugger/set_event_listener_breakpoints/paused-on-breakpoint.png diff --git a/devtools/docs/user/debugger/source_map_errors/debugger-tab.png b/devtools/docs/user/debugger/source_map_errors/debugger-tab.png Binary files differnew file mode 100644 index 0000000000..869db51aac --- /dev/null +++ b/devtools/docs/user/debugger/source_map_errors/debugger-tab.png diff --git a/devtools/docs/user/debugger/source_map_errors/index.rst b/devtools/docs/user/debugger/source_map_errors/index.rst new file mode 100644 index 0000000000..5bad6ac250 --- /dev/null +++ b/devtools/docs/user/debugger/source_map_errors/index.rst @@ -0,0 +1,81 @@ +================= +Source map errors +================= + +Source maps are JSON files providing a way to associate transformed sources, as seen by the browser, with their original sources, as written by the developer. You can sometimes encounter problems working with source maps. This page explains the most common problems and how to fix them. + +.. note:: + + If you're new to source maps, you can learn more about them in :doc:`How to use a source map <../how_to/use_a_source_map/index>`. + + +General source map error reporting +********************************** + +If you do see a problem, a message will appear in the webconsole. This message will show an error message, the resource URL, and the source map URL: + +.. image:: invalid-json.png + :alt: Error from invalid JSON + :class: border + +Here, the resource URL tells us that ``bundle.js`` mentions a source map, and the source map URL tells us where to find the source map data (in this case, relative to the resource). The error tells us that the source map is not JSON data — so we're serving the wrong file. + +There are a few common ways that source maps can go wrong; they are detailed in the following sections. + + +Source map missing or inaccessible +********************************** + +The source map resource can be missing or inaccessible. + +.. image:: missing-map.png + :alt: Source map file is missing + :class: border + +The fix here is to make sure the file is being served and is accessible to the browser + + +Invalid source map +****************** + +The source map data can be invalid — either not a JSON file at all, or with an incorrect structure. Typical error messages here are: + + +- ``SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data`` +- ``Error: "version" is a required argument`` + +.. image:: missing-field.png + :class: border + :alt: Error: "version" is a required argument + + +Original source missing +*********************** + +An original source may be missing. You may encounter this when trying to open one of the original sources in the debugger. The message looks a little different in this case: + +.. image:: screenshot_from_2017-09-15_14-32-02.png + :alt: Debugger source tab showing the error + :class: border + + +In this case, the error will also be displayed in the source tab in the debugger: + +.. image:: debugger-tab.png + :alt: Debugger source tab showing the error + :class: border + + +NetworkError when attempting to fetch resource +********************************************** + +A bug in Firefox prevents it from loading source maps for web extensions. + +See `Bug 1437937: WebExtensions Doesn't Find Source Maps <https://bugzilla.mozilla.org/show_bug.cgi?id=1437937>`_ for details. + +.. code-block: html + Source-Map-Fehler: TypeError: NetworkError when attempting to fetch resource. + Ressourcen-Adresse: moz-extension://c7f0f003-4fcf-49fd-8ec0-c49361266581/background.js + Source-Map-Adresse: background.js.map</pre> + +The only workaround is to manually change the map URL to a public one (http://localhost:1234/file.map.js) and start a local webserver at this port. diff --git a/devtools/docs/user/debugger/source_map_errors/invalid-json.png b/devtools/docs/user/debugger/source_map_errors/invalid-json.png Binary files differnew file mode 100644 index 0000000000..6d47d35325 --- /dev/null +++ b/devtools/docs/user/debugger/source_map_errors/invalid-json.png diff --git a/devtools/docs/user/debugger/source_map_errors/missing-field.png b/devtools/docs/user/debugger/source_map_errors/missing-field.png Binary files differnew file mode 100644 index 0000000000..bece5fd7ca --- /dev/null +++ b/devtools/docs/user/debugger/source_map_errors/missing-field.png diff --git a/devtools/docs/user/debugger/source_map_errors/missing-map.png b/devtools/docs/user/debugger/source_map_errors/missing-map.png Binary files differnew file mode 100644 index 0000000000..8d8ad7db4b --- /dev/null +++ b/devtools/docs/user/debugger/source_map_errors/missing-map.png diff --git a/devtools/docs/user/debugger/source_map_errors/screenshot_from_2017-09-15_14-32-02.png b/devtools/docs/user/debugger/source_map_errors/screenshot_from_2017-09-15_14-32-02.png Binary files differnew file mode 100644 index 0000000000..f71030fd7a --- /dev/null +++ b/devtools/docs/user/debugger/source_map_errors/screenshot_from_2017-09-15_14-32-02.png diff --git a/devtools/docs/user/debugger/ui_tour/debugger-source-folder-cxt-menu.png b/devtools/docs/user/debugger/ui_tour/debugger-source-folder-cxt-menu.png Binary files differnew file mode 100644 index 0000000000..8e49ffe00f --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/debugger-source-folder-cxt-menu.png diff --git a/devtools/docs/user/debugger/ui_tour/debugger-source-list-cxt-menu.png b/devtools/docs/user/debugger/ui_tour/debugger-source-list-cxt-menu.png Binary files differnew file mode 100644 index 0000000000..df837ac5ba --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/debugger-source-list-cxt-menu.png diff --git a/devtools/docs/user/debugger/ui_tour/debugger_scopes_fx78.png b/devtools/docs/user/debugger/ui_tour/debugger_scopes_fx78.png Binary files differnew file mode 100644 index 0000000000..bf99b6c010 --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/debugger_scopes_fx78.png diff --git a/devtools/docs/user/debugger/ui_tour/debugger_toolbar_with_settings_menu_four_items.jpg b/devtools/docs/user/debugger/ui_tour/debugger_toolbar_with_settings_menu_four_items.jpg Binary files differnew file mode 100644 index 0000000000..9c4c7ff5b8 --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/debugger_toolbar_with_settings_menu_four_items.jpg diff --git a/devtools/docs/user/debugger/ui_tour/debugger_uitour_01.png b/devtools/docs/user/debugger/ui_tour/debugger_uitour_01.png Binary files differnew file mode 100644 index 0000000000..2c2e57c256 --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/debugger_uitour_01.png diff --git a/devtools/docs/user/debugger/ui_tour/debugger_uitour_02.5.png b/devtools/docs/user/debugger/ui_tour/debugger_uitour_02.5.png Binary files differnew file mode 100644 index 0000000000..becf3b7221 --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/debugger_uitour_02.5.png diff --git a/devtools/docs/user/debugger/ui_tour/debugger_uitour_02.png b/devtools/docs/user/debugger/ui_tour/debugger_uitour_02.png Binary files differnew file mode 100644 index 0000000000..bdede32e71 --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/debugger_uitour_02.png diff --git a/devtools/docs/user/debugger/ui_tour/debugger_uitour_03.png b/devtools/docs/user/debugger/ui_tour/debugger_uitour_03.png Binary files differnew file mode 100644 index 0000000000..ddb14442e3 --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/debugger_uitour_03.png diff --git a/devtools/docs/user/debugger/ui_tour/debugger_uitour_breakpoints.png b/devtools/docs/user/debugger/ui_tour/debugger_uitour_breakpoints.png Binary files differnew file mode 100644 index 0000000000..5f44a76ccf --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/debugger_uitour_breakpoints.png diff --git a/devtools/docs/user/debugger/ui_tour/debugger_uitour_call_stack.png b/devtools/docs/user/debugger/ui_tour/debugger_uitour_call_stack.png Binary files differnew file mode 100644 index 0000000000..084b4c3bbf --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/debugger_uitour_call_stack.png diff --git a/devtools/docs/user/debugger/ui_tour/ff_debugger_callstack_ignore_source.png b/devtools/docs/user/debugger/ui_tour/ff_debugger_callstack_ignore_source.png Binary files differnew file mode 100644 index 0000000000..839eb35f31 --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/ff_debugger_callstack_ignore_source.png diff --git a/devtools/docs/user/debugger/ui_tour/firefox_source_pane_context_menu.jpg b/devtools/docs/user/debugger/ui_tour/firefox_source_pane_context_menu.jpg Binary files differnew file mode 100644 index 0000000000..9a6f2c6a09 --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/firefox_source_pane_context_menu.jpg diff --git a/devtools/docs/user/debugger/ui_tour/index.rst b/devtools/docs/user/debugger/ui_tour/index.rst new file mode 100644 index 0000000000..91835159a6 --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/index.rst @@ -0,0 +1,235 @@ +======= +UI Tour +======= + +This article is a quick tour of the main sections of the JavaScript Debugger's user interface. The UI is split vertically into three panels + + +- :ref:`Source list pane <debugger-ui-tour-source-list-pane>` +- :ref:`Source pane <debugger_ui_tour_source_pane>` +- The contents of the third pane depend on the current state of the debugger and may include the following sections: + + - :ref:`Toolbar <debugger-ui-tour-toolbar>` + - Watch expressions + - :ref:`Breakpoints <debugger-ui-tour-breakpoints-list>` + - :ref:`Call stack <debugger-ui-tour-call-stack>` + - :ref:`Scopes <debugger-ui-tour-scopes>` + - XHR Breakpoints + - Event Listener Breakpoints + - DOM Mutation Breakpoints + +.. image:: debugger_uitour_01.png + :class: border + + +.. _debugger-ui-tour-source-list-pane: + +Source list pane +**************** + +The source list pane lists all the JavaScript source files loaded into the page (`including scripts for active web workers <https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#debugging_worker_threads>`_), and enables you to select one to debug. At the top level sources are organized by origin, and under that they're organized by the directory structure from which they are served. + +.. image:: debugger_uitour_02.png + :class: border + +You can :ref:`search for a file <debugger-how-to-search-searching-for-files>` using :kbd:`Ctrl` + :kbd:`P` (:kbd:`Cmd` + :kbd:`P` on a Mac). + +Web Extensions are listed in the Source List pane using the extension's name. + +.. image:: source_list_pane.png + :class: border + +There are several context menu options available for individual files and folders or groups; typically viewed by right-clicking on the item. + +For files, the following context menu options are available: + +.. image:: debugger-source-list-cxt-menu.png + :alt: Screenshot showing the context menu options for files in the source list pane + :class: border + + +- **Copy source URI** copies the full identifier of the file to the clipboard. +- **Ignore source** causes the debugger to skip the file when "stepping into" functions; this can be helpful for avoiding stepping into libraries used by your code. When a file is ignored, it has a small eye icon next to it in place of its regular icon. +- **Download file** opens a file dialog so you can save the file locally. + + +For folders and groups, the following context menu options are available: + +.. image:: debugger-source-folder-cxt-menu.png + :alt: Screenshot showing context menu options for folders in the source list pane + :class: border + + +- **Collapse all** collapses all subfolders of the item. +- **Expand all** expands all subfolders of the item. +- **Set directory root** changes the Source List view so that only that item and its children are visible. The name of the selected directory is shown at the top of the Source List pane; clicking this name reverts the pane to showing all source items. +- **Ignore** (since Firefox 76) + + - **Ignore files in this directory** causes all files within the selected directory to be skipped by the debugger. All child files acquired the eye icon, and the folder menu option changes to **Unignore files in this directory**. + - **Ignore files outside this directory** causes all files other than those within the selected directory to be skipped by the debugger. All such files acquire the eye icon, and the menu option for that folder changes to **Unignore files outside this directory**. + + +Outline View +------------ + +The Outline view shows a tree for navigating the currently open file. Use it to jump directly to a function, class or method definition. + + +.. _debugger_ui_tour_source_pane: + +Source pane +*********** + +This shows the JavaScript file currently loaded. + +.. image:: debugger_uitour_02.5.png + :class: border + +When the source pane is focused you can :ref:`search for a string in the file <debugger-how-to-search-searching-within-a-file>` using :kbd:`Ctrl` + :kbd:`F` (:kbd:`Cmd` + :kbd:`F` on a Mac). + +:doc:`Breakpoints <../how_to/set_a_breakpoint/index>` have a blue arrow overlaid on the line number. :doc:`Conditional breakpoints <../how_to/set_a_conditional_breakpoint/index>` have an orange arrow. If you're stopped at a breakpoint, the entire line gets a green overlay. In the screenshot below there are three breakpoints: + + +- line 82 has a normal breakpoint and execution is paused here +- line 85 has a logpoint which logs the contents of table row to the console +- line 100 has a conditional breakpoint + +.. image:: debugger_uitour_03.png + :class: border + +The third column shows more information about the breakpoints. For example, the logpoint at line 85 logs the value of the tableRow variable to the console and the conditional breakpoint at line 100 breaks if the contents of the todoList is undefined. + +The source pane provides the following context menu options: + +.. image:: firefox_source_pane_context_menu.jpg + :alt: Debugger Source Pane Context Menu v2 + :class: center + + +- **Jump to generated location** is used when you have a sourcemap in a project and are currently viewing the original file. Selecting it takes you to the generated file where the selected item was placed. +- **Continue to here** causes the debugger to continue execution to the currently selected line (execution will stop before it gets there if there is a breakpoint "on the way"). +- **Copy to clipboard** copies selected text in the pane into the system clipboard. +- **Copy source text** copies all text in the file into the clipboard. +- **Copy source URI** copies the file location into the clipboard. +- **Download file** opens a file dialog so you can save the file locally. +- **Reveal in tree** highlights the file in the source pane list hierarchy. +- **Ignore source** causes the debugger to skip the file when "stepping into" functions; this can be helpful for avoiding stepping into libraries used by your code. +- **Show inline preview** toggles the inline preview feature, which displays the value of the variable right next to the source when execution is paused. +- **Wrap lines**/**Unwap lines** toggles the wrapping of long lines in the pane. + + +.. _debugger-ui-tour-toolbar: + +Toolbar +******* + +At the top of the right-hand pane, there's a toolbar: + +.. image:: debugger_toolbar_with_settings_menu_four_items.jpg + :class: center + +The toolbar consists of: + +- Four buttons to :doc:`control the debugger's movement through the script <../how_to/step_through_code/index>`: + + - **Play/pause** (:kbd:`F8`): pauses or resumes execution of the script you're debugging. When it displays a "play" icon, that means the script is paused, either because you've paused it with this button or because you've hit a breakpoint. + - **Step over** (:kbd:`F10`): steps to the next line of JavaScript code. + - **Step in** (:kbd:`F11`): steps into the function call on the current line of JavaScript code. + - **Step out** (:kbd:`Shift`-:kbd:`F11`): runs the script until the current function exits. + + +- A button that deactivates all breakpoints. +- A settings menu that contains: + + - **Disable JavaScript**: disables JavaScript for this tab. This option enables you to see how your web page looks if the user has disabled JavaScript via an extension or a configuration setting. The setting is reset when the Developer Tools are closed (except in Firefox 77, see `bug 1640318 <https://bugzilla.mozilla.org/show_bug.cgi?id=1640318>`_). + - **Inline Variable Preview**: enabled by default, this option displays variable values within the source pane when the debugger is paused. + - **Wrap Lines**: Toggles wrapping of long lines in the :ref:`source pane <debugger_ui_tour_source_pane>`. + - **Source Maps**: enabled by default, this option directs the Debugger to load the original versions of files, and map them to the generated ones loaded in a page, to ease debugging of transformed sources. See :doc:`Use a source map <../how_to/use_a_source_map/index>` for details. + + +.. _debugger-ui-tour-breakpoints-list: + +Breakpoints list +**************** + +Under the toolbar, you'll see all the breakpoints you've set. Next to each breakpoint is a checkbox which you can use to :doc:`enable/disable it <../how_to/disable_breakpoints/index>`: + +.. image:: debugger_uitour_breakpoints.png + :class: border + + +Watch expressions +***************** + +You can add watch expressions in the right pane. They will be evaluated when code execution is paused: + +.. image:: watch-expressions.png + :class: center + + +Variable tooltip +**************** + +Hover on a variable show a tooltip with its value inside: + +.. image:: tooltip-1.gif + :class: center + + +.. _debugger-ui-tour-call-stack: + +Call stack +********** + +The *call stack* becomes visible when the debugger is paused. + +.. image:: debugger_uitour_call_stack.png + :class: border + + +The stack lists the chain of functions that are waiting to complete, with the frame for the function that was called last at the top (i.e. the most deeply nested function).Selecting a line opens the associated file in the source pane, at the specified location. It also updates the :ref:`Scopes <debugger-ui-tour-scopes>` section with the variables for that frame/scope. + +.. note:: + + The call stack is a useful tool for tracking execution flow through your application! It allows you to confirm that functions are called in the order you expect, and with sensible variable values. + + +Call stack lines for frames in your own code show the function name and the file location in which it was called. + +.. note:: + + If you click **Step over** (:kbd:`F10`) after changing the selected line in the source pane, the debugger executes until reaching the line following the newly-selected line (disregarding whatever line the debugger originally stopped at). + + +Lines for JavaScript frameworks/libraries *used* by your code (React, jQuery, Angular, Webpack, Backbone etc.) are grouped by default,and represented by a framework-specific icon (see the *jQuery* frame in the screenshot above).Generally you won't want to debug into the code of frameworks or libraries, so grouping these reduces the complexity of the call stack list. You can still expand and inspect the grouped frames if needed, or disableframework grouping using a context menu option: **Disable framework grouping**. + +You can also use the context menu to **Ignore source** for a particular line. This will remove the line from the call stack, and the debugger will subsequently skip through any calls into that file. Note that you'll have to use the source pane "eye icon" or source list if you want to *Unignore* the source later! + +.. image:: ff_debugger_callstack_ignore_source.png + :alt: FF Debugger showing callstack with right-menu and marked up unignore/ignore source "eye" + :class: border + +Right-/Ctrl- clicking in the call stack pane opens a context menu with the following items: + + +- **Restart frame** restarts execution at the beginning of the current frame. +- **Enable framework grouping** collects items belonging to a framework into a collapsible group (for example, Webpack in the screenshot immediately above). When grouping is enabled, the menu option changes to **Disable framework grouping**. +- **Copy source URI** copies the full identifier of the source file to the clipboard. +- **Ignore source** causes the debugger to skip the file when "stepping into" functions. Any stack frames from the ignored source file are hidden in the call stack pane. (To remove this restriction, choose **Unignore source** in the context menu of the Sources list or the Source pane.) +- **Copy stack trace** copies all items in the call stack (including their URIs and line number) to the clipboard. + + +.. _debugger-ui-tour-scopes: + +Scopes +****** + +In the right-hand pane you'll see a label "Scopes" with a disclosure arrow next to it. When the debugger's paused, you'll be able to expand this section to see all objects that are in scope at this point in the program: + +.. image:: debugger_scopes_fx78.png + :alt: A screenshot of the Debugger, with the Scopes pane highlighted + :class: border + +Objects are organized by scope: the most local appears first, and the global scope (Window, in the case of page scripts) appears last. + +Within the Scopes pane, you can create :doc:`watchpoints <../how_to/use_watchpoints/index>` that pause the debugger when a value is read or assigned. diff --git a/devtools/docs/user/debugger/ui_tour/source_list_pane.png b/devtools/docs/user/debugger/ui_tour/source_list_pane.png Binary files differnew file mode 100644 index 0000000000..8d31cbc3a2 --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/source_list_pane.png diff --git a/devtools/docs/user/debugger/ui_tour/tooltip-1.gif b/devtools/docs/user/debugger/ui_tour/tooltip-1.gif Binary files differnew file mode 100644 index 0000000000..c3f24431b7 --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/tooltip-1.gif diff --git a/devtools/docs/user/debugger/ui_tour/watch-expressions.png b/devtools/docs/user/debugger/ui_tour/watch-expressions.png Binary files differnew file mode 100644 index 0000000000..eabace5088 --- /dev/null +++ b/devtools/docs/user/debugger/ui_tour/watch-expressions.png diff --git a/devtools/docs/user/debugger/using_the_debugger_map_scopes_feature/debugger_map_scope.png b/devtools/docs/user/debugger/using_the_debugger_map_scopes_feature/debugger_map_scope.png Binary files differnew file mode 100644 index 0000000000..ab769339e4 --- /dev/null +++ b/devtools/docs/user/debugger/using_the_debugger_map_scopes_feature/debugger_map_scope.png diff --git a/devtools/docs/user/debugger/using_the_debugger_map_scopes_feature/index.rst b/devtools/docs/user/debugger/using_the_debugger_map_scopes_feature/index.rst new file mode 100644 index 0000000000..c48d56f0e4 --- /dev/null +++ b/devtools/docs/user/debugger/using_the_debugger_map_scopes_feature/index.rst @@ -0,0 +1,29 @@ +===================================== +Using the Debugger map scopes feature +===================================== + +This feature is useful when debugging source-mapped code. It enables you to see the variables from the original source. It’s also possible to inspect variables from the generated scopes (e.g., a bundle file with all concatenated module files). + +Let's take a look at how this works. If you want to follow along, use `this example <https://firefox-dev.tools/debugger-examples/examples/increment/>`_. + +1. Open the `example page <https://firefox-dev.tools/debugger-examples/examples/increment/>`_ and then open the debugger using **Tools > Web Developer > Debugger** (or press :kbd:`Ctrl` + :kbd:`I` and then select the debugger). + +2. Select the "bundle.js" file in the Sources panel on the left and then set a breakpoint at line 102 in the ``increment`` function. + +3. When you click the **increment** button on the page and hit the breakpoint, an additional section is added to the right-hand panel below the Call stack to display variables mapped from the original scope, like this: + + + .. image:: debugger_map_scope.png + :class: border + +4. As useful as this is, it would be even nicer if you could view the original code (before it was packages into the "bundle.js" file. Right-click on the source code and the context menu now includes an option to **Jump to original location** as shown below. + + .. image:: map_scopes_context_menu.png + :class: border + +5. Click **Jump to original location**. The debugger opens the file "increment.js" so you can view the original code. Notice that your breakpoint is set here in the original code as it was in the corresponding line in the "bundle.js" file. And, since Map has been checked in the Scopes panel, you also see variable symbols from the original code. + + .. image:: map_scopes_original_code.png + :class: border + +Using this feature is expensive in terms of resources, but it certainly makes your life easier when you have to debug source code that has been packaged webpack or a similar tool. diff --git a/devtools/docs/user/debugger/using_the_debugger_map_scopes_feature/map_scopes_context_menu.png b/devtools/docs/user/debugger/using_the_debugger_map_scopes_feature/map_scopes_context_menu.png Binary files differnew file mode 100644 index 0000000000..ee235f7e00 --- /dev/null +++ b/devtools/docs/user/debugger/using_the_debugger_map_scopes_feature/map_scopes_context_menu.png diff --git a/devtools/docs/user/debugger/using_the_debugger_map_scopes_feature/map_scopes_original_code.png b/devtools/docs/user/debugger/using_the_debugger_map_scopes_feature/map_scopes_original_code.png Binary files differnew file mode 100644 index 0000000000..2dc8e558fa --- /dev/null +++ b/devtools/docs/user/debugger/using_the_debugger_map_scopes_feature/map_scopes_original_code.png diff --git a/devtools/docs/user/deprecated_tools/canvas_tool.png b/devtools/docs/user/deprecated_tools/canvas_tool.png Binary files differnew file mode 100644 index 0000000000..8a6f567f3e --- /dev/null +++ b/devtools/docs/user/deprecated_tools/canvas_tool.png diff --git a/devtools/docs/user/deprecated_tools/deprecated_tool_notice.png b/devtools/docs/user/deprecated_tools/deprecated_tool_notice.png Binary files differnew file mode 100644 index 0000000000..b137dc3086 --- /dev/null +++ b/devtools/docs/user/deprecated_tools/deprecated_tool_notice.png diff --git a/devtools/docs/user/deprecated_tools/devtool_settings_deprecated_notice.png b/devtools/docs/user/deprecated_tools/devtool_settings_deprecated_notice.png Binary files differnew file mode 100644 index 0000000000..dfa6dd4a74 --- /dev/null +++ b/devtools/docs/user/deprecated_tools/devtool_settings_deprecated_notice.png diff --git a/devtools/docs/user/deprecated_tools/editor_mode_toggle_icon.png b/devtools/docs/user/deprecated_tools/editor_mode_toggle_icon.png Binary files differnew file mode 100644 index 0000000000..d7be24cf05 --- /dev/null +++ b/devtools/docs/user/deprecated_tools/editor_mode_toggle_icon.png diff --git a/devtools/docs/user/deprecated_tools/index.rst b/devtools/docs/user/deprecated_tools/index.rst new file mode 100644 index 0000000000..72e28b255f --- /dev/null +++ b/devtools/docs/user/deprecated_tools/index.rst @@ -0,0 +1,153 @@ +================ +Deprecated tools +================ + +Over the course of DevTools development, we have added several experimental panels to try out new ideas. Not all of these have had wide adoption, and due to the cost of maintenance, seldom used panels are eventually removed. + +We have created this list of deprecated or removed panels. This page documents the deprecated panels and the bugs that track their removal. Although these panels have been removed, you still have access to the old code, and there are alternative webextensions that you can try to get similar functionality. + +When we deprecate a panel, we begin by getting feedback from the community to determine the impact of removing that panel. Once we have decided to remove the panel, we will provide a warning message, and finally, we will remove the panel from the codebase. + +You may see a warning message, as in the following image, when trying to activate a deprecated panel: + +.. image:: devtool_settings_deprecated_notice.png + :class: border + +In addition, if you open the panel for one of these tools, you will also see a warning message about its removal. + +.. image:: deprecated_tool_notice.png + :class: border + + +Scratchpad +********** + +Scratchpad is deprecated as of Firefox 70 (`bug 1565380 <https://bugzilla.mozilla.org/show_bug.cgi?id=1565380>`_), and will be removed as of Firefox 72 (`bug 1519103 <https://bugzilla.mozilla.org/show_bug.cgi?id=1519103>`_). + + +Description +----------- + +Scratchpad provided an environment for experimenting with JavaScript code. You can write, run, and examine the result of code that interacts with the web page. + +.. image:: screen_shot_2019-08-26_at_08.08.11.png + :alt: Screenshot of the Scratchpad window with a deprecation message + + +Alternatives +------------ + +In Firefox 71+, you can write multi-line JavaScript code in the Web Console Editor Mode, making it similar to the Scratchpad. The Editor Mode can be triggered clicking on the icon on the right of the console input, or with :kbd:`Ctrl` + :kbd:`B` (:kbd:`Cmd` + :kbd:`B` on macOS) + + +.. image:: editor_mode_toggle_icon.png + :alt: Screenshot of the Webconsole in inline mode, with the editor mode icon displayed on the right of the console input + +When in Editor Mode, the :kbd:`Enter` key adds a new line in the input, and you can evaluate the expression using :kbd:`Ctrl` + :kbd:`Enter` (:kbd:`Cmd` + :kbd:`Enter` on macOS). + +.. image:: screen_shot_2019-08-26_at_08.18.26.png + :alt: Screenshot of the Webconsole multiline input, showing an evaluation with a Syntax Error and another, correct one. + +When evaluating, the input isn't cleared, which makes it possible to quickly iterate over a snippet of code. + +The results are displayed in the console output, to the right of the input, providing immediate feedback. Unlike in Scratchpad, errors are properly displayed in the output with an expandable stacktrace, making it easier to debug the code you're currently writing. + +Starting Firefox 72, you can import a Javascript file content in the console input with :kbd:`Ctrl` + :kbd:`O` (:kbd:`Cmd` + :kbd:`O` on macOS), as well as saving the console input content to a file using :kbd:`Ctrl` + :kbd:`S` (:kbd:`Cmd` + :kbd:`S` on macOS). + + +WebIDE and Connect page +*********************** + +WebIDE was deprecated as of Firefox 69 + +Disabled as of Firefox 70 (`bug 1539451 <https://bugzilla.mozilla.org/show_bug.cgi?id=1539451>`_). + +Removed as of Firefox 71 (`bug 1539462 <https://bugzilla.mozilla.org/show_bug.cgi?id=1539462>`_). + + +Description +----------- + +WebIDE allowed you to connect the Firefox Developer Tools to remote browsers, such as Firefox for Android. It was also intended to support application development for Firefox OS. + +.. image:: webide_68.png + + +Alternatives +------------ + +Remote debugging is available in about:debugging as of Firefox 68. Features not ported to about:debugging are: WiFi debugging for Firefox for Android, application development. Features that are planned but not ported yet: remote browser screenshots and edit remote browser configuration. More details on the `mailing-list thread <https://groups.google.com/forum/#!topic/mozilla.dev.developer-tools/aWA7JJ-TpRw>`_. + + +Canvas debugger +*************** + +Bugzilla issue: `bug 1403938 <https://bugzilla.mozilla.org/show_bug.cgi?id=1403938>`_ + +Removed as of Firefox 67 + + +Description +----------- + +Canvas Debugger allowed users to inspect the canvas element and see how frequently a given function is called. It was deprecated due to lack of use. + +We do not have dedicated documentation for the Canvas Debugger. + +.. image:: canvas_tool.png + + +Alternatives +------------ + +`Spector.js <https://addons.mozilla.org/en-US/firefox/addon/spector-js/#&gid=1&pid=2>`_ is a WebExtension that can provide these features with 3D contexts.</span> + + +Web Audio editor +**************** + +Bugzilla issue: `bug 1403944 <https://bugzilla.mozilla.org/show_bug.cgi?id=1403944>`_ + +Removed as of Firefox 67 + + +Description +----------- + +The Web Audio Editor allowed you to examine an audio context constructed in the page and provided a visualization of its graph. This gave a high-level view of its operation, and enabled you to ensure that all the nodes are connected in the way you expect. It was possible to edit the AudioParam properties for each node in the graph. Some non-AudioParam properties, like an OscillatorNode's type property, were displayed and editable as well. It was deprecated due to lack of use. + +More details about the :doc:`Web Audio Editor <../web_audio_editor/index>` + +.. image:: webaudio_tool.png + :class: border + + +Alternatives +------------ + +Alternatives include `AudioN <https://github.com/google/audion>`_ and https://github.com/spite/WebAudioExtension web extensions. + + +Shader editor +************* + +Bugzilla issue: `bug 1342237 <https://bugzilla.mozilla.org/show_bug.cgi?id=1342237>`_ + +Removed as of Firefox 67 + + +Description +----------- + +The Shader Editor allowed users to examine and edit the source of the WebGL vertex and fragment shaders. It was deprecated due to low usage and maintenance costs. + +:doc:`Shader Editor <../shader_editor/index>` + +.. image:: shadereditor_tool.png + :class: border + + +Alternatives +------------ + +An alternative to this panel is this extension: https://github.com/spite/ShaderEditorExtension, or `Spector.js <https://addons.mozilla.org/en-US/firefox/addon/spector-js/#&gid=1&pid=2>`_ also supports a Shader Editor that requires a library to use a shader reloader hook. Currently only the Babylon library does. diff --git a/devtools/docs/user/deprecated_tools/screen_shot_2019-08-26_at_08.08.11.png b/devtools/docs/user/deprecated_tools/screen_shot_2019-08-26_at_08.08.11.png Binary files differnew file mode 100644 index 0000000000..4f9e6d4618 --- /dev/null +++ b/devtools/docs/user/deprecated_tools/screen_shot_2019-08-26_at_08.08.11.png diff --git a/devtools/docs/user/deprecated_tools/screen_shot_2019-08-26_at_08.18.26.png b/devtools/docs/user/deprecated_tools/screen_shot_2019-08-26_at_08.18.26.png Binary files differnew file mode 100644 index 0000000000..ebdce97f2a --- /dev/null +++ b/devtools/docs/user/deprecated_tools/screen_shot_2019-08-26_at_08.18.26.png diff --git a/devtools/docs/user/deprecated_tools/shadereditor_tool.png b/devtools/docs/user/deprecated_tools/shadereditor_tool.png Binary files differnew file mode 100644 index 0000000000..bdc24e2c57 --- /dev/null +++ b/devtools/docs/user/deprecated_tools/shadereditor_tool.png diff --git a/devtools/docs/user/deprecated_tools/webaudio_tool.png b/devtools/docs/user/deprecated_tools/webaudio_tool.png Binary files differnew file mode 100644 index 0000000000..95efede30e --- /dev/null +++ b/devtools/docs/user/deprecated_tools/webaudio_tool.png diff --git a/devtools/docs/user/deprecated_tools/webide_68.png b/devtools/docs/user/deprecated_tools/webide_68.png Binary files differnew file mode 100644 index 0000000000..798b4bffea --- /dev/null +++ b/devtools/docs/user/deprecated_tools/webide_68.png diff --git a/devtools/docs/user/devtools_layoutmenu.png b/devtools/docs/user/devtools_layoutmenu.png Binary files differnew file mode 100644 index 0000000000..bc235c7a15 --- /dev/null +++ b/devtools/docs/user/devtools_layoutmenu.png diff --git a/devtools/docs/user/devtoolsapi/index.rst b/devtools/docs/user/devtoolsapi/index.rst new file mode 100644 index 0000000000..8d86db110d --- /dev/null +++ b/devtools/docs/user/devtoolsapi/index.rst @@ -0,0 +1,796 @@ +============ +DevTools API +============ + +.. warning:: + Deprecated: This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time. + +.. warning:: + The DevTools API is still WIP. If you notice any inconsistency, please let The Firefox Developer Tools Team know. + + +While this api is currently a work-in-progress, there are usable portions of :doc:`page inspector <../page_inspector/index>` and :doc:`debugger <../debugger/index>` that may be used currently. + + +Introduction +************ + +The DevTools API provides a way to register and access developer tools in Firefox. + +In terms of User Interface, each registered tool lives in its own tab (we call one tab a **panel**). These tabs are located in a box we call a **Toolbox**. A toolbox can be *hosted* within a browser tab (at the bottom or on the side), or in its own window (we say that the toolbox is *undocked*). A Toolbox (and all the tools it contains) is linked to a **Target**, which is the object the tools are debugging. A target is usually a web page (a tab), but can be other things (a chrome window, a remote tab,…). + +In terms of code, each tool has to provide a **ToolDefinition** object. A definition is a JS light object that exposes different information about the tool (like its name and its icon), and a *build* method that will be used later-on to start an instance of this tool. The **gDevTools** global object provides methods to register a tool definition and to access tool instances. An instance of a tool is called a **ToolPanel**. The ToolPanel is built only when the tool is selected (not when the toolbox is opened). There is no way to "close/destroy" a ToolPanel. The only way to close a toolPanel is to close its containing toolbox. All these objects implement the **EventEmitter** interface. + + +API +*** + +gDevTools +--------- + +The ``gDevTools`` API can be used to register new tools, themes and handle toolboxes for different tabs and windows. To use the ``gDevTools`` API from an add-on, it can be imported with following snippet + +.. code-block:: + + const { gDevTools } = require("resource:///modules/devtools/gDevTools.jsm"); + + +Methods +~~~~~~~ + +``registerTool(toolDefinition)`` + Registers a new tool and adds a tab to each existing toolbox. + + **Parameters:** + + - ``toolDefinition {ToolDefinition}`` - An object that contains information about the tool. See :ref:`ToolDefinition <devtoolsapi-tool-definition>` for details. + +``unregisterTool(tool)`` + Unregisters the given tool and removes it from all toolboxes. + + **Parameters:** + + - ``tool {ToolDefinition|String}`` - The tool definition object or the id of the tool to unregister. + +``registerTheme(themeDefinition)`` + Registers a new theme. + + **Parameters:** + + - ``themeDefinition {ThemeDefinition}`` - An object that contains information about the theme. + +``unregisterTheme(theme)`` + Unregisters the given theme. + + **Parameters:** + + - ``theme {ThemeDefinition|String}`` - The theme definition object or the theme identifier. + +``showToolbox(commands[, toolId [, hostType [, hostOptions]]])`` + Opens a toolbox for given target either by creating a new one or activating an existing one. + + **Parameters:** + + - ``commands {Object}`` - The commands object designating which debugging context the toolbox will debug. + - ``toolId {String}`` - The tool that should be activated. If unspecified the previously active tool is shown. + - ``hostType {String}`` - The position the toolbox will be placed. One of ``bottom``, ``side``, ``window``, ``custom``. See :ref:`HostType <devtoolsapi-host-type>` for details. + - ``hostOptions {Object}`` - An options object passed to the selected host. See :ref:`HostType <devtoolsapi-host-type>` for details. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is fulfilled with the :ref:`Toolbox <devtoolsapi-toolbox>` instance once it has been initialized and the selected tool is loaded. + +``getToolbox(target)`` + Fetch the :ref:`Toolbox <devtoolsapi-toolbox>` object for the given target. + + **Parameters:** + + - ``target {Target}`` - The target the toolbox is debugging. + + **Return value:** + :ref:`Toolbox <devtoolsapi-toolbox>` object or undefined if there's no toolbox for the given target.. + +``closeToolbox(target)`` + Closes the toolbox for given target. + + **Parameters:** + + - ``target {Target}`` - The target of the toolbox that should be closed. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is fulfilled once the toolbox has been destroyed. + +``getDefaultTools()`` + Returns an `Array <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array>`_ of :ref:`ToolDefinition <devtoolsapi-tool-definition>` objects for the built-in tools. + +``getAdditionalTools()`` + Returns an `Array <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array>`_ of :ref:`ToolDefinition <devtoolsapi-tool-definition>` objects for tools added by addons. + +``getToolDefinition(toolId)`` + Fetch the :ref:`ToolDefinition <devtoolsapi-tool-definition>` object for a tool if it exists and is enabled. + + **Parameters:** + + - ``toolId {String}`` - The ID of the tool. + + **Return value:** + A :ref:`ToolDefinition <devtoolsapi-tool-definition>` if a tool with the given ID exists and is enabled, null otherwise. + +``getToolDefinitionMap()`` + Returns a toolId → :ref:`ToolDefinition <devtoolsapi-tool-definition>` map for tools that are enabled. + +``getToolDefinitionArray()`` + Returns an `Array <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array>`_ of :ref:`ToolDefinition <devtoolsapi-tool-definition>` objects for enabled tools sorted by the order they appear in the toolbox. + +``getThemeDefinition(themeId)`` + Fetch the ``ThemeDefinition`` object for the theme with the given id. + + **Parameters:** + + - ``themeId {String}`` - The ID of the theme. + + **Return value:** + A ``ThemeDefinition`` object if the theme exists, null otherwise. + +``getThemeDefinitionMap()`` + Returns a toolId → ``ThemeDefinition`` map for available themes. + +``getThemeDefinitionArray()`` + Returns an `Array <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array>`_ of ``ThemeDefinition`` objects for available themes. + + +Events +~~~~~~ + +Following events are emitted by the ``gDevTools`` object via the :ref:`EventEmitter <devtoolsapi-event-emitter>` interface. + + +``tool-registered (toolId)`` + A new tool has been registered. + +``tool-unregistered(tool)`` + A tool has been unregistered. The parameter is a :ref:`ToolDefinition <devtoolsapi-tool-definition>` object. + +``theme-registered(themeId)`` + A new theme has been registered. + +``theme-unregistered(theme)`` + A theme has been unregistered. The parameter is a ``ThemeDefinition`` object. + +``toolbox-ready(toolbox)`` + A new toolbox has been created and is ready to use. The parameter is a :ref:`Toolbox <devtoolsapi-toolbox>` object instance. + +``toolbox-destroy(target)`` + The toolbox for the specified target is about to be destroyed. + +``toolbox-destroyed(target)`` + The toolbox for the specified target has been destroyed. + +``{toolId}-init(toolbox, iframe)`` + A tool with the given ID has began to load in the given toolbox to the given frame. + +``{toolId}-build(toolbox, panel)`` + A tool with the given ID has began to initialize in the given toolbox. The panel is the object returned by the ``ToolDefinition.build()`` method. + +``{toolId}-ready(toolbox, panel)`` + A tool with the given ID has finished its initialization and is ready to be used. The panel is the object returned by the ``ToolDefinition.build()`` method. + +``{toolId}-destroy(toolbox, panel)`` + A tool with the given ID is about to be destroyed. The panel is the object returned by the ``ToolDefinition.build()`` method. + + +.. _devtoolsapi-toolbox: + +Toolbox +------- + +A Toolbox is a frame for the :ref:`ToolPanel <devtoolsapi-tool-panel>` that is debugging a specific target. + + +Properties +~~~~~~~~~~ + + +``target`` + **Target**. The Target this toolbox is debugging. + + +``hostType`` + **Toolbox.HostType**. The type of the host this Toolbox is docked to. The value is one of the ``Toolbox.HostType`` constants. + +``zoomValue`` + The current zoom level of the Toolbox. + + +Constants +~~~~~~~~~ + +The Toolbox constructor contains following constant properties. + + +``Toolbox.HostType.BOTTOM`` + Host type for the default toolbox host at the bottom of the browser window. + +``Toolbox.HostType.SIDE`` + Host type for the host at the side of the browser window. + +``Toolbox.HostType.WINDOW`` + Host type for the separate Toolbox window. + +``Toolbox.HostType.CUSTOM`` + Host type for a custom frame host. + + +Methods +~~~~~~~ + +``getCurrentPanel()`` + Get the currently active :ref:`ToolPanel <devtoolsapi-tool-panel>`. + + **Return value:** + The :ref:`ToolPanel <devtoolsapi-tool-panel>` object that was returned from ``ToolPanel.build()``. + +``getPanel(toolId)`` + Get the :ref:`ToolPanel <devtoolsapi-tool-panel>` for given tool. + + **Parameters:** + + - ``toolId {String}`` - The tool identifier. + + **Return value:** + The :ref:`ToolPanel <devtoolsapi-tool-panel>` object if the tool with the given ``toolId`` is active, otherwise ``undefined``. + +``getPanelWhenReady(toolId)`` + Similar to ``getPanel()`` but waits for the tool to load first. If the tool is not already loaded or currently loading the returned `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ won't be fulfilled until something triggers the tool to load. + + **Parameters:** + + - ``toolId {String}`` - The tool identifier. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is fulfilled with the :ref:`ToolPanel <devtoolsapi-tool-panel>` object once the tool has finished loading. + +``getToolPanels()`` + Returns a ``toolId`` → :ref:`ToolPanel <devtoolsapi-tool-panel>` `Map <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map>`_ for currently loaded tools. + +``getNotificationBox()`` + Returns a ``XULElem("notificationbox")`` element for the Toolbox that can be used to display notifications to the user. + +``loadTool(toolId)`` + Loads the tool with the given ``toolId`` in the background but does not activate it. + + **Parameters:** + + - ``toolId {String}`` - The tool identifier. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is fulfilled with the :ref:`ToolPanel <devtoolsapi-tool-panel>` object of the loaded panel once the tool has loaded. + +``selectTool(toolId)`` + Selects the tool with the given ``toolId``. + + **Parameters:** + + - ``toolId {String}`` - The tool identifier. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is fulfilled with the :ref:`ToolPanel <devtoolsapi-tool-panel>` object of the selected panel once the tool has loaded and activated. + +``selectNextTool()`` + Selects the next tool in the ``Toolbox``. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is fulfilled with the :ref:`ToolPanel <devtoolsapi-tool-panel>` object of the selected panel. + +``selectPreviousTool()`` + Selects the previous tool in the ``Toolbox``. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is fulfilled with the :ref:`ToolPanel <devtoolsapi-tool-panel>` object of the selected panel. + +``highlightTool(toolId)`` + Highlights the tab for the given tool. + + **Parameters:** + + - ``toolId {String}`` - The tool to highlight. + +``unhighlightTool(toolId)`` + Unhighlights the tab for the given tool. + + **Parameters:** + + - ``toolId {String}`` - The tool to unhighlight. + +``openSplitConsole()`` + Opens the split Console to the bottom of the toolbox. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is fulfilled once the Console has loaded. + +``closeSplitConsole()`` + Closes the split console. + +``toggleSplitConsole()`` + Toggles the state of the split console. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is fulfilled once the operation has finished. + +``switchHost(hostType)`` + Switches the location of the toolbox + + **Parameters:** + + - ``hostType {Toolbox.HostType}`` - The type of the new host. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is fulfilled once the new host is ready. + +``reloadTarget(force)`` + Reloads the current target of the toolbox. + + **Parameters:** + + - ``force {Boolean} -`` If true the target is shift-reloaded i.e. the cache is bypassed during the reload. + +``zoomIn()`` + Increases the zoom level of the ``Toolbox`` document. + +``zoomOut()`` + Decreases the zoom level of the ``Toolbox`` document. + +``zoomReset()`` + Resets the zoom level of the ``Toolbox`` document. + +``setZoom(value)`` + Set the zoom level to an arbitrary value. + + **Parameters:** + + - ``value {Number}`` - The zoom level such as ``1.2``. + +``destroy()`` + Closes the toolbox. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is resolved once the ``Toolbox`` is destroyed. + + +Events +~~~~~~ + +The Toolbox object emits following events via the :ref:`EventEmitter <devtoolsapi-event-emitter>` interface. + + +``host-changed`` + The Host for this Toolbox has changed. + +``ready`` + The ``Toolbox`` is ready to use. + +``select(toolId)`` + A tool has been selected. This event is emitted before the corresponding ``{toolId}-selected`` event. + +``{toolId}-init(frame)`` + A tool is about to be loaded. The frame is the `iframe <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe>`_ element that has been created for the tool. + +``{toolId}-build(panel)`` + The frame for a tool has loaded and the ``ToolPanel.build()`` method has been called but the asynchronous initialization has not started. The parameter is a :ref:`ToolPanel <devtoolsapi-tool-panel>` object. + +``{toolId}-ready(panel)`` + The asynchronous initialization for a tool has completed and it is ready to be used. The parameter is a :ref:`ToolPanel <devtoolsapi-tool-panel>` object. + +``{toolId}-selected(panel)`` + A tool has been selected. The parameter is a :ref:`ToolPanel <devtoolsapi-tool-panel>` object. + +``{toolId}-destroy(panel)`` + A tool is about to be destroyed. The parameter is a :ref:`ToolPanel <devtoolsapi-tool-panel>` object. + +``destroy`` + The ``Toolbox`` is about to be destroyed. + +``destroyed`` + The ``Toolbox`` has been destroyed. + + +.. _devtoolsapi-tool-definition: + +ToolDefinition +-------------- + +A ``ToolDefinition`` object contains all the required information for a tool to be shown in the toolbox. + + +Methods +~~~~~~~ + +``isToolSupported(toolbox)`` + A method that is called during toolbox construction to check if the tool supports debugging the given target of the given toolbox. + + **Parameters:** + + - ``toolbox {Toolbox}`` - The toolbox where the tool is going to be displayed, if supported. + + **Return value:** + A boolean indicating if the tool supports the given toolbox's target. + +``build(window, toolbox)`` + A method that builds the :ref:`ToolPanel <devtoolsapi-tool-panel>` for this tool. + + **Parameters:** + + - ``window {Window}`` - The `Window <https://developer.mozilla.org/en-US/docs/Web/API/Window>`_ object for frame the tool is being built into. + - ``toolbox {Toolbox}`` - The :ref:`Toolbox <devtoolsapi-toolbox>` the tool is being built for. + + **Return value:** + A :ref:`ToolPanel <devtoolsapi-tool-panel>` for the tool. + + +``onKey(panel, toolbox)`` + **Optional.** A method that is called when the keyboard shortcut for the tool is activated while the tool is the active tool. + + **Parameters:** + + - ``panel {ToolPanel}`` - The :ref:`ToolPanel <devtoolsapi-tool-panel>` for the tool. + - ``toolbox {Toolbox}`` - The toolbox for the shortcut was triggered for. + + **Return value:** + Undefined. + + +Properties +~~~~~~~~~~ + +The ToolDefinition object can contain following properties. Most of them are optional and can be used to customize the presence of the tool in the Browser and the Toolbox. + + +``id`` + **String, required.** An unique identifier for the tool. It must be a valid id for an HTML `Element <https://developer.mozilla.org/en-US/docs/Web/API/Element>`_. + +``url`` + **String, required.** An URL of the panel document. + +``label`` + **String, optional.** The tool's name. If undefined the ``icon`` should be specified. + +``tooltip`` + **String, optional.** The tooltip for the tool's tab. + +``panelLabel`` + **String, optional.** An accessibility label for the panel. + +``ordinal`` + **Integer, optional.** The position of the tool's tab within the toolbox. **Default:** 99 + +``visibilityswitch`` + **String, optional.** A preference name that controls the visibility of the tool. **Default: * ``devtools.{id}.enabled`` + +``icon`` + **String, optional.** An URL for the icon to show in the toolbox tab. If undefined the label should be defined. + +``highlightedicon`` + **String, optional.** An URL for an icon that is to be used when the tool is highlighted (see e.g. paused, inactive debugger). **Default:** ``{icon}`` + +``iconOnly`` + **Boolean, optional.** If true, the label won't be shown in the tool's tab. **Default:** false + +``invertIconForLightTheme`` + **Boolean, optional.** If true the colors of the icon will be inverted for the light theme. **Default:** false + +``key`` + **String, optional.** The key used for keyboard shortcut. Either ``key`` or ``keycode`` value. + +``modifiers`` + **String, optional.** ``modifiers`` for the keyboard shortcut. + +``preventClosingOnKey`` + **Boolean, optional.** If true the tool won't close if its keybinding is pressed while it is active. **Default:** false + +``inMenu`` + **Boolean, optional.** If true the tool will be shown in the Developer Menu. **Default:** false + +``menuLabel`` + **String, optional.** A label for the Developer Menu item. **Default:** ``{label}`` + +``accesskey`` + **String, optional.** ``accesskey`` for the Developer Menu ``xul:menuitem``. + + +Example +~~~~~~~ + +Here's a minimal definition for a tool. + +.. code-block:: javascript + + let def = { + id: "my-tool", + label: "My Tool", + icon: "chrome://browser/skin/devtools/tool-webconsole.svg", + url: "about:blank", + isToolSupported: toolbox => true, + build: (window, toolbox) => new MyToolPanel(window, toolbox) + }; + + // Register it. + gDevTools.registerTool(def); + + +.. _devtoolsapi-target-type: + +TargetType +---------- + +FIXME: + + +.. _devtoolsapi-host-type: + +HostType +-------- + +FIXME + + +.. _devtoolsapi-tool-panel: + +ToolPanel +--------- + +The ToolPanel is an interface the toolbox uses to manage the panel of a tool. The object that ``ToolDefinition.build()`` returns should implement the methods described below. + +Methods +~~~~~~~ + + +``open()`` + **Optional**. A method that can be used to perform asynchronous initialization. If the method returns a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_, many operations (e.g. ``gDevTools.showToolbox()`` or ``toolbox.selectTool()``) and events (e.g. ``toolbox-ready`` are delayed until the promise has been fulfilled. + + **Return value:** + The method should return a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is resolved with the ``ToolPanel`` object once it's ready to be used. + +``destroy()`` + A method that is called when the toolbox is closed or the tool is unregistered. If the tool needs to perform asynchronous operations during destruction the method should return a `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is resolved once the process is complete. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ if the function performs asynchronous operations, otherwise ``undefined``. + + +Example +~~~~~~~ + +Here's a basic template for a ToolPanel implementation. + +.. code-block:: javascript + + // In the ToolDefinition object, do + // build: (window, target) => new MyPanel(window, target), + + function MyPanel(window, target) { + // The window object that has loaded the URL defined in the ToolDefinition + this.window = window; + // The Target this toolbox is debugging. + this.target = target; + + // Do synchronous initialization here. + window.document.body.addEventListener("click", this.handleClick); + } + + MyPanel.prototype = { + open: function() { + // Any asynchronous operations should be done here. + return this.doSomethingAsynchronous() + .then(() => this); + }, + + destroy: function() { + // Synchronous destruction. + this.window.document.body.removeEventListener("click", this.handleClick); + + // Async destruction. + return this.destroySomethingAsynchronously() + .then(() => console.log("destroyed")); + }, + + handleClick: function(event) { + console.log("Clicked", event.originalTarget); + }, + }; + + +.. _devtoolsapi-event-emitter: + +EventEmitter +------------ + +``EventEmitter`` is an interface many Developer Tool classes and objects implement and use to notify others about changes in their internal state. + +When an event is emitted on the ``EventEmitter``, the listeners will be called with the event name as the first argument and the extra arguments are spread as the remaining parameters. + +.. note:: + Some components use Add-on SDK event module instead of the DevTools EventEmitter. Unfortunately, their API's are a bit different and it's not always evident which one a certain component is using. The main differences between the two modules are that the first parameter for Add-on SDK events is the first payload argument instead of the event name and the ``once`` method does not return a Promise. The work for unifying the event paradigms is ongoing in `bug 952653 <https://bugzilla.mozilla.org/show_bug.cgi?id=952653>`_. + + +Methods +~~~~~~~ + +The following methods are available on objects that have been decorated with the ``EventEmitter`` interface. + +``emit(eventName, ...extraArguments)`` + Emits an event with the given name to this object. + + **Parameters:** + + - ``eventName {String}`` - The name of the event. + - ``extraArguments {...Any}`` - Extra arguments that are passed to the listeners. + +``on(eventName, listener)`` + Adds a listener for the given event. + +``off(eventName, listener)`` + Removes the previously added listener from the event. + +``once(eventName, listener)`` + Adds a listener for the event that is removed after it has been emitted once. + + **Return value:** + A `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ that is fulfilled with the first extra argument for the event when then event is emitted. If the event contains multiple payload arguments, the rest are discarded and can only be received by providing the listener function to this method. + + +Examples +~~~~~~~~ + +Here's a few examples using the ``gDevTools`` object. + +.. code-block:: javascript + + let onInit = (eventName, toolbox, netmonitor) => console.log("Netmonitor initialized!"); + + // Attach a listener. + gDevTools.on("netmonitor-init", onInit); + + // Remove a listener. + gDevTools.off("netmonitor-init", onInit); + + // Attach a one time listener. + gDevTools.once("netmonitor-init", (eventName, toolbox, netmonitor) => { + console.log("Network Monitor initialized once!", toolbox, netmonitor); + }); + + // Use the Promise returned by the once method. + gDevTools.once("netmonitor-init").then(toolbox => { + // Note that the second argument is not available here. + console.log("Network Monitor initialized to toolbox", toolbox); + }); + + +ToolSidebar +----------- + +To build a sidebar in your tool, first, add a xul:tabbox where you want the sidebar to live: + +.. code-block:: xml + + <splitter class="devtools-side-splitter"/> + <tabbox id="mytool-sidebar" class="devtools-sidebar-tabs" hidden="true"> + <tabs/> + <tabpanels flex="1"/> + </tabbox> + +A sidebar is composed of tabs. Each tab will hold an iframe. For example, in the Inspector, there are 3 tabs (Computed View, Rule View, Layout View). The user can select the tab they want to see. + +If the availability of the tabs depends on some tool-related conditions, we might want to not let the user select a tab. This API provides methods to hide the tabstripe. For example, in the Web Console, there are 2 views (Network View and Object View). These views are only available in certain conditions controlled by the WebConsole code. So it's up the WebConsole the hide and show the sidebar, and select the correct tab. + +If the loaded document exposes a ``window.setPanel(ToolPanel)`` function, the sidebar will call it once the document is loaded. + +.. list-table:: Methods + :widths: 70 30 + :header-rows: 1 + + * - Method + - Description + + * - ``new ToolSidebar(xul:tabbox, ToolPanel, uid, showTabstripe=true)`` + - ToolSidebar constructor + + * - ``void addTab(tabId, url, selected=false)`` + - Add a tab in the sidebar + + * - ``void select(tabId)`` + - Select a tab + + * - ``void hide()`` + - Hide the sidebar + + * - ``void show()`` + - Show the sidebar + + * - ``void toggle()`` + - Toggle the sidebar + + * - ``void getWindowForTab(tabId)`` + - Get the iframe containing the tab content + + * - ``tabId getCurrentTabID()`` + - Return the id of tabId of the current tab + + * - ``tabbox getTab(tabId)`` + - Return a tab given its id + + * - ``destroy()`` + - Destroy the ToolSidebar object + +.. list-table:: Events + :widths: 70 30 + :header-rows: 1 + + * - Events + - Description + + * - ``new-tab-registered`` + - A new tab has been added + + * - ``{tabId}-ready`` + - Tab is loaded and can be used + + * - ``{tabId}-selected`` + - Tab has been selected and is visible + + * - ``{tabId}-unselected`` + - Tab has been unselected and is not visible + + * - ``show`` + - The sidebar has been opened. + + * - ``hide`` + - The sidebar has been closed. + + +Examples +-------- + +Register a tool + +.. code-block:: javascript + + gDevTools.registerTool({ + // FIXME: missing key related properties. + id: "inspector", + icon: "chrome://browser/skin/devtools/inspector-icon.png", + url: "chrome://browser/content/devtools/inspector/inspector.xul", + get label() { + let strings = Services.strings.createBundle("chrome://browser/locale/devtools/inspector.properties"); + return strings.GetStringFromName("inspector.label"); + }, + + isToolSupported: function(toolbox) { + return toolbox.target.isLocalTab; + }, + + build: function(iframeWindow, toolbox, node) { + return new InspectorPanel(iframeWindow, toolbox, node); + } + }); + + +Open a tool, or select it if the toolbox is already open: + +.. code-block:: javascript + + let target = TargetFactory.forTab(gBrowser.selectedTab); + let toolbox = gDevTools.openToolbox(target, null, "inspector"); + + toolbox.once("inspector-ready", function(event, panel) { + let inspector = toolbox.getToolPanels().get("inspector"); + inspector.selection.setNode(target, "browser-context-menu"); + }); + + +Add a sidebar to an existing tool: + +.. code-block:: javascript + + let sidebar = new ToolSidebar(xulTabbox, toolPanel, "toolId"); + sidebar.addTab("tab1", "chrome://browser/content/.../tab1.xhtml", true); + sidebar.addTab("tab2", "chrome://browser/content/.../tab2.xhtml", false); + sidebar.show(); diff --git a/devtools/docs/user/devtoolscolors/index.rst b/devtools/docs/user/devtoolscolors/index.rst new file mode 100644 index 0000000000..68a54a1d62 --- /dev/null +++ b/devtools/docs/user/devtoolscolors/index.rst @@ -0,0 +1,207 @@ +============== +DevToolsColors +============== + +.. warning:: + **Firefox developers**: Don't change any of these values without UX approval. If any of these values need to be changed, you will need to change some CSS code in ``/browser/themes/*/devtools/``. File a DevTools bug accordingly. + + +This chart lists colors and CSS variables as implemented in the dark theme and light theme for developer tools. + +.. |br| raw:: html + + <br/><br/> + +.. list-table:: + :widths: 25 22 22 31 + :header-rows: 1 + + * - + - Dark Theme + - Light Theme + - CSS Variables + + * - **Chrome Colors** + - + - + - + + * - **Tab Toolbar** + - #252c33 |br| + rgba(37, 44, 51, 1) + - #ebeced |br| + rgba(235, 236, 237, 1) + - ``--theme-tab-toolbar-background`` + + * - **Toolbars** + - #343c45 |br| + rgba(52, 60, 69, 1) + - #f0f1f2 |br| + rgba(240, 241, 242, 1) + - ``--theme-toolbar-background`` + + * - **Selection Background** + - #1d4f73 |br| + rgba(29, 79, 115, 1) + - #4c9ed9 |br| + rgba(76, 158, 217, 1) + - ``--theme-selection-background`` + + * - **Selection Text Color** + - #f5f7fa |br| + rgba(245, 247, 250, 1) + - #f5f7fa |br| + rgba(245, 247, 250, 1) + - ``--theme-selection-color`` + + * - **Splitters** + - #000000 |br| + rgba(0, 0, 0, 1) + - #aaaaaa |br| + rgba(170, 170, 170, 1) + - ``--theme-splitter-color`` + + * - **Comment** + - #5c6773 |br| + rgba(92, 103, 115, 1) + - #747573 |br| + rgba(116, 117, 115, 1) + - ``--theme-comment`` + + * - **Content Colors** + - + - + - + + * - **Background - Body** + - #14171a |br| + rgba(17, 19, 21, 1) + - #fcfcfc |br| + rgba(252, 252, 252, 1) + - ``--theme-body-background`` + + * - **Background - Sidebar** + - #181d20 |br| + rgba(24, 29, 32, 1) + - #f7f7f7 |br| + rgba(247, 247, 247, 1) + - ``--theme-sidebar-background`` + + * - **Background - Attention** + - #b28025 |br| + rgba(178, 128, 37, 1) + - #e6b064 |br| + rgba(230, 176, 100, 1) + - ``--theme-contrast-background`` + + * - **Text Colors** + - + - + - + + * - **Body Text** + - #8fa1b2 |br| + rgba(143, 161, 178, 1) + - #18191a |br| + rgba(24, 25, 26, 1) + - ``--theme-body-color`` + + * - **Foreground (Text) - Grey** + - #b6babf |br| + rgba(182, 186, 191, 1) + - #585959 |br| + rgba(88, 89, 89, 1) + - ``--theme-body-color-alt`` + + * - **Content (Text) - High Contrast** + - #a9bacb |br| + rgba(169, 186, 203, 1) + - #292e33 |br| + rgba(41, 46, 51, 1) + - ``--theme-content-color1`` + + * - **Content (Text) - Grey** + - #8fa1b2 |br| + rgba(143, 161, 178, 1) + - #8fa1b2 |br| + rgba(143, 161, 178, 1) + - ``--theme-content-color2`` + + * - **Content (Text) - Dark Grey** + - #667380 |br| + rgba(102, 115, 128, 1) + - #667380 |br| + rgba(102, 115, 128, 1) + - ``--theme-content-color3`` + + * - **Highlight Colors** + - + - + - + + * - **Blue** + - #46afe3 |br| + rgba(70, 175, 227, 1) + - #0088cc |br| + rgba(0, 136, 204, 1) + - ``--theme-highlight-blue`` + + * - **Purple** + - #6b7abb |br| + rgba(107, 122, 187, 1) + - #5b5fff |br| + rgba(91, 95, 255, 1) + - ``--theme-highlight-purple`` + + * - **Pink** + - #df80ff |br| + rgba(223, 128, 255, 1) + - #b82ee5 |br| + rgba(184, 46, 229, 1) + - ``--theme-highlight-pink`` + + * - **Red** + - #eb5368 |br| + rgba(235, 83, 104, 1) + - #ed2655 |br| + rgba(237, 38, 85, 1) + - ``--theme-highlight-red`` + + * - **Orange** + - #d96629 |br| + rgba(217, 102, 41, 1) + - #f13c00 |br| + rgba(241, 60, 0, 1) + - ``--theme-highlight-orange`` + + * - **Light Orange** + - #d99b28 |br| + rgba(217, 155, 40, 1) + - #d97e00 |br| + rgba(217, 126, 0, 1) + - ``--theme-highlight-lightorange`` + + * - **Green** + - #70bf53 |br| + rgba(112, 191, 83, 1) + - #2cbb0f |br| + rgba(44, 187, 15, 1) + - ``--theme-highlight-green`` + + * - **Blue-Grey** + - #5e88b0 |br| + rgba(94, 136, 176, 1) + - #0072ab |br| + rgba(0, 114, 171, 1) + - ``--theme-highlight-bluegrey`` + + * - **Yellow** + - #ffffb4 |br| + rgba(255, 255, 180, 1) + - #ffffb4 |br| + rgba(255, 255, 180, 1) + - ``--theme-highlight-yellow`` + + +.. warning:: + Not yet finalized. See `bug 916766 <https://bugzilla.mozilla.org/show_bug.cgi?id=916766>`_ for progress. diff --git a/devtools/docs/user/dom_property_viewer/dom_inspector.png b/devtools/docs/user/dom_property_viewer/dom_inspector.png Binary files differnew file mode 100644 index 0000000000..013af4b1c8 --- /dev/null +++ b/devtools/docs/user/dom_property_viewer/dom_inspector.png diff --git a/devtools/docs/user/dom_property_viewer/dom_inspector_refresh_button.png b/devtools/docs/user/dom_property_viewer/dom_inspector_refresh_button.png Binary files differnew file mode 100644 index 0000000000..ed93b9ebdb --- /dev/null +++ b/devtools/docs/user/dom_property_viewer/dom_inspector_refresh_button.png diff --git a/devtools/docs/user/dom_property_viewer/dom_inspector_search_box.png b/devtools/docs/user/dom_property_viewer/dom_inspector_search_box.png Binary files differnew file mode 100644 index 0000000000..f5f25c975e --- /dev/null +++ b/devtools/docs/user/dom_property_viewer/dom_inspector_search_box.png diff --git a/devtools/docs/user/dom_property_viewer/index.rst b/devtools/docs/user/dom_property_viewer/index.rst new file mode 100644 index 0000000000..c5893a82fa --- /dev/null +++ b/devtools/docs/user/dom_property_viewer/index.rst @@ -0,0 +1,53 @@ +=================== +DOM Property Viewer +=================== + +.. container:: block_quote + + The DOM Property Viewer is new in Firefox 48. It is disabled by default. Enable it in the :ref:`Developer Tools Settings <tool-toolbox-settings>` + +The DOM Property Viewer lets you inspect the properties of the `DOM <https://developer.mozilla.org/en-US/docs/Glossary/DOM>`_ as an expandable tree structure, starting from the `window <https://developer.mozilla.org/en-US/docs/Web/API/Window>`_ object of the current page or the :doc:`selected iframe <../working_with_iframes/index>`. + +.. image:: dom_inspector.png + :class: center + + +Enabling the DOM Property Viewer +******************************** + +The DOM Property Viewer is not enabled by default. To enable it, open the :ref:`developer tools settings <tool-toolbox-settings>` and check the "DOM" box under "Default Firefox Developer Tools". + + +Opening the DOM Property Viewer +******************************* + +Once enabled, you can select the *DOM* panel in the Web Developer Tools, accessible from the Browser Tools submenu + +The :doc:`Toolbox <../tools_toolbox/index>` will appear at the bottom of the browser window, with the DOM Property Viewer activated. It's just called "DOM" in the Toolbox. + +DOM Property Viewer user interface +********************************** + +DOM tree +-------- + +The different properties of the DOM are displayed as an expandable tree. The left-hand side shows the property's name, and the right-hand side shows its value. Up to three properties of an object and items of an array are displayed. If a property has more elements than this, you'll see a "more..." annotation, and will need to click the property to see all elements. A lock icon indicates that a property is not writable. + +Refreshing the display +---------------------- + +If the DOM changes you can hit the *Refresh* button to update the display: + +.. image:: dom_inspector_refresh_button.png + :alt: Button to update the DOM Inspector display + :class: center + +Filtering +--------- + +There is a search box within the toolbar: + +.. image:: dom_inspector_search_box.png + :class: center + +This filters the list to show only items which match the search term. Items match the search term if their name contains the search term. Matching is case-sensitive. diff --git a/devtools/docs/user/eyedropper/eyedropper.png b/devtools/docs/user/eyedropper/eyedropper.png Binary files differnew file mode 100644 index 0000000000..cad092141b --- /dev/null +++ b/devtools/docs/user/eyedropper/eyedropper.png diff --git a/devtools/docs/user/eyedropper/index.rst b/devtools/docs/user/eyedropper/index.rst new file mode 100644 index 0000000000..672c81ad23 --- /dev/null +++ b/devtools/docs/user/eyedropper/index.rst @@ -0,0 +1,47 @@ +========== +Eyedropper +========== + +The Eyedropper tool enables you to select colors in the current page. It works like a magnifying glass over the page, enabling you to select with pixel precision. Underneath the magnifying glass it shows the color value for the current pixel using whichever scheme you've selected in :ref:`Settings > Inspector <settings-inspector>` > Default color unit: + +.. image:: eyedropper.png + +You can use it in one of two ways: + +- to select a color from the page and copy it to the clipboard +- to change a color value in the Inspector's Rules view to a color you've selected from the page + +Copying a color to the clipboard +******************************** + +Open the Eyedropper in one of these two ways: + +- select "Eyedropper" under the "Browser Tools" menu +- open the :doc:`Page Inspector <../page_inspector/index>` tab and click the eyedropper button in its toolbar + +As you move the mouse around the page you'll see the current color value in the Eyedropper change. Clicking copies the current color value to the clipboard. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/xf2uk6UyRB8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +Changing a color value in the Rules view +**************************************** + +Color values appearing in the Inspector's Rules view have color samples next to them: clicking the sample shows a :doc:`color picker popup <../page_inspector/how_to/inspect_and_select_colors/index>`. From Firefox 31, the popup contains an eyedropper icon: click this icon to activate the Eyedropper. + +Now, when you click the Eyedropper, the color in the Rules view is set to the color you selected. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/0Zx1TN21QOo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +Keyboard shortcuts +****************** + +See :ref:`All keyboard shortcuts > Eyedropper <keyboard-shortcuts-eyedropper>`. diff --git a/devtools/docs/user/iframe_button.png b/devtools/docs/user/iframe_button.png Binary files differnew file mode 100644 index 0000000000..43f497e136 --- /dev/null +++ b/devtools/docs/user/iframe_button.png diff --git a/devtools/docs/user/index.rst b/devtools/docs/user/index.rst new file mode 100644 index 0000000000..86d700f682 --- /dev/null +++ b/devtools/docs/user/index.rst @@ -0,0 +1,267 @@ +.. toctree:: + :name: devtools-user-doc + +========================== +Firefox DevTools User Docs +========================== + +Firefox Developer Tools is a set of web developer tools built into Firefox. You can use them to examine, edit, and debug HTML, CSS, and JavaScript. + +This section contains detailed guides to all of the tools as well as information on how to debug Firefox for Android, how to extend DevTools, and how to debug the browser as a whole. + +If you have any feedback on DevTools or want to contribute to the project, you can `join the DevTools community <https://firefox-dev.tools/>`_. + +.. note:: + + If you are just getting started with web development and using developer tools, our `learning <https://developer.mozilla.org/en-US/docs/Learn>`_ docs will help you — see `Getting started with the Web <https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web>`_ and `What are browser developer tools? <https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools>`_ for good starting points. + + +The Core Tools +************** + +You can open the Firefox Developer Tools from the menu by selecting **Tools > Web Developer > Web Developer Tools** or use the keyboard shortcut :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`I` or :kbd:`F12` on Windows and Linux, or :kbd:`Cmd` + :kbd:`Opt` + :kbd:`I` on macOS. + +The ellipsis menu on the right-hand side of Developer Tools contains several commands that let you perform actions or change tool settings. + +.. figure:: devtools_layoutmenu.png + :align: center + + +========== ================================================================ + |image1| This button only appears when there are multiple iframes on a + page. Click it to display a list of the iframes on the current + page and select the one with which you want to work. + + |image2| Click this button to take a screenshot of the current page. + (*Note:* This feature is not turned on by + default and must be enabled in settings before the icon will + appear.) + + |image3| Toggles Responsive Design Mode + + |image4| Opens the menu that includes docking options, the ability to show + or hide the split console, and Developer Tools settings. + The menu also includes links to the documentation for Firefox + Web Tools and the Mozilla Community. + + |image5| Closes the Developer Tools + +========== ================================================================ + +.. |image1| image:: iframe_button.png + :class: center +.. |image2| image:: camera_button.png + :class: center +.. |image3| image:: responsive_button.png + :class: center +.. |image4| image:: menu_button.png + :class: center +.. |image5| image:: close_button.png + :class: center + + +Page Inspector +-------------- + +.. image:: landingpage_pageinspector.png + :class: border + :target: page_inspector + :alt: The all-new Inspector panel in Firefox 57. + +View and edit page content and layout. Visualize many aspects of the page including the box model, animations, and grid layouts + + +Web Console +----------- + +.. image:: landingpage_console.png + :class: border + :target: web_console + :alt: The all-new Console panel in Firefox 57. + +See messages logged by a web page and interact with the page using JavaScript. + + +JavaScript Debugger +------------------- + +.. image:: landingpage_debugger.png + :class: border + :target: debugger + :alt: The all-new Debugger panel in Firefox 57. + +Stop, step through, and examine the JavaScript running on a page. + + +Network Monitor +--------------- + +.. image:: landingpage_network.png + :class: border + :target: network_monitor + :alt: The Network panel in Firefox 57 DevTools. + + +See the network requests made when a page is loaded. + + +Performance Panel +----------------- + +.. image:: landingpage_performance_2022.png + :class: border + :target: https://profiler.firefox.com/docs/ + :alt: Performance Panel in Firefox 103 Developer Tools. + +Analyze your site's general responsiveness, JavaScript, and layout performance. + + +Responsive Design Mode +---------------------- + +.. image:: landingpage_responsivedesign.png + :class: border + :target: responsive_design_mode + :alt: Responsive Design mode in Firefox 57 Developer Tools. + +See how your website or app will look and behave on different devices and network types. + + +Accessibility inspector +----------------------- + +.. image:: landingpage_accessibility.png + :class: border + :target: accessibility_inspector + :alt: Performance Tools in Firefox 57 Developer Tools. + +Provides a means to access the page's accessibility tree, allowing you to check what's missing or otherwise needs attention. + + +Application panel +----------------- + +.. image:: just-application-panel.png + :class: border + :target: application + :alt: Performance Tools in Firefox 57 Developer Tools. + +Provides tools for inspecting and debugging modern web apps (also known as `Progressive Web Apps <https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps>`_). This includes inspection of `service workers <https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API>`_ and `web app manifests <https://developer.mozilla.org/en-US/docs/Web/Manifest>`_ + + +.. note:: + + The collective term for the UI inside which the DevTools all live is the :doc:`Toolbox <tools_toolbox/index>` + + +More Tools +********** + +These developer tools are also built into Firefox. Unlike the "Core Tools" above, you might not use them every day. + +.. list-table:: + :widths: 25 75 + :header-rows: 0 + + * - :doc:`Memory <memory/index>` + - Figure out which objects are keeping memory in use. + + * - :doc:`Storage Inspector <storage_inspector/index>` + - Inspect cookies, local storage, indexedDB, and session storage present in a page. + + * - :doc:`DOM Property Viewer <dom_property_viewer/index>` + - Inspect the page's DOM properties, functions, etc. + + * - :doc:`Eyedropper <eyedropper/index>` + - Select a color from the page. + + * - :doc:`Style Editor <style_editor/index>` + - View and edit CSS styles for the current page. + + * - :doc:`Taking screenshot <taking_screenshots/index>` + - Take a screenshot of the entire page or of a single element. + + * - :doc:`Measure a portion of the page <measure_a_portion_of_the_page/index>` + - Measure a specific area of a web page. + + * - :doc:`Rulers <rulers/index>` + - Overlay horizontal and vertical rulers on a web page + + +.. image:: logo-developer-quantum.png + :class: center + +.. rst-class:: center + + For the latest developer tools and features, try Firefox Developer Edition. + + `Download Firefox Developer Edition <https://www.mozilla.org/en-US/firefox/developer/>`_ + + + +Connecting the Developer Tools +****************************** + +If you open the developer tools using :ref:`keyboard shortcuts <keyboard-shortcuts-opening-and-closing-tools>` or the equivalent menu items, they'll target the document hosted by the currently active tab. But you can attach the tools to a variety of other targets, too, both within the current browser and in different browsers or even different devices. + +.. list-table:: + :widths: 30 70 + :header-rows: 0 + + * - :doc:`about:debugging <about_colon_debugging/index>` + - Debug add-ons, content tabs, and workers running in the browser. + + * - :ref:`Connecting to Firefox for Android <about-colon-debugging-connecting-to-a-remote-device>` + - Connect the developer tools to an instance of Firefox running on an Android device. + + * - :doc:`Connecting to iframes <working_with_iframes/index>` + - Connect the developer tools to a specific iframe in the current page. + + +Debugging the browser +********************* + +By default, the developer tools are attached to a web page or web app. But you can also connect them to the browser as a whole. This is useful for browser and add-on development. + +.. list-table:: + :widths: 30 70 + :header-rows: 0 + + * - :doc:`Browser Console <browser_console/index>` + - See messages logged by the browser itself and by add-ons, and run JavaScript code in the browser's scope. + + * - :doc:`Browser Toolbox <browser_toolbox/index>` + - Attach the Developer Tools to the browser itself. + + + +Extending DevTools +****************** + +For information on extending the Firefox DevTools, see `Extending the developer tools <https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Extending_the_developer_tools>`_ over in the `Browser Extensions <https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions>`_ section of MDN. + + +Migrating from Firebug +********************** + +Firebug has come to the end of its lifespan (see `Firebug lives on in Firefox DevTools <https://hacks.mozilla.org/2016/12/firebug-lives-on-in-firefox-devtools/>`_ for details of why), and we appreciate that some people will find migrating to another less familiar set of DevTools to be challenging. To ease a transition from Firebug to the Firefox developer tools, we have written a handy guide — :doc:`Migrating from Firebug <./migrating_from_firebug/index>` + + +Contribute +********** + +If you want to help improve the developer tools, these resources will get you started. + + +.. list-table:: + :widths: 30 70 + :header-rows: 0 + + * - `Get Involved <https://firefox-dev.tools/>`_ + - Our community website explains how to get involved. + + * - `bugs.firefox-dev.tools <https://bugs.firefox-dev.tools/>`_ + - A tool helping to find bugs to work on. + + * - :ref:`Read source docs <devtools-contributor-doc>` + - Firefox DevTools source code documentation. diff --git a/devtools/docs/user/index/index.rst b/devtools/docs/user/index/index.rst new file mode 100644 index 0000000000..1e0d540b8d --- /dev/null +++ b/devtools/docs/user/index/index.rst @@ -0,0 +1,7 @@ +===== +Index +===== + +.. note:: + + Draft: TODO: generate index of all pages. diff --git a/devtools/docs/user/json_viewer/index.rst b/devtools/docs/user/json_viewer/index.rst new file mode 100644 index 0000000000..f3e313dea1 --- /dev/null +++ b/devtools/docs/user/json_viewer/index.rst @@ -0,0 +1,18 @@ +=========== +JSON viewer +=========== + +The JSON viewer is new in Firefox 44. + +Before Firefox 53, the JSON viewer is enabled by default only in Firefox Developer Edition and Firefox Nightly. To enable this feature in other release channels, set the ```devtools.jsonview.enabled``` preference to ```true```. + +From Firefox 53 onwards, the JSON viewer is also enabled by default in Beta and the normal release version of Firefox. + + +Firefox includes a JSON viewer. If you open a JSON file in the browser, or view a remote URL with the Content-Type set to application/json, it is parsed and given syntax highlighting. Arrays and objects are shown collapsed, and you can expand them using the "+" icons. + +The JSON viewer provides a search box that you can use to filter the JSON. + +You can also view the raw JSON and pretty-print it. + +Finally, if the document was the result of a network request, the viewer displays the request and response headers. diff --git a/devtools/docs/user/just-application-panel.png b/devtools/docs/user/just-application-panel.png Binary files differnew file mode 100644 index 0000000000..12fadfb81f --- /dev/null +++ b/devtools/docs/user/just-application-panel.png diff --git a/devtools/docs/user/keyboard_shortcuts/index.rst b/devtools/docs/user/keyboard_shortcuts/index.rst new file mode 100644 index 0000000000..490021e533 --- /dev/null +++ b/devtools/docs/user/keyboard_shortcuts/index.rst @@ -0,0 +1,918 @@ +====================== +All keyboard shortcuts +====================== + +This page lists all keyboard shortcuts used by the developer tools built into Firefox. + +The first section lists the shortcut for opening each tool and the second section lists shortcuts that are applicable to the Toolbox itself. After that there's one section for each tool, which lists the shortcuts that you can use within that tool. + +Because access keys are locale-dependent, they're not documented in this page. + + +.. |br| raw:: html + + <br/> + + +.. _keyboard-shortcuts-opening-and-closing-tools: + +Opening and closing tools +************************* + +These shortcuts work in the main browser window to open the specified tool. The same shortcuts will work to close tools hosted in the Toolbox, if the tool is active. For tools like the Browser Console that open in a new window, you have to close the window to close the tool. + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Open Toolbox (with the most recent tool activated) + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`I` + - :kbd:`Cmd` + :kbd:`Opt` + :kbd:`I` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`I` + + * - Bring Toolbox to foreground (if the Toolbox is in a separate window and not in oreground) + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`I` or :kbd:`F12` + - :kbd:`Cmd` + :kbd:`Opt` + :kbd:`I` or :kbd:`F12` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`I` or :kbd:`F12` + + * - Close Toolbox (if the Toolbox is in a separate window and in foreground) + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`I` or :kbd:`F12` + - :kbd:`Cmd` + :kbd:`Opt` + :kbd:`I` or :kbd:`F12` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`I` or :kbd:`F12` + + * - Open Web Console [#]_ + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`K` + - :kbd:`Cmd` + :kbd:`Opt` + :kbd:`K` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`K` + + * - Toggle "Pick an element from the page" (opens the Toolbox and/or focus the Inspector ab) + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`C` + - :kbd:`Cmd` + :kbd:`Opt` + :kbd:`C` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`C` + + * - Open Style Editor + - :kbd:`Shift` + :kbd:`F7` + - :kbd:`Shift` + :kbd:`F7` + - :kbd:`Shift` + :kbd:`F7` + + * - Open Profiler + - :kbd:`Shift` + :kbd:`F5` + - :kbd:`Shift` + :kbd:`F5` + - :kbd:`Shift` + :kbd:`F5` + + * - Open Network Monitor [#]_ + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`E` + - :kbd:`Cmd` + :kbd:`Opt` + :kbd:`E` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`E` + + * - Toggle Responsive Design Mode + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`M` + - :kbd:`Cmd` + :kbd:`Opt` + :kbd:`M` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`M` + + * - Open Browser Console + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`J` + - :kbd:`Cmd` + :kbd:`Opt` + :kbd:`J` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`J` + + * - Open Browser Toolbox + - :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`Shift` + :kbd:`I` + - :kbd:`Cmd` + :kbd:`Opt` + :kbd:`Shift` + :kbd:`I` + - :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`Shift` + :kbd:`I` + + * - Storage Inspector + - :kbd:`Shift` + :kbd:`F9` + - :kbd:`Shift` + :kbd:`F9` + - :kbd:`Shift` + :kbd:`F9` + + * - Open Debugger [#]_ + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`Z` + - :kbd:`Cmd` + :kbd:`Opt` + :kbd:`Z` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`Z` + + +.. [#] Unlike the other toolbox-hosted tools, this shortcut does not also close the Web Console. Instead, it focuses on the Web Console's command line. To close the Web Console, use the global toolbox shortcut of :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`I` (:kbd:`Cmd` + :kbd:`Opt` + :kbd:`I` on a Mac). + +.. [#] Before Firefox 55, the keyboard shortcut was :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`Q` (:kbd:`Cmd` + :kbd:`Opt` + :kbd:`Q` on a Mac) + +.. [#] Starting in Firefox 71. Before Firefox 66, the letter in this shortcut was :kbd:`S`. + + +.. _keyboard-shortcuts-toolbox: + +Toolbox +******* + +Keyboard shortcuts for the :doc:`Toolbox <../tools_toolbox/index>` + +These shortcuts work whenever the toolbox is open, no matter which tool is active. + + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Cycle through tools left to right + - :kbd:`Ctrl` + :kbd:`]` + - :kbd:`Cmd` + :kbd:`]` + - :kbd:`Ctrl` + :kbd:`]` + + * - Cycle through tools right to left + - :kbd:`Ctrl` + :kbd:`[` + - :kbd:`Cmd` + :kbd:`[` + - :kbd:`Ctrl` + :kbd:`[` + + * - Toggle between active tool and settings. + - :kbd:`F1` + - :kbd:`F1` + - :kbd:`F1` + + * - Toggle toolbox between the last 2 :ref:`docking modes <tools-toolbox-docking-mode>` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`D` + - :kbd:`Cmd` + :kbd:`Shift` + :kbd:`D` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`D` + + * - Toggle split console (except if console is the currently selected tool) + - :kbd:`Esc` + - :kbd:`Esc` + - :kbd:`Esc` + + +These shortcuts work in all tools that are hosted in the toolbox. + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Increase font size + - :kbd:`Ctrl` + :kbd:`+` + - :kbd:`Cmd` + :kbd:`+` + - :kbd:`Ctrl` + :kbd:`+` + + * - Decrease font size + - :kbd:`Ctrl` + :kbd:`-` + - :kbd:`Cmd` + :kbd:`-` + - :kbd:`Ctrl` + :kbd:`-` + + * - Reset font size + - :kbd:`Ctrl` + :kbd:`0` + - :kbd:`Cmd` + :kbd:`0` + - :kbd:`Ctrl` + :kbd:`0` + + +Source editor +************* + +This table lists the default shortcuts for the source editor. + +In the :ref:`Editor Preferences <settings-editor-preferences>` section of the developer tools settings, you can choose to use Vim, Emacs, or Sublime Text key bindings instead. + +To select these, visit ``about:config``, select the setting ``devtools.editor.keymap``, and assign "vim" or "emacs", or "sublime" to that setting. If you do this, the selected bindings will be used for all the developer tools that use the source editor. You need to reopen the editor for the change to take effect. + +From Firefox 33 onwards, the key binding preference is exposed in the :ref:`Editor Preferences <settings-editor-preferences>` section of the developer tools settings, and you can set it there instead of ``about:config``. + + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Go to line + - :kbd:`Ctrl` + :kbd:`J`, :kbd:`Ctrl` + :kbd:`G` + - :kbd:`Cmd` + :kbd:`J`, :kbd:`Cmd` + :kbd:`G` + - :kbd:`Ctrl` + :kbd:`J`, :kbd:`Ctrl` + :kbd:`G` + + * - Find in file + - :kbd:`Ctrl` + :kbd:`F` + - :kbd:`Cmd` + :kbd:`F` + - :kbd:`Ctrl` + :kbd:`F` + + * - Select all + - :kbd:`Ctrl` + :kbd:`A` + - :kbd:`Cmd` + :kbd:`A` + - :kbd:`Ctrl` + :kbd:`A` + + * - Cut + - :kbd:`Ctrl` + :kbd:`X` + - :kbd:`Cmd` + :kbd:`X` + - :kbd:`Ctrl` + :kbd:`X` + + * - Copy + - :kbd:`Ctrl` + :kbd:`C` + - :kbd:`Cmd` + :kbd:`C` + - :kbd:`Ctrl` + :kbd:`C` + + * - Paste + - :kbd:`Ctrl` + :kbd:`V` + - :kbd:`Cmd` + :kbd:`V` + - :kbd:`Ctrl` + :kbd:`V` + + * - Undo + - :kbd:`Ctrl` + :kbd:`Z` + - :kbd:`Cmd` + :kbd:`Z` + - :kbd:`Ctrl` + :kbd:`Z` + + * - Redo + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`Z` / :kbd:`Ctrl` + :kbd:`Y` + - :kbd:`Cmd` + :kbd:`Shift` + :kbd:`Z` / :kbd:`Cmd` + :kbd:`Y` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`Z` / :kbd:`Ctrl` + :kbd:`Y` + + * - Indent + - :kbd:`Tab` + - :kbd:`Tab` + - :kbd:`Tab` + + * - Unindent + - :kbd:`Shift` + :kbd:`Tab` + - :kbd:`Shift` + :kbd:`Tab` + - :kbd:`Shift` + :kbd:`Tab` + + * - Move line(s) up + - :kbd:`Alt` + :kbd:`Up` + - :kbd:`Alt` + :kbd:`Up` + - :kbd:`Alt` + :kbd:`Up` + + * - Move line(s) down + - :kbd:`Alt` + :kbd:`Down` + - :kbd:`Alt` + :kbd:`Down` + - :kbd:`Alt` + :kbd:`Down` + + * - Comment/uncomment line(s) + - :kbd:`Ctrl` + :kbd:`/` + - :kbd:`Cmd` + :kbd:`/` + - :kbd:`Ctrl` + :kbd:`/` + + +.. _keyboard-shortcuts-page-inspector: + +Page Inspector +************** + +Keyboard shortcuts for the :doc:`Page inspector <../page_inspector/index>`. + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Inspect Element + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`C` + - :kbd:`Cmd` + :kbd:`Shift` + :kbd:`C` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`C` + + +Node picker +*********** + +These shortcuts work while the :ref:`node picker <page-inspector-how-to-select-an-element-with-the-node-picker>` is active. + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Select the element under the mouse and cancel picker mode + - :kbd:`Click` + - :kbd:`Click` + - :kbd:`Click` + + * - Select the element under the mouse and stay in picker mode + - :kbd:`Shift` + :kbd:`Click` + - :kbd:`Shift` + :kbd:`Click` + - :kbd:`Shift` + :kbd:`Click` + + +.. _keyboard-shortcuts-html-pane: + +HTML pane +********* + +These shortcuts work while you're in the :doc:`Inspector's HTML pane <../page_inspector/how_to/examine_and_edit_html/index>`. + +.. list-table:: + :widths: 40 20 20 20 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Delete the selected node + - :kbd:`Delete` + - :kbd:`Delete` + - :kbd:`Delete` + + * - Undo delete of a node + - :kbd:`Ctrl` + :kbd:`Z` + - :kbd:`Cmd` + :kbd:`Z` + - :kbd:`Ctrl` + :kbd:`Z` + + * - Redo delete of a node + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`Z` / :kbd:`Ctrl` + :kbd:`Y` + - :kbd:`Cmd` + :kbd:`Shift` + :kbd:`Z` / :kbd:`Cmd` + :kbd:`Y` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`Z` / :kbd:`Ctrl` + :kbd:`Y` + + * - Move to next node (expanded nodes only) + - :kbd:`↓` + - :kbd:`↓` + - :kbd:`↓` + + * - Move to previous node + - :kbd:`↑` + - :kbd:`↑` + - :kbd:`↑` + + * - Move to first node in the tree. + - :kbd:`Home` + - :kbd:`Home` + - :kbd:`Home` + + * - Move to last node in the tree. + - :kbd:`End` + - :kbd:`End` + - :kbd:`End` + + * - Expand currently selected node + - :kbd:`→` + - :kbd:`→` + - :kbd:`→` + + * - Collapse currently selected node + - :kbd:`←` + - :kbd:`←` + - :kbd:`←` + + * - (When a node is selected) move inside the node so you can start stepping through attributes. + - :kbd:`Enter` + - :kbd:`Return` + - :kbd:`Enter` + + * - Step forward through the attributes of a node + - :kbd:`Tab` + - :kbd:`Tab` + - :kbd:`Tab` + + * - Step backward through the attributes of a node + - :kbd:`Shift` + :kbd:`Tab` + - :kbd:`Shift` + :kbd:`Tab` + - :kbd:`Shift` + :kbd:`Tab` + + * - (When an attribute is selected) start editing the attribute + - :kbd:`Enter` + - :kbd:`Return` + - :kbd:`Enter` + + * - Hide/show the selected node + - :kbd:`H` + - :kbd:`H` + - :kbd:`H` + + * - Focus on the search box in the HTML pane + - :kbd:`Ctrl` + :kbd:`F` + - :kbd:`Cmd` + :kbd:`F` + - :kbd:`Ctrl` + :kbd:`F` + + * - Edit as HTML + - :kbd:`F2` + - :kbd:`F2` + - :kbd:`F2` + + * - Stop editing HTML + - :kbd:`F2` / :kbd:`Ctrl` +:kbd:`Enter` + - :kbd:`F2` / :kbd:`Cmd` + :kbd:`Return` + - :kbd:`F2` / :kbd:`Ctrl` + :kbd:`Enter` + + * - Copy the selected node's outer HTML + - :kbd:`Ctrl` + :kbd:`C` + - :kbd:`Cmd` + :kbd:`C` + - :kbd:`Ctrl` + :kbd:`C` + + * - Scroll the selected node into view + - :kbd:`S` + - :kbd:`S` + - :kbd:`S` + + * - Find the next match in the markup, when searching is active + - :kbd:`Enter` + - :kbd:`Return` + - :kbd:`Enter` + + * - Find the previous match in the markup, when searching is active + - :kbd:`Shift` + :kbd:`Enter` + - :kbd:`Shift` + :kbd:`Return` + - :kbd:`Shift` + :kbd:`Enter` + + +.. _keyboard-shortcuts-breadcrumbs-bar: + +Breadcrumbs bar +*************** + +These shortcuts work when the :ref:`breadcrumbs bar <page-inspector-how-to-examine-and-edit-html-breadcrumbs>` is focused. + +.. list-table:: + :widths: 40 20 20 20 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Move to the previous element in the breadcrumbs bar + - :kbd:`←` + - :kbd:`←` + - :kbd:`←` + + * - Move to the next element in the breadcrumbs bar + - :kbd:`→` + - :kbd:`→` + - :kbd:`→` + + * - Focus the :ref:`HTML pane <page_inspector_ui_tour_html_pane>` + - :kbd:`Shift` + :kbd:`Tab` + - :kbd:`Shift` + :kbd:`Tab` + - :kbd:`Shift` + :kbd:`Tab` + + * - Focus the :ref:`CSS pane <page_inspector_ui_tour_rules_view>` + - :kbd:`Tab` + - :kbd:`Tab` + - :kbd:`Tab` + + +CSS pane +******** + +These shortcuts work when you're in the :doc:`Inspector's CSS panel <../page_inspector/how_to/examine_and_edit_css/index>` + +.. list-table:: + :widths: 40 20 20 20 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Focus on the search box in the CSS pane + - :kbd:`Ctrl` + :kbd:`F` + - :kbd:`Cmd` + :kbd:`F` + - :kbd:`Ctrl` + :kbd:`F` + + * - Clear search box content (only when the search box is focused, and content has been entered) + - :kbd:`Esc` + - :kbd:`Esc` + - :kbd:`Esc` + + * - Step forward through properties and values + - :kbd:`Tab` + - :kbd:`Tab` + - :kbd:`Tab` + + * - Step backward through properties and values + - :kbd:`Shift` + :kbd:`Tab` + - :kbd:`Shift` + :kbd:`Tab` + - :kbd:`Shift` + :kbd:`Tab` + + * - Start editing property or value (Rules view only, when a property or value is selected, but not already being edited) + - :kbd:`Enter` or :kbd:`Space` + - :kbd:`Return` or :kbd:`Space` + - :kbd:`Enter` or :kbd:`Space` + + * - Cycle up and down through auto-complete suggestions (Rules view only, when a property or value is being edited) + - :kbd:`↑` , :kbd:`↓` + - :kbd:`↑` , :kbd:`↓` + - :kbd:`↑` , :kbd:`↓` + + * - Choose current auto-complete suggestion (Rules view only, when a property or value is being edited) + - :kbd:`Enter` or :kbd:`Tab` + - :kbd:`Return` or :kbd:`Tab` + - :kbd:`Enter` or :kbd:`Tab` + + * - Increment selected value by 1 + - :kbd:`↑` + - :kbd:`↑` + - :kbd:`↑` + + * - Decrement selected value by 1 + - :kbd:`↓` + - :kbd:`↓` + - :kbd:`↓` + + * - Increment selected value by 100 + - :kbd:`Shift` + :kbd:`PageUp` + - :kbd:`Shift` + :kbd:`PageUp` + - :kbd:`Shift` + :kbd:`PageUp` + + * - Decrement selected value by 100 + - :kbd:`Shift` + :kbd:`PageDown` + - :kbd:`Shift` + :kbd:`PageDown` + - :kbd:`Shift` + :kbd:`PageDown` + + * - Increment selected value by 10 + - :kbd:`Shift` + :kbd:`↑` + - :kbd:`Shift` + :kbd:`↑` + - :kbd:`Shift` + :kbd:`↑` + + * - Decrement selected value by 10 + - :kbd:`Shift` + :kbd:`↓` + - :kbd:`Shift` + :kbd:`↓` + - :kbd:`Shift` + :kbd:`↓` + + * - Increment selected value by 0.1 + - :kbd:`Alt` + :kbd:`↑` (:kbd:`Ctrl` + :kbd:`↑` from Firefox 60 onwards.) + - :kbd:`Alt` + :kbd:`↑` + - :kbd:`Alt` + :kbd:`↑` (:kbd:`Ctrl` + :kbd:`↑` from Firefox 60 onwards.) + + * - Decrement selected value by 0.1 + - :kbd:`Alt` + :kbd:`↓` (:kbd:`Ctrl` + :kbd:`↓` from Firefox 60 onwards). + - :kbd:`Alt` + :kbd:`↓` + - :kbd:`Alt` + :kbd:`↓` (:kbd:`Ctrl` + :kbd:`↓` from Firefox 60 onwards). + + * - Show/hide more information about current property (Computed view only, when a property is selected) + - :kbd:`Enter` or :kbd:`Space` + - :kbd:`Return` or :kbd:`Space` + - :kbd:`Enter` or :kbd:`Space` + + * - Open MDN reference page about current property (Computed view only, when a property is selected) + - :kbd:`F1` + - :kbd:`F1` + - :kbd:`F1` + + * - Open current CSS file in Style Editor (Computed view only, when more information is shown for a property and a CSS file reference is focused). + - :kbd:`Enter` + - :kbd:`Return` + - :kbd:`Enter` + + +.. _keyboard-shortcuts-debugger: + +Debugger +******** + +Keyboard shortcuts for the :doc:`Firefox JavaScript Debugger <../debugger/index>`. + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Close current file + - :kbd:`Ctrl` + :kbd:`W` + - :kbd:`Cmd` + :kbd:`W` + - :kbd:`Ctrl` + :kbd:`W` + + * - Search for a string in the current file + - :kbd:`Ctrl` + :kbd:`F` + - :kbd:`Cmd` + :kbd:`F` + - :kbd:`Ctrl` + :kbd:`F` + + * - Search for a string in all files + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`F` + - :kbd:`Cmd` + :kbd:`Shift` + :kbd:`F` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`F` + + * - Find next in the current file + - :kbd:`Ctrl` + :kbd:`G` + - :kbd:`Cmd` + :kbd:`G` + - :kbd:`Ctrl` + :kbd:`G` + + * - Search for scripts by name + - :kbd:`Ctrl` + :kbd:`P` + - :kbd:`Cmd` + :kbd:`P` + - :kbd:`Ctrl` + :kbd:`P` + + * - Resume execution when at a breakpoint + - :kbd:`F8` + - :kbd:`F8` [4]_ + - :kbd:`F8` + + * - Step over + - :kbd:`F10` + - :kbd:`F10` [4]_ + - :kbd:`F10` + + * - Step into + - :kbd:`F11` + - :kbd:`F11` [4]_ + - :kbd:`F11` + + * - Step out + - :kbd:`Shift` + :kbd:`F11` + - :kbd:`Shift` + :kbd:`F11` [4]_ + - :kbd:`Shift` + :kbd:`F11` + + * - Toggle breakpoint on the currently selected line + - :kbd:`Ctrl` + :kbd:`B` + - :kbd:`Cmd` + :kbd:`B` + - :kbd:`Ctrl` + :kbd:`B` + + * - Toggle conditional breakpoint on the currently selected line + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`B` + - :kbd:`Cmd` + :kbd:`Shift` + :kbd:`B` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`B` + + +.. [4] By default, on some Macs, the function key is remapped to use a special feature: for example, to change the screen brightness or the volume. See this `guide to using these keys as standard function keys <https://support.apple.com/kb/HT3399>`_. To use a remapped key as a standard function key, hold the Function key down as well (so to open the Profiler, use :kbd:`Shift` + :kbd:`Function` + :kbd:`F5`). + + +.. note:: + Before Firefox 66, the combination :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`S` on Windows and Linux or :kbd:`Cmd` + :kbd:`Opt` + :kbd:`S` on macOS would open/close the Debugger. From Firefox 66 and later, this is no longer the case. + + +.. _keyboard-shortcuts-web-console: + +Web Console +*********** + +Keyboard shortcuts for the :doc:`Web Console <../web_console/index>`. + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Open the Web Console + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`K` + - :kbd:`Cmd` + :kbd:`Opt` + :kbd:`K` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`K` + + * - Search in the message display pane + - :kbd:`Ctrl` + :kbd:`F` + - :kbd:`Cmd` + :kbd:`F` + - :kbd:`Ctrl` + :kbd:`F` + + * - Open the :ref:`object inspector pane <web_console_rich_output_examining_object_properties>` + - :kbd:`Ctrl` + :kbd:`Click` + - :kbd:`Ctrl` + :kbd:`Click` + - :kbd:`Ctrl` + :kbd:`Click` + + * - Clear the :ref:`object inspector pane <web_console_rich_output_examining_object_properties>` + - :kbd:`Esc` + - :kbd:`Esc` + - :kbd:`Esc` + + * - Focus on the command line + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`K` + - :kbd:`Cmd` + :kbd:`Opt` + :kbd:`K` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`K` + + * - Clear output + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`L` + - :kbd:`Ctrl` + :kbd:`L` |br| |br| From Firefox 67: |br| |br| :kbd:`Cmd` + :kbd:`K` + - :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`L` + + + +Command line interpreter +************************ + +These shortcuts apply when you're in the :doc:`command line interpreter <../web_console/the_command_line_interpreter/index>`. + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Scroll to start of console output (only if the command line is empty) + - :kbd:`Home` + - :kbd:`Home` + - :kbd:`Home` + + * - Scroll to end of console output (only if the command line is empty) + - :kbd:`End` + - :kbd:`End` + - :kbd:`End` + + * - Page up through console output + - :kbd:`PageUp` + - :kbd:`PageUp` + - :kbd:`PageUp` + + * - Page down through console output + - :kbd:`PageDown` + - :kbd:`PageDown` + - :kbd:`PageDown` + + * - Go backward through :ref:`command history <command_line_interpreter_execution_history>` + - :kbd:`↑` + - :kbd:`↑` + - :kbd:`↑` + + * - Go forward through command history + - :kbd:`↓` + - :kbd:`↓` + - :kbd:`↓` + + * - Initiate reverse search through command history/step backwards through matching commands + - :kbd:`F9` + - :kbd:`Ctrl` + :kbd:`R` + - :kbd:`F9` + + * - Step forward through matching command history (after initiating reverse search) + - :kbd:`Shift` + :kbd:`F9` + - :kbd:`Ctrl` + :kbd:`S` + - :kbd:`Shift` + :kbd:`F9` + + * - Move to the beginning of the line + - :kbd:`Home` + - :kbd:`Ctrl` + :kbd:`A` + - :kbd:`Ctrl` + :kbd:`A` + + * - Move to the end of the line + - :kbd:`End` + - :kbd:`Ctrl` + :kbd:`E` + - :kbd:`Ctrl` + :kbd:`E` + + * - Execute the current expression + - :kbd:`Enter` + - :kbd:`Return` + - :kbd:`Enter` + + * - Add a new line, for entering multiline expressions + - :kbd:`Shift` + :kbd:`Enter` + - :kbd:`Shift` + :kbd:`Return` + - :kbd:`Shift` + :kbd:`Enter` + + +Autocomplete popup +****************** + +These shortcuts apply while the :ref:`autocomplete popup <command_line_interpreter_autocomplete>` is open: + +.. list-table:: + :widths: 40 20 20 20 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Choose the current autocomplete suggestion + - :kbd:`Tab` + - :kbd:`Tab` + - :kbd:`Tab` + + * - Cancel the autocomplete popup + - :kbd:`Esc` + - :kbd:`Esc` + - :kbd:`Esc` + + * - Move to the previous autocomplete suggestion + - :kbd:`↑` + - :kbd:`↑` + - :kbd:`↑` + + * - Move to the next autocomplete suggestion + - :kbd:`↓` + - :kbd:`↓` + - :kbd:`↓` + + * - Page up through autocomplete suggestions + - :kbd:`PageUp` + - :kbd:`PageUp` + - :kbd:`PageUp` + + * - Page down through autocomplete suggestions + - :kbd:`PageDown` + - :kbd:`PageDown` + - :kbd:`PageDown` + + * - Scroll to start of autocomplete suggestions + - :kbd:`Home` + - :kbd:`Home` + - :kbd:`Home` + + * - Scroll to end of autocomplete suggestions + - :kbd:`End` + - :kbd:`End` + - :kbd:`End` + + +.. _keyboard-shortcuts-style-editor: + +Style Editor +************ + +Keyboard shortcuts for the :doc:`Style editor <../style_editor/index>`. + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Open the Style Editor + - :kbd:`Shift` + :kbd:`F7` + - :kbd:`Shift` + :kbd:`F7` + - :kbd:`Shift` + :kbd:`F7` + + * - Open autocomplete popup + - :kbd:`Ctrl` + :kbd:`Space` + - :kbd:`Cmd` + :kbd:`Space` + - :kbd:`Ctrl` + :kbd:`Space` + + * - Find Next + - :kbd:`Ctrl` + :kbd:`G` + - :kbd:`Cmd` + :kbd:`G` + - :kbd:`Ctrl` + :kbd:`G` + + * - Find Previous + - :kbd:`Shift` + :kbd:`Ctrl` + :kbd:`G` + - :kbd:`Shift` + :kbd:`Cmd` + :kbd:`G` + - :kbd:`Shift` + :kbd:`Ctrl` + :kbd:`G` + + * - Replace + - :kbd:`Shift` + :kbd:`Ctrl` + :kbd:`F` + - :kbd:`Cmd` + :kbd:`Option` + :kbd:`F` + - :kbd:`Shift` + :kbd:`Ctrl` + :kbd:`F` + + * - Focus the filter input + - :kbd:`Ctrl` + :kbd:`P` + - :kbd:`Cmd` + :kbd:`P` + - :kbd:`Ctrl` + :kbd:`P` + + * - Save file to disk + - :kbd:`Ctrl` + :kbd:`S` + - :kbd:`Cmd` + :kbd:`S` + - :kbd:`Ctrl` + :kbd:`S` + +.. _keyboard-shortcuts-eyedropper: + +Eyedropper +********** + +Keyboard shortcuts for the :doc:`Eyedropper <../eyedropper/index>`. + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - **Command** + - **Windows** + - **macOS** + - **Linux** + + * - Select the current color + - :kbd:`Enter` + - :kbd:`Return` + - :kbd:`Enter` + + * - Dismiss the Eyedropper + - :kbd:`Esc` + - :kbd:`Esc` + - :kbd:`Esc` + + * - Move by 1 pixel + - :kbd:`ArrowKeys` + - :kbd:`ArrowKeys` + - :kbd:`ArrowKeys` + + * - Move by 10 pixels + - :kbd:`Shift` + :kbd:`ArrowKeys` + - :kbd:`Shift` + :kbd:`ArrowKeys` + - :kbd:`Shift` + :kbd:`ArrowKeys` diff --git a/devtools/docs/user/landingpage_accessibility.png b/devtools/docs/user/landingpage_accessibility.png Binary files differnew file mode 100644 index 0000000000..37dab30279 --- /dev/null +++ b/devtools/docs/user/landingpage_accessibility.png diff --git a/devtools/docs/user/landingpage_console.png b/devtools/docs/user/landingpage_console.png Binary files differnew file mode 100644 index 0000000000..ed5fdd0495 --- /dev/null +++ b/devtools/docs/user/landingpage_console.png diff --git a/devtools/docs/user/landingpage_debugger.png b/devtools/docs/user/landingpage_debugger.png Binary files differnew file mode 100644 index 0000000000..5ab71c2d1d --- /dev/null +++ b/devtools/docs/user/landingpage_debugger.png diff --git a/devtools/docs/user/landingpage_network.png b/devtools/docs/user/landingpage_network.png Binary files differnew file mode 100644 index 0000000000..9c3216bee0 --- /dev/null +++ b/devtools/docs/user/landingpage_network.png diff --git a/devtools/docs/user/landingpage_pageinspector.png b/devtools/docs/user/landingpage_pageinspector.png Binary files differnew file mode 100644 index 0000000000..4f5ada19c2 --- /dev/null +++ b/devtools/docs/user/landingpage_pageinspector.png diff --git a/devtools/docs/user/landingpage_performance_2022.png b/devtools/docs/user/landingpage_performance_2022.png Binary files differnew file mode 100644 index 0000000000..cf1e82fdfe --- /dev/null +++ b/devtools/docs/user/landingpage_performance_2022.png diff --git a/devtools/docs/user/landingpage_responsivedesign.png b/devtools/docs/user/landingpage_responsivedesign.png Binary files differnew file mode 100644 index 0000000000..a48803b40e --- /dev/null +++ b/devtools/docs/user/landingpage_responsivedesign.png diff --git a/devtools/docs/user/logo-developer-quantum.png b/devtools/docs/user/logo-developer-quantum.png Binary files differnew file mode 100644 index 0000000000..778bc9e2d3 --- /dev/null +++ b/devtools/docs/user/logo-developer-quantum.png diff --git a/devtools/docs/user/measure_a_portion_of_the_page/cursor-shown.png b/devtools/docs/user/measure_a_portion_of_the_page/cursor-shown.png Binary files differnew file mode 100644 index 0000000000..dc0f9cde2a --- /dev/null +++ b/devtools/docs/user/measure_a_portion_of_the_page/cursor-shown.png diff --git a/devtools/docs/user/measure_a_portion_of_the_page/index.rst b/devtools/docs/user/measure_a_portion_of_the_page/index.rst new file mode 100644 index 0000000000..f928163b89 --- /dev/null +++ b/devtools/docs/user/measure_a_portion_of_the_page/index.rst @@ -0,0 +1,30 @@ +============================= +Measure a portion of the page +============================= + +Using the **Measuring Tool** you can measure a specific area of a web page. + +This tool is hidden by default. To enable its button: + + +- Go to the DevTools :doc:`Settings <../settings/index>`. +- From the *Available Toolbox Buttons* check the *Measure a portion of the page* checkbox. + + +You will now see the *Measure a portion of the page* button at the top right of the Toolbox, in the same place as the Settings/Options button. + +.. image:: measure-button.png + :class: center + +When you want to use the tool, click this button. Now when you mouse over the viewport, you'll see the mouse has a crosshair cursor with its current coordinates displayed beside it. + +.. image:: cursor-shown.png + :class: border + + +When you hold the mouse button down and then drag, you'll start to draw a rectangle, with its x, y, and diagonal dimensions displayed. The units are in pixels. + +When you stop holding the mouse down, the rectangle that was displayed on screen when you released the button will stay there until you click again, allowing you time to take screenshots, note the information down, etc. The rectangle can also be resized later on by clicking one of the handles around it. + +.. image:: resizable_measuring_area.png + :class: border diff --git a/devtools/docs/user/measure_a_portion_of_the_page/measure-button.png b/devtools/docs/user/measure_a_portion_of_the_page/measure-button.png Binary files differnew file mode 100644 index 0000000000..83895dcda9 --- /dev/null +++ b/devtools/docs/user/measure_a_portion_of_the_page/measure-button.png diff --git a/devtools/docs/user/measure_a_portion_of_the_page/resizable_measuring_area.png b/devtools/docs/user/measure_a_portion_of_the_page/resizable_measuring_area.png Binary files differnew file mode 100644 index 0000000000..cbb3ecb668 --- /dev/null +++ b/devtools/docs/user/measure_a_portion_of_the_page/resizable_measuring_area.png diff --git a/devtools/docs/user/memory/aggregate_view/index.rst b/devtools/docs/user/memory/aggregate_view/index.rst new file mode 100644 index 0000000000..94118e8c5b --- /dev/null +++ b/devtools/docs/user/memory/aggregate_view/index.rst @@ -0,0 +1,183 @@ +============== +Aggregate view +============== + +Before Firefox 48, this was the default view of a heap snapshot. After Firefox 48, the default view is the :doc:`Tree map view <../tree_map_view/index>`, and you can switch to the Aggregate view using the dropdown labeled "View:": + +.. image:: memory-tool-switch-view.png + :class: center + + +The Aggregate view looks something like this: + +.. image:: memory-tool-aggregate-view.png + :class: center + + +It presents a breakdown of the heap's contents, as a table. There are three main ways to group the data: + + +- :ref:`Type <memory-aggregate-view-type>` +- :ref:`Call Stack <memory-aggregate-view-call-stack>` +- :ref:`Inverted Call Stack <memory-aggregate-view-inverted-call-stack>` + +You can switch between them using the dropdown menu labeled "Group by:" located at the top of the panel: + +There's also a box labeled "Filter" at the top-right of the pane. You can use this to filter the contents of the snapshot that are displayed, so you can quickly see, for example, how many objects of a specific class were allocated. + + +.. _memory-aggregate-view-type: + +Type +**** + +This is the default view, which looks something like this: + +.. image:: memory-tool-aggregate-view.png + :class: center + +It groups the things on the heap into types, including: + + +- **JavaScript objects:** such as `Function <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function>`_ or `Array <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array>`_ +- **DOM elements:** such as `HTMLSpanElement <https://developer.mozilla.org/en-US/docs/Web/API/HTMLSpanElement>`_ or `Window <https://developer.mozilla.org/en-US/docs/Web/API/Window>`_ +- **Strings:** listed as ``"strings"`` +- **JavaScript sources:** listed as "``JSScript"`` +- **Internal objects:** such as "``js::Shape``". These are prefixed with ``"js::"``. + + +Each type gets a row in the table, and rows are ordered by the amount of memory occupied by objects of that type. For example, in the screenshot above you can see that JavaScript ``Object`` account for most memory, followed by strings. + + +- The "Total Count" column shows you the number of objects of each category that are currently allocated. +- The "Total Bytes" column shows you the number of bytes occupied by objects in each category, and that number as a percentage of the whole heap size for that tab. + + +The screenshots in this section are taken from a snapshot of the :doc:`monster example page <../monster_example/index>`. + + +For example, in the screenshot above, you can see that: + +- there are four ``Array`` objects +- that account for 15% of the total heap. + + +Next to the type's name, there's an icon that contains three stars arranged in a triangle: + +.. image:: memory-tool-in-group-icon.png + :class: center + + +Click this to see every instance of that type. For example, the entry for ``Array`` tells us that there are four ``Array`` objects in the snapshot. If we click the star-triangle, we'll see all four ``Array`` instances: + +.. image:: memory-tool-in-group.png + :class: center + + +For each instance, you can see the :ref:`retained size and shallow size <shallow-and-retained-size>` of that instance. In this case, you can see that the first three arrays have a fairly large shallow size (5% of the total heap usage) and a much larger retained size (26% of the total). + +On the right-hand side is a pane that just says "Select an item to view its retaining paths". If you select an item, you'll see the :ref:`Retaining paths panel <memory-dominators-view-retaining-paths-panel>` for that item: + +.. image:: memory-tool-in-group-retaining-paths.png + :class: center + + +.. _memory-aggregate-view-call-stack: + +Call Stack +********** + +The Call Stack shows you exactly where in your code you are making heap allocations. + +Because tracing allocations has a runtime cost, it must be explicitly enabled by checking "Record call stacks" *before* you allocate the memory in the snapshot. + +You'll then see a list of all the functions that allocated objects, ordered by the size of the allocations they made: + +.. image:: memory-tool-call-stack.png + :class: center + + +The structure of this view is very much like the structure of the :doc:`Call Tree <../../performance/call_tree/index>`, only it shows allocations rather than processor samples. So, for example, the first entry says that: + + +- 4,832,592 bytes, comprising 93% of the total heap usage, were allocated in a function at line 35 of "alloc.js", **or in functions called by that function** + + +We can use the disclosure triangle to drill down the call tree, to find the exact place your code made those allocations. + +It's easier to explain this with reference to a simple example. For :doc:`DOM allocation example <../dom_allocation_example/index>`. This page runs a script that creates a large number of DOM nodes (200 `HTMLDivElement <https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement>`_ objects and 4000 `HTMLSpanElement <https://developer.mozilla.org/en-US/docs/Web/API/HTMLSpanElement>`_ objects). + +Let's get an allocation trace: + + +1. open the Memory tool +2. check "Record call stacks" +3. load https://firefox-devtools.github.io/performance-scenarios/dom-allocs/alloc.html +4. take a snapshot +5. select "View/Aggregate" +6. select "Group by/Call Stack" + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/DyLulu9eoKY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +You should see something like this: + +.. image:: memory-tool-call-stack.png + :class: center + + +This is telling us that 93% of the total heap snapshot was allocated in functions called from "alloc.js", line 35 (our initial ``createToolbars()`` call). + +We can use the disclosure arrow to expand the tree to find out exactly where we're allocating memory: + +.. image:: memory-tool-call-stack-expanded.png + :class: center + + +This is where the "Bytes" and "Count" columns are useful: they show allocation size and number of allocations at that exact point. + +So in the example above, we can see that we made 4002 allocations, accounting for 89% of the total heap, in ``createToolbarButton()``, at `alloc.js line 9, position 23 <https://github.com/mdn/performance-scenarios/blob/gh-pages/dom-allocs/scripts/alloc.js#L9>`_: that is, the exact point where we create the `<span> <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span>`_ elements. + +The file name and line number is a link: if we click it, we go directly to that line in the debugger: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/zlnJcr1IFyY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +.. _memory-aggregate-view-inverted-call-stack: + +Inverted Call Stack +******************* + +The Call Stack view is top-down: it shows allocations that happen at that point **or points deeper in the call tree**. So it's good for getting an overview of where your program is memory-hungry. However, this view means you have to drill a long way down to find the exact place where the allocations are happening. + +The "Inverted Call Stack" view helps with that. It gives you the bottom-up view of the program showing the exact places where allocations are happening, ranked by the size of allocation at each place. The disclosure arrow then walks you back up the call tree towards the top level. + +Let's see what the example looks like when we select "Inverted Call Stack": + +.. image:: memory-tool-inverted-call-stack.png + :class: center + + +Now at the top we can immediately see the ``createToolbarButton()`` call accounting for 89% of the heap usage in our page. + + +(no stack available) +******************** + +In the example above you'll note that 7% of the heap is marked "(no stack available)". This is because not all heap usage results from your JavaScript. + +For example: + + +- any scripts the page loads occupy heap space +- sometimes an object is allocated when there is no JavaScript on the stack. For example, DOM `Event <https://developer.mozilla.org/en-US/docs/Web/API/Event>`_ objects are allocated before the JavaScript is run and event handlers are called. + + +Many real-world pages will have a much higher "(no stack available)" share than 7%. diff --git a/devtools/docs/user/memory/aggregate_view/memory-tool-aggregate-view.png b/devtools/docs/user/memory/aggregate_view/memory-tool-aggregate-view.png Binary files differnew file mode 100644 index 0000000000..653710979f --- /dev/null +++ b/devtools/docs/user/memory/aggregate_view/memory-tool-aggregate-view.png diff --git a/devtools/docs/user/memory/aggregate_view/memory-tool-call-stack-expanded.png b/devtools/docs/user/memory/aggregate_view/memory-tool-call-stack-expanded.png Binary files differnew file mode 100644 index 0000000000..fe2364da58 --- /dev/null +++ b/devtools/docs/user/memory/aggregate_view/memory-tool-call-stack-expanded.png diff --git a/devtools/docs/user/memory/aggregate_view/memory-tool-call-stack.png b/devtools/docs/user/memory/aggregate_view/memory-tool-call-stack.png Binary files differnew file mode 100644 index 0000000000..52a96015da --- /dev/null +++ b/devtools/docs/user/memory/aggregate_view/memory-tool-call-stack.png diff --git a/devtools/docs/user/memory/aggregate_view/memory-tool-in-group-icon.png b/devtools/docs/user/memory/aggregate_view/memory-tool-in-group-icon.png Binary files differnew file mode 100644 index 0000000000..6354a3d377 --- /dev/null +++ b/devtools/docs/user/memory/aggregate_view/memory-tool-in-group-icon.png diff --git a/devtools/docs/user/memory/aggregate_view/memory-tool-in-group-retaining-paths.png b/devtools/docs/user/memory/aggregate_view/memory-tool-in-group-retaining-paths.png Binary files differnew file mode 100644 index 0000000000..191115f041 --- /dev/null +++ b/devtools/docs/user/memory/aggregate_view/memory-tool-in-group-retaining-paths.png diff --git a/devtools/docs/user/memory/aggregate_view/memory-tool-in-group.png b/devtools/docs/user/memory/aggregate_view/memory-tool-in-group.png Binary files differnew file mode 100644 index 0000000000..88aac55e9e --- /dev/null +++ b/devtools/docs/user/memory/aggregate_view/memory-tool-in-group.png diff --git a/devtools/docs/user/memory/aggregate_view/memory-tool-inverted-call-stack.png b/devtools/docs/user/memory/aggregate_view/memory-tool-inverted-call-stack.png Binary files differnew file mode 100644 index 0000000000..5a951c2e8c --- /dev/null +++ b/devtools/docs/user/memory/aggregate_view/memory-tool-inverted-call-stack.png diff --git a/devtools/docs/user/memory/aggregate_view/memory-tool-switch-view.png b/devtools/docs/user/memory/aggregate_view/memory-tool-switch-view.png Binary files differnew file mode 100644 index 0000000000..bb3cb0cdb3 --- /dev/null +++ b/devtools/docs/user/memory/aggregate_view/memory-tool-switch-view.png diff --git a/devtools/docs/user/memory/basic_operations/index.rst b/devtools/docs/user/memory/basic_operations/index.rst new file mode 100644 index 0000000000..c8149ef344 --- /dev/null +++ b/devtools/docs/user/memory/basic_operations/index.rst @@ -0,0 +1,95 @@ +================ +Basic operations +================ + + +.. _memory-basic-operations-opening-the-memory-tool: + +Opening the Memory tool +*********************** + +Before Firefox 50, the Memory tool is not enabled by default. To enable it, open the developer tool settings, and check the "Memory" box under "Default Firefox Developer Tools". + +From Firefox 50 onwards, the Memory tool is enabled by default. + + +.. _memory-basic-operations-taking-a-heap-snapshot: + +Taking a heap snapshot +********************** + +To take a snapshot of the heap, click the "Take snapshot" button, or the camera icon on the left: + +.. image:: memory-1-small.png + :class: center + +The snapshot will occupy the large pane on the right-hand side. On the left, you'll see an entry for the new snapshot, including its timestamp, size, and controls to save or clear this snapshot: + +.. image:: memory-2-small.png + :class: center + + +.. _memory-basic-operations-clearing-a-snapshot: + +Clearing a snapshot +******************* + +To remove a snapshot, click the "X" icon: + +.. image:: memory-3-small.png + :class: center + + +.. _memory-basic-operations-saving-and-loading-snapshots: + +Saving and loading snapshots +**************************** + +If you close the Memory tool, all unsaved snapshots will be discarded. To save a snapshot click "Save": + +.. image:: memory-4-small.png + :class: center + +You'll be prompted for a name and location, and the file will be saved with an ``.fxsnapshot`` extension. + +To load a snapshot from an existing ``.fxsnapshot`` file, click the import button, which looks like a rectangle with an arrow rising from it (before Firefox 49, this button was labeled with the text "Import..."): + +.. image:: memory-5-small.png + :class: center + +You'll be prompted to find a snapshot file on disk. + + +.. _memory-basic-operations-comparing-snapshots: + +Comparing snapshots +******************* + +Starting in Firefox 45, you can diff two heap snapshots. The diff shows you where memory was allocated or freed between the two snapshots. + +To create a diff, click the button that looks like a Venn diagram next to the camera icon (before Firefox 47, this looked like a "+/-" icon): + +.. image:: memory-6-small.png + :class: center + +You'll be prompted to select the snapshot to use as a baseline, then the snapshot to compare. The tool then shows you the differences between the two snapshots: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/3Ow-mdK6b2M" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +.. note:: + When you're looking at a comparison, you can't use the Dominators view or the Tree Map view. + + +.. _memory-basic-operations-recording-call-stacks: + +Recording call stacks +********************* + +The Memory tool can tell you exactly where in your code you are allocating memory. However, recording this information has a run-time cost, so you must ask the tool to record memory calls *before* the memory is allocated, if you want to see memory call sites in the snapshot. To do this, check "Record call stacks" (before Firefox 49 this was labeled "Record allocation stacks"): + +.. image:: memory-7-small.png + :class: center diff --git a/devtools/docs/user/memory/basic_operations/memory-1-small.png b/devtools/docs/user/memory/basic_operations/memory-1-small.png Binary files differnew file mode 100644 index 0000000000..a2076330b8 --- /dev/null +++ b/devtools/docs/user/memory/basic_operations/memory-1-small.png diff --git a/devtools/docs/user/memory/basic_operations/memory-2-small.png b/devtools/docs/user/memory/basic_operations/memory-2-small.png Binary files differnew file mode 100644 index 0000000000..569b0d9d66 --- /dev/null +++ b/devtools/docs/user/memory/basic_operations/memory-2-small.png diff --git a/devtools/docs/user/memory/basic_operations/memory-3-small.png b/devtools/docs/user/memory/basic_operations/memory-3-small.png Binary files differnew file mode 100644 index 0000000000..5d77bd7f60 --- /dev/null +++ b/devtools/docs/user/memory/basic_operations/memory-3-small.png diff --git a/devtools/docs/user/memory/basic_operations/memory-4-small.png b/devtools/docs/user/memory/basic_operations/memory-4-small.png Binary files differnew file mode 100644 index 0000000000..9a1e18da6f --- /dev/null +++ b/devtools/docs/user/memory/basic_operations/memory-4-small.png diff --git a/devtools/docs/user/memory/basic_operations/memory-5-small.png b/devtools/docs/user/memory/basic_operations/memory-5-small.png Binary files differnew file mode 100644 index 0000000000..e3277186dc --- /dev/null +++ b/devtools/docs/user/memory/basic_operations/memory-5-small.png diff --git a/devtools/docs/user/memory/basic_operations/memory-6-small.png b/devtools/docs/user/memory/basic_operations/memory-6-small.png Binary files differnew file mode 100644 index 0000000000..da69b93e51 --- /dev/null +++ b/devtools/docs/user/memory/basic_operations/memory-6-small.png diff --git a/devtools/docs/user/memory/basic_operations/memory-7-small.png b/devtools/docs/user/memory/basic_operations/memory-7-small.png Binary files differnew file mode 100644 index 0000000000..844565a8b4 --- /dev/null +++ b/devtools/docs/user/memory/basic_operations/memory-7-small.png diff --git a/devtools/docs/user/memory/dom_allocation_example/index.rst b/devtools/docs/user/memory/dom_allocation_example/index.rst new file mode 100644 index 0000000000..85a9ab9036 --- /dev/null +++ b/devtools/docs/user/memory/dom_allocation_example/index.rst @@ -0,0 +1,57 @@ +====================== +DOM allocation example +====================== + +This article describes a very simple web page that we'll use to illustrate some features of the Memory tool. + +You can try out the site at https://firefox-devtools.github.io/performance-scenarios/dom-allocs/alloc.html. + +It just contains a script that creates a large number of DOM nodes: + +.. code-block:: javascript + + var toolbarButtonCount = 20; + var toolbarCount = 200; + + function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + function createToolbarButton() { + var toolbarButton = document.createElement("span"); + toolbarButton.classList.add("toolbarbutton"); + // stop Spidermonkey from sharing instances + toolbarButton[getRandomInt(0,5000)] = "foo"; + return toolbarButton; + } + + function createToolbar() { + var toolbar = document.createElement("div"); + // stop Spidermonkey from sharing instances + toolbar[getRandomInt(0,5000)] = "foo"; + for (var i = 0; i < toolbarButtonCount; i++) { + var toolbarButton = createToolbarButton(); + toolbar.appendChild(toolbarButton); + } + return toolbar; + } + + function createToolbars() { + var container = document.getElementById("container"); + for (var i = 0; i < toolbarCount; i++) { + var toolbar = createToolbar(); + container.appendChild(toolbar); + } + } + + createToolbars(); + +A simple pseudocode representation of how this code operates looks like this: + +.. code-block:: JavaScript + + createToolbars() + -> createToolbar() // called 200 times, creates 1 DIV element each time + -> createToolbarButton() // called 20 times per toolbar, creates 1 SPAN element each time</pre> + +In total, then, it creates 200 `HTMLDivElement <https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement>`_ objects, and 4000 `HTMLSpanElement <https://developer.mozilla.org/en-US/docs/Web/API/HTMLSpanElement>`_ objects. diff --git a/devtools/docs/user/memory/dominators/index.rst b/devtools/docs/user/memory/dominators/index.rst new file mode 100644 index 0000000000..443b4c6e7b --- /dev/null +++ b/devtools/docs/user/memory/dominators/index.rst @@ -0,0 +1,85 @@ +========== +Dominators +========== + +This article provides an introduction to the concepts of *Reachability*, *Shallow* versus *Retained* size, and *Dominators*, as they apply in garbage-collected languages like JavaScript. + +These concepts matter in memory analysis, because often an object may itself be small, but may hold references to other much larger objects, and by doing this will prevent the garbage collector from freeing that extra memory. + +You can see the dominators in a page using the :doc:`Dominators view <../dominators_view/index>` in the Memory tool. + + +With a garbage-collected language, like JavaScript, the programmer doesn't generally have to worry about deallocating memory. They can just create and use objects, and when the objects are no longer needed, the runtime takes care of cleaning up, and frees the memory the objects occupied. + + +.. _memory-dominators-reachability: + +Reachability +************ + +In modern JavaScript implementations, the runtime decides whether an object is no longer needed based on *reachability*. In this system the heap is represented as one or more graphs. Each node in the graph represents an object, and each connection between nodes (edge) represents a reference from one object to another. The graph starts at a root node, indicated in these diagrams with "R". + +.. image:: memory-graph.svg + :class: center + + +During garbage collection, the runtime traverses the graph, starting at the root, and marks every object it finds. Any objects it doesn't find are unreachable, and can be deallocated. + +So when an object becomes unreachable (for example, because it is only referenced by a single local variable which goes out of scope) then any objects it references also become unreachable, as long as no other objects reference them: + +.. image:: memory-graph-unreachable.svg + :class: center + + +Conversely, this means that objects are kept alive as long as some other reachable object is holding a reference to them. + + +.. _shallow-and-retained-size: + +Shallow and retained size +************************* + +This gives rise to a distinction between two ways to look at the size of an object: + + +- *shallow size*: the size of the object itself +- *retained size*: the size of the object itself, plus the size of other objects that are kept alive by this object + + +Often, objects will have a small shallow size but a much larger retained size, through the references they contain to other objects. Retained size is an important concept in analyzing memory usage, because it answers the question "if this object ceases to exist, what's the total amount of memory freed?". + + +Dominators +********** + +A related concept is that of the *dominator*. Node B is said to dominate node A if every path from the root to A passes through B: + +.. image:: memory-graph-dominators.svg + :class: center + + +If any of node A's dominators are freed, then node A itself becomes eligible for garbage collection. + + +.. _memory-dominators-immediate-dominator: + +If node B dominates node A, but does not dominate any of A's other dominators, then B is the *immediate dominator* of A: + +.. image:: memory-graph-immediate-dominator.svg + :class: center + + +.. _memory-dominators-multiple-paths: + +One slight subtlety here is that if an object A is referenced by two other unrelated objects B and C, then neither object is its dominator, because you could remove either B or C from the graph, and A would still be retained by its other referrer. Instead, the immediate dominator of A would be its first common ancestor: + +.. image:: memory-graph-dominator-multiple-references.svg + :class: center + + +See also +******** + + +- `Dominators in graph theory <https://en.wikipedia.org/wiki/Dominator_%28graph_theory%29>`_. +- `Tracing garbage collection <https://en.wikipedia.org/wiki/Tracing_garbage_collection>`_. diff --git a/devtools/docs/user/memory/dominators/memory-graph-dominator-multiple-references.svg b/devtools/docs/user/memory/dominators/memory-graph-dominator-multiple-references.svg new file mode 100644 index 0000000000..4ac6b45812 --- /dev/null +++ b/devtools/docs/user/memory/dominators/memory-graph-dominator-multiple-references.svg @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" viewBox="114 20 158 212" width="158pt" height="212pt"><defs><linearGradient x1="0" x2="1" id="a" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#e5f2f2"/><stop offset="1" stop-color="#cff2f2"/></linearGradient><linearGradient id="b" xl:href="#a" gradientTransform="matrix(0 38 -38 0 183 90.5)"/><linearGradient id="c" xl:href="#a" gradientTransform="matrix(0 38 -38 0 135 141.5)"/><linearGradient id="d" xl:href="#a" gradientTransform="rotate(90 44.5 186) scale(38)"/><linearGradient id="f" xl:href="#a" gradientTransform="matrix(0 38 -38 0 183 191.5)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="e" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#000"><path d="M8 0L0-3v6z" fill="currentColor" stroke="currentColor"/></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="g" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#000"><path d="M8 0H0m0-3l8 3-8 3" fill="none" stroke="currentColor"/></marker></defs><g fill="none"><circle cx="183" cy="109.5" r="19" fill="url(#b)"/><circle cx="183" cy="109.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="135" cy="160.5" r="19" fill="url(#c)"/><circle cx="135" cy="160.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="230.5" cy="160.5" r="19" fill="url(#d)"/><circle cx="230.5" cy="160.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path marker-end="url(#e)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M169.978 123.336l-15.171 16.119m41.143-16.051l14.853 15.948"/><circle cx="183" cy="210.5" r="19" fill="url(#f)"/><circle cx="183" cy="210.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><text transform="translate(172.8 201.5)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="5.016" y="15" textLength="10.368">A</tspan></text><path marker-end="url(#e)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M148.158 174.206l14.828 15.446"/><text transform="translate(169 26.5)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x=".096" y="15" textLength="10.368">A</tspan><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="9.28" y="15" textLength="4.448">’</tspan><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="12.544" y="15" textLength="85.36">s dominator</tspan></text><path marker-end="url(#g)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="4,4" d="M210.905 50.5l-15.546 32.87"/><path marker-end="url(#e)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M217.414 174.275l-14.509 15.272"/></g></svg>
\ No newline at end of file diff --git a/devtools/docs/user/memory/dominators/memory-graph-dominators.svg b/devtools/docs/user/memory/dominators/memory-graph-dominators.svg new file mode 100644 index 0000000000..e3a63c96df --- /dev/null +++ b/devtools/docs/user/memory/dominators/memory-graph-dominators.svg @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" viewBox="60 57 353 232" width="353pt" height="232pt"><defs><linearGradient x1="0" x2="1" id="a" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#e5f2f2"/><stop offset="1" stop-color="#cff2f2"/></linearGradient><linearGradient id="b" xl:href="#a" gradientTransform="matrix(0 38 -38 0 183 90.5)"/><linearGradient id="c" xl:href="#a" gradientTransform="matrix(0 38 -38 0 135 141.5)"/><linearGradient id="d" xl:href="#a" gradientTransform="rotate(90 44.5 186) scale(38)"/><linearGradient id="f" xl:href="#a" gradientTransform="matrix(0 38 -38 0 81.5 196.5)"/><linearGradient id="g" xl:href="#a" gradientTransform="rotate(90 41 238.5) scale(38)"/><linearGradient id="h" xl:href="#a" gradientTransform="rotate(90 -8.5 240) scale(38)"/><linearGradient id="i" xl:href="#a" gradientTransform="matrix(0 38 -38 0 327 248.5)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="e" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#000"><path d="M8 0L0-3v6z" fill="currentColor" stroke="currentColor"/></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="j" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#000"><path d="M8 0H0m0-3l8 3-8 3" fill="none" stroke="currentColor"/></marker></defs><g fill="none"><circle cx="183" cy="109.5" r="19" fill="url(#b)"/><circle cx="183" cy="109.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><text transform="translate(172.8 100.5)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="4.72" y="15" textLength="10.96">R</tspan></text><circle cx="135" cy="160.5" r="19" fill="url(#c)"/><circle cx="135" cy="160.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="230.5" cy="160.5" r="19" fill="url(#d)"/><circle cx="230.5" cy="160.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path marker-end="url(#e)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M169.978 123.336l-15.171 16.119m41.143-16.051l14.853 15.948"/><circle cx="81.5" cy="215.5" r="19" fill="url(#f)"/><circle cx="81.5" cy="215.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path marker-end="url(#e)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M121.752 174.119l-20.101 20.665"/><circle cx="279.5" cy="216.5" r="19" fill="url(#g)"/><circle cx="279.5" cy="216.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="231.5" cy="267.5" r="19" fill="url(#h)"/><circle cx="231.5" cy="267.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="327" cy="267.5" r="19" fill="url(#i)"/><circle cx="327" cy="267.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><text transform="translate(316.8 258.5)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="5.016" y="15" textLength="10.368">A</tspan></text><path marker-end="url(#e)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M243.012 174.799l17.457 19.951m31.981 35.654l14.853 15.948m-40.825-16.016l-15.171 16.119"/><text transform="translate(301.5 63)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x=".096" y="15" textLength="10.368">A</tspan><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="9.28" y="15" textLength="4.448">’</tspan><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="12.544" y="15" textLength="93.36">s dominators</tspan></text><path marker-end="url(#j)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="4,4" d="M296.5 84.682l-85.265 18.644M333.483 87l-79.457 56.709M346.715 87l-53.899 103.845"/></g></svg>
\ No newline at end of file diff --git a/devtools/docs/user/memory/dominators/memory-graph-immediate-dominator.svg b/devtools/docs/user/memory/dominators/memory-graph-immediate-dominator.svg new file mode 100644 index 0000000000..8288524884 --- /dev/null +++ b/devtools/docs/user/memory/dominators/memory-graph-immediate-dominator.svg @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" viewBox="60 57 350 232" width="350pt" height="232pt"><defs><linearGradient x1="0" x2="1" id="a" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#e5f2f2"/><stop offset="1" stop-color="#cff2f2"/></linearGradient><linearGradient id="b" xl:href="#a" gradientTransform="matrix(0 38 -38 0 183 90.5)"/><linearGradient id="c" xl:href="#a" gradientTransform="matrix(0 38 -38 0 135 141.5)"/><linearGradient id="d" xl:href="#a" gradientTransform="rotate(90 44.5 186) scale(38)"/><linearGradient id="f" xl:href="#a" gradientTransform="matrix(0 38 -38 0 81.5 196.5)"/><linearGradient id="g" xl:href="#a" gradientTransform="rotate(90 41 238.5) scale(38)"/><linearGradient id="h" xl:href="#a" gradientTransform="rotate(90 -8.5 240) scale(38)"/><linearGradient id="i" xl:href="#a" gradientTransform="matrix(0 38 -38 0 327 248.5)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="e" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#000"><path d="M8 0L0-3v6z" fill="currentColor" stroke="currentColor"/></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="j" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#000"><path d="M8 0H0m0-3l8 3-8 3" fill="none" stroke="currentColor"/></marker></defs><g fill="none"><circle cx="183" cy="109.5" r="19" fill="url(#b)"/><circle cx="183" cy="109.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><text transform="translate(172.8 100.5)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="4.72" y="15" textLength="10.96">R</tspan></text><circle cx="135" cy="160.5" r="19" fill="url(#c)"/><circle cx="135" cy="160.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="230.5" cy="160.5" r="19" fill="url(#d)"/><circle cx="230.5" cy="160.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path marker-end="url(#e)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M169.978 123.336l-15.171 16.119m41.143-16.051l14.853 15.948"/><circle cx="81.5" cy="215.5" r="19" fill="url(#f)"/><circle cx="81.5" cy="215.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path marker-end="url(#e)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M121.752 174.119l-20.101 20.665"/><circle cx="279.5" cy="216.5" r="19" fill="url(#g)"/><circle cx="279.5" cy="216.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="231.5" cy="267.5" r="19" fill="url(#h)"/><circle cx="231.5" cy="267.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="327" cy="267.5" r="19" fill="url(#i)"/><circle cx="327" cy="267.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><text transform="translate(316.8 258.5)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="5.016" y="15" textLength="10.368">A</tspan></text><path marker-end="url(#e)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M243.012 174.799l17.457 19.951m31.981 35.654l14.853 15.948m-40.825-16.016l-15.171 16.119"/><text transform="translate(304.5 62)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x=".2" y="15" textLength="10.368">A</tspan><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="9.384" y="15" textLength="4.448">’</tspan><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="12.648" y="15" textLength="87.152">s immediate</tspan><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="13.544" y="33" textLength="72.912">dominator</tspan></text><path marker-end="url(#j)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="4,4" d="M341.863 103l-48.444 88.167"/></g></svg>
\ No newline at end of file diff --git a/devtools/docs/user/memory/dominators/memory-graph-unreachable.svg b/devtools/docs/user/memory/dominators/memory-graph-unreachable.svg new file mode 100644 index 0000000000..62abb0e016 --- /dev/null +++ b/devtools/docs/user/memory/dominators/memory-graph-unreachable.svg @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" viewBox="60 88 287 200" width="287pt" height="200pt"><defs><linearGradient x1="0" x2="1" id="a" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#e5f2f2"/><stop offset="1" stop-color="#cff2f2"/></linearGradient><linearGradient id="c" xl:href="#a" gradientTransform="matrix(0 38 -38 0 183 90.5)"/><linearGradient id="d" xl:href="#a" gradientTransform="matrix(0 38 -38 0 135 141.5)"/><linearGradient id="e" xl:href="#a" gradientTransform="rotate(90 44.5 186) scale(38)"/><linearGradient x1="0" x2="1" id="b" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f29a8b"/><stop offset="1" stop-color="#f23d1c"/></linearGradient><linearGradient id="f" xl:href="#b" gradientTransform="rotate(90 41 237.5) scale(38)"/><linearGradient id="g" xl:href="#b" gradientTransform="rotate(90 -8.5 239) scale(38)"/><linearGradient id="h" xl:href="#b" gradientTransform="matrix(0 38 -38 0 326 247.5)"/><linearGradient id="j" xl:href="#a" gradientTransform="matrix(0 38 -38 0 81.5 196.5)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="i" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#000"><path d="M8 0L0-3v6z" fill="currentColor" stroke="currentColor"/></marker></defs><g fill="none"><circle cx="183" cy="109.5" r="19" fill="url(#c)"/><circle cx="183" cy="109.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><text transform="translate(172.8 100.5)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="4.72" y="15" textLength="10.96">R</tspan></text><circle cx="135" cy="160.5" r="19" fill="url(#d)"/><circle cx="135" cy="160.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="230.5" cy="160.5" r="19" fill="url(#e)"/><circle cx="230.5" cy="160.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="278.5" cy="215.5" r="19" fill="url(#f)"/><circle cx="278.5" cy="215.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="230.5" cy="266.5" r="19" fill="url(#g)"/><circle cx="230.5" cy="266.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="326" cy="266.5" r="19" fill="url(#h)"/><circle cx="326" cy="266.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path marker-end="url(#i)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M169.978 123.336l-15.171 16.119m41.143-16.051l14.853 15.948m54.675 89.984l-15.171 16.119m41.143-16.051l14.853 15.948"/><circle cx="81.5" cy="215.5" r="19" fill="url(#j)"/><circle cx="81.5" cy="215.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path marker-end="url(#i)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M121.752 174.119l-20.101 20.665"/></g></svg>
\ No newline at end of file diff --git a/devtools/docs/user/memory/dominators/memory-graph.svg b/devtools/docs/user/memory/dominators/memory-graph.svg new file mode 100644 index 0000000000..0b4ee2d64c --- /dev/null +++ b/devtools/docs/user/memory/dominators/memory-graph.svg @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" viewBox="60 88 288 201" width="384" height="268"><defs><linearGradient x1="0" x2="1" id="a" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#e5f2f2"/><stop offset="1" stop-color="#cff2f2"/></linearGradient><linearGradient id="b" xl:href="#a" gradientTransform="matrix(0 38 -38 0 183 90.5)"/><linearGradient id="c" xl:href="#a" gradientTransform="matrix(0 38 -38 0 135 141.5)"/><linearGradient id="d" xl:href="#a" gradientTransform="rotate(90 44.5 186) scale(38)"/><linearGradient id="f" xl:href="#a" gradientTransform="matrix(0 38 -38 0 81.5 196.5)"/><linearGradient id="g" xl:href="#a" gradientTransform="rotate(90 41 238.5) scale(38)"/><linearGradient id="h" xl:href="#a" gradientTransform="rotate(90 -8.5 240) scale(38)"/><linearGradient id="i" xl:href="#a" gradientTransform="matrix(0 38 -38 0 327 248.5)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="e" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#000"><path d="M8 0L0-3v6z" fill="currentColor" stroke="currentColor"/></marker></defs><g fill="none"><circle cx="183" cy="109.5" r="19" fill="url(#b)"/><circle cx="183" cy="109.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><text transform="translate(172.8 100.5)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="4.72" y="15" textLength="10.96">R</tspan></text><circle cx="135" cy="160.5" r="19" fill="url(#c)"/><circle cx="135" cy="160.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="230.5" cy="160.5" r="19" fill="url(#d)"/><circle cx="230.5" cy="160.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path marker-end="url(#e)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M169.978 123.336l-15.171 16.119m41.143-16.051l14.853 15.948"/><circle cx="81.5" cy="215.5" r="19" fill="url(#f)"/><circle cx="81.5" cy="215.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path marker-end="url(#e)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M121.752 174.119l-20.101 20.665"/><circle cx="279.5" cy="216.5" r="19" fill="url(#g)"/><circle cx="279.5" cy="216.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="231.5" cy="267.5" r="19" fill="url(#h)"/><circle cx="231.5" cy="267.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><circle cx="327" cy="267.5" r="19" fill="url(#i)"/><circle cx="327" cy="267.5" r="19" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path marker-end="url(#e)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M243.012 174.799l17.457 19.951m31.981 35.654l14.853 15.948m-40.825-16.016l-15.171 16.119"/></g></svg>
\ No newline at end of file diff --git a/devtools/docs/user/memory/dominators_view/dominators-1.png b/devtools/docs/user/memory/dominators_view/dominators-1.png Binary files differnew file mode 100644 index 0000000000..163a80016c --- /dev/null +++ b/devtools/docs/user/memory/dominators_view/dominators-1.png diff --git a/devtools/docs/user/memory/dominators_view/dominators-10.png b/devtools/docs/user/memory/dominators_view/dominators-10.png Binary files differnew file mode 100644 index 0000000000..e6688060af --- /dev/null +++ b/devtools/docs/user/memory/dominators_view/dominators-10.png diff --git a/devtools/docs/user/memory/dominators_view/dominators-2.png b/devtools/docs/user/memory/dominators_view/dominators-2.png Binary files differnew file mode 100644 index 0000000000..99b7db7b09 --- /dev/null +++ b/devtools/docs/user/memory/dominators_view/dominators-2.png diff --git a/devtools/docs/user/memory/dominators_view/dominators-3.png b/devtools/docs/user/memory/dominators_view/dominators-3.png Binary files differnew file mode 100644 index 0000000000..2d380f6e21 --- /dev/null +++ b/devtools/docs/user/memory/dominators_view/dominators-3.png diff --git a/devtools/docs/user/memory/dominators_view/dominators-4.png b/devtools/docs/user/memory/dominators_view/dominators-4.png Binary files differnew file mode 100644 index 0000000000..d3d5eef59c --- /dev/null +++ b/devtools/docs/user/memory/dominators_view/dominators-4.png diff --git a/devtools/docs/user/memory/dominators_view/dominators-5.png b/devtools/docs/user/memory/dominators_view/dominators-5.png Binary files differnew file mode 100644 index 0000000000..41a03488e9 --- /dev/null +++ b/devtools/docs/user/memory/dominators_view/dominators-5.png diff --git a/devtools/docs/user/memory/dominators_view/dominators-6.png b/devtools/docs/user/memory/dominators_view/dominators-6.png Binary files differnew file mode 100644 index 0000000000..a3d3026eb2 --- /dev/null +++ b/devtools/docs/user/memory/dominators_view/dominators-6.png diff --git a/devtools/docs/user/memory/dominators_view/dominators-7.png b/devtools/docs/user/memory/dominators_view/dominators-7.png Binary files differnew file mode 100644 index 0000000000..160f205391 --- /dev/null +++ b/devtools/docs/user/memory/dominators_view/dominators-7.png diff --git a/devtools/docs/user/memory/dominators_view/dominators-8.png b/devtools/docs/user/memory/dominators_view/dominators-8.png Binary files differnew file mode 100644 index 0000000000..e9512b9b05 --- /dev/null +++ b/devtools/docs/user/memory/dominators_view/dominators-8.png diff --git a/devtools/docs/user/memory/dominators_view/dominators-9.png b/devtools/docs/user/memory/dominators_view/dominators-9.png Binary files differnew file mode 100644 index 0000000000..af396abc21 --- /dev/null +++ b/devtools/docs/user/memory/dominators_view/dominators-9.png diff --git a/devtools/docs/user/memory/dominators_view/index.rst b/devtools/docs/user/memory/dominators_view/index.rst new file mode 100644 index 0000000000..a0e2c9db87 --- /dev/null +++ b/devtools/docs/user/memory/dominators_view/index.rst @@ -0,0 +1,174 @@ +=============== +Dominators view +=============== + +The Dominators view is new in Firefox 46. + +Starting in Firefox 46, the Memory tool includes a new view called the Dominators view. This is useful for understanding the "retained size" of objects allocated by your site: that is, the size of the objects themselves plus the size of the objects that they keep alive through references. + +If you already know what shallow size, retained size, and dominators are, skip to the Dominators UI section. Otherwise, you might want to review the article on :doc:`Dominators concepts <../dominators/index>`. + + +Dominators UI +************* + +To see the Dominators view for a snapshot, select "Dominators" in the "View" drop-down list. It looks something like this: + +.. image:: dominators-1.png + :class: center + +The Dominators view consists of two panels: + +- the :ref:`Dominators Tree panel <memory-dominators-view-dominators-tree-panel>` shows you which nodes in the snapshot are retaining the most memory +- the :ref:`Retaining Paths panel <memory-dominators-view-retaining-paths-panel>` (new in Firefox 47) shows the 5 shortest retaining paths for a single node. + +.. image:: dominators-2.png + :class: center + + +.. _memory-dominators-view-dominators-tree-panel: + +Dominators Tree panel +********************* + +The Dominators Tree tells you which objects in the snapshot are retaining the most memory. + +In the main part of the UI, the first row is labeled "GC Roots". Immediately underneath that is an entry for: + + +- Every GC root node. In Gecko, there is more than one memory graph, and therefore more than one root. There may be many (often temporary) roots. For example: variables allocated on the stack need to be rooted, or internal caches may need to root their elements. +- Any other node that's referenced from two different roots (since in this case, neither root dominates it). + + +Each entry displays: + + +- the retained size of the node, as bytes and as a percentage of the total +- the shallow size of the node, as bytes and as a percentage of the total +- the nodes's name and address in memory. + + +Entries are ordered by the amount of memory that they retain. For example: + +.. image:: dominators-3.png + :class: center + +In this screenshot we can see five entries under "GC Roots". The first two are Call and Window objects, and retain about 21% and 8% of the total size of the memory snapshot, respectively. You can also see that these objects have a relatively tiny "Shallow Size", so almost all of the retained size is in the objects that they dominate. + +Immediately under each GC root, you'll see all the nodes for which this root is the :ref:`immediate dominator <memory-dominators-immediate-dominator>`. These nodes are also ordered by their retained size. + +For example, if we click on the first Window object: + +.. image:: dominators-4.png + :class: center + +We can see that this Window dominates a CSS2Properties object, whose retained size is 2% of the total snapshot size. Again the shallow size is very small: almost all of its retained size is in the nodes that it dominates. By clicking on the disclosure arrow next to the Function, we can see those nodes. + +In this way you can quickly get a sense of which objects retain the most memory in the snapshot. + +You can use :kbd:`Alt` + :kbd:`click` to expand the whole graph under a node. + +Call Stack +********** + +In the toolbar at the top of the tool is a dropdown called "Label by": + +.. image:: dominators-5.png + :class: center + +By default, this is set to "Type". However, you can set it instead to "Call Stack" to see exactly where in your code the objects are being allocated. + +.. note:: + + This option is called "Allocation Stack" in Firefox 46. + + +To enable this, you must check the box labeled "Record call stacks" *before* you run the code that allocates the objects. Then take a snapshot, then select "Call Stack" in the "Label by" drop-down. + +Now the node's name will contain the name of the function that allocated it, and the file, line number and character position of the exact spot where the function allocated it. Clicking the file name will take you to that spot in the Debugger. + + +.. note:: + + Sometimes you'll see "(no stack available)" here. In particular, allocation stacks are currently only recorded for objects, not for arrays, strings, or internal structures. + + +.. _memory-dominators-view-retaining-paths-panel: + +Retaining Paths panel +********************* + +The Retaining Paths panel is new in Firefox 47. + +The Retaining Paths panel shows you, for a given node, the 5 shortest paths back from this node to a GC root. This enables you to see all the nodes that are keeping the given node from being garbage-collected. If you suspect that an object is being leaked, this will show you exactly which objects are holding a reference to it. + +To see the retaining paths for a node, you have to select the node in the Dominators Tree panel: + +.. image:: dominators-6.png + :class: center + + +Here, we've selected an object, and can see a single path back to a GC root. + +The ``Window`` GC root holds a reference to an ``HTMLDivElement`` object, and that holds a reference to an ``Object``, and so on. If you look in the Dominators Tree panel, you can trace the same path there. If either of these references were removed, the items below them could be garbage-collected. + +Each connection in the graph is labeled with the variable name for the referenced object. + +Sometimes there's more than one retaining path back from a node: + +.. image:: dominators-7.png + :class: center + + +Here there are three paths back from the ``DocumentPrototype`` node to a GC root. If one were removed, then the ``DocumentPrototype`` would still not be garbage-collected, because it's still retained by the other two path. + + +Example +******* + +Let's see how some simple code is reflected in the Dominators view. + +We'll use the :doc:`monster allocation example <../monster_example/index>`, which creates three arrays, each containing 5000 monsters, each monster having a randomly-generated name. + +Taking a snapshot +***************** + +To see what it looks like in the Dominators view: + +- load the page +- enable the Memory tool in the :ref:`Settings <tool-toolbox-settings>`, if you haven't already +- open the Memory tool +- check "Record call stacks" +- press the button labeled "Make monsters!" +- take a snapshot +- switch to the "Dominators" view + + +Analyzing the Dominators Tree +***************************** + +You'll see the three arrays as the top three GC roots, each retaining about 23% of the total memory usage: + +.. image:: dominators-8.png + :class: center + + +If you expand an array, you'll see the objects (monsters) it contains. Each monster has a relatively small shallow size of 160 bytes. This includes the integer eye- and tentacle-counts. Each monster has a bigger retained size, which is accounted for by the string used for the monster's name: + +.. image:: dominators-9.png + :class: center + +All this maps closely to the :ref:`memory graph we were expecting to see <memory-dominators-immediate-dominator>`. One thing you might be wondering, though, is: where's the top-level object that retains all three arrays? If we look at the Retaining Paths panel for one of the arrays, we'll see it: + +.. image:: dominators-10.png + :class: center + +Here we can see the retaining object, and even that this particular array is the array of ``fierce`` monsters. But the array is also rooted directly, so if the object were to stop referencing the array, it would still not be eligible for garbage collection. + +This means that the object does not dominate the array, and is therefore not shown in the Dominators Tree view. :ref:`See the relevant section of the Dominators concepts article <memory-dominators-multiple-paths>`. + + +Using the Call Stack view +************************* + +Finally, you can switch to the Call Stack view, see where the objects are being allocated, and jump to that point in the Debugger. diff --git a/devtools/docs/user/memory/index.rst b/devtools/docs/user/memory/index.rst new file mode 100644 index 0000000000..7a508015a1 --- /dev/null +++ b/devtools/docs/user/memory/index.rst @@ -0,0 +1,52 @@ +====== +Memory +====== + +The Memory tool lets you take a snapshot of the current tab's memory `heap <https://en.wikipedia.org/wiki/Memory_management#HEAP>`_. It then provides a number of views of the heap that can show you which objects account for memory usage and exactly where in your code you are allocating memory. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/DJLoq5E5ww0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +The basics +********** + +- :ref:`Opening the memory tool <memory-basic-operations-opening-the-memory-tool>` +- :ref:`Taking a heap snapshot <memory-basic-operations-taking-a-heap-snapshot>` +- :ref:`Comparing two snapshots <memory-basic-operations-comparing-snapshots>` +- :ref:`Deleting snapshots <memory-basic-operations-clearing-a-snapshot>` +- :ref:`Saving and loading snapshots <memory-basic-operations-saving-and-loading-snapshots>` +- :ref:`Recording call stacks <memory-basic-operations-recording-call-stacks>` + + +Analyzing snapshots +******************* + +The Tree map view is new in Firefox 48, and the Dominators view is new in Firefox 46. + +Once you've taken a snapshot, there are three main views the Memory tool provides: + + +- :doc:`the Tree map view <tree_map_view/index>` shows memory usage as a `treemap <https://en.wikipedia.org/wiki/Treemapping>`_. +- :doc:`the Aggregate view <aggregate_view/index>` shows memory usage as a table of allocated types. +- :doc:`the Dominators view <dominators_view/index>` shows the "retained size" of objects: that is, the size of objects plus the size of other objects that they keep alive through references. + + +If you've opted to record allocation stacks for the snapshot, the Aggregate and Dominators views can show you exactly where in your code allocations are happening. + +Concepts +******** + +- :doc:`Dominators <dominators_view/index>` + + +Example pages +************* + +Examples used in the Memory tool documentation. + +- :doc:`Monster example <monster_example/index>` +- :doc:`DOM allocation example <dom_allocation_example/index>` diff --git a/devtools/docs/user/memory/monster_example/index.rst b/devtools/docs/user/memory/monster_example/index.rst new file mode 100644 index 0000000000..93042541a1 --- /dev/null +++ b/devtools/docs/user/memory/monster_example/index.rst @@ -0,0 +1,80 @@ +=============== +Monster example +=============== + +This article describes a very simple web page that we'll use to illustrate some features of the Memory tool. + +You can try the site at https://firefox-devtools.github.io/performance-scenarios/js-allocs/alloc.html. Here's the code: + +.. code-block:: javascript + + var MONSTER_COUNT = 5000; + var MIN_NAME_LENGTH = 2; + var MAX_NAME_LENGTH = 48; + + function Monster() { + + function randomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + function randomName() { + var chars = "abcdefghijklmnopqrstuvwxyz"; + var nameLength = randomInt(MIN_NAME_LENGTH, MAX_NAME_LENGTH); + var name = ""; + for (var j = 0; j &lt; nameLength; j++) { + name += chars[randomInt(0, chars.length-1)]; + } + return name; + } + + this.name = randomName(); + this.eyeCount = randomInt(0, 25); + this.tentacleCount = randomInt(0, 250); + } + + function makeMonsters() { + var monsters = { + "friendly": [], + "fierce": [], + "undecided": [] + }; + + for (var i = 0; i < MONSTER_COUNT; i++) { + monsters.friendly.push(new Monster()); + } + + for (var i = 0; i < MONSTER_COUNT; i++) { + monsters.fierce.push(new Monster()); + } + + for (var i = 0; i < MONSTER_COUNT; i++) { + monsters.undecided.push(new Monster()); + } + + console.log(monsters); + } + + var makeMonstersButton = document.getElementById("make-monsters"); + makeMonstersButton.addEventListener("click", makeMonsters); + +The page contains a button: when you push the button, the code creates some monsters. Specifically: + + +- the code creates an object with three properties, each an array: + + - one for fierce monsters + - one for friendly monsters + - one for monsters who haven't decided yet. + + +- for each array, the code creates and appends 5000 randomly-initialized monsters. Each monster has: + + - a string, for the monster's name + - a number representing the number of eyes it has + - a number representing the number of tentacles it has. + +So the structure of the memory allocated on the JavaScript heap is an object containing three arrays, each containing 5000 objects (monsters), each object containing a string and two integers: + +.. image:: monsters.svg + :class: center diff --git a/devtools/docs/user/memory/monster_example/monsters.svg b/devtools/docs/user/memory/monster_example/monsters.svg new file mode 100644 index 0000000000..1ecb9b1f77 --- /dev/null +++ b/devtools/docs/user/memory/monster_example/monsters.svg @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" viewBox="62 78 464 484" width="464pt" height="484pt"><defs><linearGradient x1="0" x2="1" id="a" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#e5f2f2"/><stop offset="1" stop-color="#cff2f2"/></linearGradient><linearGradient id="b" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 103.25 304)"/><linearGradient id="c" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 231.25 136)"/><linearGradient id="d" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 359.25 136)"/><linearGradient id="e" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 359.25 80)"/><linearGradient id="f" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 359.25 192)"/><linearGradient id="g" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 484.75 81)"/><linearGradient id="h" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 484.75 136)"/><linearGradient id="j" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 231.25 304)"/><linearGradient id="k" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 359.25 304)"/><linearGradient id="l" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 359.25 248)"/><linearGradient id="m" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 359.25 360)"/><linearGradient id="n" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 484.75 249)"/><linearGradient id="o" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 484.75 304)"/><linearGradient id="p" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 231.25 472)"/><linearGradient id="q" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 359.25 472)"/><linearGradient id="r" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 359.25 416)"/><linearGradient id="s" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 359.25 528)"/><linearGradient id="t" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 484.75 417)"/><linearGradient id="u" xl:href="#a" gradientTransform="matrix(0 31.5 -31.5 0 484.75 472)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="i" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#000"><path d="M8 0L0-3v6z" fill="currentColor" stroke="currentColor"/></marker></defs><g fill="none"><path fill="url(#b)" d="M64.5 304H142v31.5H64.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M64.5 304H142v31.5H64.5z"/><text transform="translate(69.5 310.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="10.038" y="15" textLength="47.424">Object</tspan></text><path fill="url(#c)" d="M192.5 136H270v31.5h-77.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M192.5 136H270v31.5h-77.5z"/><text transform="translate(197.5 142.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="14.942" y="15" textLength="37.616">Array</tspan></text><path fill="url(#d)" d="M320.5 136H398v31.5h-77.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M320.5 136H398v31.5h-77.5z"/><text transform="translate(325.5 142.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="4.262" y="15" textLength="58.976">Monster</tspan></text><path fill="url(#e)" d="M320.5 80H398v31.5h-77.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M320.5 80H398v31.5h-77.5z"/><text transform="translate(325.5 86.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="4.262" y="15" textLength="58.976">Monster</tspan></text><path fill="url(#f)" d="M320.5 192H398v31.5h-77.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M320.5 192H398v31.5h-77.5z"/><text transform="translate(325.5 198.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="25.75" y="15" textLength="16">…</tspan></text><path fill="url(#g)" d="M446 81h77.5v31.5H446z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M446 81h77.5v31.5H446z"/><text transform="translate(451 87.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="12.566" y="15" textLength="42.368">String</tspan></text><path fill="url(#h)" d="M446 136h77.5v31.5H446z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M446 136h77.5v31.5H446z"/><text transform="translate(451 142.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="12.566" y="15" textLength="42.368">String</tspan></text><path d="M103.25 304V151.75h79.35m112.65 0h15.35m-15.35 0v-56h15.35m-40.6 56h25.25v56h15.35m87.4-112l38.101.439M398 151.75h38.1" marker-end="url(#i)" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path fill="url(#j)" d="M192.5 304H270v31.5h-77.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M192.5 304H270v31.5h-77.5z"/><text transform="translate(197.5 310.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="14.942" y="15" textLength="37.616">Array</tspan></text><path fill="url(#k)" d="M320.5 304H398v31.5h-77.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M320.5 304H398v31.5h-77.5z"/><text transform="translate(325.5 310.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="4.262" y="15" textLength="58.976">Monster</tspan></text><path fill="url(#l)" d="M320.5 248H398v31.5h-77.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M320.5 248H398v31.5h-77.5z"/><text transform="translate(325.5 254.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="4.262" y="15" textLength="58.976">Monster</tspan></text><path fill="url(#m)" d="M320.5 360H398v31.5h-77.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M320.5 360H398v31.5h-77.5z"/><text transform="translate(325.5 366.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="25.75" y="15" textLength="16">…</tspan></text><path fill="url(#n)" d="M446 249h77.5v31.5H446z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M446 249h77.5v31.5H446z"/><text transform="translate(451 255.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="12.566" y="15" textLength="42.368">String</tspan></text><path fill="url(#o)" d="M446 304h77.5v31.5H446z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M446 304h77.5v31.5H446z"/><text transform="translate(451 310.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="12.566" y="15" textLength="42.368">String</tspan></text><path d="M142 319.75h40.6m112.65 0h15.35m-15.35 0v-56h15.35m-40.6 56h25.25v56h15.35m87.4-112l38.101.439M398 319.75h38.1" marker-end="url(#i)" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/><path fill="url(#p)" d="M192.5 472H270v31.5h-77.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M192.5 472H270v31.5h-77.5z"/><text transform="translate(197.5 478.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="14.942" y="15" textLength="37.616">Array</tspan></text><path fill="url(#q)" d="M320.5 472H398v31.5h-77.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M320.5 472H398v31.5h-77.5z"/><text transform="translate(325.5 478.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="4.262" y="15" textLength="58.976">Monster</tspan></text><path fill="url(#r)" d="M320.5 416H398v31.5h-77.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M320.5 416H398v31.5h-77.5z"/><text transform="translate(325.5 422.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="4.262" y="15" textLength="58.976">Monster</tspan></text><path fill="url(#s)" d="M320.5 528H398v31.5h-77.5z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M320.5 528H398v31.5h-77.5z"/><text transform="translate(325.5 534.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="25.75" y="15" textLength="16">…</tspan></text><path fill="url(#t)" d="M446 417h77.5v31.5H446z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M446 417h77.5v31.5H446z"/><text transform="translate(451 423.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="12.566" y="15" textLength="42.368">String</tspan></text><path fill="url(#u)" d="M446 472h77.5v31.5H446z"/><path stroke="#000" stroke-linecap="round" stroke-linejoin="round" d="M446 472h77.5v31.5H446z"/><text transform="translate(451 478.75)" fill="#000"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="12.566" y="15" textLength="42.368">String</tspan></text><path d="M103.25 335.5v152.25h79.35m112.65 0h15.35m-15.35 0v-56h15.35m-40.6 56h25.25v56h15.35m87.4-112l38.101.439M398 487.75h38.1" marker-end="url(#i)" stroke="#000" stroke-linecap="round" stroke-linejoin="round"/></g></svg>
\ No newline at end of file diff --git a/devtools/docs/user/memory/tree_map_view/index.rst b/devtools/docs/user/memory/tree_map_view/index.rst new file mode 100644 index 0000000000..3ab11d733b --- /dev/null +++ b/devtools/docs/user/memory/tree_map_view/index.rst @@ -0,0 +1,47 @@ +============= +Tree map view +============= + +The Tree map view is new in Firefox 48. + +The Tree map view provides a visual representation of the snapshot, that helps you quickly get an idea of which objects are using the most memory. + +A treemap displays `"hierarchical (tree-structured) data as a set of nested rectangles" <https://en.wikipedia.org/wiki/Treemapping>`_. The size of the rectangles corresponds to some quantitative aspect of the data. + +For the treemaps shown in the Memory tool, things on the heap are divided at the top level into four categories: + + +- **objects**: JavaScript and DOM objects, such as `Function <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function>`_, `Object <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object>`_, or `Array <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array>`_, and DOM types like `Window <https://developer.mozilla.org/en-US/docs/Web/API/Window>`_ and `HTMLDivElement <https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement>`_. +- **scripts**: JavaScript sources loaded by the page. +- **strings** +- **other**: this includes internal `SpiderMonkey <https://spidermonkey.dev/>`_ objects. + + +Each category is represented with a rectangle, and the size of the rectangle corresponds to the proportion of the heap occupied by items in that category. This means you can quickly get an idea of roughly what sorts of things allocated by your site are using the most memory. + +Within top-level categories: + + +- **objects** is further divided by the object's type. +- **scripts** is further subdivided by the script's origin. It also includes a separate rectangle for code that can't be correlated with a file, such as JIT-optimized code. +- **other** is further subdivided by the object's type. + + +Here are some example snapshots, as they appear in the Tree map view: + +.. image:: treemap-domnodes.png + :class: center + + +This treemap is from the :doc:`DOM allocation example <../dom_allocation_example/index>`, which runs a script that creates a large number of DOM nodes (200 `HTMLDivElement <https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement>`_ objects and 4000 `HTMLSpanElement <https://developer.mozilla.org/en-US/docs/Web/API/HTMLSpanElement>`_ objects). You can see how almost all the heap usage is from the ``HTMLSpanElement`` objects that it creates. + +.. image:: treemap-monsters.png + :class: center + + +This treemap is from the :doc:`monster allocation example <../monster_example/index>`, which creates three arrays, each containing 5000 monsters, each monster having a randomly-generated name. You can see that most of the heap is occupied by the strings used for the monsters' names, and the objects used to contain the monsters' other attributes. + +.. image:: treemap-bbc.png + :class: center + +This treemap is from http://www.bbc.com/, and is probably more representative of real life than the examples. You can see the much larger proportion of the heap occupied by scripts, that are loaded from a large number of origins. diff --git a/devtools/docs/user/memory/tree_map_view/treemap-bbc.png b/devtools/docs/user/memory/tree_map_view/treemap-bbc.png Binary files differnew file mode 100644 index 0000000000..55552b8382 --- /dev/null +++ b/devtools/docs/user/memory/tree_map_view/treemap-bbc.png diff --git a/devtools/docs/user/memory/tree_map_view/treemap-domnodes.png b/devtools/docs/user/memory/tree_map_view/treemap-domnodes.png Binary files differnew file mode 100644 index 0000000000..1192e390da --- /dev/null +++ b/devtools/docs/user/memory/tree_map_view/treemap-domnodes.png diff --git a/devtools/docs/user/memory/tree_map_view/treemap-monsters.png b/devtools/docs/user/memory/tree_map_view/treemap-monsters.png Binary files differnew file mode 100644 index 0000000000..513adab923 --- /dev/null +++ b/devtools/docs/user/memory/tree_map_view/treemap-monsters.png diff --git a/devtools/docs/user/menu_button.png b/devtools/docs/user/menu_button.png Binary files differnew file mode 100644 index 0000000000..d8a58075dc --- /dev/null +++ b/devtools/docs/user/menu_button.png diff --git a/devtools/docs/user/migrating_from_firebug/index.rst b/devtools/docs/user/migrating_from_firebug/index.rst new file mode 100644 index 0000000000..7bb8b1083e --- /dev/null +++ b/devtools/docs/user/migrating_from_firebug/index.rst @@ -0,0 +1,351 @@ +====================== +Migrating from Firebug +====================== + +When migrating from Firebug to the Firefox Developer Tools, you may wonder where the features you loved in Firebug are available in the Developer Tools. The following list aims to help Firebug users to find their way into the Developer Tools. + +.. image:: logo-developer-quantum.png + :class: center + +.. rst-class:: center + + For the latest developer tools and features, try Firefox Developer Edition. + +.. raw:: html + + <a href="https://www.mozilla.org/en-US/firefox/developer/" style="width: 280px; display: block; margin-left: auto; margin-right: auto; padding: 10px; text-align: center; border-radius: 4px; background-color: #81BC2E; white-space: nowrap; color: white; text-shadow: 0px 1px 0px rgba(0, 0, 0, 0.25); box-shadow: 0px 1px 0px 0px rgba(0, 0, 0, 0.2), 0px -1px 0px 0px rgba(0, 0, 0, 0.3) inset;">Download Firefox Developer Edition</a> + + +General +******* + +Activation +---------- + +Firebug's activation is URL based respecting the `same origin policy <https://en.wikipedia.org/wiki/Same_origin_policy>`_. That means that when you open a page on the same origin in a different tab, Firebug gets opened automatically. And when you open a page of a different origin in the same tab, it closes automatically. The DevTools' activation on the other hand is tab based. That means, that when you open the DevTools in a tab, they stay open even when you switch between different websites. When you switch to another tab, though, they're closed. + + +Open the tools +-------------- + +Firebug can be opened by pressing F12. To open it to inspect an element it is possible to press :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`C` / :kbd:`Cmd` + :kbd:`Opt` + :kbd:`C`. The DevTools share the same shortcuts, but also provide :ref:`shortcuts for the different panels <keyboard-shortcuts-opening-and-closing-tools>`. E.g. the :doc:`Network Monitor <../network_monitor/index>` can be opened via :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`Q` / :kbd:`Cmd` + :kbd:`Opt` + :kbd:`Q`, the :doc:`Web Console <../web_console/index>` via :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`K` / :kbd:`Cmd` + :kbd:`Opt` + :kbd:`K` and the :doc:`Debugger <../debugger/index>` via :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`S` / :kbd:`Cmd` + :kbd:`Opt` + :kbd:`S`. + + +Web Console +*********** + +The :doc:`Web Console <../web_console/index>` is the equivalent of Firebug's Console panel. It shows log information associated with a web page and allows you to execute JavaScript expressions via its :doc:`command line <../web_console/the_command_line_interpreter/index>`. The display between both is somewhat different. This may be changed in `bug 1269730 <https://bugzilla.mozilla.org/show_bug.cgi?id=1269730>`_. + + +Filter log messages +------------------- + +Firebug offers two ways to filter log messages, via the options menu and via the filter buttons within the toolbar. The Developer Tools console offers similar functionality via the :ref:`filter buttons inside its toolbar <web_console_ui_tour_filtering_by_category>` — centralized at one place. + + +Command Line API +---------------- + +The Command Line API in Firebug provides some special functions for your convenience. The Developer Tools command line has :ref:`some functions in common <command_line_interpreter_helper_commands>`, but also has some other functions and misses others. + + +Console API +----------- + +To log things to the console from within the web page Firebug makes a Console API available within the page. The Developer Tools share the `same API <https://developer.mozilla.org/en-US/docs/Web/API/console>`_, so your ``console.*`` statements will continue to work. + + +Persist logs +------------ + +In Firebug you can click the *Persist* button within the toolbar to keep the logged messages between page navigations and reloads. In the DevTools this option is called :ref:`Enable persistent logs <settings-common-preferences>` and is available within the Toolbox Options panel. + + +Server logs +----------- + +Firebug extensions like FirePHP allow to log server-side messages to the Firebug console. This functionality is already :ref:`integrated into the DevTools <web_console_server>` using the `ChromeLogger <https://craig.is/writing/chrome-logger>`_ protocol and doesn't require any extensions to be installed. + + +Command history +--------------- + +The :ref:`command history <command_line_interpreter_execution_history>` available through a button in Firebug's command line, is available by pressing :kbd:`↑`/:kbd:`↓` within the DevTools command line. + + +Inspect object properties +------------------------- + +By clicking on an object logged within the console you can inspect the object's properties and methods within the DOM panel. In the Firefox DevTools you can also inspect the objects. The difference is that they :ref:`show the properties and methods within a side panel inside the Web Console <web_console_rich_output_examining_object_properties>`. + + +Show network requests +--------------------- + +The Console panel in Firebug allows to log `AJAX <https://developer.mozilla.org/en-US/docs/Glossary/AJAX>`_ requests (aka `XMLHttpRequest <https://developer.mozilla.org/en-US/docs/Glossary/XHR_(XMLHttpRequest)>`_). This option is also available within the DevTools Web Console via the *Net* > *XHR*. Furthermore, the Web Console even allows to display all other network requests via *Net* > *Log*. + + +View JSON and XML structures +---------------------------- + +To view JSON and XML responses of `AJAX <https://developer.mozilla.org/en-US/docs/Glossary/AJAX>`_ requests, Firebug has special tabs when expanding the request within the Console panel. The DevTools Web Console shows those structures directly under the "Response" tab. + + +Multi-line command line +----------------------- + +Firebug's console has a multi-line command line called Command Editor. The DevTools have a :ref:`side panel <command_line_interpreter_multi_line_mode>` like the Command Editor. + + +Response preview +---------------- + +There is a *Preview* tab when a network request logged to the console is expanded in Firebug. The Web Console displays a preview within the *Response* tab. It is currently missing the preview for HTML, XML and SVG, though, which is tracked in `bug 1247392 <https://bugzilla.mozilla.org/show_bug.cgi?id=1247392>`_ and `bug 1262796 <https://bugzilla.mozilla.org/show_bug.cgi?id=1262796>`_, but when you click on the URL of the request you switch to the :doc:`Network Monitor <../network_monitor/index>`, which has a *Preview* tab. + + +Inspector +********* + +Firebug has an HTML panel, which allows to edit HTML/XML/SVG and the CSS related to it. Within the DevTools this functionality is served by the :doc:`Page Inspector <../page_inspector/index>`. + + +Edit HTML +--------- + +Within the Page Inspector the tag attributes and the contents can be edited inline just like in Firebug. Beyond that it allows to edit the tag names inline. + +You can also edit the HTML directly. In Firebug you do this by right-clicking a node and clicking Edit HTML... in the context menu. In the DevTools this option is also available via the context menu. There the option is called :ref:`Edit As HTML <page-inspector-how-to-examine-and-edit-html-editing_html>`. Only the live preview of changes is currently missing, which is tracked in `bug 1067318 <https://bugzilla.mozilla.org/show_bug.cgi?id=1067318>`_ and `bug 815464 <https://bugzilla.mozilla.org/show_bug.cgi?id=815464>`_. + + +Copy HTML and related information +--------------------------------- + +Firebug's HTML panel allows to copy the inner and outer HTML of an element as well as the CSS and XPath to it via the context menu of an element. The Page Inspector provides the same functionality except copying XPaths. This is covered by `bug 987877 <https://bugzilla.mozilla.org/show_bug.cgi?id=987877>`_. + + +Edit CSS +-------- + +Both tools allow to view and edit the CSS rules related to the element selected within the node view in a similar way. Firebug has a Style side panel for this, the DevTools have a :doc:`Rules side panel <../page_inspector/how_to/examine_and_edit_css/index>`. + +In Firebug you add new rules by right-clicking and choosing *Add Rule...* from the context menu. The DevTools also have a context menu option for that named :ref:`Add New Rule and additionally have a + button <page_inspector_how_to_examine_and_edit_css_add_rules>` within the Rules panel's toolbar to create new rules. + +To edit element styles, i.e. the CSS properties of the `style <https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#attr-style>`_ attribute of an element, in Firebug you have to right-click into the Style side panel and choose Edit Element Style... from the context menu. The DevTools display an :ref:`element {} rule <page_inspector_how_to_examine_and_edit_css_element_rule>` for this purpose, which requires a single click into it to start editing the properties. + + +Auto-completion of CSS +---------------------- + +As in Firebug, the Rules view provides an auto-completion for the CSS property names and their values. A few property values are not auto-completed yet, which is tracked in `bug 1337918 <https://bugzilla.mozilla.org/show_bug.cgi?id=1337918>`_. + + +Copy & paste CSS +---------------- + +Firebug's Style side panel as well as the DevTools' Rules side panel provide options within their context menus to copy the CSS rule or the style declarations. The DevTools additionally provide an option to copy the selector of a rule and copy disabled property declarations as commented out. They are missing the option to copy the whole style declaration, though this can be achieved by selecting them within the panel and copying the selection by pressing :kbd:`Ctrl` + :kbd:`C` or via the context menu. + +The Rules side panel of the DevTools is smarter when it comes to pasting CSS into it. You can paste whole style declarations into an existing rule property declarations which are commented out are automatically disabled. + + +Toggle pseudo-classes +--------------------- + +Firebug lets you toggle the CSS `pseudo-classes <https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes>`_ `:hover <https://developer.mozilla.org/en-US/docs/Web/CSS/:hover>`_, `:active <https://developer.mozilla.org/en-US/docs/Web/CSS/:active>`_ and `:focus <https://developer.mozilla.org/en-US/docs/Web/CSS/:focus>`_ for an element via the options menu of the Style side panel. In the DevTools there are two ways to do the same. The first one is to toggle them via the pseudo-class panel within the Rules side panel. The second one is to right-click and element within the node view and toggle the pseudo-classes via the :ref:`context menu <page_inspector_how_to_examine_and_edit_html_context_menu_reference>`. + + +Examine CSS shorthand properties +-------------------------------- + +CSS `shorthand properties <https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties>`_ can be split into their related longhand properties by setting the option *Expand Shorthand Properties* within the Style side panel. The DevTools' Rules panel is a bit smarter and allows you to expand individual shorthand properties by clicking the twisty besides them. + + +Only show applied styles +------------------------ + +The Style side panel in Firebug has an option to display only the properties of a CSS rule that are applied to the selected element and hide all overwritten styles. There is no such feature in the :doc:`Rules side panel <../page_inspector/how_to/examine_and_edit_css/index>` of the DevTools, but it is requested in `bug 1335327 <https://bugzilla.mozilla.org/show_bug.cgi?id=1335327>`_. + + +Inspect box model +----------------- + +In Firebug the `box model <https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model>`_ can be inspected via the Layout side panel. In the DevTools the :doc:`box model is part of the Computed side panel <../page_inspector/how_to/examine_and_edit_the_box_model/index>`. Both tools highlight the different parts of the box model within the page when hovering them in the box model view. Also, both tools allow you to edit the different values inline via a click on them. + + +Inspect computed styles +----------------------- + +The computed values of CSS properties are displayed within the DevTools' :ref:`Computed side panel <page_inspector_how_to_examine_and_edit_css_examine_computed_css>` like within Firebug's Computed side panel. The difference is that in the DevTools the properties are always listed alphabetically and not grouped (see `bug 977128 <https://bugzilla.mozilla.org/show_bug.cgi?id=977128>`_) and there is no option to hide the Mozilla specific styles, therefore there is an input field allowing to filter the properties. + + +Inspect events +-------------- + +Events assigned to an element are displayed in the Events side panel in Firebug. In the DevTools they are shown when clicking the small 'ev' icon besides an element within the node view. Both tools allow to display wrapped event listeners (e.g. listeners wrapped in jQuery functions). To improve the UI of the DevTools, there is also a request to add an Events side panel to them like the one in Firebug (see `bug 1226640 <https://bugzilla.mozilla.org/show_bug.cgi?id=1226640>`_). + + +Stop script execution on DOM mutation +------------------------------------- + +In Firebug you can break on DOM mutations, that means that when an element is changed, the script execution is stopped at the related line within the JavaScript file, which caused the change. This feature can globally be enabled via the *Break On Mutate* button, or individually for each element and for different types of changes like attribute changes, content changes or element removal. Unfortunately, the DevTools do not have this feature yet (see `bug 1004678 <https://bugzilla.mozilla.org/show_bug.cgi?id=1004678>`_). To stop the script execution there, you need to set a breakpoint on the line with the modification within the :doc:`Debugger panel <../debugger/index>`. + + +Search for elements via CSS selectors or XPaths +----------------------------------------------- + +Firebug allows to search for elements within the HTML panel via CSS selectors or XPaths. Also the :ref:`DevTools' Inspector panel allows to search for CSS selectors <page_inspector_how_to_examine_and_edit_html_searching>`. It even displays a list with matching IDs or classes. Searching by XPaths is not supported though (see `bug 963933 <https://bugzilla.mozilla.org/show_bug.cgi?id=963933>`_). + + +Debugger +******** + +What's the Script panel in Firebug, is the :doc:`Debugger panel <../debugger/index>` in the DevTools. Both allow you to debug JavaScript code executed on a website. + + +Switch between sources +---------------------- + +Firebug has a Script Location Menu listing all JavaScript sources related to the website. Those sources can be static, i.e. files, or they can be dynamically generated (i.e. scripts executed via event handlers, ``eval()``, ``new Function()``, etc.). In the DevTools' Debugger panel the scripts are listed at the left side within the :ref:`Sources side panel <debugger-ui-tour-source-list-pane>`. Dynamically generated scripts are only listed there when they are :doc:`named via a //# sourceURL comment <../debugger/how_to/debug_eval_sources/index>`. + + +Managing breakpoints +-------------------- + +In Firebug you can set different types of breakpoints, which are all listed within the Breakpoints side panel. In the DevTools the breakpoints are shown below each script source within the :ref:`Sources side panel <debugger-ui-tour-source-list-pane>`. Those panels allow you to enable and disable single or all breakpoints and to remove single breakpoints or all of them at once. They do currently only allow to set script breakpoints. XHR, DOM, Cookie and Error breakpoints are not supported yet (see `bug 821610 <https://bugzilla.mozilla.org/show_bug.cgi?id=821610>`_, `bug 1004678 <https://bugzilla.mozilla.org/show_bug.cgi?id=1004678>`_, `bug 895893 <https://bugzilla.mozilla.org/show_bug.cgi?id=895893>`_ and `bug 1165010 <https://bugzilla.mozilla.org/show_bug.cgi?id=1165010>`_). While there are no breakpoints for single JavaScript errors, there is a setting *Pause on Exceptions* within the :ref:`Debugger panel options <settings-debugger>`. + + +Step through code +----------------- + +Once the script execution is stopped, you can step through the code using the Continue (:kbd:`F8`), Step Over (:kbd:`F10`), Step Into (:kbd:`F11`) and Step Out (:kbd:`Shift` + :kbd:`F11`) options. They work the same in both tools. + + +Examine call stack +------------------ + +When the script execution is paused, Firebug displays the function call stack within its Stack side panel. In there the functions are listed together with their call parameters. In the DevTools the function call stack is shown within the :ref:`Call Stack side panel <debugger-ui-tour-call-stack>`. To see the call parameters in the DevTools, you need to have a look at the :doc:`Variables side panel <../debugger/how_to/set_watch_expressions/index>`. + + +Examine variables +----------------- + +The Watch side panel in Firebug displays the `window <https://developer.mozilla.org/en-US/docs/Web/API/Window>`_ object (the global scope) by default. With the script execution halted it shows the different variable scopes available within the current call stack frame. Furthermore, it allows you to add and manipulate watch expressions. The DevTools have a :doc:`Variables side panel <../debugger/how_to/set_watch_expressions/index>`, which works basically the same. The main difference is that it is empty when the script execution is not stopped, i.e. it doesn't display the ``window`` object. Though you can inspect that object either via the :doc:`DOM property viewer <../dom_property_viewer/index>` or via the :doc:`Web Console <../web_console/index>`. + + +Style Editor +************ + +The :doc:`Style Editor <../style_editor/index>` in the Firefox DevTools allows you to examine and edit the different CSS style sheets of a page like Firebug's CSS panel does it. In addition to that it allows to create new style sheets and to import existing style sheets and apply them to the page. It also allows you to toggle individual style sheets. + + +Switch between sources +---------------------- + +The CSS panel of Firebug allows to switch between different CSS sources using the CSS Location Menu. The Style Editor has a :ref:`sidebar <style-editor-the-style-sheet-pane>` for this purpose. + + +Edit a style sheet +------------------ + +Firebug's CSS panel offers three different ways for editing style sheets. The default one is to edit them inline like within the Style side panel. Furthermore it has a Source and a Live Edit mode, which allow to edit the selected style sheet like within a text editor. The Style Editor of the DevTools only has one way to edit style sheets, which corresponds to Firebug's Live Edit mode. + + +Try out CSS selectors +--------------------- + +Firebug's Selectors side panel provides a way to validate a CSS selector. It lists all elements matching the entered selector. The DevTools don't have this feature yet, but it's requested in `bug 1323746 <https://bugzilla.mozilla.org/show_bug.cgi?id=1323746>`_. + + +Searching within the style sheets +--------------------------------- + +Firebug allows to search within the style sheets via the search field. The Style Editor in the DevTools also provides a way to search within a style sheet, though there is currently no option to search within multiple sheets (see `bug 889571 <https://bugzilla.mozilla.org/show_bug.cgi?id=889571>`_ and also not via a regular expression (see `bug 1362030 <https://bugzilla.mozilla.org/show_bug.cgi?id=1362030>`_). + + +Performance Tool +**************** + +Firebug allows to profile JavaScript performance via the "Profile" button within the Console panel or the ``console.profile()`` and ``console.profileEnd()`` commands. The DevTools provide advanced tooling regarding performance profiling. A profile can be created via `console.profile() <https://developer.mozilla.org/en-US/docs/Web/API/console/profile>`_ and `console.profileEnd() <https://developer.mozilla.org/en-US/docs/Web/API/console/profileEnd>`_ like in Firebug or via the "Start Recording Performance" button in the :doc:`Performance Tool <../performance/index>`. The output of the :doc:`Call Tree <../performance/call_tree/index>` is the one that comes nearest to the output in Firebug, but the Performance panel provides much more information than just the JavaScript performance. E.g. it also provides information about HTML parsing or layout. + +This is the part where Firebug and the DevTools differ the most, because the outputs are completely different. While Firebug focuses on JavaScript performance and provides detailed information about JavaScript function calls during the profiling session, the Performance Tool in the DevTools offers a broad spectrum of information regarding a website's performance but doesn't go into detail regarding JavaScript function calls. + + +View JavaScript call performance +-------------------------------- + +What comes nearest to Firebug's profiler output is the :doc:`Call Tree view <../performance/index>` in the Performance panel. Like in Firebug it lists the total execution time of each function call under *Total Time* as well as the number of calls under *Samples*, the time spent within the function under *Self Time* and the related percentages in reference to the total execution time. + + +.. note:: + + The times and percentages listed in the DevTools' Call Tree view is not equivalent to the ones shown in Firebug, because it uses different APIs sampling the execution of the JavaScript code. + + +Jump to function declaration +---------------------------- + +Like in Firebug's profiler output the :doc:`Call Tree view <../performance/call_tree/index>` of the DevTools' Performance Tool allows to jump to the line of code where the called JavaScript function is defined. In Firebug the source link to the function is located at the right side of the Console panel output while within the DevTools the link is placed on the right side within the Call Tree View. + + +Network Monitor +*************** + +To monitor network requests Firebug provides a Net panel. The Firefox DevTools allow to inspect the network traffic using the :doc:`Network Monitor <../network_monitor/index>`. Both tools provide similar information including a timeline showing the request and response times of the network requests. + + +Inspect request information +--------------------------- + +Both Firebug and the Firefox DevTools' Network Monitor allow you to inspect the information about a request by clicking on it. The only difference is that Firebug shows the information below the request while the Network Monitor displays it within a side panel. + +In both tools there are different tabs containing different kinds of information for the selected request. They contain a *Headers*, *Params*, *Response* and *Cookies* panel. A preview of the response is shown within specifically named panels like *HTML*. The Network Monitor has a *Preview* panel for this purpose. It doesn't provide information about the cached data yet (see `bug 859051 <https://bugzilla.mozilla.org/show_bug.cgi?id=859051>`_), but provides a *Security* tab in addition to Firebug's information and a *Timings* tab showing detailed information about the network timings. + + +View request timings +-------------------- + +Firebug offers detailed information about the network timings related to a request by hovering the Timeline column within its Net panel. The Network Monitor shows this information within a :ref:`Timings side panel <network-monitor-request-details-timings-tab>` when you select a request. + + +View remote address +------------------- + +The remote address of a request is shown within the Remote IP column within Firebug. In the Network Monitor the address is shown at *Remote Address* in the *Headers* tab when a request is selected. + + +Search within requests +---------------------- + +The search field within Firebug allows to search within the requests. The search field in the Firefox DevTools filters the requests by the entered string. + +Firebug allowed to search within the response body of the network requests by checking *Response Bodies* within its search field options. This feature is not available yet within the Network Monitor, but it's requested in `bug 1334408 <https://bugzilla.mozilla.org/show_bug.cgi?id=1334408>`_. While response bodies can't be searched yet, the Network Monitor allows to :ref:`filter by different request properties <request-list-filtering-by-properties>`. + + +Storage Inspector +***************** + +The Cookies panel in Firebug displays information related to the cookies created by a page and allows to manipulate the information they store. Within the DevTools this functionality is located within the :doc:`Storage Inspector <../storage_inspector/index>`. In contrast to Firebug the Storage Inspector not only allows to inspect cookies but also other kinds of storages like the local and session storage, the cache and `IndexedDB <https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API>`_ databases. + + +Inspect cookies +--------------- + +All cookies related to a website are listed inside the Cookies panel in Firebug. Inside the DevTools, the cookies are grouped by domain under the Cookies section within the :doc:`Storage Inspector <../storage_inspector/index>`. Both show pretty much the same information per cookie, i.e. the name, value, domain, path, expiration date and whether the cookie is HTTP-only. + +The DevTools don't show by default whether a cookie is secure, but this can be enabled by right-clicking the table header and checking *Secure* from the context menu. Additionally, the DevTools allow to display the creation date of a cookie as well as when it was last accessed and whether it is host-only. + + +Edit cookies +------------ + +To edit a cookie in Firebug you have to right-click the cookie and choose *Edit* from the context menu. Then a dialog pops up allowing you to edit the data of the cookie and save it. Inside the Storage Inspector you just have to double-click the data you want to edit. Then an inline editor allows you to edit the value. + +Delete cookies +-------------- + +Firebug's Cookies panel allows you to delete all cookies of a website via the menu option *Cookies* > *Remove Cookies* or by pressing :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`O`. It also allows you to only remove session cookies via *Cookies* > *Remove Session Cookies* and to remove single cookies by right-clicking them and choosing *Delete*. The DevTools Storage Inspector allows to remove all cookies and a single one by right-clicking on a cookie and choosing *Delete All* resp. *Delete "<cookie name>"*. Additionally, it allows to delete all cookies from a specific domain via the context menu option *Delete All From "<domain name>"*. It currently does not allow to only delete session cookies (see `bug 1336934 <https://bugzilla.mozilla.org/show_bug.cgi?id=1336934>`_). + + +Feedback +******** + +We are always happy to respond to feedback and questions. If you have any queries or points of view, feel free to share them on our `DevTools Discourse Forum <https://discourse.mozilla.org/c/devtools>`_. diff --git a/devtools/docs/user/migrating_from_firebug/logo-developer-quantum.png b/devtools/docs/user/migrating_from_firebug/logo-developer-quantum.png Binary files differnew file mode 100644 index 0000000000..778bc9e2d3 --- /dev/null +++ b/devtools/docs/user/migrating_from_firebug/logo-developer-quantum.png diff --git a/devtools/docs/user/network_monitor/hamburger.png b/devtools/docs/user/network_monitor/hamburger.png Binary files differnew file mode 100644 index 0000000000..0a86806250 --- /dev/null +++ b/devtools/docs/user/network_monitor/hamburger.png diff --git a/devtools/docs/user/network_monitor/index.rst b/devtools/docs/user/network_monitor/index.rst new file mode 100644 index 0000000000..4de8266d67 --- /dev/null +++ b/devtools/docs/user/network_monitor/index.rst @@ -0,0 +1,62 @@ +=============== +Network Monitor +=============== + +The Network Monitor shows you all the HTTP requests Firefox makes (for example, when it loads a page, or due to `XMLHttpRequests <https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest>`_), how long each request takes, and details of each request. + +Opening the Network Monitor +*************************** + +There are a few different ways to open the Network Monitor: + +- Press :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`E` (:kbd:`Cmd` + :kbd:`Opt` + :kbd:`E` on a Mac). + +- Select the *Network* panel in the Web Developer Tools, accessible from the Browser Tools submenu + +- Click the wrench icon |image1|, which is in the main toolbar or under the Hamburger menu |image2|, then select "Network". + +.. |image1| image:: wrench-icon.png + :width: 20 + +.. |image2| image:: hamburger.png + :width: 20 + +The Network Monitor will appear at the bottom of the browser window. When it first opens, the Network Monitor does not show request information. The just opened tool looks like this: + +.. image:: network_monitor_new.png + :class: border + +Either action causes the Network Monitor to begin monitoring network activity. Once the tool is monitoring network requests, the display looks like this: + +.. image:: network_monitor.png + :class: border + +When it is actively monitoring activity, the Network Monitor records network requests any time the Toolbox is open, even if the Network Monitor itself is not selected. This means you can start debugging a page in, for example, the Web Console, then switch to the Network Monitor to see network activity without having to reload the page. + +UI overview +*********** + +The UI is divided into four main pieces: + +- The main screen contains the :doc:`toolbar <toolbar/index>`, the :doc:`network request list <request_list/index>`, and the :doc:`network request details pane <request_details/index>`: + +.. image:: network_monitor_closeup.png + :class: border + +- The :doc:`performance analysis <peformance_analysis/index>` view is a separate screen: + +.. image:: network_performance.png + :class: border + +Working with the network monitor +******************************** + +The following articles cover different aspects of using the network monitor: + +- :doc:`Toolbar <toolbar/index>` +- :doc:`Network request list <request_list/index>` +- :doc:`Network request details <request_details/index>` +- :doc:`Network traffic recording <performance_analysis/index>` +- :doc:`Throttling <throttling/index>` +- :doc:`Inspecting web sockets <inspecting_web_sockets/index>` +- :doc:`Inspecting server-sent events <inspecting_server-sent_events/index>` diff --git a/devtools/docs/user/network_monitor/inspecting_server-sent_events/basic-sse-message-view.png b/devtools/docs/user/network_monitor/inspecting_server-sent_events/basic-sse-message-view.png Binary files differnew file mode 100644 index 0000000000..98b7d5c8e7 --- /dev/null +++ b/devtools/docs/user/network_monitor/inspecting_server-sent_events/basic-sse-message-view.png diff --git a/devtools/docs/user/network_monitor/inspecting_server-sent_events/customize-columns.png b/devtools/docs/user/network_monitor/inspecting_server-sent_events/customize-columns.png Binary files differnew file mode 100644 index 0000000000..e856d2a929 --- /dev/null +++ b/devtools/docs/user/network_monitor/inspecting_server-sent_events/customize-columns.png diff --git a/devtools/docs/user/network_monitor/inspecting_server-sent_events/index.rst b/devtools/docs/user/network_monitor/inspecting_server-sent_events/index.rst new file mode 100644 index 0000000000..a75666e618 --- /dev/null +++ b/devtools/docs/user/network_monitor/inspecting_server-sent_events/index.rst @@ -0,0 +1,63 @@ +============================= +Inspecting server-sent events +============================= + +`Server-sent events <https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events>`_ allow for an inversion of the traditional client-initiated web request model, with a server sending new data to a web page at any time by pushing messages. From Firefox 82 onwards, you can inspect server-sent events and their message contents using the :doc:`Network Monitor <../index>`. This article shows you how. + +Inspecting the SSE network activity +*********************************** + +When you are viewing a document that is receiving server-sent events, you can go to the Network Monitor, select the file that is sending the server-sent events, and view the received messages in the *Response* tab on the right-hand panel. + +.. image:: basic-sse-message-view.png + :class: border + + +At the top of the panel there is a trash can icon, which allows you to trash the messages sent so far, and a filter field in which you can enter a text string to filter the displayed messages by. + + +Viewing the message contents +**************************** + +Select one of the messages listed in the *Response* tab, and you'll see the message contents displayed at the bottom of that same tab. + +.. image:: see-message-detail-view.png + :class: border + +In the above example, you can see that JSON and raw data representations of the content are shown. For a plain text message, you'd just see a raw data section. + +The supported data formats are as follows: + +- Mercure protocol +- JSON + + +Customizing the displayed columns +********************************* + +For each message, you'll see *Data* and Time columns by default, but you can right-click on the table header to bring up a context menu in which you can toggle columns on and off, and reset it back to its original state. + +.. image:: customize-columns.png + :class: border + +The available columns are as follows: + +- *Data*: A summary of the contained message data. +- *Size*: The size of the message. +- *Time*: A timestamp representing when the message was sent. +- *Event Name*: The name of the event type that resulted in the message being sent (e.g. ```message``` or ```ping```). +- *Last Event ID*: The ID of the last event that was fired. +- *Retry*: The interval after which failed message will be resent. + + +Network Monitor features +************************ + +The following articles cover different aspects of using the network monitor: + +- :doc:`Toolbar <../toolbar/index>` +- :doc:`Network request list <../request_list/index>` +- :doc:`Network request details <../request_details/index>` +- :doc:`Network traffic recording <../performance_analysis/index>` +- :doc:`Throttling <../throttling/index>` +- :doc:`Inspecting web sockets <../inspecting_web_sockets/index>` diff --git a/devtools/docs/user/network_monitor/inspecting_server-sent_events/see-message-detail-view.png b/devtools/docs/user/network_monitor/inspecting_server-sent_events/see-message-detail-view.png Binary files differnew file mode 100644 index 0000000000..b6dcce5c15 --- /dev/null +++ b/devtools/docs/user/network_monitor/inspecting_server-sent_events/see-message-detail-view.png diff --git a/devtools/docs/user/network_monitor/inspecting_web_sockets/index.rst b/devtools/docs/user/network_monitor/inspecting_web_sockets/index.rst new file mode 100644 index 0000000000..f69edfa770 --- /dev/null +++ b/devtools/docs/user/network_monitor/inspecting_web_sockets/index.rst @@ -0,0 +1,121 @@ +====================== +Inspecting web sockets +====================== + +Since Firefox 71, the :doc:`Network Monitor <../index>` has had the ability to inspect `web socket <https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API>`_ connections. This article explores what functionality the Web Socket Inspector makes available. + +Accessing the inspector +*********************** + +When you are inspecting a web app that utilizes a web socket connection, the web socket requests are listed in the list of requests in the Network Monitor along with all other requests. + +.. image:: wsi-filiter.png + :alt: WS filter in the network inspector + +You can use the WS button to filter the list for just web socket connections. Only requests with the `101 status code <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/101>`_ (WebSocket Protocol Handshake) are visible, which indicates that the server is switching to a web socket connection. + +Clicking on a web socket request opens the usual sidebar to reveal additional details. Choose the **Response** tab to inspect web socket frames sent and received through the selected connection. + +.. image:: new-web-sockets.png + :alt: Messages panel in the web socket inspector + :class: border + +The live-updated table shows data for sent (green arrow) and received (red arrow) frames. Each frame expands on click, so you can inspect the formatted data. + +Pausing web socket traffic +************************** + +You can use the pause/resume button in the Network Monitor toolbar to stop intercepting web socket traffic. This allows you to capture only the frames that you are interested in. + +.. image:: ws-pause.png + :alt: Pausing the web socket inspector + +Filtering web socket frames +*************************** + +To focus on specific messages, frames can be filtered using the filter at the top of the *Response* panel. + +.. image:: ws_response_text_filter.png + :class: border + :alt: web socket frame filter + +There are also predefined filters, available in the tool bar of the Response pane, grouped into a selection list. + +.. image:: ws_filter_menu.png + :alt: Screenshot showing the filter menu for WebSocket messages + +The following filters are available: + +.. list-table:: + :widths: 20 80 + :header-rows: 0 + + * - **All** + - Displays all messages (by default, except control messages). + + * - **Sent** + - Displays only messages sent by the browser (by default, except control messages). + + * - **Received`** + - Displays only messages received from the server (by default, except control messages). + + * - **Control** + - (Available starting in Firefox 76). Displays messages for control frames (Ping, Pong, or Close). This filter can be combined with the others, to display, for example, only messages for control frames sent by the browser. + +Columns in the Response pane +**************************** + +In the **Response** pane, you can choose to show the following information about each frame: + +- Data +- Size +- Time +- OpCode +- MaskBit +- FinBit + +The *Data* and *Time* columns are visible by default, but you can customize the interface to see more columns by choosing which ones to show from the context menu that is opened by right-clicking in the table header. + + +.. image:: ws_message_columns.png + :class: border + :alt: columns in the messages panel + + +Expanding each message +********************** + +Selecting a message in the list shows a preview of the data being sent in that message, at the bottom of the Response pane. + +.. image:: ws_expand_message.png + :class: border + :alt: web socket payload preview + + +Supported WS protocols +********************** + +The inspector currently supports the following web socket protocols: + +- Plain JSON +- Socket.IO +- SockJS +- SignalR +- WAMP +- STOMP +- STOMP inside SockJS + +The payload based on those protocols is parsed and displayed as an expandable tree for easy inspection, although you can of course still see the raw data (as sent over the wire) as well. + +Network Monitor features +************************ + +The following articles cover different aspects of using the network monitor: + +- :doc:`Toolbar <../toolbar/index>` +- :doc:`Network request list <../request_list/index>` +- :doc:`Network request details <../request_details/index>` +- :doc:`Network traffic recording <../performance_analysis/index>` +- :doc:`Throttling <../throttling/index>` +- :doc:`Inspecting web sockets <../inspecting_web_sockets/index>` +- :doc:`Inspecting server-sent events <../inspecting_server-sent_events/index>` diff --git a/devtools/docs/user/network_monitor/inspecting_web_sockets/new-web-sockets.png b/devtools/docs/user/network_monitor/inspecting_web_sockets/new-web-sockets.png Binary files differnew file mode 100644 index 0000000000..1d9c8f3450 --- /dev/null +++ b/devtools/docs/user/network_monitor/inspecting_web_sockets/new-web-sockets.png diff --git a/devtools/docs/user/network_monitor/inspecting_web_sockets/ws-pause.png b/devtools/docs/user/network_monitor/inspecting_web_sockets/ws-pause.png Binary files differnew file mode 100644 index 0000000000..606c40ebcd --- /dev/null +++ b/devtools/docs/user/network_monitor/inspecting_web_sockets/ws-pause.png diff --git a/devtools/docs/user/network_monitor/inspecting_web_sockets/ws_expand_message.png b/devtools/docs/user/network_monitor/inspecting_web_sockets/ws_expand_message.png Binary files differnew file mode 100644 index 0000000000..d7dbc3f0b3 --- /dev/null +++ b/devtools/docs/user/network_monitor/inspecting_web_sockets/ws_expand_message.png diff --git a/devtools/docs/user/network_monitor/inspecting_web_sockets/ws_filter_menu.png b/devtools/docs/user/network_monitor/inspecting_web_sockets/ws_filter_menu.png Binary files differnew file mode 100644 index 0000000000..8d284f29db --- /dev/null +++ b/devtools/docs/user/network_monitor/inspecting_web_sockets/ws_filter_menu.png diff --git a/devtools/docs/user/network_monitor/inspecting_web_sockets/ws_message_columns.png b/devtools/docs/user/network_monitor/inspecting_web_sockets/ws_message_columns.png Binary files differnew file mode 100644 index 0000000000..93972466a6 --- /dev/null +++ b/devtools/docs/user/network_monitor/inspecting_web_sockets/ws_message_columns.png diff --git a/devtools/docs/user/network_monitor/inspecting_web_sockets/ws_response_text_filter.png b/devtools/docs/user/network_monitor/inspecting_web_sockets/ws_response_text_filter.png Binary files differnew file mode 100644 index 0000000000..f82692f22c --- /dev/null +++ b/devtools/docs/user/network_monitor/inspecting_web_sockets/ws_response_text_filter.png diff --git a/devtools/docs/user/network_monitor/inspecting_web_sockets/wsi-filiter.png b/devtools/docs/user/network_monitor/inspecting_web_sockets/wsi-filiter.png Binary files differnew file mode 100644 index 0000000000..bef54d39ca --- /dev/null +++ b/devtools/docs/user/network_monitor/inspecting_web_sockets/wsi-filiter.png diff --git a/devtools/docs/user/network_monitor/network_monitor.png b/devtools/docs/user/network_monitor/network_monitor.png Binary files differnew file mode 100644 index 0000000000..000bd945f9 --- /dev/null +++ b/devtools/docs/user/network_monitor/network_monitor.png diff --git a/devtools/docs/user/network_monitor/network_monitor_closeup.png b/devtools/docs/user/network_monitor/network_monitor_closeup.png Binary files differnew file mode 100644 index 0000000000..7037eb090a --- /dev/null +++ b/devtools/docs/user/network_monitor/network_monitor_closeup.png diff --git a/devtools/docs/user/network_monitor/network_monitor_new.png b/devtools/docs/user/network_monitor/network_monitor_new.png Binary files differnew file mode 100644 index 0000000000..24fba42a12 --- /dev/null +++ b/devtools/docs/user/network_monitor/network_monitor_new.png diff --git a/devtools/docs/user/network_monitor/network_performance.png b/devtools/docs/user/network_monitor/network_performance.png Binary files differnew file mode 100644 index 0000000000..44453cac8c --- /dev/null +++ b/devtools/docs/user/network_monitor/network_performance.png diff --git a/devtools/docs/user/network_monitor/performance_analysis/index.rst b/devtools/docs/user/network_monitor/performance_analysis/index.rst new file mode 100644 index 0000000000..f6eeacfc24 --- /dev/null +++ b/devtools/docs/user/network_monitor/performance_analysis/index.rst @@ -0,0 +1,35 @@ +==================== +Performance Analysis +==================== + +The Network Monitor includes a performance analysis tool, to help show you how long the browser takes to download the different parts of your site. + +Using the Performance analysis tool +*********************************** + +To run the performance analysis tool click the stopwatch icon in the :doc:`Toolbar <../toolbar/index>` + +(Alternatively, if you have only just opened the Network Monitor, so it's not yet populated with the list of requests, you'll get a stopwatch icon in the main window.) + +The Network Monitor then loads the site twice: once with an empty browser cache, and once with a primed browser cache. This simulates the first time a user visits your site, and subsequent visits. It displays the results for each run side by side, or vertically if the browser window is narrow: + +.. image:: network_performance.png + :class: border + +The results for each run are summarized in a table and a pie chart. The tables group resources by type, and show the total size of each resource and the total time it took to load them. The accompanying pie chart shows the relative size of each resource type. + +To get back to the Network Monitor's list of network requests click the "Back" button on the left. + +Clicking on a slice of the pie takes you to the Network Monitor for that run, with a filter automatically applied to see :ref:`only that resource type <request-list-filtering-requests>`. + +Network Monitor Features +************************ + +The following articles cover different aspects of using the network monitor: + +- :doc:`Toolbar <../toolbar/index>` +- :doc:`Network request list <../request_list/index>` +- :doc:`Network request details <../request_details/index>` +- :doc:`Network traffic recording <../recording/index/>` +- :doc:`Network traffic recording <../recording/index/>` +- :doc:`Throttling <../throttling/index>` diff --git a/devtools/docs/user/network_monitor/performance_analysis/network_performance.png b/devtools/docs/user/network_monitor/performance_analysis/network_performance.png Binary files differnew file mode 100644 index 0000000000..44453cac8c --- /dev/null +++ b/devtools/docs/user/network_monitor/performance_analysis/network_performance.png diff --git a/devtools/docs/user/network_monitor/recording/index.rst b/devtools/docs/user/network_monitor/recording/index.rst new file mode 100644 index 0000000000..379ac43f42 --- /dev/null +++ b/devtools/docs/user/network_monitor/recording/index.rst @@ -0,0 +1,34 @@ +========================= +Network monitor recording +========================= + +You can pause and resume the monitoring of network traffic using the pause button. + +Pausing and resume network traffic recording +******************************************** + +The Network Monitor has a button that pauses and resumes recording of the current page's network traffic. This is useful in situations where, for example, you are trying to get a stable view of a page for debugging purposes, but under normal circumstances the view keeps evolving due to persistent network requests. The pause button allows you to look at a certain snapshot. + +The button can be found at the far left of the main Network Monitor toolbar, and looks like a typical pause button — |image1|. + +.. |image1| image:: pause-icon.png + :width: 20 + +You can see it here in context: + +.. image:: play-pause-network-traffic.png + :class: border + +Once pressed, the button changes to a play icon, and you can toggle network traffic recording back on by pressing it again. + +Network Monitor features +************************ + +The following articles cover different aspects of using the network monitor: + +- :doc:`Toolbar <../toolbar/index>` +- :doc:`Network request list <../request_list/index>` +- :doc:`Network request details <../request_details/index>` +- :doc:`Network traffic recording <../recording/index/>` +- :doc:`Performance analysis <../performance_analysis/index>` +- :doc:`Throttling <../throttling/index>` diff --git a/devtools/docs/user/network_monitor/recording/pause-icon.png b/devtools/docs/user/network_monitor/recording/pause-icon.png Binary files differnew file mode 100644 index 0000000000..d8aa4d5c77 --- /dev/null +++ b/devtools/docs/user/network_monitor/recording/pause-icon.png diff --git a/devtools/docs/user/network_monitor/recording/play-pause-network-traffic.png b/devtools/docs/user/network_monitor/recording/play-pause-network-traffic.png Binary files differnew file mode 100644 index 0000000000..fa36656e2f --- /dev/null +++ b/devtools/docs/user/network_monitor/recording/play-pause-network-traffic.png diff --git a/devtools/docs/user/network_monitor/request_details/copy-response-headers-fx78.png b/devtools/docs/user/network_monitor/request_details/copy-response-headers-fx78.png Binary files differnew file mode 100644 index 0000000000..8a43bce10a --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/copy-response-headers-fx78.png diff --git a/devtools/docs/user/network_monitor/request_details/highlight-samesite-attribute.png b/devtools/docs/user/network_monitor/request_details/highlight-samesite-attribute.png Binary files differnew file mode 100644 index 0000000000..244ffabfa0 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/highlight-samesite-attribute.png diff --git a/devtools/docs/user/network_monitor/request_details/html_formatted_response.png b/devtools/docs/user/network_monitor/request_details/html_formatted_response.png Binary files differnew file mode 100644 index 0000000000..b40149f02b --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/html_formatted_response.png diff --git a/devtools/docs/user/network_monitor/request_details/html_raw_response.png b/devtools/docs/user/network_monitor/request_details/html_raw_response.png Binary files differnew file mode 100644 index 0000000000..7f9b399b31 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/html_raw_response.png diff --git a/devtools/docs/user/network_monitor/request_details/index.rst b/devtools/docs/user/network_monitor/request_details/index.rst new file mode 100644 index 0000000000..67a6fbc754 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/index.rst @@ -0,0 +1,501 @@ +======================= +Network request details +======================= + +The request details pane appears when you click on a network request in the request list. This pane provides more detailed information about the request. + +Network request details +*********************** + +Clicking on a row displays a new pane in the right-hand side of the network monitor, which provides more detailed information about the request. + +.. image:: network-details-fx78.png + :alt: Screenshot of the Network Request details pane, without callouts for the close-pane button and the detail tabs + :class: center + +.. note:: + + The screenshots and descriptions in this section reflect Firefox 78. Earlier versions appeared similarly, but might not include some functionality. + + +The tabs at the top of this pane enable you to switch between the following pages: + +- :ref:`Headers <network-monitor-request-details-headers-tab>` +- **Messages** (only for WebSocket items) +- :ref:`Request <network-monitor-request-details-request-tab>` +- :ref:`Response <network-monitor-request-details-response-tab>` +- :ref:`Cache <network-monitor-request-details-cache-tab>` +- :ref:`Timings <network-monitor-request-details-timings-tab>` +- :ref:`Security <network-monitor-request-details-security-tab>` +- :ref:`Stack trace <network-monitor-request-details-stack-trace-tab>` (only when the request has a stack trace, e.g. a script called by another script). + + +Clicking the icon at the right-hand end of the :doc:`toolbar <../toolbar/index>` closes the details pane and returns you to the list view. + + +.. _network-monitor-request-details-headers-tab: + +Headers tab +----------- + +The Headers tab has a toolbar, followed by three main sections. + +This includes: + + +- Information about the request + + - Status: The response status code for the request; click the "?" icon to go to the reference page for the status code. + - Version: The version of HTTP used. + - Transferred: The amount of data transferred for the request. + - Referrer policy: The value of the `Referrer-policy header <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy>`_. + +- **HTTP Response headers** +- **HTTP Request headers** + +Each section has a disclosure triangle to expand the section to show more information. + + +Headers toolbar +~~~~~~~~~~~~~~~ + +Using the Headers toolbar, you can: + + +- Filter the headers in the Response Headers and Request Headers sections. +- **Block** the domain involved in this request. The domain is added to the :ref:`Blocking sidebar <network_monitor_blocking_specific_urls>`. +- Resend the request. The **Resend** button opens a menu with two items: + + - **Resend**: Resends the request. + - **Edit and Resend**: Enables an editing mode, where you can modify the method, URL, request headers, or request body of the request. Click **Send** to send the modified request, or **Cancel** to cancel editing. + + +Request Information +~~~~~~~~~~~~~~~~~~~ + +The following information is shown only when the section is expanded: + +- **Scheme**: The scheme used in the URL +- **Host**: The server involved in the request +- **Filename**: The full path to the file requested +- **Address**: The IP address of the host + +The following information is shown in both the collapsed and the expanded states: + + +- **Status:** The `HTTP response code <https://developer.mozilla.org/en-US/docs/Web/HTTP/Status>`_ for the request. +- **Version**: The HTTP version used +- **Transferred**: The amount of data transferred with the request +- The **Referrer Policy**, which governs which referrer information, sent in the `Referer <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer>`_ header, should be included with requests. (See `Referrer-Policy <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy>`_ for a description of possible values) +- **Blocking**: If the request is to a site that is associated with a known tracker, an icon and a message are shown; otherwise, this field is not shown. + + +Response headers +~~~~~~~~~~~~~~~~ + +The response headers section shows details about the response. For each line in the response headers section, a question mark links to the documentation for that response header, if one is available. + +A **Raw** toggle button in the section heading controls whether the headers are shown with formatting, or as plain, unformatted text. + +.. note:: + Note that the keys in the response header are all in lowercase, while the request headers keys are not. `HTTP/2 requires that all headers be lowercase <https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2>`_; response headers are shown as they are received from the server. (There may be some exceptions, such as ``X-Firefox-Spdy``, which is added by Firefox.) + + +.. image:: response-headers-fx78.png + :alt: Screenshot showing the Request headers section of the Request details pane + :class: border + +You can copy some or all of the response header in JSON format by using the context menu: + +.. image:: copy-response-headers-fx78.png + :alt: Screenshot showing the Response headers pane, and its context menu with "Copy" and "Copy all" items + :class: border + +If you select **Copy**, a single key word, value pair is copied. If you select **Copy All**, the entire header is copied in JSON format, giving you something like this (after running the results through a JSON validator): + + +.. code-block:: json + + { + "Response headers (1.113 KB)": { + "headers": [ + { + "name": "accept-ranges", + "value": "bytes" + }, + { + "name": "age", + "value": "0" + }, + { + "name": "backend-timing", + "value": "D=74716 t=1560258099074460" + }, + { + "name": "cache-control", + "value": "private, must-revalidate, max-age=0" + }, + { + "name": "content-disposition", + "value": "inline; filename=api-result.js" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "content-length", + "value": "673" + }, + { + "name": "content-type", + "value": "text/javascript; charset=utf-8" + }, + { + "name": "date", + "value": "Tue, 11 Jun 2019 13:01:39 GMT" + }, + { + "name": "mediawiki-login-suppressed", + "value": "true" + }, + { + "name": "p3p", + "value": "CP=\"This is not a P3P policy! See https://en.wikipedia.org/wiki/Special:CentralAutoLogin/P3P for more info.\"" + }, + { + "name": "server", + "value": "mw1316.eqiad.wmnet" + }, + { + "name": "server-timing", + "value": "cache;desc=\"pass\"" + }, + { + "name": "strict-transport-security", + "value": "max-age=106384710; includeSubDomains; preload" + }, + { + "name": "vary", + "value": "Accept-Encoding,Treat-as-Untrusted,X-Forwarded-Proto,Cookie,Authorization,X-Seven" + }, + { + "name": "via", + "value": "1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)" + }, + { + "name": "x-analytics", + "value": "ns=-1;special=Badtitle;WMF-Last-Access=11-Jun-2019;WMF-Last-Access-Global=11-Jun-2019;https=1" + }, + { + "name": "x-cache", + "value": "cp1075 pass, cp1075 pass" + }, + { + "name": "x-cache-status", + "value": "pass" + }, + { + "name": "x-client-ip", + "value": "204.210.158.136" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "X-Firefox-Spdy", + "value": "h2" + }, + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-powered-by", + "value": "HHVM/3.18.6-dev" + }, + { + "name": "x-search-id", + "value": "esvan0r5bnnwscyk2wq09i1im" + }, + { + "name": "x-varnish", + "value": "766019457, 417549316" + } + ] + }, + "Request headers (665 B)": { + "headers": [ + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Accept-Encoding", + "value": "gzip, deflate, br" + }, + { + "name": "Accept-Language", + "value": "en-US,en;q=0.5" + }, + { + "name": "Connection", + "value": "keep-alive" + }, + { + "name": "Cookie", + "value": "WMF-Last-Access=11-Jun-2019; WMF-Last-Access-Global=11-Jun-2019; mwPhp7Seed=5c9; GeoIP=US:NY:Port_Jervis:41.38:-74.67:v4" + }, + { + "name": "DNT", + "value": "1" + }, + { + "name": "Host", + "value": "en.wikipedia.org" + }, + { + "name": "Referer", + "value": "https://www.wikipedia.org/" + }, + { + "name": "TE", + "value": "Trailers" + }, + { + "name": "User-Agent", + "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0" + } + ] + } + } + +Request headers +~~~~~~~~~~~~~~~ + +The Request headers section shows details about the request headers. For each line in the request headers section, a question mark links to the documentation for that request header, if one is available. + +A **Raw** toggle button in the section heading controls whether the headers are shown with formatting, or as plain, unformatted text. + +.. image:: request-headers-fx78.png + :alt: Screenshot showing the Request headers section of the Request details pane + :class: border + + +.. _network-monitor-request-details-cookies-tab: + +Cookies tab +----------- + +This tab lists full details of any cookies sent with the request or response: + +.. image:: network_cookies.png + :class: border + +As with headers, you can filter the list of cookies displayed. The full list of cookie attributes is shown—see the following screenshot showing Response cookies with further attributes shown. + +.. image:: highlight-samesite-attribute.png + :alt: cookies panel in firefox devtools network monitor, showing a number of cookie attributes including samesite + +The ``samesite`` attribute has been shown since Firefox 62 (`bug 1452715 <https://bugzilla.mozilla.org/show_bug.cgi?id=1452715>`_). + + +.. _network-monitor-request-details-request-tab: + +Request tab +----------- + +Request shows the complete request parameters, by default, in a formatted view: + +.. image:: json_formatted_request.png + :class: border + + +Switch the toggle button to have the raw view presented: + +.. image:: json_raw_request.png + :class: border + + +.. _network-monitor-request-details-response-tab: + +Response tab +------------ + +The complete content of the response. If the response is HTML, JS, or CSS, it will be shown as text: + +.. image:: html_formatted_response.png + :class: border + + +The toggle button for switching between raw and formatted response view has been implemented (`bug 1693147 <https://bugzilla.mozilla.org/show_bug.cgi?id=1693147>`_). The previous HTML example makes use of the formatted view. When the toggle button is turned on, the raw response view will be enabled: + +.. image:: html_raw_response.png + :class: border + + +If the response is JSON, it will be shown as an inspectable object: + +.. image:: json_formatted_response.png + :class: border + + +In the raw response view the response will be shown as a string: + +.. image:: json_raw_response.png + :class: border + + +If the response is an image, the tab displays a preview: + +.. image:: response_pane_image.png + :class: border + + +If the response is a web font, the tab also displays a preview: + +.. image:: response_font.png + :class: border + + +For network responses that are initiated by a `WebSocket <https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API>`_ connection, the details pane shows any associated messages. For more information, see :doc:`Inspecting web sockets <../inspecting_web_sockets/index>`. + + +.. _network-monitor-request-details-cache-tab: + +Cache tab +--------- + +If the response is cached (i.e. a 304), the Cache tab displays details about that cached resource. + +.. image:: response_cache.png + :class: border + +These details include: + +- **Last fetched:** The date the resource was last fetched +- **Fetched count:** The number of times in the current session that the resource has been fetched +- **Data size:** The size of the resource. +- **Last modified:** The date the resource was last modified. +- **Expires:** The date the resource expires. +- **Device:** The device the resource was fetched from (e.g. "disk"). + + +HTML preview +~~~~~~~~~~~~ + +If the response is HTML, a preview of the rendered HTML appears inside the Response tab, above the response payload. + + +.. _network-monitor-request-details-timings-tab: + +Timings tab +----------- + +The Timings tab provides information about how long each stage of a network request took, with a more detailed, annotated, view of the timeline bar, so it is easy to locate performance bottlenecks. + +.. image:: network-timings-tab.png + :class: border + + +This tab can include the following sections. + + +Queued, Started, Downloaded +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +New in Firefox 72, we now show the following timings at the top of the Timings tab, making dependency analysis a lot easier: + +- Queued: When the resource was queued for download. +- Started: When the resource started downloading. +- Downloaded: When the resource finished downloading. + + +.. note:: + Future versions will also show this information when entries in the network monitor timeline graph are moused over (see `bug 1580493 <https://bugzilla.mozilla.org/show_bug.cgi?id=1580493>`_). + + +Request Timing +~~~~~~~~~~~~~~ + +The *Request Timing* section breaks a network request down into the following subset of the stages defined in the `HTTP Archive <https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html>`_ specification: + + +.. list-table:: + :widths: 20 80 + :header-rows: 0 + + * - Name + - Description + + * - Blocked + - Time spent in a queue waiting for a network connection. + + The browser imposes a limit on the number of simultaneous connections that can be made to a single server. In Firefox this defaults to 6, but can be changed using the `network.http.max-persistent-connections-per-server <http://kb.mozillazine.org/Network.http.max-persistent-connections-per-server>`_ preference. If all connections are in use, the browser can't download more resources until a connection is released. + + * - DNS resolution + - Time taken to resolve a host name. + + * - Connecting + - Time taken to create a TCP connection. + + * - Sending + - Time taken to send the HTTP request to the server. + + * - Waiting + - Waiting for a response from the server. + + * - Receiving + - Time taken to read the entire response from the server (or cache). + + +Server Timing +~~~~~~~~~~~~~ + +New in Firefox 71, the *Server Timing* section lists any information provided in the `Server-Timing <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing>`_ header — this is used to surface any backend server timing metrics you've recorded (e.g. database read/write, CPU time, file system access, etc.). + +The header takes a series of descriptions and durations, which can be anything you like. In the above screenshot for example, the highlighted request's ``Server-Timing`` header contains 4 items — *data*, *markup*, *total*, and *miss*. + + +.. _network-monitor-request-details-security-tab: + +Security tab +------------ + +If the site is being served over HTTPS, you get an extra tab labeled "Security". This contains details about the secure connection used including the protocol, the cipher suite, and certificate details: + +.. image:: network_security.png + :alt: border + +The Security tab shows a warning for security weaknesses. Currently it warns you about two weaknesses: + +1. Using SSLv3 instead of TLS +2. Using the RC4 cipher + +.. image:: security-warning.png + :class: center + + +.. _network-monitor-request-details-stack-trace-tab: + +Stack trace tab +--------------- + +Stack traces are shown in the *Stack Trace* tab, for responses that have a stack trace of course. + +.. image:: network_stack_trace.png + :class: border + + +Network Monitor Features +************************ + +The following articles cover different aspects of using the network monitor: + +- :doc:`Toolbar <../toolbar/index>` +- :doc:`Network request list <../request_list/index>` +- :doc:`Network request details <../request_details/index>` +- :doc:`Network traffic recording <../performance_analysis/index>` +- :doc:`Throttling <../throttling/index>` +- :doc:`Inspecting web sockets <../inspecting_web_sockets/index>` diff --git a/devtools/docs/user/network_monitor/request_details/json_formatted_request.png b/devtools/docs/user/network_monitor/request_details/json_formatted_request.png Binary files differnew file mode 100644 index 0000000000..88c544bd9a --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/json_formatted_request.png diff --git a/devtools/docs/user/network_monitor/request_details/json_formatted_response.png b/devtools/docs/user/network_monitor/request_details/json_formatted_response.png Binary files differnew file mode 100644 index 0000000000..500d3597f4 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/json_formatted_response.png diff --git a/devtools/docs/user/network_monitor/request_details/json_raw_request.png b/devtools/docs/user/network_monitor/request_details/json_raw_request.png Binary files differnew file mode 100644 index 0000000000..de12a3d284 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/json_raw_request.png diff --git a/devtools/docs/user/network_monitor/request_details/json_raw_response.png b/devtools/docs/user/network_monitor/request_details/json_raw_response.png Binary files differnew file mode 100644 index 0000000000..d02e896d03 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/json_raw_response.png diff --git a/devtools/docs/user/network_monitor/request_details/network-details-fx78.png b/devtools/docs/user/network_monitor/request_details/network-details-fx78.png Binary files differnew file mode 100644 index 0000000000..c97f52c350 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/network-details-fx78.png diff --git a/devtools/docs/user/network_monitor/request_details/network-timings-tab.png b/devtools/docs/user/network_monitor/request_details/network-timings-tab.png Binary files differnew file mode 100644 index 0000000000..e5cbfa4432 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/network-timings-tab.png diff --git a/devtools/docs/user/network_monitor/request_details/network_cookies.png b/devtools/docs/user/network_monitor/request_details/network_cookies.png Binary files differnew file mode 100644 index 0000000000..c20a83539f --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/network_cookies.png diff --git a/devtools/docs/user/network_monitor/request_details/network_response.png b/devtools/docs/user/network_monitor/request_details/network_response.png Binary files differnew file mode 100644 index 0000000000..080c8c5730 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/network_response.png diff --git a/devtools/docs/user/network_monitor/request_details/network_security.png b/devtools/docs/user/network_monitor/request_details/network_security.png Binary files differnew file mode 100644 index 0000000000..5ca146c615 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/network_security.png diff --git a/devtools/docs/user/network_monitor/request_details/network_stack_trace.png b/devtools/docs/user/network_monitor/request_details/network_stack_trace.png Binary files differnew file mode 100644 index 0000000000..29ee5f91b6 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/network_stack_trace.png diff --git a/devtools/docs/user/network_monitor/request_details/params.png b/devtools/docs/user/network_monitor/request_details/params.png Binary files differnew file mode 100644 index 0000000000..9f5ceb0ccc --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/params.png diff --git a/devtools/docs/user/network_monitor/request_details/request-headers-fx78.png b/devtools/docs/user/network_monitor/request_details/request-headers-fx78.png Binary files differnew file mode 100644 index 0000000000..581c4e8200 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/request-headers-fx78.png diff --git a/devtools/docs/user/network_monitor/request_details/response-headers-fx78.png b/devtools/docs/user/network_monitor/request_details/response-headers-fx78.png Binary files differnew file mode 100644 index 0000000000..9e9f4a114c --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/response-headers-fx78.png diff --git a/devtools/docs/user/network_monitor/request_details/response_cache.png b/devtools/docs/user/network_monitor/request_details/response_cache.png Binary files differnew file mode 100644 index 0000000000..6b69e7990c --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/response_cache.png diff --git a/devtools/docs/user/network_monitor/request_details/response_font.png b/devtools/docs/user/network_monitor/request_details/response_font.png Binary files differnew file mode 100644 index 0000000000..28aa850026 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/response_font.png diff --git a/devtools/docs/user/network_monitor/request_details/response_json.png b/devtools/docs/user/network_monitor/request_details/response_json.png Binary files differnew file mode 100644 index 0000000000..a3e74c7472 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/response_json.png diff --git a/devtools/docs/user/network_monitor/request_details/response_pane_image.png b/devtools/docs/user/network_monitor/request_details/response_pane_image.png Binary files differnew file mode 100644 index 0000000000..4dc1e7485f --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/response_pane_image.png diff --git a/devtools/docs/user/network_monitor/request_details/security-warning.png b/devtools/docs/user/network_monitor/request_details/security-warning.png Binary files differnew file mode 100644 index 0000000000..1685395c86 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_details/security-warning.png diff --git a/devtools/docs/user/network_monitor/request_list/404_filter.png b/devtools/docs/user/network_monitor/request_list/404_filter.png Binary files differnew file mode 100644 index 0000000000..a70e1ce9fe --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/404_filter.png diff --git a/devtools/docs/user/network_monitor/request_list/afterblocking.png b/devtools/docs/user/network_monitor/request_list/afterblocking.png Binary files differnew file mode 100644 index 0000000000..71f62ecced --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/afterblocking.png diff --git a/devtools/docs/user/network_monitor/request_list/beforeblocking.png b/devtools/docs/user/network_monitor/request_list/beforeblocking.png Binary files differnew file mode 100644 index 0000000000..044d4b11bd --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/beforeblocking.png diff --git a/devtools/docs/user/network_monitor/request_list/blocked_nw_icon.png b/devtools/docs/user/network_monitor/request_list/blocked_nw_icon.png Binary files differnew file mode 100644 index 0000000000..c2ed85b941 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/blocked_nw_icon.png diff --git a/devtools/docs/user/network_monitor/request_list/har-dropdown.png b/devtools/docs/user/network_monitor/request_list/har-dropdown.png Binary files differnew file mode 100644 index 0000000000..93e309e247 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/har-dropdown.png diff --git a/devtools/docs/user/network_monitor/request_list/http.svg b/devtools/docs/user/network_monitor/request_list/http.svg new file mode 100644 index 0000000000..fc7062c9c1 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/http.svg @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><style>.icon-default{fill:#999}</style><defs><rect id="shape-lock-clasp-outer" x="4" y="2" width="8" height="10" rx="4" ry="4"/><rect id="shape-lock-clasp-inner" x="6" y="4" width="4" height="6" rx="2" ry="2"/><rect id="shape-lock-base" x="3" y="7" width="10" height="7" rx="1" ry="1"/><mask id="mask-clasp-cutout"><path d="M0 0h16v16H0z"/><use xlink:href="#shape-lock-clasp-outer" fill="#fff"/><use xlink:href="#shape-lock-clasp-inner"/><path stroke="#000" stroke-width="2" d="M2 13L14 1.5M2 15L14 3.5"/><rect x="3" y="7" width="10" height="7" rx="1" ry="1"/></mask><mask id="mask-base-cutout"><path d="M0 0h16v16H0z"/><use xlink:href="#shape-lock-base" fill="#fff"/><path stroke="#000" stroke-width="1.8" d="M2 14.8L14 3.2"/></mask></defs><use xlink:href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)" class="icon-default"/><use xlink:href="#shape-lock-base" mask="url(#mask-base-cutout)" class="icon-default"/><path stroke="#d92d21" stroke-width="1.8" d="M2 14.1L14 2.5"/></svg>
\ No newline at end of file diff --git a/devtools/docs/user/network_monitor/request_list/https-failed.svg b/devtools/docs/user/network_monitor/request_list/https-failed.svg new file mode 100644 index 0000000000..bfc8eeee58 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/https-failed.svg @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="gray" d="M14.8 12.5L9.3 1.9C9 1.3 8.5 1 8 1s-1 .3-1.3.9L1.2 12.5c-.3.6-.3 1.2 0 1.7s.8.8 1.4.8h10.8c.6 0 1.1-.3 1.4-.8.3-.5.3-1.1 0-1.7z"/><path fill="#fff" d="M8 11c-.8 0-1.5.7-1.5 1.5S7.2 14 8 14s1.5-.7 1.5-1.5S8.8 11 8 11zm0-1c.6 0 1-.4 1-1l.2-4.2c0-.7-.5-1.2-1.2-1.2s-1.2.5-1.2 1.2L7 9c0 .6.4 1 1 1z"/></svg>
\ No newline at end of file diff --git a/devtools/docs/user/network_monitor/request_list/https-weak.svg b/devtools/docs/user/network_monitor/request_list/https-weak.svg new file mode 100644 index 0000000000..7206803009 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/https-weak.svg @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><style>.icon-default{fill:gray}</style><defs><rect id="shape-lock-clasp-outer" x="2" y="1" width="8" height="10" rx="4" ry="4"/><rect id="shape-lock-clasp-inner" x="4" y="3" width="4" height="6" rx="2" ry="2"/><rect id="shape-lock-base" x="1" y="6" width="10" height="7" rx="1" ry="1"/><mask id="mask-clasp-cutout"><path d="M0 0h16v16H0z"/><use xlink:href="#shape-lock-clasp-outer" fill="#fff"/><use xlink:href="#shape-lock-clasp-inner"/></mask></defs><use xlink:href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)" class="icon-default"/><use xlink:href="#shape-lock-base" class="icon-default"/><path fill="#fff" d="M10.5 5c-.7 0-1.4.4-1.7 1.2L5.3 13c-.4.7-.4 1.4 0 2 .4.6 1 1 1.8 1H14c.8 0 1.4-.4 1.8-1 .3-.6.3-1.4 0-2l-3.5-6.8c-.4-.8-1.1-1.2-1.8-1.2z"/><path fill="#ffbf00" d="M14.8 13.4l-3.5-6.8c-.1-.4-.4-.6-.8-.6-.3 0-.7.2-.9.6l-3.5 6.8c-.2.4-.2.8 0 1.1.2.3.5.5.9.5h7c.4 0 .7-.2.9-.5.2-.3.1-.7-.1-1.1z"/><path fill="#fff" d="M10 8.5c0-.3.2-.5.5-.5s.5.2.5.5l-.2 2.5h-.6L10 8.5z"/><circle fill="#fff" cx="10.5" cy="12.5" r=".75"/></svg>
\ No newline at end of file diff --git a/devtools/docs/user/network_monitor/request_list/https.svg b/devtools/docs/user/network_monitor/request_list/https.svg new file mode 100644 index 0000000000..e5da9f4e38 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/https.svg @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><style>.icon-default{fill:#4d9a26}</style><defs><rect id="shape-lock-clasp-outer" x="4" y="2" width="8" height="10" rx="4" ry="4"/><rect id="shape-lock-clasp-inner" x="6" y="4" width="4" height="6" rx="2" ry="2"/><rect id="shape-lock-base" x="3" y="7" width="10" height="7" rx="1" ry="1"/><mask id="mask-clasp-cutout"><path d="M0 0h16v16H0z"/><use xlink:href="#shape-lock-clasp-outer" fill="#fff"/><use xlink:href="#shape-lock-clasp-inner"/></mask></defs><use xlink:href="#shape-lock-clasp-outer" mask="url(#mask-clasp-cutout)" class="icon-default"/><use xlink:href="#shape-lock-base" class="icon-default"/></svg>
\ No newline at end of file diff --git a/devtools/docs/user/network_monitor/request_list/image_preview.png b/devtools/docs/user/network_monitor/request_list/image_preview.png Binary files differnew file mode 100644 index 0000000000..fda8e08cf2 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/image_preview.png diff --git a/devtools/docs/user/network_monitor/request_list/index.rst b/devtools/docs/user/network_monitor/request_list/index.rst new file mode 100644 index 0000000000..c0345290c8 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/index.rst @@ -0,0 +1,497 @@ +==================== +Network request list +==================== + +The request list of the Network Monitor shows a list of all the network requests made in the course of loading the page. + + +Network request list +-------------------- + +By default, the Network Monitor shows a list of all the network requests made in the course of loading the page. Each request is displayed in its own row: + +.. image:: network_request_list.png + :class: border + +By default, the Network Monitor is cleared each time you navigate to a new page or reload the current page. You can override this behavior by checking "Enable persistent logs" in the :ref:`Settings <settings-common-preferences>`. + + +Network request columns +----------------------- + +You can toggle columns on and off by right-clicking on the table header and choosing the specific column from the context menu. A **Reset Columns** command is available on the context menu to reset the columns to their initial configuration. + +You can also change the width of the columns to help make the information you are looking for easier to view. The mouse pointer changes to a resize icon when you move it over the border of a column. You can drag to manually set the size of column. Starting in Firefox 76 you can double-click a column divider to resize the column to the left of it to fit its contents. + +The **Reset Columns** command on the context menu also resets the width of the columns to the default values. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/5fbuDO2s9Pk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +Clicking the column header label sorts the request list by that column. You can reset the sort to the default by selecting "Reset Sorting" from the context menu. + +.. image:: network_monitor_columns_menu.png + :alt: Screenshot of the context menu for selecting columns to display in the Network monitor + :class: center + + +Here is a list of all available columns: + +.. |image1| image:: blocked_nw_icon.png + :alt: Red circle with a diagonal slash + :width: 20 + +.. |image2| image:: nwmon-turtle-tooltip.png + :alt: Screenshot of a network request with a turtle icon, and a tooltip explaining the problem + :class: border + + +- **Status**: The HTTP status code returned. The numeric code is displayed on a colored background, to help unusual ones stand out. If there was no response, this column is empty. Or you might see a |image1| red circle with a diagonal slash for responses that were blocked by the browser or the server. +- **Method**: The HTTP request method used. +- **Domain**: Domain of the path requested. + + - If the request used SSL/TLS and the connection had security weaknesses such as weak ciphers, you'll see a warning triangle next to the domain. Y + - Hover over the domain to see the IP address. + - There's an icon next to the domain that gives you extra information about the security status of that request. See :ref:`Security icons <network-monitor-request-list-security-icons>`. + +- **File**: The basename of the file requested. + + - (Starting in Firefox 80) On the right edge of the File column, a turtle icon appears if the server waiting time exceeds a threshold (default: 500 ms). A tooltip explains the problem. You can configure the threshold in the `Configuration Editor <https://support.mozilla.org/en-US/kb/about-config-editor-firefox>`_ (about:config) by modifying the ``devtools.netmonitor.audits.slow`` setting. + - |image2| + +- **URL**: The `URL <https://developer.mozilla.org/en-US/docs/Glossary/URL>`_ of the file requested. +- **Protocol:** The network protocol used to transfer the data, this column is hidden by default. +- **Scheme:** The scheme (https/http/ftp/...) of the path requested. This column is hidden by default. +- **Remote IP**: The IP address of the server answering the request. This column is hidden by default. +- **Type**: ``Content-type`` of the response. +- **Cookies:** The number of request cookies associated to the request. This column is hidden by default. This is new in Firefox 55. +- **Set-Cookies:** The number of response cookies associated to the request. This column is hidden by default. This is new in Firefox 55. +- **Transferred**: The number of bytes that were actually transferred to load the resource, or a message about why the resource was not transferred. A number value is less than **Size** if the resource was compressed. + + - If the resource was fetched from a `service worker <https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API>`_ cache, then this cell displays "service worker". + - Cached resources may be fetched from the cache and the network simultaneously, which may improve load time for slow caches. `Starting with Firefox 68 <https://bugzilla.mozilla.org/show_bug.cgi?id=1358038>`_, the transferred column lists either "cached (raced)" or "[size] (raced)" depending on the faster source. This feature is called `Race Cache With Network (RCWN) <https://slides.com/valentingosu/race-cache-with-network-2017#>`_. + - If the resource was blocked, the message indicates why it was blocked. For example, "CSP", "Malware", "CORS Missing Allow Origin", "Blocked by [Name of Extension]". + +- **Size**: The size of the transferred resource. + + +Image thumbnails +~~~~~~~~~~~~~~~~ + +If the request is for an Image, hovering over its filename shows a preview of the image in a tooltip: + +.. image:: image_preview.png + :class: border + + +.. _network-monitor-request-list-security-icons: + +Security icons +~~~~~~~~~~~~~~ + +The Network Monitor displays an icon in the Domain column: + +.. image:: network_message_list_63.png + :class: border + + +This gives you extra information about the security status of the request: + +.. |image3| image:: https.svg + :width: 20 + +.. |image4| image:: https-weak.svg + :width: 20 + +.. |image5| image:: https-failed.svg + :width: 20 + +.. |image6| image:: http.svg + :width: 20 + +.. |image7| image:: localhost.svg + :width: 20 + +.. |image8| image:: tracker_icon.png + :width: 20 + + +.. list-table:: + :widths: 25 75 + :header-rows: 1 + + * - Icon + - Meaning + + * - |image3| + - HTTPS + + * - |image4| + - Weak HTTPS (for example, a weak cipher was used) + + * - |image5| + - Failed HTTPS (for example, a certificate was invalid) + + * - |image6| + - HTTP + + * - |image7| + - Localhost + + * - |image8| + - Indicates that the URL belongs to a known tracker that would be blocked with content blocking enabled. + + +Timeline +-------- + +The request list also displays a timeline for the different parts of each request. + +.. image:: timeline.png + :class: border + + +Each timeline is given a horizontal position in its row relative to the other network requests, so you can see the total time taken to load the page. For more details on the color-coding used here, see the section on the :ref:`Timings <network-monitor-request-details-timings-tab>` page. + +Starting in Firefox 45, the timeline also contains two vertical lines: + + +- The blue line marks the point at which thepage's `DOMContentLoaded <https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event>`_ event is triggered. +- The red line marks the point at which the page's `load <https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event>`_ event is triggered. + + +.. _network_monitor_blocking_specific_urls: + +Blocking specific URLs +---------------------- + +If you want to view your page as it would look without a resource (e.g., if it were blocked by the browser or an extension), you can block requests matching patterns you specify. + + +1. Click the **Request Blocking** icon in the toolbar. This opens the **Blocking** sidebar. (Click the icon again when you want to close the sidebar.) + + .. image:: request_blocking_panel.png + :alt: Screen shot of the Blocking panel, with arrows indicating the panel and the Request Blocking toolbar icon + :class: center + +2. Enter a string in the field with the placeholder text *Block resource when URL contains*. +3. Reload the page to test it with the specified URL blocked. + + +Other actions you can take with Request Blocking: + + +- To turn all request blocking off or on: Toggle the checkbox next to Enable Request Blocking. +- To turn a specific block off or on: Toggle the checkbox next to that item. +- To delete a blocked item, click the X icon that appears when you focus the item. +- (Starting with Firefox 77) Right-click any item in the list and choose from the context menu: + + - **Enable all** enables blocking of all items in the list. + - **Disable all** disables blocking of all items in the list. + - **Remove all** deletes all items in the list. + + +Blocking a specific URL from the request list +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can also block a URL from the request list: + +.. image:: beforeblocking.png + :class: border + + +1. Hover over the item you want to block in the Request List. +2. Select Block URL from the context menu. +3. When you refresh the page, that specific URL will be blocked and a message will be added to the item in the list indicating that it has been blocked by the DevTools. + +.. image:: afterblocking.png + :class: border + + +Stop blocking a URL from the Request List +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. image:: unblockurl.png + :class: border + +1. Hover over the item. +2. Select **Unblock URL**. +3. Now when you refresh the page, the item will once enabled. + + +.. note:: + (Starting in Firefox 80) You can also block and unblock URLs from the :doc:`Web Console <../../web_console/index>`, using the ``:block`` and ``:unblock`` helper commands. These accept any string, and affect any URL containing the string. + + +.. _request-list-filtering-requests: + +Filtering requests +****************** + +You can filter requests by content type, by whether they are XMLHttpRequests or WebSocket requests, or by request properties. + +.. |br| raw:: html + + <br/> + + +.. list-table:: + :widths: 25 75 + :header-rows: 1 + + * - Filter type + - How to apply + + * - Content type + - Use the buttons in the :doc:`toolbar <../toolbar/index>` (**HTML**, **CSS**, **JS**). + + * - `XHR <https://developer.mozilla.org/en-US/docs/Glossary/XHR_(XMLHttpRequest)>`_ requests + - Use the **XHR** button in the :doc:`toolbar <../toolbar/index>`. + + * - `WebSocket <https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API>`_ connections + - Use the **WS** button in the :doc:`toolbar <../toolbar/index>`. You can filter by plain text (in which case the text is used to find partial matches; entering "for" will match any message that contains the word "for") or—as of `Firefox 75 <https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/75>`_ — using `regular expressions <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions>`_ (by writing the regexp bracketed within slashes; "/.+Corp.*/" will look for any occurrence of "Corp" which has at least one character before it and may or may not have any characters after it, for example). |br| |br| The third-party add-on `WebSocket Sniffer <https://addons.mozilla.org/en-US/firefox/addon/websocketsniff>`_ may be helpful as well. + + * - URL + - Use the *Filter URLs* box in the :doc:`toolbar <../toolbar/index>`. You can focus it by clicking in the filter box, or by pressing :kbd:`Ctrl` + :kbd:`F` (or :kbd:`Cmd` + :kbd:`F` on a Mac); then start typing. The list of network requests is filtered to include only requests that contain your filter string, in either the Domain or the File portions. |br| |br| You can filter requests that *don't* contain your filter string by prefixing your query with the "-" operator. For example, the query "-google.com" will show all requests that don't have "google.com" in the URL. + + * - Request properties + - Use the search box in the :doc:`toolbar <../toolbar/index>`. See next section. + + +.. _request-list-filtering-by-properties: +.. _request-list-requst-list-cause-column: + +Filtering by properties +~~~~~~~~~~~~~~~~~~~~~~~ + +The search box recognizes specific keywords, which can be used to filter the requests by specific request properties. Those keywords are followed by a colon and a related filter value. The filter values are matched case insensitive. Prepending a minus (``-``) negates the filter. You can combine different filters together by separating them with a space. + + +.. list-table:: + :widths: 20 40 40 + :header-rows: 1 + + * - Keyword + - Meaning + - Examples + + * - ``status-code`` + - Shows resources that have the specific HTTP status code. + - ``status-code:304`` + + * - ``method`` + - Shows resources that have were requested via the specific HTTP request method. + - ``method:post`` + + * - ``domain`` + - Shows resources coming from a specific domain. + - ``domain:mozilla.org`` + + * - ``remote-ip`` + - Shows resources coming from a server with the specified IP. + - ``remote-ip:63.245.215.53`` |br| |br| ``remote-ip:[2400:cb00:2048:1::6810:2802]`` + + * - ``cause`` + - Shows resources matching a specific cause type. The types can be found in the description of the cause column. + - ``cause:js`` |br| |br| ``cause:stylesheet`` |br| |br| ``cause:img`` + + * - ``transferred`` + - Shows resources having a specific transferred size or a transferred size close to the one specified. ``k`` can be used as suffix for kilobytes and ``m`` for megabytes, e.g. the value ``1k`` is equivalent to ``1024``. + - ``transferred:1k`` + + * - ``size`` + - Shows resources having a specific size (after decompression) or a size close to the one specified. ``k`` can be used as suffix for kilobytes and ``m`` for megabytes, e.g. the value ``1k`` is equivalent to ``1024``. + - ``size:2m`` + + * - ``larger-than`` + - Shows resources that are larger than the specified size in bytes. ``k`` can be used as suffix for kilobytes and ``m`` for megabytes, e.g. the value ``1k`` is equivalent to ``1024``. + - ``larger-than:2000`` |br| |br| ``-larger-than:4k`` + + * - ``mime-type`` + - Shows resources that match the specified MIME type. + - ``mime-type:text/html`` |br| |br| ``mime-type:image/png`` |br| |br| ``mime-type:application/javascript`` + + * - ``is`` + - ``is:cached`` and ``is:from-cache`` shows only resources coming from cache. ``is:running`` shows only resources, which are currently being transferred. + - ``is:cached`` |br| |br| ``-is:running`` + + * - ``scheme`` + - Shows resources transferred via the given scheme. + - ``scheme:http`` + + * - ``has-response-header`` + - Shows resources that contain the specified HTTP response header. + - ``has-response-header:cache-control`` |br| |br| ``has-response-header:X-Firefox-Spdy`` + + * - ``set-cookie-domain`` + - Shows the resources that have a ``Set-Cookie`` header with a ``Domain`` attribute that matches the specified value. + - ``set-cookie-domain:.mozilla.org`` + + * - ``set-cookie-name`` + - Shows the resources that have a ``Set-Cookie`` header with a name that matches the specified value. + - ``set-cookie-name:_ga`` + + * - ``set-cookie-value`` + - Shows the resources that have a ``Set-Cookie`` header with a value that matches the specified value. + - ``set-cookie-value:true`` + + * - ``regexp`` + - Shows the resources having a URL that matches the given `regular expression <https://developer.mozilla.org/en-US/docs/Glossary/Regular_expression>`_. + - ``regexp:\d{5}`` |br| |br| ``regexp:mdn|mozilla`` + + +For example, to find all 404, not found, errors, you can type "404" into the search and auto-complete suggests "status-code:404" so you'll end up with something like this: + +.. image:: 404_filter.png + :class: border + + +Search in requests +------------------ + +Use the *Search* panel to run a full-text search on headers and content. + + +1. Click the **Search** icon in the toolbar. This opens the Search sidebar. + +.. image:: search_panel.png + :alt: Screenshot of the Network monitor, with the request search sidebar displayed, and arrows indicating the search toolbar icon and the search box. + :class: border + +2. Enter a string in the search field of the sidebar, and press :kbd:`Enter` or :kbd:`Return`. The search results area below the search field displays the requests that contain that string in the request or response headers or in the content of the response. You can expand each item to show the specific item that matches the string. Clicking an item in the search results highlights that item in the monitor list, and displays the corresponding information in the request details pane. + +.. image:: search_panel_matches.png + :alt: Screenshot of the search panel, with "newsletter" as the search string, and callouts for the expanded results, and corresponding items displayed in the request list and headers tab. + :class: border + + +Other ways to use the search panel: + + +- To clear the search string: click the **X** icon in the search field. +- To make the search case sensitive: click the **Case Sensitive** (**Aa**) icon next to the search field. +- To close the search panel, do one of the following: + + - Click the **X** icon next to the search field. + - Click the **Search** icon in the Network Monitor toolbar. + + +.. _network-monitor-request-list-edit-and-resend: + +Context menu +------------ + +Context-clicking on a row in the list displays a context menu with the following options: + +.. list-table:: + :widths: 25 75 + :header-rows: 1 + + * - Menuitem + - Description + + * - Copy > Copy URL + - Copies the URL. + + * - Copy > Copy as cURL + - Copies the network request to the clipboard as a `cURL <https://curl.haxx.se/>`_ command, so you can execute it from a command line. See :ref:`Copy as cURL <request-list-copy-as-curl>`, below. + + * - Copy > Copy as Fetch + - Copies the request as a call to the <a href="/en-US/docs/Web/API/fetch">fetch()</a> method, including the URL and any settings object. + + * - Copy > Copy Request Headers + - Copies the request's header to the clipboard. + + * - Copy > Copy Response Headers + - Copies the headers of the response for this request, to the clipboard. + + * - Copy > Copy Response + - Copies the entire response that was sent for this request. + + * - Copy > Copy All As HAR + - Creates an `HTTP Archive <https://w3c.github.io/web-performance/specs/HAR/Overview.html>`_ (HAR) for all requests listed, and copies it to the clipboard. + + * - Save All As HAR + - Creates an `HTTP Archive <https://w3c.github.io/web-performance/specs/HAR/Overview.html>`_ (HAR) for all requests listed, and opens a file dialog, so you can save it to a file. + + * - Resend + - Resends the request as it was originally sent with no changes made. + + * - Edit and Resend + - Opens an editor enabling you to edit the request's method, URL, parameters, and headers, and resend the request. + + * - Block URL + - Blocks the selected URL for future requests. See :ref:`Blocking a specific URL from the Request List <network_monitor_blocking_specific_urls>`. + + * - Open in New Tab + - Resends the request in a new tab — very useful for debugging asynchronous requests. + + * - Open in Style Editor + - For a CSS resource, opens it in the :doc:`Style Editor <../../style_editor/index>`. + + * - Start :doc:`Performance Analysis <../performance_analysis/index>` + - + + * - Use as Fetch in Console + - Submits the request as a call to the `fetch() <https://developer.mozilla.org/en-US/docs/Web/API/fetch>` method in the console. + + +.. _request-list-copy-as-curl: + +Copy as cURL +~~~~~~~~~~~~ + +The command may include the following options: + +.. list-table:: + :widths: 25 75 + :header-rows: 0 + + * - ``-X [METHOD]`` + - If the method is not GET or POST + + * - ``--data`` + - For URL encoded request parameters + + * - ``--data-binary`` + - For multipart request parameters + + * - ``--http/VERSION`` + - If the HTTP version is not 1.1 + + * - ``-I`` + - If the method is HEAD + + * - ``-H`` + - One for each request header. |br| |br| If the "Accept-Encoding" header is present, the cURL command includes ``--compressed`` instead of ``-H "Accept-Encoding: gzip, deflate"``. This means that the response will be automatically decompressed. + + + * - ``--globoff`` + - Suppresses cURL's globbing (wildcard matching) feature if the copied URL includes square bracket characters (``[`` or ``]``). (Starting in Firefox 76) + + + +Managing HAR data +~~~~~~~~~~~~~~~~~ + +The HAR format enables you to export detailed information about network requests. In addition to the **Copy** and **Save** menu items for HAR in the context menu, similar menu items are available in the **HAR** dropdown menu in the toolbar, as well as an **Import** menuitem. + +.. image:: har-dropdown.png + :class: border + + +Network Monitor features +************************ + +The following articles cover different aspects of using the network monitor: + +- :doc:`Toolbar <../toolbar/index>` +- :doc:`Network request list <../request_list/index>` +- :doc:`Network request details <../request_details/index>` +- :doc:`Network traffic recording <../performance_analysis/index>` +- :doc:`Throttling <../throttling/index>` +- :doc:`Inspecting web sockets <../inspecting_web_sockets/index>` +- :doc:`Inspecting server-sent events <../inspecting_server-sent_events/index>` diff --git a/devtools/docs/user/network_monitor/request_list/localhost.svg b/devtools/docs/user/network_monitor/request_list/localhost.svg new file mode 100644 index 0000000000..4e321aadcd --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/localhost.svg @@ -0,0 +1,4 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><circle fill="#a6a6a6" cx="8" cy="8" r="7"/><path fill="#fff" d="M6.316 2.741a.125.125 0 00-.054.007.042.042 0 00-.013.011l.018-.001c.018-.004.031-.016.05-.017zm.023.859c.023-.029-.032-.053-.062-.051.008-.035.052-.053.04-.097-.01-.046-.066-.037-.096-.012-.026.023-.04.063-.063.09-.013.015-.036.02-.045.038-.008.017.002.046 0 .065a.122.122 0 00.103-.032l.017-.008c-.004.003-.006.01-.008.014.018.022.097.016.114-.007zm.037-1.227c-.002.054.05.06.09.081-.013.031-.055.03-.075.055-.024.031.02.058.042.072.042.026.018.056.01.093-.011.052.1.038.125.037.042-.002.11.005.15-.015.043-.024.066-.077.111-.103.038-.022.091-.034.132-.019.044.015.039.071.074.095.042.029.087.038.123-.005.022-.027.073-.061.075-.09.004-.051.019-.091.075-.102.045-.009.035.035.067.043.07.018.103-.196.18-.133.017.015.022.073.053.068.031-.005.032-.051.066-.052.01.031-.058.069-.067.103.042-.035.063-.03.111-.035.013.033-.082.087-.106.092-.035.009-.056-.011-.082.008-.021.014-.05.013-.074.015-.035.004-.1.05-.099.088 0 .015.012.049 0 .061-.013.013-.041.001-.044-.012-.03.044-.068-.033-.093.029.04.01.076.048.12.059l.13.034c.072.022.18-.065.234-.107a.403.403 0 00.133-.19c.015-.069.09-.148.075-.216-.014-.064-.024-.094.048-.117.03-.01.105-.026.115-.061.016-.052-.147-.038-.171-.05-.082-.037-.118-.078-.212-.041-.05.019-.098.035-.15.049-.026.007-.053.009-.067.032a.06.06 0 01-.024.021c-.043.017.01-.064.015-.069.013-.016.036-.065-.008-.054-.065.015-.113.115-.183.12-.053.004-.036-.043-.022-.07.026-.049-.05-.055-.083-.055-.048 0-.083.027-.129.032-.042.004-.091.012-.134.011-.085-.003-.14.047-.225.019-.089-.029-.184.045-.27.055-.029.004-.07-.002-.082.032-.01.028 0 .071.022.092l.007-.006c-.02.019-.021.046-.049.056-.026.009-.053.044-.067.068-.01.018-.041.093.012.054.038-.029.06-.083.121-.072zM3.184 7.108c-.091-.066-.297-.082-.272-.236.016-.094.113-.167.19-.213a.544.544 0 01.333-.052c.028.003.08-.002.095.02a.075.075 0 00.036.022c.031.009.063.01.094.016.047.009.081.05.128.016.055-.038.064-.046.13-.038.059.007.094-.038.144-.034.015.001.03.004.041.009.005-.017.012-.032.023-.035.024-.007.079.053.104.058.066.014.062-.032.066-.079.033-.006.049.039.078.011 0 .009.005.023.005.032a.015.015 0 00.02-.001.018.018 0 00.001-.016c.016.005.024-.006.026-.025.012.001.03-.006.042-.004.01-.034.029-.079.004-.11l.02-.004c0-.034.023-.049.024-.076-.036-.005-.075-.003-.112-.002.022-.02.074-.064.079-.091.01-.048-.052-.077-.047-.134.006.03.04.099.07.109.072.024.051-.047.056-.084.018-.117.122-.02.124.041.03-.07.107.008.072.068-.017.029-.04.018-.018.056.014.027.036.028.068.021a.122.122 0 00.014-.053c.055-.017.09.048.059.087a.553.553 0 01.125-.054c-.026-.09-.052-.178-.036-.274.004-.021.007-.045.023-.061.02-.021-.005-.013-.007-.027-.006-.042.039-.085.055-.123-.046-.01-.004-.102.027-.118.034-.017.132.011.14-.016.019.011.038.027.061.027.055.001.091.002.129.046.018.023.046.074.08.078-.001.038.042.066-.003.098-.036.025-.085.019-.1.067-.01.03-.039.044.005.065.019.01.042.012.063.012.005.029.02.068.057.063.073-.009.089-.101.147-.132.095-.05.08.166.162.112.02-.013.02-.068.03-.089a.703.703 0 01.07-.128c.037-.05.083-.102.067-.167-.01-.036-.078-.051-.11-.076a.368.368 0 01-.096-.107c-.014-.026-.022-.035 0-.048.012-.007.008-.021.004-.031-.024-.05-.088-.139.001-.17.02-.007.062-.071.065-.096.005-.042-.059-.08-.03-.122.02-.031.073-.051.1-.082a.086.086 0 01.047-.032c.001-.017.005-.036.02-.047.023-.018.06-.009.089-.018.045-.014.065-.061.1-.087.03-.022.063-.014.095-.03.017-.008.024-.026.04-.034.042-.02.09.012.108.047.044.084.1.214.226.18a.158.158 0 00.105-.104c.015-.046-.003-.084 0-.129.004-.08.082-.131.091-.21-.059.001-.028-.036-.048-.07-.022-.038-.071-.01-.104-.016.034-.081.034-.109-.045-.148-.035-.017-.092-.097-.119-.094.022-.03.073.02.09.034a.168.168 0 00.113.052.107.107 0 01-.012-.083c.009-.022-.013-.045-.011-.073.055.071.046.152.078.23.013.033.045.055.06.089.017.043.005.042.043.068.022.016.03.044.034.069.007.044.023.025.047.05.013.015.049.017.042.048-.005.022-.02.04-.023.063-.009.066.12-.024.133-.033.028-.021.073-.025.095-.05.023-.026.018-.064.042-.088.03-.031.058-.01.093-.016.042-.006.077-.039.108-.064.065-.055.106-.115.16-.178-.023.006-.104.065-.109.01-.03 0-.101-.005-.112-.039-.01-.025-.006-.053-.006-.078-.001-.027-.034-.018-.055-.032-.044-.028-.066-.08-.112-.104a.458.458 0 01-.163-.169c-.025-.039-.116-.118-.11-.165.005-.031.03-.064.028-.095 0-.028-.022-.043-.02-.074.004-.036-.083-.099-.007-.106.023-.002.027-.031.054-.047.03-.018.023-.034.055-.025.053.016.089-.042.125-.073.06-.054-.037-.055-.042-.096-.005-.041-.028-.071-.035-.119-.004-.035-.037-.021-.056-.012-.025.012-.051-.006-.076-.011-.022-.005-.041-.043-.069-.031-.02.01-.02.035-.049.032-.022-.002-.036-.023-.057-.027-.034-.004-.004.028-.042.031-.026.002-.112-.034-.114 0-.027-.046-.038.032-.066.04-.032.009-.065.001-.097.013-.069.028-.046.097.02.108.053.008-.016.045.002.081.016.032.02.054.055.067.057.021.118.038.099.112a.348.348 0 01-.18.221c-.086.04-.11-.069-.175-.097-.04-.017-.085-.011-.128-.006-.007.011.06.032.07.051.02.039-.034.034-.038.064-.004.025-.037.043-.019.068-.019-.023-.055.008-.068.022-.019.02-.015.033-.007.058.016.051-.061.104-.107.098-.038-.006-.074-.003-.111-.021-.043-.021-.029-.008-.037-.057-.01-.046-.073-.066-.035-.123.027-.042.014-.038.009-.078-.006-.042.013-.048.046-.054.037-.007.053-.072.074-.103.005-.007.028-.065-.008-.05-.02.009-.005.034-.035.038-.022.004-.044-.011-.066-.011-.026 0-.053.013-.076-.003.012-.014.096-.084.03-.098-.027-.006-.006.038-.041.032-.007.035-.05.032-.07.054.009-.037.087-.065.061-.095.056-.049.068-.058-.008-.085-.12-.042-.11-.165-.028-.239.074-.068.198-.154.27-.041.079.121.128.033.192-.045-.021-.009-.002-.015-.009-.042-.07.029-.134-.063-.085-.118.03-.033.075-.024.114-.034.035-.009.066-.043.08-.075-.029.008-.025-.01-.014-.023-.018-.002-.037-.01-.052-.015-.043-.015-.04-.048-.085-.054-.108-.016.112-.14.005-.14-.035-.001-.065-.053-.09-.043-.019.007-.024.021-.045.012-.015-.006-.033-.019-.05-.009-.04.024-.047-.005-.084.006-.031.01-.048.04-.083.032.035-.048.079-.088.11-.138a.249.249 0 01.081-.086c.019-.011.072-.022.075-.048.005-.043-.022-.039-.051-.022a6.919 6.919 0 00-.232.144c-.046.029-.081.054-.137.046-.044-.007-.061.041-.096.037-.017-.067-.387.176-.432.197-.072.032-.153.087-.23.106-.032.008-.098.082-.094 0-.04-.005-.069.035-.095.055-.038.029-.082.047-.123.072-.087.056-.169.123-.25.186-.076.06-.153.13-.234.184-.028.019-.13.071-.127.11.072.014.318-.294.387-.211.018.021-.104.083-.124.095-.017.009-.037.008-.053.017-.022.013-.036.035-.057.049a.591.591 0 00-.142.13c-.028.037-.049.085-.08.119.004-.036-.003-.062-.002-.097-.04.026-.057.07-.113.058-.051-.012-.092.04-.128.068-.085.065-.142.137-.213.213-.04.043-.082.073-.107.126-.027.057-.064.108-.099.161-.066.097-.142.186-.207.283-.133.199-.22.427-.33.639-.057.11-.11.218-.134.341-.02.106-.02.214-.018.322.06-.047.057.051.041.08a.463.463 0 00-.038.146c-.01.065-.022.13-.022.196 0 .056-.018.108-.019.162-.02-.016.024-.084-.016-.074-.02.005-.02.034-.025.049-.016.052-.086.047-.097.106-.006.036-.01.057-.034.086-.018.022 0 .032.003.054.01.052-.058.124-.04.166.017.041.005.087.024.126.01.02.032.046.023.071-.042.008.012.106.016.136.006.049.05.203.097.225.059.091.14.218.245.264.074.032.1-.064.142-.104a.494.494 0 01.189-.109c.058-.021.302-.092.185-.176zm.102 3.285c.012-.02.003-.071-.019-.088-.052-.044-.073.06-.039.091.013.033.043.022.058-.003zM3.5 7.369c-.015-.013-.025-.005-.026-.032 0-.024.003-.07-.029-.041-.009.003.003.008-.01.013-.009.003-.016-.004-.022-.007-.016-.007-.026-.008-.039.01-.009.012-.009.026-.024.036l-.025.009c-.01.003-.035.021-.036.031-.004.015.019.026.035.03.012.009.03.017.042.026.013.009.034.025.05.029.035.02.09.042.118 0 .008-.016.014-.028.003-.042-.01-.015-.026-.019-.03-.031-.005-.013.006-.021-.007-.031zm4.745.755a.14.14 0 00-.067.026c-.03.026.034.041.058.048.028.016.067.024.094.04.023.017.038.04.064.051.032.015.075.022.11.031.014.005.037.004.057.008.022.013.031.033.05.047.03.028.073.035.114.033.038.004.067.011.101.002.04-.01.067.01.104.01.015 0 .03-.012.043-.011.019 0 .021.008.03.024.016.023.056.058.085.059.017 0 .031-.003.046.002.017.01.024.01.036.02.02.009.038.015.047.031.016.028.015.059.04.081l.052.04c.012.011.002.009.02.009.01.002.028.002.04-.002.05-.003.024-.073.008-.096-.01-.02-.019-.036-.015-.055.003-.023.014-.039-.001-.058a.075.075 0 00-.03-.024c-.006-.008-.008-.016-.015-.028-.015-.019-.044-.025-.063-.043-.031-.032-.048-.078-.088-.108-.022-.013-.042-.003-.068-.014-.011-.007-.017-.014-.033-.019-.015-.005-.028-.002-.041-.003-.029-.002-.054-.027-.081-.025-.03.004-.037.037-.055.055-.015.013-.033.013-.04-.008-.001-.027.009-.043.023-.06.021-.023 0-.036-.029-.038-.036 0-.043.029-.058.06-.024.033-.039.01-.072.005-.023.001-.039.01-.06.001-.015-.005-.018-.017-.029-.024a.036.036 0 00-.041.002c-.017.004-.017.004-.034-.005-.015-.006-.019-.018-.037-.022-.03-.006-.06.019-.085.012-.011-.006-.02-.022-.035-.027-.017-.01-.014-.001-.025.011-.018.018-.044.024-.062.01-.022-.016-.024-.043-.058-.048zm1.127 1.182c.016-.002.024-.018.037-.016.016-.003.008.013.02.023.009.009.019.009.029.009.017.003.052.005.059-.011.008-.025-.034-.03-.045-.05-.01-.029.013-.057.021-.082.012-.034-.03-.049-.026-.076-.001-.029.018-.039.012-.067a.162.162 0 00-.035-.059c-.012-.016-.035-.031-.033-.054.002-.024.046-.023.025-.052-.012-.025-.043-.02-.072-.024-.01 0-.02.001-.03-.009-.009-.014-.005-.021-.005-.031-.006-.027-.026-.037-.05-.048-.008-.004-.02-.009-.025-.021-.004-.012.008-.016.004-.027-.012-.026-.06.01-.08.001-.013-.002-.011-.015-.02-.029l-.024-.011c-.033-.015-.045.012-.04.039.015.061.06.101.055.162a.207.207 0 00.016.06c.008.033.016.049.002.081-.024.018-.002.04.006.063.004.031.012.055.011.088-.006.06-.024.119-.02.18.004.025.002.048.01.072.004.031.03.042.055.059.024.021.139.09.105.006-.01-.019-.025-.046-.03-.068-.008-.023.02-.039.02-.063.004-.027-.016-.036.022-.043.008-.006.02 0 .026-.002zM8.182 2.1c.03-.007.061.002.09-.009.015-.006.063-.023.06-.043-.005-.037-.162-.013-.188-.002-.007.023.016.041.037.047V2.1zm.754 6.688c-.006-.013 0-.025 0-.038-.003-.021-.01-.027-.006-.049.007-.012.007-.031.004-.046a.126.126 0 00-.025-.031.032.032 0 00-.01-.022c-.012-.012-.024.006-.037.012-.01.009-.029.015-.032.025-.01.015-.004.027-.004.04l.003.015c-.022.022 0 .08-.004.102 0 .025-.035.095.012.074.013-.006.023-.015.035-.021.015-.009.035-.009.054-.015.006 0 .04-.003.04-.009.001-.013-.027-.022-.03-.037zm-.891 1.264c.04.019.108-.019.146-.031.047-.015.122-.065.17-.037.02.011.03.034.05.043.026.011.057.001.083-.005.026-.006.057-.01.082-.022.021-.011.035-.029.054-.043.048-.037.09-.002.143-.01.03-.004.06-.019.089-.027.021-.005.06-.005.077-.021.018-.018.01-.057.01-.08-.001-.031 0-.063-.01-.092-.022-.057-.1-.124-.019-.169.016-.1-.1-.083-.134-.155-.022-.047-.029-.083-.091-.088-.052-.005-.084.023-.13.041-.05.019-.09.002-.134-.023-.027-.015-.083-.05-.094-.004-.01.04.028.079-.003.115-.028.032-.076.046-.116.055-.085.018-.153.084-.216.141l.007.006c-.024-.001-.06.065-.062.085l.03.009c-.001.034.037.011.04-.013.008.002.016.007.024.008.007.002.022.001.027.004.017.007.02.023.04.025-.01.052 0 .107-.025.156-.016.03-.095.104-.038.132zm.407-7.692c.03.032.066.041.06.091.037.005.06.019.082-.013a.12.12 0 01.058-.044c.027-.011.14-.01.136.037-.003.023-.017.043-.021.066-.005.032.03.009.044.017a.167.167 0 01-.058.024.033.033 0 01.016.024c-.026.006-.038.078-.084.092-.027.009-.068-.01-.096-.013-.032-.004-.057-.014-.09-.016-.03-.002-.005-.044-.045-.036-.007.028.007.099.012.127.005.035.035.055.07.061.048.008.07.024.112.048.032.018.069.007.104.01a.106.106 0 01.061.026c-.004.011-.012.029-.007.041.007.016.058-.002.07-.003.035-.004.069-.043.102-.038.013.002.072.02.07.035-.032-.013-.053.026-.078.005-.021-.019-.08-.003-.042.024a.687.687 0 01.013.086c-.003.028-.054.058-.05.076.007.001.053.004.062.013.003-.012-.005-.017.022-.025a.094.094 0 01.066.002c.008.035-.017.071.03.062.046-.009.066.021.113-.01a.066.066 0 01.087.011c.035.034-.035.081.005.114.016.013.029.053.045.06.01.005.07-.016.08-.021.02.034.036-.018.05-.021a.078.078 0 01.06-.05c.04-.004.041.003.068.022.078.054.067-.078.108-.112.073-.06.11-.117.16-.194.039-.062.094-.077.164-.088.055-.009.14-.022.163-.082.027-.07-.038-.108-.094-.129-.063-.022-.134-.046-.106-.126.032-.093.004-.147-.095-.177-.208-.065-.396-.174-.608-.234a2.557 2.557 0 00-.57-.082c-.085-.03-.266-.033-.319.04-.033.047.01.088.005.137a.22.22 0 00.065.163zm3.275 9.271l-.001-.001c.003.005.001.014.002.021.038 0 .055.035.097.022.042-.012.067-.053.033-.087-.03-.029-.055-.054-.098-.046-.052.01-.04.051-.033.091zm1.34-1.337l-.005-.026c-.04-.012-.066.03-.104-.001-.072.049.005.146-.113.14.02.025.019.053.009.082-.015.045-.027.041-.057.047-.065.01-.095-.03-.115-.084-.063.002-.15.1-.2.131-.012.007-.035.028-.049.037l-.05.027c-.034.017-.106.04-.11.078-.017-.003-.043.007-.06.005-.006.008-.006.017 0 .026.077.013.118-.013.183-.041.068-.031.141-.024.205-.048.03-.011.032-.045.082-.025.022.01.047.042.052.064.01.05-.042.124-.094.127-.013-.031.006-.063.011-.088-.069-.023-.182.075-.2.133.07.015.1.119.061.176-.012.014-.027.032-.052.04-.04.012-.06-.025-.099.004-.05.039.005.146-.024.206-.023.046-.061.063-.094.095-.022.023-.035.048-.064.068-.039.026-.132.082-.124.136.084.029.26-.119.333-.168.046-.031.075-.079.122-.11.054-.033.124-.05.158-.109.02-.034.004-.064.016-.098.01-.03.032-.04.051-.063.037-.044.07-.058.112-.094.051-.046.04-.118.063-.179.02-.053.061-.094.09-.144.044-.079.16-.267.111-.356-.012.01-.033.007-.044.012zm1.008-2.559c-.025-.048.013-.186.013-.243 0-.105-.033-.184-.047-.282-.01-.092-.022-.336.012-.419.047-.117-.184-.315-.197-.442a.396.396 0 00-.164-.277c-.036-.027-.115-.39-.161-.373-.023.011.024.091.021.116-.013.088-.057 0-.103.02-.086.035-.177.118-.188.203-.04.315-.263-.011-.245-.024.049-.038.067-.026.125-.034.064-.023-.036-.069.065-.079-.024-.065.03-.086.004-.137-.039-.075-.066-.066-.028-.147.017-.044-.084-.183-.091-.236-.008-.052-.01-.12-.016-.178-.004-.037.056-.072.045-.101-.002-.103.02-.214-.016-.314-.025-.068-.055-.158-.098-.216a.727.727 0 01-.066-.152c-.012-.064-.04-.04-.076-.065a.794.794 0 00-.135-.12 1.639 1.639 0 01-.199-.181c-.015-.045-.111-.08-.1-.129.015-.074-.242-.266-.314-.279-.047-.008.142.218.14.213.004.013.122.164.122.164.027.009.092.194.09.218-.007.07-.188-.115-.201-.14-.09-.109-.25-.197-.352-.27-.072-.067-.036-.105-.16-.162-.045-.021-.168-.122-.207-.125-.046-.002.007.096.008.107.007.07.083.075.129.121.036.038.069.085.037.127 0 0-.057.104-.06.095.016.044.147.141.18.177-.006-.009.171.224.186.1.006-.045-.044-.099-.038-.138.005-.024.204.227.215.249.048.133.044-.02.094-.004.042.013.153.152.06.158-.14.009.026.132.066.151.081.04.138.133.223.168.133.054.105.15.172.248.022.031-.383 0-.414.018-.053.042.203.329.204.372.002.09.053.156.067.246.008.083-.002.192.052.259.045.041.086-.068.156-.003.026.012.062.041.07.063.024.062.158.441-.003.413-.07-.013-.03.298-.025.343.021.098.06.09.035.214.003.103-.08.155-.139.229a.333.333 0 00-.058.117c-.033-.032-.048-.083-.094-.1-.05-.019-.158.041-.201.062-.105.053-.028.149-.07.232-.029.061-.118.091-.176.121-.07.036-.164.074-.233.016-.059-.048-.033-.145-.095-.187-.07-.047-.077.058-.093.1-.03.079-.122.106-.099.205.01.041.03.078.039.119.01.051-.016.097-.019.148-.006.087.085.107.109.178.02.064-.001.163-.072.187-.076.027-.16-.042-.235-.05-.075-.008-.171.013-.185.099-.013.076.064.138.027.216-.016.033-.044.059-.063.089-.035.05-.055.108-.087.159.038.001.034-.023.069-.016.036.008.069-.029.1-.041.006.024.003.049.006.073.025.008.05-.002.074-.011.002.021-.007.045.001.066.007.018.028.024.04.038.03.037-.007.098-.029.13-.062.092-.168.143-.237.229-.066.08-.071.177-.12.264-.017.03-.033.071.011.085.01-.016.025-.029.045-.029.031-.001.02.022.035.04.064.082.132-.053.16-.092.027-.042.142-.105.173-.035.025.053-.004.128-.028.176.041.019.03.049.04.085.012.05.057.082.057.137 0 .066-.144.197-.087.247.07.062.153-.11.18-.147.053-.071.172-.081.202-.168.032-.099.021-.158.152-.161.056-.001.096-.036.148-.047.058-.011.083-.017.12-.061.052-.063.1.012.101.062.002.051-.02.115.011.161.039.056.081-.018.12-.054-.003.038.047.06.077.072.045-.031.074-.082.122-.11a.212.212 0 01.074-.022c.007.041.014.085.05.105.053.03-.012.059.058.091.085.033.114.12.155.19.02.032.2-.163.269-.17.229-.026.348-.325.433-.502.122-.253.178-.534.21-.795.077-.161.109-.456.077-.643-.019-.114.046-.272-.012-.38zm-2.072 3.797c.006-.028.065-.132.01-.147-.02-.005-.036.025-.054.03-.024.008-.05-.007-.071.004-.02.011-.039.043-.05.061-.015.022-.01.031.014.043.024.013.055.019.068.045.012.023.006.054.003.078 0-.001.004-.005.005-.009a.038.038 0 01.015-.001l-.005.01c.063.01.058-.073.065-.114zm.74-1.251c.001.03.034.027.055.019.02-.006.033-.023.046-.038.018-.023.028-.046.013-.073a.28.28 0 01-.034-.081c-.013.007-.028.019-.042.023-.014.005-.016.001-.032 0-.036-.001-.028.027-.044.05-.013.02-.042.028-.03.054.007.02.04.037.058.046l.005-.006c0 .002-.002.003-.003.005a.017.017 0 00.007.001zM9.18 12.428c.037-.065.102-.106.16-.151a1.14 1.14 0 00.188-.185c-.033-.008-.035-.039-.056-.059-.031-.031-.08-.011-.086-.058-.007-.042-.042-.051-.075-.068-.077-.039-.109-.115-.171-.168-.068-.06-.16-.04-.244-.056-.073-.014-.147-.131-.225-.081-.049.031-.072.11-.038.159.026.038.072.057.09.102-.027.025-.033.041 0 .064.038.027.112.055.093.114-.01.032-.036.063-.07.069-.025.005-.086-.009-.073.036-.027-.074-.105.042-.148-.019-.034-.049-.056-.093-.11-.126-.07-.043.028-.085.017-.15-.018-.095-.134-.069-.175-.139-.025-.04.009-.072.025-.107.015-.036.058-.007.08.003a.25.25 0 00.245-.029c.029-.024.092-.135.012-.135-.055.001-.098.049-.15.052-.008-.063-.037-.17.03-.212.06-.038.19-.082.135-.18-.02-.034-.054.022-.083-.003-.012-.01-.003-.033 0-.044a.179.179 0 01-.045-.062c-.044-.106.084-.189.03-.301a.274.274 0 00-.103-.104c-.043-.03-.045-.068-.06-.113-.009-.025-.05-.084-.085-.069-.031.012-.04.065-.064.087-.053.052-.165.072-.236.055-.058-.013-.055-.035-.082-.073-.01-.014-.029-.015-.044-.021-.025-.01-.028-.033-.034-.056-.024-.087-.196.021-.222-.101-.006-.028.004-.075-.037-.081-.045-.008-.047-.052-.047-.089 0-.03.002-.072-.03-.089-.04-.022-.05-.012-.063-.058-.015-.059-.055.002-.089-.01-.07-.028-.058.007-.115.037-.098.052-.109-.189-.142-.236-.065-.09-.049.111-.093.136-.04.023-.083-.03-.098-.061-.008-.018-.013-.038-.024-.056-.017-.026-.047-.037-.063-.063-.014-.023-.035-.05-.045-.075-.009-.022-.007-.05-.024-.068-.02-.023.005-.061.02-.09.026-.01.064.01.083.027.046.039.117.21.197.178-.016-.022-.005-.049-.017-.073-.013-.025-.038-.04-.057-.06-.041-.048-.086-.096-.114-.154-.024-.05-.036-.103-.084-.138-.04-.029-.117-.057-.1-.119v-.002c.033.007.056.031.08.052.034.03.078.045.119.065.074.036.16.061.223.116.04.033.019.107.068.148.036.03.09.131.158.091.024-.015.035-.044.06-.061.027-.019.07-.036.102-.05.018-.009.05-.006.065-.021.023-.023-.033-.089-.046-.106-.05-.065-.096-.136-.164-.185-.034-.025-.068-.052-.108-.069-.022-.009-.06-.008-.064-.038.01.007.017.005.019-.006-.001-.02-.03-.021-.044-.025-.033-.009-.056-.009-.069-.037-.017-.034-.076-.033-.107-.041-.048-.012-.085-.048-.13-.066-.054-.02-.094.001-.147.013-.009.002-.026.033-.043.059a.128.128 0 00-.11.02c-.048.037-.076.093-.113.139-.016.019-.039.038-.061.031-.004-.002-.002-.007-.005-.009.028-.205.044-.412.025-.371-.037.08-.065.136-.093.194-.042-.018-.09-.019-.108.025-.019.047.006.108-.024.149-.006.011-.017.01-.027.015-.002-.005-.012-.018-.011-.019l-.018.033a.33.33 0 01-.102-.023l-.001-.003-.002.002c-.044-.015-.09-.029-.129-.015-.079.028-.082.141-.132.21-.075.105-.268.06-.302-.065l.082-.102a.29.29 0 00-.27-.18c-.033.001-.067.007-.097-.007-.03-.015-.047-.047-.074-.067-.083-.062-.193.02-.262.097a.489.489 0 00-.302.183l-.141-.012c.027.076-.064.138-.106.208-.051.085-.025.184.026.275-.006.014-.01.029-.023.035-.06.032-.044.051-.027.116a.554.554 0 01-.003.217c-.01.044-.053.15-.106.125-.028-.014-.057-.025-.083.001a.083.083 0 00-.024.04.3.3 0 00-.052.007c-.034.007-.069.015-.102.001-.032-.014-.076-.041-.112-.029-.03.01-.09.037-.104.069-.006.014.013.051.013.07 0 .034.026.085.015.116-.025-.012-.061-.016-.078-.041-.016-.021-.04-.016-.057-.038-.004.038-.018.091-.063.101-.044.01-.086-.025-.13-.013-.117.03.07.176.094.204.042.047.055.109.086.162.034.058.103.077.143.128.034.043.04.101.089.133.053.037.101.07.123.133.023-.022.07.082.119-.001.025-.045.07-.084.1-.011.024.063 0 .103.053.158.042.043.04.094-.031.089.012.032.03.064 0 .093-.014.014-.053.043-.025.063.033-.015.067-.024.1-.039.035-.015.07-.051.111-.05.002.013-.055.051-.02.054.03.003.073-.028.096-.002.027.028-.007.071.01.102.015.032.068.008.094.013-.01.028-.052.023-.075.034.052.064-.016.158-.092.16-.036 0-.166-.149-.171-.053a.285.285 0 00.016.09c.012.035.097.02.128.033a.22.22 0 01.113.106c.017.046.055.076.07.12.029.082.107.093.188.117.107.032.055.199.049.279-.005.079.105.098.156.143.056.049.065.144-.028.152-.047.004-.126-.018-.144.043-.026.086.105.077.162.096.027.009.134.02.144.045.015.039.003.092.017.133.033.105.13.175.224.227.201.113.44.184.66.247a2.6 2.6 0 00.376.081c.122.016.23.006.334.074.072.047.118.012.192.025.031.006.045.035.069.052.026.021.057-.006.085.005a.076.076 0 00-.005-.055c.056.021.125.081.184.033.03-.024.05-.057.08-.08.036.003.072.005.109.005.152 0 .273-.07.395-.152.131-.088.29-.09.441-.104a1.45 1.45 0 00.479-.107c.128-.062.16-.175.197-.3.04-.136.158-.191.236-.301.092-.128.031-.295.106-.428zm-5.885-1.804a.1.1 0 00-.041-.071l-.002.009c-.034-.017-.038-.081-.083-.076-.038-.06-.132-.033-.162.022-.02.037.004.054.026.081.027.034.022.06.032.099.025.101.109.032.168.071.023.015.031.058.065.051.037-.009.036-.065.026-.09-.014-.036-.024-.057-.029-.096zm.788 1.191c.006-.017-.02-.036-.038-.035-.016.001-.03.023-.037.035a.054.054 0 00.048.082c.01-.019.007-.056.035-.059-.002-.009-.008-.012-.016-.015l.008-.008zm-.838-1.269a.054.054 0 01.009.007c.003-.005.006-.009.007-.015l-.016.008zm9.245 1.403c-.017.016-.012.034-.02.053-.009.023-.043.032-.062.044-.03.019-.04.064-.072.075-.014-.021-.035-.08-.063-.029-.017.033-.006.066-.033.096-.027.028-.025.062-.043.093a.273.273 0 01-.106.107c-.038.023-.046.066-.074.098-.03.035-.075.048-.115.066-.029.012-.076.046-.109.027-.043-.027.016-.075.037-.092.02-.016.113-.073.091-.105-.014-.021-.068-.018-.09-.016-.044.005-.077.05-.115.071a.723.723 0 01-.125.06c-.053.019-.057.069-.1.099a.204.204 0 01-.12.046c-.057 0-.054-.048-.071-.088-.022.003-.04.031-.061.04-.034.014-.056.03-.04.067.013.038-.14.073-.167.092-.006-.019.024-.039.036-.05-.047-.004-.1.037-.15.044-.045.006-.103.038-.11.086-.005.038-.055.037-.086.05-.053.022-.031.053-.04.095-.02.079-.185.019-.102-.086.03-.037.074-.061.101-.099.03-.044.036-.099.06-.146-.053.015-.092.045-.137.074-.05.033-.093.025-.149.014-.065-.014-.11.019-.17.034-.039.009-.125.006-.13.062-.003.036.06.043.08.064.03.032.064.079.09.115.02.028.098.067.093.103-.008.07-.096.058-.143.07-.048.012-.055.055-.09.081-.039.029-.092-.005-.135.013-.045.017-.076.057-.115.083-.065.045-.107-.004-.174-.007-.055-.002-.104.023-.155.034-.047.01-.103.018-.138.051-.078.07.172.051.196.049-.011.04-.022.087-.058.113-.046.033-.11.025-.162.037-.04.01-.097.055-.038.087.054.027.122.013.175-.006.061-.021.118-.054.183-.068.067-.015.137-.008.204-.02.072-.014.135-.053.202-.08a.569.569 0 01.2-.038c-.014.025-.082.023-.107.029-.05.01-.085.054-.137.051-.06-.002-.053.04-.095.05-.028.007-.093.071-.114.031-.015-.031-.046-.025-.056.013-.008.028.008.038-.03.039-.03 0-.044-.012-.07-.019-.054-.014-.077.041-.115.055-.055.021-.114.014-.168.049-.03.02-.064.026-.1.037-.066.021-.129.046-.195.068-.05.018-.102.04-.157.041-.023 0-.114-.016-.129.011-.027.05.057.031.075.021.05-.027.112-.011.168-.011a.39.39 0 00.188-.046c.017-.009.129-.033.135-.02.01.006.083-.021.096-.024l.18-.042c.182-.047.362-.118.54-.179.354-.12.68-.323.987-.529.14-.093.253-.215.404-.291.152-.076.288-.177.427-.272.136-.093.24-.221.353-.338.115-.119.211-.231.262-.389-.029-.007-.059.052-.082.063-.04.019-.109.009-.14.039zm-.85-.161c.034-.047.004-.111-.057-.074-.023.013-.02.039-.038.055-.018.017-.02-.003-.039-.008-.027-.006-.07.03-.079.054-.036-.001-.069.04-.052.072.048-.018.078-.063.13-.049.041.011.108-.015.134-.05z"/></svg>
\ No newline at end of file diff --git a/devtools/docs/user/network_monitor/request_list/network_message_list_63.png b/devtools/docs/user/network_monitor/request_list/network_message_list_63.png Binary files differnew file mode 100644 index 0000000000..255dab1f9c --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/network_message_list_63.png diff --git a/devtools/docs/user/network_monitor/request_list/network_monitor_columns_menu.png b/devtools/docs/user/network_monitor/request_list/network_monitor_columns_menu.png Binary files differnew file mode 100644 index 0000000000..5a347a9886 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/network_monitor_columns_menu.png diff --git a/devtools/docs/user/network_monitor/request_list/network_request_list.png b/devtools/docs/user/network_monitor/request_list/network_request_list.png Binary files differnew file mode 100644 index 0000000000..2f570377df --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/network_request_list.png diff --git a/devtools/docs/user/network_monitor/request_list/nwmon-turtle-tooltip.png b/devtools/docs/user/network_monitor/request_list/nwmon-turtle-tooltip.png Binary files differnew file mode 100644 index 0000000000..8f17571f74 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/nwmon-turtle-tooltip.png diff --git a/devtools/docs/user/network_monitor/request_list/request_blocking_panel.png b/devtools/docs/user/network_monitor/request_list/request_blocking_panel.png Binary files differnew file mode 100644 index 0000000000..a5d0024c11 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/request_blocking_panel.png diff --git a/devtools/docs/user/network_monitor/request_list/search_panel.png b/devtools/docs/user/network_monitor/request_list/search_panel.png Binary files differnew file mode 100644 index 0000000000..4f9b1aae1f --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/search_panel.png diff --git a/devtools/docs/user/network_monitor/request_list/search_panel_matches.png b/devtools/docs/user/network_monitor/request_list/search_panel_matches.png Binary files differnew file mode 100644 index 0000000000..59fd659ccb --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/search_panel_matches.png diff --git a/devtools/docs/user/network_monitor/request_list/timeline.png b/devtools/docs/user/network_monitor/request_list/timeline.png Binary files differnew file mode 100644 index 0000000000..f479ff3439 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/timeline.png diff --git a/devtools/docs/user/network_monitor/request_list/tracker_icon.png b/devtools/docs/user/network_monitor/request_list/tracker_icon.png Binary files differnew file mode 100644 index 0000000000..fa91611f34 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/tracker_icon.png diff --git a/devtools/docs/user/network_monitor/request_list/unblockurl.png b/devtools/docs/user/network_monitor/request_list/unblockurl.png Binary files differnew file mode 100644 index 0000000000..b59042c413 --- /dev/null +++ b/devtools/docs/user/network_monitor/request_list/unblockurl.png diff --git a/devtools/docs/user/network_monitor/throttling/index.rst b/devtools/docs/user/network_monitor/throttling/index.rst new file mode 100644 index 0000000000..344e4d83d0 --- /dev/null +++ b/devtools/docs/user/network_monitor/throttling/index.rst @@ -0,0 +1,45 @@ +========== +Throttling +========== + +The network monitor allows you to throttle your network speed to emulate various connection speeds so you can see how your app will behave under different connection types. + +Throttling +********** + +The toolbar includes a Throttling dropdown, which allows you to throttle your network speed to emulate various different network speed conditions. Choose an option from the menu, and it will persist across reloads. + +.. image:: throttling.png + :class: border + +The characteristics emulated are: + +- Download speed +- Upload speed +- Minimum latency + +The table below lists the numbers associated with each network type, but please do not rely on this feature for exact performance measurements; it's intended to give an approximate idea of the user experience in different conditions. The speeds are expressed in multiples of bits per second. + +.. csv-table:: + :header: "Selection", "Download speed", "Upload speed", "Minimum latency" + :widths: auto + + GPRS, 50 Kbps, 20 Kbps, 500 + Regular 2G, 250 Kbps, 50 Kbps, 300 + Good 2G, 450 Kbps, 150 Kbps, 150 + Regular 3G, 750 Kbps, 250 Kbps, 100 + Good 3G, 1.5 Mbps, 750 Kbps, 40 + Regular 4G/LTE, 4 Mbps, 3 Mbps, 20 + DSL, 2 Mbps, 1 Mbps, 5 + Wi-Fi, 30 Mbps, 15 Mbps, 2 + +Network Monitor Features +************************ + +The following articles cover different aspects of using the network monitor: + +- :doc:`Toolbar <../toolbar/index>` +- :doc:`Network request list <../request_list/index>` +- :doc:`Network request details <../request_details/index>` +- :doc:`Network traffic recording <../recording/index/>` +- :doc:`Performance analysis <../performance_analysis/index>` diff --git a/devtools/docs/user/network_monitor/throttling/throttling.png b/devtools/docs/user/network_monitor/throttling/throttling.png Binary files differnew file mode 100644 index 0000000000..094eb33290 --- /dev/null +++ b/devtools/docs/user/network_monitor/throttling/throttling.png diff --git a/devtools/docs/user/network_monitor/toolbar/index.rst b/devtools/docs/user/network_monitor/toolbar/index.rst new file mode 100644 index 0000000000..9407c4fba0 --- /dev/null +++ b/devtools/docs/user/network_monitor/toolbar/index.rst @@ -0,0 +1,59 @@ +======================= +Network monitor toolbar +======================= + +The network monitor provides two toolbar areas, one above the main section, and another below. + +Toolbar +******* + +The toolbar is at the top of the main network monitor window. (Prior to Firefox 77, this toolbar was arranged somewhat differently.) + +.. image:: network_toolbar_callouts.png + :alt: Screenshot of the Network toolbar, without callouts for the parts + :class: border + +It provides: + +- An icon to clear the :doc:`network request list <../request_list/index>` +- A box enabling you to :ref:`filter requests <request-list-filtering-requests>` by URL and by properties. +- A set of tool icons: + + - Pause (or **resume**) recording network log + - **Search** the log + - **Request Blocking** + +- An array of buttons to filter the network request list by type: + + - by the content type of the response + - XHR requests + - WebSocket upgrades and messages (labeled **WS**) + - Other requests + +- A checkbox that allows you to disable caching. +- **Throttling** menu, to simulate various connection types +- A menu of other actions: + + - **Persist Logs**: By default, the Network Monitor is cleared each time you navigate to a new page or reload the current page. When you select **Persist Logs**, the log is not cleared on page load. + - **Import HAR** imports a HAR (HTTP Archive) file. + - **Save All as HAR** opens a file dialog box so you can save the current contents of the Network log as a HAR file with the extension ```.har```. + - **Copy All as HAR** copies the current contents of the Network log to the clipboard in HAR format. + +A second toolbar area at the bottom of the network monitor provides: + +.. image:: network_monitor_bottom_toolbar.png + +- An icon to launch :doc:`performance analysis <../performance_analysis/index>`. +- A summary of this page, including the number of requests, total size, and total time. + + +Network Monitor features +************************ + +The following articles cover different aspects of using the network monitor: + +- :doc:`Network request list <../request_list/index>` +- :doc:`Network request details <../request_details/index>` +- :doc:`Network traffic recording <../recording/index/>` +- :doc:`Performance analysis <../performance_analysis/index>` +- :doc:`Throttling <../throttling/index>` diff --git a/devtools/docs/user/network_monitor/toolbar/network_monitor_bottom_toolbar.png b/devtools/docs/user/network_monitor/toolbar/network_monitor_bottom_toolbar.png Binary files differnew file mode 100644 index 0000000000..fe89e59f00 --- /dev/null +++ b/devtools/docs/user/network_monitor/toolbar/network_monitor_bottom_toolbar.png diff --git a/devtools/docs/user/network_monitor/toolbar/network_toolbar_callouts.png b/devtools/docs/user/network_monitor/toolbar/network_toolbar_callouts.png Binary files differnew file mode 100644 index 0000000000..71189a9840 --- /dev/null +++ b/devtools/docs/user/network_monitor/toolbar/network_toolbar_callouts.png diff --git a/devtools/docs/user/network_monitor/wrench-icon.png b/devtools/docs/user/network_monitor/wrench-icon.png Binary files differnew file mode 100644 index 0000000000..b0d7a57776 --- /dev/null +++ b/devtools/docs/user/network_monitor/wrench-icon.png diff --git a/devtools/docs/user/page_inspector/3-pane_mode/2-pane-view-final.png b/devtools/docs/user/page_inspector/3-pane_mode/2-pane-view-final.png Binary files differnew file mode 100644 index 0000000000..0598aed36d --- /dev/null +++ b/devtools/docs/user/page_inspector/3-pane_mode/2-pane-view-final.png diff --git a/devtools/docs/user/page_inspector/3-pane_mode/3-pane-view-final.png b/devtools/docs/user/page_inspector/3-pane_mode/3-pane-view-final.png Binary files differnew file mode 100644 index 0000000000..df8b007dcf --- /dev/null +++ b/devtools/docs/user/page_inspector/3-pane_mode/3-pane-view-final.png diff --git a/devtools/docs/user/page_inspector/3-pane_mode/index.rst b/devtools/docs/user/page_inspector/3-pane_mode/index.rst new file mode 100644 index 0000000000..8cd0b09440 --- /dev/null +++ b/devtools/docs/user/page_inspector/3-pane_mode/index.rst @@ -0,0 +1,78 @@ +========================== +Page inspector 3-pane mode +========================== + +This article explains how to use the Page Inspector's 3-pane mode. + +Feature summary +*************** + +From Firefox 62 onwards, the :doc:`Page Inspector <../index>` has a new mode available — **3-Pane mode**. When activated, this allows you to see the following simultaneously: + + +- The :doc:`HTML pane <../how_to/examine_and_edit_html/index>` on the left hand side, as usual. + +- The :ref:`CSS Rules <page-inspector-how-to-examine-and-edit-css-examine-css-rules>` in the middle in their own separate pane, rather than as a tab. + +- The other CSS related features — such as :ref:`Computed styles view <page_inspector_how_to_examine_and_edit_css_examine_computed_css>`, :doc:`Animations view <../how_to/work_with_animations/index>`, and :doc:`Fonts view <../how_to/edit_fonts/index>` — in tabs on the right hand side, as usual. + +.. image:: 3-pane-view-final.png + :alt: The firefox page inspector in 3 pane mode, with HTML pane on left, CSS rules pane in center, and CSS tool tabs on right + :class: border + +.. note:: + + At narrower browser window widths, the tabs appear below the CSS Rules pane. + + +Having the CSS Rules in their own pane is very useful because it allows you to not only inspect your HTML and edit the CSS applied to it, but also see the effect this has on CSS features such as computed styles and grids in real time. You just need to have the relevant tab open to see the effect. + +A brief walkthrough +******************* + +The 3-pane inspector is disabled by default. It is enabled via a toggle control found in the tabs pane on the left hand side. + +.. image:: toggle-icon-final.png + :alt: a view of the tabs panel, showing the 2 to 3 pane toggle icon + :class: center + +Press the toggle control to toggle between the 2- and 3-pane views. + +.. image:: 2-pane-view-final.png + :alt: The firefox page inspector in 2 pane mode, with HTML pane on left and CSS tool tabs on right + :class: border + + +.. image:: 3-pane-view-final.png + :alt: The firefox page inspector in 3 pane mode, with HTML pane on left, CSS rules pane in center, and CSS tool tabs on right + :class: border + + +With the 3-pane mode enabled, you can observe live changes in CSS features as you edit the rules applied to the page. For example, you could edit a `CSS Grid <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout>`_ property and observe the change immediately in the :doc:`Grid Inspector <../how_to/examine_grid_layouts/index>`. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/ELS2OOUvxIw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +Enabling the 3-pane inspector pre-Firefox 62 +******************************************** + +In earlier versions of Firefox (since Firefox 59/60), you can enable 3 pane mode in Release/Beta by going to about:config and flipping the following prefs to ``true``: + +``devtools.inspector.split-rule-enabled`` — this switches 3-pane mode on and off. + +``devtools.inspector.split-sidebar-toggle`` — this adds the UI toggle button that lets you toggle it on and off. + +In Firefox 61, these preferences got renamed to: + + +- ``devtools.inspector.three-pane-enabled`` +- ``devtools.inspector.three-pane-toggle`` + +You need to flip these two to ``true`` in Release/Beta to test the feature in Firefox 61. + +.. note:: + + The 3-pane inspector is already enabled in Nightly/Developer edition before Firefox 62. diff --git a/devtools/docs/user/page_inspector/3-pane_mode/toggle-icon-final.png b/devtools/docs/user/page_inspector/3-pane_mode/toggle-icon-final.png Binary files differnew file mode 100644 index 0000000000..437234bda8 --- /dev/null +++ b/devtools/docs/user/page_inspector/3-pane_mode/toggle-icon-final.png diff --git a/devtools/docs/user/page_inspector/how_to/debug_scrollable_overflow/index.rst b/devtools/docs/user/page_inspector/how_to/debug_scrollable_overflow/index.rst new file mode 100644 index 0000000000..795e8bc3ad --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/debug_scrollable_overflow/index.rst @@ -0,0 +1,24 @@ +========================= +Debug scrollable overflow +========================= + +A `scroll container <https://developer.mozilla.org/en-US/docs/Glossary/Scroll_container>`_ is created by applying `overflow: scroll <https://developer.mozilla.org/en-US/docs/Web/CSS/overflow>`_ to a container, or `overflow: auto <https://developer.mozilla.org/en-US/docs/Web/CSS/overflow>`_ when there is enough content to cause overflow. The Firefox DevTools make it easy to discover both scrollable elements and any elements that are causing overflow. + + +In the :ref:`HTML Pane <page_inspector_ui_tour_html_pane>`, ascrollable element has the ``scroll`` badge next to it, as shown in the following image: + +.. image:: scroll_hover.png + :alt: HTML Pane Scroll badge + :class: center + +You can toggle the ``scroll`` badge to highlight elements causing an overflow, expanding nodes as needed to make the nodes visible: + +.. image:: scroll_badge_pressed.png + :alt: Scroll badge toggled on 1 + :class: center + +You will also see an ``overflow`` badge next to the node causing the overflow. + +.. image:: overflow_badge.png + :alt: HTML Pane: Overflow badge + :class: center diff --git a/devtools/docs/user/page_inspector/how_to/debug_scrollable_overflow/overflow_badge.png b/devtools/docs/user/page_inspector/how_to/debug_scrollable_overflow/overflow_badge.png Binary files differnew file mode 100644 index 0000000000..49f11b96c0 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/debug_scrollable_overflow/overflow_badge.png diff --git a/devtools/docs/user/page_inspector/how_to/debug_scrollable_overflow/scroll_badge_pressed.png b/devtools/docs/user/page_inspector/how_to/debug_scrollable_overflow/scroll_badge_pressed.png Binary files differnew file mode 100644 index 0000000000..f6071b8c2d --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/debug_scrollable_overflow/scroll_badge_pressed.png diff --git a/devtools/docs/user/page_inspector/how_to/debug_scrollable_overflow/scroll_hover.png b/devtools/docs/user/page_inspector/how_to/debug_scrollable_overflow/scroll_hover.png Binary files differnew file mode 100644 index 0000000000..7eba64c6fe --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/debug_scrollable_overflow/scroll_hover.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_filters/click_to_edit_filter.png b/devtools/docs/user/page_inspector/how_to/edit_css_filters/click_to_edit_filter.png Binary files differnew file mode 100644 index 0000000000..f89427ef01 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_filters/click_to_edit_filter.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_filters/css_filter_edit.png b/devtools/docs/user/page_inspector/how_to/edit_css_filters/css_filter_edit.png Binary files differnew file mode 100644 index 0000000000..9eeb63b57a --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_filters/css_filter_edit.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_filters/edit_filter_settings.png b/devtools/docs/user/page_inspector/how_to/edit_css_filters/edit_filter_settings.png Binary files differnew file mode 100644 index 0000000000..c93f688af7 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_filters/edit_filter_settings.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_filters/filter_presets.png b/devtools/docs/user/page_inspector/how_to/edit_css_filters/filter_presets.png Binary files differnew file mode 100644 index 0000000000..a3edb85c50 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_filters/filter_presets.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_filters/index.rst b/devtools/docs/user/page_inspector/how_to/edit_css_filters/index.rst new file mode 100644 index 0000000000..1cf3e9e4bd --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_filters/index.rst @@ -0,0 +1,36 @@ +================ +Edit CSS filters +================ + +`CSS filter <https://developer.mozilla.org/en-US/docs/Web/CSS/filter>`_ properties in the :ref:`Rules view <page_inspector_ui_tour_rules_view>` have a circular gray and white swatch next to them: + +.. image:: click_to_edit_filter.png + :class: border + +Clicking the swatch opens a filter editor: + +.. image:: css_filter_edit.png + :class: center + +The filter editor lists each of the effects performed by that filter on a separate line. You can edit these lines, remove them individually, or drag the effects to change the order in which they are applied. + +You can also add new effects by selecting an effect from the dropdown list at the bottom of the dialog. Once you have selected the effect you want to add, click the plus sign (+) to the right of the dropdown list. + +.. image:: edit_filter_settings.png + :class: center + +Once you have added an effect, enter the settings you want and then press :kbd:`Enter` to update the effect. The change will be apparent as soon as you press :kbd:`Enter`. + +Saving filter presets +********************* + +From Firefox 42 onwards, you can also add filters to a list of presets. The list of presets will be preserved between browser sessions, making it easy to apply the settings in the future. You can save the current filter to the preset list: + + +1. Click to edit the filter, display the preset list by clicking the icon as shown below. +2. Type a name for your preset, and then click the plus sign to add it to the list of presets. + +.. image:: filter_presets.png + :class: center + +You can then apply saved filters to new elements. To apply one of your saved presets, click its name in the list. diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_shapes/circle.png b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/circle.png Binary files differnew file mode 100644 index 0000000000..4e8dc9ec3f --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/circle.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_shapes/clipped-margin-box.png b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/clipped-margin-box.png Binary files differnew file mode 100644 index 0000000000..1ca15471c7 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/clipped-margin-box.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_shapes/ellipse.png b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/ellipse.png Binary files differnew file mode 100644 index 0000000000..473d7049fe --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/ellipse.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_shapes/enable-shapes-editor.png b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/enable-shapes-editor.png Binary files differnew file mode 100644 index 0000000000..d0869701e9 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/enable-shapes-editor.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_shapes/index.rst b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/index.rst new file mode 100644 index 0000000000..d8eadf0c2c --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/index.rst @@ -0,0 +1,104 @@ +======================= +Edit Shape Paths in CSS +======================= + +The Shape Path Editor is a tool that helps you see and edit shapes created using `clip-path <https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path>`_ and also the CSS `shape-outside <https://developer.mozilla.org/en-US/docs/Web/CSS/shape-outside>`_ property and `<basic-shape> <https://developer.mozilla.org/en-US/docs/Web/CSS/basic-shape>`_ values. This guide walks you through all of the available options. + + +Activate / deactivate the Shape Path Editor +******************************************* + +The Shape Path Editor is accessed via the CSS Rules Panel, which can be opened as described in the guide to :doc:`Opening the Inspector <../open_the_inspector/index>`. Once you have selected your element, you should see the shape icon alongside any valid value, e.g. one for ``shape-outside``. + +.. image:: enable-shapes-editor.png + :class: border + + +Clicking the icon will cause the Editor to highlight the shape. + +.. image:: circle.png + :class: center + + +To deactivate the Shape Path Editor click on the icon again, or select another element or a different editor. Note that the Shape Path Editor does not persist between page reloads — if you reload your page you will need to select the element again. + + +Understanding the lines drawn by the editor +******************************************* + +Once you have selected a shape on your page, the Shape Path Editor will draw lines to help you understand the path that is being created. + + +- **A solid line** shows the outline of the shape that is wrapping the text. This is your shape. If the shape is clipped by the margin box then the margin box will make up part of this line. +- **A dashed line** demonstrates the outline of the shape which extends past the margin box reference; this is the area that will be clipped by the margin box. To understand the margin box, and other boxes used by CSS Shapes see our guide to `Shapes from box values <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Shapes/From_box_values>`_. + +.. image:: clipped-margin-box.png + :class: center + + +Editing Basic Shapes +******************** + +The options given to you by the tool will differ depending on the type of basic shape that you are editing. Options can be accessed by activating the Shape Path Editor with a regular click on the icon, and you can use the context menu (:kbd:`Ctrl`/:kbd:`Cmd` + click) to access additional functionality. + +circle() +-------- + +If the value of ``shape-outside`` is ``circle()``, you are creating a `circle basic shape <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Shapes/Basic_Shapes#circle()>`_. Clicking on the shapes icon next to the value of ``circle()`` will highlight the shape, and also give you the option to resize the circle or move its center. If you move or resize the circle past the margin box, the edges become clipped by it. + +.. image:: clipped-margin-box.png + :class: center + + +In the Rules Panel you can see the values for ``circle()`` change as you edit the shape. You can then copy these values back into your stylesheet to create the new, tweaked shape path. + + +ellipse() +--------- + +If the value of ``shape-outside`` is ``ellipse()`` then you are using the `ellipse basic shape <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Shapes/Basic_Shapes#ellipse()>`_. The ``ellipse()`` value works in much the same way as ``circle()`` in the Shape Path Editor. An ellipse is a squashed circle and therefore has the option to resize horizontally and vertically when you click on the shapes icon. + +.. image:: ellipse.png + :class: center + + +inset() +------- + +If the value of ``shape-outside`` is ``inset()`` then you are using the `inset basic shape <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Shapes/Basic_Shapes#inset()>`_, which enables the creation of offset values pulling the content in from the margin box. + +Each side of the rectangle can be targeted after clicking on the shapes icon to activate the Shape Path Editor, and dragging each side will update the values for the top, right, bottom, and left offset values. + +.. image:: inset.png + :class: center + + +polygon() +--------- + +If the value of ``shape-outside`` is ``polygon()`` then you are using the `polygon basic shape <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Shapes/Basic_Shapes#polygon()>`_. This is the most complex of the basic shape values, therefore the tool gives you more options when editing these: + + +- Clicking on the shapes icon will activate the Shapes Path Editor and give you the option to move any of the points of your polygon shape. +- Double-click anywhere on a line of the shape and you will get a new point to adjust. +- Double click on an existing point and it will be removed. *Remember that a polygon needs at least three points*. + +.. image:: polygon-edit.png + :class: center + + +Moving and scaling shapes +------------------------- + +There is extra functionality available on the shape highlight — if you :kbd:`Ctrl`/:kbd:`Cmd` + click on the shapes icon for your shape the highlight will change, instead providing the ability to scale and/or move it. Once again, clipping will occur if you exceed the bounds of the margin box. + +.. image:: scaled-circle.png + :class: center + + +If your shape is a polygon, you also get the ability to rotate it around an axis. + +Browser support +*************** + +The Shape Path Editor currently works for shapes generated via `clip-path <https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path>`_; it will also work for shapes generated via `shape-outside <https://developer.mozilla.org/en-US/docs/Web/CSS/shape-outside>`_ as of Firefox 62. diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_shapes/inset.png b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/inset.png Binary files differnew file mode 100644 index 0000000000..70968ea11f --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/inset.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_shapes/polygon-edit.png b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/polygon-edit.png Binary files differnew file mode 100644 index 0000000000..90d9df8c31 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/polygon-edit.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_css_shapes/scaled-circle.png b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/scaled-circle.png Binary files differnew file mode 100644 index 0000000000..faa9935fd7 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_css_shapes/scaled-circle.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/all-fonts-on-page_new63.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/all-fonts-on-page_new63.png Binary files differnew file mode 100644 index 0000000000..8896ec40c7 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/all-fonts-on-page_new63.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/change_font_after_cropped.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/change_font_after_cropped.png Binary files differnew file mode 100644 index 0000000000..b5ff4d99a0 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/change_font_after_cropped.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/change_font_before_cropped.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/change_font_before_cropped.png Binary files differnew file mode 100644 index 0000000000..4a4437ecfc --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/change_font_before_cropped.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/font-italic_cropped.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/font-italic_cropped.png Binary files differnew file mode 100644 index 0000000000..6d33a65246 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/font-italic_cropped.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/font-size_cropped.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/font-size_cropped.png Binary files differnew file mode 100644 index 0000000000..eea408e6c3 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/font-size_cropped.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/font-weight_cropped.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/font-weight_cropped.png Binary files differnew file mode 100644 index 0000000000..56510effc5 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/font-weight_cropped.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/fonts-editor-closeup-63-cropped.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/fonts-editor-closeup-63-cropped.png Binary files differnew file mode 100644 index 0000000000..e63bfc6f64 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/fonts-editor-closeup-63-cropped.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/fonts-used.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/fonts-used.png Binary files differnew file mode 100644 index 0000000000..f28a2c0e9b --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/fonts-used.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/fonts_62_tooltip_cropped.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/fonts_62_tooltip_cropped.png Binary files differnew file mode 100644 index 0000000000..176d17c10c --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/fonts_62_tooltip_cropped.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/full-fonts-tab-new63.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/full-fonts-tab-new63.png Binary files differnew file mode 100644 index 0000000000..2f3b73b032 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/full-fonts-tab-new63.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/index.rst b/devtools/docs/user/page_inspector/how_to/edit_fonts/index.rst new file mode 100644 index 0000000000..8a08ca39fb --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/index.rst @@ -0,0 +1,257 @@ +========== +Edit fonts +========== + + +This article provides a tour of the Font tools available in the Firefox DevTools. This tool contains several useful features for viewing and manipulating fonts applied to any document loaded in the browser including inspection of all fonts applied to the page, and precise adjustment of variable font axis values. + +.. note:: + + The updated Font tools as shown in this article are available in Firefox 63 onwards; if you are using an older version of Firefox the tools will not look or behave quite the same, but they will be similar (most notably the Font Editor will not be available). + + +The Fonts tab +************* + +The Fonts tab is located on the right-hand side of the :doc:`Page Inspector <../../index>` when it is docked to the bottom of the screen. When it is docked to the right or left sides of the screen, the Fonts tab appears beneath the HTML pane. To access it: + + +1. :doc:`Open the Page Inspector <../open_the_inspector/index>`. +2. Select the Fonts tab; the last of the tabs shown in the right-hand side of the CSS pane. + +.. image:: full-fonts-tab-new63.png + :class: border + + +The Fonts tab has three major sections: + +- "Fonts used" by the currently inspected element. +- The new Fonts Editor. In Firefox 61 and 62, this section does not exist. +- "All fonts on page" — This section lists all of the fonts in use on the page. In Firefox 61 and 62, this area is labeled "Other fonts in page" and doesn't include the fonts mentioned in the "Fonts used" section. + + +Fonts used +********** + +The top section of the Font Editor shows the fonts used by the currently inspected element, grouped by font family. + +.. image:: fonts-used.png + :class: border + +Fonts are considered "used" when there is text content in the inspected element that has the font applied to it. Empty elements will not have any fonts used and will display the message "No fonts were found for the current element." + +Fonts will be included in this section for one of the following reasons: + + +- They are listed in the element's ``font-family`` CSS declaration value. +- They are applied to the element as part of the browser's default styling (Times New Roman for most browsers), and no author-defined font has been supplied. +- They are used by a descendant of the inspected element, for example, when it is a container for other elements which have text content with fonts applied. +- They are system fallback fonts used when nothing from the ``font-family`` CSS declaration has been applied. + + +Fonts Editor +************ + +Firefox 63 adds the Font Editor — a new area below "Fonts used" with additional controls for editing the fonts’ characteristics. + +.. image:: fonts-editor-closeup-63-cropped.png + :class: border + + +For standard (static) fonts, you will be able to change the settings listed below + + +Size +---- + +The `font-size <https://developer.mozilla.org/en-US/docs/Web/CSS/font-size>`_ for the inspected element. + +.. image:: font-size_cropped.png + :class: border + + +This can be set using ``em``, ``rem``, ``%``, ``px``, ``vh``, or ``vw`` units. You can select values using the slider or enter a numeric value directly into the text box. + +.. note:: + If you want to use a different unit such as ``pt`` for ``font-size`` or ``line-height``, you can set the property value applied to the currently inspected element to use that unit via the :ref:`rules view <page_inspector_ui_tour_rules_view>`, and the font editor will automatically pick it up and make it available in the associated units dropdown menu. + + +Changing the unit of measure converts the numerical value to its equivalent in the new unit, so the same computed value is maintained. + +Example: If ``1rem`` is equivalent to 10 pixels, when you change the unit of measurement from ``rem`` to ``px``, ``2rem`` becomes ``20px``. + + +Line height +----------- + +The `line-height <https://developer.mozilla.org/en-US/docs/Web/CSS/line-height>`_ of the inspected element. + +.. image:: line-height_cropped.png + :class: border + + +This can be set using unitless, ``em``, *%*, or ``px`` units. You can select values using the slider or enter a numeric value directly into the text box. + +Changing the unit of measure changes the value relative to the ``font-size`` setting. + +Example: If the font is 20 pixels high and the line-height is ``1.5em``, when you change the unit of measure from ``em`` to ``px``, the value will become ``30px``. + + +Weight +------ + +The `font-weight <https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight>`_ for the inspected element. + +.. image:: font-weight_cropped.png + :alt: Font weight setting + :class: border + + +You can select values using the slider or enter a numeric value directly into the text box. For non-variable fonts the slider ranges from 100 to 900 in increments of 100. + +.. note:: + For `variable fonts <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide>`_ (see below) that define a ``wght`` variation axis, this range is custom. + + +Italic +------ + +The `font-style <https://developer.mozilla.org/en-US/docs/Web/CSS/font-style>`_ for the inspected element. + +.. image:: font-italic_cropped.png + :class: border + + +This setting toggles between ``italic`` and ``normal`` values for the ``font-style`` CSS property. + +.. note:: + + As you change settings, Firefox applies inline styles to the element to make the changes instantly visible on the page. + + +All fonts on page +***************** + +The remaining area, at the bottom of the Fonts tab, shows an expandable list of all of the fonts in use on the page. + +.. image:: all-fonts-on-page_new63.png + :class: border + + +The list is useful because you can easily determine whether a font is a web font or a font hosted on your system. + +Each font listed in this section shows you: + + +- The ``font-family`` identifier and full name of the font. +- The URL to the font file in the case of web fonts not available on your system, or "System" in the case of fonts loaded from your computer (either default system fonts, or web fonts that you've also got installed on your system). You can copy the URL to the font file by clicking on the icon to the right of the URL. +- The `@font-face <https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face>`_ descriptor that loads the font into the page, in the case of web fonts. The descriptor is expandable — when opened it shows its full syntax as defined in the stylesheet. +- A text sample, to give you an idea of what the font looks like when rendered. The default text for the sample is "Abc" but the preview text can be edited by clicking on the input field at the top of the section and entering a new value. Once entered, all of the sample text will be set to the same custom value. + + +Variable font support in Firefox Developer Tools +************************************************ + +Firefox 62 added support for variable fonts and Firefox 63 features support for editing the properties of variable fonts in the Font Editor. + +What are variable fonts? +------------------------ + +`Variable Fonts <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide>`_, or **OpenType Font Variations**, define a new font file format that allows the font designer to include multiple variations of a typeface inside a single font file. That means you no longer have to apply several different web fonts to a single page to represent a complete typeface for which a variable font is available, provided it includes the desired values for the different characteristics you want to vary. + +Variable fonts make it easy to vary font characteristics in a much more granular fashion because their allowable ranges are defined by **axes of variation** (see `Introducing the 'variation axis' <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide#introducing_the_'variation_axis'>`_ for more information). For example, `font-weight <https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight>`_ can be set to any value between 1 and 1000 in variable fonts (although it is not guaranteed that a variable font will support this entire range). + +There are several registered axes. Although it isn't required that these axes be defined for every font, if the font designer *does* implement a registered axis, its behavior *must* follow the defined behavior. + +All variable font axes have a four-character axis tag. The CSS `font-variation-settings <https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings>`_ property uses the tag as part of the key-value pair. For example, to set `font-weight <https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight>`_ using ``font-variation-settings``, you could do something like this: + + +.. code-block:: css + + font-variation-settings: "wght" 350; + + +However, you should only use ``font-variation-settings`` as a last resort if there is no basic font property available for setting those characteristic values (e.g. custom axes). + +.. note:: + Font characteristics set using ``font-variation-settings`` will always override those set using the corresponding basic font properties, e.g. ``font-weight``, no matter where they appear in the cascade. + + +Here are the registered axes along with their corresponding CSS properties: + + +.. list-table:: + :widths: 40 60 + :header-rows: 1 + + * - Axis Tab + - CSS Property + + * - "wght" + - `font-weight <https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight>`_ + + * - "wdth" + - `font-stretch <https://developer.mozilla.org/en-US/docs/Web/CSS/font-stretch>`_ + + * - "slnt" (slant) + - `font-style <https://developer.mozilla.org/en-US/docs/Web/CSS/font-style>`_: ``oblique + angle`` + + * - "ital" + - `font-style <https://developer.mozilla.org/en-US/docs/Web/CSS/font-style>`_: ``italic`` + + * - "opsz" + - `font-optical-sizing <https://developer.mozilla.org/en-US/docs/Web/CSS/font-optical-sizing>`_ + + +Any axis that is not on the list of registered axes is considered a custom axis. Custom axes do not have corresponding CSS font properties. Font designers can define whatever axis they want; each one needs to be given a unique four-character tag. The axis name and its range is up to the font designer. + +.. note:: + Registered axis tags are identified using lower-case tags, whereas custom axes should be given upper-case tags. Note that font designers aren't forced follow this practice in any way, and some won't. The important takeaway here is that axis tags are case-sensitive. + + +.. warning:: + In order to use variable fonts, you need to make sure that your operating system is up to date. For example Linux OSes need the latest Linux Freetype version, and macOS prior to 10.13 does not support variable fonts. If your operating system is not up to date, you will not be able to use variable fonts in web pages or the Firefox Developer Tools. + + +Working with Variable fonts in the Font Editor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the inspected element uses a variable font, the Fonts tab shows the axes that have been implemented for that particular font, providing control to alter the value of each one. This is very useful for quickly finding out what axes are available in a particular font — they can vary quite dramatically as font designers can implement basically anything they like. + +.. image:: v_fonts_example_cropped.png + :class: border + + +You can adjust the axes individually or, if the font designer has included defined instances, you can select one from the "Instance" drop-down list and view the updates live on your page. + +Here are a couple of examples of fonts with different axes defined: + +.. image:: v_fonts-examples_cropped.png + :class: border + + +In the following example, you can see that the font "Cheee Variable" includes settings for Yeast and Gravity. These are custom axes defined by the font designer. + +.. image:: change_font_before_cropped.png + :class: border + + +The first image shows the font as it is used on the page with default settings. The second image shows the same font after selecting the "Hi Yeast Hi Gravity" variation. + +.. image:: change_font_after_cropped.png + :class: border + + +Tips +**** + +Finally, here are a few tips for making effective use of the Fonts tab: + + +- When using the Page Inspector's :doc:`3-pane mode <../../3-pane_mode/index>`, you can view the CSS rules for the inspected element simultaneously alongside the Fonts tab. +- If you hover over the `font-family <https://developer.mozilla.org/en-US/docs/Web/CSS/font-family>`_ property in the Rules view, a tooltip shows a sample of the font: + + .. image:: fonts_62_tooltip_cropped.png + :class: border + +- You'll also notice in the above screenshot that the font in the ``font-family`` font stack that is actually applied to the inspected element is underlined. This makes it easy to see exactly what is being applied where, when font stacks are specified. diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/line-height_cropped.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/line-height_cropped.png Binary files differnew file mode 100644 index 0000000000..21c3b25345 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/line-height_cropped.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/v_fonts-examples_cropped.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/v_fonts-examples_cropped.png Binary files differnew file mode 100644 index 0000000000..8489e04aef --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/v_fonts-examples_cropped.png diff --git a/devtools/docs/user/page_inspector/how_to/edit_fonts/v_fonts_example_cropped.png b/devtools/docs/user/page_inspector/how_to/edit_fonts/v_fonts_example_cropped.png Binary files differnew file mode 100644 index 0000000000..6c3ea01f74 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/edit_fonts/v_fonts_example_cropped.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/add_new_rule.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/add_new_rule.png Binary files differnew file mode 100644 index 0000000000..85ec6e49aa --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/add_new_rule.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/color_scheme_dark.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/color_scheme_dark.png Binary files differnew file mode 100644 index 0000000000..680708d33a --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/color_scheme_dark.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/color_scheme_light.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/color_scheme_light.png Binary files differnew file mode 100644 index 0000000000..46da0f8872 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/color_scheme_light.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/color_scheme_null.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/color_scheme_null.png Binary files differnew file mode 100644 index 0000000000..f498ddf585 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/color_scheme_null.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/compat_view.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/compat_view.png Binary files differnew file mode 100644 index 0000000000..159b43fecf --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/compat_view.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/computed_css.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/computed_css.png Binary files differnew file mode 100644 index 0000000000..cb58b1e39d --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/computed_css.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/computed_css_details.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/computed_css_details.png Binary files differnew file mode 100644 index 0000000000..3c8d15719e --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/computed_css_details.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/edit_rule_autocomplete.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/edit_rule_autocomplete.png Binary files differnew file mode 100644 index 0000000000..0b84b74c19 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/edit_rule_autocomplete.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/edit_rule_var_autocomplete.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/edit_rule_var_autocomplete.png Binary files differnew file mode 100644 index 0000000000..f881a3a28e --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/edit_rule_var_autocomplete.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/editable-context-menu.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/editable-context-menu.png Binary files differnew file mode 100644 index 0000000000..3fc6df7dfc --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/editable-context-menu.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/filter_rules.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/filter_rules.png Binary files differnew file mode 100644 index 0000000000..9e708b77be --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/filter_rules.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/filter_rules_2.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/filter_rules_2.png Binary files differnew file mode 100644 index 0000000000..d76abc6e47 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/filter_rules_2.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/filter_rules_2_strict.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/filter_rules_2_strict.png Binary files differnew file mode 100644 index 0000000000..2a44bc79ce --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/filter_rules_2_strict.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/filtered_rules.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/filtered_rules.png Binary files differnew file mode 100644 index 0000000000..b7f3628981 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/filtered_rules.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/firefox_compatibility_tootips.jpg b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/firefox_compatibility_tootips.jpg Binary files differnew file mode 100644 index 0000000000..9aa0b05047 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/firefox_compatibility_tootips.jpg diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/hover_indicators.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/hover_indicators.png Binary files differnew file mode 100644 index 0000000000..ce9f7f1ddc --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/hover_indicators.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/index.rst b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/index.rst new file mode 100644 index 0000000000..1f0fb56c15 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/index.rst @@ -0,0 +1,474 @@ +==================== +Examine and edit CSS +==================== + +You can examine and edit CSS in the Inspector's :ref:`CSS pane <page_inspector_ui_tour_rules_view>`. + + +.. _page-inspector-how-to-examine-and-edit-css-examine-css-rules: + +Examine CSS rules +***************** + +The :ref:`Rules view <page_inspector_ui_tour_rules_view>` lists all the rules that apply to the selected element, ordered from most-specific to least-specific: + +.. image:: rules_view_ff_87.png + :alt: Rules view panel as of Firefox 87 + :class: border + + +The six buttons on the right top of the rules view allow you to change the display of certain CSS and rules view features. You can: + + +- :ref:`toggle pseudo-classes <page-inspector-how-to-examine-and-edit-css-viewing-common-pseudo-classes>`; +- :ref:`toggle classes <page-inspector-how-to-examine-and-edit-css-viewing-and-changing-classes-on-an-element>` +- add a new rule; +- change the display based on :ref:`prefers-color-scheme media rules <page-inspector-view-media-rules-for-prefers-color-scheme>`. +- change the display based on :ref:`print media rules <page-inspector-view-media-rules-for-print>`. + +.. image:: rules_view_buttons_fx_72.png + :alt: Toolbar buttons of the Rules view, as of Fx 72 + :class: center + + +Invalid value warnings +---------------------- + +A warning icon appears next to unsupported CSS properties or rules that have invalid values. This can help you understand why certain styles are not being applied. In the following example, a spelling error, "background-colour" instead of "background-color" has made the rule invalid: + +.. image:: invalid_property.png + :class: border + + +Browser compat warnings +----------------------- + +CSS properties have varied level of support across different browsers. From Firefox 81, compatibility tooltips may be displayed next to any CSS properties that have known compatibility issues,as shown below. + +.. note:: + This feature is enabled from Firefox 81 by setting the preference ``devtools.inspector.ruleview.inline-compatibility-warning.enabled`` to ``true`` (open ``about:config`` in the URL bar to view/set Firefox preferences). + + +.. image:: firefox_compatibility_tootips.jpg + :alt: Tooltip displayed next to CSS element. Hover to find out browsers with compatibility issues. + :class: center + + +The tooltips, which arepopulated from the MDN `browser compatibility project <https://github.com/mdn/browser-compat-data>`_, identify the *reason* for the incompatibility (not supported, deprecated, experimental etc.), display icons for incompatible browsers, and provide a link to the associated property page for more information. + + +CSS Compatibility +----------------- + +In addition to compatibility tooltips, the *CSS Compatibility View* shows any CSS compatibility issues for both the selected element and for the current page (as a whole). + +.. image:: compat_view.png + :alt: Screenshot of the Compatibility view + :class: center + + +For more information see: :ref:`Page Inspector > CSS Compatibility View <page_inspector_ui_tour_compatibility_view>`. + + +Rule display +------------ + +It displays each rule as in a stylesheet, with a list of selectors followed by a list of ``property:value;`` declarations. + +.. image:: rules_pane.png + :class: center + + +- *Highlight matched elements*: next to the selector is a target icon: click this to highlight all nodes in the page that match this selector. +- *Overridden declaration*: declarations that are overridden by later rules are crossed out. See :ref:`overridden declarations <page-inspector-how-to-examine-and-edit-css-overridden-declarations>`. +- *Inactive rules* (not shown): if a rule is inactive (e.g., ``padding`` on a ``:visited`` pseudo-element), it is colored gray, with an info icon that gives more information when clicked. +- *Filter rules containing this property*: next to overridden declarations is an icon you can click to filter the rules list to show only those rules that include that property. See :ref:`overridden declarations <page-inspector-how-to-examine-and-edit-css-overridden-declarations>`. +- *Enable/disable*: if you hover over a declaration, a checkbox appears next to it: you can use this to toggle the declaration on or off. +- *Filename and line number*: on the right-hand side is a link to the rule. See :ref:`link to CSS file <page-inspector-how-to-examine-and-edit-css-link-to-css-file>`. + + +.. |image1| image:: screen_shot_2016-12-16_at_10.51.15_am.png + :width: 20 + +If the element has a `display: grid <https://developer.mozilla.org/en-US/docs/Web/CSS/display>`_ declaration, then it gets a grid icon next to it, like this: |image1|. Click that icon to display the grid overlaid on the page, including grid lines and tracks. See :doc:`Examine grid layouts <../examine_grid_layouts/index>` for more on this. + +To view `user-agent styles <https://developer.mozilla.org/en-US/docs/Web/CSS/Cascade>`_ (*i.e.,* browser-default CSS rules), enable "Inspector > Show Browser Styles" under the :doc:`developer tool settings <../../../settings/index>` panel. (Note that this setting is independent of the "Browser styles" checkbox in the :ref:`Computed view <page_inspector_how_to_examine_and_edit_css_examine_computed_css>`.) + +User-agent styles are displayed against a different background, and the link to the filename and line number contains the prefix ``(user agent)``: + +.. image:: user-agent_css.png + :class: border + + +.. _page_inspector_how_to_examine_and_edit_css_element_rule: + +element {} rule +--------------- + +The ``element {}`` rule at the top of the rules list isn't actually a CSS rule. It represents the CSS properties assigned to the element via its `style <https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes#attr-style>`_ attribute. + +.. |image2| image:: target-icon.png + :width: 20 + +This also gets the target icon: |image2|, giving you a convenient way to highlight the currently selected element in the page. + + +Filtering rules +--------------- + +There's a box at the top of the Rules view labeled "Filter Styles": + +.. image:: filter_rules.png + :class: border + +As you type: + +- any rules which don't contain the typed string at all are hidden +- any declarations which contain the typed string are highlighted + +.. image:: filtered_rules.png + :class: border + +Click the "X" at the end of the search box to remove the filter. + +.. note:: + While in the Rules view, you can press :kbd:`Ctrl` / :kbd:`Cmd` + :kbd:`F` to focus the search field. Once you've typed in a filter, you can press :kbd:`Esc` to remove it again. + + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/9w8vDIWqnAE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +Strict search +~~~~~~~~~~~~~ + +By default, the search box highlights all declarations which contain any part of the string. For example, searching for "color" will highlight declarations containing `border-bottom-color <https://developer.mozilla.org/en-US/docs/Web/CSS/border-bottom-color>`_ and `background-color <https://developer.mozilla.org/en-US/docs/Web/CSS/background-color>`_ as well as just `color <https://developer.mozilla.org/en-US/docs/Web/CSS/color>`_: + +.. image:: filter_rules_2.png + :class: border + +If you enclose the search query in backticks, like this: `color`, the search is restricted to exact matches: + +.. image:: filter_rules_2_strict.png + :class: border + + +Expanding shorthand properties +------------------------------ + +`Shorthand properties <https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties>`_ can be expanded to display their related longhand properties by clicking the arrow besides them. + + +Displaying pseudo-elements +-------------------------- + +The Rule view displays the following `pseudo-elements <https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements>`_, if they are applied to the selected element: + +- ``::after`` +- ``::backdrop`` +- ``::before`` +- ``::first-letter`` +- ``::first-line`` +- ``::selection`` +- ``:-moz-color-swatch`` +- ``:-moz-number-spin-box`` +- ``:-moz-number-spin-down`` +- ``:-moz-number-spin-up`` +- ``:-moz-number-text`` +- ``:-moz-number-wrapper`` +- ``:-moz-placeholder`` +- ``:-moz-progress-bar`` +- ``:-moz-range-progress`` +- ``:-moz-range-thumb`` +- ``:-moz-range-track`` +- ``:-moz-selection`` + +If the selected element has pseudo-elements applied to it, they are displayed before the selected element but hidden by a disclosure triangle: + +.. image:: pseudo-elements.png + :class: border + + +Clicking the triangle displays them: + +.. image:: pseudo-elements_displayed.png + :class: border + + +.. _page-inspector-how-to-examine-and-edit-css-viewing-common-pseudo-classes: + +Viewing common pseudo-classes +----------------------------- + +There's a button to the right of the filter box: + +.. image:: show_pseudo_classes.png + :class: border + + +Click the button to see checkboxes that you can use to enable the `:hover <https://developer.mozilla.org/en-US/docs/Web/CSS/:hover>`_, `:active <https://developer.mozilla.org/en-US/docs/Web/CSS/:active>`_ and `:focus <https://developer.mozilla.org/en-US/docs/Web/CSS/:focus>`_, `:focus-within <https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-within>`_, `:focus-visible <https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible>`_, `:visited <https://developer.mozilla.org/en-US/docs/Web/CSS/:visited>`_, and `:target <https://developer.mozilla.org/en-US/docs/Web/CSS/:target>`_ pseudo-classes for the selected element: + + +.. image:: show_pseudo_classes_hover.png + :class: border + +This feature can also be accessed from the :ref:`popup menu in the HTML view <page-inspector-how-to-element-popup-context-menu>` + +If you enable one of these pseudo-classes for a node, an orange dot appears in the markup view next to all nodes to which the pseudo-class has been applied. In addition, the information that appears on the page itself show you what pseudo-class you are examining. For example: + +.. image:: hover_indicators.png + :class: border + + +.. _page-inspector-how-to-examine-and-edit-css-viewing-and-changing-classes-on-an-element: + +Viewing and changing classes on an element +------------------------------------------ + +With an element selected in the HTML pane, you can click the **.cls** button in the Rules pane toolbar, to display the classes defined on that element. + + +- You can clear the checkbox for a particular class name, to see how the element would appear without that class applied to it. +- You can add a class to the element by typing a name in the *Add new class* field below the Rules toolbar. From Firefox 81, autocompletions based on existing classes are suggested as you type. + + +.. _page-inspector-how-to-examine-and-edit-css-link-to-css-file: + +Link to CSS file +---------------- + +At the top right of each rule, the source filename and line number is displayed as a link: clicking it opens the file in the :doc:`Style Editor <../../../style_editor/index>`. + +You can copy the location of the source file: right-click the link and select "Copy Location". + +The Inspector understands CSS source maps. That means that if you are using a CSS preprocessor that has support for source maps, and you've enabled source map support in the :ref:`Style Editor settings <settings-style-editor>`, then the link will take you to the original source, not the generated CSS. Read more about CSS source map support in the :ref:`Style Editor documentation <style-editor-source-map-support>`. + + +.. _page-inspector-how-to-examine-and-edit-css-overridden-declarations: + +Overridden declarations +----------------------- + +If a CSS declaration is overridden by some other CSS rule with a greater weight, then the declaration is shown with a line through it. + +Overridden declarations have a funnel next to them. Click the funnel to filter the rule view to show only the rules applying to the current node that try to set the same property: that is, the complete cascade for the given property. + +This makes it easy to see which rule is overriding the declaration + + +.. _page-inspector-view-media-rules-for-print: + +View @media rules for Print +--------------------------- + +You can toggle the display into a mode that emulates @media rules for print. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/AEmq9hNDOGU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +When on, any rules defined for printing the page will be displayed similar to the "Print Preview" mode that many word processing applications provide. + + +.. _page-inspector-view-media-rules-for-prefers-color-scheme: + +View @media rules for prefers-color-scheme +------------------------------------------ + +The color scheme simulator buttons can be used to test the rendering of styles based on the `prefers-color-scheme <https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme>`_ media query (if any are defined for the page). There are two buttons, which enable the light and dark preference, respectively. Selecting either button deselects the other. If neither button is selected then the simulator does not set a preference, and the browser renders using the default feature value set by the operating system. + + +.. |image3| image:: color_scheme_null.png + :class: border + +.. |image4| image:: color_scheme_light.png + :class: border + +.. |image5| image:: color_scheme_dark.png + :class: border + + +.. list-table:: + :widths: 30 20 50 + :header-rows: 1 + + * - Icon + - Value + - Description + + * - |image3| + - null + - The ``prefers-color-scheme`` media feature is not set by the simulator. + + + * - |image4| + - ``light`` + - The ``prefers-color-scheme`` media feature is set to ``light``. + + * - |image5| + - ``dark`` + - The ``prefers-color-scheme`` media feature is set to ``dark``. + + +Note that if the operating system mode is set to a particular mode, then simulating that mode will not change page rendering (i.e. simulating dark mode when the operating system is using dark mode will not change the display). + +.. note:: + If ``privacy.resistFingerprinting`` has been set **true**, the `prefers-color-scheme <https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme>`_ preference is forced to ``light``. You must set``privacy.resistFingerprinting`` to **false** in order to use this feature. + + +.. note:: + Before Firefox 87 this feature is behind the preference ``devtools.inspector.color-scheme-simulation.enabled``. + + +.. _page_inspector_how_to_examine_and_edit_css_examine_computed_css: + +Examine computed CSS +******************** + +To see the complete computed CSS for the selected element, select the :ref:`Computed panel <page_inspector_ui_tour_computed_view>` in the righthand pane.This panel shows the calculated value that each CSS property has for the selected element. (This calculated value is exactly the same as what `getComputedStyle <https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle>`_ would return.) + +.. image:: computed_css.png + :class: border + + +You can :kbd:`Tab` through the stylesto select them, and you can find more information about each property— pressing :kbd:`F1` on a selected property will open up its MDN reference page. + +Clicking the arrow next to the property name (or pressing :kbd:`Enter` or :kbd:`Space` while it is selected) shows the rule that set this value, along with a link to the source filename and line number: + +.. image:: computed_css_details.png + :class: border + + +By default, this view only shows values that have been explicitly set by the page: to see all values, click the "Browser styles" box. You can :kbd:`Tab` through the filenames/line numbers; pressing :kbd:`Enter`/:kbd:`Return` will open up the relevant file at that point in the :doc:`Style Editor <../../../style_editor/index>`. + +Typing in the search box performs a live filtering of the list, so, for example, if you just want to see font-related settings, you can type "font" in the search box, and only properties with "font" in the name will be listed. You can also search for the values of properties: to find the rule responsible for setting the font to "Lucida Grande", type that in the search box. + +.. note:: + While in the Computed view, you can press :kbd:`Ctrl` / :kbd:`Cmd` + :kbd:`F` to focus the search field. Once you've typed in a filter, you can press :kbd:`Esc` to remove it again. + + +Edit rules +********** + +If you click on a declaration or a selector in the Rules view you can edit it and see the results immediately. You can also :kbd:`Tab` through the different existing properties and values, and start editing them by pressing :kbd:`Enter` or :kbd:`Space`. To add a new declaration to a rule, click on the last line of the rule (the line occupied by the closing brace). + +As you start typing a property name, you'll see a list of autocomplete suggestions. Press:kbd:`Tab` to accept the current suggestion or :kbd:`Up` and :kbd:`Down` to move through the list. The default choice is the most common property that starts with the letters you've typed. For example, here the user has typed "c" and the default choice is "color": + +.. image:: edit_rule_autocomplete.png + :class: border + + +If you enter an invalid value for a property when editing it, or an unknown property name, a yellow alert icon appears besides the declaration. + +Edits that you make in the Rules view are reflected in the :doc:`Style Editor <../../../style_editor/index>`, and vice versa. Any changes you make are temporary: reloading the page will restore the original styling. + +While you're editing CSS, the context menu you'll see is the normal one for working with editable text: + +.. image:: editable-context-menu.png + :class: center + + +CSS variable autocompletion +--------------------------- + +`CSS variable names <https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties>`_ will auto-complete depending on the variables defined in the CSS. If you enter ``var(`` into a property value and then type a dash (``-``), any variables you have declared in your CSS will then appear in an autocomplete list, which shows a color swatch so you can see exactly what color each variable choice is storing (`bug 1451211 <https://bugzilla.mozilla.org/show_bug.cgi?id=1451211>`_) + +.. image:: edit_rule_var_autocomplete.png + :class: border + + +In addition, hovering over a CSS variable name brings up a tooltip showing what color value is stored in that variable `bug 1431949 <https://bugzilla.mozilla.org/show_bug.cgi?id=1431949>`_. + +.. image:: var_value.png + :class: border + + +Editing keyboard shortcuts +-------------------------- + +You can use the arrow and page up/down keys (along with others) to increase/decrease numeric rules while editing: + + +- The :kbd:`Up` arrow increments values by 1 — for example, "1px" changes to "2px". +- :kbd:`Shift` + :kbd:`Up`/:kbd:`Down` increments or decrements values by 10. +- :kbd:`Ctrl` + :kbd:`Up`/:kbd:`Down` (on Linux and Windows) or :kbd:`Alt` + :kbd:`Up`/:kbd:`Down` (on Mac) increments or decrements values by 0.1. +- :kbd:`Shift` + :kbd:`Page up`/:kbd:`Page down` increments or decrements values by 100. + + +Track changes +------------- + +When you are editing the rules in the rules view, you can see the changes you have made in the Changes pane. + +.. image:: track_changes.png + :class: border + + +.. note:: + You can view changes made to the rules view only. If you edit the CSS using the Style Editor, the changes will not be shown in the changes pane. + + Also remember, as noted above, that changes you make to the CSS rules are temporary and will be reset if you reload the page. + + +If you are satisfied with the changes you have made, you can copy the new settings to page the edited rule into your stylesheet. Right-click on the changes panel and select **Copy Rule** from the context menu. + +.. image:: save_changes_panel.png + :class: border + + +The Copy Rule command copies the entire element, class, or id definition, including any unchanged rules and the rules that describe your changes. For example, copying the changes in the preceding image, you get the following: + +.. code-block:: css + + .text-content p { + box-sizing:border-box; + max-width:24rem; + text-decoration: underline; + color: cadetblue; + font-weight: bold; + } + + +.. _page_inspector_how_to_examine_and_edit_css_add_rules: + +Add rules +********* + +You can add new rules in the Rules view. Just right-click to show the context menu and select "Add rule". This will add a new CSS rule whose selector matches the currently selected node. + +.. image:: add_new_rule.png + :class: border + + +There's also a button that enables you to do the same thing: + +.. image:: rules_panel.png + :class: border + + +Copy rules +********** + +To copy rules, and pieces of rules, use one of the following context menu items in the Rules view: + + +- Copy Rule +- Copy Selector +- Copy Property Declaration +- Copy Property Name +- Copy Property Value + +.. image:: rules_context_menu.png + :class: center + + +See also +******** + +- Complete list of Page Inspector :ref:`Keyboard shortcuts <keyboard-shortcuts-page-inspector>`. +- The Inspector also includes a number of specialized tools for working with particular CSS features, such as colors, fonts, and animations. To read about these see the list of :doc:`how to guides <../../index>`. diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/invalid_property.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/invalid_property.png Binary files differnew file mode 100644 index 0000000000..2015da3d84 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/invalid_property.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/pseudo-elements.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/pseudo-elements.png Binary files differnew file mode 100644 index 0000000000..45dd756270 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/pseudo-elements.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/pseudo-elements_displayed.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/pseudo-elements_displayed.png Binary files differnew file mode 100644 index 0000000000..6f8f2df915 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/pseudo-elements_displayed.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_context_menu.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_context_menu.png Binary files differnew file mode 100644 index 0000000000..f9b04aad7b --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_context_menu.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_pane.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_pane.png Binary files differnew file mode 100644 index 0000000000..93572dfc00 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_pane.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_panel.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_panel.png Binary files differnew file mode 100644 index 0000000000..0e37e4f376 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_panel.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_view_buttons_fx_72.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_view_buttons_fx_72.png Binary files differnew file mode 100644 index 0000000000..34a9de97e7 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_view_buttons_fx_72.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_view_ff_87.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_view_ff_87.png Binary files differnew file mode 100644 index 0000000000..0d92933d8a --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/rules_view_ff_87.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/save_changes_panel.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/save_changes_panel.png Binary files differnew file mode 100644 index 0000000000..6637725c27 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/save_changes_panel.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/screen_shot_2016-12-16_at_10.51.15_am.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/screen_shot_2016-12-16_at_10.51.15_am.png Binary files differnew file mode 100644 index 0000000000..3cb6150e42 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/screen_shot_2016-12-16_at_10.51.15_am.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/show_pseudo_classes.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/show_pseudo_classes.png Binary files differnew file mode 100644 index 0000000000..6436db0188 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/show_pseudo_classes.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/show_pseudo_classes_hover.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/show_pseudo_classes_hover.png Binary files differnew file mode 100644 index 0000000000..e4f078722e --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/show_pseudo_classes_hover.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/target-icon.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/target-icon.png Binary files differnew file mode 100644 index 0000000000..54ab83e68e --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/target-icon.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/track_changes.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/track_changes.png Binary files differnew file mode 100644 index 0000000000..5ab5cb6f5a --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/track_changes.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/user-agent_css.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/user-agent_css.png Binary files differnew file mode 100644 index 0000000000..8bc5fe28d1 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/user-agent_css.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/var_value.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/var_value.png Binary files differnew file mode 100644 index 0000000000..333367b35b --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_css/var_value.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/child-node-indicator.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/child-node-indicator.png Binary files differnew file mode 100644 index 0000000000..e0626644f8 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/child-node-indicator.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/custom_pc_01.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/custom_pc_01.png Binary files differnew file mode 100644 index 0000000000..94a66c4cf8 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/custom_pc_01.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/custom_pc_02.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/custom_pc_02.png Binary files differnew file mode 100644 index 0000000000..496b9a0471 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/custom_pc_02.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/edit_html.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/edit_html.png Binary files differnew file mode 100644 index 0000000000..698bce9182 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/edit_html.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/editable-context-menu.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/editable-context-menu.png Binary files differnew file mode 100644 index 0000000000..cbc9415ce1 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/editable-context-menu.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/html_breadcrumbs.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/html_breadcrumbs.png Binary files differnew file mode 100644 index 0000000000..d5cf8d0dd6 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/html_breadcrumbs.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/html_tree.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/html_tree.png Binary files differnew file mode 100644 index 0000000000..ee08f0dc30 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/html_tree.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/index.rst b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/index.rst new file mode 100644 index 0000000000..dcc688fd91 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/index.rst @@ -0,0 +1,466 @@ +===================== +Examine and edit HTML +===================== + +You can examine and edit the page's HTML in the :ref:`HTML pane <page_inspector_ui_tour_html_pane>`. + + +Navigating the HTML +******************* + +.. _page-inspector-how-to-examine-and-edit-html-breadcrumbs: + +HTML breadcrumbs +---------------- + +At the bottom on the HTML pane is a breadcrumbs toolbar. This shows the complete hierarchy through the document for the branch containing the selected element: + +.. image:: html_breadcrumbs.png + :class: border + + +Hovering over a breadcrumb highlights that element in the page. + +The breadcrumbs bar has its own :ref:`keyboard shortcuts <keyboard-shortcuts-breadcrumbs-bar>`. + + +.. _page_inspector_how_to_examine_and_edit_html_searching: + +Searching +--------- + +The Page Inspector's search box matches all markup in the current document and in any frames. + +To start searching the markup, click in the search box to expand it or press :kbd:`Ctrl` + :kbd:`F` , or :kbd:`Cmd` + :kbd:`F` on a Mac. There are three types of searches that are performed automatically depending on what you enter, a full text search, a CSS selector search, and an XPath search. + + +Full text search +~~~~~~~~~~~~~~~~ + +The full text search will always be executed, independently of what you enter. That allows you to find CSS selectors and XPath expressions occurring within the text. + + +CSS selector search +~~~~~~~~~~~~~~~~~~~ + +You can search elements by entering a `CSS selector <https://developer.mozilla.org/en-US/docs/Glossary/CSS_Selector>`_ + +As you type, an autocomplete popup shows any class or ID attributes that match the current search term: + +.. image:: search_html.png + :class: border + + +Press :kbd:`Up` and :kbd:`Down` to cycle through suggestions, :kbd:`Tab` to choose the current suggestion, then :kbd:`Enter` to select the first node with that attribute. + + +To cycle through matches, press :kbd:`Enter`. You can cycle backwards through matches using :kbd:`Shift` + :kbd:`Enter`. + + +XPath search +~~~~~~~~~~~~ + +It is also possible to search via `XPaths <https://developer.mozilla.org/en-US/docs/Web/XPath>`_. This allows you to search for specific elements without the conflict of matching words within the text. For example, ``//a`` matches all `a <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a>`_ elements but not the letter "a" within the text content. Furthermore it allows for some more advanced searches like finding elements that start with a specific text, for example. + +.. image:: xpath_search.png + :alt: Match of an Inspector search using an XPath expression + :class: border + + +HTML tree +--------- + +The rest of the pane shows you the page's HTML as a tree (this UI is also called the Markup View). Just to the left of each node is an arrow: click the arrow to expand the node. If you hold the Alt key while clicking the arrow, it expands the node and all the nodes underneath it. + +.. image:: html_tree.png + :alt: The new Firefox 57 inspector HTML tree. + :class: center + + +Moving the mouse over a node in the tree highlights that element in the page. + +Nodes that are not visible are shown faded/desaturated. This can happen for different reasons such as using `display: none <https://developer.mozilla.org/en-US/docs/Web/CSS/display>`_ or that the element doesn't have any dimensions. + +.. |image1| image:: child-node-indicator.png + :width: 20 + +There is an ellipsis shown between the opening and closing tag of an element when the node is collapsed if it has larger contents. Now children are indicated in the tree with this icon: |image1| + + +Markers ("badges") are displayed to the right of some nodes. The table below explains the meaning of each badge: + +.. |br| raw:: html + + <br/> + + +.. list-table:: + :widths: 25 75 + :header-rows: 0 + + * - ``event`` + - The element has one or several event listeners attached to it. Clicking the marker opens a tooltip listing the event listeners and allows you for each listener to switch to the line of JavaScript code in the :doc:`Debugger <../../../debugger/index>` where the listener is defined. + + * - ``scroll`` + - The element is a `scroll container <https://developer.mozilla.org/en-US/docs/Glossary/Scroll_container>`_, i.e. it has either ``overflow: scroll`` applied, or ``overflow: auto`` and sufficient content to cause `scrollable overflow <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Overflow>`_. |br| |br| If preference ``devtools.overflow.debugging.enabled`` is ``true``, toggling the ``scroll`` badge will highlight any elements causing the overflow, and these nodes will additionally display the ``overflow`` badge. + + * - ``overflow`` + - The element is causing `scrollable overflow <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Overflow>`_ in a `scroll container <https://developer.mozilla.org/en-US/docs/Glossary/Scroll_container>`_ (either the current node or a parent node—the affected nodewill display the ``scroll`` badge). |br| |br| **Note**: The ``overflow`` badge is introduced in Firefox 83. In earlier versions it can be enabled using the preference ``devtools.overflow.debugging.enabled`` is ``true``. + + * - ``grid`` + - The element is a `grid container <https://developer.mozilla.org/en-US/docs/Glossary/Grid_Container>`_, i.e. it has `display: grid <https://developer.mozilla.org/en-US/docs/Web/CSS/display>`_ applied to it. Clicking the marker enables the grid highlighter. + + * - ``flex`` + - The element is a `flex container <https://developer.mozilla.org/en-US/docs/Glossary/Flex_Container>`_, i.e. it has `display: flex <https://developer.mozilla.org/en-US/docs/Web/CSS/display>`_ applied to it. Clicking the marker enables the flexbox highlighter. + + * - ``inline-grid`` + - The element is an inline grid container, i.e. it has `display: inline-grid <https://developer.mozilla.org/en-US/docs/Web/CSS/display>`_ or ``display: inline grid`` applied to it. Clicking the marker enables the grid highlighter. + + * - ``inline-flex`` + - The element is an inline flex container, i.e. it has `display: inline-flex <https://developer.mozilla.org/en-US/docs/Web/CSS/display>`_ or ``display: inline flex`` applied to it. Clicking the marker enables the flexbox highlighter. + + * - ``custom…`` + - The element is a custom element. Clicking the marker switches to the line of JavaScript code in the Debugger where the custom element got defined. + + + +.. note:: + There are some useful keyboard shortcuts that can be used in the HTML tree — see the :ref:`HTML pane keyboard shortcuts list <keyboard-shortcuts-html-pane>`. + + +::before and ::after +-------------------- + +You can inspect pseudo-elements added using `::before <https://developer.mozilla.org/en-US/docs/Web/CSS/::before>`_ and `::after <https://developer.mozilla.org/en-US/docs/Web/CSS/::after>`_ + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/ecfqTGvzsNc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +Custom element definition +------------------------- + +When you open the Inspector on a page that includes custom elements, you can view the class definition for the custom element in the Debugger: + + +1. Inspect the element +2. Click on the word ``custom`` + + +.. image:: custom_pc_01.png + :class: center + + +The source for the element's class will be displayed in the Debugger. + +.. image:: custom_pc_02.png + :class: center + + +Whitespace-only text nodes +-------------------------- + +Web developers don’t write all their code in just one line of text. They use white space such as spaces, returns, or tabs between their HTML elements because it makes markup more readable. + +Usually this white space seems to have no effect and no visual output, but in fact, when a browser parses HTML it will automatically generate anonymous text nodes for elements not contained in a node. This includes white space (which is after all a type of text). + +If these auto generated text nodes are `inline level <https://developer.mozilla.org/en-US/docs/Web/CSS/Visual_formatting_model#inline-level_elements_and_inline_boxes>`_, browsers will give them a non-zero width and height. Then you will find strange gaps between elements, even if you haven’t set any margin or padding on them. + +.. |image2| image:: new-whitespace-text-indicator.png + :width: 20 + +Since Firefox 52, the Inspector displays these whitespace nodes, so you can see where the gaps in your markup come from. Whitespace nodes are represented with a dot: |image2| and you get an explanatory tooltip when you hover over them: + +.. image:: white_space_only.png + :class: center + + +To see this in action, see the demo at https://firefox-devtools.github.io/devtools-examples/whitespace-only-demo/index.html. + + +Shadow roots +------------ + +Any shadow roots present in the DOM are exposed in the HTML page in the same manner as the regular DOM. The shadow root is signified by a node named ``#shadow-root`` — you can click its expansion arrow to see the full contents of the shadow DOM, and then manipulate the contained nodes in a similar way to other part of the page's DOM (although with a limited featureset — you can't, for example, drag and drop or delete shadow DOM nodes). + + +.. image:: inspector_shadowdom.png + :alt: A view of a shadow root shown inside the DOM tree in the Firefox DevTools + :class: center + + +If a shadow DOM contains a "slotted" element (an element with a ``slot`` attribute after it has been inserted inside a `slot <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot>`_ element — see `Adding flexibility with slots <https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots#adding_flexibility_with_slots>`_ for an explanation of how these are used), the "slotted" element will be shown inside its corresponding `slot <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot>`_ element, with a "reveal" link alongside it. Clicking the "reveal" link will highlight the element with the ``slot`` attribute as it exists outside the shadow DOM + +.. image:: inspector_slot.png + :alt: A view of a shadow root shown inside the DOM tree in the Firefox DevTools + :class: center + + +This is very useful when you've got a ``<slot>`` element and you can't find the source of its content. + + +.. note:: + + Shadow DOM inspection was implemented in Firefox 61, but was hidden behind the ``dom.webcomponents.shadowdom.enabled`` pref until Firefox 63. It is now turned on by default. + + +.. _page-inspector-how-to-element-popup-context-menu: + +Element popup context menu +-------------------------- + +You can perform certain common tasks on a specific node using a popup context menu. To activate this menu, context-click the element. The menu contains the following items — click on the links to find the description of each command in the :ref:`Context menu reference <page_inspector_how_to_examine_and_edit_html_context_menu_reference>`: + + +- Edit As HTML +- Create New Node +- Duplicate Node +- Delete Node +- Attributes + + - Add Attribute + - Copy Attribute Value + - Edit Attribute + - Remove Attribute + +- Break on ... + + - Subtree Modification + - Attribute Modification + - Node Removal + +.. _page_inspector_how_to_examine_and_edit_html_use_in_console: + +- Use in Console +- Show DOM Properties +- Show Accessibility Properties +- Change Pseudo-class + + - hover + - active + - focus + - focus-visible + - focus-within + - visited + +- Screenshot Node + +.. _page_inspector_how_to_examine_and_edit_scroll_into_view: + +- Scroll Into View +- Copy + + - Inner HTML + - Outer HTML + - CSS Selector + - CSS Path + - XPath + - Image Data-URL + - Attribute + +- Paste + + - Inner HTML + - Outer HTML + - Before + - After + - As First Child + - As Last Child + +- Expand All +- Collapse All +- Open Link in New Tab [1] +- Open File in Debugger [1] +- Open File in Style-Editor [1] +- Copy Link Address [1] + + +[1] These options only appear in certain contexts, for example the "Open File in Style-Editor" option only appears when you context-click over the top of a link to a CSS file. + + +.. _page_inspector_how_to_examine_and_edit_html_context_menu_reference: + +Context menu reference +---------------------- + +.. list-table:: + :widths: 30 70 + :header-rows: 0 + + * - Edit as HTML + - :ref:`Edit the element's HTML <page-inspector-how-to-examine-and-edit-html-editing_html>`. + + * - (Copy) Inner HTML + - Copy the inner HTML for the element. + + * - (Copy) Outer HTML + - Copy the outer HTML for the element. + + Pressing :kbd:`Ctrl` + :kbd:`C` (or :kbd:`Cmd` + :kbd:`C` on a Mac) also performs this action. + + + * - (Copy) Unique Selector/CSS Selector + - Copy a CSS selector that uniquely selects the element. + + * - (Copy) CSS Path + - Copy a CSS selector that represents the full path to the element. + + * - (Copy) Image Data-URL + - Copy image as a data:// URL, if the selected element is an image. + + * - (Copy) Attribute + - Copy the attribute of the element. + + * - Show DOM Properties + - Open the :doc:`split console <../../../web_console/split_console/index>` and enter the console command "``inspect($0)``" to :doc:`inspect <../../../web_console/index>` the currently selected element. + + * - Use in Console + - Assigns the currently selected node to a variable named ``temp0`` (or ``temp1`` if ``temp0`` is already taken, and so on), then opens the :doc:`split console <../../../web_console/split_console/index>`, enabling you to interact with that node using the console's command line. + + * - Expand All + - In the tree view, expand the current element and all the elements underneath it. This is equivalent to holding the :kbd:`Alt` key and clicking the disclosure triangle next to an element. + + * - Collapse + - In the tree view, collapse the current element. This is equivalent to clicking the disclosure arrow next to an element that's expanded. + + * - (Paste) Inner HTML + - Paste the clipboard contents into the node as its `innerHTML <https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML>`_. + + * - (Paste) Outer HTML + - Paste the clipboard contents into the node as its `outerHTML <https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML>`_. + + * - (Paste) Before + - Paste the clipboard contents into the document immediately before this node. + + * - (Paste) After + - Paste the clipboard contents into the document immediately after this node. + + * - (Paste) As First Child + - Paste the clipboard contents into the document as the first child of this node. + + * - (Paste) As Last Child + - Paste the clipboard contents into the document as the last child of this node. + + * - Scroll Into View + - Scrolls the web page so the selected node is visible. + + From Firefox 44, pressing the keyboard shortcut :kbd:`S` will also scroll the selected node into view. + + * - Screenshot Node + - Takes a screenshot of the selected node, saved to your Downloads directory. See :doc:`Taking screenshots <../../../taking_screenshots/index>`. + + * - Create New Node + - Create a new empty <div> as the last child of the currently selected element. See :ref:`Inserting new nodes <page-inspector-how-to-examine-and-edit-html-inserting-new-nodes>`. + + * - Duplicate Node + - Create a copy of this element, and insert the copy immediately after this element. + + * - Delete Node + - Delete the element from the DOM. + + * - Attribute/Add Attribute + - Add an attribute to the element. + + * - Attribute/Edit Attribute + - (only when invoked on an attribute) Edit the attribute. + + * - Attribute/Remove Attribute + - (only when invoked on an attribute) Remove the attribute. + + * - Open Link in New Tab + - (only when invoked over a link, such as an href attribute) Opens the linked item in a new tab. + + * - Open File in Debugger + - (only when invoked over a link to a JS source) Opens the linked source in the Debugger. + + * - Open File in Style-Editor + - (only when invoked over a link to a CSS source) Opens the linked source in the Style Editor. + + * - Copy Link Address + - (only when invoked over a URL) Copy the URL. + + * - (Change Pseudo-class) hover + - Set the `:hover <https://developer.mozilla.org/en-US/docs/Web/CSS/:hover>`_ CSS pseudo-class. + + * - (Change Pseudo-class) active + - Set the `:active <https://developer.mozilla.org/en-US/docs/Web/CSS/:active>`_ CSS pseudo-class. + + * - (Change Pseudo-class) focus + - Set the `:focus <https://developer.mozilla.org/en-US/docs/Web/CSS/:focus>`_ CSS pseudo-class. + + * - (Change Pseudo-class) focus-visible + - Set the `:focus-visible <https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible>`_ CSS pseudo-class. + + * - (Change Pseudo-class) focus-within + - Set the `:focus-within <https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-within>`_ CSS pseudo-class. + + * - (Change Pseudo-class) visited + - Set the :visited CSS pseudo-class. + + +.. _page-inspector-how-to-examine-and-edit-html-editing_html: + +Editing HTML +************ + +You can edit the HTML — tags, attributes, and content — directly in the HTML pane: double-click the text you want to edit, change it, and press Enter to see the changes reflected immediately. + +To edit an element's `outerHTML <https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML>`_, activate the element's popup menu and select "Edit As HTML". You'll see a text box in the HTML pane: + +.. image:: edit_html.png + :alt: Edit HTML directly in the Inspector panel in Firefox 57 + :class: border + +You can add any HTML in here: changing the element's tag, changing existing elements, or adding new ones. Once you click outside the box, the changes are applied to the page. + +When you're editing HTML, the context menu you'll see is the normal one for working with editable text: + +.. image:: editable-context-menu.png + :class: center + + +Copy and paste +-------------- + +You can use the :ref:`popup menu <page-inspector-how-to-element-popup-context-menu>` to copy nodes in the HTML tree and paste them into the desired location. + + +Drag and drop +------------- + +You can reorganize the HTML content of a page by moving nodes in the HTML tree. Just click and hold on any element and drag it up or down in the tree. When you release the mouse button, the element will be inserted at the corresponding position: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/oI-a035nfWk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +You can cancel the drag and drop by pressing the :kbd:`Esc` key. + + +.. _page-inspector-how-to-examine-and-edit-html-inserting-new-nodes: + +Inserting new nodes +------------------- + +There's a "+" icon at the top of the markup view: + +.. image:: html_tree.png + :class: border + + +Click this icon to insert an empty {{HTMLElement("div")}} into the document as the last child of the currently selected element. You can then edit the new node's content and styling just as you would any other node in the document. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/NG5daffvVZM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +You can access the same functionality using the "Create New Node" popup menu item. + +Note that this button is disabled if the selected element's type is such that adding a last-child would have no effect (for example, if it is an `html <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html>`_ or `iframe <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe>`_ element). However, it is enabled in places where it is not valid to insert a `div <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div>`_, such as `style <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style>`_ or `link <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link>`_. In these cases the element is added as text. diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/inspector_shadowdom.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/inspector_shadowdom.png Binary files differnew file mode 100644 index 0000000000..515ff2bfdc --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/inspector_shadowdom.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/inspector_slot.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/inspector_slot.png Binary files differnew file mode 100644 index 0000000000..590ba86f50 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/inspector_slot.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/new-whitespace-text-indicator.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/new-whitespace-text-indicator.png Binary files differnew file mode 100644 index 0000000000..e925cabd49 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/new-whitespace-text-indicator.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/search_html.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/search_html.png Binary files differnew file mode 100644 index 0000000000..c1b84eaa18 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/search_html.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/white_space_only.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/white_space_only.png Binary files differnew file mode 100644 index 0000000000..d7a6f5c428 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/white_space_only.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/xpath_search.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/xpath_search.png Binary files differnew file mode 100644 index 0000000000..95342b7f8d --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_html/xpath_search.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_the_box_model/box-model-tooltip.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_the_box_model/box-model-tooltip.png Binary files differnew file mode 100644 index 0000000000..0b83f8b144 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_the_box_model/box-model-tooltip.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_the_box_model/box-model.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_the_box_model/box-model.png Binary files differnew file mode 100644 index 0000000000..cbb926183f --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_the_box_model/box-model.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_the_box_model/index.rst b/devtools/docs/user/page_inspector/how_to/examine_and_edit_the_box_model/index.rst new file mode 100644 index 0000000000..691630f211 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_the_box_model/index.rst @@ -0,0 +1,52 @@ +============================== +Examine and edit the box model +============================== + +Viewing the box model +********************* + +With the :ref:`Select Element button <page_inspector_select_element_button>` pressed, if you hover over an element in the page, the `box model <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model>`_ for the element is shown overlaid on the page: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/vDRzN-svJHQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +It's also shown overlaid if you hover over an element's markup in the HTML pane: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/xA4IxTttNLk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +If the element is inline and is split over multiple line boxes, the highlighter shows each individual line box that together make up the element: + +.. image:: inline-box-model.png + :class: center + + +.. _page-inspector-how-to-examine-and-edit-the-box-model-view: + + +The Box Model view +------------------ + +When an element's selected, you can get a detailed look at the box model in the :ref:`Box Model view <page_inspector_ui_tour_computed_view>`: + +.. image:: box-model.png + :class: center + + +If you hover over a value, you'll see a tooltip telling you which rule the value comes from: + +.. image:: box-model-tooltip.png + :class: center + + +Editing the box model +********************* + +You can also edit the values in the :ref:`Box Model view <page-inspector-how-to-examine-and-edit-the-box-model-view>`, and see the results immediately in the page. diff --git a/devtools/docs/user/page_inspector/how_to/examine_and_edit_the_box_model/inline-box-model.png b/devtools/docs/user/page_inspector/how_to/examine_and_edit_the_box_model/inline-box-model.png Binary files differnew file mode 100644 index 0000000000..5da14d5e0f --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_and_edit_the_box_model/inline-box-model.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_event_listeners/index.rst b/devtools/docs/user/page_inspector/how_to/examine_event_listeners/index.rst new file mode 100644 index 0000000000..0595d6164b --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_event_listeners/index.rst @@ -0,0 +1,27 @@ +======================= +Examine Event Listeners +======================= + +The inspector shows the word "event" next to elements in the :ref:`HTML Pane <page_inspector_ui_tour_html_pane>`, that have event listeners bound to them: + +.. image:: inspect_element_with_eventhandler.png + :class: border + +Click the icon, then you'll see a popup listing all the event listeners bound to this element: + +.. image:: inspector_event_handlers.png + :class: border + +Each line contains: + + +- a right-pointing arrowhead; click to expand the row and show the listener function source code +- the name of the event for which a handler was attached to this element +- the name and line number for the listener; you can also click here to expand the row and view the listener function source code +- a curved arrow pointing to a stack; click it to show the code for the handler in the debugger +- a label indicating whether the event bubbles +- a label indicating the system that defines the event. Firefox can display: + + - standard DOM events + - `jQuery events <https://api.jquery.com/category/events/>`_ + - `React events <https://facebook.github.io/react/docs/events.html>`_ diff --git a/devtools/docs/user/page_inspector/how_to/examine_event_listeners/inspect_element_with_eventhandler.png b/devtools/docs/user/page_inspector/how_to/examine_event_listeners/inspect_element_with_eventhandler.png Binary files differnew file mode 100644 index 0000000000..df8b66eb53 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_event_listeners/inspect_element_with_eventhandler.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_event_listeners/inspector_event_handlers.png b/devtools/docs/user/page_inspector/how_to/examine_event_listeners/inspector_event_handlers.png Binary files differnew file mode 100644 index 0000000000..34e3efecee --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_event_listeners/inspector_event_handlers.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/css-pane.png b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/css-pane.png Binary files differnew file mode 100644 index 0000000000..3cb2104e12 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/css-pane.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/flex-cont.png b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/flex-cont.png Binary files differnew file mode 100644 index 0000000000..7eb08580a6 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/flex-cont.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/flex-items.png b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/flex-items.png Binary files differnew file mode 100644 index 0000000000..8e423449ed --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/flex-items.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/flexbox_icon.gif b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/flexbox_icon.gif Binary files differnew file mode 100644 index 0000000000..53a045837e --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/flexbox_icon.gif diff --git a/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/html-pane.png b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/html-pane.png Binary files differnew file mode 100644 index 0000000000..989bf63ec7 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/html-pane.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/index.rst b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/index.rst new file mode 100644 index 0000000000..cbd14ea5e8 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/index.rst @@ -0,0 +1,133 @@ +============================================== +CSS Flexbox Inspector: Examine Flexbox layouts +============================================== + +The **Flexbox Inspector** allows you to examine `CSS Flexbox Layouts <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout>`_ using the Firefox DevTools, which is useful for discovering flex containers on a page, examining and modifying them, debugging layout issues, and more. + + +Discovering Flex Containers +*************************** + +When an HTML element on your page has `display: flex <https://developer.mozilla.org/en-US/docs/Web/CSS/display>`_ applied to it, a number of features are made available in the DevTools to provide easy access to Flexbox layout features. + + +In the HTML pane +---------------- + +In the :ref:`HTML Pane <page_inspector_ui_tour_html_pane>`, an element laid out with Flexbox has the word ``flex`` next to it as shown in the following image: + +.. image:: html-pane.png + :alt: Indicators in the inspector showing an element is a flex container + :class: center + + +Click the word ``flex`` in the HTML pane to keep the overlay visible when you move the mouse away from the container. + + +In the infobar +-------------- + +When you hover over an element in the HTML pane, you will see a tooltip that gives you more information about the element. When you hover over a flex container or flex item, the tooltip includes the appropriate information. + +This header is a flex container: + +.. image:: infobar-cont.png + :alt: Tooltip showing element is a flex container + :class: center + +Each navbar link is a flex item: + +.. image:: infobar-item.png + :alt: Tooltip showing an element is a flex item + :class: center + +The ``nav`` element within the header is both a flex item and a flex container which holds the navigation links: + +.. image:: infobar-both.png + :alt: Tooltip showing an element is both a flex container and a flex item + :class: center + + +In the CSS pane +--------------- + +.. |image1| image:: flexbox_icon.gif + :width: 20 + +In the :ref:`CSS pane <page_inspector_ui_tour_rules_view>`'s Rules view, any instance of a `display: flex <https://developer.mozilla.org/en-US/docs/Web/CSS/display>`_ declaration gets a small Flexbox icon |image1| next to the word ``flex``. + +.. image:: css-pane.png + :alt: The CSS pane of the Firefox devtools, showing the CSS for a flex container with an icon to toggle the Flexbox overly + :class: center + + +Clicking the icon toggles the display of an overlay on the page, which appears over the selected flex container that displays an outline around each flex item: + +.. image:: overlay.png + :alt: Flexbox overlay showing a flex container and its children + :class: center + + +The overlay will still be shown when you select other elements from the Inspector panel, so you can edit related CSS properties and see how the flex items are affected by your changes. + + +The Layout Flex Container section +--------------------------------- + +The CSS pane's Layout view includes a collapsible "Flex Container" section. If you expand the section without selecting a flexbox container, it will only display the message, "Select a Flex container or item to continue". Once you select an element whose display is defined as flex, the panel will include a number of options for viewing details about the flex container and flex items within it. You can find out more about those in the section below. + +.. note:: + + The Layout view can be found underneath the *Layout* tab on the right-hand pane of the Page Inspector. The above and below screenshots should give you further hints on how to get to this. + + +Flex Container options +********************** + +The Flex Container section of the Layout view looks like this: + +.. image:: flex-cont.png + :alt: Layout pane in Firefox Devtools showing options for the flexbox overlay + :class: center + + +There are two settings you can change in the Flex Container section: + + +- You can control the color of the overlay by clicking on the small circle next to the selector. This will toggle a color picker so you can select a different color for the overlay. +- The switch on the right-hand side of the Flex Container section will also toggle the overlay on and off. + + +Flex item properties +******************** + +The flex items within the flex container are displayed as a numbered list in the Flex Items section. Each entry displays the item's selector. Hover over an element to highlight it on the page. + +.. image:: flex-items.png + :alt: List of flex items displayed in the Layout pane of Firefox Devtools + :class: center + +If you click on the item, the display shifts to show details about that element: + +.. image:: item-details.png + :alt: Details of flex item sizing in the Layout pane of Firefox DevTools + :class: center + + +This view shows information about the calculations for the size of the selected flex item: + + +- A diagram visualizing the sizing of the flex item +- Content Size - the size of the component without any restraints imposed on it by its parent +- Flexibility - how much a flex item grew or shrunk based on its flex-grow value when there is extra free space or its flex-shrink value when there is not enough space +- Minimum Size (only appears when an item is clamped to its minimum size) - the minimum content size of a flex item when there is no more free space in the flex container +- Final Size - the size of the flex item after all sizing constraints imposed on it have been applied (based on the values of flex-grow, flex-shrink and flex-basis) + +At the top of the section is a drop-down list of all the items in the selected flexbox container: + +.. image:: select-items.png + :alt: Dropdown in Layout pane that allows you to select between different flex children + :class: border + + +You can use this drop-down to select any of the other flex items in the flex container. diff --git a/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/infobar-both.png b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/infobar-both.png Binary files differnew file mode 100644 index 0000000000..90c528d096 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/infobar-both.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/infobar-cont.png b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/infobar-cont.png Binary files differnew file mode 100644 index 0000000000..44dda1790e --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/infobar-cont.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/infobar-item.png b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/infobar-item.png Binary files differnew file mode 100644 index 0000000000..71b827be14 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/infobar-item.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/item-details.png b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/item-details.png Binary files differnew file mode 100644 index 0000000000..05e25aae17 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/item-details.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/overlay.png b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/overlay.png Binary files differnew file mode 100644 index 0000000000..048973f54c --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/overlay.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/select-items.png b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/select-items.png Binary files differnew file mode 100644 index 0000000000..82b26ab657 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_flexbox_layouts/select-items.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/css-pane.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/css-pane.png Binary files differnew file mode 100644 index 0000000000..39f785c741 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/css-pane.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/extend-lines.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/extend-lines.png Binary files differnew file mode 100644 index 0000000000..32cc8ba968 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/extend-lines.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/grid-named-areas.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/grid-named-areas.png Binary files differnew file mode 100644 index 0000000000..8d488f4a33 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/grid-named-areas.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/grid-options.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/grid-options.png Binary files differnew file mode 100644 index 0000000000..12f2973c6f --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/grid-options.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/grid-overlay.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/grid-overlay.png Binary files differnew file mode 100644 index 0000000000..c039dbdb50 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/grid-overlay.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/html-pane.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/html-pane.png Binary files differnew file mode 100644 index 0000000000..680dbf0f7c --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/html-pane.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/index.rst b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/index.rst new file mode 100644 index 0000000000..f8ae34c1ea --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/index.rst @@ -0,0 +1,189 @@ +======================================== +CSS Grid Inspector: Examine grid layouts +======================================== + +The **Grid Inspector** allows you to examine `CSS Grid Layouts <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout>`_ using the Firefox DevTools, discovering grids present on a page, examining and modifying them, debugging layout issues, and more. + +.. note:: + + The examples shown in the screenshots appearing in this article are Jen Simmons' `Futurismo <https://labs.jensimmons.com/2016/examples/futurismo-1.html>`_ and `Variations on a grid <https://labs.jensimmons.com/2017/01-003.html>`_ experiments, and a `live named grid area example <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Grid_Template_Areas#naming_a_grid_area>`_ from Rachel Andrew. + + +Discovering CSS grids +********************* + +When an HTML element on your page has `display: grid <https://developer.mozilla.org/en-US/docs/Web/CSS/display>`_ applied to it, a number of features are made available in the DevTools to provide easy access to grid features. + + +In the HTML pane +---------------- + +In the :ref:`HTML Pane <page_inspector_ui_tour_html_pane>`, elements laid out using a grid have a "grid" marker beside them. + +.. image:: html-pane.png + :alt: The HTML pane of the Firefox devtools, showing an element annotated with a grid marker, meaning that it has display: grid set on it + :class: border + + +In the CSS pane +--------------- + +.. |image1| image:: screen_shot_2016-12-16_at_10.51.15_am.png + :width: 20 + +In the :ref:`CSS pane <page_inspector_ui_tour_rules_view>`'s Rules view, any instance of a `display: grid <https://developer.mozilla.org/en-US/docs/Web/CSS/display>`_ declaration gets a grid icon included within it: |image1|. + +.. image:: css-pane.png + :alt: The CSS pane of the Firefox devtools, showing the CSS for a grid layout with a grid icon included next to display: grid + :class: border + + +Clicking the icon toggles the display of a grid overlay on the page, which appears over the element, laid out like a grid to show the position of its grid lines and tracks: + +.. image:: grid-overlay.png + :alt: A screenshot of the Firefox web browser, showing a colored overlay on top of a section of the page laid out like a grid + :class: border + + +The overlay is still shown when you select other elements, so you can edit related CSS properties and see how the grid is affected. + + +The Layout view Grid section +---------------------------- + +When grids are included on a page, the CSS pane's Layout view includes a "Grid" section containing a number of options for viewing those Grids. You can find out more about those in the section below. + +.. note:: + + The Layout view can be found underneath the *Layout* tab on the right-hand pane of the Page Inspector. The above and below screenshots should give you further hints on how to get to this. + + +Grid options +************ + +The Grid section of the Layout view looks like this: + +.. image:: grid-options.png + :alt: The grid options section of the Firefox devtools Layout view, showing multiple options for specifying how you want to see CSS grids displayed + :class: border + + +You'll see a number of options contained within: + + +- Overlay Grid: Contains a checkbox for each grid present on the page, along with various options. Allows overlay views to be toggled on and off. +- Grid Display Settings: + + - Display line numbers: Turn the line numbers shown for each grid overlay on and off (on by default). + - Display area names: Turn area names on and off, in the case of grids with named grid areas (on by default, where relevant). + - Extend lines infinitely: By default, grid lines/tracks are only shown inside the element with ``display: grid`` set on it; when toggling this option on, the grid lines extend to the edge of the viewport along each axis. + +- Mini grid view: A smaller view of the currently overlaid grid. + + +.. note:: + + Your grid preferences such as overlay color and display settings choices are persisted across page loads for each separate page. + +Let's examine these features in more detail. + + +Overlay grid +------------ + +Each grid present on a page has an entry in the "Overlay grid" section: + +.. image:: overlay-grid-entry.png + :alt: An entry for a single grid in the Overlay Grid section of the Grid options, showing a grid's name, overlay color, and more. + :class: border + +Each entry consists of (from left to right): + + +- A checkbox allowing you to toggle the grid overlay for that grid on and off. +- A name label to represent the grid, consisting of a selector identifying the HTML element that has the grid applied to it. Clicking this also toggles the grid overlay on and off. +- A target icon that when clicked immediately selects the HTML element that this grid entry relates to, inside the HTML pane. +- A color picker icon that allows you to change the primary color of the grid overlay. This is useful for selecting different colors so you can easily tell your grids apart. + + +Inspecting a subgrid +-------------------- + +When the page contains a grid with a subgrid, the entry for the subgrid is indented under its parent in the Overlay grid section. When you select the checkbox for the subgrid, the lines for the parent grid are displayed also displayed; if the checkbox for the parent grid is unselected, then its lines are translucent. + +.. image:: subgrid-lines.png + :alt: Screenshot showing the overlay lines for a subgrid, with the subgrid lines and parent grid lines called out. + :class: center + + +Display line numbers +-------------------- + +By default, the line numbers are displayed on the grid overlay. + +.. image:: line-numbers.png + :alt: A CSS grid overlay with grid line numbers displayed + :class: border + + +Unchecking the "Display line numbers" box turns them off. + +.. image:: no-line-numbers.png + :alt: A CSS grid overlay with grid line numbers not displayed + :class: border + + +Display area names +------------------ + +In a grid with named areas, the area names are shown on the grid overlay by default. + +.. image:: grid-named-areas.png + :alt: A CSS grid overlay with named area names displayed + :class: border + +Unchecking the "Display area names" box turns them off. + +.. image:: no-grid-named-areas.png + :alt: A CSS grid overlay with named area names not displayed + :class: border + + +Extend lines infinitely +----------------------- + +By default, the grid lines/tracks are only shown inside the element with ``display: grid`` set on it. + +.. image:: no-extend-lines.png + :alt: A CSS grid overlay with grid lines not extended infinitely + :class: border + +When you check the "Extend lines infinitely" option, the grid lines extend to the edge of the viewport along each axis. + +.. image:: extend-lines.png + :alt: A CSS grid overlay with grid lines extended infinitely + :class: border + + +Mini grid view +-------------- + +Shows a small version of the currently overlaid grid, which is in proportion to the real thing. + +.. image:: mini-grid-view.png + :alt: A mini CSS grid view from the Firefox DevTools + :class: border + +Hovering over the different areas of the mini grid causes the equivalent area on the grid overlay to also highlight, along with a tooltip containing useful information such as the dimensions of that area, its row and column numbers, etc. + +.. image:: mini-grid-highlight.png + :alt: A firefox screenshot showing an area of a mini CSS grid being highlighted in the DevTools, and the corresponding area in the real grid being highlighted on the web page. + :class: border + + +See also +******** + +- `labs.jensimmons.com <https://labs.jensimmons.com/>`_ — lots of interesting grid examples. +- `Grid by Example <https://gridbyexample.com/>`_ — CSS Grid learning resources from Rachel Andrew. +- `CSS Grid Layout <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout>`_ — MDN CSS Grid Layout references and tutorials. diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/line-numbers.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/line-numbers.png Binary files differnew file mode 100644 index 0000000000..7ee2f66af1 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/line-numbers.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/mini-grid-highlight.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/mini-grid-highlight.png Binary files differnew file mode 100644 index 0000000000..52b6c6cbfd --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/mini-grid-highlight.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/mini-grid-view.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/mini-grid-view.png Binary files differnew file mode 100644 index 0000000000..3b183a30f2 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/mini-grid-view.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/no-extend-lines.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/no-extend-lines.png Binary files differnew file mode 100644 index 0000000000..60fef3b1ce --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/no-extend-lines.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/no-grid-named-areas.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/no-grid-named-areas.png Binary files differnew file mode 100644 index 0000000000..7663001908 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/no-grid-named-areas.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/no-line-numbers.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/no-line-numbers.png Binary files differnew file mode 100644 index 0000000000..0f7eeacb00 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/no-line-numbers.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/overlay-grid-entry.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/overlay-grid-entry.png Binary files differnew file mode 100644 index 0000000000..3362f2577d --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/overlay-grid-entry.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/screen_shot_2016-12-16_at_10.51.15_am.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/screen_shot_2016-12-16_at_10.51.15_am.png Binary files differnew file mode 100644 index 0000000000..1ab8f708fa --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/screen_shot_2016-12-16_at_10.51.15_am.png diff --git a/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/subgrid-lines.png b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/subgrid-lines.png Binary files differnew file mode 100644 index 0000000000..ba95af74dc --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/examine_grid_layouts/subgrid-lines.png diff --git a/devtools/docs/user/page_inspector/how_to/index.rst b/devtools/docs/user/page_inspector/how_to/index.rst new file mode 100644 index 0000000000..07b4fd5a8a --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/index.rst @@ -0,0 +1,26 @@ +====== +How to +====== + +Links for various HOW TO's can be found here. These links describe in depth the HOW TO techniques. + + +- :doc:`CSS Flexbox Inspector: Examine Flexbox layouts <examine_flexbox_layouts/index>` +- :doc:`CSS Grid Inspector: Examine grid layouts <examine_grid_layouts/index>` +- :doc:`Debug scrollable overflow <debug_scrollable_overflow/index>` +- :doc:`Edit CSS filters <edit_css_filters/index>` +- :doc:`Edit Shape Paths in CSS <edit_css_shapes/index>` +- :doc:`Edit fonts <edit_fonts/index>` +- :doc:`Examine Event Listeners <examine_event_listeners/index>` +- :doc:`Examine and edit CSS <examine_and_edit_css/index>` +- :doc:`Examine and edit HTML <examine_and_edit_html/index>` +- :doc:`Examine and edit the box model <examine_and_edit_the_box_model/index>` +- :doc:`Inspect and select colors <inspect_and_select_colors/index>` +- :doc:`Open the Inspector <open_the_inspector/index>` +- :doc:`Reposition elements in the page <reposition_elements_in_the_page/index>` +- :doc:`Select an element <select_an_element/index>` +- :doc:`Select and highlight elements <select_and_highlight_elements/index>` +- :doc:`Use the Inspector from the Web Console <use_the_inspector_from_the_web_console/index>` +- :doc:`View background images <view_background_images/index>` +- :doc:`Visualize transforms <visualize_transforms/index>` +- :doc:`Work with animations <work_with_animations/index>` diff --git a/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/color-picker-bad-contrast.png b/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/color-picker-bad-contrast.png Binary files differnew file mode 100644 index 0000000000..237cedfd2f --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/color-picker-bad-contrast.png diff --git a/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/color-picker-good-contrast.png b/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/color-picker-good-contrast.png Binary files differnew file mode 100644 index 0000000000..56f47743ca --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/color-picker-good-contrast.png diff --git a/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/css_color_vars.png b/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/css_color_vars.png Binary files differnew file mode 100644 index 0000000000..49701dd100 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/css_color_vars.png diff --git a/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/index.rst b/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/index.rst new file mode 100644 index 0000000000..e521ef3947 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/index.rst @@ -0,0 +1,31 @@ +========================= +Inspect and select colors +========================= + +In the CSS Pane's :ref:`Rules view <page_inspector_ui_tour_rules_view>`, if a rule contains a color value, you'll see a sample of the color next to the value: + +.. image:: inspector-css-color-swatch.png + +A color sample is also shown for CSS custom properties (variables) that represent colors. + +.. image:: css_color_vars.png + :alt: CSS in the Rules pane showing a color swatch on a CSS variable + :class: border + +If you click on the color sample, you'll see a color picker popup, enabling you to change the color: + +.. image:: color-picker-good-contrast.png + :alt: Color picker showing a case of good contrast with the background + +.. image:: color-picker-bad-contrast.png + :alt: Color picker showing a case of poor contrast" src="color-picker-bad-contrast.png + +If the color is a foreground color, the color picker tells you whether its contrast with the background color meets accessibility guidelines. Hovering the mouse over the contrast message gives a more detailed explanation. + +The color picker includes an eyedropper icon: clicking this icon enables you to use the eyedropper to select a new color for the element from the page: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/0Zx1TN21QOo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> diff --git a/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/inspector-css-color-swatch.png b/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/inspector-css-color-swatch.png Binary files differnew file mode 100644 index 0000000000..742a25678d --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/inspect_and_select_colors/inspector-css-color-swatch.png diff --git a/devtools/docs/user/page_inspector/how_to/open_the_inspector/index.rst b/devtools/docs/user/page_inspector/how_to/open_the_inspector/index.rst new file mode 100644 index 0000000000..4e8fac602b --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/open_the_inspector/index.rst @@ -0,0 +1,32 @@ +================== +Open the Inspector +================== + +There are two main ways to open the Inspector: + +- Select the *Inspector* panel in the Web Developer Tools, accessible from the Browser Tools submenu +- Right-click an element on a web page and select *Inspect Element*. + + +The Inspector will appear at the bottom of the browser window: + +.. image:: pageinspector.png + :alt: The all-new Inspector in Firefox 57 DevTools. + :class: center + +You can also set the pane to appear at the left side of the browser window: + +.. image:: inspector_leftside.png + :class: center + +To the right side of the browser window: + +.. image:: inspector_rightside.png + :class: center + +Or in a separate window: + +.. image:: inspector_sidexside.png + :class: center + +To start finding your way around the Inspector, see the :doc:`UI tour <../../ui_tour/index>`. diff --git a/devtools/docs/user/page_inspector/how_to/open_the_inspector/inspector_leftside.png b/devtools/docs/user/page_inspector/how_to/open_the_inspector/inspector_leftside.png Binary files differnew file mode 100644 index 0000000000..be76e2862c --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/open_the_inspector/inspector_leftside.png diff --git a/devtools/docs/user/page_inspector/how_to/open_the_inspector/inspector_rightside.png b/devtools/docs/user/page_inspector/how_to/open_the_inspector/inspector_rightside.png Binary files differnew file mode 100644 index 0000000000..187bd6f3c6 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/open_the_inspector/inspector_rightside.png diff --git a/devtools/docs/user/page_inspector/how_to/open_the_inspector/inspector_sidexside.png b/devtools/docs/user/page_inspector/how_to/open_the_inspector/inspector_sidexside.png Binary files differnew file mode 100644 index 0000000000..f8d8f079d4 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/open_the_inspector/inspector_sidexside.png diff --git a/devtools/docs/user/page_inspector/how_to/open_the_inspector/pageinspector.png b/devtools/docs/user/page_inspector/how_to/open_the_inspector/pageinspector.png Binary files differnew file mode 100644 index 0000000000..a43b0d71e9 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/open_the_inspector/pageinspector.png diff --git a/devtools/docs/user/page_inspector/how_to/reposition_elements_in_the_page/box-model-reposition.png b/devtools/docs/user/page_inspector/how_to/reposition_elements_in_the_page/box-model-reposition.png Binary files differnew file mode 100644 index 0000000000..5df8f12024 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/reposition_elements_in_the_page/box-model-reposition.png diff --git a/devtools/docs/user/page_inspector/how_to/reposition_elements_in_the_page/in-content-box-model-editing.png b/devtools/docs/user/page_inspector/how_to/reposition_elements_in_the_page/in-content-box-model-editing.png Binary files differnew file mode 100644 index 0000000000..ba5e561554 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/reposition_elements_in_the_page/in-content-box-model-editing.png diff --git a/devtools/docs/user/page_inspector/how_to/reposition_elements_in_the_page/index.rst b/devtools/docs/user/page_inspector/how_to/reposition_elements_in_the_page/index.rst new file mode 100644 index 0000000000..80c3af6910 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/reposition_elements_in_the_page/index.rst @@ -0,0 +1,20 @@ +=============================== +Reposition elements in the page +=============================== + +Starting in Firefox 48 you can move absolutely positioned elements by dragging them around the page. + +If an element has its `position <https://developer.mozilla.org/en-US/docs/Web/CSS/position>`_ property set to ``absolute``, ``relative`` or ``fixed`` and one or more of the `top <https://developer.mozilla.org/en-US/docs/Web/CSS/top>`_, `bottom <https://developer.mozilla.org/en-US/docs/Web/CSS/bottom>`_ , `left <https://developer.mozilla.org/en-US/docs/Web/CSS/left>`_ or `right <https://developer.mozilla.org/en-US/docs/Web/CSS/right>`_ properties, :ref:`Box Model view <page_inspector_ui_tour_computed_view>` displays a button: + +.. image:: box-model-reposition.png + :class: center + +If you click that button, two handles appear next to the element: + +.. image:: in-content-box-model-editing.png + :alt: Example for changing the position of an element within the content + :class: center + +You can use these handles to drag the element around the page. + +If the element is absolutely positioned, dashed lines are shown representing the offset parent. For relatively positioned elements the dashed lines indicate the original position of the node. The offsets are indicated by a line and a tooltip for each side. diff --git a/devtools/docs/user/page_inspector/how_to/select_an_element/index.rst b/devtools/docs/user/page_inspector/how_to/select_an_element/index.rst new file mode 100644 index 0000000000..b6cfe9fba1 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/select_an_element/index.rst @@ -0,0 +1,37 @@ +================= +Select an element +================= + +The *selected element* is the element in the page that the Inspector is currently focused on. The selected element is shown in the :ref:`HTML pane <page_inspector_ui_tour_html_pane>` and its CSS is displayed in the :doc:`CSS pane <../../ui_tour/index>`. + +The *highlighted element* is the element that's overlaid in the page with a graphic showing the box model, and a tooltip showing its tag and size: + +.. image:: inspector-highlighted.png + :class: center + + +With the context menu +********************* + +To open the Inspector and select an element immediately, activate the context menu over the element in the page and select "Inspect Element" + + +With the HTML pane +****************** + +When the inspector is open, as you move the mouse around the elements listed in the HTML pane, the corresponding elements are highlighted in the page. Click an element in the HTML pane to select it. + +You can also use the arrow keys to move around the DOM with the keyboard. + + +.. _page-inspector-how-to-select-an-element-with-the-node-picker: + +With the node picker +******************** + +.. |image1| image:: node-picker.png + :width: 20 + +To select an element in the page itself, activate the "node picker" by clicking its icon: |image1| (also called the *Select Element* icon). After that, as you move the mouse around the page, the element under the mouse is highlighted. Click the element to select it: + +Starting in Firefox 52, if you :kbd:`Shift` + click the element, then it is selected but the picker stays active. This lets you see the rules for the element in the CSS pane, but conveniently select another element in the page. diff --git a/devtools/docs/user/page_inspector/how_to/select_an_element/inspector-highlighted.png b/devtools/docs/user/page_inspector/how_to/select_an_element/inspector-highlighted.png Binary files differnew file mode 100644 index 0000000000..2afa672cb5 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/select_an_element/inspector-highlighted.png diff --git a/devtools/docs/user/page_inspector/how_to/select_an_element/node-picker.png b/devtools/docs/user/page_inspector/how_to/select_an_element/node-picker.png Binary files differnew file mode 100644 index 0000000000..b1f86488b8 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/select_an_element/node-picker.png diff --git a/devtools/docs/user/page_inspector/how_to/select_and_highlight_elements/index.rst b/devtools/docs/user/page_inspector/how_to/select_and_highlight_elements/index.rst new file mode 100644 index 0000000000..e24dd5121d --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/select_and_highlight_elements/index.rst @@ -0,0 +1,33 @@ +============================= +Select and highlight elements +============================= + +The *selected* element is the element in the page that the Inspector is currently focused on. The selected element is shown in the :ref:`HTML pane <page_inspector_ui_tour_html_pane>` and its CSS is displayed in the :doc:`CSS pane <../../ui_tour/index>`. + +The *highlighted* element is the element that's overlaid in the page with a graphic showing the box model, and a tooltip showing its tag and size: + +.. image:: inspector-highlighted.png + :class: center + + +With the context menu +********************* + +To open the Inspector and select an element immediately, activate the context menu over the element in the page and select "Inspect Element" + + +With the HTML pane +****************** + +When the inspector is open, as you move the mouse around the elements listed in the HTML pane, the corresponding elements are highlighted in the page. Click an element in the HTML pane to select it + +You can also use the arrow keys to move around the DOM with the keyboard. + + +With the node picker +******************** + +.. |image1| image:: node-picker.png + :width: 20 + +To select an element in the page itself, activate the "node picker" by clicking its icon: |image1| (also called the *Select Element* icon). After that, as you move the mouse around the page, the element under the mouse is highlighted. Click the element to select it: diff --git a/devtools/docs/user/page_inspector/how_to/select_and_highlight_elements/inspector-highlighted.png b/devtools/docs/user/page_inspector/how_to/select_and_highlight_elements/inspector-highlighted.png Binary files differnew file mode 100644 index 0000000000..2afa672cb5 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/select_and_highlight_elements/inspector-highlighted.png diff --git a/devtools/docs/user/page_inspector/how_to/select_and_highlight_elements/node-picker.png b/devtools/docs/user/page_inspector/how_to/select_and_highlight_elements/node-picker.png Binary files differnew file mode 100644 index 0000000000..b1f86488b8 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/select_and_highlight_elements/node-picker.png diff --git a/devtools/docs/user/page_inspector/how_to/use_the_inspector_from_the_web_console/console-$0.png b/devtools/docs/user/page_inspector/how_to/use_the_inspector_from_the_web_console/console-$0.png Binary files differnew file mode 100644 index 0000000000..9ce705cfbe --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/use_the_inspector_from_the_web_console/console-$0.png diff --git a/devtools/docs/user/page_inspector/how_to/use_the_inspector_from_the_web_console/console-highlight.png b/devtools/docs/user/page_inspector/how_to/use_the_inspector_from_the_web_console/console-highlight.png Binary files differnew file mode 100644 index 0000000000..35bcda498b --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/use_the_inspector_from_the_web_console/console-highlight.png diff --git a/devtools/docs/user/page_inspector/how_to/use_the_inspector_from_the_web_console/index.rst b/devtools/docs/user/page_inspector/how_to/use_the_inspector_from_the_web_console/index.rst new file mode 100644 index 0000000000..b3a015dbba --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/use_the_inspector_from_the_web_console/index.rst @@ -0,0 +1,13 @@ +====================================== +Use the Inspector from the Web Console +====================================== + +The element that's currently selected in the Page Inspector can be referenced in the Web Console using the variable ``$0``. + +.. image:: console-$0.png + :class: center + +DOM elements in the Web Console output get a target next to them. If you hover over this target, the element is highlighted in the page, and if you click the target, the element is selected in the Inspector: + +.. image:: console-highlight.png + :class: center diff --git a/devtools/docs/user/page_inspector/how_to/view_background_images/css-copy-image-data-url.png b/devtools/docs/user/page_inspector/how_to/view_background_images/css-copy-image-data-url.png Binary files differnew file mode 100644 index 0000000000..461d442e4d --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/view_background_images/css-copy-image-data-url.png diff --git a/devtools/docs/user/page_inspector/how_to/view_background_images/css-image-preview.png b/devtools/docs/user/page_inspector/how_to/view_background_images/css-image-preview.png Binary files differnew file mode 100644 index 0000000000..14d0109f24 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/view_background_images/css-image-preview.png diff --git a/devtools/docs/user/page_inspector/how_to/view_background_images/index.rst b/devtools/docs/user/page_inspector/how_to/view_background_images/index.rst new file mode 100644 index 0000000000..ae2a72aadd --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/view_background_images/index.rst @@ -0,0 +1,14 @@ +====================== +View background images +====================== + +In the :ref:`Rules view <page_inspector_ui_tour_rules_view>`, you can see a preview of images specified using `background-image <https://developer.mozilla.org/en-US/docs/Web/CSS/background-image>`_. Just hover over the rule: + +.. image:: css-image-preview.png + :class: center + + +From Firefox 41, if you right-click the image declaration, you'll see an option to copy the image as a data: URL: + +.. image:: css-copy-image-data-url.png + :class: center diff --git a/devtools/docs/user/page_inspector/how_to/visualize_transforms/index.rst b/devtools/docs/user/page_inspector/how_to/visualize_transforms/index.rst new file mode 100644 index 0000000000..46eb500581 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/visualize_transforms/index.rst @@ -0,0 +1,8 @@ +==================== +Visualize transforms +==================== + +If you hover over a `transform <https://developer.mozilla.org/en-US/docs/Web/CSS/transform>`_ property in the :ref:`Rules view <page_inspector_ui_tour_rules_view>`, you'll see the transformation overlaid in the page: + +.. image:: transform.png + :class: center diff --git a/devtools/docs/user/page_inspector/how_to/visualize_transforms/transform.png b/devtools/docs/user/page_inspector/how_to/visualize_transforms/transform.png Binary files differnew file mode 100644 index 0000000000..e11bba7292 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/visualize_transforms/transform.png diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_details.png b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_details.png Binary files differnew file mode 100644 index 0000000000..1e6e934fae --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_details.png diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_icon_details.png b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_icon_details.png Binary files differnew file mode 100644 index 0000000000..ef3fdd74c7 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_icon_details.png diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_icon_scale.png b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_icon_scale.png Binary files differnew file mode 100644 index 0000000000..44a5dbb37a --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_icon_scale.png diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_(firefox_41_and_42)/index.rst b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_(firefox_41_and_42)/index.rst new file mode 100644 index 0000000000..1299bca57c --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_(firefox_41_and_42)/index.rst @@ -0,0 +1,25 @@ +======================================= +Animation inspector (Firefox 41 and 42) +======================================= + +.. note:: + Note that the Animation inspector's UI was revamped in Firefox 43. To see what the Animation inspector looks like in Firefox 43 and subsequent releases, see :ref:`the main "Work with animations" page <page-inspector-how-to-work-with-animations-animation-inspector>`. + + +The Animation inspector enables you to: + + +- see information about all animations running in the page +- play/pause all animations +- play/pause/rewind/fast-forward each animation +- jump to a specific point in an animation +- highlight and inspect the animated node +- adjust the playback rate of each animation +- see whether an animation is running in the compositor thread (a lightning bolt icon is displayed next to such animations) + + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/0vSIuKaqD8o" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_example_colon__css_transitions/developer.png b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_example_colon__css_transitions/developer.png Binary files differnew file mode 100644 index 0000000000..331428f162 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_example_colon__css_transitions/developer.png diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_example_colon__css_transitions/index.rst b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_example_colon__css_transitions/index.rst new file mode 100644 index 0000000000..a4bd7bea2d --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_example_colon__css_transitions/index.rst @@ -0,0 +1,93 @@ +============================================ +Animation inspector example: CSS transitions +============================================ + +Firefox Logo Animation +********************** + +Example animation using `CSS transitions <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions>`_. + +HTML Content +------------ + +.. code-block:: html + + <div class="channel"> + <img src="developer.png" class="icon"/> + <span class="note">Firefox Developer Edition</span> + </div> + + +CSS Content +----------- + +.. code-block:: css + + .channel { + padding: 2em; + margin: 0.5em; + box-shadow: 1px 1px 5px #808080; + margin: 1.5em; + } + + .channel > * { + vertical-align: middle; + line-height: normal; + } + + .icon { + width: 50px; + height: 50px; + filter: grayscale(100%); + transition: transform 750ms ease-in, filter 750ms ease-in-out; + } + + .note { + margin-left: 1em; + font: 1.5em "Open Sans",Arial,sans-serif; + overflow: hidden; + white-space: nowrap; + display: inline-block; + + opacity: 0; + width: 0; + transition: opacity 500ms 150ms, width 500ms 150ms; + } + + .icon#selected { + filter: grayscale(0%); + transform: scale(1.5); + } + + .icon#selected+span { + opacity: 1; + width: 300px; + } + + +JavaScript Content +------------------ + +.. code-block:: JavaScript + + function toggleSelection(e) { + if (e.button != 0) { + return; + } + if (e.target.classList.contains("icon")) { + var wasSelected = (e.target.getAttribute("id") == "selected"); + clearSelection(); + if (!wasSelected) { + e.target.setAttribute("id", "selected"); + } + } + } + + function clearSelection() { + var selected = document.getElementById("selected"); + if (selected) { + selected.removeAttribute("id"); + } + } + + document.addEventListener("click", toggleSelection); diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_example_colon__web_animations_api/developer.png b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_example_colon__web_animations_api/developer.png Binary files differnew file mode 100644 index 0000000000..331428f162 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_example_colon__web_animations_api/developer.png diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_example_colon__web_animations_api/index.rst b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_example_colon__web_animations_api/index.rst new file mode 100644 index 0000000000..6d6f2cb045 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_inspector_example_colon__web_animations_api/index.rst @@ -0,0 +1,117 @@ +=============================================== +Animation inspector example: Web Animations API +=============================================== + +Firefox Logo Animation +********************** + +Example animation using the `Web Animations API <https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API>`_. + + +HTML Content +------------ + +.. code-block:: html + + <div class="channel"> + <img src="developer.png" id="icon"/> + <span id="note">Firefox Developer Edition</span> + </div> + + +CSS Content +----------- + +.. code-block:: css + + .channel { + padding: 2em; + margin: 0.5em; + box-shadow: 1px 1px 5px #808080; + margin: 1.5em; + } + + .channel > * { + vertical-align: middle; + line-height: normal; + } + + #icon { + width: 50px; + height: 50px; + filter: grayscale(100%); + } + + #note { + margin-left: 1em; + font: 1.5em "Open Sans",Arial,sans-serif; + overflow: hidden; + white-space: nowrap; + display: inline-block; + opacity: 0; + width: 0; + } + + +.. _page-inspector-work-with-animations-web-example-js-content: + +JavaScript Content +------------------ + +.. code-block:: JavaScript + + var iconKeyframeSet = [ + { transform: 'scale(1)', filter: 'grayscale(100%)'}, + { filter: 'grayscale(100%)', offset: 0.333}, + { transform: 'scale(1.5)', offset: 0.666 }, + { transform: 'scale(1.5)', filter: 'grayscale(0%)'} + ]; + + var noteKeyframeSet = [ + { opacity: '0', width: '0'}, + { opacity: '1', width: '300px'} + ]; + + var iconKeyframeOptions = { + duration: 750, + fill: 'forwards', + easing: 'ease-in', + endDelay: 100 + } + + var noteKeyframeOptions = { + duration: 500, + fill: 'forwards', + easing: 'ease-out', + delay: 150 + } + + var icon = document.getElementById("icon"); + var note = document.getElementById("note"); + + var iconAnimation = icon.animate(iconKeyframeSet, iconKeyframeOptions); + var noteAnimation = note.animate(noteKeyframeSet, noteKeyframeOptions); + + iconAnimation.pause(); + noteAnimation.pause(); + + var firstTime = true; + + function animateChannel(e) { + if (e.button != 0) { + return; + } + if (e.target.id != "icon") { + return; + } + if (firstTime) { + iconAnimation.play(); + noteAnimation.play(); + firstTime = false; + } else { + iconAnimation.reverse(); + noteAnimation.reverse(); + } + } + + document.addEventListener("click", animateChannel); diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_not_optimized.png b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_not_optimized.png Binary files differnew file mode 100644 index 0000000000..9105060404 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_not_optimized.png diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_pane.png b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_pane.png Binary files differnew file mode 100644 index 0000000000..02fe47b31e --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_pane.png diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_swoosh_01.png b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_swoosh_01.png Binary files differnew file mode 100644 index 0000000000..ea91cc8756 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/animation_swoosh_01.png diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/compositor.png b/devtools/docs/user/page_inspector/how_to/work_with_animations/compositor.png Binary files differnew file mode 100644 index 0000000000..ec9f9a4789 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/compositor.png diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/index.rst b/devtools/docs/user/page_inspector/how_to/work_with_animations/index.rst new file mode 100644 index 0000000000..215a86a318 --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/index.rst @@ -0,0 +1,227 @@ +==================== +Work with animations +==================== + +This article covers three tools you can use to visualize and edit animations: + + +- :ref:`the animation inspector <page-inspector-how-to-work-with-animations-animation-inspector>` + +- :ref:`diting @keyframes <page-inspector-how-to-work-with-animations-edit-keyframes>` + +- :ref:`editing timing functions <page-inspector-how-to-work-with-animations-edit-timing-functions>` + + +.. _page-inspector-how-to-work-with-animations-animation-inspector: + +Animation inspector +******************* + +The Page Inspector's :ref:`Animations view <page_inspector_ui_tour_animations_view>` displays animations in the page synchronized along a timeline, with a draggable widget you can use to move to any point in the timeline and see the page at that point. + +It displays animations created using `CSS transitions <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions>`_, `CSS @keyframes rules <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations>`_, or the `Web Animations API <https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API>`_. Starting in Firefox 48, it will show animations applied to the `::before <https://developer.mozilla.org/en-US/docs/Web/CSS/::before>`_ and `::after <https://developer.mozilla.org/en-US/docs/Web/CSS/::after>`_ pseudo-elements. + +To see how it works, we'll walk through an example. The box below contains a grayscale icon, representing `Firefox Developer Edition <https://www.mozilla.org/en-US/firefox/developer/>`_. If you click the icon, it enlarges and changes to color, and the name of the browser appears. Click the icon again to reverse the effect. + + +These animations are made using the `Web Animations API <https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API>`_. + +Let's use the animation inspector to see what's going on in this example. + + +1. Right-click in the box and select "Inspect Element" +2. Make sure the selected element is the ``<div class="channel">`` +3. Switch over to the "Animations" tab +4. Play the animation + + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/XmKeAKryE5I" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +Let's take a closer look at the contents of the animation inspector here: + +.. image:: animation_pane.png + :class: border + + +It shows a synchronized timeline for every animation applied to the selected element or its children. The timeline starts at the start of the first animation, ends at the end of the last animation, and is labeled with markers every 250 milliseconds (this depends on the time scale of the animations currently displayed). + + +Animation bars +-------------- + +Each animation or transition is shown as a horizontal bar laid across the timeline. The bar is: + + +- blue if a `transition <https://developer.mozilla.org/en-US/docs/Web/CSS/transition>`_) was used to animate a property +- orange if a `@keyframes animation <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations>`_ was used +- green if the `Web Animations API <https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API>`_ was used + +.. |image1| image:: compositor.png + :width: 20 + +The bar contains a lightning bolt icon |image1| if the property was animated using the compositor thread (see more about the :ref:`cost of animating different CSS properties <performance-scenarios-animating_css_properties-css-property-cost>`). + +The bar is shaped to reflect the easing effect used for the animation. In the example above you can see that the first bar is concave, representing ease-in, and the second is convex, representing ease-out. + +If the animation used CSS transitions, there is one bar for each property transitioned, and it is labeled with the name of the property being transitioned. If the animation used CSS ``@keyframes``, there is one bar for each animation, labeled with its name. + +If the animation or transition had a delay, this is shown as a cross-hatched portion of the bar. `delay and endDelay <https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/KeyframeEffect>`_ are both represented. + +If you hover over the bar, a tooltip appears, giving you more detailed information about the animation or transition, including: + + +- the type of animation: CSS transition, CSS animation, or Web Animations API +- the duration of the animation +- the animation's start and end delay +- the animation's easing (or timing function). +- the animation's fill +- the Playback rate of the animation + + +.. image:: animation_details.png + :class: border + + +Information about the animated element +-------------------------------------- + +To the left of each bar is a selector for the element that the animation applies to. If you hover over this selector, the element is highlighted in the page. Click the selector to select the element in the inspector. + +.. |image2| image:: target-icon.png + +To the left of the selector is a "target" icon (|image2|). Clicking this icon locks the highlighter on the element. + + +Animation details +----------------- + +If you click one of the bars, you'll see details of all the properties that were changed in the animation. For example, try clicking on the bar for ``img#icon`` animation: + +.. image:: animation_icon_details.png + :class: border + + +This is telling us that two properties were modified: `filter <https://developer.mozilla.org/en-US/docs/Web/CSS/filter>`_ and `transform <https://developer.mozilla.org/en-US/docs/Web/CSS/transform>`_. Each dot represents an entry for that property in the set of keyframes used for the animation. Both properties were initialized at 0ms and finalized at 750ms. ``filter`` was given a value at 250ms and ``transform`` at 500ms. If you hover over a dot, you'll see the value assigned to that property at that point in the timeline: + +.. image:: animation_icon_scale.png + :class: border + + +This is essentially a visual representation of the animation's +:ref:`keyframes <page-inspector-work-with-animations-web-example-js-content>`: + +.. code-block:: JavaScript + + var iconKeyframeSet = [ + { transform: 'scale(1)', filter: 'grayscale(100%)' }, + { filter: 'grayscale(100%)', offset: 0.333 }, + { transform: 'scale(1.5)', offset: 0.666 }, + { transform: 'scale(1.5)', filter: 'grayscale(0%)' } + ]; + + +Application to the example +-------------------------- + +Applying all this to our example, we can see that: + + +- The animation involved two elements, ``span#note`` and ``img#icon``. Hovering over these selectors, we can see that those elements are, respectively, the browser name "Firefox Developer Edition" and the browser icon. +- The ``img#icon`` animation: + + - animated the `filter <https://developer.mozilla.org/en-US/docs/Web/CSS/filter>`_ and `transform <https://developer.mozilla.org/en-US/docs/Web/CSS/transform>`_ properties, to scale the icon and color it + - lasted 750ms, had an ``endDelay`` of 100ms + - used the compositor thread + - was given an `easing <https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/KeyframeEffect>`_ value of ``ease-in``: you can see this by the concave shape of the green bar. + +- The ``span#note`` animation: + + - animated the `width <https://developer.mozilla.org/en-US/docs/Web/CSS/width>`_ and `opacity <https://developer.mozilla.org/en-US/docs/Web/CSS/opacity>`_ properties, to make the name gradually appear + - lasted 500ms, and had a ``delay`` of 150ms + - was given an `easing <https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/KeyframeEffect>`_ value of ``ease-out``: you can see this by the convex shape of the green bar. + + +Animation playback +------------------ + +At the top of the animation inspector: + + +- there are buttons to play/pause and restart the animation +- there's a dropdown to change the animation playback rate +- the current time in the animation is displayed. + + +Finally, if you click inside the bar at the top of the timeline, you get a scrubber that you can drag left and right to move backwards and forwards through the animation, and pinpoint exactly what's happening when. + + +Further information about animation compositing +----------------------------------------------- + +If you open `animation-inspector-compositing.html <https://firefox-devtools.github.io/devtools-examples/animation-inspector/animation-inspector-compositing.html>`_ and click the red rectangle, a simple `opacity <https://developer.mozilla.org/en-US/docs/Web/CSS/opacity>`_ animation will start. If you look at this in the Animation Inspector in Firefox 49+, you'll see that: + + +- The white lightning bolt icon now indicates whether all the animation properties have been optimized by running them through the compositor, where possible. +- The bar tooltip also includes this information, as a further reminder. You'll get a message of "All animation properties are optimized." +- The expanded animation information now includes a lightning bolt icon next to the properties whose animation has been optimized via the compositor. + + +.. image:: animation_swoosh_01.png + :class: border + +Let's now look at `animation-inspector-compositing-silly.html <https://firefox-devtools.github.io/devtools-examples/animation-inspector/animation-inspector-compositing-silly.html>`_ — this is the same example, except that now once the red rectangle is clicked we animate both the `left <https://developer.mozilla.org/en-US/docs/Web/CSS/left>`_ and `transform <https://developer.mozilla.org/en-US/docs/Web/CSS/transform>`_ (with a translation) properties at the same time as `opacity <https://developer.mozilla.org/en-US/docs/Web/CSS/opacity>`_. It doesn't make much sense to try to animate a geometric property and a translation at the same time — the two effects won't be synchronized — so the ``transform`` property is deliberately not handed over to the compositor to handle. The Animation Inspector will tell you this — look at it now and you'll see that: + + +- The white lightning bolt icon in the bar has been replaced with a grey lightning bolt icon, to indicate that only some of the relevant properties are being optimized by the compositor. +- The bar tooltip also includes this information, as a further reminder. You'll get a message of "Some animation properties are optimized." +- Properties whose animation is **not** being optimized, but could be if you improved your code, are now given a dotted underline — see transform in the screenshot below. Hovering over this gives you a tooltip that explains why. In this case, the message is "Animations of 'transform' cannot be run on the compositor when geometric properties are animated on the same element at the same time." + + +.. image:: animation_not_optimized.png + :class: border + + +.. _page-inspector-how-to-work-with-animations-edit-keyframes: + +Edit @keyframes +*************** + +Any `@keyframes rules <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations>`_ associated with the currently selected element are displayed in the :ref:`Rules view <page_inspector_ui_tour_rules_view>` and are editable: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/mDHtLK88ZW4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +.. _page-inspector-how-to-work-with-animations-edit-timing-functions: + +Edit timing functions +********************* + +When you `create a CSS animation <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations>`_ you can specify a `timing function <https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function>`_: this determines the rate at which the animation progresses. One way to specify the timing function is with a cubic Bézier curve. + +Timing functions defined as cubic Bézier curves get an icon in the Rules view. If you click the icon you get a visual editor for the curve, enabling you to drag `P1 and P2 <https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function#the_cubic-bezier()_class_of_timing-functions>`_, and see the results in the page: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/GW5-R2ewaqA" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +This feature uses open source code from `Lea Verou’s cubic-bezier.com <https://cubic-bezier.com/>`_. + +The cubic Bézier editor includes a number of presets, grouped under "Ease-in", "Ease-out", and "Ease-in-out": + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/Jx-J2Yy0aSg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> diff --git a/devtools/docs/user/page_inspector/how_to/work_with_animations/target-icon.png b/devtools/docs/user/page_inspector/how_to/work_with_animations/target-icon.png Binary files differnew file mode 100644 index 0000000000..84ab9afa0c --- /dev/null +++ b/devtools/docs/user/page_inspector/how_to/work_with_animations/target-icon.png diff --git a/devtools/docs/user/page_inspector/index.rst b/devtools/docs/user/page_inspector/index.rst new file mode 100644 index 0000000000..f9608e2ac7 --- /dev/null +++ b/devtools/docs/user/page_inspector/index.rst @@ -0,0 +1,47 @@ +============== +Page Inspector +============== + +Use the Page Inspector to examine and modify the HTML and CSS of a page. + +You can examine pages loaded in the local copy of Firefox or in a remote target such as Firefox for Android. See :doc:`about debugging <../about_colon_debugging/index>` to learn how to connect the developer tools to a remote target. + + +User Interface Tour +******************* + +To find your way around the Inspector, here's a :doc:`quick tour of the UI <ui_tour/index>`. + +You can split the Rules view out into its own pane, separate from the other tabs on the CSS pane — this is called :doc:`3-pane mode <3-pane_mode/index>`. + + +How to +****** + +To find out what you can do with the Inspector, see the following how to guides: + +- :doc:`Open the Inspector <how_to/open_the_inspector/index>` +- :doc:`Examine and edit HTML <how_to/examine_and_edit_html/index>` +- :doc:`Examine and edit the box model <how_to/examine_and_edit_the_box_model/index>` +- :doc:`Inspect and select colors <how_to/inspect_and_select_colors/index>` +- :doc:`Reposition elements in the page <how_to/reposition_elements_in_the_page/index>` +- :doc:`Edit fonts <how_to/edit_fonts/index>` +- :doc:`Visualize transforms <how_to/visualize_transforms/index>` +- :doc:`Select an element <how_to/select_an_element/index>` +- :doc:`Examine and edit CSS <how_to/examine_and_edit_css/index>` +- :doc:`Examine event listeners <how_to/examine_event_listeners/index>` +- :doc:`Work with animations <how_to/work_with_animations/index>` +- :doc:`Edit CSS filters <how_to/edit_css_filters/index>` +- :doc:`Edit CSS shapes <how_to/edit_css_shapes/index>` +- :doc:`View background images <how_to/view_background_images/index>` +- :doc:`Use the Inspector from the Web Console <how_to/use_the_inspector_from_the_web_console/index>` +- :doc:`Examine CSS grid layouts <how_to/examine_grid_layouts/index>` +- :doc:`Examine CSS flexbox layouts <how_to/examine_flexbox_layouts/index>` +- :doc:`Use the Accessibility Inspector <../accessibility_inspector/index>` + + +Reference +********* + +- :ref:`Keyboard shortcuts <keyboard-shortcuts-page-inspector>` +- :ref:`Settings <settings-inspector>` diff --git a/devtools/docs/user/page_inspector/ui_tour/animation_detail.png b/devtools/docs/user/page_inspector/ui_tour/animation_detail.png Binary files differnew file mode 100644 index 0000000000..95909753cf --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/animation_detail.png diff --git a/devtools/docs/user/page_inspector/ui_tour/compat_panel_settings.png b/devtools/docs/user/page_inspector/ui_tour/compat_panel_settings.png Binary files differnew file mode 100644 index 0000000000..2401873c8e --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/compat_panel_settings.png diff --git a/devtools/docs/user/page_inspector/ui_tour/compat_view.png b/devtools/docs/user/page_inspector/ui_tour/compat_view.png Binary files differnew file mode 100644 index 0000000000..138621bcd2 --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/compat_view.png diff --git a/devtools/docs/user/page_inspector/ui_tour/index.rst b/devtools/docs/user/page_inspector/ui_tour/index.rst new file mode 100644 index 0000000000..33abe11538 --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/index.rst @@ -0,0 +1,161 @@ +======= +UI Tour +======= + +This article is a quick tour of the main sections of the Page Inspector's user interface. + +It covers the three top-level components of the Inspector's UI: + +- the "Select element" button +- the HTML pane +- the CSS pane + + +.. image:: pageinspector.png + :alt: The all-new Inspector panel in Firefox 57. + +This guide is intentionally kept as short as possible. It links to various how to guides for the details of how to work with the Inspector. + + +.. _page_inspector_select_element_button: + +Select element button +********************* + +The Inspector gives you detailed information about the currently selected element. The Select element button is one way you can select an element for inspection: + +.. image:: select_element_button.png + :alt: This is the button in Firefox 57 Inspector you can use to select elements on a web page. + :class: center + +Note that it's actually part of the :ref:`main toolbox toolbar <tools-toolbox-toolbar>`, so it's immediately accessible from any tool, not just the Inspector. + +To learn how to select an element, see the guide to :doc:`selecting an element <../how_to/select_an_element/index>`. + + +.. _page_inspector_ui_tour_html_pane: + +HTML pane +********* + +The Inspector is split into two or three sections, depending on your settings. You can toggle the 3-pane view of the Inspector. The following image shows the 2-pane layout: + +.. image:: inspector_2pane.png + :alt: These are the tasty new HTML and CSS panes in Firefox 57. + :class: border + + +In 2-pane mode, the Inspector includes the HTML Pane, and the CSS Pane, which can contain one of six tools: + +- Rules view +- Layout view +- Computed view +- Changes view +- Compatibility view (Firefox Developer Edition 77 and later) +- Fonts view +- Animations view + +The following image shows the 3-pane mode (available from Firefox 62 onwards) which moves the CSS Rules view into a separate pane in the center of the Inspector. The following image shows 3-pane mode: + +.. image:: inspector_tool.png + :class: border + +As you can see, the CSS pane has moved into the center of the Inspector. To learn more about the structure of the HTML pane, see the guide to :doc:`examining and editing HTML <../how_to/examine_and_edit_html/index>`. + + +.. _page_inspector_ui_tour_rules_view: + +Rules view +********** + +The Rules view lists all the rules that apply to the selected element, ordered from most-specific to least-specific. See above. + +.. image:: indpextor_rules.png + :alt: The rules view within the Inspector. + :class: border + +See :doc:`Examine and edit CSS <../how_to/examine_and_edit_css/index>` for more details. + + +Layout view +*********** + +The Layout view displays the box model of the page. If the page includes any sections using either the Flexbox display model or CSS Grids, this view shows the Flexbox or Grid settings used on the page. + +.. image:: inspector_layout.png + :class: border + +To learn more about the Layout view, see :doc:`Examine and edit the box model <../how_to/examine_and_edit_the_box_model/index>`. Note that before Firefox 50, the box model view did not appear in the "Layout view" tab, but had its own tab. + + +Changes view +************ + +When you are editing in the Rules view, you can see the changes you have made in the Changes view. + +.. image:: track_changes.png + :class: border + +.. _page_inspector_ui_tour_computed_view: + +Computed view +************* + +The Computed view shows you the complete computed CSS for the selected element (The computed values are the same as what `getComputedStyle <https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle>`_ would return): + +.. image:: inspector_computed.png + :alt: The Computed view within the Inspector + :class: border + +To learn more about the CSS declarations listed in this view, see :ref:`Examine computed CSS <page_inspector_how_to_examine_and_edit_css_examine_computed_css>`. + + +.. _page_inspector_ui_tour_compatibility_view: + +Compatibility view +****************** + +Starting with Firefox Developer Edition version 77, the Compatibility view shows CSS compatibility issues, if any, for properties applied to the selected element, and for the current page as a whole. It shows icons for the browsers that *do* support the properties, and notes properties that are experimental or deprecated. + +.. image:: compat_view.png + :alt: Screenshot of the Compatibility view + :class: center + + +- Click the name of the property to open the reference article for that property on *MDN Web Docs*. The "Browser compatibility" section of the article gives details of browser support for the property. + +- In the **All Issues** section, click the name of the element that uses the property to select that element in the inspector. If more than one element has a given property applied to it, click the triangle to show all the occurrences. + +- To configure the set of browsers you want the Compatibility view to check for, click **Settings** at the bottom of the panel. + +.. image:: compat_panel_settings.png + :alt: Screenshot of the Settings for the Compatibility view + + +Untick the checkbox for any browser you are not interested in. As new browser versions are released, the version numbers in this list will be updated. + + +Fonts view +********** + +The Fonts view shows all the fonts in the page along with editable samples. + +.. image:: inspector_fonts.png + :alt: The all-new Inspector panel in Firefox 57. + :class: border + +See :doc:`View fonts <../how_to/edit_fonts/index>` for more details. + + +.. _page_inspector_ui_tour_animations_view: + +Animations view +*************** + +The Animations view gives you details of any animations applied to the selected element, and a controller to pause them: + +.. image:: animation_detail.png + :alt: This is the Animations pane in the Firefox 57 Inspector. + :class: border + +See :doc:`Work with animations <../how_to/work_with_animations/index>` for more details. diff --git a/devtools/docs/user/page_inspector/ui_tour/indpextor_rules.png b/devtools/docs/user/page_inspector/ui_tour/indpextor_rules.png Binary files differnew file mode 100644 index 0000000000..c1f3c35e7c --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/indpextor_rules.png diff --git a/devtools/docs/user/page_inspector/ui_tour/inspector_2pane.png b/devtools/docs/user/page_inspector/ui_tour/inspector_2pane.png Binary files differnew file mode 100644 index 0000000000..42834664ea --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/inspector_2pane.png diff --git a/devtools/docs/user/page_inspector/ui_tour/inspector_computed.png b/devtools/docs/user/page_inspector/ui_tour/inspector_computed.png Binary files differnew file mode 100644 index 0000000000..dcd8594aca --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/inspector_computed.png diff --git a/devtools/docs/user/page_inspector/ui_tour/inspector_fonts.png b/devtools/docs/user/page_inspector/ui_tour/inspector_fonts.png Binary files differnew file mode 100644 index 0000000000..138361f572 --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/inspector_fonts.png diff --git a/devtools/docs/user/page_inspector/ui_tour/inspector_layout.png b/devtools/docs/user/page_inspector/ui_tour/inspector_layout.png Binary files differnew file mode 100644 index 0000000000..dfdcc2b714 --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/inspector_layout.png diff --git a/devtools/docs/user/page_inspector/ui_tour/inspector_tool.png b/devtools/docs/user/page_inspector/ui_tour/inspector_tool.png Binary files differnew file mode 100644 index 0000000000..9c32d3ae62 --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/inspector_tool.png diff --git a/devtools/docs/user/page_inspector/ui_tour/pageinspector.png b/devtools/docs/user/page_inspector/ui_tour/pageinspector.png Binary files differnew file mode 100644 index 0000000000..a43b0d71e9 --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/pageinspector.png diff --git a/devtools/docs/user/page_inspector/ui_tour/select_element_button.png b/devtools/docs/user/page_inspector/ui_tour/select_element_button.png Binary files differnew file mode 100644 index 0000000000..8c19f3b1c0 --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/select_element_button.png diff --git a/devtools/docs/user/page_inspector/ui_tour/track_changes.png b/devtools/docs/user/page_inspector/ui_tour/track_changes.png Binary files differnew file mode 100644 index 0000000000..d4e234b9c5 --- /dev/null +++ b/devtools/docs/user/page_inspector/ui_tour/track_changes.png diff --git a/devtools/docs/user/performance/index.rst b/devtools/docs/user/performance/index.rst new file mode 100644 index 0000000000..99758279ff --- /dev/null +++ b/devtools/docs/user/performance/index.rst @@ -0,0 +1,7 @@ +=========== +Performance +=========== + +The documentation about the new performance took (also known as the Firefox +Profiler) can be found on the `Firefox Profiler website +<https://profiler.firefox.com/docs/>`_. diff --git a/devtools/docs/user/responsive_button.png b/devtools/docs/user/responsive_button.png Binary files differnew file mode 100644 index 0000000000..71d2bc8d1a --- /dev/null +++ b/devtools/docs/user/responsive_button.png diff --git a/devtools/docs/user/responsive_design_mode/index.rst b/devtools/docs/user/responsive_design_mode/index.rst new file mode 100644 index 0000000000..cf11458e2a --- /dev/null +++ b/devtools/docs/user/responsive_design_mode/index.rst @@ -0,0 +1,208 @@ +====================== +Responsive Design Mode +====================== + +`Responsive design <https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Responsive/responsive_design_building_blocks>`_ is the practice of designing a website so it looks and works properly on a range of different devices — particularly mobile phones and tablets as well as desktops and laptops. + +The most obvious factor here is screen size, but there are other factors as well, including the pixel density of the display and whether it supports touch. Responsive Design Mode gives you a simple way to simulate these factors, to test how your website will look and behave on different devices. + + +Toggling Responsive Design Mode +******************************* + +There are three ways to toggle Responsive Design Mode: + + +- From the Firefox menu: Select **Responsive Design Mode** from the **Browser Tools** submenu in the Firefox Menu (or **Tools** menu if you display the menu bar or are on macOS). +- From the Developer Tools toolbox: Press the **Responsive Design Mode** button in the :ref:`Toolbox's toolbar <tools-toolbox-toolbar>` +- From the keyboard: Press :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`M` (or :kbd:`Cmd` + :kbd:`Opt` + :kbd:`M` on macOS). + + +Controlling Responsive Design Mode +********************************** + +With Responsive Design Mode enabled, the content area for web pages is set to the screen size for a mobile device. Initially, it's set to 320 x 480 pixels. + +.. note:: + + The device you select when in Responsive Design Mode and the orientation (portrait or landscape) is saved between sessions. + +.. image:: responsive_ui.png + :class: center + +Information for the selected device is centered over the display. From left to right, the display includes: + + +- *Name of the selected device* - A drop-down list that includes whatever devices you have selected from the Device Settings screen. +- *Screen size* - You can edit the width and height values to change the device size by editing a number directly or using the :kbd:`Up` and :kbd:`Down` keys to increase or decrease the value by 1 pixels on each keypress or hold and :kbd:`Shift` to change the value by 10. + + - The mouse wheel changes the size values by 1 pixel at a time + - You can also change the device's screen size by grabbing the bottom-right corner of the viewport and dragging it to the size you want. + + +- *Orientation (portrait or landscape)* - This setting persists between sessions +- *DPR (pixel ratio)* - Beginning with Firefox 68, the DPR is no longer editable; create a custom device in order to change the DPR +- *Throttling* - A drop-down list where you can select the connection throttling to apply, for example 2G, 3G, or LTE +- *Enable/Disable touch simulation* - Toggles whether or not Responsive Design Mode simulates touch events. While touch event simulation is enabled, mouse events are translated into `touch events <https://developer.mozilla.org/en-US/docs/Web/API/Touch_events>`_; this includes (starting in Firefox 79) translating a mouse-drag event into a touch-drag event. (Note that when touch simulation is enabled, this toolbar icon is blue; when simulation is disabled, it is black. + + +On the right end of the screen, three buttons allow you to: + + +.. _responsive-design-mode-camera-button: + +- *Camera button* - take a screenshot + + - Screenshots are saved to Firefox's default download location. + - If you checked "Screenshot to clipboard" in the Developer Tools :doc:`Settings <../settings/index>` page, then the screenshot will be copied to the system clipboard. + +- *Settings button* - Opens the RDM settings menu +- *Close button* - closes RDM mode and returns to regular browsing + + +The Settings menu includes the following commands: + +.. image:: responsive_setting_menu.png + :class: center + +- *Left-align Viewport* - when checked moves the RDM viewport to the left side of the browser window +- *Show user agent* - when checked displays the user agent string +- The final two options define when the page is reloaded: + + - *Reload when touch simulation is toggled:* when this option is enabled, the page is reloaded whenever you toggle touch support. + - *Reload when user agent is changed:* when this option is enabled, the page is reloaded whenever the user agent is changed. + + +Reloading on these changes can be helpful because certain page behaviors would otherwise not be applied. For example, many pages check for touch support on load and only add event handlers if it is supported, or only enable certain features on certain browsers. + +However, if you are not interested in examining such features (maybe you are just checking the overall layout at different sizes), these reloads can waste time and possibly result in the loss of productive work, so it is useful to be able to control these reloads. + +Now when you change such settings for the first time, you are given a warning message that tells you these reloads are no longer automatic, and informed about how you can make them automatic. For example: + + +.. image:: page-reload-warning.png + :class: center + + +Developer Toolbox with RDM +************************** + +You can show or hide the Developer Tools toolbox independently of toggling Responsive Design Mode itself: + +.. image:: rdmdevtools.png + :class: center + +While Responsive Design Mode is enabled, you can continue browsing as you normally would in the resized content area. + + +Device selection +**************** + +Just above the viewport there is a label "no device selected"; click this to see a list of device names. Select a device, and Responsive Design Mode sets the following properties to match the selected device: + + +- Screen size +- Device pixel ratio (the ratio of device physical pixels to device-independent pixels) +- Touch event simulation + + +Additionally, Firefox sets the `User-Agent <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent>`_ HTTP request header to identify itself as the default browser on the selected device. For example, if you've selected an iPhone, then Firefox identifies itself as Safari. The `navigator.userAgent <https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgent>`_ property is set to the same value. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/JNAyKemudv0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +The devices listed in the drop-down are just a subset of the devices that can be selected. At the end of the drop-down, there is an item labeled **Edit list**. Select this to see a panel containing all the choices, which enables you to check the devices you want to see in the drop-down. + + +Creating custom devices +----------------------- + +You can create and save custom devices in Responsive Design Mode by clicking the **Add Custom Device** button. Each device can have its own: + + +- Name +- Size +- DevicePixelRatio +- User Agent String +- Touch Screen + + +Also, you can preview the properties of existing devices by hovering over the name in the device modal, where they display in a tooltip. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/SA0RlGtOCmE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +Network throttling +****************** + +If you do all your development and testing using a very fast network connection, users may experience problems with your site if they are using a slower connection. In Responsive Design Mode, you can instruct the browser to emulate, very approximately, the characteristics of various different types of networks. + +The characteristics emulated are: + +- Download speed +- Upload speed +- Minimum latency + + +The table below lists the numbers associated with each network type, but please do not rely on this feature for exact performance measurements; it's intended to give an approximate idea of the user experience in different conditions. + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - Selection + - Download speed + - Upload speed + - Minimum latency (ms) + + * - GPRS + - 50 Kb/s + - 20 Kb/s + - 500 + + * - Regular 2G + - 250 Kb/s + - 50 Kb/s + - 300 + + * - Good 2G + - 450 Kb/s + - 150 Kb/s + - 150 + + * - Regular 3G + - 750 Kb/s + - 250 Kb/s + - 100 + + * - Good 3G + - 1.5 Mb/s + - 750 Kb/s + - 40 + + * - Regular 4G/LTE + - 4 Mb/s + - 3 Mb/s + - 20 + + * - DSL + - 2 Mb/s + - 1 Mb/s + - 5 + + * - Wi-Fi + - 30 Mb/s + - 15 Mb/s + - 2 + +To select a network, click the list box that's initially labeled "No throttling": + +.. image:: rdm_throttling.png + :class: center diff --git a/devtools/docs/user/responsive_design_mode/page-reload-warning.png b/devtools/docs/user/responsive_design_mode/page-reload-warning.png Binary files differnew file mode 100644 index 0000000000..3091b6ae5f --- /dev/null +++ b/devtools/docs/user/responsive_design_mode/page-reload-warning.png diff --git a/devtools/docs/user/responsive_design_mode/rdm_button.png b/devtools/docs/user/responsive_design_mode/rdm_button.png Binary files differnew file mode 100644 index 0000000000..5e3e3dcdc2 --- /dev/null +++ b/devtools/docs/user/responsive_design_mode/rdm_button.png diff --git a/devtools/docs/user/responsive_design_mode/rdm_throttling.png b/devtools/docs/user/responsive_design_mode/rdm_throttling.png Binary files differnew file mode 100644 index 0000000000..978d97254b --- /dev/null +++ b/devtools/docs/user/responsive_design_mode/rdm_throttling.png diff --git a/devtools/docs/user/responsive_design_mode/rdmdevtools.png b/devtools/docs/user/responsive_design_mode/rdmdevtools.png Binary files differnew file mode 100644 index 0000000000..c84263c770 --- /dev/null +++ b/devtools/docs/user/responsive_design_mode/rdmdevtools.png diff --git a/devtools/docs/user/responsive_design_mode/responsive_setting_menu.png b/devtools/docs/user/responsive_design_mode/responsive_setting_menu.png Binary files differnew file mode 100644 index 0000000000..7a1a75469f --- /dev/null +++ b/devtools/docs/user/responsive_design_mode/responsive_setting_menu.png diff --git a/devtools/docs/user/responsive_design_mode/responsive_ui.png b/devtools/docs/user/responsive_design_mode/responsive_ui.png Binary files differnew file mode 100644 index 0000000000..a2df5e6e50 --- /dev/null +++ b/devtools/docs/user/responsive_design_mode/responsive_ui.png diff --git a/devtools/docs/user/rulers/index.rst b/devtools/docs/user/rulers/index.rst new file mode 100644 index 0000000000..4f4408cb18 --- /dev/null +++ b/devtools/docs/user/rulers/index.rst @@ -0,0 +1,28 @@ +====== +Rulers +====== + +You can overlay horizontal and vertical rulers on a web page: + +.. image:: viewport_rulers.png + :class: center + +The units are in pixels. + +The dimensions of the viewport are displayed near the top-right corner of the viewport. + +To be able to toggle rulers for a page, you first need to enable the button by going to the settings page for the Developer Tools and checking "Toggle rulers for the page" under Available Toolbox Buttons. + +.. image:: settings_available_buttons.png + :class: center + +Once enabled, the "Toggle rulers for the page" button appears at the top right of the Toolbox, in the same place as the Settings/Options button. + +.. image:: ruler_toggle_button.png + :class: center + + +Behavior to keep in mind when using rulers: + +- The rulers command must be reapplied in **new tabs** and after **each page refresh.** +- The command isn't permanent. diff --git a/devtools/docs/user/rulers/ruler_toggle_button.png b/devtools/docs/user/rulers/ruler_toggle_button.png Binary files differnew file mode 100644 index 0000000000..5ce8d715e0 --- /dev/null +++ b/devtools/docs/user/rulers/ruler_toggle_button.png diff --git a/devtools/docs/user/rulers/settings_available_buttons.png b/devtools/docs/user/rulers/settings_available_buttons.png Binary files differnew file mode 100644 index 0000000000..0aeeaa65cd --- /dev/null +++ b/devtools/docs/user/rulers/settings_available_buttons.png diff --git a/devtools/docs/user/rulers/viewport_rulers.png b/devtools/docs/user/rulers/viewport_rulers.png Binary files differnew file mode 100644 index 0000000000..4a6f61ea5b --- /dev/null +++ b/devtools/docs/user/rulers/viewport_rulers.png diff --git a/devtools/docs/user/settings/dev_tools_settings.png b/devtools/docs/user/settings/dev_tools_settings.png Binary files differnew file mode 100644 index 0000000000..820c928597 --- /dev/null +++ b/devtools/docs/user/settings/dev_tools_settings.png diff --git a/devtools/docs/user/settings/devtools_layoutmenu.png b/devtools/docs/user/settings/devtools_layoutmenu.png Binary files differnew file mode 100644 index 0000000000..69dbb71603 --- /dev/null +++ b/devtools/docs/user/settings/devtools_layoutmenu.png diff --git a/devtools/docs/user/settings/devtools_menu.png b/devtools/docs/user/settings/devtools_menu.png Binary files differnew file mode 100644 index 0000000000..48dd74639d --- /dev/null +++ b/devtools/docs/user/settings/devtools_menu.png diff --git a/devtools/docs/user/settings/index.rst b/devtools/docs/user/settings/index.rst new file mode 100644 index 0000000000..e81ddb7611 --- /dev/null +++ b/devtools/docs/user/settings/index.rst @@ -0,0 +1,225 @@ +======== +Settings +======== + +.. _tool-toolbox-settings: + +Opening Settings +**************** + +Beginning with Firefox 62, the icon to open Developer Tools settings has been moved into a menu accessed by clicking/touching ... (the ellipsis) on the right of the tab. + +.. image:: devtools_layoutmenu.png + :class: center + +The menu includes settings to control the location of the Developer Tools. You can choose between the default setting at the bottom of the windows, or move the tools to the left or right side of the screen. These settings are particularly useful if you have a widescreen monitor. You can also choose to open the tools in a separate window. + +**Show split console** adds a section at the bottom of the tools showing the console. It makes visible the command line and one or two lines of the console output. + +.. image:: split_console.png + :class: center + +The rest of the settings are on the Developer Tools Settings Pane. To see the settings, open any of the Developer Tools, and then: + + +- click the "Settings" command in the menu: + + .. image:: devtools_menu.png + :class: border + +- or press :kbd:`F1` to toggle between the active tool and the Settings pane + + +The Settings pane looks something like this: + +.. image:: dev_tools_settings.png + :alt: Depicts the Toolbox options + :class: center + + +Categories +********** + +Default Firefox Developer Tools +------------------------------- + +This group of checkboxes determines which tools are enabled in the toolbox. New tools are often included in Firefox but not enabled by default. + + +Available Toolbox Buttons +------------------------- + +This group of checkboxes determines which tools get :ref:`an icon in the Toolbox's toolbar <tools-toolbox-extra-tools>`. + +As of Firefox 62, if the option to "Select an iframe as the currently targeted document" is checked, the icon will appear in the toolbar while the Settings tab is displayed, even if the current page doesn't include any iframes. + +Note that in Firefox 52 we removed the checkbox to toggle the :ref:`"Select element" button <page_inspector_select_element_button>`. The "Select element" button is now always shown. + + +.. _settings-themes: + +Themes +****** + +This enables you to choose one of two themes. + +- There's a light theme, which is the default: + + .. image:: theme-light.png + :alt: Light theme for DevTools + :class: border + +- A dark theme (the default on `Firefox Developer Edition <https://www.mozilla.org/en-US/firefox/developer/>`_): + + .. image:: theme-dark.png + :alt: Dark theme for DevTools + :class: border + + +.. _settings-common-preferences: + +Common preferences +------------------ + +Settings that apply to more than one tool. There's just one of these: + +*Enable persistent logs* + A setting to control whether or not the Web Console and Network Monitor clear their output when you navigate to a new page. + +.. note:: + + If Common Preferences is not included in the Settings, Web Console logs can be persisted by using the 'about:config' url in browser address bar, searching for: 'devtools.webconsole.persistlog' then toggling this value to true + + +.. _settings-inspector: + +Inspector +--------- + +*Show browser styles* + A setting to control whether styles applied by the browser (`user-agent styles <https://developer.mozilla.org/en-US/docs/Web/CSS/Cascade>`_) should be displayed in the Inspector's :doc:`Rules view <../page_inspector/how_to/examine_and_edit_css/index>`. Note that this setting is independent of the "Browser styles" checkbox in the Inspector's :ref:`Computed view <page_inspector_how_to_examine_and_edit_css_examine_computed_css>`. + +*Truncate DOM attributes* + By default, the Inspector truncates DOM attributes that are more than 120 characters long. Uncheck this box to prevent this behavior. This setting works by toggling the about:config preference ``devtools.markup.collapseAttributes``. To change the threshold at which attributes are truncated, you can edit the about:config preference ``devtools.markup.collapseAttributeLength``. + +*Default color unit* + A setting to control how colors are represented in the inspector: + + - Hex + - HSL(A) + - RGB(A) + - HWB + - color name + - As authored. + + +*Enable layout panel* + Enable the experimental layout panel. This setting only exists in Firefox Nightly. + + +.. _settings-web-console: + +Web Console +----------- + +*Enable timestamps* + Controls whether the Web Console displays timestamps. The Web Console defaults to hiding timestamps. + +*Enable new console frontend* + Switch to the experimental new console. This setting only exists in Firefox Nightly. + + +.. _settings-debugger: + +Debugger +-------- + +*Enable Source Maps* + Enable :doc:`source map support <../debugger/how_to/use_a_source_map/index>` in the debugger. + +*Enable new debugger frontend* + Enable the new debugger. This setting only exists in Firefox Nightly. + + + +.. _settings-style-editor: + +Style Editor +------------ + +*Show original sources* + When a CSS preprocessor supporting source maps is used, this enables the Style Editor to display the original, preprocessor, sources rather than the generated CSS. :ref:`Learn more about Style Editor support for CSS source maps <style-editor-source-map-support>`. With this setting checked, the :ref:`Page Inspector Rules view will also provide links to the original sources <page-inspector-how-to-examine-and-edit-css-link-to-css-file>`. + +*Autocomplete CSS* + Enable the Style Editor to offer autocomplete suggestions. + + +Screenshot Behavior +------------------- + +*Screenshot to clipboard* + When you click the icon for the :doc:`Screenshot tool <../taking_screenshots/index>`, copy the screenshot image to the clipboard (the image will still be saved to your Downloads directory). New in Firefox 53. + +*Play camera shutter sound* + When you click the icon for the :doc:`Screenshot tool <../taking_screenshots/index>`, play a shutter sound. New in Firefox 53. + + + +.. _settings-editor-preferences: + +Editor Preferences +****************** + +Preferences for the `CodeMirror <https://codemirror.net/>`_ source editor, which is included in Firefox and used by several developer tools, including the :doc:`Style Editor <../style_editor/index>`. + +*Detect indentation* + Auto-indent new lines based on the current indentation. + +*Autoclose brackets* + Determines whether typing an opening character like ``[`` or ``{`` will cause the editor to insert the matching closing character ``]`` or ``}`` for you. + +*Indent using spaces* + When checked, indentation will be performed using spaces, when off, the editor will use tabs instead. + +*Tab size* + The frequency of tab stops in the editor. Select 2, 4, or 8. + +*Keybindings* + Choose the default CodeMirror keybindings or keybindings from one of several popular editors: + + - Vim + - Emacs + - Sublime Text + + +.. _settings_advanced_settings: + +Advanced settings +***************** + +*Show Gecko platform data* + A setting to control whether or not profiles should include Gecko platform symbols. + +*Disable HTTP Cache* + Disable the browser HTTP cache to simulate first-load performance in all tabs that have the Toolbox open. This setting persists, meaning that if it is set, caching will be disabled whenever you reopen the devtools. Caching is re-enabled when the devtools are closed. Note that service workers are not affected by this option. + +.. note:: + + Note that this option was called "Disable Cache" in Firefox versions previous to 49, but it was renamed to make it clearer that this affects the HTTP cache, and not `Service Workers <https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API>`_/the `Cache API <https://developer.mozilla.org/en-US/docs/Web/API/Cache>`_.</div> + +*Disable JavaScript* + Reload the current tab with JavaScript disabled. + +*Enable Service Workers over HTTP* + Enable Service Worker registrations from insecure websites. + +*Enable browser chrome and add-on debugging toolboxes* + Enable you to use developer tools in the context of the browser itself, and not only web content. + +*Enable remote debugging* + Enable the developer tools debug remote Firefox instances. + +*Enable worker debugging* + Enable a panel within the debugger to debug workers. + + Note: This option got removed from the UI in Firefox 56, because this version ships with a :doc:`new Debugger UI <../debugger/index>`, but it can still be enabled for the old UI by setting the preference ``devtools.debugger.workers`` to ``true``. diff --git a/devtools/docs/user/settings/split_console.png b/devtools/docs/user/settings/split_console.png Binary files differnew file mode 100644 index 0000000000..0f8210aa09 --- /dev/null +++ b/devtools/docs/user/settings/split_console.png diff --git a/devtools/docs/user/settings/theme-dark.png b/devtools/docs/user/settings/theme-dark.png Binary files differnew file mode 100644 index 0000000000..67b5aa0a97 --- /dev/null +++ b/devtools/docs/user/settings/theme-dark.png diff --git a/devtools/docs/user/settings/theme-light.png b/devtools/docs/user/settings/theme-light.png Binary files differnew file mode 100644 index 0000000000..ed5e377544 --- /dev/null +++ b/devtools/docs/user/settings/theme-light.png diff --git a/devtools/docs/user/shader_editor/index.rst b/devtools/docs/user/shader_editor/index.rst new file mode 100644 index 0000000000..3a4310480f --- /dev/null +++ b/devtools/docs/user/shader_editor/index.rst @@ -0,0 +1,71 @@ +============= +Shader Editor +============= + +.. warning:: + + This tool has been deprecated and will soon be removed from Firefox. For details, see :doc:`Deprecated tools <../index>`. + +The Shader Editor enables you to see and edit the vertex and fragment shaders used by `WebGL <https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API>`_. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/hnoKqFuJhu0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +WebGL is a `JavaScript <https://developer.mozilla.org/en-US/docs/Web/JavaScript>`_ API for rendering interactive 3D graphics and 2D graphics in the browser without using plugins. With WebGL you provide 2 programs called **shaders** which are called at the appropriate stages of the `OpenGL rendering pipeline <https://www.opengl.org/wiki/Rendering_Pipeline_Overview>`_: a `vertex shader <https://www.opengl.org/wiki/Vertex_Shader>`_, which computes the clip space coordinates of each vertex to be drawn, and a `fragment shader <https://www.opengl.org/wiki/Fragment_Shader>`_, which determines the color for each pixel to be drawn. + + +These shaders are written in **OpenGL Shading Language**, or `GLSL <https://www.opengl.org/documentation/glsl/>`_. In WebGL they can be included in a page in several ways: as text hardcoded in JavaScript strings, as separate files included using `<script> <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script>`_ tags, or retrieved from the server as plain text. JavaScript code running in the page then sends them for compilation using the WebGL APIs, and they're executed on the device's GPU when needed. + +With the Shader Editor, you can examine and edit the source of the vertex and fragment shaders. + + +Opening the Shader Editor +************************* + +The Shader Editor is disabled by default. To enable it, open the :doc:`Toolbox settings <../tools_toolbox/index>` and check "Shader Editor" in the "Default Firefox Developer Tools" item. You'll now see "Shader Editor" appear in the toolbar. Click it and the Shader Editor opens. + + +At first you'll just see a blank window with a button asking you to reload the page: + +.. image:: shader-editor-open.png + +To get started, load a page which creates a WebGL context and loads a program into it. The screenshots below are from the `Unreal Engine <https://www.unrealengine.com/html5/>`_ demo. + +You'll now see a window divided into three panes: a list of all the GLSL programs on the left, the vertex shader for the currently selected program in the middle, and the fragment shader for the currently selected program on the right: + +.. image:: shader-editor-loaded.png + + +Managing programs +***************** + +The left hand pane lists all programs currently in use by a WebGL context. If you hover over an entry in the list, the geometry drawn by that program is highlighted in red: + +.. image:: shader-editor-highlight.png + + +If you click the eyeball icon just left of the program's entry, that program is disabled. This is useful for focusing on certain shaders or hiding overlapping geometry: + +.. image:: shader-editor-disable.png + +If you click the entry, its vertex and fragment shaders are shown in the other two panes, and you can edit them. + +Editing shaders +*************** + +The middle and right hand panes show the vertex and fragment shaders for the currently selected program. + +You can edit these programs and see the results the next time the WebGL context redraws (for example, in the next animation frame). For example, you can modify the colors: + +.. image:: shader-editor-edit-color.png + +The editor highlights syntax errors in your code: + +.. image:: shader-editor-error.png + +If you hover over the cross shown next to a line containing an error, you'll see more details about the problem: + +.. image:: shader-editor-error-info.png diff --git a/devtools/docs/user/shader_editor/shader-editor-disable.png b/devtools/docs/user/shader_editor/shader-editor-disable.png Binary files differnew file mode 100644 index 0000000000..ec634eea9f --- /dev/null +++ b/devtools/docs/user/shader_editor/shader-editor-disable.png diff --git a/devtools/docs/user/shader_editor/shader-editor-edit-color.png b/devtools/docs/user/shader_editor/shader-editor-edit-color.png Binary files differnew file mode 100644 index 0000000000..3fc215c26c --- /dev/null +++ b/devtools/docs/user/shader_editor/shader-editor-edit-color.png diff --git a/devtools/docs/user/shader_editor/shader-editor-error-info.png b/devtools/docs/user/shader_editor/shader-editor-error-info.png Binary files differnew file mode 100644 index 0000000000..f41172a218 --- /dev/null +++ b/devtools/docs/user/shader_editor/shader-editor-error-info.png diff --git a/devtools/docs/user/shader_editor/shader-editor-error.png b/devtools/docs/user/shader_editor/shader-editor-error.png Binary files differnew file mode 100644 index 0000000000..f52cc86db2 --- /dev/null +++ b/devtools/docs/user/shader_editor/shader-editor-error.png diff --git a/devtools/docs/user/shader_editor/shader-editor-highlight.png b/devtools/docs/user/shader_editor/shader-editor-highlight.png Binary files differnew file mode 100644 index 0000000000..cfa449377d --- /dev/null +++ b/devtools/docs/user/shader_editor/shader-editor-highlight.png diff --git a/devtools/docs/user/shader_editor/shader-editor-loaded.png b/devtools/docs/user/shader_editor/shader-editor-loaded.png Binary files differnew file mode 100644 index 0000000000..91c2102319 --- /dev/null +++ b/devtools/docs/user/shader_editor/shader-editor-loaded.png diff --git a/devtools/docs/user/shader_editor/shader-editor-open.png b/devtools/docs/user/shader_editor/shader-editor-open.png Binary files differnew file mode 100644 index 0000000000..c354a6f70b --- /dev/null +++ b/devtools/docs/user/shader_editor/shader-editor-open.png diff --git a/devtools/docs/user/storage_inspector/cache_storage.png b/devtools/docs/user/storage_inspector/cache_storage.png Binary files differnew file mode 100644 index 0000000000..e45e4da184 --- /dev/null +++ b/devtools/docs/user/storage_inspector/cache_storage.png diff --git a/devtools/docs/user/storage_inspector/cache_storage/cache_storage_detail.png b/devtools/docs/user/storage_inspector/cache_storage/cache_storage_detail.png Binary files differnew file mode 100644 index 0000000000..42a8f5c7e3 --- /dev/null +++ b/devtools/docs/user/storage_inspector/cache_storage/cache_storage_detail.png diff --git a/devtools/docs/user/storage_inspector/cache_storage/index.rst b/devtools/docs/user/storage_inspector/cache_storage/index.rst new file mode 100644 index 0000000000..68dc86cd2b --- /dev/null +++ b/devtools/docs/user/storage_inspector/cache_storage/index.rst @@ -0,0 +1,12 @@ +============= +Cache Storage +============= + +Under the *Cache Storage* type within the :doc:`Storage Inspector <../index>` you can see the contents of any DOM caches created using the `Cache API <https://developer.mozilla.org/en-US/docs/Web/API/Cache>`_. If you select a cache, you'll see a list of the resources it contains. For each resource, you'll see: + + +- the URL for the resource. +- the status code for the request that was made to fetch it. + +.. image:: cache_storage_detail.png + :class: border diff --git a/devtools/docs/user/storage_inspector/cookie_context_menu.png b/devtools/docs/user/storage_inspector/cookie_context_menu.png Binary files differnew file mode 100644 index 0000000000..1dce7ef370 --- /dev/null +++ b/devtools/docs/user/storage_inspector/cookie_context_menu.png diff --git a/devtools/docs/user/storage_inspector/cookie_storage.png b/devtools/docs/user/storage_inspector/cookie_storage.png Binary files differnew file mode 100644 index 0000000000..300c729834 --- /dev/null +++ b/devtools/docs/user/storage_inspector/cookie_storage.png diff --git a/devtools/docs/user/storage_inspector/cookies/cookie_table_widget_context.png b/devtools/docs/user/storage_inspector/cookies/cookie_table_widget_context.png Binary files differnew file mode 100644 index 0000000000..e0f898dc9e --- /dev/null +++ b/devtools/docs/user/storage_inspector/cookies/cookie_table_widget_context.png diff --git a/devtools/docs/user/storage_inspector/cookies/index.rst b/devtools/docs/user/storage_inspector/cookies/index.rst new file mode 100644 index 0000000000..d6dabffd26 --- /dev/null +++ b/devtools/docs/user/storage_inspector/cookies/index.rst @@ -0,0 +1,42 @@ +======= +Cookies +======= + +When you select an origin inside the *Cookies* storage type from the storage tree, all the cookies present for that origin will be listed in a table. The cookies table has the following columns: + + +- *Name* — The name of the cookie. +- *Value* — The value of the cookie. +- *Domain* — The domain of the cookie. +- *Path* — The path property of the cookie. +- *Expires / Max-Age* — The time when the cookie will expire. If the cookie is a session cookie, the value of this column will be "Session" +- *Size* — The size of the cookie name plus value in bytes. +- *HttpOnly* — Is this cookie HTTP only? +- *Secure* — Is this cookie a secure cookie? +- *SameSite* — Is this cookie a same-site cookie? Same-site cookies allow servers to mitigate the risk of CSRF and information leakage attacks by asserting that a particular cookie should only be sent with requests initiated from the same registrable domain. +- *Last accessed* — Date and time when the cookie was last read. +- *Created* — Date and time when the cookie was created. +- *HostOnly* — Is this cookie a domain cookie? That is, the domain value matches exactly the domain of the current website. + + +.. note:: + + Some of the columns are not shown by default — to change the column display, right-click on the existing table headings and use the resulting context menu to show/hide the columns. + + +You can edit cookies by double-clicking inside cells in the :ref:`Table Widget <storage-inspector-table-widget>` and editing the values they contain, and add new cookies by clicking the "Plus" (+) button and then editing the resulting new row to the value you want. + +Context menu +------------ + +The context menu for each cookie includes the following commands: + + +- *Add item* - add a new cookie. +- *Delete <cookie name>.<domain>* - deletes the selected cookie +- *Delete All From <domain>* - deletes all cookies from the selected domain. This must be an exact match. For example, if you select "Delete All From test8.example.com" only cookies from that domain will be deleted. Cookies from "test13.example.com" will not be deleted. +- *Delete All* - deletes all cookies for the current host. +- *Delete All Session Cookies* - deletes all cookies for the current host that are scheduled to be deleted when the browser shuts down + +.. image:: cookie_table_widget_context.png + :class: border diff --git a/devtools/docs/user/storage_inspector/extension_storage/extension_storage.png b/devtools/docs/user/storage_inspector/extension_storage/extension_storage.png Binary files differnew file mode 100644 index 0000000000..aaf3787932 --- /dev/null +++ b/devtools/docs/user/storage_inspector/extension_storage/extension_storage.png diff --git a/devtools/docs/user/storage_inspector/extension_storage/index.rst b/devtools/docs/user/storage_inspector/extension_storage/index.rst new file mode 100644 index 0000000000..5c6b8300e9 --- /dev/null +++ b/devtools/docs/user/storage_inspector/extension_storage/index.rst @@ -0,0 +1,13 @@ +================= +Extension Storage +================= + +This storage type is only shown when debugging extensions. When selecting an extension ID in the storage tree of the :doc:`Storage Inspector <../index>`, a table lists the details of all the extension storage present for the extension. This table contains the following columns: + + +- *Key* — The name of the stored item. +- *Value* — The value of the stored item. +- *Storage Area* — The name of the area where the item is stored. + +.. image:: extension_storage.png + :class: center diff --git a/devtools/docs/user/storage_inspector/index.rst b/devtools/docs/user/storage_inspector/index.rst new file mode 100644 index 0000000000..fcb6d6226a --- /dev/null +++ b/devtools/docs/user/storage_inspector/index.rst @@ -0,0 +1,144 @@ +================= +Storage Inspector +================= + +The Storage Inspector enables you to inspect various types of storage that a web page can use. Currently it can be used to inspect the following storage types: + + +- *Cache Storage* — any DOM caches created using the `Cache API <https://developer.mozilla.org/en-US/docs/Web/API/Cache>`_. + +- *Cookies* — All the `cookies <https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie>`_ created by the page or any iframes inside of the page. Cookies created as a part of response of network calls are also listed, but only for calls that happened while the tool is open. + +- *IndexedDB* — All `IndexedDB <https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API>`_ databases created by the page or any iframes inside the page, their Object Stores and the items stored in these Object Stores. + +- *Local Storage* — All `local storage <https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage>`_ items created by the page or any iframes inside the page. + +- *Session Storage* — All `session storage <https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage>`_ items created by the page or any iframes inside the page. + + +For the time being, the Storage Inspector only gives you a read-only view of storage. But we're working to let you edit storage contents in future releases. + + +Opening the Storage Inspector +***************************** + +You can open the Storage Inspector by selecting the *Storage* panel in the Web Developer Tools, accessible from the Browser Tools submenu + +The :doc:`Toolbox <../tools_toolbox/index>` will appear at the bottom of the browser window, with the Storage Inspector activated. It's just called "Storage" in the Developer Toolbox. + +.. image:: storage_inspector.png + :class: center + + +Storage Inspector User Interface +******************************** + +The Storage Inspector UI is split into three main components: + +- :ref:`Storage tree <storage-inspector-storage-tree>` +- :ref:`Table Widget <storage-inspector-table-widget>` +- :ref:`Sidebar <storage-inspector-sidebar>` + + +.. image:: storage_labeled.png + :class: center + + +.. _storage-inspector-storage-tree: + +Storage tree +------------ + +The storage tree lists all the storage types that the Storage Inspector can inspect: + +.. image:: storage_types.png + :class: center + +Under each type, objects are organized by origin. For cookies, the protocol does not differentiate the origin. For Indexed DB or local storage an origin is a combination of protocol + hostname. For example, "``http://mozilla.org``" and "``https://mozilla.org``" are two different origins so local storage items cannot be shared between them. + +Under "Cache Storage", objects are organized by origin and then by the name of the cache: + +.. image:: cache_storage.png + :class: border + + +IndexedDB objects are organized by origin, then by database name, then by object store name: + +.. image:: indexeddb_storage.png + :class: border + + +With the Cookies, Local Storage, and Session Storage types, there's only one level in the hierarchy, so stored items are listed directly under each origin: + +.. image:: cookie_storage.png + :class: border + + +You can click on each item in the tree to expand or collapse its children. The tree is live, so if a new origin gets added (by adding an iframe, for example), it will be added to each storage type automatically. + +Clicking on a tree item will display detailed information about that item in the Table Widget on the right. For example, clicking on an origin which is a child of the Cookies storage type will show all the cookies belonging to that domain. + + +.. _storage-inspector-table-widget: + +Table Widget +------------ + +The table widget displays a list of all the items corresponding to the selected tree item (be it an origin, or database) are listed. Depending on the storage type and tree item, the number of columns in the table might differ. + +All the columns in a Table Widget are resizable. You can hide and show columns by context-clicking on the table header and selecting the columns you want to see: + +.. image:: cookie_context_menu.png + :class: border + + +Search +------ + +There's a search box at the top of the Table Widget: + +.. image:: storage_detail_filter.png + :class: border + + +This filters the table to show only items which match the search term. Items match the search term if any of their fields (including fields whose columns you have hidden) contain the search term. + +You can use :kbd:`Ctrl` + :kbd:`F` (:kbd:`Cmd` + :kbd:`F` on a Mac) to focus the search box. + + +Add and refresh storage +----------------------- + +You'll also have buttons available to add a new storage entry or refresh the view of the currently viewed storage type where applicable (you can't add new entries to IndexedDB or Cache): + +.. image:: storage_detail_add_refresh.png + :class: border + + +.. _storage-inspector-sidebar: + +Sidebar +------- + +When you select any row in the Storage table widget, the sidebar is shown with details about that row. If a cookie is selected, it will list all the details about that cookie. + +The sidebar can parse the value of the cookie or local storage item or an IndexedDB item and convert it into a meaningful object instead of just a string. For example: + + +- A stringified JSON like ``'{"foo": "bar"}'`` is shown as the origin JSON: ``{foo: "bar"}``. +- A string containing a key separated value, like ``"1~2~3~4"`` or ``"1=2=3=4"`` is shown like an array: ``[1, 2, 3, 4]``. +- A string containing key-value pairs, like ``"ID=1234:foo=bar"`` is shown as JSON: ``{ID:1234, foo: "bar"}``. + +The shown values can also be filtered using the search box at the top of the sidebar. + + +Working with the Storage Inspector +********************************** + +The following articles cover different aspects of using the Storage Inspector: + +- :doc:`Cookies <../storage_inspector/cookies/index>` +- :doc:`Local Storage / Session Storage <../storage_inspector/local_storage_session_storage/index>` +- :doc:`Cache Storage <../storage_inspector/cache_storage/index>` +- :doc:`IndexedDB <../storage_inspector/indexeddb/index>` +- :doc:`Extension Storage <../storage_inspector/extension_storage/index>` diff --git a/devtools/docs/user/storage_inspector/indexeddb/index.rst b/devtools/docs/user/storage_inspector/indexeddb/index.rst new file mode 100644 index 0000000000..7273e8c456 --- /dev/null +++ b/devtools/docs/user/storage_inspector/indexeddb/index.rst @@ -0,0 +1,52 @@ +========= +IndexedDB +========= + +When you select an origin inside the *Indexed DB* storage type in the storage tree of the :doc:`Storage Inspector <../index>`, a table lists the details of all the databases present for that origin. + +.. note:: + + The data shown in an IndexedDB database is a snapshot of the data as it was when you opened the Storage Inspector tool. + + +Databases have the following details: + + +- *Database Name* — The name of the database. +- *Storage* — The `storage type <https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria#different_types_of_data_storage>`_ specified for the database. +- *Origin* — The origin of the database. +- *Version* — The database version. +- *Object Stores* — The number of object stores in the database. + + +When an IndexedDB database is selected in the storage tree, details about all the object stores are listed in the table. Any object store has the following details: + + +- *Object Store Name* — The name of the object store. +- *Key* — The `keyPath <https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/keyPath>`_ property of the object store. +- *Auto Increment* — Is automatic incrementation of the keys enabled? +- *Indexes* — Array of indexes present in the object store as shown below. + + +.. image:: indexed_db_details.png + :class: border + + +When an object store is selected in the storage tree, all the items in that object store are listed in the table. All items have a key and a value associated with them. + +You can delete an IndexedDB database using the context menu in the storage tree: + +.. image:: indexed_db_context_menu.png + :class: border + + +If the database cannot be deleted (most commonly because there are still active connections to the database), a warning message will be displayed in the Storage Inspector: + +.. image:: indexeddb_delete_warning.png + :class: border + + +You can use the context menu in the table widget to delete all items in an object store, or a particular item: + +.. image:: indexed_db_table_widget_context.png + :class: border diff --git a/devtools/docs/user/storage_inspector/indexeddb/indexed_db_context_menu.png b/devtools/docs/user/storage_inspector/indexeddb/indexed_db_context_menu.png Binary files differnew file mode 100644 index 0000000000..45dbc214fe --- /dev/null +++ b/devtools/docs/user/storage_inspector/indexeddb/indexed_db_context_menu.png diff --git a/devtools/docs/user/storage_inspector/indexeddb/indexed_db_details.png b/devtools/docs/user/storage_inspector/indexeddb/indexed_db_details.png Binary files differnew file mode 100644 index 0000000000..8cb4602a26 --- /dev/null +++ b/devtools/docs/user/storage_inspector/indexeddb/indexed_db_details.png diff --git a/devtools/docs/user/storage_inspector/indexeddb/indexed_db_table_widget_context.png b/devtools/docs/user/storage_inspector/indexeddb/indexed_db_table_widget_context.png Binary files differnew file mode 100644 index 0000000000..6c243c1816 --- /dev/null +++ b/devtools/docs/user/storage_inspector/indexeddb/indexed_db_table_widget_context.png diff --git a/devtools/docs/user/storage_inspector/indexeddb/indexeddb_delete_warning.png b/devtools/docs/user/storage_inspector/indexeddb/indexeddb_delete_warning.png Binary files differnew file mode 100644 index 0000000000..8e4dc2c30f --- /dev/null +++ b/devtools/docs/user/storage_inspector/indexeddb/indexeddb_delete_warning.png diff --git a/devtools/docs/user/storage_inspector/indexeddb_storage.png b/devtools/docs/user/storage_inspector/indexeddb_storage.png Binary files differnew file mode 100644 index 0000000000..c83f347e67 --- /dev/null +++ b/devtools/docs/user/storage_inspector/indexeddb_storage.png diff --git a/devtools/docs/user/storage_inspector/local_storage_session_storage/delete_storage_menu.png b/devtools/docs/user/storage_inspector/local_storage_session_storage/delete_storage_menu.png Binary files differnew file mode 100644 index 0000000000..48226a9456 --- /dev/null +++ b/devtools/docs/user/storage_inspector/local_storage_session_storage/delete_storage_menu.png diff --git a/devtools/docs/user/storage_inspector/local_storage_session_storage/index.rst b/devtools/docs/user/storage_inspector/local_storage_session_storage/index.rst new file mode 100644 index 0000000000..31e0b0ebe4 --- /dev/null +++ b/devtools/docs/user/storage_inspector/local_storage_session_storage/index.rst @@ -0,0 +1,23 @@ +=============================== +Local Storage / Session Storage +=============================== + +When an origin corresponding to local storage or session storage is selected within the :doc:`Storage Inspector <../index>`, the names and values of all the items corresponding to local storage or session storage will be listed in a table. + +You can edit local and session storage items by double-clicking inside cells in the :ref:`Table Widget <storage-inspector-table-widget>` and editing the values they contain: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/UKLgBBUi11c" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +You can delete local storage and session storage entries using the context menu: + +.. image:: delete_storage_menu.png + :class: border + + +You can also delete local and session storage entries by selecting an item and pressing the :kbd:`Delete` or :kbd:`Backspace` key. + +Finally, you can add new storage items by clicking the "Plus" (+) button and then editing the resulting new row to the value you want. diff --git a/devtools/docs/user/storage_inspector/storage_detail_add_refresh.png b/devtools/docs/user/storage_inspector/storage_detail_add_refresh.png Binary files differnew file mode 100644 index 0000000000..4ffeed2800 --- /dev/null +++ b/devtools/docs/user/storage_inspector/storage_detail_add_refresh.png diff --git a/devtools/docs/user/storage_inspector/storage_detail_filter.png b/devtools/docs/user/storage_inspector/storage_detail_filter.png Binary files differnew file mode 100644 index 0000000000..dc6888654a --- /dev/null +++ b/devtools/docs/user/storage_inspector/storage_detail_filter.png diff --git a/devtools/docs/user/storage_inspector/storage_inspector.png b/devtools/docs/user/storage_inspector/storage_inspector.png Binary files differnew file mode 100644 index 0000000000..ec9c21f77d --- /dev/null +++ b/devtools/docs/user/storage_inspector/storage_inspector.png diff --git a/devtools/docs/user/storage_inspector/storage_labeled.png b/devtools/docs/user/storage_inspector/storage_labeled.png Binary files differnew file mode 100644 index 0000000000..28cecc6ea0 --- /dev/null +++ b/devtools/docs/user/storage_inspector/storage_labeled.png diff --git a/devtools/docs/user/storage_inspector/storage_types.png b/devtools/docs/user/storage_inspector/storage_types.png Binary files differnew file mode 100644 index 0000000000..4a17e73fc5 --- /dev/null +++ b/devtools/docs/user/storage_inspector/storage_types.png diff --git a/devtools/docs/user/style_editor/index.rst b/devtools/docs/user/style_editor/index.rst new file mode 100644 index 0000000000..fb91a5d95f --- /dev/null +++ b/devtools/docs/user/style_editor/index.rst @@ -0,0 +1,153 @@ +============ +Style Editor +============ + +The Style Editor enables you to: + + +- view and edit all the stylesheets associated with a page +- create new stylesheets from scratch and apply them to the page +- import existing stylesheets and apply them to the page + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/7839qc55r7o" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +To open the Style Editor select the *Style Editor* panel in the Web Developer Tools, accessible from the Browser Tools submenu + +.. image:: style-editor.png + :class: center + +The Style Editor is divided into three main sections: + +- :ref:`the style sheet pane on the left <style-editor-the-style-sheet-pane>` +- :ref:`the editor on the right <style-editor-the-editor-pane>` +- :ref:`the At-rules sidebar <style-editor-the-at-rules-sidebar>` + + +.. _style-editor-the-style-sheet-pane: + +The style sheet pane +******************** + +The style sheet pane, on the left, lists all the style sheets being used by the current document. You can quickly toggle the use of a given sheet on and off by clicking the eyeball icon to the left of the sheet's name. You can save any changes you've made to the style sheet to your local computer by clicking the Save button in the bottom-right corner of each sheet's entry in the list. + +From Firefox 40 onwards, the style sheet pane also includes a context menu that lets you open the selected style sheet in a new tab. + + +.. _style-editor-the-editor-pane: + +The editor pane +*************** + +On the right is the editor pane. This is where the source for the selected style sheet is available for you to read and edit. Any changes you make are immediately applied to the page. This makes it easy to experiment with, revise, and test changes. Once you're satisfied with your changes, you can save a copy locally by clicking the Save button on the sheet's entry in the style sheet pane. + +The editor provides line numbers and syntax highlighting to help make it easier to read your CSS. It also supports a number of :ref:`keyboard shortcuts <style-editor-keyboard-shortcuts>`. + +The Style Editor automatically de-minimizes style sheets that it detects, without affecting the original. This makes it much easier to work on pages that have been optimized. + +The Style Editor supports autocomplete. Just start typing, and it will offer you a list of suggestions. + +.. image:: style-editor-autocomplete.png + :class: center + +You can switch autocomplete off in the :ref:`Style Editor settings <settings-style-editor>`. + + +.. _style-editor-the-at-rules-sidebar: + +The At-rules sidebar +******************** + +The Style Editor displays a sidebar on the right-hand side whenever the current sheet contains any of the following At-rules: + +- `@media <https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries>`_ +- `@supports <https://developer.mozilla.org/en-US/docs/Web/CSS/@supports>`_ +- `@layer <https://developer.mozilla.org/en-US/docs/Web/CSS/@layer>`_ +- `@container <https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries>`_ + +The sidebar lists the rules and provides a link to the line of the sheet where the rule is defined. Click an item to jump to that rule in the sheet. For ``@media`` rules, the condition text of the rule is greyed-out if the media query doesn’t currently apply. + +.. image:: style-editor-at-rules-sidebar-detail.png + :class: center + +The At-rules sidebar works especially well with :doc:`Responsive Design View <../responsive_design_mode/index>` for creating and debugging responsive layouts: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/aVUXmvLSwoM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +From Firefox 46 onwards, if an ``@media`` rule contains a screen size in a condition, then it is made clickable: clicking it then resizes the screen to that size using the Responsive Design View. + + +Creating and importing style sheets +*********************************** + +You can create a new style sheet by clicking the New button in the toolbar. Then you can just start entering CSS into the new editor and watch as the new styles are applied in real time just like changes to the other sheets. + +You can load a style sheet from disk and apply it to the page by clicking the Import button. + + +.. _style-editor-source-map-support: + +Source map support +****************** + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/zu2eZbYtEUQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +Web developers often create CSS files using a preprocessor like `Sass <https://sass-lang.com/>`_, `Less <https://lesscss.org/>`_, or `Stylus <https://learnboost.github.io/stylus/>`_. These tools generate CSS files from a richer and more expressive syntax. If you do this, being able to see and edit the generated CSS is not so useful, because the code you maintain is the preprocessor syntax, not the generated CSS. So you'd need to edit the generated CSS, then manually work out how to reapply that to the original source. + +Source maps enable the tools to map back from the generated CSS to the original syntax, so they can display, and allow you to edit, files in the original syntax. From Firefox 29 onwards, the Style Editor can understand CSS source maps. + +This means that if you use, for example, Sass, then the Style Editor will show you, and allow you to edit, Sass files, rather than the CSS that is generated from them: + +.. image:: style-editor-sourcemap.png + :class: center + + +For this to work, you must: + + +- use a CSS preprocessor that understands the `Source Map Revision 3 proposal <https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit>`_. Currently this means `Sass 3.3.0 <https://sass-lang.com/>`_ or above or the `1.5.0 version of Less <http://roots.io/using-less-source-maps/>`_. Other preprocessors are actively working on adding support, or considering it. +- actually instruct the preprocessor to generate a source map, for example by passing the ``--source-map`` argument to the Lass command-line tool, but in some preprocessors like Sass, source maps are generated by default and you don't need to do anything. + + +Viewing original sources +------------------------ + +Now, if you check "Show original sources" in the :ref:`Style Editor settings <settings-style-editor>`, the links next to CSS rules in the :ref:`Rules view <page_inspector_ui_tour_rules_view>` will link to the original sources in the Style Editor. + +From Firefox 35 onwards original sources are displayed by default. + + +Editing original sources +------------------------ + +You can also edit the original sources in the Style Editor and see the results applied to the page immediately. To get this to work there are two extra steps. + +First, set up your preprocessor so it watches the original source and automatically regenerates the CSS when the source changes. With Sass you can do this by passing the ``--watch`` option: + +.. code-block:: + + sass index.scss:index.css --watch + + +Next, save the original source in the Style Editor by clicking the "Save" button next to the file, and saving it over the original file. + +Now when you make changes to the source file in the Style Editor the CSS is regenerated and you can see the changes right away. + + +.. _style-editor-keyboard-shortcuts: + +Keyboard shortcuts +****************** + + - :ref:`Source editor shortcuts <keyboard-shortcuts-style-editor>` diff --git a/devtools/docs/user/style_editor/style-editor-at-rules-sidebar-detail.png b/devtools/docs/user/style_editor/style-editor-at-rules-sidebar-detail.png Binary files differnew file mode 100644 index 0000000000..bf6200a1a4 --- /dev/null +++ b/devtools/docs/user/style_editor/style-editor-at-rules-sidebar-detail.png diff --git a/devtools/docs/user/style_editor/style-editor-autocomplete.png b/devtools/docs/user/style_editor/style-editor-autocomplete.png Binary files differnew file mode 100644 index 0000000000..fa916dbf43 --- /dev/null +++ b/devtools/docs/user/style_editor/style-editor-autocomplete.png diff --git a/devtools/docs/user/style_editor/style-editor-sourcemap.png b/devtools/docs/user/style_editor/style-editor-sourcemap.png Binary files differnew file mode 100644 index 0000000000..1b77fb5afe --- /dev/null +++ b/devtools/docs/user/style_editor/style-editor-sourcemap.png diff --git a/devtools/docs/user/style_editor/style-editor.png b/devtools/docs/user/style_editor/style-editor.png Binary files differnew file mode 100644 index 0000000000..8d322cc5b7 --- /dev/null +++ b/devtools/docs/user/style_editor/style-editor.png diff --git a/devtools/docs/user/taking_screenshots/camera-icon.png b/devtools/docs/user/taking_screenshots/camera-icon.png Binary files differnew file mode 100644 index 0000000000..faa229d55c --- /dev/null +++ b/devtools/docs/user/taking_screenshots/camera-icon.png diff --git a/devtools/docs/user/taking_screenshots/index.rst b/devtools/docs/user/taking_screenshots/index.rst new file mode 100644 index 0000000000..e989879818 --- /dev/null +++ b/devtools/docs/user/taking_screenshots/index.rst @@ -0,0 +1,127 @@ +================== +Taking screenshots +================== + +You can use the Developer Tools to take a screenshot of the entire page, or of a single element in the page. + +.. _taking_screenshots_taking_a_screenshot_of_the_page: + + +Taking a screenshot of the page +******************************* + +.. |image1| image:: camera-icon.png + :width: 20 + + +Use the screenshot icon: |image1| to take a full-page screenshot of the current page. + +By default, the screenshot icon is not enabled. To enable it: + + +- visit the :doc:`Settings <../settings/index>` page +- find the section labeled "Available Toolbox Buttons" +- check the box labeled "Take a screenshot of the entire page". + + +You'll now see the icon in the toolbar: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/KB5V9uJgcS4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +Click the icon to take a screenshot of the current page. The screenshot is saved to your browser's "Downloads" directory: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/HKS6MofdXVE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +.. _taking-screenshots-of-an-element: + +Taking a screenshot of an element +********************************* + +To take a screenshot of a single element in the page, activate the context menu on that element in the :ref:`Inspector's HTML pane <page_inspector_ui_tour_html_pane>`, and select "Screenshot Node". The screenshot is saved to the browser's "Downloads" directory: + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/p2pjF_BrE1o" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +Copying screenshots to the clipboard +************************************ + +From Firefox 53, you can also copy the screenshot to the clipboard. Just check the box in Settings labeled "Screenshot to clipboard": + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/AZedFGh6F78" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +Now, whenever you take a screenshot, the screenshot is also copied to the clipboard. + + +Taking screenshots with the web console +*************************************** + +If you need to specify a different device-pixel-ratio, set a delay before taking the screenshot, or specify your own file name, starting in Firefox 62 you can use the ``:screenshot`` helper function in the :doc:`Web Console <../web_console/index>`. + +Type ``:screenshot`` in the Web Console to create a screenshot of the current page. By default, the image file will be named ``Screen Shot yyy-mm-dd at hh.mm.ss.png``. + +.. note:: + + **Tip**: You could type ``:s`` and then hit :kbd:`Tab` to autocomplete ``:screenshot``. + +The command has the following optional parameters: + +.. list-table:: + :widths: 20 20 60 + :header-rows: 1 + + * - Command + - Type + - Description + + * - ``--clipboard`` + - boolean + - When present, this parameter will cause the screenshot to be copied to the clipboard. Prevents saving to a file unless you use the ``--file`` option to force file writing. + + * - ``--delay`` + - number + - The number of seconds to delay before taking the screenshot; you can use an integer or floating point number. This is useful if you want to pop open a menu or invoke a hover state for the screenshot. + + * - ``--dpr`` + - number + - The device pixel ratio to use when taking the screenshot. Values above 1 yield "zoomed-in" images, whereas values below 1 create "zoomed-out" images. + + * - ``--file`` + - boolean + - When present, the screenshot will be saved to a file, even if other options (e.g. ``--clipboard``) are included. + + * - ``--filename`` + - string + - The name to use in saving the file. The file should have a ".png" extension. + + * - ``--fullpage`` + - boolean + - If included, the full webpage will be saved. With this parameter, even the parts of the webpage which are outside the current bounds of the window will be included in the screenshot. When used, "<span style="white-space: nowrap;">-fullpage</span>" will be appended to the file name. + + * - ``--selector`` + - string + - A CSS selector that selects a single element on the page. When supplied, only this element and its descendants will be included in the screenshot. + + +.. note:: + If you capture an image to a filename like ``test.png``, and then you capture to that same filename, the new image will overwrite the old image. So if you’re using the up-arrow history scroll to capture images in quick succession, be careful — you need to remember to change the filename for each new capture. + + +.. note:: + Thanks to Eric Meyer for his enthusiasm for our screenshot feature, and help! Small portions of this section have been borrowed from his `Firefox’s :screenshot command <https://meyerweb.com/eric/thoughts/2018/08/24/firefoxs-screenshot-command-2018/>`_ article. diff --git a/devtools/docs/user/tips/black_boxing.png b/devtools/docs/user/tips/black_boxing.png Binary files differnew file mode 100644 index 0000000000..2c0eeba27a --- /dev/null +++ b/devtools/docs/user/tips/black_boxing.png diff --git a/devtools/docs/user/tips/create_style_sheet_button.png b/devtools/docs/user/tips/create_style_sheet_button.png Binary files differnew file mode 100644 index 0000000000..34d2280408 --- /dev/null +++ b/devtools/docs/user/tips/create_style_sheet_button.png diff --git a/devtools/docs/user/tips/filter.png b/devtools/docs/user/tips/filter.png Binary files differnew file mode 100644 index 0000000000..627542687c --- /dev/null +++ b/devtools/docs/user/tips/filter.png diff --git a/devtools/docs/user/tips/import_button.png b/devtools/docs/user/tips/import_button.png Binary files differnew file mode 100644 index 0000000000..59379cdf90 --- /dev/null +++ b/devtools/docs/user/tips/import_button.png diff --git a/devtools/docs/user/tips/index.rst b/devtools/docs/user/tips/index.rst new file mode 100644 index 0000000000..5b9e51244b --- /dev/null +++ b/devtools/docs/user/tips/index.rst @@ -0,0 +1,144 @@ +==== +Tips +==== + +General +******* + +Screenshots: + +.. |image1| image:: screenshot_button.png + :alt: Button to take screenshots of the entire page + :width: 20 + +- Entire page: Click the screenshot button (|image1| :ref:`needs to be enabled <tools-toolbox-extra-tools>` first). +- Viewport: Click the screenshot button (|image1|) in :ref:`Responsive Design Mode <responsive-design-mode-camera-button>`. +- Node: Right-click a node within the Inspector and click :ref:`"Screenshot Node" <taking-screenshots-of-an-element>`. +- Via :doc:`Web Console command <../web_console/helpers/index>`: ``screenshot <filename.png> --fullpage``. + + +Settings: + +- Choose between a :ref:`light and a dark theme <settings-themes>` for the developer tools. +- :ref:`Change the keyboard bindings <settings-editor-preferences>` to Vim, Emacs or Sublime Text if you're used to different shortcuts. +- Check or uncheck the different tools to enable or disable them. (There are more than the default tools!) + + +Page Inspector +************** + +In the :ref:`markup view <page_inspector_ui_tour_html_pane>`: + + +- Press :kbd:`H` with a node selected to hide/show it. +- Press :kbd:`Backspace` or :kbd:`Del` with a node selected to delete it. +- :kbd:`Alt` + click on a node to expand or collapse it and all its descendants. +- Click on the last :ref:`breadcrumb button <page-inspector-how-to-examine-and-edit-html-breadcrumbs>` to scroll the selection into view in the inspector. +- Click the "ev" icon besides a node to :doc:`see all event listeners attached to it <../page_inspector/examine_event_listeners/index>`. +- Press :kbd:`S` with a node selected to see it in the page (same as right-click a node and click :ref:`Scroll Into View <page_inspector_how_to_examine_and_edit_scroll_into_view>`). +- Right-click a node and click :ref:`Use in Console<page_inspector_how_to_examine_and_edit_html_use_in_console>` to :doc:`command line <../web_console/the_command_line_interpreter/index>` as ``tempN`` variable. + + +When :ref:`selecting elements <page_inspector_select_element_button>`: + + +- :kbd:`Shift` + click to select an element but keep selecting (picker mode doesn't disengage). +- Use :kbd:`←`/:kbd:`→` to navigate to parents/children elements (if they're hard to select). + + +In the :ref:`rules view <page_inspector_ui_tour_rules_view>`: + +.. |image2| image:: picker.png + :width: 20 + +.. |image3| image:: filter.png + :width: 20 + +- Click the inspector icon |image2| next to any selector to highlight all elements that match it. +- Click the inspector icon |image2| next to the ``element{}`` rule to lock the highlighter on the current element. +- Right-click any property and select "Show MDN Docs" to view the MDN docs for this property. +- Click on the filter icon |image3| next to an overridden property to :ref:`find which other property overrides it <page-inspector-how-to-examine-and-edit-css-overridden-declarations>`. +- Right-click on a name, value, or rule to copy anything from the name, the value, the declaration or the whole rule to your clipboard. + + +Web Console +*********** + +In all panels: + +- :kbd:`Esc` opens the :doc:`split console <../web_console/split_console/index>`; useful when debugging, or inspecting nodes + + +In the :doc:`command line <../web_console/the_command_line_interpreter/index>`: + + +- :ref:`$0 <web_console_helpers_$0>` references the currently selected node. +- :ref:`$() <web_console_helpers_$>` is a shortcut to `document.querySelector() <https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector>`_ +- :ref:`$$() <web_console_helpers_$$>` returns an array with the results from `document.querySelector() <https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector>`_. +- :ref:`copy <web_console_helpers_copy>` copies anything as a string. +- Right-click a node in the Inspector and click :ref:`Use in Console <page_inspector_how_to_examine_and_edit_html_use_in_console>` to create a :ref:`temp*N* <web_console_helpers_tempn>` variable for it. +- `console.table() <https://developer.mozilla.org/en-US/docs/Web/API/console/table>`_ displays tabular data as table. +- :ref:`help <web_console_helpers_help>` opens the MDN page describing the available commands. +- ``:screenshot <filename.png> --fullpage`` saves a screenshot to your downloads directory using the optional file name. If no filename is included, the file name will be in this format: + +.. code-block:: + + Screen Shot date at time.png + +The ``--fullpage`` parameter is optional. If you include it, the screenshot will be of the whole page, not just the section visible in the browser windows. The file name will have ``-fullpage`` appended to it as well. See :doc:`Web Console Helpers <../web_console/helpers/index>` for all parameters. + + +In the console output: + + +- Click on the inspector icon |image2| next to an element in the output to :ref:`select it within the Inspector <web_console_rich_output_highlighting_and_inspecting_dom_nodes>`. +- Check :ref:`"Enable persistent logs" <settings-common-preferences>` in the settings to keep logged messages from before even after navigation. +- Check :ref:`"Enable timestamps" <settings-web-console>` in the settings to show timestamps besides the logged messages. + + +Debugger +******** + +.. |image4| image:: black_boxing.png + :width: 20 + + +- Skip JavaScript libraries in debugging sessions via the black box icon |image4|. +- Press :kbd:`Ctrl` + :kbd:`Alt` + :kbd:`F` to search in all scripts. +- Press :kbd:`Ctrl` + :kbd:`D` to search for a function definition. +- Press :kbd:`Ctrl` + :kbd:`L` to go to a specific line. + + +Style Editor +************ + +.. |image5| image:: import_button.png + :alt: Button to import a style sheet from the file system + :width: 20 + +.. |image6| image:: create_style_sheet_button.png + :alt: Button to create a new style sheet + :width: 20 + +- The black box icon |image4| in the style sheet pane toggles the visibility of a style sheet. +- Click an :ref:`@media rule <style-editor-the-at-rules-sidebar>` to apply it in :doc:`Responsive Design Mode <../responsive_design_mode/index>`. +- Click the import button |image5| to import a style sheet or the create button |image6| to create a new one. +- Click the options button in the :ref:`style sheet pane <style-editor-the-style-sheet-pane>` and click :ref:`"Show original sources" <style-editor-source-map-support>` to toggle the display of CSS preprocessor files. + + +Network Monitor +*************** + +- Click the request summary to :doc:`compare performance of cache vs. no-cache page loading <../network_monitor/performance_analysis/index>`. +- When a request is selected click :ref:`"Edit and Resend" <network-monitor-request-list-edit-and-resend>` to modify its headers and send it again. +- Check :ref:`"enable persistent logs" <settings-common-preferences>` in the settings to keep requests from before even after navigation. +- Hover the :ref:`"js" icon within the "Cause" column <request-list-requst-list-cause-column>` to see the JavaScript stack trace, which caused the request. +- Check :ref:`"Disable HTTP Cache (when toolbox is open)" <settings_advanced_settings>` in the settings to disable the network cache while debugging network issues. + + +Storage Inspector +***************** + +- Right-click the column headers to open a menu allowing to toggle the display of the columns. +- Right-click an entry and click "Delete *name*" to delete it or "Delete All" to delete all entries. +- Select an entry to see the parsed value of it in the sidebar. diff --git a/devtools/docs/user/tips/picker.png b/devtools/docs/user/tips/picker.png Binary files differnew file mode 100644 index 0000000000..39ea6adc6a --- /dev/null +++ b/devtools/docs/user/tips/picker.png diff --git a/devtools/docs/user/tips/screenshot_button.png b/devtools/docs/user/tips/screenshot_button.png Binary files differnew file mode 100644 index 0000000000..f3ee5ae219 --- /dev/null +++ b/devtools/docs/user/tips/screenshot_button.png diff --git a/devtools/docs/user/tools_toolbox/docking-mode.png b/devtools/docs/user/tools_toolbox/docking-mode.png Binary files differnew file mode 100644 index 0000000000..3bc91ceb8a --- /dev/null +++ b/devtools/docs/user/tools_toolbox/docking-mode.png diff --git a/devtools/docs/user/tools_toolbox/hamburger.png b/devtools/docs/user/tools_toolbox/hamburger.png Binary files differnew file mode 100644 index 0000000000..93e525c3c6 --- /dev/null +++ b/devtools/docs/user/tools_toolbox/hamburger.png diff --git a/devtools/docs/user/tools_toolbox/index.rst b/devtools/docs/user/tools_toolbox/index.rst new file mode 100644 index 0000000000..428c117a4c --- /dev/null +++ b/devtools/docs/user/tools_toolbox/index.rst @@ -0,0 +1,133 @@ +======= +Toolbox +======= + +The Toolbox provides a single home for most of the developer tools that are built into Firefox. + +There are three main ways to open the Toolbox: + +- Right-click a mouse on any element in the page, and select **Inspect** from the popup menu. + +- Open the Hamburger menu |image1| and select **More tools > Web Developer Tools** (Firefox 88 and earlier use the top-level menu **Web Developer** rather than **More tools**). + +- Press :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`I` on Windows and Linux, or :kbd:`Cmd` + :kbd:`Opt` + :kbd:`I` on OS X. See also :doc:`keyboard shortcuts <../keyboard_shortcuts/index>`. + +.. |image1| image:: hamburger.png + :width: 20 + + +By default, the window appears docked to the bottom side of the Firefox window, but you can detach it if you like. This is what it looks like when it's docked: + +.. image:: toolbox.png + :class: border + +The window itself is split into two parts: a toolbar along the top, and a main pane underneath: + +.. image:: toolbox-labelled.png + :class: border + +.. note:: + + Since Firefox 62, you can drag and drop tabs in the main toolbar of the toolbox to reorder your tools as you wish (`Bug 1226272 <https://bugzilla.mozilla.org/show_bug.cgi?id=1226272>`_) + + +.. _tools-toolbox-docking-mode: + +Docking mode +************ + +By default, the Toolbox appears docked to the bottom of the browser window, but you can also dock it to the right-hand side of the window, or make it a standalone window, using :ref:`buttons in toolbar <tools-toolbox-toolbox-controls>`. + +.. image:: docking-mode.png + :class: center + + +.. _tools-toolbox-toolbar: + +Toolbar +******* + +The toolbar contains controls to activate a particular tool, to dock/float the window, and to close the window. + +.. image:: toolbox-toolbar-labelled.png + :class: center + + +Node picker +*********** + +On the far left there's a button to activate the node picker. This lets you select a page element for inspection. See :doc:`Selecting elements <../page_inspector/how_to/select_an_element>` + + +Toolbox-hosted tools +******************** + +Then there is an array of labeled buttons which enables you to switch between the different tools hosted by the Toolbox. The array may include the following tools: + +- :doc:`Page Inspector <../page_inspector/index>` +- :doc:`Web Console <../web_console/index>` +- :doc:`JavaScript Debugger <../debugger/index>` +- :doc:`Network Monitor <../network_monitor/index>` +- :doc:`Style Editor <../style_editor/index>` +- :doc:`Performance <../performance/index>` +- :doc:`Memory Tool <../memory/index>` +- :doc:`Storage Inspector <../storage_inspector/index>` +- :doc:`Accessibility Inspector <../accessibility_inspector/index>` +- :doc:`Application Tool <../application/index>` +- :doc:`DOM Property Viewer <../dom_property_viewer/index>` + + +Note that not all the hosted tools are always listed here: only the tools actually available in this context are shown (for example, not all tools support remote debugging yet, so if the debugging target is not the Firefox instance that launched the window, not all the hosted tools will be shown). + + +.. _tools-toolbox-extra-tools: + +Extra tools +*********** + +Next there's an array of buttons that can be added or removed in the :ref:`settings <tool-toolbox-settings>`. None of these tools are enabled by default, but you can add them in the :doc:`developer tools settings <../settings/index>` + + +- :doc:`Select a frame as the currently targeted document <../working_with_iframes/index>` (this is only included by default from Firefox 41 onwards) +- :doc:`Highlight painted area <../paint_flashing_tool/index>` +- :ref:`Take a screenshot of the entire page <taking_screenshots_taking_a_screenshot_of_the_page>`: take a screenshot of the complete web page and saves it in your Downloads directory +- :doc:`Toggle rulers for the page <../rulers/index>` +- :doc:`Measure a portion of the page <../measure_a_portion_of_the_page/index>`: measure a part of the website by selecting areas within the page + + + +.. _tools-toolbox-toolbox-controls: + +Toolbox controls +**************** + +Finally there's a row of buttons to: + +- close the window +- :doc:`Responsive Design Mode <../responsive_design_mode/index>` + + +There's also a meatball menu button that consists of following options: + +- A group of options to toggle the toolbox to be docked bottom, right, left or even be a separate window by itself. + +- access :doc:`developer tool settings <../settings/index>` + +- :doc:`Toggle split console <../web_console/split_console/index>` + +- two buttons leading to documentation and community + + + +Settings +******** + +:doc:`See the separate page on the Developer Tools Settings <../settings/index>` + + +Main Pane +********* + +The content of the main pane in the window is entirely controlled by, and specific to, the hosted tool currently selected. + +:ref:`Toolbox shortcuts <keyboard-shortcuts-toolbox>` lists the shortcuts that work whenever the toolbox is open, no matter which tool is active. This same page also lists tool-specific keyboard shortcuts. diff --git a/devtools/docs/user/tools_toolbox/toolbox-labelled.png b/devtools/docs/user/tools_toolbox/toolbox-labelled.png Binary files differnew file mode 100644 index 0000000000..44c240ccbc --- /dev/null +++ b/devtools/docs/user/tools_toolbox/toolbox-labelled.png diff --git a/devtools/docs/user/tools_toolbox/toolbox-toolbar-labelled.png b/devtools/docs/user/tools_toolbox/toolbox-toolbar-labelled.png Binary files differnew file mode 100644 index 0000000000..ab127817ff --- /dev/null +++ b/devtools/docs/user/tools_toolbox/toolbox-toolbar-labelled.png diff --git a/devtools/docs/user/tools_toolbox/toolbox.png b/devtools/docs/user/tools_toolbox/toolbox.png Binary files differnew file mode 100644 index 0000000000..a1a75935e8 --- /dev/null +++ b/devtools/docs/user/tools_toolbox/toolbox.png diff --git a/devtools/docs/user/validators/index.rst b/devtools/docs/user/validators/index.rst new file mode 100644 index 0000000000..65abf1c4f2 --- /dev/null +++ b/devtools/docs/user/validators/index.rst @@ -0,0 +1,11 @@ +========== +Validators +========== + +This article lists different resources for developers to check web documents. + + +- `W3C HTML Validator <https://validator.w3.org/>`_ validates HTML documents. +- `W3C CSS Validator <https://jigsaw.w3.org/css-validator/>`_ validates CSS stylesheets. +- `W3C Link Checker <https://validator.w3.org/checklink>`_ checks for broken links in HTML documents. +- `HTML Tidy <https://www.html-tidy.org/>`_ tool finds errors in HTML documents, and can automatically fix some errors. diff --git a/devtools/docs/user/view_source/index.rst b/devtools/docs/user/view_source/index.rst new file mode 100644 index 0000000000..12f7983dc8 --- /dev/null +++ b/devtools/docs/user/view_source/index.rst @@ -0,0 +1,90 @@ +=========== +View Source +=========== + +View Source lets you look at the HTML or XML source for the page you're viewing. To activate View Source: + +- context-click in the page and select *View Page Source* +- press :kbd:`Ctrl` + :kbd:`U` on Windows and Linux, or :kbd:`Cmd` + :kbd:`U` on macOS + +The command opens a new tab with the source for the current page. + +View Source features +******************** + +View Source has three additional features, which can be accessed from the context menu in the View Source tab: + +.. image:: view_source_context_menu.png + :class: center + +Go to Line + Scrolls to the specified line. If the number is higher than the lines in a file, you receive an error message. +Wrap Long Lines (toggle) + Wraps long lines to the width of the page. +Syntax Highlighting (toggle) + Applies syntax highlighting to the code.When syntax highlighting is on, View Source also highlights parsing errors in red. Hovering your mouse over errors displays a tooltip explaining the error. + +.. image:: view_source_error.png + :class: border + + +This feature is useful when you're looking for HTML errors. + +To access Go to Line from the keyboard, press :kbd:`Control` + :kbd:`Option` + :kbd:`L` on macOS, or :kbd:`Alt` + :kbd:`Shift` + :kbd:`L` on Windows or Linux. + + +Link to a line number +********************* + +It is possible to link to a particular line, by adding the #lineNNN anchor to the url the browser will jump to the NNN line. + +For example view-source:https://www.mozilla.org/#line100 + + +View Selection Source +********************* + +If you select part of a web page and context-click, you'll see a context menu item labeled "View Selection Source", that behaves just like "View Page Source", except you only see the source for the selection. + + +View MathML Source +****************** + +If you context-click while the mouse is over some `MathML <https://developer.mozilla.org/en-US/docs/Web/MathML>`_, you'll see a context menu item labeled "View MathML Source": click it to see the MathML source. + + +Limitations of View Source +************************** + +There are limitations to what View Source does for you that you need to be aware of. + +Error reporter ≠ validator +-------------------------- + +View Source only reports parsing errors, **not** HTML validity errors. For example, putting a `div <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div>`_ element as a child of a `ul <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul>`_ element isn't a parse error, but it **is not** valid HTML. Therefore, this error will not be flagged in View Source. If you want to check that HTML is valid, you should use an HTML validator, such as `the one offered by W3C <https://validator.w3.org/>`_. + +Not all parse errors are reported +--------------------------------- + +Even though all the reported errors are parse errors according to the HTML specification, not all parse errors are reported by View Source. Among the errors that don't get reported: + + +- Bytes that are illegal according to the document's encoding aren't marked as errors. +- Forbidden characters aren't reported as errors. +- Errors related to the end-of-file aren't reported. +- Tree builder errors relating to text (as opposed to tags, comments, or doctypes) aren't reported. +- Parse errors related to ``xmlns`` attributes aren't reported. + + +XML syntax highlighting +*********************** + +View Source uses the HTML tokenizer when highlighting XML source. While the tokenizer has support for processing instructions when highlighting XML source, that's the only XML-specific capability provided. Because of this, doctypes that have an internal subset are not highlighted correctly, and entity references to custom entities are also not highlighted correctly. + +This mishighlighting can be seen by viewing the source of Firefox chrome files (such as XUL documents). However, this shouldn't be a problem in practice when viewing typical XML files. + + +See also +******** + +- `HTML5 Parser-Based View Source Syntax Highlighting <https://hsivonen.iki.fi/view-source/>`_ (Blog post) diff --git a/devtools/docs/user/view_source/view_source_context_menu.png b/devtools/docs/user/view_source/view_source_context_menu.png Binary files differnew file mode 100644 index 0000000000..c0f2039997 --- /dev/null +++ b/devtools/docs/user/view_source/view_source_context_menu.png diff --git a/devtools/docs/user/view_source/view_source_error.png b/devtools/docs/user/view_source/view_source_error.png Binary files differnew file mode 100644 index 0000000000..a38c932f9b --- /dev/null +++ b/devtools/docs/user/view_source/view_source_error.png diff --git a/devtools/docs/user/web_audio_editor/index.rst b/devtools/docs/user/web_audio_editor/index.rst new file mode 100644 index 0000000000..4318714424 --- /dev/null +++ b/devtools/docs/user/web_audio_editor/index.rst @@ -0,0 +1,86 @@ +================ +Web Audio Editor +================ + +.. note:: + + Notice: This tool has been deprecated and will soon be removed from Firefox. For details, see :doc:`Deprecated tools <../deprecated_tools/index>`. + + +With the `Web Audio API <https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Using_Web_Audio_API>`_, developers create an `audio context <https://developer.mozilla.org/en-US/docs/Web/API/AudioContext>`_ Within that context they then construct a number of `audio nodes <https://developer.mozilla.org/en-US/docs/Web/API/AudioNode>`_, including: + + +- nodes providing the `audio source <https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API#defining_audio_sources>`_, such as an oscillator or a data buffer source +- nodes performing `transformations <https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API#defining_audio_effects_filters>`_ such as delay and gain +- nodes representing the `destination of the audio stream <https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API#defining_audio_destinations>`_, such as the speakers + + +Each node has zero or more `AudioParam <https://developer.mozilla.org/en-US/docs/Web/API/AudioParam>`_ properties that configure its operation. For example, the `GainNode <https://developer.mozilla.org/en-US/docs/Web/API/GainNode>`_ has a single ``gain`` property, while the `OscillatorNode <https://developer.mozilla.org/en-US/docs/Web/API/OscillatorNode>`_ has ``frequency`` and ``detune`` properties. + +The developer connects the nodes in a graph, and the complete graph defines the behavior of the audio stream. + +The Web Audio Editor examines an audio context constructed in the page and provides a visualization of its graph. This gives you a high-level view of its operation, and enables you to ensure that all the nodes are connected in the way you expect. You can then examine and edit the ``AudioParam`` properties for each node in the graph. Some non-``AudioParam`` properties, like an ``OscillatorNode``'s ``type`` property, are displayed, and you can edit these as well. + +This tool is still experimental. If you find bugs, we'd love it if you `filed them in Bugzilla <https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=Developer%20Tools%3A%20Web%20Audio%20Editor>`_. If you have feedback or suggestions for new features, `dev-developer-tools <https://lists.mozilla.org/listinfo/dev-developer-tools>`_ or `Twitter <https://twitter.com/firefoxdevtools>`_ are great places to register them. + + +Opening the Web Audio Editor +**************************** + +The Web Audio Editor is not enabled by default in Firefox 32. To enable it, open the :ref:`Developer Tool Settings <tool-toolbox-settings>` and check "Web Audio". Now there should be an extra tab in the :ref:`Toolbox toolbar <tools-toolbox-toolbar>` labeled "Web Audio". Click the tab and load a page that constructs an audio context. Two good demos are: + + +- the `Voice-change-O-Matic <https://github.com/mdn/voice-change-o-matic>`_, which can apply various effects to the microphone input and also provides a visualisation of the result +- the `Violent Theremin <https://mdn.github.io/webaudio-examples/violent-theremin/>`_, which changes the pitch and volume of a sine wave as you move the mouse pointer + + +Visualizing the graph +********************* + +The Web Audio Editor will now display the graph for the loaded audio context. Here's the graph for the Violent Theremin demo: + +.. image:: web-audio-editor.png + :class: center + +You can see that it uses three nodes: an `OscillatorNode <https://developer.mozilla.org/en-US/docs/Web/API/OscillatorNode>`_ as the source, a `GainNode <https://developer.mozilla.org/en-US/docs/Web/API/GainNode>`_ to control the volume, and an `GainNode <https://developer.mozilla.org/en-US/docs/Web/API/GainNode>`_ as the destination. + + +Connections to AudioParams +-------------------------- + +Displaying connections to AudioParams is new in Firefox 34. + + +Connections between nodes are displayed as solid lines. If, instead, you've connected a node to an AudioParam in another node, then the connection is shown as a dashed line between the nodes, and is labeled with the name of the ``AudioParam``: + +.. image:: web_audio-editor-connect-param.png + :class: center + + +Inspecting and modifying AudioNodes +*********************************** + +If you click on a node, it's highlighted and you get a node inspector on the right hand side. This list the values of that node's ``AudioParam`` properties. For example, here's what the OscillatorNode looks like: + +.. image:: web-audio-editor-props.png + :class: center + +With the Violent Theremin demo, the frequency parameter is modified as the user moves the mouse left and right, and you can see this reflected in the node inspector. However, the value isn't updated in real time: you have to click the node again to see the updated value. + +If you click on a value in the node inspector you can modify it: press :kbd:`Enter` or :kbd:`Tab` and the new value takes effect immediately. + + +Bypassing nodes +*************** + +New in Firefox 38. + +In the pane that shows you the node's details, there's an on/off button: + +.. image:: web-audio-editor-on-off.png + :class: center + +Click it, and the graph will be modified to bypass this node, so it will no longer have any effect. Nodes that are bypassed are shown with a hatched background: + +.. image:: web-audio-editor-bypassed.png + :class: center diff --git a/devtools/docs/user/web_audio_editor/web-audio-editor-bypassed.png b/devtools/docs/user/web_audio_editor/web-audio-editor-bypassed.png Binary files differnew file mode 100644 index 0000000000..ed8a734fb8 --- /dev/null +++ b/devtools/docs/user/web_audio_editor/web-audio-editor-bypassed.png diff --git a/devtools/docs/user/web_audio_editor/web-audio-editor-on-off.png b/devtools/docs/user/web_audio_editor/web-audio-editor-on-off.png Binary files differnew file mode 100644 index 0000000000..b9aabc4552 --- /dev/null +++ b/devtools/docs/user/web_audio_editor/web-audio-editor-on-off.png diff --git a/devtools/docs/user/web_audio_editor/web-audio-editor-props.png b/devtools/docs/user/web_audio_editor/web-audio-editor-props.png Binary files differnew file mode 100644 index 0000000000..f9d8d973e9 --- /dev/null +++ b/devtools/docs/user/web_audio_editor/web-audio-editor-props.png diff --git a/devtools/docs/user/web_audio_editor/web-audio-editor.png b/devtools/docs/user/web_audio_editor/web-audio-editor.png Binary files differnew file mode 100644 index 0000000000..295b242544 --- /dev/null +++ b/devtools/docs/user/web_audio_editor/web-audio-editor.png diff --git a/devtools/docs/user/web_audio_editor/web_audio-editor-connect-param.png b/devtools/docs/user/web_audio_editor/web_audio-editor-connect-param.png Binary files differnew file mode 100644 index 0000000000..33bd74359a --- /dev/null +++ b/devtools/docs/user/web_audio_editor/web_audio-editor-connect-param.png diff --git a/devtools/docs/user/web_console/console_messages/async-trace.png b/devtools/docs/user/web_console/console_messages/async-trace.png Binary files differnew file mode 100644 index 0000000000..d9c83ac57d --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/async-trace.png diff --git a/devtools/docs/user/web_console/console_messages/blocked-icon.png b/devtools/docs/user/web_console/console_messages/blocked-icon.png Binary files differnew file mode 100644 index 0000000000..79643681b3 --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/blocked-icon.png diff --git a/devtools/docs/user/web_console/console_messages/console-messages-fx79.png b/devtools/docs/user/web_console/console_messages/console-messages-fx79.png Binary files differnew file mode 100644 index 0000000000..99fbe9237e --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/console-messages-fx79.png diff --git a/devtools/docs/user/web_console/console_messages/console-msg-annotated.png b/devtools/docs/user/web_console/console_messages/console-msg-annotated.png Binary files differnew file mode 100644 index 0000000000..c0038402de --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/console-msg-annotated.png diff --git a/devtools/docs/user/web_console/console_messages/console-toolbar.png b/devtools/docs/user/web_console/console_messages/console-toolbar.png Binary files differnew file mode 100644 index 0000000000..55ba23e4e6 --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/console-toolbar.png diff --git a/devtools/docs/user/web_console/console_messages/console_clear_filter.png b/devtools/docs/user/web_console/console_messages/console_clear_filter.png Binary files differnew file mode 100644 index 0000000000..2fa5712f2b --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/console_clear_filter.png diff --git a/devtools/docs/user/web_console/console_messages/css_warnings.png b/devtools/docs/user/web_console/console_messages/css_warnings.png Binary files differnew file mode 100644 index 0000000000..3b5093fbe4 --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/css_warnings.png diff --git a/devtools/docs/user/web_console/console_messages/error-icon.png b/devtools/docs/user/web_console/console_messages/error-icon.png Binary files differnew file mode 100644 index 0000000000..57c2f5245b --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/error-icon.png diff --git a/devtools/docs/user/web_console/console_messages/error-stack.png b/devtools/docs/user/web_console/console_messages/error-stack.png Binary files differnew file mode 100644 index 0000000000..f31eda5410 --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/error-stack.png diff --git a/devtools/docs/user/web_console/console_messages/index.rst b/devtools/docs/user/web_console/console_messages/index.rst new file mode 100644 index 0000000000..71b26064d8 --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/index.rst @@ -0,0 +1,374 @@ +================ +Console messages +================ + +Most of the Web Console is occupied by the message display pane: + +.. image:: console-messages-fx79.png + :alt: Screenshot of the web console, highlighting the messages area + :class: center + +Each message is displayed as a separate row: + +.. image:: console-msg-annotated.png + :alt: Screenshot of a single console message, with its parts annotated + :class: center + +.. |image1| image:: info-icon.png + :alt: An "i" inside a circle + +.. |image2| image:: warning-icon.png + :alt: A "!" inside a yellow triangle + +.. |image3| image:: error-icon.png + :alt: A "!" inside a solid red circle + +.. |image4| image:: blocked-icon.png + :alt: A red circle with a slash across it + + +.. list-table:: + :widths: 25 75 + :header-rows: 0 + + * - **Time** + - The time the message was recorded. This is not shown by default: you can opt to see timestamps by selecting **Show Timestamps** in the console settings menu (gear icon in the console toolbar). + + * - **Icon** + - Not all console messages contain icons. The following icons may be used: + + - |image1| Informational message + - |image2| Warning + - |image3| Error + - |image4| Blocked; for network messages + + In addition, a disclosure triangle indicates that further information is available; clicking it displays or collapses that information. + + * - **Message** + - The message itself. + + * - **Number of occurrences** + - If a line that generates a warning or error is executed more than once, it is only logged once and this counter appears to indicate how many times it was encountered. + + * - **Filename and line number** + - For JavaScript, CSS and console API messages, the message can be traced to a specific line of code. The console then provides a link to the filename and line number that generated the message. + + +By default, the console is cleared each time you navigate to a new page or reload the current page. To override this behavior, enable **Persist Logs** in the console settings menu (gear icon). + +The context menu options listed below are available on all message categories. Additional context menu options are described in the subsection for the message category they apply to. + + +- **Copy Message** copies the selected message to the clipboard. +- **Select All** selects all messages available in the message display pane. +- **Export Visible Messages To** + + - **Clipboard** copies all messages available in the display pane to the clipboard. + - **File** opens a file dialog box so you can save an export of all messages available in the display pane. + + +Message categories +****************** + +.. _web_console_console_messages: + +Network +------- + +.. note:: + Network log messages are not shown by default. Use the :ref:`filtering <web_console_ui_tour_filtering_by_category>` feature to show them. + + +Network requests are logged with a line that looks like this: + +.. image:: response-msg-annotated.png + :alt: Screenshot of a network response message, with its parts annotated + :class: center + + +.. list-table:: + :widths: 25 75 + :header-rows: 0 + + * - **Time** + - The time the message was recorded. + + * - **Method** + - The specific HTTP request method. + + If the request was made as an `XMLHttpRequest <https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest>`_, there's an additional "XHR" tag indicating this. + + If more information is available, a disclosure triangle lets you display it, in an embedded panel that is identical to the :doc:`Network Monitor request details <../../network_monitor/request_details/index>`. + + * - **URI** + - The target URI. + + * - **Summary** + - The HTTP version, response code, and time taken to complete. Clicking the response code takes you to the reference page for that code. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/cFlcWzJ9j4I" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +The context menu for network messages includes a few extra items in addition the globally-available ones: + +.. image:: response-msg-context-menu.png + :alt: Screenshot showing the context menu for network response messages + :class: border + + +Copy link location + Acts as you would expect, copying the URL into the clipboard +Open in Network Panel + Switches context to the Network tab, selects the request and shows you the details +Resend Request + Sends the network request again. +Open URL in New Tab + Opens the URL in a new browser tab. If the resource is an image, the image will be opened in a new tab. If the resource is a stylesheet, you will see the CSS rules, etc. + + +JS +-- + +JavaScript errors contain a "Learn more" linkthat takes you to the `JavaScript error reference <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors>`_ containing additional advice for fixing issues. + + +Source maps +~~~~~~~~~~~ + +The Web Console understands `source maps <https://blog.teamtreehouse.com/introduction-source-maps>`_. This means that if your JavaScript sources are compressed, you can supply a source map for them. Then any messages or errors your source generates will show up in the Web Console with a link back to the original source, not the compressed version. + +Async stack frames +~~~~~~~~~~~~~~~~~~ + +Stack traces show stack frames for `async functions <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function>`_ separately from those for synchronous functions. When you run code containing an async function, its traces (`console.trace <https://developer.mozilla.org/en-US/docs/Web/API/console/trace>`_ or `thrown error <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error>`_) are shown with an *Async:* prefix. + +.. image:: async-trace.png + :alt: Console log showing a trace containing async code + :class: center + + +CSS +--- + +.. note:: + CSS warnings and reflow messages are not shown by default, for performance reasons (see `bug 1452143 <https://bugzilla.mozilla.org/show_bug.cgi?id=1452143>`_. Use the :ref:`filtering <web_console_ui_tour_filtering_by_category>` feature to show them. + + +Some CSS messages contain a disclosure triangle at the left of the message. Click it to view more information about the error, as well as which DOM nodes are affected by the error. + +.. image:: css_warnings.png + :class: center + + +Security +-------- + +The security messages shown in the Web Console help developers find potential or actual vulnerabilities in their sites. Additionally, many of these messages help educate developers because they end with a “Learn More” link that takes you to a page with background information and advice for mitigating the issue. + +The complete list of security messages is as follows: + +.. list-table:: + :widths: 50 50 + :header-rows: 1 + + * - Messages + - Details + + * - Blocked loading mixed active content + - The page contained mixed active content: that is, the main page was served over HTTPS, but asked the browser to load "active content", such as scripts, over HTTP. The browser blocked this active content. See `Mixed Content <https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content>`_ for more details. + + * - Blocked loading mixed display content + - The page contained mixed display content: that is, the main page was served over HTTPS, but asked the browser to load "display content", such as images, over HTTP. The browser blocked this display content. See `Mixed Content <https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content>`_ for more details. + + * - Loading mixed (insecure) active content on a secure page + - The page contained mixed active content: that is, the main page was served over HTTPS, but asked the browser to load "active content", such as scripts, over HTTP. The browser loaded this active content. See `Mixed Content <https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content>`_ for more details. + + * - Loading mixed (insecure) display content on a secure page + - The page contained mixed display content: that is, the main page was served over HTTPS, but asked the browser to load "display content", such as images, over HTTP. The browser loaded this display content. `Mixed Content <https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content>`_ for more details. + + * - This site specified both an X-Content-Security-Policy/Report-Only header and a Content-Security-Policy/Report-Only header. The X-Content-Security-Policy/Report-Only header(s) will be ignored. + - See `Content Security Policy <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>`_ for more details. + + * - The X-Content-Security-Policy and X-Content-Security-Report-Only headers will be deprecated in the future. Please use the Content-Security-Policy and Content-Security-Report-Only headers with CSP spec compliant syntax instead. + - See `Content Security Policy <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>`_ for more details. + + * - Password fields present on an insecure (http://) page. This is a security risk that allows user login credentials to be stolen. + - Pages containing login forms must be served over HTTPS, not HTTP. + + * - Password fields present in a form with an insecure (http://) form action. This is a security risk that allows user login credentials to be stolen. + - Forms containing password fields must submit them over HTTPS, not HTTP. + + * - Password fields present on an insecure (http://) iframe. This is a security risk that allows user login credentials to be stolen. + - iframes containing login forms must be served over HTTPS, not HTTP. + + * - The site specified an invalid Strict-Transport-Security header. + - See `HTTP Strict Transport Security <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security>`_ for more details. + + * - This site makes use of a SHA-1 Certificate; it's recommended you use certificates with signature algorithms that use hash functions stronger than SHA-1. + + - The site uses a certificate whose signature uses the SHA-1 hash algorithm. + + SHA-1 is still widely used in certificates, but it is starting to show its age. Web sites and Certification Authorities are encouraged to switch to stronger hash algorithms in future. See the `Weak Signature Algorithm <https://developer.mozilla.org/en-US/docs/Web/Security/Weak_Signature_Algorithm>`_ article for more details. + + Note that the SHA-1 certificate may not be your site's own certificate, but may be the certificate belonging to a Certification Authority that was used to sign your site's certificate. + + +`Bug 863874 <https://bugzilla.mozilla.org/show_bug.cgi?id=863874>`_ is the meta-bug for logging relevant security messages to the Web Console. If you have more ideas for useful features like the ones discussed here, or are interested in contributing, check out the metabug and its dependencies. + + +Logging +------- + +.. note:: + Messages logged from `Shared Workers <https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker>`_, `Service Workers <https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API>`_, add-ons, and **Chrome Workers** are not shown by default. Use the :ref:`filtering <web_console_ui_tour_filtering_by_category>` feature to show them. + + +The Logging category includes messages logged using the `Console API <https://developer.mozilla.org/en-US/docs/Web/API/console>`_. + +The Web console supports the following `Console API <https://developer.mozilla.org/en-US/docs/Web/API/console>`_ messages: + + +- `assert() <https://developer.mozilla.org/en-US/docs/Web/API/console/assert>`_ +- `clear() <https://developer.mozilla.org/en-US/docs/Web/API/console/clear>`_ +- `count() <https://developer.mozilla.org/en-US/docs/Web/API/console/count>`_ +- `dir() <https://developer.mozilla.org/en-US/docs/Web/API/console/dir>`_ +- `dirxml() <https://developer.mozilla.org/en-US/docs/Web/API/console/dirxml>`_ +- `error() <https://developer.mozilla.org/en-US/docs/Web/API/console/error>`_ +- ``exception()`` +- `group() <https://developer.mozilla.org/en-US/docs/Web/API/console/group>`_ +- `groupEnd() <https://developer.mozilla.org/en-US/docs/Web/API/console/groupEnd>`_ +- ``info()`` +- `log() <https://developer.mozilla.org/en-US/docs/Web/API/console/log>`_ +- `table() <https://developer.mozilla.org/en-US/docs/Web/API/console/table>`_ +- `time() <https://developer.mozilla.org/en-US/docs/Web/API/console/time>`_ +- `timeEnd() <https://developer.mozilla.org/en-US/docs/Web/API/console/timeEnd>`_ +- `trace() <https://developer.mozilla.org/en-US/docs/Web/API/console/trace>`_ +- `warn() <https://developer.mozilla.org/en-US/docs/Web/API/console/warn>`_ + + +The console prints a stack trace for all error messages, like this: + +.. code-block:: javascript + + function foo() { + console.error("it explodes"); + } + + function bar() { + foo(); + } + + function doStuff() { + bar(); + } + + doStuff(); + +.. image:: error-stack.png + :class: center + + +.. _web_console_server: + +Server +------ + +.. note:: + + Server-side log messages was introduced in Firefox 43, but removed in Firefox 56. You can install the `Chrome Logger extension <https://addons.mozilla.org/en-US/firefox/addon/chromelogger/>`_ to (re)-enable the feature. + + +With the `Chrome Logger extension <https://addons.mozilla.org/en-US/firefox/addon/chromelogger/>`_, Web Console can display messages sent from the server. This enables you to use the Web Console to debug server-side code. + + +It uses the `Chrome Logger <https://craig.is/writing/chrome-logger>`_ protocol. Briefly, the way it works is: + + +- Your server-side code — Python, PHP, Node.js, etc. — includes a library that provides a console API. +- Your server-side code uses this API to log messages. +- The server-side library creates JSON objects from the messages and encodes them for transmission. +- The messages are transmitted to the client as a response header named ``X-ChromeLogger-Data``. +- The Web Console decodes these headers and displays them. + + +To find a suitable library for your server code, see the `Chrome Logger documentation <https://craig.is/writing/chrome-logger>`_. + + +.. _web_console_console_messages_interpreter_io: + +Interpreter input/output +------------------------ + +Commands sent to the browser using the :doc:`Web Console's JavaScript interpreter <../the_command_line_interpreter/index>`, and the corresponding responses, are logged in the console messages. + +For responses that contain objects or variables, the following context menu options are available: + + +Reveal in Inspector + Shows the selected DOM node in the Inspector pane. +Store as Global Variable + Creates a global variable (with a name like ``temp0``, ``temp1``, etc.) whose value is the selected object. The name of the variable appears as an input to the interpreter, and its value appears as a response. +Copy Object + Copies the selected object to the clipboard. + + + +Filtering and searching +*********************** + +.. _web_console_ui_tour_filtering_by_category: + +Filtering by category +--------------------- + +You can use the toolbar along the top to constrain the results displayed. + +.. image:: console-toolbar.png + :alt: Screenshot showing the web console, with the toolbar highlighted + :class: center + +To see only messages of particular categories, click the button labeled with that category (**Errors**, **CSS**, and so on). + +For Errors and Warnings, when you turn off display of the category, a number appears next to the button text to indicate how many messages of that type are available. For example, "Warnings (25)". + +Network requests with response codes in the 400-499 (client error) or 500-599 (server error) ranges are considered errors. Their display is controlled by the **Errors** button, not the **Requests** button. + + +.. _web_console_ui_tour_filtering_by_text: + +Filtering by text +----------------- + +To see only messages that contain a specific string, type in the text box labeled "Filter output". For example, if you entered the string img into the text box, you would have a list something like this: + +.. image:: console_clear_filter.png + :class: border + + +A small "x" icon appears at the right end of the text box when you have entered a string on which to filter the output. Click the "x" icon to clear the filter and show the entire list again. + +You can negate a text search by prefixing it with the ``-`` character. For example, ``-img`` shows only items that *do not* contain the string ``img``. + + +.. _web_console_ui_tour_filtering_by_regular_expressions: + +Filtering with Regular Expressions +---------------------------------- + +You can also use a valid regular expression to filter the console output. For example, the following video shows the results when filtering on two simple regular expressions: ``/(cool|rad)/`` and ``/(cool)/``. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/E6bGOe2fvW0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +You can negate a regular expression search by prefixing it with the ``-`` character. For example, ``-/(cool|rad)/`` shows only items that *do not* match the expression ``/(cool|rad)/``. + +Clearing the log +---------------- + +Finally, you can use the trashcan icon on the left to clear the contents of the console. When you clear the console, the console cache is also cleared. This prevents errors that have already been logged from reappearing when you reopen the console. diff --git a/devtools/docs/user/web_console/console_messages/info-icon.png b/devtools/docs/user/web_console/console_messages/info-icon.png Binary files differnew file mode 100644 index 0000000000..a823b483a8 --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/info-icon.png diff --git a/devtools/docs/user/web_console/console_messages/response-msg-annotated.png b/devtools/docs/user/web_console/console_messages/response-msg-annotated.png Binary files differnew file mode 100644 index 0000000000..cec871d88c --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/response-msg-annotated.png diff --git a/devtools/docs/user/web_console/console_messages/response-msg-context-menu.png b/devtools/docs/user/web_console/console_messages/response-msg-context-menu.png Binary files differnew file mode 100644 index 0000000000..18b0fca315 --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/response-msg-context-menu.png diff --git a/devtools/docs/user/web_console/console_messages/warning-icon.png b/devtools/docs/user/web_console/console_messages/warning-icon.png Binary files differnew file mode 100644 index 0000000000..f8e8769a6e --- /dev/null +++ b/devtools/docs/user/web_console/console_messages/warning-icon.png diff --git a/devtools/docs/user/web_console/helpers/index.rst b/devtools/docs/user/web_console/helpers/index.rst new file mode 100644 index 0000000000..9a1013dc3e --- /dev/null +++ b/devtools/docs/user/web_console/helpers/index.rst @@ -0,0 +1,140 @@ +=================== +Web Console Helpers +=================== + +The JavaScript command line provided by the Web Console offers a few built-in helper functions that make certain tasks easier. + +$(selector, element) + Looks up a CSS selector string ``selector`` , returning the first node descended from ``element`` that matches. If unspecified, ``element`` defaults to ``document``. Equivalent to `document.querySelector() <https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector>`_ or calls the $ function in the page, if it exists. + + See the `QuerySelector code snippet <https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector>`_. + +.. _web_console_helpers_$$: + +$$(selector, element) + Looks up a CSS selector string ``selector``, returning an array of DOMnodesdescended from ``element`` that match. If unspecified, ``element`` defaults to ``document``. This is like for `document.querySelectorAll() <https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll>`_, but returns an array instead of a `NodeList <https://developer.mozilla.org/en-US/docs/Web/API/NodeList>`_. + +.. _web_console_helpers_$0: + +$0 + The currently-inspected element in the page. + +.. _web_console_helpers_$: + +$_ + Stores the result of the last expression executed in the console's command line. For example, if you type "2+2 <enter>", then "$_ <enter>", the console will print 4. + +$x(xpath, element, resultType) + Evaluates the `XPath <https://developer.mozilla.org/en-US/docs/Web/XPath>`_ ``xpath`` expression in the context of ``element`` and returns an array of matching nodes. If unspecified, ``element`` defaults to ``document``. The resultType parameter specifies the type of result to return; it can be an `XPathResult constant <https://developer.mozilla.org/en-US/docs/Web/API/XPathResult#constants>`_, or a corresponding string: ``"number"``, ``"string"``, ``"bool"``, ``"node"``, or ``"nodes"``; if not provided, ``ANY_TYPE`` is used. + +:block + (Starting in Firefox 80) Followed by an unquoted string, blocks requests where the URL contains that string. In the :doc:`Network Monitor <../../network_monitor/index>`, the string now appears and is selected in the :ref:`Request Blocking sidebar <network_monitor_blocking_specific_urls>`. Unblock with ``:unblock``. + +clear() + Clears the console output area. + +clearHistory() + Just like a normal command line, the console command line :ref:`remembers the commands you've typed <command_line_interpreter_execution_history>`. Use this function to clear the console's command history. + +.. _web_console_helpers_copy: + +copy() + Copies the argument to the clipboard. If the argument is a string, it's copied as-is. If the argument is a DOM node, its `outerHTML <https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML>`_ is copied. Otherwise, `JSON.stringify <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify>`_ will be called on the argument, and the result will be copied to the clipboard. + +**help()** (deprecated) + +.. _web_console_helpers_help: + +:help + Displays help text. Actually, in a delightful example of recursion, it brings you to this page. + +inspect() + Given an object, generates:doc:`rich output <../rich_output/index>` for that object. Once you select the object in the output area, you can use the arrow keys to navigate the object. + +keys() + Given an object, returns a list of the keys (or property names) on that object. This is a shortcut for `Object.keys <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys>`_. + +pprint() (deprecated) + Formats the specified value in a readable way; this is useful for dumping the contents of objects and arrays. + +:screenshot + Creates a screenshot of the current page with the supplied filename. If you don't supply a filename, the image file will be named with the following format: + + ``Screen Shot yyy-mm-dd at hh.mm.ss.png`` + + The command has the following optional parameters: + +.. list-table:: + :widths: 20 20 60 + :header-rows: 1 + + * - Command + - Type + - Description + + * - ``--clipboard`` + - boolean + - When present, this parameter will cause the screenshot to be copied to the clipboard. + + * - ``--dpr`` + - number + - The device pixel ratio to use when taking the screenshot. + + * - ``--file`` + - boolean + - When present, the screenshot will be saved to a file, even if other options (e.g. ``--clipboard``) are included. + + * - ``--filename`` + - string + - The name to use in saving the file. The file should have a ".png" extension. + + * - ``--fullpage`` + - boolean + - If included, the full webpage will be saved. With this parameter, even the parts of the webpage which are outside the current bounds of the window will be included in the screenshot. When used, ``-fullpage`` will be appended to the file name. + + * - ``--selector`` + - string + - The CSS query selector for a single element on the page. When supplied, only this element will be included in the screenshot. + + +:unblock + (Starting in Firefox 80) Followed by an unquoted string, removes blocking for URLs containing that string. In the :doc:`Network Monitor <../../network_monitor/index>`, the string is removed from the :ref:`Request Blocking sidebar <network_monitor_blocking_specific_urls>`. No error is given if the string was not previously blocked. + +values() + Given an object, returns a list of the values on that object; serves as a companion to ``keys()``. + + +Please refer to the `Console API <https://developer.mozilla.org/en-US/docs/Web/API/console>`_ for more information about logging from content. + + +Variables +********* + +.. _web_console_helpers_tempn: + +temp*N* + The :ref:`Use in Console <page_inspector_how_to_examine_and_edit_html_use_in_console>` option in the Inspector generates a variable for a node named ``temp0``, ``temp1``, ``temp2``, etc. referencing the node. + + +Examples +******** + +Looking at the contents of a DOMnode +------------------------------------ + +Let's say you have a DOMnode with the class"title". In fact, this page you're reading right now has one, so you can open up the Web Console and try this right now. + +Let's take a look at the contents of that node by using the ``$()`` and ``inspect()`` functions: + +.. code-block:: javascript + + inspect($(".title")) + + +This automatically generates rich output for the object, showing you the contents of the first DOMnode that matches the CSS selector ``".title"``, which is of course the first element with class ``"title"``. You can use the up- and down-arrow keys to navigate through the output, the right-arrow key to expand an item, and the left-arrow key to collapse it. + + +See also +******** + +- `console <https://developer.mozilla.org/en-US/docs/Web/API/console>`_ diff --git a/devtools/docs/user/web_console/index.rst b/devtools/docs/user/web_console/index.rst new file mode 100644 index 0000000000..2d0713410f --- /dev/null +++ b/devtools/docs/user/web_console/index.rst @@ -0,0 +1,55 @@ +=========== +Web Console +=========== + + +The **Web Console:** + + 1. Logs information associated with a web page: network requests, JavaScript, CSS, security errors and warnings as well as error, warning and informational messages explicitly logged by JavaScript code running in the page context + + 2. Enables you to interact with a web page by executing JavaScript expressions in the context of the page + + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/C6Cyrpkb25k" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +.. list-table:: + :widths: 30 70 + :header-rows: 0 + + * - :doc:`User interface of the Web Console <ui_tour/index>` + - Parts of the Web Console UI. + + * - :doc:`The JavaScript input interpreter <the_command_line_interpreter/index>` + - How to interact with a document using the Console. + + * - :doc:`Split console <split_console/index>` + - Use the Console alongside other tools. + + * - :doc:`Console messages <console_messages/index>` + - Details of the messages that the Console logs. + + * - :doc:`Helper commands <helpers/index>` + - Commands use can use that are not part of JavaScript. + + * - :doc:`Rich output <rich_output/index>` + - See and interact with objects logged by the Console. + + * - :ref:`Keyboard shortcuts <keyboard-shortcuts-web-console>` + - Shortcut reference + + +Opening the Web Console +*********************** + +You open the Web Console from a menu or with a keyboard shortcut: + +- Select the *Web Console* panel in the Web Developer Tools, accessible from the Browser Tools submenu + +- Press the :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`K` (:kbd:`Cmd` + :kbd:`Opt` + :kbd:`K` on OS X) keyboard shortcut. + + +The :doc:`Toolbox <../tools_toolbox/index>` appear at the bottom, left, or right of the browser window (depending on your docking settings), with the Web Console activated (it's just called **Console** in the :ref:`DevTools toolbar <tools-toolbox-toolbar>` diff --git a/devtools/docs/user/web_console/invoke_getters_from_autocomplete/index.rst b/devtools/docs/user/web_console/invoke_getters_from_autocomplete/index.rst new file mode 100644 index 0000000000..c40b236686 --- /dev/null +++ b/devtools/docs/user/web_console/invoke_getters_from_autocomplete/index.rst @@ -0,0 +1,6 @@ +================================ +Invoke getters from autocomplete +================================ + +.. note:: + Draft diff --git a/devtools/docs/user/web_console/remoting/index.rst b/devtools/docs/user/web_console/remoting/index.rst new file mode 100644 index 0000000000..ed7578c7b0 --- /dev/null +++ b/devtools/docs/user/web_console/remoting/index.rst @@ -0,0 +1,666 @@ +==================== +Web Console remoting +==================== + +Introduction +************ + +This document describes the way Web Console remoting works. The Web Console is split between a client with its user interface, and the server which has listeners for all the things that happen in the tab. For communication between the server and the client we use the `Remote Debugging Protocol <https://wiki.mozilla.org/Remote_Debugging_Protocol>`_. This architecture allows you to connect a Web Console client instance to a server running on B2G, Fennec or some other Firefox instance. + +To better understand the architecture of the Web Console we recommend learning about the `debugger architecture <https://wiki.mozilla.org/Debugger_Architecture>`_. + +.. note:: + The remote Web Console is a feature introduced in Firefox 18. This document describes the latest protocol, with changes that have been made since then. + + +The ``WebConsoleActor`` and the ``WebConsoleClient`` +**************************************************** + +The ``WebConsoleActor`` lives in `dbg-webconsole-actors.js <http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/webconsole/dbg-webconsole-actors.js>`_, in the `toolkit/devtools/webconsole <http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/webconsole/>`_ folder. + +The ``WebConsoleClient`` lives in `WebConsoleClient.jsm <http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/webconsole/WebConsoleClient.jsm/>`_ (in `toolkit/devtools/webconsole <http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/webconsole/>`_) and it is used by the Web Console when working with the Web Console actor. + +To see how the debugger is used in the Web Console code, look in `browser/devtools/webconsole/webconsole.js <http://mxr.mozilla.org/mozilla-central/source/browser/devtools/webconsole/webconsole.js/>`_, and search for ``WebConsoleConnectionProxy``. + +The new Web Console actors are: + +- The ``WebConsoleActor`` allows JS evaluation, autocomplete, start/stop listeners, etc. +- The ``NetworkEventActor`` is used for each new network request. The client can request further network event details - like response body or request headers. + + +To attach to the ``WebConsoleActor``, follow these steps: + +.. code-block:: javascript + + connectToServer() // the usual + listTabs() + pickTheTabYouWant() + debuggerClient.attachConsole(tab.consoleActor, listeners, onAttachConsole) + + +The ``listeners`` argument is an array which specifies listeners you want to start in the web console. These can be: page errors, ``window.console`` API messages, network activity, and file activity. For example: + +.. code-block:: javascript + + ["PageError", "ConsoleAPI", "FileActivity"] + + +.. note:: + The Web Console actor does not start any listeners by default. The client has the option to start each listener when needed. This approach allows for lower resource usage on the server - this is a potential issue if the server runs on devices with fewer resources. + + +The ``onAttachConsole`` callback receives a new instance of the ``WebConsoleClient`` object. This object provides methods that abstract away protocol packets, things like ``startListeners(), stopListeners()``, etc. + +Protocol packets look as follows: + +.. code-block:: javascript + + { + "to": "root", + "type": "listTabs" + } + { + "from": "root", + "consoleActor": "conn0.console9", + "selected": 2, + "tabs": [ + { + "actor": "conn0.tab2", + "consoleActor": "conn0.console7", + "title": "", + "url": "https://tbpl.mozilla.org/?tree=Fx-Team" + }, + // ... + ] + } + + +Notice that the ``consoleActor`` is also available as a **global actor**. When you attach to the global ``consoleActor`` you receive all of the network requests, page errors, and the other events from all of the tabs and windows, including chrome errors and network events. This actor is used for the Browser Console implementation and for debugging remote Firefox/B2G instances. + + +``startListeners(listeners, onResponse)`` +----------------------------------------- + +The new ``startListeners`` packet: + +.. code-block:: javascript + + { + "to": "conn0.console9", + "type": "startListeners", + "listeners": [ + "PageError", + "ConsoleAPI", + "FileActivity" + ] + } + +The reply is: + +.. code-block:: javascript + + { + "startedListeners": [ + "PageError", + "ConsoleAPI", + "FileActivity" + ], + "from": "conn0.console9" + } + + +The reply tells which listeners were started. + + +Tab navigation +-------------- + +To listen to the tab navigation events you also need to attach to the tab actor for the given tab. The ``tabNavigated`` notification comes from tab actors. + +.. warning:: + Prior to Firefox 20 the Web Console actor provided a ``LocationChange`` listener, with an associated ``locationChanged`` notification. This is no longer the case: we have made changes to allow the Web Console client to reuse the ``tabNavigated`` notification (`bug 792062 <https://bugzilla.mozilla.org/show_bug.cgi?id=792062>`_). + + +When page navigation starts the following packet is sent from the tab actor: + +.. code-block:: + + { + "from": tabActor, + "type": "tabNavigated", + "state": "start", + "url": newURL, + } + + +When navigation stops the following packet is sent: + +.. code-block:: + + { + "from": tabActor, + "type": "tabNavigated", + "state": "stop", + "url": newURL, + "title": newTitle, + } + + +``getCachedMessages(types, onResponse)`` +---------------------------------------- + +The ``webConsoleClient.getCachedMessages(types, onResponse)`` method sends the following packet to the server: + +.. code-block:: json + + { + "to": "conn0.console9", + "type": "getCachedMessages", + "messageTypes": [ + "PageError", + "ConsoleAPI" + ] + } + + +The ``getCachedMessages`` packet allows one to retrieve the cached messages from before the Web Console was open. You can only get cached messages for page errors and console API calls. The reply looks like this: + +.. code-block:: + + { + "messages": [ ... ], + "from": "conn0.console9" + } + +Each message in the array is of the same type as when we send typical page errors and console API calls. These will be explained in the following sections of this document. + +Private browsing +---------------- + +The Browser Console can be used while the user has private windows open. Each page error, console API message and network request is annotated with a ``private`` flag. Private messages are cleared whenever the last private window is closed. The console actor provides the ``lastPrivateContextExited`` notification: + +.. code-block:: json + + { + "from": "conn0.console19", + "type": "lastPrivateContextExited" + } + + +This notification is sent only when your client is attached to the global console actor, it does not make sense for tab console actors. + +.. note:: + This notification has been introduced in Firefox 24. + +Page errors +*********** + +Page errors come from the ``nsIConsoleService``. Each allowed page error is an ``nsIScriptError`` object. + +The ``pageError`` packet is: + +.. code-block:: json + + { + "from": "conn0.console9", + "type": "pageError", + "pageError": { + "errorMessage": "ReferenceError: foo is not defined", + "sourceName": "http://localhost/~mihai/mozilla/test.js", + "lineText": "", + "lineNumber": 6, + "columnNumber": 0, + "category": "content javascript", + "timeStamp": 1347294508210, + "error": false, + "warning": false, + "exception": true, + "strict": false, + "private": false, + } + } + + +The packet is similar to ``nsIScriptError`` - for simplicity. We only removed several unneeded properties and changed how flags work. + +The ``private`` flag tells if the error comes from a private window/tab (added in Firefox 24). + +Starting with Firefox 24 the ``errorMessage`` and ``lineText`` properties can be long string actor grips if the string is very long. + + +Console API messages +******************** + +The `window.console API <https://developer.mozilla.org/en-US/docs/Web/API/console>`_ calls send internal messages throughout Gecko which allow us to do whatever we want for each call. The Web Console actor sends these messages to the remote debugging client. + +We use the ``ObjectActor`` from `dbg-script-actors.js <https://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/debugger/server/dbg-script-actors.js>`_ without a ``ThreadActor``, to avoid slowing down the page scripts - the debugger deoptimizes JavaScript execution in the target page. The `lifetime of object actors <https://wiki.mozilla.org/Remote_Debugging_Protocol#Grip_Lifetimes>`_ in the Web Console is different than the lifetime of these objects in the debugger - which is usually per pause or per thread. The Web Console manages the lifetime of ``ObjectActors`` manually. + + +.. warning:: + Prior to Firefox 23 we used a different actor (``WebConsoleObjectActor``) for working with JavaScript objects through the protocol. In `bug 783499 <https://bugzilla.mozilla.org/show_bug.cgi?id=783499>`_ we did a number of changes that allowed us to reuse the ``ObjectActor`` from the debugger. + + +Console API messages come through the ``nsIObserverService`` - the console object implementation lives in `dom/base/ConsoleAPI.js <http://mxr.mozilla.org/mozilla-central/source/dom/base/ConsoleAPI.js>`_. + +For each console message we receive in the server, we send the following ``consoleAPICall`` packet to the client: + +.. code-block:: json + + { + "from": "conn0.console9", + "type": "consoleAPICall", + "message": { + "level": "error", + "filename": "http://localhost/~mihai/mozilla/test.html", + "lineNumber": 149, + "functionName": "", + "timeStamp": 1347302713771, + "private": false, + "arguments": [ + "error omg aloha ", + { + "type": "object", + "className": "HTMLBodyElement", + "actor": "conn0.consoleObj20" + }, + " 960 739 3.141592653589793 %a", + "zuzu", + { "type": "null" }, + { "type": "undefined" } + ] + } + } + +Similar to how we send the page errors, here we send the actual console event received from the ``nsIObserverService``. We change the ``arguments`` array - we create ``ObjectActor`` instances for each object passed as an argument - and, lastly, we remove some unneeded properties (like window IDs). In the case of long strings we use the ``LongStringActor``. The Web Console can then inspect the arguments. + +The ``private`` flag tells if the console API call comes from a private window/tab (added in Firefox 24). + +We have small variations for the object, depending on the console API call method - just like there are small differences in the console event object received from the observer service. To see these differences please look in the Console API implementation: `dom/base/ConsoleAPI.js <http://mxr.mozilla.org/mozilla-central/source/dom/base/ConsoleAPI.js>`_. + + +JavaScript evaluation +--------------------- + +The ``evaluateJS`` request and response packets +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Web Console client provides the ``evaluateJS(requestId, string, onResponse)`` method which sends the following packet: + +.. code-block:: json + + { + "to": "conn0.console9", + "type": "evaluateJS", + "text": "document", + "bindObjectActor": null, + "frameActor": null, + "url": null, + "selectedNodeActor": null, + } + + +The ``bindObjectActor`` property is an optional ``ObjectActor`` ID that points to a ``Debugger.Object``. This option allows you to bind ``_self`` to the ``Debugger.Object`` of the given object actor, during string evaluation. See ``evalInGlobalWithBindings()`` for information about bindings. + +.. note:: + The variable view needs to update objects and it does so by binding ``_self`` to the ``Debugger.Object`` of the ``ObjectActor`` that is being viewed. As such, variable view sends strings like these for evaluation: + +.. code-block:: javascript + + _self["prop"] = value; + +The ``frameActor`` property is an optional ``FrameActor`` ID. The FA holds a reference to a ``Debugger.Frame``. This option allows you to evaluate the string in the frame of the given FA. + +The ``url`` property is an optional URL to evaluate the script as (new in Firefox 25). The default source URL for evaluation is "debugger eval code". + +The ``selectedNodeActor`` property is an optional ``NodeActor`` ID, which is used to indicate which node is currently selected in the Inspector, if any. This ``NodeActor`` can then be referred to by the ``$0`` JSTerm helper. + +The response packet: + +.. code-block:: json + + { + "from": "conn0.console9", + "input": "document", + "result": { + "type": "object", + "className": "HTMLDocument", + "actor": "conn0.consoleObj20" + "extensible": true, + "frozen": false, + "sealed": false + }, + "timestamp": 1347306273605, + "exception": null, + "exceptionMessage": null, + "helperResult": null + } + + +- ``exception`` holds the JSON-ification of the exception thrown during evaluation. +- ``exceptionMessage`` holds the ``exception.toString()`` result. +- ``result`` has the result ``ObjectActor`` instance. +- ``helperResult`` is anything that might come from a JSTerm helper result, JSON stuff (not content objects!). + + +.. warning:: + In Firefox 23: we renamed the ``error`` and ``errorMessage`` properties to ``exception`` and ``exceptionMessage`` respectively, to avoid conflict with the default properties used when protocol errors occur. + + +Autocomplete and more +--------------------- + +The ``autocomplete`` request packet: + +.. code-block:: json + + { + "to": "conn0.console9", + "type": "autocomplete", + "text": "d", + "cursor": 1 + } + + +The response packet: + +.. code-block:: json + + { + "from": "conn0.console9", + "matches": [ + "decodeURI", + "decodeURIComponent", + "defaultStatus", + "devicePixelRatio", + "disableExternalCapture", + "dispatchEvent", + "doMyXHR", + "document", + "dump" + ], + "matchProp": "d" + } + + +There's also the ``clearMessagesCache`` request packet that has no response. This clears the console API calls cache and should clear the page errors cache - see `bug 717611 <https://bugzilla.mozilla.org/show_bug.cgi?id=717611>`_. +An alternate version was added in Firefox 104, ``clearMessagesCacheAsync``, which does exactly the same thing but resolves when the cache was actually cleared. + + +Network logging +*************** + +The ``networkEvent`` packet +--------------------------- + +Whenever a new network request starts being logged the ``networkEvent`` packet is sent: + +.. code-block:: json + + { + "from": "conn0.console10", + "type": "networkEvent", + "eventActor": { + "actor": "conn0.netEvent14", + "startedDateTime": "2012-09-17T19:50:03.699Z", + "url": "http://localhost/~mihai/mozilla/test2.css", + "method": "GET" + "isXHR": false, + "private": false + } + } + + +This packet is used to inform the Web Console of a new network event. For each request a new ``NetworkEventActor`` instance is created. The ``isXHR`` flag indicates if the request was initiated via an `XMLHttpRequest <https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest>`_ instance, that is: the ``nsIHttpChannel``'s notification is of an ``nsIXMLHttpRequest`` interface. + +The ``private`` flag tells if the network request comes from a private window/tab (added in Firefox 24). + + +The ``NetworkEventActor`` +------------------------- + +The new network event actor stores further request and response information. + +The ``networkEventUpdate`` packet +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Web Console UI needs to be kept up-to-date when changes happen, when new stuff is added. The new ``networkEventUpdate`` packet is sent for this purpose. Examples: + +.. code-block:: + + { + "from": "conn0.netEvent14", + "type": "networkEventUpdate", + "updateType": "requestHeaders", + "headers": 10, + "headersSize": 425 + }, + { + "from": "conn0.netEvent14", + "type": "networkEventUpdate", + "updateType": "requestCookies", + "cookies": 0 + }, + { + "from": "conn0.netEvent14", + "type": "networkEventUpdate", + "updateType": "requestPostData", + "dataSize": 1024, + "discardRequestBody": false + }, + { + "from": "conn0.netEvent14", + "type": "networkEventUpdate", + "updateType": "responseStart", + "response": { + "httpVersion": "HTTP/1.1", + "status": "304", + "statusText": "Not Modified", + "headersSize": 194, + "discardResponseBody": true + } + }, + { + "from": "conn0.netEvent14", + "type": "networkEventUpdate", + "updateType": "eventTimings", + "totalTime": 1 + }, + { + "from": "conn0.netEvent14", + "type": "networkEventUpdate", + "updateType": "responseHeaders", + "headers": 6, + "headersSize": 194 + }, + { + "from": "conn0.netEvent14", + "type": "networkEventUpdate", + "updateType": "responseCookies", + "cookies": 0 + }, + { + "from": "conn0.netEvent14", + "type": "networkEventUpdate", + "updateType": "responseContent", + "mimeType": "text/css", + "contentSize": 0, + "discardResponseBody": true + } + + +Actual headers, cookies, and bodies are not sent. + + +The ``getRequestHeaders`` and other packets +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To get more details about a network event you can use the following packet requests (and replies). + +The ``getRequestHeaders`` packet: + +.. code-block:: + + { + "to": "conn0.netEvent15", + "type": "getRequestHeaders" + } + { + "from": "conn0.netEvent15", + "headers": [ + { + "name": "Host", + "value": "localhost" + }, ... + ], + "headersSize": 350 + } + + +The ``getRequestCookies`` packet: + +.. code-block:: json + + { + "to": "conn0.netEvent15", + "type": "getRequestCookies" + } + { + "from": "conn0.netEvent15", + "cookies": [] + } + + +The ``getResponseHeaders`` packet: + +.. code-block:: + + { + "to": "conn0.netEvent15", + "type": "getResponseHeaders" + } + { + "from": "conn0.netEvent15", + "headers": [ + { + "name": "Date", + "value": "Mon, 17 Sep 2012 20:05:27 GMT" + }, ... + ], + "headersSize": 320 + } + + +The ``getResponseCookies`` packet: + +.. code-block:: json + + { + "to": "conn0.netEvent15", + "type": "getResponseCookies" + } + { + "from": "conn0.netEvent15", + "cookies": [] + } + + +.. note:: + Starting with Firefox 19: for all of the header and cookie values in the above packets we use `LongStringActor grips <https://wiki.mozilla.org/Remote_Debugging_Protocol#Objects>`_ when the value is very long. This helps us avoid using too much of the network bandwidth. + + +The ``getRequestPostData`` packet: + +.. code-block:: + + { + "to": "conn0.netEvent15", + "type": "getRequestPostData" + } + { + "from": "conn0.netEvent15", + "postData": { text: "foobar" }, + "postDataDiscarded": false + } + +The ``getResponseContent`` packet: + +.. code-block:: json + + { + "to": "conn0.netEvent15", + "type": "getResponseContent" + } + { + "from": "conn0.netEvent15", + "content": { + "mimeType": "text/css", + "text": "\n@import \"test.css\";\n\n.foobar { color: green }\n\n" + }, + "contentDiscarded": false + } + + +The request and response content text value is most commonly sent using a ``LongStringActor`` grip. For very short request/response bodies we send the raw text. + +.. note:: + Starting with Firefox 19: for non-text response types we send the content in base64 encoding (again, most likely a ``LongStringActor`` grip). To tell the difference just check if ``response.content.encoding == "base64"``. + + +The ``getEventTimings`` packet: + +.. code-block:: json + + { + "to": "conn0.netEvent15", + "type": "getEventTimings" + } + { + "from": "conn0.netEvent15", + "timings": { + "blocked": 0, + "dns": 0, + "connect": 0, + "send": 0, + "wait": 16, + "receive": 0 + }, + "totalTime": 16 + } + + +The ``fileActivity`` packet +--------------------------- + +When a file load is observed the following ``fileActivity`` packet is sent to the client: + +.. code-block:: json + + { + "from": "conn0.console9", + "type": "fileActivity", + "uri": "file:///home/mihai/public_html/mozilla/test2.css" + } + + +History +******* + +Protocol changes by Firefox version: + +- Firefox 18: initial version. +- Firefox 19: `bug <https://bugzilla.mozilla.org/show_bug.cgi?id=787981>`_ - added ``LongStringActor`` usage in several places. +- Firefox 20: `bug <https://bugzilla.mozilla.org/show_bug.cgi?id=792062>`_ - removed ``locationChanged`` packet and updated the ``tabNavigated`` packet for tab actors. +- Firefox 23: `bug <https://bugzilla.mozilla.org/show_bug.cgi?id=783499>`_ - removed the ``WebConsoleObjectActor``. Now the Web Console uses the JavaScript debugger API and the ``ObjectActor``. +- Firefox 23: added the ``bindObjectActor`` and ``frameActor`` options to the ``evaluateJS`` request packet. +- Firefox 24: new ``private`` flags for the console actor notifications, `bug <https://bugzilla.mozilla.org/show_bug.cgi?id=874061>`_. Also added the ``lastPrivateContextExited`` notification for the global console actor. +- Firefox 24: new ``isXHR`` flag for the ``networkEvent`` notification, `bug <https://bugzilla.mozilla.org/show_bug.cgi?id=859046>`_. +- Firefox 24: removed the ``message`` property from the ``pageError`` packet notification, `bug <https://bugzilla.mozilla.org/show_bug.cgi?id=877773>`_. The ``lineText`` and ``errorMessage`` properties can be long string actors now. +- Firefox 25: added the ``url`` option to the ``evaluateJS`` request packet. + + +Conclusions +*********** + +As of this writing, this document is a dense summary of the work we did in `bug 768096 <https://bugzilla.mozilla.org/show_bug.cgi?id=768096>`_ and subsequent changes. We try to keep this document up-to-date. We hope this is helpful for you. + +If you make changes to the Web Console server please update this document. Thank you! diff --git a/devtools/docs/user/web_console/rich_output/commandline-highlightnode.png b/devtools/docs/user/web_console/rich_output/commandline-highlightnode.png Binary files differnew file mode 100644 index 0000000000..578f2e1f95 --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/commandline-highlightnode.png diff --git a/devtools/docs/user/web_console/rich_output/console_export.png b/devtools/docs/user/web_console/rich_output/console_export.png Binary files differnew file mode 100644 index 0000000000..f973acbd4c --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/console_export.png diff --git a/devtools/docs/user/web_console/rich_output/console_logobject.png b/devtools/docs/user/web_console/rich_output/console_logobject.png Binary files differnew file mode 100644 index 0000000000..3435d8cfe8 --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/console_logobject.png diff --git a/devtools/docs/user/web_console/rich_output/index.rst b/devtools/docs/user/web_console/rich_output/index.rst new file mode 100644 index 0000000000..3ebed8428e --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/index.rst @@ -0,0 +1,129 @@ +=========== +Rich output +=========== + +When the Web console prints objects, it includes a richer set of information than just the object's name. In particular, it: + + +- :ref:`provides extra information for certain types <web_console_rich_output_type_specific>` +- :ref:`enables detailed examination of the object's properties <web_console_rich_output_examining_object_properties>` +- :ref:`provides richer information for DOM elements, and enables you to select them in the Inspector <web_console_rich_output_highlighting_and_inspecting_dom_nodes>` + + +.. _web_console_rich_output_type_specific: + +Type-specific rich output +************************* + +The Web Console provides rich output for many object types, including the following: + + +.. list-table:: + :widths: 20 80 + :header-rows: 0 + + * - ``Object`` + - .. image:: web-console-object.png + + * - ``Date`` + - .. image:: web-console-date.png + + * - ``Promise`` + - .. image:: web-console-promise.png + + * - ``RegExp`` + - .. image:: web-console-regexp.png + + * - ``Window`` + - .. image:: web-console-window.png + + * - ``Document`` + - .. image:: web-console-document.png + + * - ``Element`` + - .. image:: web-console-element.png + + * - ``Event`` + - .. image:: webconsole-events.png + + +.. _web_console_rich_output_examining_object_properties: + +Examining object properties +*************************** + +When an object is logged to the console it has a right-pointing triangle next to it, indicating that it can be expanded. Click on the triangle, and the object will be expanded to show its contents: + +.. image:: console_logobject.png + :class: border + + +Starting with Firefox 67 (available now in Firefox Developer) you can use the arrow keys on your keyboard to navigate through objects displayed in the console. The right-arrow key opens the details of an object and the left-arrow key closes open objects. + + +.. _web_console_rich_output_examining_request_details: + +Examining request details +************************* + + +Similar to examining object details, you can see the details about a network request directly in the console. Click on the arrow next to the request and a details panel will open that is equivalent to the Headers panel in the Network Monitor tool. + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/Cj3Pjq6jk9s" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + + +Export output to the clipboard +****************************** + +Once you have output in the console window, you can save it to the clipboard by right-clicking on the output and selecting **Export visible messages to clipboard**: + +.. image:: console_export.png + :class: center + + +This will copy all of the output to the clipboard. Then you can paste it into a document. The output will look something like this: + +.. code-block:: + + console.log(todoList) + Array(4) [ {…}, {…}, {…}, {…} ] + debugger eval code:1:9 + undefined + +If you expand objects, such as arrays, you get slightly different content. For example, by expanding the array in the above list, I get the following: + +.. code-block:: + + console.log(todoList) + (4) […] + + 0: Object { status: "Done", description: "Morning Pages", dateCreated: 1552404478137 } + + 1: Object { status: "In Progress", description: "Refactor styles", dateCreated: 1552404493169 } + + 2: Object { status: "To Do", description: "Create feedback form", dateCreated: 1552404512630 } + + 3: Object { status: "To Do", description: "Normalize table", dateCreated: 1552404533790 } + + length: 4 + + <prototype>: Array [] + debugger eval code:1:9 + undefined + + +.. _web_console_rich_output_highlighting_and_inspecting_dom_nodes: + +Highlighting and inspecting DOM nodes +************************************* + +If you hover the mouse over any DOM element in the console output, it's highlighted on the page: + +.. image:: commandline-highlightnode.png + :class: center + +In the screenshot above you'll also see a blue "target" icon next to the node in the console output: click it to switch to the :doc:`Inspector <../../page_inspector/index>` with that node selected. diff --git a/devtools/docs/user/web_console/rich_output/web-console-array.png b/devtools/docs/user/web_console/rich_output/web-console-array.png Binary files differnew file mode 100644 index 0000000000..3593201ae2 --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/web-console-array.png diff --git a/devtools/docs/user/web_console/rich_output/web-console-date.png b/devtools/docs/user/web_console/rich_output/web-console-date.png Binary files differnew file mode 100644 index 0000000000..f88b8e6a33 --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/web-console-date.png diff --git a/devtools/docs/user/web_console/rich_output/web-console-document.png b/devtools/docs/user/web_console/rich_output/web-console-document.png Binary files differnew file mode 100644 index 0000000000..b960ff3758 --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/web-console-document.png diff --git a/devtools/docs/user/web_console/rich_output/web-console-element.png b/devtools/docs/user/web_console/rich_output/web-console-element.png Binary files differnew file mode 100644 index 0000000000..5df11b1b70 --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/web-console-element.png diff --git a/devtools/docs/user/web_console/rich_output/web-console-object.png b/devtools/docs/user/web_console/rich_output/web-console-object.png Binary files differnew file mode 100644 index 0000000000..55a145597b --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/web-console-object.png diff --git a/devtools/docs/user/web_console/rich_output/web-console-promise.png b/devtools/docs/user/web_console/rich_output/web-console-promise.png Binary files differnew file mode 100644 index 0000000000..d4d1f33c7e --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/web-console-promise.png diff --git a/devtools/docs/user/web_console/rich_output/web-console-regexp.png b/devtools/docs/user/web_console/rich_output/web-console-regexp.png Binary files differnew file mode 100644 index 0000000000..573132cee2 --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/web-console-regexp.png diff --git a/devtools/docs/user/web_console/rich_output/web-console-window.png b/devtools/docs/user/web_console/rich_output/web-console-window.png Binary files differnew file mode 100644 index 0000000000..d88e1e925f --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/web-console-window.png diff --git a/devtools/docs/user/web_console/rich_output/webconsole-events.png b/devtools/docs/user/web_console/rich_output/webconsole-events.png Binary files differnew file mode 100644 index 0000000000..797c709c3f --- /dev/null +++ b/devtools/docs/user/web_console/rich_output/webconsole-events.png diff --git a/devtools/docs/user/web_console/split_console/index.rst b/devtools/docs/user/web_console/split_console/index.rst new file mode 100644 index 0000000000..13bee8efc7 --- /dev/null +++ b/devtools/docs/user/web_console/split_console/index.rst @@ -0,0 +1,26 @@ +============= +Split console +============= + +You can use the console alongside other tools. While you're in another tool in the Toolbox, just press :kbd:`Esc` or select the "Show split console" command in the :ref:`Toolbar <tools-toolbox-toolbar>` menu. The toolbox will now appear split, with the original tool above and the web console underneath. + +You can close the split console by pressing :kbd:`Esc` again, or by selecting the "Hide split console" menu command. + +.. image:: split-console.png + :class: border + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/G2hyxhPHyXo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +As usual, ``$0`` works as a shorthand for the element currently selected in the Inspector: + +.. image:: split-console-debugger.png + :class: center + +When you use the split console with the debugger, the console's scope is the currently executing stack frame. So if you hit a breakpoint in a function, the scope will be the function's scope. You'll get autocomplete for objects defined in the function, and can easily modify them on the fly: + +.. image:: split-console-show-debug.png + :class: center diff --git a/devtools/docs/user/web_console/split_console/split-console-debugger.png b/devtools/docs/user/web_console/split_console/split-console-debugger.png Binary files differnew file mode 100644 index 0000000000..91991f487e --- /dev/null +++ b/devtools/docs/user/web_console/split_console/split-console-debugger.png diff --git a/devtools/docs/user/web_console/split_console/split-console-show-debug.png b/devtools/docs/user/web_console/split_console/split-console-show-debug.png Binary files differnew file mode 100644 index 0000000000..3a24d6d9a1 --- /dev/null +++ b/devtools/docs/user/web_console/split_console/split-console-show-debug.png diff --git a/devtools/docs/user/web_console/split_console/split-console.png b/devtools/docs/user/web_console/split_console/split-console.png Binary files differnew file mode 100644 index 0000000000..a848139a20 --- /dev/null +++ b/devtools/docs/user/web_console/split_console/split-console.png diff --git a/devtools/docs/user/web_console/the_command_line_interpreter/arraylist_autocomplete.png b/devtools/docs/user/web_console/the_command_line_interpreter/arraylist_autocomplete.png Binary files differnew file mode 100644 index 0000000000..dc104eb679 --- /dev/null +++ b/devtools/docs/user/web_console/the_command_line_interpreter/arraylist_autocomplete.png diff --git a/devtools/docs/user/web_console/the_command_line_interpreter/commandline-accessbuiltin.png b/devtools/docs/user/web_console/the_command_line_interpreter/commandline-accessbuiltin.png Binary files differnew file mode 100644 index 0000000000..9dfc3f96e4 --- /dev/null +++ b/devtools/docs/user/web_console/the_command_line_interpreter/commandline-accessbuiltin.png diff --git a/devtools/docs/user/web_console/the_command_line_interpreter/commandline-accesspageadded.png b/devtools/docs/user/web_console/the_command_line_interpreter/commandline-accesspageadded.png Binary files differnew file mode 100644 index 0000000000..19b4167d3d --- /dev/null +++ b/devtools/docs/user/web_console/the_command_line_interpreter/commandline-accesspageadded.png diff --git a/devtools/docs/user/web_console/the_command_line_interpreter/console_autocomplete_cropped.png b/devtools/docs/user/web_console/the_command_line_interpreter/console_autocomplete_cropped.png Binary files differnew file mode 100644 index 0000000000..4b1da9a82e --- /dev/null +++ b/devtools/docs/user/web_console/the_command_line_interpreter/console_autocomplete_cropped.png diff --git a/devtools/docs/user/web_console/the_command_line_interpreter/console_syntaxhighlighting.png b/devtools/docs/user/web_console/the_command_line_interpreter/console_syntaxhighlighting.png Binary files differnew file mode 100644 index 0000000000..4483a1d038 --- /dev/null +++ b/devtools/docs/user/web_console/the_command_line_interpreter/console_syntaxhighlighting.png diff --git a/devtools/docs/user/web_console/the_command_line_interpreter/index.rst b/devtools/docs/user/web_console/the_command_line_interpreter/index.rst new file mode 100644 index 0000000000..bb3ec8ce83 --- /dev/null +++ b/devtools/docs/user/web_console/the_command_line_interpreter/index.rst @@ -0,0 +1,170 @@ +================================ +The JavaScript input interpreter +================================ + +You can interpret JavaScript expressions in real time using the interpreter provided by the Web Console. It has two modes: single-line entry and multi-line entry. + +Single-line mode +**************** + +For single-line entry, you can type JavaScript expressions in the field at the bottom of the console log, at the **>>** prompt. + +.. image:: web_console_single.png + :alt: The Web Console, showing single-line mode + :class: center + + +To enter expressions in single-line mode, type at the prompt and press :kbd:`Enter`. To enter multi-line expressions, press :kbd:`Shift` + :kbd:`Enter` after typing each line, then :kbd:`Enter` to run all the entered lines. + +The expression you type is echoed under the input prompt, followed by the result. + +If your input does not appear to be complete when you press :kbd:`Enter`, then the Console treats this as :kbd:`Shift` + :kbd:`Enter` , enabling you to finish your input. + +For example, if you type: + +.. code-block:: JavaScript + + function foo() { + + +and then :kbd:`Enter`, the Console does not immediately execute the input, but behaves as if you had pressed :kbd:`Shift` + :kbd:`Enter` , so you can finish entering the function definition. + + +.. _command_line_interpreter_multi_line_mode: + +Multi-line mode +*************** + +For multi-line entry, click the "split panel" icon at the right hand side of the single-line entry field, or press :kbd:`Ctrl` + :kbd:`B` (Windows/Linux) or :kbd:`Cmd` + :kbd:`B` (macOS). The multi-line editing pane opens on the left side the of Web Console. + +.. image:: web_console_multi.png + :alt: Web Console in multi-line mode + :class: center + +You can enter multiple lines of JavaScript by default in this mode, pressing :kbd:`Enter` after each one. To execute the snippet that is currently in the editing pane, click the **Run** button or press :kbd:`Ctrl` + :kbd:`Enter` (or :kbd:`Cmd` + :kbd:`Return` on MacOS). The snippet is echoed under the input prompt (in the right-side pane), followed by the result. + +Starting in Firefox 76, if the code snippet is more than five lines long, only the first five lines are echoed in the console, preceded by a disclosure triangle (or "twistie"), and followed by an ellipsis (…). Click anywhere in the area containing the echoed code to show the whole snippet; click again in that area to collapse it. + +You can open files when in multi-line mode, and save the current contents of the editing pane to a file. + + +- To open a file, press :kbd:`Ctrl` + :kbd:`O` (:kbd:`Cmd` + :kbd:`O` on MacOS). A file dialog box opens so you can select the file to open. +- To save the contents of the editing pane, press :kbd:`Ctrl` + :kbd:`S` (:kbd:`Cmd` + :kbd:`S` on MacOS). A file dialog box opens so you can specify the location to save to. + + +To switch back to single-line mode, click the **X** icon at the top of the multi-line editing pane, or press :kbd:`Ctrl` + :kbd:`B` (Windows/Linux) or :kbd:`Cmd` + :kbd:`B` (MacOS). + + +Accessing variables +******************* + +You can access variables defined in the page, both built-in variables like ``window`` and variables added by JavaScript libraries like *jQuery*: + +.. image:: commandline-accessbuiltin.png + :class: center + +.. image:: commandline-accesspageadded.png + :class: center + + +.. _command_line_interpreter_autocomplete: + +Autocomplete +************ + +The editor has autocomplete: enter the first few letters and a popup appears with possible completions: + +.. image:: console_autocomplete_cropped.png + :class: center + + +Press :kbd:`Enter`, :kbd:`Tab`, or the right arrow key to accept the suggestion, use the up/down arrows to move to a different suggestion, or just keep typing if you don't like any of the suggestions. + +Console autocomplete suggestions are case-insensitive. + +The console suggests completions from the scope of the currently executing stack frame. This means that if you've hit a breakpoint in a function you get autocomplete for objects local to the function. + +You get autocomplete suggestions for array elements, as well: + +.. image:: arraylist_autocomplete.png + :class: border + + +You can enable or disable autocompletion via the Settings ("gear") menu in the Web Console toolbar. The menuitem **Enable Autocompletion** has a checkmark next to it when the feature is enabled, which is missing when it is disabled. Select the menuitem to change the state. + + +Instant evaluation +****************** + +.. note:: + This feature is available in Firefox Nightly, in versions labeled 74 and later. + +When the "instant evaluation" feature is enabled, the interpreter displays results of expressions as you're typing them in single-line mode. Note that the result might be an error message. Expressions that have side effects are not evaluated. + +You can enable or disable instant evaluation via the Settings ("gear") menu in the Web Console toolbar. The menuitem **Instant Evaluation** has a checkmark next to it when the feature is enabled, which is missing when it is disabled. Select the menuitem to change the state. + + +Execution context +***************** + +Code that you have executed becomes part of the execution context, regardless of what editing mode you were in when you executed it. For example, if you type a function definition in the multi-line editor, and click **Run**, you can switch to single-line mode and still use your function. + + +Syntax highlighting +******************* + +.. image:: console_syntaxhighlighting.png + :alt: Console output showing syntax highlighting + :class: border + + +The text you enter has syntax highlighting as soon as you have typed enough for the highlighter to parse it and infer the meanings of the "words". + +The output is highlighted as well where appropriate. + +.. note:: + Syntax highlighting is not visible in your browser if Accessibility features have been enabled. + + +.. _command_line_interpreter_execution_history: + +Execution history +***************** + +The interpreter remembers expressions you've typed. To move back and forward through your history: + + +- In single-line mode, use the up and down arrows. +- In multi-line mode, use the **⋀** and **⋁** icons in the editing panel's toolbar. + + +The expression history is persisted across sessions. To clear the history, use the ``clearHistory()`` :ref:`helper function <command_line_interpreter_helper_commands>`. + +You can initiate a reverse search through the expression history, much like you can in bash on Linux and Mac or PowerShell on Windows. On Windows and Linux press :kbd:`F9`. On Mac press :kbd:`Ctrl` + :kbd:`R` (**note:** not :kbd:`Cmd` + :kbd:`R`!) to initiate the reverse search. + +.. image:: reverse_search.png + :class: border + + +Enter the text you want to search for in the input box at the bottom of the Console. Start typing part of the expression you are looking for and the first match is displayed in the console. Repeatedly typing :kbd:`F9` on Windows and Linux ( :kbd:`Ctrl` + :kbd:`R` on Mac) cycles backwards through the matches. + +.. image:: reverse_search_example.png + :class: border + +Once you have initiated the reverse search, you can use :kbd:`Shift` + :kbd:`F9` on Windows or Linux ( :kbd:`Ctrl` + :kbd:`S` on Mac) to search forward in the list of matches. You can also use the **⋀** and **⋁** icons in the expression search bar. + +When you find the expression you want, press :kbd:`Enter` (:kbd:`Return`) to execute the statement. + + +Working with iframes +******************** + +:doc:`Working with iframes <../../working_with_iframes/index>` explains how to direct all debugging tools to target a particular iframe, including the command line interpreter. + + +.. _command_line_interpreter_helper_commands: + +Helper commands +*************** + +The JavaScript command line provided by the Web Console offers a few built-in helper functions that make certain tasks easier. For more information see :doc:`Web Console Helpers <../helpers/index>`. diff --git a/devtools/docs/user/web_console/the_command_line_interpreter/reverse_search.png b/devtools/docs/user/web_console/the_command_line_interpreter/reverse_search.png Binary files differnew file mode 100644 index 0000000000..b9dcd3f0b7 --- /dev/null +++ b/devtools/docs/user/web_console/the_command_line_interpreter/reverse_search.png diff --git a/devtools/docs/user/web_console/the_command_line_interpreter/reverse_search_example.png b/devtools/docs/user/web_console/the_command_line_interpreter/reverse_search_example.png Binary files differnew file mode 100644 index 0000000000..6899c3bb93 --- /dev/null +++ b/devtools/docs/user/web_console/the_command_line_interpreter/reverse_search_example.png diff --git a/devtools/docs/user/web_console/the_command_line_interpreter/web-console-iframe-document.png b/devtools/docs/user/web_console/the_command_line_interpreter/web-console-iframe-document.png Binary files differnew file mode 100644 index 0000000000..db22cfa616 --- /dev/null +++ b/devtools/docs/user/web_console/the_command_line_interpreter/web-console-iframe-document.png diff --git a/devtools/docs/user/web_console/the_command_line_interpreter/web-console-iframe-function.png b/devtools/docs/user/web_console/the_command_line_interpreter/web-console-iframe-function.png Binary files differnew file mode 100644 index 0000000000..88b5b47e1d --- /dev/null +++ b/devtools/docs/user/web_console/the_command_line_interpreter/web-console-iframe-function.png diff --git a/devtools/docs/user/web_console/the_command_line_interpreter/web_console_multi.png b/devtools/docs/user/web_console/the_command_line_interpreter/web_console_multi.png Binary files differnew file mode 100644 index 0000000000..b1be269584 --- /dev/null +++ b/devtools/docs/user/web_console/the_command_line_interpreter/web_console_multi.png diff --git a/devtools/docs/user/web_console/the_command_line_interpreter/web_console_single.png b/devtools/docs/user/web_console/the_command_line_interpreter/web_console_single.png Binary files differnew file mode 100644 index 0000000000..62827ea7a0 --- /dev/null +++ b/devtools/docs/user/web_console/the_command_line_interpreter/web_console_single.png diff --git a/devtools/docs/user/web_console/ui_tour/index.rst b/devtools/docs/user/web_console/ui_tour/index.rst new file mode 100644 index 0000000000..50495744b5 --- /dev/null +++ b/devtools/docs/user/web_console/ui_tour/index.rst @@ -0,0 +1,50 @@ +=================== +Web Console UI Tour +=================== + +The Web Console's interface is split into three horizontal sections, detailed in the sections below. + +.. image:: web_console.png + :alt: Web console" alt="Screenshot of FF web console + :class: center + + +.. _web_console_ui_tour_toolbar: + +Toolbar +******* + +The toolbar across the top contains a number of features: + + +- **Garbage can:** Click this icon to clear the contents of the console. +- **Funnel (filter):** Enter text to filter the messages that are displayed in the console message pane. :ref:`Plain-text <web_console_ui_tour_filtering_by_text>` and :ref:`regular expression <web_console_ui_tour_filtering_by_regular_expressions>` filtering are supported. +- :ref:`Filter categories <web_console_ui_tour_filtering_by_category>`: Toggle a filter category (such as Errors, Warnings, CSS, or XHR) to display messages of that type in the message page (the UI shows the number of hidden message for unselected categories). +- **Settings ("gear" menu):** Select the gear icon to access the settings menu (New in Firefox 71), where you can toggle the following features on and off: + + - **Persist Logs**: When enabled, the console doesn't clear on page reload, or new page load. + - **Show Timestamps**: When enabled, timestamps are shown on the left-hand side of each message row to say when the messages were logged. + - **Group Similar Messages**: When enabled, similar types of messages are grouped together, with an indicator of the number of occurrences. + - **Enable Autocompletion**: When enabled, the JavaScript interpreter attempts to autocomplete while you type. + - **Instant Evaluation**: When enabled, the interpreter displays the evaluated results of an expression, when possible, before you press :kbd:`Enter` to submit it. + + + +Message display pane +******************** + +This is where the messages appear, both those generated by the code in the page, and those generated by the commands entered on the command line. + +See :doc:`Console messages <../the_command_line_interpreter/index>` for a lot more detail on what the messages can contain. + +.. note:: + + You can clear the contents of the console by entering the keyboard command :kbd:`Ctrl` + :kbd:`Shift` + :kbd:`L` (Windows, macOS, and Linux) or :kbd:`Cmd` + :kbd:`K` on macOS. + + +Command line +************ + +The :doc:`command line <../the_command_line_interpreter/index>` starts with double angle brackets (>>). Use it to enter JavaScript expressions. + +In Firefox 71 onwards, there is a new "split pane" icon on the right hand side of the command line — clicking this will open the new console :ref:`multi-line mode <command_line_interpreter_multi_line_mode>`. diff --git a/devtools/docs/user/web_console/ui_tour/web_console.png b/devtools/docs/user/web_console/ui_tour/web_console.png Binary files differnew file mode 100644 index 0000000000..e8f5e27203 --- /dev/null +++ b/devtools/docs/user/web_console/ui_tour/web_console.png diff --git a/devtools/docs/user/working_with_iframes/developer_tools_select_iframe.png b/devtools/docs/user/working_with_iframes/developer_tools_select_iframe.png Binary files differnew file mode 100644 index 0000000000..17d2ec698b --- /dev/null +++ b/devtools/docs/user/working_with_iframes/developer_tools_select_iframe.png diff --git a/devtools/docs/user/working_with_iframes/developer_tools_settings_iframes.png b/devtools/docs/user/working_with_iframes/developer_tools_settings_iframes.png Binary files differnew file mode 100644 index 0000000000..04585375fe --- /dev/null +++ b/devtools/docs/user/working_with_iframes/developer_tools_settings_iframes.png diff --git a/devtools/docs/user/working_with_iframes/frame-selection-button.png b/devtools/docs/user/working_with_iframes/frame-selection-button.png Binary files differnew file mode 100644 index 0000000000..0820cebf1c --- /dev/null +++ b/devtools/docs/user/working_with_iframes/frame-selection-button.png diff --git a/devtools/docs/user/working_with_iframes/frame-selection-popup.png b/devtools/docs/user/working_with_iframes/frame-selection-popup.png Binary files differnew file mode 100644 index 0000000000..ae96760ae2 --- /dev/null +++ b/devtools/docs/user/working_with_iframes/frame-selection-popup.png diff --git a/devtools/docs/user/working_with_iframes/index.rst b/devtools/docs/user/working_with_iframes/index.rst new file mode 100644 index 0000000000..8d75d97a64 --- /dev/null +++ b/devtools/docs/user/working_with_iframes/index.rst @@ -0,0 +1,25 @@ +==================== +Working with iframes +==================== + +You can point the developer tools at a specific `iframe <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe>`_ within a document. The :doc:`Inspector <../page_inspector/index>`, :doc:`Console <../web_console/index>`, :doc:`Debugger <../debugger/index>` and all other developer tools will then target that iframe (essentially behaving as if the rest of the page does not exist). + +.. raw:: html + + <iframe width="560" height="315" src="https://www.youtube.com/embed/Me9hjqd74m8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + <br/> + <br/> + +To set an iframe as the target for the developer tools: + +- Select the *iframe context picker button* to launch a popup listing all the iframes in the document (and the main document itself). Note that the button is only displayed if the page includes iframes! + + .. image:: developer_tools_select_iframe.png + :alt: Screenshot showing how to set an iframe as the target of developer tools (using the iframe button) + :class: center + +- Select an entry to make it the *target iframe*. + + +.. note:: + The iframe context picker button feature is enabled by default (if it has been disabled the iframe button is never displayed). The feature can be re-enabled from the Settings menu, using the "Select an iframe as the currently targeted document" checkbox. |