Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 14 Jul 2016 11:56:37 +0200
changeset 345058 1983612a169bfc1720ef48b9371e65bd0ec9eef1
parent 345057 553ce3faa35fcb8ae90499d88ebc7b123fde15e4 (current diff)
parent 344976 08f8a5aacd8308a73f6040fe522be7ba38497561 (diff)
child 345059 dda1fd43e2aac64dc5f4585ec24248bb3b551081
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
devtools/client/shared/widgets/mdn-docs-frame.xhtml
devtools/client/themes/images/debugger-blackbox.png
devtools/client/themes/images/debugger-blackbox@2x.png
devtools/client/themes/images/debugger-pause.png
devtools/client/themes/images/debugger-pause@2x.png
devtools/client/themes/images/debugger-play.png
devtools/client/themes/images/debugger-play@2x.png
devtools/client/themes/images/debugger-prettyprint.png
devtools/client/themes/images/debugger-prettyprint@2x.png
devtools/client/themes/images/debugger-step-in.png
devtools/client/themes/images/debugger-step-in@2x.png
devtools/client/themes/images/debugger-step-out.png
devtools/client/themes/images/debugger-step-out@2x.png
devtools/client/themes/images/fast-forward.png
devtools/client/themes/images/fast-forward@2x.png
devtools/client/themes/images/itemToggle.png
devtools/client/themes/images/itemToggle@2x.png
devtools/client/themes/images/rewind.png
devtools/client/themes/images/rewind@2x.png
devtools/client/themes/images/timeline-filter.svg
dom/base/test/mochitest-child-permissions.ini
dom/base/test/test_child_process_shutdown_message.html
dom/base/test/test_messagemanager_assertpermission.html
dom/html/HTMLFormSubmission.cpp
dom/html/HTMLInputElement.cpp
dom/html/HTMLInputElement.h
dom/ipc/Blob.cpp
dom/ipc/ContentParent.cpp
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1449,8 +1449,11 @@ pref("dom.mozBrowserFramesEnabled", true
 
 pref("extensions.pocket.enabled", true);
 
 pref("signon.schemeUpgrades", true);
 
 // Enable the "Simplify Page" feature in Print Preview
 pref("print.use_simplify_page", true);
 
+// Space separated list of URLS that are allowed to send objects (instead of
+// only strings) through webchannels.
+pref("webchannel.allowObject.urlWhitelist", "https://accounts.firefox.com https://content.cdn.mozilla.net https://hello.firefox.com https://input.mozilla.org https://support.mozilla.org https://install.mozilla.org");
--- a/browser/base/content/test/general/browser_fxa_oauth.html
+++ b/browser/base/content/test/general/browser_fxa_oauth.html
@@ -3,16 +3,18 @@
 <head>
   <meta charset="utf-8">
   <title>fxa_oauth_test</title>
 </head>
 <body>
 <script>
   window.onload = function(){
     var event = new window.CustomEvent("WebChannelMessageToChrome", {
+      // Note: This intentionally sends an object instead of a string, to ensure both work
+      // (see browser_fxa_oauth_with_keys.html for the other test)
       detail: {
         id: "oauth_client_id",
         message: {
           command: "oauth_complete",
           data: {
             state: "state",
             code: "code1",
             closeWindow: "signin",
--- a/browser/base/content/test/general/browser_fxa_oauth.js
+++ b/browser/base/content/test/general/browser_fxa_oauth.js
@@ -304,17 +304,25 @@ function waitForTab(aCallback) {
     }, true);
   }, false);
 }
 
 function test() {
   waitForExplicitFinish();
 
   Task.spawn(function () {
-    for (let test of gTests) {
-      info("Running: " + test.desc);
-      yield test.run();
+    const webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
+    let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
+    let newWhitelist = origWhitelist + " http://example.com";
+    Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
+    try {
+      for (let test of gTests) {
+        info("Running: " + test.desc);
+        yield test.run();
+      }
+    } finally {
+      Services.prefs.clearUserPref(webchannelWhitelistPref);
     }
   }).then(finish, ex => {
     Assert.ok(false, "Unexpected Exception: " + ex);
     finish();
   });
 }
--- a/browser/base/content/test/general/browser_fxa_oauth_with_keys.html
+++ b/browser/base/content/test/general/browser_fxa_oauth_with_keys.html
@@ -3,29 +3,31 @@
 <head>
   <meta charset="utf-8">
   <title>fxa_oauth_test</title>
 </head>
 <body>
 <script>
   window.onload = function(){
     var event = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      // Note: This intentionally sends a string instead of an object, to ensure both work
+      // (see browser_fxa_oauth.html for the other test)
+      detail: JSON.stringify({
         id: "oauth_client_id",
         message: {
           command: "oauth_complete",
           data: {
             state: "state",
             code: "code1",
             closeWindow: "signin",
             // Keys normally contain more information, but this is enough
             // to keep Loop's tests happy.
             keys: { kAr: { k: 'kAr' }, kBr: { k: 'kBr' }},
           },
         },
-      },
+      }),
     });
 
     window.dispatchEvent(event);
   };
 </script>
 </body>
 </html>
--- a/browser/base/content/test/general/browser_fxa_web_channel.html
+++ b/browser/base/content/test/general/browser_fxa_web_channel.html
@@ -27,112 +27,112 @@
       case "delete":
         test_delete();
         break;
     }
   };
 
   function test_profile_change() {
     var event = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: webChannelId,
         message: {
           command: "profile:change",
           data: {
             uid: "abc123",
           },
         },
-      },
+      }),
     });
 
     window.dispatchEvent(event);
   }
 
   function test_login() {
     var event = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: webChannelId,
         message: {
           command: "fxaccounts:login",
           data: {
             authAt: Date.now(),
             email: "testuser@testuser.com",
             keyFetchToken: 'key_fetch_token',
             sessionToken: 'session_token',
             uid: 'uid',
             unwrapBKey: 'unwrap_b_key',
             verified: true,
           },
           messageId: 1,
         },
-      },
+      }),
     });
 
     window.dispatchEvent(event);
   }
 
   function test_can_link_account() {
     window.addEventListener("WebChannelMessageToContent", function (e) {
       // echo any responses from the browser back to the tests on the
       // fxaccounts_webchannel_response_echo WebChannel. The tests are
       // listening for events and do the appropriate checks.
       var event = new window.CustomEvent("WebChannelMessageToChrome", {
-        detail: {
+        detail: JSON.stringify({
           id: 'fxaccounts_webchannel_response_echo',
           message: e.detail.message,
-        }
+        })
       });
 
       window.dispatchEvent(event);
     }, true);
 
     var event = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: webChannelId,
         message: {
           command: "fxaccounts:can_link_account",
           data: {
             email: "testuser@testuser.com",
           },
           messageId: 2,
         },
-      },
+      }),
     });
 
     window.dispatchEvent(event);
   }
 
   function test_logout() {
     var event = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: webChannelId,
         message: {
           command: "fxaccounts:logout",
           data: {
             uid: 'uid'
           },
           messageId: 3,
         },
-      },
+      }),
     });
 
     window.dispatchEvent(event);
   }
 
   function test_delete() {
     var event = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: webChannelId,
         message: {
           command: "fxaccounts:delete",
           data: {
             uid: 'uid'
           },
           messageId: 4,
         },
-      },
+      }),
     });
 
     window.dispatchEvent(event);
   }
 </script>
 </body>
 </html>
--- a/browser/base/content/test/general/browser_remoteTroubleshoot.js
+++ b/browser/base/content/test/general/browser_remoteTroubleshoot.js
@@ -2,16 +2,17 @@
  * 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/. */
 
 var {WebChannel} = Cu.import("resource://gre/modules/WebChannel.jsm", {});
 
 const TEST_URL_TAIL = "example.com/browser/browser/base/content/test/general/test_remoteTroubleshoot.html"
 const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URL_TAIL, null, null);
 const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URL_TAIL, null, null);
+const TEST_URI_GOOD_OBJECT = Services.io.newURI("https://" + TEST_URL_TAIL + "?object", null, null);
 
 // Creates a one-shot web-channel for the test data to be sent back from the test page.
 function promiseChannelResponse(channelID, originOrPermission) {
   return new Promise((resolve, reject) => {
     let channel = new WebChannel(channelID, originOrPermission);
     channel.listen((id, data, target) => {
       channel.stopListening();
       resolve(data);
@@ -73,9 +74,20 @@ add_task(function*() {
 
   // And check some keys we know we decline to return.
   Assert.ok(!got.message.modifiedPreferences, "should not have a modifiedPreferences key");
   Assert.ok(!got.message.crashes, "should not have crash info");
 
   // Now a http:// URI - should get nothing even with the permission setup.
   got = yield promiseNewChannelResponse(TEST_URI_BAD);
   Assert.ok(got.message === undefined, "should have failed to get any data");
+
+  // Check that the page can send an object as well if it's in the whitelist
+  let webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
+  let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
+  let newWhitelist = origWhitelist + " https://example.com";
+  Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref(webchannelWhitelistPref);
+  });
+  got = yield promiseNewChannelResponse(TEST_URI_GOOD_OBJECT);
+  Assert.ok(got.message, "should have gotten some data back");
 });
--- a/browser/base/content/test/general/browser_web_channel.html
+++ b/browser/base/content/test/general/browser_web_channel.html
@@ -28,79 +28,82 @@
         test_iframe_pre_redirect();
         break;
       case "unsolicited":
         test_unsolicited();
         break;
       case "bubbles":
         test_bubbles();
         break;
+      case "object":
+        test_object();
+        break;
       default:
         throw new Error(`INVALID TEST NAME ${testName}`);
         break;
     }
   };
 
   function test_generic() {
     var event = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: "generic",
         message: {
           something: {
             nested: "hello",
           },
         }
-      }
+      })
     });
 
     window.dispatchEvent(event);
   }
 
   function test_twoWay() {
     var firstMessage = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: "twoway",
         message: {
           command: "one",
         },
-      }
+      })
     });
 
     window.addEventListener("WebChannelMessageToContent", function(e) {
       var secondMessage = new window.CustomEvent("WebChannelMessageToChrome", {
-        detail: {
+        detail: JSON.stringify({
           id: "twoway",
           message: {
             command: "two",
             detail: e.detail.message,
           },
-        },
+        }),
       });
 
       if (!e.detail.message.error) {
         window.dispatchEvent(secondMessage);
       }
     }, true);
 
     window.dispatchEvent(firstMessage);
   }
 
   function test_multichannel() {
     var event1 = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: "wrongchannel",
         message: {},
-      }
+      })
     });
 
     var event2 = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: "multichannel",
         message: {},
-      }
+      })
     });
 
     window.dispatchEvent(event1);
     window.dispatchEvent(event2);
   }
 
   function test_iframe() {
     // Note that this message is the response to the message sent
@@ -127,40 +130,60 @@
     // echo any unsolicted events back to chrome.
     window.addEventListener("WebChannelMessageToContent", function(e) {
       echoEventToChannel(e, "echo");
     }, true);
   }
 
   function test_bubbles() {
     var event = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: "not_a_window",
         message: {
           command: "start"
         }
-      }
+      })
     });
 
     var nonWindowTarget = document.getElementById("not_a_window");
 
     nonWindowTarget.addEventListener("WebChannelMessageToContent", function(e) {
       echoEventToChannel(e, "not_a_window");
     }, true);
 
 
     nonWindowTarget.dispatchEvent(event);
   }
 
+  function test_object() {
+    let objectMessage = new window.CustomEvent("WebChannelMessageToChrome", {
+      detail: {
+        id: "objects",
+        message: { type: "object" }
+      }
+    });
+
+    let stringMessage = new window.CustomEvent("WebChannelMessageToChrome", {
+      detail: JSON.stringify({
+        id: "objects",
+        message: { type: "string" }
+      })
+    });
+    // Test fails if objectMessage is received, we send stringMessage to know
+    // when we should stop listening for objectMessage
+    window.dispatchEvent(objectMessage);
+    window.dispatchEvent(stringMessage);
+  }
+
   function echoEventToChannel(e, channelId) {
     var echoedEvent = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: channelId,
         message: e.detail.message,
-      }
+      })
     });
 
     e.target.dispatchEvent(echoedEvent);
   }
 </script>
 
 <div id="not_a_window"></div>
 </body>
--- a/browser/base/content/test/general/browser_web_channel.js
+++ b/browser/base/content/test/general/browser_web_channel.js
@@ -39,18 +39,18 @@ var gTests = [
   {
     desc: "WebChannel two way communication",
     run: function* () {
       return new Promise(function(resolve, reject) {
         let tab;
         let channel = new WebChannel("twoway", Services.io.newURI(HTTP_PATH, null, null));
 
         channel.listen(function (id, message, sender) {
-          is(id, "twoway");
-          ok(message.command);
+          is(id, "twoway", "bad id");
+          ok(message.command, "command not ok");
 
           if (message.command === "one") {
             channel.send({ data: { nested: true } }, sender);
           }
 
           if (message.command === "two") {
             is(message.detail.data.nested, true);
             channel.stopListening();
@@ -69,18 +69,18 @@ var gTests = [
       let parentChannel = new WebChannel("echo", Services.io.newURI(HTTP_PATH, null, null));
       let iframeChannel = new WebChannel("twoway", Services.io.newURI(HTTP_IFRAME_PATH, null, null));
       let promiseTestDone = new Promise(function (resolve, reject) {
         parentChannel.listen(function (id, message, sender) {
           reject(new Error("WebChannel message incorrectly sent to parent"));
         });
 
         iframeChannel.listen(function (id, message, sender) {
-          is(id, "twoway");
-          ok(message.command);
+          is(id, "twoway", "bad id (2)");
+          ok(message.command, "command not ok (2)");
 
           if (message.command === "one") {
             iframeChannel.send({ data: { nested: true } }, sender);
           }
 
           if (message.command === "two") {
             is(message.detail.data.nested, true);
             resolve();
@@ -323,16 +323,85 @@ var gTests = [
         gBrowser,
         url: HTTP_PATH + HTTP_ENDPOINT + "?bubbles"
       }, function* () {
         yield testDonePromise;
         channel.stopListening();
       });
     }
   },
+  {
+    desc: "WebChannel disallows non-string message from non-whitelisted origin",
+    run: function* () {
+      /**
+       * This test ensures that non-string messages can't be sent via WebChannels.
+       * We create a page (on a non-whitelisted origin) which should send us two
+       * messages immediately. The first message has an object for it's detail,
+       * and the second has a string. We check that we only get the second
+       * message.
+       */
+      let channel = new WebChannel("objects", Services.io.newURI(HTTP_PATH, null, null));
+      let testDonePromise = new Promise((resolve, reject) => {
+        channel.listen((id, message, sender) => {
+          is(id, "objects");
+          is(message.type, "string");
+          resolve();
+        });
+      });
+      yield BrowserTestUtils.withNewTab({
+        gBrowser,
+        url: HTTP_PATH + HTTP_ENDPOINT + "?object"
+      }, function* () {
+        yield testDonePromise;
+        channel.stopListening();
+      });
+    }
+  },
+  {
+    desc: "WebChannel allows both string and non-string message from whitelisted origin",
+    run: function* () {
+      /**
+       * Same process as above, but we whitelist the origin before loading the page,
+       * and expect to get *both* messages back (each exactly once).
+       */
+      let channel = new WebChannel("objects", Services.io.newURI(HTTP_PATH, null, null));
+
+      let testDonePromise = new Promise((resolve, reject) => {
+        let sawObject = false;
+        let sawString = false;
+        channel.listen((id, message, sender) => {
+          is(id, "objects");
+          if (message.type === "object") {
+            ok(!sawObject);
+            sawObject = true;
+          } else if (message.type === "string") {
+            ok(!sawString);
+            sawString = true;
+          } else {
+            reject(new Error(`Unknown message type: ${message.type}`))
+          }
+          if (sawObject && sawString) {
+            resolve();
+          }
+        });
+      });
+      const webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
+      let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
+      let newWhitelist = origWhitelist + " " + HTTP_PATH;
+      Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
+      yield BrowserTestUtils.withNewTab({
+        gBrowser,
+        url: HTTP_PATH + HTTP_ENDPOINT + "?object"
+      }, function* () {
+        yield testDonePromise;
+        Services.prefs.setCharPref(webchannelWhitelistPref, origWhitelist);
+        channel.stopListening();
+      });
+    }
+  }
 ]; // gTests
 
 function test() {
   waitForExplicitFinish();
 
   Task.spawn(function () {
     for (let test of gTests) {
       info("Running: " + test.desc);
--- a/browser/base/content/test/general/browser_web_channel_iframe.html
+++ b/browser/base/content/test/general/browser_web_channel_iframe.html
@@ -23,75 +23,75 @@
       default:
         throw new Error(`INVALID TEST NAME ${testName}`);
         break;
     }
   };
 
   function test_iframe() {
     var firstMessage = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: "twoway",
         message: {
           command: "one",
         },
-      }
+      })
     });
 
     window.addEventListener("WebChannelMessageToContent", function(e) {
       var secondMessage = new window.CustomEvent("WebChannelMessageToChrome", {
-        detail: {
+        detail: JSON.stringify({
           id: "twoway",
           message: {
             command: "two",
             detail: e.detail.message,
           },
-        },
+        }),
       });
 
       if (!e.detail.message.error) {
         window.dispatchEvent(secondMessage);
       }
     }, true);
 
     window.dispatchEvent(firstMessage);
   }
 
 
   function test_iframe_pre_redirect() {
     var firstMessage = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: "pre_redirect",
         message: {
           command: "redirecting",
         },
-      },
+      }),
     });
     window.dispatchEvent(firstMessage);
     document.location = REDIRECTED_IFRAME_SRC_ROOT + "?iframe_post_redirect";
   }
 
   function test_iframe_post_redirect() {
     window.addEventListener("WebChannelMessageToContent", function(e) {
       var echoMessage = new window.CustomEvent("WebChannelMessageToChrome", {
-        detail: {
+        detail: JSON.stringify({
           id: "post_redirect",
           message: e.detail.message,
-        },
+        }),
       });
 
       window.dispatchEvent(echoMessage);
     }, true);
 
     // Let the test parent know the page has loaded and is ready to echo events
     var loadedMessage = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: "post_redirect",
         message: {
           command: "loaded",
         },
-      },
+      }),
     });
     window.dispatchEvent(loadedMessage);
   }
 </script>
 </body>
 </html>
--- a/browser/base/content/test/general/test_remoteTroubleshoot.html
+++ b/browser/base/content/test/general/test_remoteTroubleshoot.html
@@ -1,39 +1,48 @@
 <!DOCTYPE HTML>
 <html>
 <script>
+// This test is run multiple times, once with only strings allowed through the
+// WebChannel, and once with objects allowed. This function allows us to handle
+// both cases without too much pain.
+function makeDetails(object) {
+  if (window.location.search.indexOf("object") >= 0) {
+    return object;
+  }
+  return JSON.stringify(object)
+}
 // Add a listener for responses to our remote requests.
 window.addEventListener("WebChannelMessageToContent", function (event) {
   if (event.detail.id == "remote-troubleshooting") {
     // Send what we got back to the test.
     var backEvent = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: makeDetails({
         id: "test-remote-troubleshooting-backchannel",
         message: {
           message: event.detail.message,
         },
-      },
+      }),
     });
     window.dispatchEvent(backEvent);
     // and stick it in our DOM just for good measure/diagnostics.
     document.getElementById("troubleshooting").textContent =
       JSON.stringify(event.detail.message, null, 2);
   }
 });
 
 // Make a request for the troubleshooting data as we load.
 window.onload = function() {
   var event = new window.CustomEvent("WebChannelMessageToChrome", {
-    detail: {
+    detail: makeDetails({
       id: "remote-troubleshooting",
       message: {
         command: "request",
       },
-    },
+    }),
   });
   window.dispatchEvent(event);
 }
 </script>
 
 <body>
   <pre id="troubleshooting"/>
 </body>
--- a/browser/components/newtab/tests/browser/browser_newtabwebchannel.js
+++ b/browser/components/newtab/tests/browser/browser_newtabwebchannel.js
@@ -147,22 +147,24 @@ add_task(function* webchannel_switch() {
     return new Promise(resolve => {
       NewTabWebChannel.once("foo", function(name, msg) {
         resolve(msg.target);
       }.bind(this));
     });
   }
 
   let replyCount = 0;
-  let replyPromise = new Promise(resolve => {
-    NewTabWebChannel.on("reply", function() {
-      replyCount += 1;
-      resolve();
-    }.bind(this));
-  });
+  function newReplyPromise() {
+    return new Promise(resolve => {
+      NewTabWebChannel.on("reply", function() {
+        replyCount += 1;
+        resolve();
+      });
+    });
+  }
 
   let unloadPromise = new Promise(resolve => {
     NewTabWebChannel.once("targetUnload", function() {
       resolve();
     });
   });
 
   let unloadAllPromise = new Promise(resolve => {
@@ -183,19 +185,30 @@ add_task(function* webchannel_switch() {
   messagePromise = newMessagePromise();
   Preferences.set("browser.newtabpage.remote.mode", "test2");
   tabs.push(yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL_2));
   yield unloadAllPromise;
   yield messagePromise;
   is(NewTabWebChannel.numBrowsers, 1, "Correct number of targets");
 
   NewTabWebChannel.broadcast("respond", null);
-  yield replyPromise;
+  yield newReplyPromise();
   is(replyCount, 1, "only current channel is listened to for replies");
 
+  const webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
+  let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
+  let newWhitelist = origWhitelist + " http://mochi.test:8888";
+  Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
+  try {
+    NewTabWebChannel.broadcast("respond_object", null);
+    yield newReplyPromise();
+  } finally {
+    Services.prefs.clearUserPref(webchannelWhitelistPref);
+  }
+
   for (let tab of tabs) {
     yield BrowserTestUtils.removeTab(tab);
   }
 
   Cu.forceGC();
   is(NewTabWebChannel.numBrowsers, 0, "Sanity check");
   yield unloadPromise;
   cleanup();
--- a/browser/components/newtab/tests/browser/newtabmessages_places.html
+++ b/browser/components/newtab/tests/browser/newtabmessages_places.html
@@ -6,44 +6,44 @@
   <body>
     <script>
       window.addEventListener("WebChannelMessageToContent", function(e) {
         if (e.detail.message) {
           let reply;
           switch (e.detail.message.type) {
             case "RECEIVE_FRECENT":
               reply = new window.CustomEvent("WebChannelMessageToChrome", {
-                detail: {
+                detail: JSON.stringify({
                   id: "newtab",
                   message: JSON.stringify({type: "numItemsAck", data: e.detail.message.data.length}),
-                }
+                })
               });
               window.dispatchEvent(reply);
               break;
             case "RECEIVE_PLACES_CHANGE":
               if (e.detail.message.data.type === "clearHistory") {
                 reply = new window.CustomEvent("WebChannelMessageToChrome", {
-                  detail: {
+                  detail: JSON.stringify({
                     id: "newtab",
                     message: JSON.stringify({type: "clearHistoryAck", data: e.detail.message.data.type}),
-                  }
+                  })
                 });
                 window.dispatchEvent(reply);
               }
               break;
           }
         }
       }, true);
 
       document.onreadystatechange = function () {
         if (document.readyState === "complete") {
           let msg = new window.CustomEvent("WebChannelMessageToChrome", {
-            detail: {
+            detail: JSON.stringify({
               id: "newtab",
               message: JSON.stringify({type: "REQUEST_FRECENT"}),
-            }
+            })
           });
           window.dispatchEvent(msg);
         }
       }
     </script>
   </body>
 </html>
--- a/browser/components/newtab/tests/browser/newtabmessages_prefs.html
+++ b/browser/components/newtab/tests/browser/newtabmessages_prefs.html
@@ -3,30 +3,30 @@
         <meta charset="utf8">
         <title>Newtab WebChannel test</title>
     </head>
     <body>
         <script>
             window.addEventListener("WebChannelMessageToContent", function(e) {
                 if (e.detail.message && e.detail.message.type === "RECEIVE_PREFS") {
                     let reply = new window.CustomEvent("WebChannelMessageToChrome", {
-                        detail: {
+                        detail: JSON.stringify({
                             id: "newtab",
                             message: JSON.stringify({type: "responseAck"}),
-                        }
+                        })
                     });
                     window.dispatchEvent(reply);
                 }
             }, true);
 
             document.onreadystatechange = function () {
                 let msg = new window.CustomEvent("WebChannelMessageToChrome", {
-                    detail: {
+                    detail: JSON.stringify({
                         id: "newtab",
                         message: JSON.stringify({type: "REQUEST_PREFS"}),
-                    }
+                    })
                 });
                 window.dispatchEvent(msg);
             };
 
         </script>
     </body>
 </html>
--- a/browser/components/newtab/tests/browser/newtabmessages_preview.html
+++ b/browser/components/newtab/tests/browser/newtabmessages_preview.html
@@ -6,32 +6,32 @@
   <body>
     <script>
       let thumbURL = "https://example.com/browser/browser/components/newtab/tests/browser/blue_page.html";
 
       window.addEventListener("WebChannelMessageToContent", function(e) {
         if (e.detail.message && e.detail.message.type === "RECEIVE_THUMB") {
           if (e.detail.message.data.imgData && e.detail.message.data.url === thumbURL) {
             let reply = new window.CustomEvent("WebChannelMessageToChrome", {
-              detail: {
+              detail: JSON.stringify({
                 id: "newtab",
                 message: JSON.stringify({type: "responseAck"}),
-              }
+              })
             });
             window.dispatchEvent(reply);
           }
         }
       }, true);
 
       document.onreadystatechange = function () {
         if (document.readyState === "complete") {
           let msg = new window.CustomEvent("WebChannelMessageToChrome", {
-            detail: {
+            detail: JSON.stringify({
               id: "newtab",
               message: JSON.stringify({type: "REQUEST_THUMB", data: thumbURL}),
-            }
+            })
           });
           window.dispatchEvent(msg);
         }
       };
     </script>
   </body>
 </html>
--- a/browser/components/newtab/tests/browser/newtabwebchannel_basic.html
+++ b/browser/components/newtab/tests/browser/newtabwebchannel_basic.html
@@ -2,31 +2,35 @@
     <head>
         <meta charset="utf8">
         <title>Newtab WebChannel test</title>
     </head>
     <body>
         <script>
             document.onreadystatechange = function () {
                 let msg = new window.CustomEvent("WebChannelMessageToChrome", {
-                    detail: {
+                    detail: JSON.stringify({
                         id: "newtab",
                         message: JSON.stringify({type: "foo", data: "bar"}),
-                    }
+                    })
                 });
                 window.dispatchEvent(msg);
             };
 
             window.addEventListener("WebChannelMessageToContent", function(e) {
-                if (e.detail.message && e.detail.message.type === "respond") {
+                if (e.detail.message && e.detail.message.type.startsWith("respond")) {
+                    var detail = {
+                        id: "newtab",
+                        message: JSON.stringify({type: "reply", data: "quuz"}),
+                    };
+                    if (e.detail.message.type !== "respond_object") {
+                        detail = JSON.stringify(detail);
+                    }
                     let reply = new window.CustomEvent("WebChannelMessageToChrome", {
-                        detail: {
-                            id: "newtab",
-                            message: JSON.stringify({type: "reply", data: "quuz"}),
-                        }
+                        detail: detail
                     });
                     window.dispatchEvent(reply);
                 }
             }, true);
             
         </script>
     </body>
 </html>
--- a/browser/extensions/loop/chrome/content/shared/js/activeRoomStore.js
+++ b/browser/extensions/loop/chrome/content/shared/js/activeRoomStore.js
@@ -490,21 +490,21 @@ loop.store.ActiveRoomStore = function (m
 
 
         webChannelListenerFunc = webChannelListener.bind(this);
 
         window.addEventListener("WebChannelMessageToContent", webChannelListenerFunc);
 
         // Now send a message to the chrome to see if it can handle this room.
         window.dispatchEvent(new window.CustomEvent("WebChannelMessageToChrome", { 
-          detail: { 
+          detail: JSON.stringify({ 
             id: "loop-link-clicker", 
             message: { 
               command: "checkWillOpenRoom", 
-              roomToken: this._storeState.roomToken } } }));}.
+              roomToken: this._storeState.roomToken } }) }));}.
 
 
 
       bind(this));}, 
 
 
     /**
      * Handles the updateRoomInfo action. Updates the room data.
@@ -616,21 +616,21 @@ loop.store.ActiveRoomStore = function (m
 
 
       channelListener = handleRoomJoinResponse.bind(this);
 
       window.addEventListener("WebChannelMessageToContent", channelListener);
 
       // Now we're set up, dispatch an event.
       window.dispatchEvent(new window.CustomEvent("WebChannelMessageToChrome", { 
-        detail: { 
+        detail: JSON.stringify({ 
           id: "loop-link-clicker", 
           message: { 
             command: "openRoom", 
-            roomToken: this._storeState.roomToken } } }));}, 
+            roomToken: this._storeState.roomToken } }) }));}, 
 
 
 
 
 
     /**
      * Handles the action to join to a room.
      */
--- a/browser/extensions/loop/chrome/content/shared/test/activeRoomStore_test.js
+++ b/browser/extensions/loop/chrome/content/shared/test/activeRoomStore_test.js
@@ -885,21 +885,21 @@ describe("loop.store.ActiveRoomStore", f
       it("should dispatch an event to Firefox", function () {
         sandbox.stub(window, "dispatchEvent");
 
         store.joinRoom();
 
         sinon.assert.calledOnce(window.dispatchEvent);
         sinon.assert.calledWithExactly(window.dispatchEvent, new window.CustomEvent(
         "WebChannelMessageToChrome", { 
-          detail: { 
+          detail: JSON.stringify({ 
             id: "loop-link-clicker", 
             message: { 
               command: "openRoom", 
-              roomToken: "fakeToken" } } }));});
+              roomToken: "fakeToken" } }) }));});
 
 
 
 
 
       it("should log an error if Firefox doesn't handle the room", function () {
         // Start the join.
         store.joinRoom();
--- a/browser/extensions/loop/chrome/test/mochitest/browser_LoopRooms_channel.js
+++ b/browser/extensions/loop/chrome/test/mochitest/browser_LoopRooms_channel.js
@@ -10,16 +10,17 @@
 
 var { WebChannel } = Cu.import("resource://gre/modules/WebChannel.jsm", {});
 var { Chat } = Cu.import("resource:///modules/Chat.jsm", {});
 
 const TEST_URI =
   "example.com/browser/browser/extensions/loop/chrome/test/mochitest/test_loopLinkClicker_channel.html";
 const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URI, null, null);
 const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URI, null, null);
+const TEST_URI_GOOD_OBJECT = Services.io.newURI("https://" + TEST_URI + "?object", null, null);
 
 const ROOM_TOKEN = "fake1234";
 const LINKCLICKER_URL_PREFNAME = "loop.linkClicker.url";
 
 var openChatOrig = Chat.open;
 
 var fakeRoomList = new Map([[ROOM_TOKEN, { roomToken: ROOM_TOKEN }]]);
 
@@ -160,9 +161,23 @@ add_task(function* test_loopRooms_webcha
 
   // Simulate a window already being open.
   MozLoopServiceInternal.mocks.isChatWindowOpen = true;
 
   got = yield promiseNewChannelResponse(TEST_URI_GOOD, gGoodBackChannel, "openRoom");
 
   Assert.equal(got.message.response, true, "should have got a response of true");
   Assert.equal(got.message.alreadyOpen, true, "should indicate the room is already open");
+
+  // Ensure this still works properly when passing an object through the WebChannel
+  let webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
+  let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
+  let newWhitelist = origWhitelist + " https://example.com";
+  Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref(webchannelWhitelistPref);
+  });
+  got = yield promiseNewChannelResponse(TEST_URI_GOOD_OBJECT, gGoodBackChannel, "openRoom");
+
+  Assert.equal(got.message.response, true, "should have got a response of true with objects");
+  Assert.equal(got.message.alreadyOpen, true, "should indicate the room is already open with objects");
+
 });
--- a/browser/extensions/loop/chrome/test/mochitest/test_loopLinkClicker_channel.html
+++ b/browser/extensions/loop/chrome/test/mochitest/test_loopLinkClicker_channel.html
@@ -1,44 +1,53 @@
 <!DOCTYPE HTML>
 <html>
 <script>
 "use strict";
+// This test is run multiple times, once with only strings allowed through the
+// WebChannel, and once with objects allowed. This function allows us to handle
+// both cases without too much pain.
+function makeDetails(object) {
+  if (window.location.search.indexOf("object") >= 0) {
+    return object;
+  }
+  return JSON.stringify(object)
+}
 
 // Add a listener for responses to our remote requests.
 window.addEventListener("WebChannelMessageToContent", function (event) {
   if (event.detail.id == "loop-link-clicker") {
     // Send what we got back to the test.
     var backEvent = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: makeDetails({
         id: "test-loop-link-clicker-backchannel",
         message: {
           message: event.detail.message
         }
-      }
+      })
     });
     window.dispatchEvent(backEvent);
     // and stick it in our DOM just for good measure/diagnostics.
     document.getElementById("troubleshooting").textContent =
       JSON.stringify(event.detail.message, null, 2);
   }
 });
 
 // Send a message on load requesting that the room is opened.
 window.onload = function() {
   var hash = window.location.hash;
 
   var event = new window.CustomEvent("WebChannelMessageToChrome", {
-    detail: {
+    detail: makeDetails({
       id: "loop-link-clicker",
       message: {
         command: hash.substring(1, hash.length),
         roomToken: "fake1234"
       }
-    }
+    })
   });
   window.dispatchEvent(event);
 };
 </script>
 
 <body>
   <pre id="troubleshooting"/>
 </body>
--- a/devtools/client/aboutdebugging/test/browser_addons_debug_bootstrapped.js
+++ b/devtools/client/aboutdebugging/test/browser_addons_debug_bootstrapped.js
@@ -1,12 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
+// Avoid test timeouts that can occur while waiting for the "addon-console-works" message.
+requestLongerTimeout(2);
+
 const ADDON_ID = "test-devtools@mozilla.org";
 const ADDON_NAME = "test-devtools";
 
 const { BrowserToolboxProcess } = Cu.import("resource://devtools/client/framework/ToolboxProcess.jsm", {});
 
 add_task(function* () {
   yield new Promise(resolve => {
     let options = {"set": [
--- a/devtools/client/canvasdebugger/canvasdebugger.xul
+++ b/devtools/client/canvasdebugger/canvasdebugger.xul
@@ -29,17 +29,17 @@
           <toolbarbutton id="record-snapshot"
                          class="devtools-toolbarbutton"
                          oncommand="SnapshotsListView._onRecordButtonClick()"
                          tooltiptext="&canvasDebuggerUI.recordSnapshot.tooltip;"
                          hidden="true"/>
           <toolbarbutton id="import-snapshot"
                          class="devtools-toolbarbutton"
                          oncommand="SnapshotsListView._onImportButtonClick()"
-                         label="&canvasDebuggerUI.importSnapshot;"/>
+                         tooltiptext="&canvasDebuggerUI.importSnapshot;"/>
         </hbox>
       </toolbar>
       <vbox id="snapshots-list" flex="1"/>
     </vbox>
 
     <vbox id="debugging-pane" class="devtools-main-content" flex="1">
       <hbox id="reload-notice"
             class="notice-container"
--- a/devtools/client/debugger/test/mochitest/browser_dbg_sources-webext-contentscript.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_sources-webext-contentscript.js
@@ -37,17 +37,17 @@ function test() {
     let options = {
       source: `moz-extension://${uuid}/webext-content-script.js`,
       line: 1
     };
     [,, gPanel] = yield initDebugger(TAB_URL, options);
     gDebugger = gPanel.panelWin;
     gSources = gDebugger.DebuggerView.Sources;
 
-    is(gSources.values.length, 1, "Should have 1 source");
+    is(gSources.values.length, 2, "Should have 2 sources");
 
     let item = gSources.getItemForAttachment(attachment => {
       return attachment.source.url.includes("moz-extension");
     });
 
     ok(item, "Got the expected WebExtensions ContentScript source");
     ok(item && item.attachment.source.url.includes(item.attachment.group),
        "The source is in the expected source group");
--- a/devtools/client/framework/menu.js
+++ b/devtools/client/framework/menu.js
@@ -59,17 +59,27 @@ Menu.prototype.insert = function (pos, m
  *
  * @param {int} screenX
  * @param {int} screenY
  * @param Toolbox toolbox (non standard)
  *        Needed so we in which window to inject XUL
  */
 Menu.prototype.popup = function (screenX, screenY, toolbox) {
   let doc = toolbox.doc;
-  let popup = doc.createElement("menupopup");
+  let popupset = doc.querySelector("popupset");
+  // See bug 1285229, on Windows, opening the same popup multiple times in a
+  // row ends up duplicating the popup. The newly inserted popup doesn't
+  // dismiss the old one. So remove any previously displayed popup before
+  // opening a new one.
+  let popup = popupset.querySelector("menupopup[menu-api=\"true\"]");
+  if (popup) {
+    popup.hidePopup();
+  }
+
+  popup = doc.createElement("menupopup");
   popup.setAttribute("menu-api", "true");
 
   if (this.id) {
     popup.id = this.id;
   }
   this._createMenuItems(popup);
 
   // Remove the menu from the DOM once it's hidden.
@@ -81,17 +91,17 @@ Menu.prototype.popup = function (screenX
   });
 
   popup.addEventListener("popupshown", (e) => {
     if (e.target === popup) {
       this.emit("open");
     }
   });
 
-  doc.querySelector("popupset").appendChild(popup);
+  popupset.appendChild(popup);
   popup.openPopupAtScreen(screenX, screenY, true);
 };
 
 Menu.prototype._createMenuItems = function (parent) {
   let doc = parent.ownerDocument;
   this.menuitems.forEach(item => {
     if (!item.visible) {
       return;
--- a/devtools/client/framework/toolbox.xul
+++ b/devtools/client/framework/toolbox.xul
@@ -1,13 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://devtools/skin/toolbox.css" type="text/css"?>
 <?xml-stylesheet href="resource://devtools/client/shared/components/notification-box.css" type="text/css"?>
 
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 
 <!DOCTYPE window [
 <!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
 %toolboxDTD;
 <!ENTITY % editMenuStrings SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -2640,35 +2640,33 @@ function MarkupElementContainer(markupVi
   } else {
     throw new Error("Invalid node for MarkupElementContainer");
   }
 
   this.tagLine.appendChild(this.editor.elt);
 }
 
 MarkupElementContainer.prototype = Heritage.extend(MarkupContainer.prototype, {
-  _buildEventTooltipContent: function (target, tooltip) {
+  _buildEventTooltipContent: Task.async(function* (target, tooltip) {
     if (target.hasAttribute("data-event")) {
-      tooltip.hide(target);
-
-      this.node.getEventListenerInfo().then(listenerInfo => {
-        let toolbox = this.markup._inspector.toolbox;
-        setEventTooltip(tooltip, listenerInfo, toolbox);
-        // Disable the image preview tooltip while we display the event details
-        this.markup._disableImagePreviewTooltip();
-        tooltip.once("hidden", () => {
-          // Enable the image preview tooltip after closing the event details
-          this.markup._enableImagePreviewTooltip();
-        });
-        tooltip.show(target);
+      yield tooltip.hide();
+
+      let listenerInfo = yield this.node.getEventListenerInfo();
+
+      let toolbox = this.markup._inspector.toolbox;
+      setEventTooltip(tooltip, listenerInfo, toolbox);
+      // Disable the image preview tooltip while we display the event details
+      this.markup._disableImagePreviewTooltip();
+      tooltip.once("hidden", () => {
+        // Enable the image preview tooltip after closing the event details
+        this.markup._enableImagePreviewTooltip();
       });
-      return true;
+      tooltip.show(target);
     }
-    return undefined;
-  },
+  }),
 
   /**
    * Generates the an image preview for this Element. The element must be an
    * image or canvas (@see isPreviewable).
    *
    * @return {Promise} that is resolved with an object of form
    *         { data, size: { naturalWidth, naturalHeight, resizeRatio } } where
    *         - data is the data-uri for the image preview.
--- a/devtools/client/inspector/markup/test/browser_markup_load_01.js
+++ b/devtools/client/inspector/markup/test/browser_markup_load_01.js
@@ -53,17 +53,17 @@ add_task(function* () {
 });
 
 function* chooseWithInspectElementContextMenu(selector, testActor) {
   yield BrowserTestUtils.synthesizeMouseAtCenter(selector, {
     type: "contextmenu",
     button: 2
   }, gBrowser.selectedBrowser);
 
-  yield testActor.synthesizeKey({key: "Q", options: {}});
+  yield EventUtils.synthesizeKey("Q", {});
 }
 
 function waitForLinkedBrowserEvent(tab, event) {
   let def = defer();
   tab.linkedBrowser.addEventListener(event, function cb() {
     tab.linkedBrowser.removeEventListener(event, cb, true);
     def.resolve();
   }, true);
--- a/devtools/client/inspector/markup/test/helper_events_test_runner.js
+++ b/devtools/client/inspector/markup/test/helper_events_test_runner.js
@@ -97,15 +97,15 @@ function* checkEventsForNode(test, inspe
     }
 
     // Make sure the header is not hidden by scrollbars before clicking.
     header.scrollIntoView();
 
     EventUtils.synthesizeMouseAtCenter(header, {}, type.ownerGlobal);
     yield tooltip.once("event-tooltip-ready");
 
-    let editor = tooltip.eventEditors.get(contentBox).editor;
+    let editor = tooltip.eventTooltip._eventEditors.get(contentBox).editor;
     is(editor.getText(), expected[i].handler,
        "handler matches for " + cssSelector);
   }
 
   tooltip.hide();
 }
--- a/devtools/client/inspector/rules/test/browser_rules_context-menu-show-mdn-docs-02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_context-menu-show-mdn-docs-02.js
@@ -7,17 +7,17 @@
  * with the MDN docs tooltip.
  *
  * If you display the context click on a property name in the rule view, you
  * should see a menu item "Show MDN Docs". If you click that item, the MDN
  * docs tooltip should be shown, containing docs from MDN for that property.
  *
  * This file tests that:
  * - clicking the context menu item shows the tooltip
- * - pressing "Escape" while the tooltip is showing hides the tooltip
+ * - the tooltip content matches the property name for which the context menu was opened
  */
 
 "use strict";
 
 const {setBaseCssDocsUrl} =
   require("devtools/client/shared/widgets/MdnDocsWidget");
 
 const PROPERTYNAME = "color";
@@ -31,20 +31,17 @@ const TEST_DOC = `
     </body>
   </html>
 `;
 
 add_task(function* () {
   yield addTab("data:text/html;charset=utf8," + encodeURIComponent(TEST_DOC));
   let {inspector, view} = yield openRuleView();
   yield selectNode("div", inspector);
-  yield testShowAndHideMdnTooltip(view);
-});
 
-function* testShowMdnTooltip(view) {
   setBaseCssDocsUrl(URL_ROOT);
 
   info("Setting the popupNode for the MDN docs tooltip");
 
   let {nameSpan} = getRuleViewProperty(view, "element", PROPERTYNAME);
 
   let allMenuItems = openStyleContextMenuAndGetAllItems(view, nameSpan.firstChild);
   let menuitemShowMdnDocs = allMenuItems.find(item => item.label ===
@@ -52,35 +49,13 @@ function* testShowMdnTooltip(view) {
 
   let cssDocs = view.tooltips.cssDocs;
 
   info("Showing the MDN docs tooltip");
   let onShown = cssDocs.tooltip.once("shown");
   menuitemShowMdnDocs.click();
   yield onShown;
   ok(true, "The MDN docs tooltip was shown");
-}
-
-/**
- * Test that:
- *  - the MDN tooltip is shown when we click the context menu item
- *  - the tooltip's contents have been initialized (we don't fully
- *  test this here, as it's fully tested with the tooltip test code)
- *  - the tooltip is hidden when we press Escape
- */
-function* testShowAndHideMdnTooltip(view) {
-  yield testShowMdnTooltip(view);
 
   info("Quick check that the tooltip contents are set");
-  let cssDocs = view.tooltips.cssDocs;
-
-  // FIXME: Remove the comment below when bug 1246896 is fixed.
-  /* eslint-disable mozilla/no-cpows-in-tests */
-  let tooltipDocument = cssDocs.tooltip.content.contentDocument;
-  let h1 = tooltipDocument.getElementById("property-name");
+  let h1 = cssDocs.tooltip.container.querySelector(".mdn-property-name");
   is(h1.textContent, PROPERTYNAME, "The MDN docs tooltip h1 is correct");
-
-  info("Simulate pressing the 'Escape' key");
-  let onHidden = cssDocs.tooltip.once("hidden");
-  EventUtils.sendKey("escape");
-  yield onHidden;
-  ok(true, "The MDN docs tooltip was hidden on pressing 'escape'");
-}
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_css-docs-tooltip_closes-on-escape.js
@@ -0,0 +1,51 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test that the CssDocs tooltip of the ruleview can be closed when pressing the Escape
+ * key.
+ */
+
+"use strict";
+
+const {setBaseCssDocsUrl} =
+  require("devtools/client/shared/widgets/MdnDocsWidget");
+
+const PROPERTYNAME = "color";
+
+const TEST_URI = `
+  <html>
+    <body>
+      <div style="color: red">
+        Test "Show MDN Docs" closes on escape
+      </div>
+    </body>
+  </html>
+`;
+
+/**
+ * Test that the tooltip is hidden when we press Escape
+ */
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("div", inspector);
+
+  setBaseCssDocsUrl(URL_ROOT);
+
+  info("Retrieve a valid anchor for the CssDocs tooltip");
+  let {nameSpan} = getRuleViewProperty(view, "element", PROPERTYNAME);
+
+  info("Showing the MDN docs tooltip");
+  let onShown = view.tooltips.cssDocs.tooltip.once("shown");
+  view.tooltips.cssDocs.show(nameSpan, PROPERTYNAME);
+  yield onShown;
+  ok(true, "The MDN docs tooltip was shown");
+
+  info("Simulate pressing the 'Escape' key");
+  let onHidden = view.tooltips.cssDocs.tooltip.once("hidden");
+  EventUtils.sendKey("escape");
+  yield onHidden;
+  ok(true, "The MDN docs tooltip was hidden on pressing 'escape'");
+});
--- a/devtools/client/inspector/shared/style-inspector-overlays.js
+++ b/devtools/client/inspector/shared/style-inspector-overlays.js
@@ -15,19 +15,21 @@
 const {getColor} = require("devtools/client/shared/theme");
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 const {
   getImageDimensions,
   setImageTooltip,
   setBrokenImageTooltip,
 } = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
 const {
+  CssDocsTooltip,
+} = require("devtools/client/shared/widgets/tooltip/CssDocsTooltip");
+const {
   SwatchColorPickerTooltip,
   SwatchCubicBezierTooltip,
-  CssDocsTooltip,
   SwatchFilterTooltip
 } = require("devtools/client/shared/widgets/Tooltip");
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
 const {Task} = require("devtools/shared/task");
 const Services = require("Services");
 
 const PREF_IMAGE_TOOLTIP_SIZE = "devtools.inspector.imagePreviewTooltipSize";
@@ -271,32 +273,31 @@ TooltipsOverlay.prototype = {
    * Add the tooltips overlay to the view. This will start tracking mouse
    * movements and display tooltips when needed
    */
   addToView: function () {
     if (this._isStarted || this._isDestroyed) {
       return;
     }
 
-    let panelDoc = this.view.inspector.panelDoc;
+    let { toolbox } = this.view.inspector;
 
     // Image, fonts, ... preview tooltip
-    this.previewTooltip = new HTMLTooltip(this.view.inspector.toolbox, {
+    this.previewTooltip = new HTMLTooltip(toolbox, {
       type: "arrow",
       useXulWrapper: true
     });
     this.previewTooltip.startTogglingOnHover(this.view.element,
       this._onPreviewTooltipTargetHover.bind(this));
 
     // MDN CSS help tooltip
-    this.cssDocs = new CssDocsTooltip(panelDoc);
+    this.cssDocs = new CssDocsTooltip(toolbox);
 
     if (this.isRuleView) {
       // Color picker tooltip
-      let { toolbox } = this.view.inspector;
       this.colorPicker = new SwatchColorPickerTooltip(toolbox);
       // Cubic bezier tooltip
       this.cubicBezier = new SwatchCubicBezierTooltip(toolbox);
       // Filter editor tooltip
       this.filterEditor = new SwatchFilterTooltip(toolbox);
     }
 
     this._isStarted = true;
@@ -388,17 +389,17 @@ TooltipsOverlay.prototype = {
       this.colorPicker.hide();
     }
 
     if (this.isRuleView && this.cubicBezier.tooltip.isVisible()) {
       this.cubicBezier.revert();
       this.cubicBezier.hide();
     }
 
-    if (this.isRuleView && this.cssDocs.tooltip.isShown()) {
+    if (this.isRuleView && this.cssDocs.tooltip.isVisible()) {
       this.cssDocs.hide();
     }
 
     if (this.isRuleView && this.filterEditor.tooltip.isVisible()) {
       this.filterEditor.revert();
       this.filterEdtior.hide();
     }
 
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -123,17 +123,16 @@ devtools.jar:
     content/framework/dev-edition-promo/dev-edition-logo.png (framework/dev-edition-promo/dev-edition-logo.png)
     content/inspector/inspector.xul (inspector/inspector.xul)
     content/inspector/inspector.css (inspector/inspector.css)
     content/framework/connect/connect.xhtml (framework/connect/connect.xhtml)
     content/framework/connect/connect.css (framework/connect/connect.css)
     content/framework/connect/connect.js (framework/connect/connect.js)
     content/shared/widgets/graphs-frame.xhtml (shared/widgets/graphs-frame.xhtml)
     content/shared/widgets/cubic-bezier.css (shared/widgets/cubic-bezier.css)
-    content/shared/widgets/mdn-docs-frame.xhtml (shared/widgets/mdn-docs-frame.xhtml)
     content/shared/widgets/mdn-docs.css (shared/widgets/mdn-docs.css)
     content/shared/widgets/filter-widget.css (shared/widgets/filter-widget.css)
     content/shared/widgets/spectrum.css (shared/widgets/spectrum.css)
     content/eyedropper/eyedropper.xul (eyedropper/eyedropper.xul)
     content/eyedropper/crosshairs.css (eyedropper/crosshairs.css)
     content/eyedropper/nocursor.css (eyedropper/nocursor.css)
     content/aboutdebugging/aboutdebugging.xhtml (aboutdebugging/aboutdebugging.xhtml)
     content/aboutdebugging/aboutdebugging.css (aboutdebugging/aboutdebugging.css)
@@ -146,16 +145,17 @@ devtools.jar:
 %   skin devtools classic/1.0 %skin/
     skin/devtools-browser.css (themes/devtools-browser.css)
     skin/common.css (themes/common.css)
     skin/splitters.css (themes/splitters.css)
     skin/dark-theme.css (themes/dark-theme.css)
     skin/light-theme.css (themes/light-theme.css)
     skin/firebug-theme.css (themes/firebug-theme.css)
     skin/toolbars.css (themes/toolbars.css)
+    skin/toolbox.css (themes/toolbox.css)
     skin/tooltips.css (themes/tooltips.css)
     skin/variables.css (themes/variables.css)
     skin/images/add.svg (themes/images/add.svg)
     skin/images/filters.svg (themes/images/filters.svg)
     skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
     skin/images/angle-swatch.svg (themes/images/angle-swatch.svg)
     skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
     skin/images/controls.png (themes/images/controls.png)
@@ -196,60 +196,49 @@ devtools.jar:
     skin/eyedropper.css (themes/eyedropper.css)
     skin/canvasdebugger.css (themes/canvasdebugger.css)
     skin/debugger.css (themes/debugger.css)
     skin/netmonitor.css (themes/netmonitor.css)
     skin/dom.css (themes/dom.css)
     skin/performance.css (themes/performance.css)
     skin/memory.css (themes/memory.css)
     skin/promisedebugger.css (themes/promisedebugger.css)
-    skin/images/timeline-filter.svg (themes/images/timeline-filter.svg)
     skin/scratchpad.css (themes/scratchpad.css)
     skin/shadereditor.css (themes/shadereditor.css)
     skin/storage.css (themes/storage.css)
     skin/splitview.css (themes/splitview.css)
     skin/styleeditor.css (themes/styleeditor.css)
     skin/webaudioeditor.css (themes/webaudioeditor.css)
     skin/components-frame.css (themes/components-frame.css)
     skin/components-h-split-box.css (themes/components-h-split-box.css)
     skin/jit-optimizations.css (themes/jit-optimizations.css)
     skin/images/magnifying-glass.png (themes/images/magnifying-glass.png)
     skin/images/magnifying-glass@2x.png (themes/images/magnifying-glass@2x.png)
     skin/images/magnifying-glass-light.png (themes/images/magnifying-glass-light.png)
     skin/images/magnifying-glass-light@2x.png (themes/images/magnifying-glass-light@2x.png)
     skin/images/filter.svg (themes/images/filter.svg)
     skin/images/search.svg (themes/images/search.svg)
-    skin/images/itemToggle.png (themes/images/itemToggle.png)
-    skin/images/itemToggle@2x.png (themes/images/itemToggle@2x.png)
+    skin/images/itemToggle.svg (themes/images/itemToggle.svg)
     skin/images/itemArrow-dark-rtl.svg (themes/images/itemArrow-dark-rtl.svg)
     skin/images/itemArrow-dark-ltr.svg (themes/images/itemArrow-dark-ltr.svg)
     skin/images/itemArrow-rtl.svg (themes/images/itemArrow-rtl.svg)
     skin/images/itemArrow-ltr.svg (themes/images/itemArrow-ltr.svg)
     skin/images/noise.png (themes/images/noise.png)
     skin/images/dropmarker.svg (themes/images/dropmarker.svg)
     skin/layout.css (themes/layout.css)
     skin/images/geometry-editor.svg (themes/images/geometry-editor.svg)
-    skin/images/debugger-pause.png (themes/images/debugger-pause.png)
-    skin/images/debugger-pause@2x.png (themes/images/debugger-pause@2x.png)
-    skin/images/debugger-play.png (themes/images/debugger-play.png)
-    skin/images/debugger-play@2x.png (themes/images/debugger-play@2x.png)
-    skin/images/fast-forward.png (themes/images/fast-forward.png)
-    skin/images/fast-forward@2x.png (themes/images/fast-forward@2x.png)
-    skin/images/rewind.png (themes/images/rewind.png)
-    skin/images/rewind@2x.png (themes/images/rewind@2x.png)
-    skin/images/debugger-step-in.png (themes/images/debugger-step-in.png)
-    skin/images/debugger-step-in@2x.png (themes/images/debugger-step-in@2x.png)
-    skin/images/debugger-step-out.png (themes/images/debugger-step-out.png)
-    skin/images/debugger-step-out@2x.png (themes/images/debugger-step-out@2x.png)
-    skin/images/debugger-step-over.png (themes/images/debugger-step-over.png)
-    skin/images/debugger-step-over@2x.png (themes/images/debugger-step-over@2x.png)
-    skin/images/debugger-blackbox.png (themes/images/debugger-blackbox.png)
-    skin/images/debugger-blackbox@2x.png (themes/images/debugger-blackbox@2x.png)
-    skin/images/debugger-prettyprint.png (themes/images/debugger-prettyprint.png)
-    skin/images/debugger-prettyprint@2x.png (themes/images/debugger-prettyprint@2x.png)
+    skin/images/pause.svg (themes/images/pause.svg)
+    skin/images/play.svg (themes/images/play.svg)
+    skin/images/fast-forward.svg (themes/images/fast-forward.svg)
+    skin/images/rewind.svg (themes/images/rewind.svg)
+    skin/images/debugger-step-in.svg (themes/images/debugger-step-in.svg)
+    skin/images/debugger-step-out.svg (themes/images/debugger-step-out.svg)
+    skin/images/debugger-step-over.svg (themes/images/debugger-step-over.svg)
+    skin/images/debugger-blackbox.svg (themes/images/debugger-blackbox.svg)
+    skin/images/debugger-prettyprint.svg (themes/images/debugger-prettyprint.svg)
     skin/images/debugger-toggleBreakpoints.svg (themes/images/debugger-toggleBreakpoints.svg)
     skin/images/tracer-icon.png (themes/images/tracer-icon.png)
     skin/images/tracer-icon@2x.png (themes/images/tracer-icon@2x.png)
     skin/images/responsivemode/responsive-se-resizer.png (themes/images/responsivemode/responsive-se-resizer.png)
     skin/images/responsivemode/responsive-se-resizer@2x.png (themes/images/responsivemode/responsive-se-resizer@2x.png)
     skin/images/responsivemode/responsive-vertical-resizer.png (themes/images/responsivemode/responsive-vertical-resizer.png)
     skin/images/responsivemode/responsive-vertical-resizer@2x.png (themes/images/responsivemode/responsive-vertical-resizer@2x.png)
     skin/images/responsivemode/responsive-horizontal-resizer.png (themes/images/responsivemode/responsive-horizontal-resizer.png)
@@ -337,16 +326,17 @@ devtools.jar:
     skin/tooltip/arrow-vertical-light@2x.png (themes/tooltip/arrow-vertical-light@2x.png)
     skin/images/reload.svg (themes/images/reload.svg)
     skin/images/security-state-broken.svg (themes/images/security-state-broken.svg)
     skin/images/security-state-insecure.svg (themes/images/security-state-insecure.svg)
     skin/images/security-state-local.svg (themes/images/security-state-local.svg)
     skin/images/security-state-secure.svg (themes/images/security-state-secure.svg)
     skin/images/security-state-weak.svg (themes/images/security-state-weak.svg)
     skin/images/diff.svg (themes/images/diff.svg)
+    skin/images/import.svg (themes/images/import.svg)
     skin/images/pane-collapse.svg (themes/images/pane-collapse.svg)
     skin/images/pane-expand.svg (themes/images/pane-expand.svg)
 
     # Firebug Theme
     skin/images/firebug/read-only.svg (themes/images/firebug/read-only.svg)
     skin/images/firebug/spinner.png (themes/images/firebug/spinner.png)
     skin/images/firebug/twisty-closed-firebug.svg (themes/images/firebug/twisty-closed-firebug.svg)
     skin/images/firebug/twisty-open-firebug.svg (themes/images/firebug/twisty-open-firebug.svg)
--- a/devtools/client/memory/components/toolbar.js
+++ b/devtools/client/memory/components/toolbar.js
@@ -267,19 +267,17 @@ module.exports = createClass({
           ),
 
           dom.button(
             {
               id: "import-snapshot",
               className: "devtools-toolbarbutton import-snapshot devtools-button",
               onClick: onImportClick,
               title: L10N.getStr("import-snapshot"),
-              "data-text-only": true,
-            },
-            L10N.getStr("import-snapshot")
+            }
           )
         ),
 
         dom.label(
           {
             id: "record-allocation-stacks-label",
             title: L10N.getStr("checkbox.recordAllocationStacks.tooltip"),
           },
--- a/devtools/client/performance/performance.xul
+++ b/devtools/client/performance/performance.xul
@@ -87,17 +87,17 @@
           <toolbarbutton id="clear-button"
                          class="devtools-toolbarbutton devtools-clear-icon"
                          tooltiptext="&performanceUI.clearButton;"/>
           <toolbarbutton id="main-record-button"
                          class="devtools-toolbarbutton record-button"
                          tooltiptext="&performanceUI.recordButton.tooltip;"/>
           <toolbarbutton id="import-button"
                          class="devtools-toolbarbutton"
-                         label="&performanceUI.importButton;"/>
+                         tooltiptext="&performanceUI.importButton;"/>
         </hbox>
       </toolbar>
       <vbox id="recordings-list" class="theme-sidebar" flex="1"/>
     </vbox>
 
     <!-- Main panel content -->
     <vbox id="performance-pane" flex="1">
 
--- a/devtools/client/shared/test/browser_mdn-docs-01.js
+++ b/devtools/client/shared/test/browser_mdn-docs-01.js
@@ -15,22 +15,18 @@
  *
  * In this file we test:
  * - the initial state of the document before the docs have loaded
  * - the state of the document after the docs have loaded
  */
 
 "use strict";
 
-const {CssDocsTooltip} = require("devtools/client/shared/widgets/Tooltip");
 const {setBaseCssDocsUrl, MdnDocsWidget} = require("devtools/client/shared/widgets/MdnDocsWidget");
 
-// frame to load the tooltip into
-const MDN_DOCS_TOOLTIP_FRAME = "chrome://devtools/content/shared/widgets/mdn-docs-frame.xhtml";
-
 /**
  * Test properties
  *
  * In the real tooltip, a CSS property name is used to look up an MDN page
  * for that property.
  * In the test code, the names defined here is used to look up a page
  * served by the test server.
  */
@@ -40,24 +36,26 @@ const BASIC_EXPECTED_SUMMARY = "A summar
 const BASIC_EXPECTED_SYNTAX = [{type: "comment", text: "/* The part we want   */"},
                                {type: "text", text: "\n"},
                                {type: "property-name", text: "this"},
                                {type: "text", text: ":"},
                                {type: "text", text: " "},
                                {type: "property-value", text: "is-the-part-we-want"},
                                {type: "text", text: ";"}];
 
-const URI_PARAMS = "?utm_source=mozilla&utm_medium=firefox-inspector&utm_campaign=default";
+const URI_PARAMS =
+  "?utm_source=mozilla&utm_medium=firefox-inspector&utm_campaign=default";
 
 add_task(function* () {
   setBaseCssDocsUrl(TEST_URI_ROOT);
 
   yield addTab("about:blank");
-  let [host, win, doc] = yield createHost("bottom", MDN_DOCS_TOOLTIP_FRAME);
-  let widget = new MdnDocsWidget(win.document);
+  let [host, win] = yield createHost("bottom", "data:text/html," +
+    "<div class='mdn-container'></div>");
+  let widget = new MdnDocsWidget(win.document.querySelector("div"));
 
   yield testTheBasics(widget);
 
   host.destroy();
   gBrowser.removeCurrentTab();
 });
 
 /**
--- a/devtools/client/shared/test/browser_mdn-docs-02.js
+++ b/devtools/client/shared/test/browser_mdn-docs-02.js
@@ -16,21 +16,20 @@
  * error conditions like parts of the document being missing.
  *
  * We also test that the tooltip properly handles the case where the page
  * doesn't exist at all.
  */
 
 "use strict";
 
-const {CssDocsTooltip} = require("devtools/client/shared/widgets/Tooltip");
-const {setBaseCssDocsUrl, MdnDocsWidget} = require("devtools/client/shared/widgets/MdnDocsWidget");
-
-// frame to load the tooltip into
-const MDN_DOCS_TOOLTIP_FRAME = "chrome://devtools/content/shared/widgets/mdn-docs-frame.xhtml";
+const {
+  setBaseCssDocsUrl,
+  MdnDocsWidget
+} = require("devtools/client/shared/widgets/MdnDocsWidget");
 
 const BASIC_EXPECTED_SUMMARY = "A summary of the property.";
 const BASIC_EXPECTED_SYNTAX = [{type: "comment", text: "/* The part we want   */"},
                                {type: "text", text: "\n"},
                                {type: "property-name", text: "this"},
                                {type: "text", text: ":"},
                                {type: "text", text: " "},
                                {type: "property-value", text: "is-the-part-we-want"},
@@ -95,43 +94,33 @@ const TEST_DATA = [{
   }
 }
 ];
 
 add_task(function* () {
   setBaseCssDocsUrl(TEST_URI_ROOT);
 
   yield addTab("about:blank");
-  let [host, win, doc] = yield createHost("bottom", MDN_DOCS_TOOLTIP_FRAME);
-  let widget = new MdnDocsWidget(win.document);
+  let [host, win] = yield createHost("bottom", "data:text/html," +
+    "<div class='mdn-container'></div>");
+  let widget = new MdnDocsWidget(win.document.querySelector("div"));
 
   for (let {desc, docsPageUrl, expectedContents} of TEST_DATA) {
     info(desc);
     yield widget.loadCssDocs(docsPageUrl);
     checkTooltipContents(widget.elements, expectedContents);
   }
   host.destroy();
   gBrowser.removeCurrentTab();
 });
 
-function* testNonExistentPage(widget) {
-  info("Test a property for which we don't have a page");
-  yield widget.loadCssDocs("i-dont-exist.html");
-  checkTooltipContents(widget.elements, {
-    propertyName: "i-dont-exist.html",
-    summary: ERROR_MESSAGE,
-    syntax: ""
-  });
-}
-
 /*
  * Utility function to check content of the tooltip.
  */
 function checkTooltipContents(doc, expected) {
-
   is(doc.heading.textContent,
      expected.propertyName,
      "Property name is correct");
 
   is(doc.summary.textContent,
      expected.summary,
      "Summary is correct");
 
--- a/devtools/client/shared/widgets/MdnDocsWidget.js
+++ b/devtools/client/shared/widgets/MdnDocsWidget.js
@@ -24,16 +24,18 @@
 
 "use strict";
 
 const Services = require("Services");
 const defer = require("devtools/shared/defer");
 const {getCSSLexer} = require("devtools/shared/css-lexer");
 const {gDevTools} = require("devtools/client/framework/devtools");
 
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
 // Parameters for the XHR request
 // see https://developer.mozilla.org/en-US/docs/MDN/Kuma/API#Document_parameters
 const XHR_PARAMS = "?raw&macros";
 // URL for the XHR request
 var XHR_CSS_URL = "https://developer.mozilla.org/en-US/docs/Web/CSS/";
 
 // Parameters for the link to MDN in the tooltip, so
 // so we know which MDN visits come from this feature
@@ -82,17 +84,17 @@ function appendSyntaxHighlightedCSS(cssT
   let doc = parentElement.ownerDocument;
   let identClass = PROPERTY_NAME_COLOR;
   let lexer = getCSSLexer(cssText);
 
   /**
    * Create a SPAN node with the given text content and class.
    */
   function createStyledNode(textContent, className) {
-    let newNode = doc.createElement("span");
+    let newNode = doc.createElementNS(XHTML_NS, "span");
     newNode.classList.add(className);
     newNode.textContent = textContent;
     return newNode;
   }
 
   /**
    * If the symbol is ":", we will expect the next
    * "ident" token to be part of a property value.
@@ -219,42 +221,49 @@ function getCssDocs(cssProperty) {
 
   return deferred.promise;
 }
 
 exports.getCssDocs = getCssDocs;
 
 /**
  * The MdnDocsWidget is used by tooltip code that needs to display docs
- * from MDN in a tooltip. The tooltip code loads a document that contains the
- * basic structure of a docs tooltip (loaded from mdn-docs-frame.xhtml),
- * and passes this document into the widget's constructor.
+ * from MDN in a tooltip.
  *
  * In the constructor, the widget does some general setup that's not
  * dependent on the particular item we need docs for.
  *
  * After that, when the tooltip code needs to display docs for an item, it
  * asks the widget to retrieve the docs and update the document with them.
  *
- * @param {Document} tooltipDocument
- * A DOM document. The widget expects the document to have a particular
- * structure.
+ * @param {Element} tooltipContainer
+ * A DOM element where the MdnDocs widget markup should be created.
  */
-function MdnDocsWidget(tooltipDocument) {
+function MdnDocsWidget(tooltipContainer) {
+  tooltipContainer.innerHTML =
+    `<header>
+       <h1 class="mdn-property-name theme-fg-color5"></h1>
+     </header>
+     <div class="mdn-property-info">
+       <div class="mdn-summary"></div>
+       <pre class="mdn-syntax devtools-monospace"></pre>
+     </div>
+     <footer>
+       <a class="mdn-visit-page theme-link" href="#">Visit MDN (placeholder)</a>
+     </footer>`;
+
   // fetch all the bits of the document that we will manipulate later
   this.elements = {
-    heading: tooltipDocument.getElementById("property-name"),
-    summary: tooltipDocument.getElementById("summary"),
-    syntax: tooltipDocument.getElementById("syntax"),
-    info: tooltipDocument.getElementById("property-info"),
-    linkToMdn: tooltipDocument.getElementById("visit-mdn-page")
+    heading: tooltipContainer.querySelector(".mdn-property-name"),
+    summary: tooltipContainer.querySelector(".mdn-summary"),
+    syntax: tooltipContainer.querySelector(".mdn-syntax"),
+    info: tooltipContainer.querySelector(".mdn-property-info"),
+    linkToMdn: tooltipContainer.querySelector(".mdn-visit-page")
   };
 
-  this.doc = tooltipDocument;
-
   // get the localized string for the link text
   this.elements.linkToMdn.textContent =
     l10n.strings.GetStringFromName("docsTooltip.visitMDN");
 
   // listen for clicks and open in the browser window instead
   let mainWindow = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
   this.elements.linkToMdn.addEventListener("click", function (e) {
     e.stopPropagation();
@@ -352,17 +361,16 @@ MdnDocsWidget.prototype = {
     initializeDocument(propertyName);
     getCssDocs(propertyName).then(finalizeDocument, gotError);
 
     return deferred.promise;
   },
 
   destroy: function () {
     this.elements = null;
-    this.doc = null;
   }
 };
 
 /**
  * L10N utility class
  */
 function L10N() {}
 L10N.prototype = {};
--- a/devtools/client/shared/widgets/Tooltip.js
+++ b/devtools/client/shared/widgets/Tooltip.js
@@ -4,17 +4,16 @@
 
 "use strict";
 
 const {Ci} = require("chrome");
 const defer = require("devtools/shared/defer");
 const {Spectrum} = require("devtools/client/shared/widgets/Spectrum");
 const {CubicBezierWidget} =
       require("devtools/client/shared/widgets/CubicBezierWidget");
-const {MdnDocsWidget} = require("devtools/client/shared/widgets/MdnDocsWidget");
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
 const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipToggle");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {colorUtils} = require("devtools/client/shared/css-color");
 const Heritage = require("sdk/core/heritage");
 const {Eyedropper} = require("devtools/client/eyedropper/eyedropper");
 const {gDevTools} = require("devtools/client/framework/devtools");
 const Services = require("Services");
@@ -28,19 +27,17 @@ loader.lazyRequireGetter(this, "clearNam
 loader.lazyRequireGetter(this, "setNamedTimeout", "devtools/client/shared/widgets/view-helpers", true);
 
 XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
   "resource://devtools/client/shared/widgets/VariablesView.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "VariablesViewController",
   "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
 
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
-const MDN_DOCS_FRAME = "chrome://devtools/content/shared/widgets/mdn-docs-frame.xhtml";
 const ESCAPE_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE;
-const RETURN_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_RETURN;
 const POPUP_EVENTS = ["shown", "hidden", "showing", "hiding"];
 
 /**
  * Tooltip widget.
  *
  * This widget is intended at any tool that may need to show rich content in the
  * form of floating panels.
  * A common use case is image previewing in the CSS rule view, but more complex
@@ -548,42 +545,16 @@ Tooltip.prototype = {
 
     // load the document from url into the iframe
     iframe.setAttribute("src", url);
 
     // Put the iframe in the tooltip
     this.content = iframe;
 
     return def.promise;
-  },
-
-  /**
-   * Set the content of this tooltip to the MDN docs widget.
-   *
-   * This is called when the tooltip is first constructed.
-   *
-   * @return {promise} A promise which is resolved with an MdnDocsWidget.
-   *
-   * It loads the tooltip's structure from a separate XHTML file
-   * into an iframe. When the iframe is loaded it constructs
-   * an MdnDocsWidget and passes that into resolve.
-   *
-   * The caller can use the MdnDocsWidget to update the tooltip's
-   * UI with new content each time the tooltip is shown.
-   */
-  setMdnDocsContent: function () {
-    let dimensions = {width: "410", height: "300"};
-    return this.setIFrameContent(dimensions, MDN_DOCS_FRAME).then(onLoaded);
-
-    function onLoaded(iframe) {
-      let win = iframe.contentWindow.wrappedJSObject;
-      // create an MdnDocsWidget, initializing it with the content document
-      let widget = new MdnDocsWidget(win.document);
-      return widget;
-    }
   }
 };
 
 /**
  * Base class for all (color, gradient, ...)-swatch based value editors inside
  * tooltips
  *
  * @param {Toolbox} toolbox
@@ -1000,55 +971,16 @@ Heritage.extend(SwatchBasedEditorTooltip
     this.widget.then(widget => {
       widget.off("updated", this._onUpdate);
       widget.destroy();
     });
   }
 });
 
 /**
- * Tooltip for displaying docs for CSS properties from MDN.
- *
- * @param {XULDocument} doc
- */
-function CssDocsTooltip(doc) {
-  this.tooltip = new Tooltip(doc, {
-    consumeOutsideClick: true,
-    closeOnKeys: [ESCAPE_KEYCODE, RETURN_KEYCODE],
-    noAutoFocus: false
-  });
-  this.widget = this.tooltip.setMdnDocsContent();
-}
-
-module.exports.CssDocsTooltip = CssDocsTooltip;
-
-CssDocsTooltip.prototype = {
-  /**
-   * Load CSS docs for the given property,
-   * then display the tooltip.
-   */
-  show: function (anchor, propertyName) {
-    function loadCssDocs(widget) {
-      return widget.loadCssDocs(propertyName);
-    }
-
-    this.widget.then(loadCssDocs);
-    this.tooltip.show(anchor, "topcenter bottomleft");
-  },
-
-  hide: function () {
-    this.tooltip.hide();
-  },
-
-  destroy: function () {
-    this.tooltip.destroy();
-  }
-};
-
-/**
  * The swatch-based css filter tooltip class is a specific class meant to be
  * used along with rule-view's generated css filter swatches.
  * It extends the parent SwatchBasedEditorTooltip class.
  * It just wraps a standard Tooltip and sets its content with an instance of a
  * CSSFilterEditorWidget.
  *
  * @param {Toolbox} toolbox
  *        The devtools toolbox, needed to get the devtools main window.
deleted file mode 100644
--- a/devtools/client/shared/widgets/mdn-docs-frame.xhtml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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/. -->
-<!DOCTYPE html>
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
-  <link rel="stylesheet" href="chrome://devtools/content/shared/widgets/mdn-docs.css" type="text/css"/>
-  <script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"/>
-</head>
-<body class="theme-body">
-
-  <div id = "container">
-
-    <header>
-      <h1 id="property-name" class="theme-fg-color5"></h1>
-    </header>
-
-    <div id="property-info">
-      <div id="summary"></div>
-      <pre id="syntax" class="devtools-monospace"></pre>
-    </div>
-
-    <footer>
-      <a id="visit-mdn-page" class="theme-link" href="#">Visit MDN (placeholder)</a>
-    </footer>
-
-  </div>
-
-</body>
-</html>
\ No newline at end of file
--- a/devtools/client/shared/widgets/mdn-docs.css
+++ b/devtools/client/shared/widgets/mdn-docs.css
@@ -1,41 +1,39 @@
 /* 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/. */
 
-#visit-mdn-page {
-  display: inline-block;
-  padding: 1em 0;
-}
-
-html, body, #container {
-  height: 100%;
-  width: 100%;
-  margin: 0;
-  padding: 0;
-}
-
-#container {
+.mdn-container {
+  height: 300px;
+  margin: 4px;
+  overflow: auto;
+  box-sizing: border-box;
   display: flex;
   flex-direction: column;
 }
 
-header, footer {
+.mdn-container header,
+.mdn-container footer {
   flex: 1;
   padding: 0 1em;
 }
 
-#property-info {
+.mdn-property-info {
   flex: 10;
   padding: 0 1em;
   overflow: auto;
   transition: opacity 400ms ease-in;
 }
 
-#syntax {
+.mdn-syntax {
   margin-top: 1em;
 }
 
 .devtools-throbber {
+  align-self: center;
   opacity: 0;
-  align-self: center;
 }
+
+.mdn-visit-page {
+  display: inline-block;
+  padding: 1em 0;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/CssDocsTooltip.js
@@ -0,0 +1,95 @@
+/* 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/. */
+
+"use strict";
+
+const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
+const {MdnDocsWidget} = require("devtools/client/shared/widgets/MdnDocsWidget");
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+loader.lazyRequireGetter(this, "KeyShortcuts",
+  "devtools/client/shared/key-shortcuts", true);
+
+const TOOLTIP_WIDTH = 418;
+const TOOLTIP_HEIGHT = 308;
+
+/**
+ * Tooltip for displaying docs for CSS properties from MDN.
+ *
+ * @param {Toolbox} toolbox
+ *        Toolbox used to create the tooltip.
+ */
+function CssDocsTooltip(toolbox) {
+  this.tooltip = new HTMLTooltip(toolbox, {
+    type: "arrow",
+    consumeOutsideClicks: true,
+    autofocus: true,
+    useXulWrapper: true,
+    stylesheet: "chrome://devtools/content/shared/widgets/mdn-docs.css",
+  });
+  this.widget = this.setMdnDocsContent();
+
+  // Initialize keyboard shortcuts
+  this.shortcuts = new KeyShortcuts({ window: toolbox.doc.defaultView });
+  this._onShortcut = this._onShortcut.bind(this);
+
+  this.shortcuts.on("Escape", this._onShortcut);
+  this.shortcuts.on("Return", this._onShortcut);
+}
+
+module.exports.CssDocsTooltip = CssDocsTooltip;
+
+CssDocsTooltip.prototype = {
+  /**
+   * Load CSS docs for the given property,
+   * then display the tooltip.
+   */
+  show: function (anchor, propertyName) {
+    this.tooltip.once("shown", () => {
+      this.widget.loadCssDocs(propertyName);
+    });
+    this.tooltip.show(anchor);
+  },
+
+  hide: function () {
+    this.tooltip.hide();
+  },
+
+  _onShortcut: function (shortcut, event) {
+    if (!this.tooltip.isVisible()) {
+      return;
+    }
+
+    event.stopPropagation();
+    if (shortcut === "Return") {
+      // If user is pressing return, do not prevent default and delay hiding the tooltip
+      // in case the focus is on the "Visit MDN page" link.
+      this.tooltip.doc.defaultView.setTimeout(this.hide.bind(this), 0);
+    } else {
+      // For any other key, preventDefault() and hide straight away.
+      event.preventDefault();
+      this.hide();
+    }
+  },
+
+  /**
+   * Set the content of this tooltip to the MDN docs widget. This is called when the
+   * tooltip is first constructed.
+   * The caller can use the MdnDocsWidget to update the tooltip's  UI with new content
+   * each time the tooltip is shown.
+   *
+   * @return {MdnDocsWidget} the created MdnDocsWidget instance.
+   */
+  setMdnDocsContent: function () {
+    let container = this.tooltip.doc.createElementNS(XHTML_NS, "div");
+    container.setAttribute("class", "mdn-container theme-body");
+    this.tooltip.setContent(container, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
+    return new MdnDocsWidget(container);
+  },
+
+  destroy: function () {
+    this.shortcuts.destroy();
+    this.tooltip.destroy();
+  }
+};
--- a/devtools/client/shared/widgets/tooltip/EventTooltipHelper.js
+++ b/devtools/client/shared/widgets/tooltip/EventTooltipHelper.js
@@ -37,17 +37,20 @@ function setEventTooltip(tooltip, eventL
   let eventTooltip = new EventTooltip(tooltip, eventListenerInfos, toolbox);
   eventTooltip.init();
 }
 
 function EventTooltip(tooltip, eventListenerInfos, toolbox) {
   this._tooltip = tooltip;
   this._eventListenerInfos = eventListenerInfos;
   this._toolbox = toolbox;
-  this._tooltip.eventEditors = new WeakMap();
+  this._eventEditors = new WeakMap();
+
+  // Used in tests: add a reference to the EventTooltip instance on the HTMLTooltip.
+  this._tooltip.eventTooltip = this;
 
   this._headerClicked = this._headerClicked.bind(this);
   this._debugClicked = this._debugClicked.bind(this);
   this.destroy = this.destroy.bind(this);
 }
 
 EventTooltip.prototype = {
   init: function () {
@@ -140,17 +143,17 @@ EventTooltip.prototype = {
         dom0.textContent = level;
         dom0.setAttribute("title", level);
         attributesBox.appendChild(dom0);
       }
 
       // Content
       let content = doc.createElementNS(XHTML_NS, "div");
       let editor = new Editor(config);
-      this._tooltip.eventEditors.set(content, {
+      this._eventEditors.set(content, {
         editor: editor,
         handler: listener.handler,
         searchString: listener.searchString,
         uri: listener.origin,
         dom0: listener.DOM0,
         appended: false
       });
 
@@ -187,32 +190,32 @@ EventTooltip.prototype = {
       for (let node of contentNodes) {
         if (node !== content) {
           node.removeAttribute("open");
         }
       }
 
       content.setAttribute("open", "");
 
-      let eventEditors = this._tooltip.eventEditors.get(content);
+      let eventEditor = this._eventEditors.get(content);
 
-      if (eventEditors.appended) {
+      if (eventEditor.appended) {
         return;
       }
 
-      let {editor, handler} = eventEditors;
+      let {editor, handler} = eventEditor;
 
       let iframe = doc.createElementNS(XHTML_NS, "iframe");
       iframe.setAttribute("style", "width: 100%; height: 100%; border-style: none;");
 
       editor.appendTo(content, iframe).then(() => {
         let tidied = beautify.js(handler, { "indent_size": 2 });
         editor.setText(tidied);
 
-        eventEditors.appended = true;
+        eventEditor.appended = true;
 
         let container = header.parentElement.getBoundingClientRect();
         if (header.getBoundingClientRect().top < container.top) {
           header.scrollIntoView(true);
         } else if (content.getBoundingClientRect().bottom > container.bottom) {
           content.scrollIntoView(false);
         }
 
@@ -220,17 +223,17 @@ EventTooltip.prototype = {
       });
     }
   },
 
   _debugClicked: function (event) {
     let header = event.currentTarget;
     let content = header.nextElementSibling;
 
-    let {uri, searchString, dom0} = this._tooltip.eventEditors.get(content);
+    let {uri, searchString, dom0} = this._eventEditors.get(content);
 
     if (uri && uri !== "?") {
       // Save a copy of toolbox as it will be set to null when we hide the tooltip.
       let toolbox = this._toolbox;
 
       this._tooltip.hide();
 
       uri = uri.replace(/"/g, "");
@@ -285,21 +288,22 @@ EventTooltip.prototype = {
 
   destroy: function () {
     if (this._tooltip) {
       this._tooltip.off("hidden", this.destroy);
 
       let boxes = this.container.querySelectorAll(".event-tooltip-content-box");
 
       for (let box of boxes) {
-        let {editor} = this._tooltip.eventEditors.get(box);
+        let {editor} = this._eventEditors.get(box);
         editor.destroy();
       }
 
-      this._tooltip.eventEditors = null;
+      this._eventEditors = null;
+      this._tooltip.eventTooltip = null;
     }
 
     let headerNodes = this.container.querySelectorAll(".event-header");
 
     for (let node of headerNodes) {
       node.removeEventListener("click", this._headerClicked);
     }
 
--- a/devtools/client/shared/widgets/tooltip/moz.build
+++ b/devtools/client/shared/widgets/tooltip/moz.build
@@ -1,11 +1,12 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 DevToolsModules(
+    'CssDocsTooltip.js',
     'EventTooltipHelper.js',
     'ImageTooltipHelper.js',
     'TooltipToggle.js',
 )
--- a/devtools/client/styleeditor/styleeditor.xul
+++ b/devtools/client/styleeditor/styleeditor.xul
@@ -92,22 +92,20 @@
 
     <xul:box class="splitview-root devtools-responsive-container" context="sidebar-context">
       <xul:box class="splitview-controller">
         <xul:box class="splitview-main">
           <xul:toolbar class="devtools-toolbar">
              <xul:hbox class="devtools-toolbarbutton-group">
               <xul:toolbarbutton class="style-editor-newButton devtools-toolbarbutton"
                           accesskey="&newButton.accesskey;"
-                          tooltiptext="&newButton.tooltip;"
-                          label="&newButton.label;"/>
+                          tooltiptext="&newButton.tooltip;"/>
               <xul:toolbarbutton class="style-editor-importButton devtools-toolbarbutton"
                           accesskey="&importButton.accesskey;"
-                          tooltiptext="&importButton.tooltip;"
-                          label="&importButton.label;"/>
+                          tooltiptext="&importButton.tooltip;"/>
             </xul:hbox>
             <xul:spacer/>
             <xul:toolbarbutton id="style-editor-options"
                         class="devtools-toolbarbutton devtools-option-toolbarbutton"
                         tooltiptext="&optionsButton.tooltip;"
                         popup="style-editor-options-popup"/>
           </xul:toolbar>
         </xul:box>
--- a/devtools/client/themes/animationinspector.css
+++ b/devtools/client/themes/animationinspector.css
@@ -2,44 +2,35 @@
  * 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/. */
 
 /* Animation-inspector specific theme variables */
 
 .theme-dark {
   --even-animation-timeline-background-color: rgba(255,255,255,0.03);
   --command-pick-image: url(chrome://devtools/skin/images/command-pick.svg);
-  --pause-image: url(chrome://devtools/skin/images/debugger-pause.png);
-  --pause-image-2x: url(chrome://devtools/skin/images/debugger-pause@2x.png);
-  --rewind-image: url(chrome://devtools/skin/images/rewind.png);
-  --rewind-image-2x: url(chrome://devtools/skin/images/rewind@2x.png);
-  --play-image: url(chrome://devtools/skin/images/debugger-play.png);
-  --play-image-2x: url(chrome://devtools/skin/images/debugger-play@2x.png);
+  --pause-image: url(chrome://devtools/skin/images/pause.svg);
+  --rewind-image: url(chrome://devtools/skin/images/rewind.svg);
+  --play-image: url(chrome://devtools/skin/images/play.svg);
 }
 
 .theme-light {
   --even-animation-timeline-background-color: rgba(128,128,128,0.03);
   --command-pick-image: url(chrome://devtools/skin/images/command-pick.svg);
-  --pause-image: url(chrome://devtools/skin/images/debugger-pause.png);
-  --pause-image-2x: url(chrome://devtools/skin/images/debugger-pause@2x.png);
-  --rewind-image: url(chrome://devtools/skin/images/rewind.png);
-  --rewind-image-2x: url(chrome://devtools/skin/images/rewind@2x.png);
-  --play-image: url(chrome://devtools/skin/images/debugger-play.png);
-  --play-image-2x: url(chrome://devtools/skin/images/debugger-play@2x.png);
+  --pause-image: url(chrome://devtools/skin/images/pause.svg);
+  --rewind-image: url(chrome://devtools/skin/images/rewind.svg);
+  --play-image: url(chrome://devtools/skin/images/play.svg);
 }
 
 .theme-firebug {
   --even-animation-timeline-background-color: rgba(128,128,128,0.03);
   --command-pick-image: url(chrome://devtools/skin/images/firebug/command-pick.svg);
   --pause-image: url(chrome://devtools/skin/images/firebug/pause.svg);
-  --pause-image-2x: url(chrome://devtools/skin/images/firebug/pause.svg);
   --rewind-image: url(chrome://devtools/skin/images/firebug/rewind.svg);
-  --rewind-image-2x: url(chrome://devtools/skin/images/firebug/rewind.svg);
   --play-image: url(chrome://devtools/skin/images/firebug/play.svg);
-  --play-image-2x: url(chrome://devtools/skin/images/firebug/play.svg);
 }
 
 :root {
   /* How high should toolbars be */
   --toolbar-height: 20px;
   /* How wide should the sidebar be (should be wide enough to contain long
      property names like 'border-bottom-right-radius' without ellipsis) */
   --timeline-sidebar-width: 200px;
@@ -179,30 +170,16 @@ body {
 #rewind-timeline::before {
   background-image: var(--rewind-image);
 }
 
 .pause-button.paused::before {
   background-image: var(--play-image);
 }
 
-@media (min-resolution: 1.1dppx) {
-  .pause-button::before {
-    background-image: var(--pause-image-2x);
-  }
-
-  .pause-button.paused::before {
-    background-image: var(--play-image-2x);
-  }
-
-  #rewind-timeline::before {
-    background-image: var(--rewind-image-2x);
-  }
-}
-
 #timeline-rate select.devtools-button {
   -moz-appearance: none;
   text-align: center;
   font-family: inherit;
   color: var(--theme-body-color);
   font-size: 1em;
   position: absolute;
   top: 0;
--- a/devtools/client/themes/canvasdebugger.css
+++ b/devtools/client/themes/canvasdebugger.css
@@ -46,16 +46,20 @@
 #snapshots-pane {
   border-inline-end: 1px solid var(--theme-splitter-color);
 }
 
 #record-snapshot {
   list-style-image: url("chrome://devtools/skin/images/profiler-stopwatch.svg");
 }
 
+#import-snapshot {
+  list-style-image: url("images/import.svg");
+}
+
 /* Snapshots items */
 
 .snapshot-item-thumbnail {
   image-rendering: -moz-crisp-edges;
   background-image: var(--checkerboard-pattern);
   background-size: 12px 12px, 12px 12px;
   background-position: 0px 0px, 6px 6px;
   background-repeat: repeat, repeat;
@@ -104,47 +108,29 @@
 
 #snapshots-list .selected label {
   /* Text inside a selected item should not be custom colored. */
   color: inherit !important;
 }
 
 /* Debugging pane controls */
 #resume {
-  list-style-image: url(images/debugger-play.png);
+  list-style-image: url(images/play.svg);
 }
 
 #step-over {
-  list-style-image: url(images/debugger-step-over.png);
+  list-style-image: url(images/debugger-step-over.svg);
 }
 
 #step-in {
-  list-style-image: url(images/debugger-step-in.png);
+  list-style-image: url(images/debugger-step-in.svg);
 }
 
 #step-out {
-  list-style-image: url(images/debugger-step-out.png);
-}
-
-@media (min-resolution: 1.1dppx) {
-  #resume {
-    list-style-image: url(images/debugger-play@2x.png);
-  }
-
-  #step-over {
-    list-style-image: url(images/debugger-step-over@2x.png);
-  }
-
-  #step-in {
-    list-style-image: url(images/debugger-step-in@2x.png);
-  }
-
-  #step-out {
-    list-style-image: url(images/debugger-step-out@2x.png);
-  }
+  list-style-image: url(images/debugger-step-out.svg);
 }
 
 #calls-slider {
   padding-inline-end: 24px;
 }
 
 #calls-slider .scale-slider {
   margin: 0;
@@ -191,26 +177,18 @@
   padding-bottom: 2px;
   border-inline-end: 1px solid var(--theme-splitter-color);
   margin-inline-end: 6px;
   background-color: var(--theme-sidebar-background);
   color: var(--theme-content-color3);
 }
 
 .selected .call-item-gutter {
-  background-image: url("images/editor-debug-location.png");
-  background-repeat: no-repeat;
-  background-position: 6px center;
-  background-size: 12px;
-}
-
-@media (min-resolution: 1.1dppx) {
-  .selected .call-item-gutter {
-    background-image: url("images/editor-debug-location@2x.png");
-  }
+  background-color: #2cbb0f;
+  color: white;
 }
 
 .call-item-index {
   text-align: end;
 }
 
 .call-item-context {
   color: var(--theme-highlight-orange);
--- a/devtools/client/themes/debugger.css
+++ b/devtools/client/themes/debugger.css
@@ -89,66 +89,44 @@
 
 /* Sources toolbar */
 
 #sources-toolbar > .devtools-toolbarbutton,
 #sources-controls > .devtools-toolbarbutton {
   min-width: 32px;
 }
 
-:root:not(.theme-firebug) #sources-toolbar .devtools-toolbarbutton:not([label]) {
-  -moz-image-region: rect(0,16px,16px,0);
-}
-
-@media (min-resolution: 1.1dppx) {
-  :root:not(.theme-firebug) #sources-toolbar .devtools-toolbarbutton:not([label]) {
-    -moz-image-region: rect(0,32px,32px,0);
-  }
-}
-
 #black-box {
-  list-style-image: url(images/debugger-blackbox.png);
-}
-
-@media (min-resolution: 1.1dppx) {
-  #black-box {
-    list-style-image: url(images/debugger-blackbox@2x.png);
-  }
+  list-style-image: url(images/debugger-blackbox.svg);
 }
 
 .theme-firebug #black-box {
   list-style-image: url(images/firebug/debugger-blackbox.svg);
 }
 
 #pretty-print {
-  list-style-image: url(images/debugger-prettyprint.png);
-}
-
-@media (min-resolution: 1.1dppx) {
-  #pretty-print {
-    list-style-image: url(images/debugger-prettyprint@2x.png);
-  }
+  list-style-image: url(images/debugger-prettyprint.svg);
 }
 
 .theme-firebug #pretty-print {
   list-style-image: url(images/firebug/debugger-prettyprint.svg);
 }
 
 #toggle-breakpoints {
   list-style-image: url(images/debugger-toggleBreakpoints.svg);
-  -moz-image-region: rect(0,32px,16px,16px) !important;
+  -moz-image-region: rect(0,32px,16px,16px);
 }
 
 .theme-firebug #toggle-breakpoints {
   list-style-image: url(images/firebug/debugger-toggleBreakpoints.svg);
-  -moz-image-region: unset !important;
+  -moz-image-region: unset;
 }
 
 #toggle-breakpoints[checked] {
-  -moz-image-region: rect(0,16px,16px,0) !important;
+  -moz-image-region: rect(0,16px,16px,0);
 }
 
 #toggle-breakpoints[checked] > image {
   /* This button has a special checked image, don't make it blue */
   filter: none;
 }
 
 #toggle-promise-debugger {
@@ -167,27 +145,19 @@
   display: none;
 }
 
 /* Debugger unblackbox button */
 
 #black-boxed-message-button > .button-box > .button-icon {
   width: 16px;
   height: 16px;
-  background-image: url(images/debugger-blackbox.png);
+  background-image: url(images/debugger-blackbox.svg);
   background-position: 0 0;
-  background-size: 32px 16px;
-  background-repeat: no-repeat;
-  margin-inline-end: 5px;
-}
-
-@media (min-resolution: 1.1dppx) {
-  #black-boxed-message-button > .button-box > .button-icon {
-    background-image: url(images/debugger-blackbox@2x.png);
-  }
+  background-size: cover;
 }
 
 /* Black box message and source progress meter */
 
 #black-boxed-message,
 #source-progress-container {
   /* Prevent the container deck from aquiring the size from this message. */
   min-width: 1px;
@@ -567,69 +537,45 @@
 
 .theme-light .dbg-results-line-contents-string[match=true] {
   color: var(--theme-body-color);
 }
 
 /* Toolbar controls */
 
 #resume {
-  list-style-image: url(images/debugger-pause.png);
+  list-style-image: url(images/pause.svg);
 }
 
 #resume[checked] {
-  list-style-image: url(images/debugger-play.png);
-}
-
-@media (min-resolution: 1.1dppx) {
-  #resume {
-    list-style-image: url(images/debugger-pause@2x.png);
-  }
-
-  #resume[checked] {
-    list-style-image: url(images/debugger-play@2x.png);
-  }
+  list-style-image: url(images/play.svg);
 }
 
 .theme-firebug #resume {
   list-style-image: url(images/firebug/pause.svg);
 }
 
 .theme-firebug #resume[checked] {
   list-style-image: url(images/firebug/play.svg);
 }
 
 #resume[break-on-next] {
   background: var(--theme-highlight-lightorange);
 }
 
 #step-over {
-  list-style-image: url(images/debugger-step-over.png);
+  list-style-image: url(images/debugger-step-over.svg);
 }
 
 #step-in {
-  list-style-image: url(images/debugger-step-in.png);
+  list-style-image: url(images/debugger-step-in.svg);
 }
 
 #step-out {
-  list-style-image: url(images/debugger-step-out.png);
-}
-
-@media (min-resolution: 1.1dppx) {
-  #step-over {
-    list-style-image: url(images/debugger-step-over@2x.png);
-  }
-
-  #step-in {
-    list-style-image: url(images/debugger-step-in@2x.png);
-  }
-
-  #step-out {
-    list-style-image: url(images/debugger-step-out@2x.png);
-  }
+  list-style-image: url(images/debugger-step-out.svg);
 }
 
 .theme-firebug #step-over {
   list-style-image: url(images/firebug/debugger-step-over.svg);
 }
 
 .theme-firebug #step-in {
   list-style-image: url(images/firebug/debugger-step-in.svg);
--- a/devtools/client/themes/firebug-theme.css
+++ b/devtools/client/themes/firebug-theme.css
@@ -239,15 +239,12 @@
   border-width: 1px !important;
   min-width: 24px;
 }
 
 .theme-firebug .devtools-toolbarbutton {
   min-width: 24px;
 }
 
-.theme-firebug #command-button-frames {
-  min-width: 32px;
-}
 
 .theme-firebug #element-picker {
   min-height: 21px;
 }
--- a/devtools/client/themes/images/add.svg
+++ b/devtools/client/themes/images/add.svg
@@ -1,9 +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/. -->
-<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-  <g fill="#babec3">
-    <rect x="3" y="7" width="10" height="2" />
-    <rect x="7" y="3" width="2" height="10" />
-  </g>
-</svg>
\ No newline at end of file
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M8.5 8.5V14a.5.5 0 1 1-1 0V8.5H2a.5.5 0 0 1 0-1h5.5V2a.5.5 0 0 1 1 0v5.5H14a.5.5 0 1 1 0 1H8.5z"/>
+</svg>
--- a/devtools/client/themes/images/clear.svg
+++ b/devtools/client/themes/images/clear.svg
@@ -1,7 +1,7 @@
 <!-- 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 width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#babec3">
-  <path d="M6 3h3V2c0-.003-3 0-3 0-.002 0 0 1 0 1zm-5 .5c0-.276.226-.5.494-.5h12.012c.273 0 .494.232.494.5 0 .276-.226.5-.494.5H1.494C1.22 4 1 3.768 1 3.5zM5 3V2c0-.553.444-1 1-1h3c.552 0 1 .443 1 1v1H5z"/>
-  <path d="M5 13h1V7H5v6zm4 0h1V7H9v6zm3-8v8.998c-.046.553-.45 1.002-1 1.002H4c-.55 0-.954-.456-1-1.002V5h9zm-5 8h1V7H7v6z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M5 3h3V2c0-.003-3 0-3 0-.002 0 0 1 0 1zm-5 .5A.5.5 0 0 1 .494 3h12.012a.5.5 0 0 1 0 1H.494A.502.502 0 0 1 0 3.5zM4 3V2c0-.553.444-1 1-1h3c.552 0 1 .443 1 1v1H4zM5 11V6a.5.5 0 0 0-1 0v5a.5.5 0 1 0 1 0zM7 11V6a.5.5 0 0 0-1 0v5a.5.5 0 1 0 1 0zM9 11V6a.5.5 0 0 0-1 0v5a.5.5 0 1 0 1 0z"/>
+  <path d="M3 4v9h7V4H3zm0-1h7a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z"/>
 </svg>
--- a/devtools/client/themes/images/command-noautohide.svg
+++ b/devtools/client/themes/images/command-noautohide.svg
@@ -1,8 +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/. -->
-<svg width="16" height="16" viewBox="0 0 30 29" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
-  <g fill-rule="evenodd">
-    <path d="M3 1v11c0-1.104-.896-2-2-2h11c-1.104 0-2 .896-2 2V1c0 1.104.896 2 2 2H1c1.104 0 2-.896 2-2zM0 1c0-.553.447-1 1-1h11c.553 0 1 .447 1 1v11c0 .553-.447 1-1 1H1c-.553 0-1-.447-1-1V1zM20 1v11c0-1.104-.896-2-2-2h11c-1.104 0-2 .896-2 2V1c0 1.104.896 2 2 2H18c1.104 0 2-.896 2-2zm-3 0c0-.553.447-1 1-1h11c.553 0 1 .447 1 1v11c0 .553-.447 1-1 1H18c-.553 0-1-.447-1-1V1zM20 17v11c0-1.104-.896-2-2-2h11c-1.104 0-2 .896-2 2V17c0 1.104.896 2 2 2H18c1.104 0 2-.896 2-2zm-3 0c0-.553.447-1 1-1h11c.553 0 1 .447 1 1v11c0 .553-.447 1-1 1H18c-.553 0-1-.447-1-1V17zM3 17v11c0-1.104-.896-2-2-2h11c-1.104 0-2 .896-2 2V17c0 1.104.896 2 2 2H1c1.104 0 2-.896 2-2zm-3 0c0-.553.447-1 1-1h11c.553 0 1 .447 1 1v11c0 .553-.447 1-1 1H1c-.553 0-1-.447-1-1V17z"/>
-  </g>
-</svg>
\ No newline at end of file
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M2 1.99v4.02C2 6 2 6 1.99 6h4.02C6 6 6 6 6 6.01V1.99C6 2 6 2 6.01 2H1.99C2 2 2 2 2 1.99zm-1 0c0-.546.451-.99.99-.99h4.02c.546 0 .99.451.99.99v4.02c0 .546-.451.99-.99.99H1.99A.996.996 0 0 1 1 6.01V1.99zM10 1.99v4.02C10 6 10 6 9.99 6h4.02C14 6 14 6 14 6.01V1.99c0 .01 0 .01.01.01H9.99C10 2 10 2 10 1.99zm-1 0c0-.546.451-.99.99-.99h4.02c.546 0 .99.451.99.99v4.02c0 .546-.451.99-.99.99H9.99A.996.996 0 0 1 9 6.01V1.99zM10 9.99v4.02c0-.01 0-.01-.01-.01h4.02c-.01 0-.01 0-.01.01V9.99c0 .01 0 .01.01.01H9.99c.01 0 .01 0 .01-.01zm-1 0c0-.546.451-.99.99-.99h4.02c.546 0 .99.451.99.99v4.02c0 .546-.451.99-.99.99H9.99a.996.996 0 0 1-.99-.99V9.99zM2 9.99v4.02C2 14 2 14 1.99 14h4.02C6 14 6 14 6 14.01V9.99c0 .01 0 .01.01.01H1.99C2 10 2 10 2 9.99zm-1 0c0-.546.451-.99.99-.99h4.02c.546 0 .99.451.99.99v4.02c0 .546-.451.99-.99.99H1.99a.996.996 0 0 1-.99-.99V9.99z"/>
+</svg>
--- a/devtools/client/themes/images/command-paintflashing.svg
+++ b/devtools/client/themes/images/command-paintflashing.svg
@@ -1,7 +1,7 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path opacity="0.5" d="M4 11h10v3H4z"/>
-  <path d="M4.7 6.1h7.6c.4 0 .7-.1.7-.5s-.3-.5-.7-.5H8.9V2.3c0-.4-.1-.7-.5-.7s-.5.4-.5.7v2.8H4.7c-.4 0-.7.1-.7.5s.3.5.7.5zM13.3 6.8H3.9c-.4 0-.8.4-.8.9v6.2c0 .4.3.9.8.9h9.4c.4 0 .8-.4.8-.9V7.7s-.6-.9-.8-.9zm-.2 7h-9v-6h9v6z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M3 6.997v6.006c0-.006.003-.003.002-.003h9.996c-.001 0 .002-.003.002.003V6.997c0 .006-.003.003-.002.003H3.002C3.003 7 3 7.003 3 6.997zm-1 0C2 6.447 2.456 6 3.002 6h9.996C13.55 6 14 6.453 14 6.997v6.006c0 .55-.456.997-1.002.997H3.002A1.004 1.004 0 0 1 2 13.003V6.997zM8.5 4V1.5a.5.5 0 0 0-1 0V4H4a.5.5 0 0 0 0 1h8a.5.5 0 1 0 0-1H8.5z"/>
+  <path fill-opacity=".3" d="M13 10v3H3v-3z"/>
 </svg>
--- a/devtools/client/themes/images/command-pick.svg
+++ b/devtools/client/themes/images/command-pick.svg
@@ -1,8 +1,9 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path opacity="0.5" d="M8.1 8l1.5 5.5 3.7-3z"/>
-  <path d="M12.7 2H.8c-.4 0-.8.5-.8.9v8.8c0 .5.4 1.3.8 1.3h6.5l.1-1H1V3h12v5.4l1 .8V2.9c0-.4-.8-.9-1.3-.9zM11.2 12.2l2.4 2.3.7-.6-2.4-2.4z"/>
-  <path d="M8.8 7.6c-.4-.1-.9-.1-1.2.2-.2.3-.1 1-.1 1l2.1 5.4 4.7-3.8-5.5-2.8zm-.5.7l4.4 2.2-2.8 2.2-1.6-4.4z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M15 7.667V3.002A1.01 1.01 0 0 0 13.993 2H2.007C1.45 2 1 2.449 1 3.002v9.996C1 13.544 1.45 14 2.007 14h6.818l-.37-1H2V3h12v4.334l1 .333z"/>
+  <path fill-opacity=".3" d="M9 8l1.981 5.843 4.044-3.966z"/>
+  <path d="M8.526 8.16l1.982 5.844a.5.5 0 0 0 .824.196l4.043-3.966a.5.5 0 0 0-.202-.835L9.15 7.523a.5.5 0 0 0-.623.638zm.948-.32l-.623.637 6.025 1.877-.201-.834-4.044 3.966.824.197-1.981-5.844z"/>
+  <path d="M12.674 12.39l1.973 1.964a.5.5 0 1 0 .706-.708L13.38 11.68a.5.5 0 0 0-.706.709z"/>
 </svg>
--- a/devtools/client/themes/images/command-scratchpad.svg
+++ b/devtools/client/themes/images/command-scratchpad.svg
@@ -1,7 +1,7 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M13.2 3h-1.3v-.8c0-.3-.3-.6-.6-.6s-.6.3-.6.6V3H7.8v-.8c0-.3-.3-.6-.6-.6s-.7.2-.7.6V3H3.7v-.8c0-.3-.3-.6-.6-.6s-.6.2-.6.6V3H.8c-.4 0-.8.4-.8.9v10.2c0 .4.3.9.8.9h12.4c.4 0 .8-.4.8-.9V3.9s-.6-.9-.8-.9zM13 14H1V3.9h12V14z"/>
-  <path d="M8.7 12.1h-6c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h6c.3 0 .5.2.5.5s-.3.5-.5.5zM11.5 9.9H5.4c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h6.1c.3 0 .5.2.5.5s-.2.5-.5.5zM7.7 7.8H3c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h4.7c.3 0 .5.2.5.5s-.2.5-.5.5z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M5 1.5a.5.5 0 0 0-1 0v2a.5.5 0 0 0 1 0v-2zM8.5 3.5v-2a.5.5 0 0 0-1 0v2a.5.5 0 0 0 1 0zM12 3.5v-2a.5.5 0 1 0-1 0v2a.5.5 0 1 0 1 0zM5 7h4a.5.5 0 0 0 0-1H5a.5.5 0 0 0 0 1zM5 11h2a.5.5 0 1 0 0-1H5a.5.5 0 1 0 0 1zM6 9h5a.5.5 0 1 0 0-1H6a.5.5 0 0 0 0 1z"/>
+  <path d="M3 3.996v9.008c0-.003 0-.004.002-.004h9.996c-.001 0 .002-.003.002.004V3.996c0 .003 0 .004-.002.004H3.002C3.003 4 3 4.003 3 3.996zm-1 0C2 3.446 2.456 3 3.002 3h9.996A.998.998 0 0 1 14 3.996v9.008c0 .55-.456.996-1.002.996H3.002A.998.998 0 0 1 2 13.004V3.996z"/>
 </svg>
deleted file mode 100644
index c64f4bb92fa980e97bafb5bd5ff8ab2ddef9a278..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/debugger-blackbox.svg
@@ -0,0 +1,7 @@
+<!-- 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 width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <circle cx="8" cy="8.5" r="1.5"/>
+  <path d="M15.498 8.28l-.001-.03v-.002-.004l-.002-.018-.004-.031c0-.002 0-.002 0 0l-.004-.035.006.082c-.037-.296-.133-.501-.28-.661-.4-.522-.915-1.042-1.562-1.604-1.36-1.182-2.74-1.975-4.178-2.309a6.544 6.544 0 0 0-2.755-.042c-.78.153-1.565.462-2.369.91C3.252 5.147 2.207 6 1.252 7.035c-.216.233-.36.398-.499.577-.338.437-.338 1 0 1.437.428.552.941 1.072 1.59 1.635 1.359 1.181 2.739 1.975 4.177 2.308.907.21 1.829.223 2.756.043.78-.153 1.564-.462 2.369-.91 1.097-.612 2.141-1.464 3.097-2.499.217-.235.36-.398.498-.578.12-.128.216-.334.248-.554 0 .01 0 .01-.008.04l.013-.079-.001.011.003-.031.001-.017v.005l.001-.02v.008l.002-.03.001-.05-.001-.044v-.004-.004zm-.954.045v.007l.001.004V8.33v.012l-.001.01v-.005-.005l.002-.015-.001.008c-.002.014-.002.014 0 0l-.007.084c.003-.057-.004-.041-.014-.031-.143.182-.27.327-.468.543-.89.963-1.856 1.752-2.86 2.311-.724.404-1.419.677-2.095.81a5.63 5.63 0 0 1-2.374-.036c-1.273-.295-2.523-1.014-3.774-2.101-.604-.525-1.075-1.001-1.457-1.496-.054-.07-.054-.107 0-.177.117-.152.244-.298.442-.512.89-.963 1.856-1.752 2.86-2.311.724-.404 1.419-.678 2.095-.81a5.631 5.631 0 0 1 2.374.036c1.272.295 2.523 1.014 3.774 2.101.603.524 1.074 1 1.457 1.496.035.041.043.057.046.076 0 .01 0 .01.008.043l-.009-.047.003.02-.002-.013v-.008.016c0-.004 0-.004 0 0v-.004z"/>
+</svg>
deleted file mode 100644
index 2dd33d2fa0337ff2e6c1056dd548e498d65044ba..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 5863380b0d373db4f5982ef4fc9a1754d46b9ede..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 3d38eccd2af098728d1a18ba3755d2ddd5312245..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 2bde10cb475dbab35236b4a2003a0dcec5e5118b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index b6b06502e830a8de7e3003d7e6497799784f9180..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index fb59dcc33dba6f686feff4ccd5d56c716515610b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/debugger-prettyprint.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/. -->
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M5.5 2C3.565 2 2.806 3.12 3.065 4.587c.239 1.346.117 1.76-.435 2.39l-.054.06c-.252.288-.39.474-.523.74L1.94 8l.112.224c.132.265.27.45.523.739l.054.06c.552.63.674 1.044.435 2.39C2.802 12.904 3.527 14 5.5 14a.5.5 0 1 0 0-1c-1.291 0-1.614-.487-1.45-1.413.292-1.65.081-2.37-.669-3.223l-.053-.06c-.2-.229-.296-.357-.38-.528v.448c.084-.17.18-.299.38-.528l.053-.06c.75-.854.961-1.573.67-3.223C3.89 3.515 4.24 3 5.5 3a.5.5 0 1 0 0-1zM10.5 3c1.26 0 1.609.515 1.45 1.413-.292 1.65-.081 2.37.669 3.223l.053.06c.2.229.296.357.38.528v-.448c-.084.17-.18.299-.38.528l-.053.06c-.75.854-.961 1.573-.67 3.223.165.926-.158 1.413-1.449 1.413a.5.5 0 1 0 0 1c1.973 0 2.698-1.096 2.435-2.587-.239-1.346-.117-1.76.435-2.39l.054-.06c.252-.288.39-.474.523-.74L14.06 8l-.112-.224c-.132-.265-.27-.45-.523-.739l-.054-.06c-.552-.63-.674-1.044-.435-2.39C13.194 3.12 12.435 2 10.5 2a.5.5 0 0 0 0 1z"/>
+</svg>
deleted file mode 100644
index e3791bfa13a63a525029f55a45559e79530ab307..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index b16b706024e7cdb35b06eb09b898f8165051ebe8..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/debugger-step-in.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/. -->
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M1.5 14.042h4.095a.5.5 0 0 0 0-1H1.5a.5.5 0 1 0 0 1zM7.5 3v6.983L4.364 6.657a.5.5 0 0 0-.728.686l4 4.243a.51.51 0 0 0 .021.02.5.5 0 0 0 .71-.024l3.997-4.239a.5.5 0 1 0-.728-.686L8.5 9.983V2.5a.5.5 0 0 0-.536-.5H1.536C1.24 2 1 2.224 1 2.5s.24.5.536.5H7.5zM10.5 14.042h4.095a.5.5 0 0 0 0-1H10.5a.5.5 0 1 0 0 1z"/>
+</svg>
deleted file mode 100644
index c0b45008e1d28eb7e29d5a7d06ac6bb2a7af38b8..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 859c727cdbf559957cb9a0e5365b651609b7e0e5..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/debugger-step-out.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/. -->
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M5 13.5H1a.5.5 0 1 0 0 1h4a.5.5 0 1 0 0-1zM12 13.5H8a.5.5 0 1 0 0 1h4a.5.5 0 1 0 0-1zM6.11 5.012A.427.427 0 0 1 6.21 5h7.083L9.646 1.354a.5.5 0 1 1 .708-.708l4.5 4.5a.498.498 0 0 1 0 .708l-4.5 4.5a.5.5 0 0 1-.708-.708L13.293 6H6.5v5.5a.5.5 0 1 1-1 0v-6a.5.5 0 0 1 .61-.488z"/>
+</svg>
deleted file mode 100644
index 1c19679354bcde539334d1e8b72ac52ec34014bc..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/debugger-step-over.svg
@@ -0,0 +1,7 @@
+<!-- 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 width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M13.297 6.912C12.595 4.39 10.167 2.5 7.398 2.5A5.898 5.898 0 0 0 1.5 8.398a.5.5 0 0 0 1 0A4.898 4.898 0 0 1 7.398 3.5c2.75 0 5.102 2.236 5.102 4.898v.004L8.669 7.029a.5.5 0 0 0-.338.942l4.462 1.598a.5.5 0 0 0 .651-.34.506.506 0 0 0 .02-.043l2-5a.5.5 0 1 0-.928-.372l-1.24 3.098z"/>
+  <circle cx="7" cy="12" r="1"/>
+</svg>
--- a/devtools/client/themes/images/debugger-toggleBreakpoints.svg
+++ b/devtools/client/themes/images/debugger-toggleBreakpoints.svg
@@ -1,6 +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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="32" height="16" fill="#babec3">
-  <path d="M1 5c-.553 0-1 .45-1 .99v4.02c0 .546.447.99 1 .99h12l3-3-3-3H1zm16 6c-.553 0-1-.45-1-.99V5.99c0-.546.45-.99 1.008-.99h8.577l3.208-3.207.707-.707L30.914 2.5l-.707.707-11 11-.707.707-1.414-1.414.707-.707L19.586 11H17zm12 0l3-3-2.18-2.697L24 11h5z"/>
+<svg width="32" height="16" viewBox="0 0 32 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M3.233 11.25l-.417 1H1.712C.763 12.25 0 11.574 0 10.747V6.503C0 5.675.755 5 1.712 5h4.127l-.417 1H1.597C1.257 6 1 6.225 1 6.503v4.244c0 .277.267.503.597.503h1.636zM7.405 11.02L7 12.056c.865.01 2.212-.024 2.315-.04.112-.016.112-.016.185-.035.075-.02.156-.046.251-.082.152-.056.349-.138.592-.244.415-.182.962-.435 1.612-.744l.138-.066a179.35 179.35 0 0 0 2.255-1.094c1.191-.546 1.191-2.074-.025-2.632l-.737-.34A3547.554 3547.554 0 0 0 9.732 5c-.029.11-.065.222-.11.336l-.232.596c.894.408 4.56 2.107 4.56 2.107.458.21.458.596 0 .806L9.197 11.02H7.405zM20.462 14.192l5-12a.5.5 0 0 0-.924-.384l-5 12a.5.5 0 0 0 .924.384zM19.233 11.25l-.417 1h-1.104c-.949 0-1.712-.676-1.712-1.503V6.503C16 5.675 16.755 5 17.712 5h4.127l-.417 1h-3.825c-.34 0-.597.225-.597.503v4.244c0 .277.267.503.597.503h1.636zM23.405 11.02L23 12.056c.865.01 2.212-.024 2.315-.04.112-.016.112-.016.185-.035.075-.02.156-.046.251-.082.152-.056.349-.138.592-.244.415-.182.962-.435 1.612-.744l.138-.066a179.35 179.35 0 0 0 2.255-1.094c1.191-.546 1.191-2.074-.025-2.632l-.737-.34A3547.554 3547.554 0 0 0 25.732 5c-.029.11-.065.222-.11.336l-.232.596c.894.408 4.56 2.107 4.56 2.107.458.21.458.596 0 .806l-4.753 2.174h-1.792z"/>
 </svg>
--- a/devtools/client/themes/images/diff.svg
+++ b/devtools/client/themes/images/diff.svg
@@ -1,7 +1,9 @@
 <!-- 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 height="16" width="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#babec3">
-  <path d="M10.2 4.1c-.6 0-1.2.2-1.8.4-.6-.4-1.4-.6-2.2-.6-2.5 0-4.6 2.1-4.6 4.6s2.1 4.6 4.6 4.6c.9 0 1.6-.2 2.3-.7.5.2 1.1.4 1.7.4 2.4 0 4.3-1.9 4.3-4.3.1-2.4-1.9-4.4-4.3-4.4zm-4 7.9c-1.9 0-3.5-1.6-3.5-3.5S4.2 5 6.2 5c.3 0 .7 0 1 .1-.8.9-1.4 2.1-1.4 3.4 0 1.3.6 2.5 1.5 3.3-.4.1-.8.2-1.1.2zm2.1-.7c-.9-.6-1.4-1.6-1.4-2.8 0-1.1.6-2.1 1.4-2.8.8.6 1.4 1.6 1.4 2.8 0 1.1-.6 2.1-1.4 2.8z"/>
-  <path d="M7.6 8c-.2 0-.3-.1-.4-.2-.1-.2 0-.4.2-.5l1.1-.6c.2-.1.4 0 .5.2.1.2 0 .4-.2.5l-1.1.5c0 .1-.1.1-.1.1zM7.6 9.1c-.1 0-.3-.1-.4-.2-.1-.2 0-.4.2-.5l1.1-.6c.3-.1.5 0 .6.2.1.2 0 .4-.2.5l-1.1.6h-.2zM7.8 10.3c-.1 0-.3-.1-.4-.2-.1-.2 0-.4.2-.5L8.8 9c.2-.1.4 0 .5.2.1.2 0 .4-.2.5l-1.1.6h-.2z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M6 13A5 5 0 1 0 6 3a5 5 0 0 0 0 10zm0-.91a4.09 4.09 0 1 1 0-8.18 4.09 4.09 0 0 1 0 8.18z"/>
+  <path d="M10 13a5 5 0 1 0 0-10 5 5 0 0 0 0 10zm0-.91a4.09 4.09 0 1 1 0-8.18 4.09 4.09 0 0 1 0 8.18z"/>
+  <path d="M7.146 8.854l1 1a.5.5 0 0 0 .708-.708l-1-1a.5.5 0 1 0-.708.708zM7.146 6.854l1 1a.5.5 0 1 0 .708-.708l-1-1a.5.5 0 1 0-.708.708z"/>
+  <path d="M12.656 11.723c-2.044 1.169-3.872 1.015-4.282.577-.41-.438 2.115-1.269 2.115-3.925 0-2.657-2.115-4.827-2.115-4.827s2.919-.47 4.282.624c1.364 1.094 2.12 1.975 1.85 3.828-.103.703.194 2.555-1.85 3.723z" fill-opacity=".3"/>
 </svg>
--- a/devtools/client/themes/images/dock-bottom.svg
+++ b/devtools/client/themes/images/dock-bottom.svg
@@ -1,6 +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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M11.9 13.5V3.9c0-.3-.3-.7-.7-.7H2c-.3 0-.7.2-.7.7v9.6c0 .3.3.7.7.7h9.2c.6-.1.7-.6.7-.7zM2.1 9.3V4h9v5.3h-9zm0 3.9v-3h9v3h-9z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M10.004 3H.996C.999 3 1 3 1 3.002v9.996c0-.001.003.002-.004.002h9.008c-.003 0-.004 0-.004-.002V3.002c0 .001-.003-.002.004-.002zm0-1c.55 0 .996.456.996 1.002v9.996A.998.998 0 0 1 10.004 14H.996C.446 14 0 13.544 0 12.998V3.002A.998.998 0 0 1 .996 2h9.008zm-.41 8H.996v1h9.01v-1h-.41z"/>
 </svg>
--- a/devtools/client/themes/images/dock-side.svg
+++ b/devtools/client/themes/images/dock-side.svg
@@ -1,6 +1,3 @@
-<!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M-1.5 3.2h-9.6c-.1 0-.2.1-.2.2V13c0 .1.1.2.2.2h9.6c.1 0 .2-.1.2-.2V3.4c0-.1-.1-.2-.2-.2zm-3.6 8.9h-5V4.4h5v7.7zm3.2-.1h-2.7V4.4h2.7V12zM11.1 3.2H.9c-.3 0-.7.3-.7.7v9.2c0 .3.2.7.7.7h10.2c.3 0 .7-.3.7-.7V3.9c-.1-.6-.6-.7-.7-.7zM6.9 13H1.1V4H7v9zm4 0h-3V4h3v9z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M1 2.996v9.008c0-.003 0-.004.002-.004h9.996c-.001 0 .002-.003.002.004V2.996c0 .003 0 .004-.002.004H1.002C1.003 3 1 3.003 1 2.996zm-1 0C0 2.446.456 2 1.002 2h9.996A.998.998 0 0 1 12 2.996v9.008c0 .55-.456.996-1.002.996H1.002A.998.998 0 0 1 0 12.004V2.996zm8 .413V12h1V3H8v.41z"/>
 </svg>
--- a/devtools/client/themes/images/dock-undock.svg
+++ b/devtools/client/themes/images/dock-undock.svg
@@ -1,7 +1,8 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M4.3 2.8v5.9c0 .2.2.4.5.4h5.7c.2 0 .5-.2.5-.4V2.8c0-.2-.2-.4-.5-.4H4.8c-.4 0-.5.3-.5.4zm5.8 2.8v2.6h-5V5.6h5zm0-2.5V5h-5V3.1h5z"/>
-  <path d="M7.1 9.9v2.2h-5V9.7h1.2V9H2.1V7h1.2v-.7H1.7c-.4 0-.5.3-.5.4v5.9c0 .2.2.4.5.4h5.7c.2 0 .5-.2.5-.4V9.9h-.8z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M13.003 1.941H6.997c.008 0 .003.004.003.008v6.102c0 .004.004.008-.003.008h6.006c-.008 0-.003-.004-.003-.008V1.949c0-.004-.004-.008.003-.008zm0-.941c.55 0 .997.43.997.95v6.1c0 .525-.453.95-.997.95H6.997C6.447 9 6 8.57 6 8.05v-6.1c0-.525.453-.95.997-.95h6.006z"/>
+  <path d="M9 9.91v-.278h1v1.183c0 .516-.453.935-.997.935H2.997c-.55 0-.997-.43-.997-.95V4.7c0-.525.444-.95 1.006-.95h2.288v.941H3.006C3 4.691 3 4.691 3 4.7v6.102c0 .004.004.008-.003.008h6.006c-.004 0-.003-.001-.003.006v-.248-.657-.278h1v1.183c0 .516-.453.935-.997.935H2.997c-.55 0-.997-.43-.997-.95V4.7c0-.525.444-.95 1.006-.95h2.288v.941H3.006C3 4.691 3 4.691 3 4.7v6.102c0 .004.004.008-.003.008h6.006c-.004 0-.003-.001-.003.006v-.248-.657z"/>
+  <path d="M12.52 5H6.976v1h6.046V5zM6.5 7H2.975v1H7V7z"/>
 </svg>
deleted file mode 100644
index 5cbc7274f071b80e6660f23f3ddd66f1aca7af86..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/fast-forward.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/. -->
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M4 12.5l8-5-8-5v10zm-1 0v-10a1 1 0 0 1 1.53-.848l8 5a1 1 0 0 1 0 1.696l-8 5A1 1 0 0 1 3 12.5zM15 12.497l-.04-7.342-.01-1.658A.488.488 0 0 0 14.474 3a.488.488 0 0 0-.473.503l.05 9a.488.488 0 0 0 .477.497.488.488 0 0 0 .473-.503z"/>
+</svg>
deleted file mode 100644
index 345d07cea08f86cd1095c1e5ceb953dc75efddd5..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/devtools/client/themes/images/filter.svg
+++ b/devtools/client/themes/images/filter.svg
@@ -1,7 +1,7 @@
 <!-- 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" viewBox="0 0 16 16" fill="#aaa">
-  <path fill-opacity="0.3" d="M6.6 8.4c0-.6-1.7.3-1.7-.3C4.9 7.7 2.4 4 2.4 4h11.3s-2.5 3.4-2.5 4.1c0 .3-2.1-.1-2.1.3v5.9H7s-.4-3.9-.4-5.9z"/>
-  <path d="M2 2v2.3l2.7 4.5h1.6v5.5s1.1.6 1.8.6c.5 0 1.8-.6 1.8-.6V8.8h1.6L14 4.3V2H2zm10.8 2l-2.1 3.6H8.5v5.9c-.1 0-.1.1-.2.1-.2.1-.3.1-.3.1s-.1 0-.2-.1c-.1 0-.2-.1-.3-.1V7.6H5.4L3.2 4v-.8h9.5V4z"/>
+  <path fill-opacity=".3" d="M6.6 8.4c0-.6-1.7.3-1.7-.3 0-.4-1.7-2.7-1.7-2.7H13s-1.8 2-1.8 2.7c0 .3-2.1-.1-2.1.3v6.1H7s-.4-4.1-.4-6.1z"/>
+  <path d="M2 2v2.3L4.7 9H6v5.4l2.1 1 1.8-.9V9h1.3L14 4.3V2H2zm11 2l-2.2 4H9v5.8l-.9.4-1.1-.5V8H5.2L3 4V3h10v1z"/>
 </svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/import.svg
@@ -0,0 +1,8 @@
+<!-- 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 width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M7.864 1.417c-.123-.13-.305-.185-.48-.144-.173.04-.312.172-.363.343-.05.17-.007.357.116.487l4 4.243c.19.2.506.21.707.02.2-.188.21-.505.02-.706l-4-4.243z"/>
+  <path d="M7.136 1.414l-4 4.243c-.19.2-.18.518.02.707.202.19.52.18.708-.02l4-4.244c.123-.13.166-.316.115-.487-.052-.17-.19-.302-.365-.343-.174-.04-.356.014-.48.144zM1.5 8c-.276 0-.5.224-.5.5v5c0 .2.224.5.5.5h12c.276 0 .5-.3.5-.5v-5c0-.276-.224-.5-.5-.5h-3c-.28 0-.5.224-.5.5s.22.5.5.5H13v4H2V9h2.5c.27 0 .5-.224.5-.5S4.77 8 4.5 8h-3z"/>
+  <path d="M7 2v9c0 .276.224.5.5.5s.5-.224.5-.5V2c0-.276-.224-.5-.5-.5S7 1.724 7 2z"/>
+</svg>
deleted file mode 100644
index dc30c224dcb26262855aae0a6271b0f2ee4cae6b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/itemToggle.svg
@@ -0,0 +1,7 @@
+<!-- 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 width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <circle cx="8" cy="8.5" r="1.5"/>
+  <path d="M15.498 8.28l-.001-.03v-.002-.004l-.002-.018-.004-.031c0-.002 0-.002 0 0l-.004-.035.006.082c-.037-.296-.133-.501-.28-.661-.4-.522-.915-1.042-1.562-1.604-1.36-1.182-2.74-1.975-4.178-2.309a6.544 6.544 0 0 0-2.755-.042c-.78.153-1.565.462-2.369.91C3.252 5.147 2.207 6 1.252 7.035c-.216.233-.36.398-.499.577-.338.437-.338 1 0 1.437.428.552.941 1.072 1.59 1.635 1.359 1.181 2.739 1.975 4.177 2.308.907.21 1.829.223 2.756.043.78-.153 1.564-.462 2.369-.91 1.097-.612 2.141-1.464 3.097-2.499.217-.235.36-.398.498-.578.12-.128.216-.334.248-.554 0 .01 0 .01-.008.04l.013-.079-.001.011.003-.031.001-.017v.005l.001-.02v.008l.002-.03.001-.05-.001-.044v-.004-.004zm-.954.045v.007l.001.004V8.33v.012l-.001.01v-.005-.005l.002-.015-.001.008c-.002.014-.002.014 0 0l-.007.084c.003-.057-.004-.041-.014-.031-.143.182-.27.327-.468.543-.89.963-1.856 1.752-2.86 2.311-.724.404-1.419.677-2.095.81a5.63 5.63 0 0 1-2.374-.036c-1.273-.295-2.523-1.014-3.774-2.101-.604-.525-1.075-1.001-1.457-1.496-.054-.07-.054-.107 0-.177.117-.152.244-.298.442-.512.89-.963 1.856-1.752 2.86-2.311.724-.404 1.419-.678 2.095-.81a5.631 5.631 0 0 1 2.374.036c1.272.295 2.523 1.014 3.774 2.101.603.524 1.074 1 1.457 1.496.035.041.043.057.046.076 0 .01 0 .01.008.043l-.009-.047.003.02-.002-.013v-.008.016c0-.004 0-.004 0 0v-.004z"/>
+</svg>
deleted file mode 100644
index 90421287cc6c9631cb8c182a9e89697fe71b22f7..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/devtools/client/themes/images/pane-collapse.svg
+++ b/devtools/client/themes/images/pane-collapse.svg
@@ -1,6 +1,9 @@
 <!-- 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 width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#babec3">
-  <path d="M2 2h12v12H2V2zm1 1h7v10H3V3zm6 5l-4 3V5l4 3z" fill-rule="evenodd"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path fill-opacity=".3" d="M12 3h2v10h-2z"/>
+  <path d="M2 3.002v9.996c0-.004.006.002.007.002h11.986c.005 0 .007-.002.007-.002V3.002c0 .004-.006-.002-.007-.002H2.007C2.002 3 2 3.002 2 3.002zm-1 0C1 2.45 1.45 2 2.007 2h11.986A1.01 1.01 0 0 1 15 3.002v9.996C15 13.55 14.55 14 13.993 14H2.007A1.01 1.01 0 0 1 1 12.998V3.002zm10 .453V13h1V3h-1v.455z"/>
+  <path d="M5 10.25l3-1.875L5 6.5v3.75zm-1 0V6.5a1 1 0 0 1 1.53-.848l3 1.875a1 1 0 0 1 0 1.696l-3 1.875A1 1 0 0 1 4 10.25z"/>
+  <path fill-opacity=".3" d="M4.5 10.75V6L9 8.375z"/>
 </svg>
--- a/devtools/client/themes/images/pane-expand.svg
+++ b/devtools/client/themes/images/pane-expand.svg
@@ -1,6 +1,9 @@
 <!-- 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 width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#babec3">
-  <path d="M2 2h12v12H2V2zm1 1h7v10H3V3zm1 5l4 3V5L4 8z" fill-rule="evenodd"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path fill-opacity=".3" d="M12 3h2v10h-2z"/>
+  <path d="M2 3.002v9.996c0-.004.006.002.007.002h11.986c.005 0 .007-.002.007-.002V3.002c0 .004-.006-.002-.007-.002H2.007C2.002 3 2 3.002 2 3.002zm-1 0C1 2.45 1.45 2 2.007 2h11.986A1.01 1.01 0 0 1 15 3.002v9.996C15 13.55 14.55 14 13.993 14H2.007A1.01 1.01 0 0 1 1 12.998V3.002zm10 .453V13h1V3h-1v.455z"/>
+  <path d="M8 6.5L5 8.375l3 1.875V6.5zm1 0v3.75a1 1 0 0 1-1.53.848l-3-1.875a1 1 0 0 1 0-1.696l3-1.875A1 1 0 0 1 9 6.5z"/>
+  <path fill-opacity=".3" d="M8.5 6v4.75L4 8.375z"/>
 </svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/pause.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/. -->
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M5 12.503l.052-9a.5.5 0 0 0-1-.006l-.052 9a.5.5 0 0 0 1 .006zM12 12.497l-.05-9A.488.488 0 0 0 11.474 3a.488.488 0 0 0-.473.503l.05 9a.488.488 0 0 0 .477.497.488.488 0 0 0 .473-.503z"/>
+</svg>
--- a/devtools/client/themes/images/performance-icons.svg
+++ b/devtools/client/themes/images/performance-icons.svg
@@ -1,45 +1,42 @@
 <!-- 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="16px" height="16px">
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="whitesmoke">
   <style>
-    g {
-      fill: #babec3;
-    }
     g:not(:target) {
       display: none;
     }
   </style>
   <g id="overview-markers">
-    <rect x="0px" y="3px" width="5px" height="2.5px" rx="1" ry="1"/>
-    <rect x="7px" y="3px" width="9px" height="2.5px" rx="1" ry="1"/>
-    <rect x="0px" y="7px" width="9px" height="2.5px" rx="1" ry="1"/>
-    <rect x="10px" y="7px" width="6px" height="2.5px" rx="1" ry="1"/>
-    <rect x="4px" y="11px" width="5px" height="2.5px" rx="1" ry="1"/>
-    <rect x="12px" y="11px" width="4px" height="2.5px" rx="1" ry="1"/>
+    <rect x="0" y="4" width="5" height="1"/>
+    <rect x="7" y="4" width="9" height="1"/>
+    <rect x="0" y="8" width="8" height="1"/>
+    <rect x="10" y="8" width="6" height="1"/>
+    <rect x="0" y="12" width="9" height="1"/>
+    <rect x="12" y="12" width="4" height="1"/>
   </g>
   <g id="overview-frames">
-    <rect x="1px" y="4px" width="2px" height="12px" rx="1" ry="1"/>
-    <rect x="5px" y="12px" width="2px" height="4px" rx="1" ry="1"/>
-    <rect x="9px" y="9px" width="2px" height="7px" rx="1" ry="1"/>
-    <rect x="13px" y="7px" width="2px" height="9px" rx="1" ry="1"/>
+    <rect x="1" y="4" width="2" height="12" rx="1" ry="1"/>
+    <rect x="5" y="12" width="2" height="4" rx="1" ry="1"/>
+    <rect x="9" y="9" width="2" height="7" rx="1" ry="1"/>
+    <rect x="13" y="7" width="2" height="9" rx="1" ry="1"/>
   </g>
   <g id="details-waterfall">
-    <rect x="0px" y="3px" width="9px" height="2.5px" rx="1" ry="1"/>
-    <rect x="5px" y="7px" width="8px" height="2.5px" rx="1" ry="1"/>
-    <rect x="7px" y="11px" width="9px" height="2.5px" rx="1" ry="1"/>
+    <rect x="0" y="4" width="9" height="1"/>
+    <rect x="5" y="8" width="8" height="1"/>
+    <rect x="7" y="12" width="9" height="1"/>
   </g>
   <g id="details-call-tree">
-    <rect x="0px" y="3px" width="16px" height="2px" rx="1" ry="1"/>
-    <rect x="0px" y="6px" width="8px" height="2px" rx="1" ry="1"/>
-    <rect x="0px" y="9px" width="11px" height="2px" rx="1" ry="1"/>
-    <rect x="0px" y="12px" width="6px" height="2px" rx="1" ry="1"/>
+    <rect x="1" y="4" width="10" height="1"/>
+    <rect x="5" y="7" width="10" height="1"/>
+    <rect x="1" y="10" width="10" height="1"/>
+    <rect x="5" y="13" width="10" height="1"/>
   </g>
   <g id="details-flamegraph">
-    <rect x="0px" y="3px" width="16px" height="2px" rx="1" ry="1"/>
-    <rect x="0px" y="6px" width="8px" height="2px" rx="1" ry="1"/>
-    <rect x="10px" y="6px" width="6px" height="2px" rx="1" ry="1"/>
-    <rect x="2px" y="9px" width="6px" height="2px" rx="1" ry="1"/>
-    <rect x="5px" y="12px" width="3px" height="2px" rx="1" ry="1"/>
+    <rect x="0" y="4" width="16" height="1"/>
+    <rect x="0" y="7" width="8" height="1"/>
+    <rect x="10" y="7" width="6" height="1"/>
+    <rect x="2" y="10" width="6" height="1"/>
+    <rect x="5" y="13" width="3" height="1"/>
   </g>
 </svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/play.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/. -->
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M4 12.5l8-5-8-5v10zm-1 0v-10a1 1 0 0 1 1.53-.848l8 5a1 1 0 0 1 0 1.696l-8 5A1 1 0 0 1 3 12.5z" fill-rule="evenodd"/>
+</svg>
--- a/devtools/client/themes/images/power.svg
+++ b/devtools/client/themes/images/power.svg
@@ -1,14 +1,7 @@
-<!--
-Logo from raphaeljs.com, MIT License
-
-Copyright © 2008 Dmitry Baranovskiy
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-The software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.
--->
-<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
-  <path stroke="#babec3" stroke-width="0" fill="#babec3" d="m10.89891,2.50043c-0.49827,-0.24134 -1.09841,-0.03411 -1.34129,0.46514c-0.24185,0.49928 -0.03311,1.09942 0.46517,1.34128c1.56306,0.76071 2.64193,2.36094 2.64092,4.21555c-0.00501,2.58626 -2.09749,4.6787 -4.68322,4.68321c-2.58623,-0.005 -4.67869,-2.09746 -4.68371,-4.68321c-0.001,-1.85561 1.07834,-3.45731 2.64294,-4.21654c0.49928,-0.24185 0.7065,-0.84201 0.46514,-1.34129c-0.24185,-0.49825 -0.84098,-0.70697 -1.34029,-0.46513c-2.23396,1.08135 -3.77446,3.37351 -3.77545,6.02296c0.00099,3.69518 2.99518,6.68989 6.69138,6.69088c3.6957,-0.00099 6.69037,-2.9957 6.69089,-6.69088c-0.00102,-2.64846 -1.53948,-4.9391 -3.77247,-6.02197zm-2.91842,4.9346c0.55398,0 1.00309,-0.44861 1.00309,-1.00357l0,-4.68373c0,-0.55446 -0.44911,-1.00309 -1.00309,-1.00309c-0.555,0 -1.00358,0.44911 -1.00358,1.00309l0,4.68321c0,0.55499 0.44858,1.00409 1.00358,1.00409z"/>
+<!-- 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 width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M8 14.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zm0-1a4.5 4.5 0 1 1 0-9 4.5 4.5 0 0 1 0 9z"/>
+  <path d="M8.5 7.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0z"/>
 </svg>
--- a/devtools/client/themes/images/profiler-stopwatch.svg
+++ b/devtools/client/themes/images/profiler-stopwatch.svg
@@ -1,17 +1,11 @@
 <!-- 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" viewBox="0 0 16 16">
-  <g fill="#babec3" fill-rule="evenodd">
-    <path d="m8,1c-3.9,0-7,3.1-7,7s3.1,7 7,7c3.9,0 7-3.1 7-7s-3.1-7-7-7zm-.1,12c-2.8,0-5-2.2-5-5 0-2.8 2.2-5 5-5s5,2.2 5,5c0,2.8-2.2,5-5,5z"/>
-    <path d="m8,6.9c.6,0 1.1,.5 1.1,1.1 0,.6-.5,1.1-1.1,1.1-.6,0-1.1-.5-1.1-1.1 0-.6 .5-1.1 1.1-1.1z"/>
-    <path d="m11.3,4.6l-3.9,2.5 1.5,1.4 2.4-3.9z"/>
-    <path opacity=".4" d="m4.6,10c.7,1.2 2,2 3.4,2 1.5,0 2.7-.8 3.4-2h-6.8z"/>
-    <g opacity=".3">
-      <path d="m7.1,5.1l-.6-1.3-.9,.4 .7,1.3c.2-.1 .5-.3 .8-.4z"/>
-      <path d="m9.8,5.6l.7-1.4-.9-.4-.7,1.3c.3,.2 .6,.3 .9,.5z"/>
-      <path d="m10.8,7c.1,.3 .2,.7 .2,1h2v-1h-2.2z"/>
-      <path d="m5,8c0-.3 .1-.7 .2-1h-2.2l-.1,1h2.1z"/>
-    </g>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <g fill-rule="evenodd">
+    <path d="M15 9.004C14.51 12.394 11.578 15 8.035 15 4.15 15 1 11.866 1 8s3.15-7 7.036-7c1.941 0 3.7.783 4.972 2.048l-.709.709A6.027 6.027 0 0 0 8.036 2c-3.33 0-6.03 2.686-6.03 6s2.7 6 6.03 6a6.023 6.023 0 0 0 5.946-4.993l1.017-.003z"/>
+    <path d="M4.137 9H3.1a5.002 5.002 0 0 0 9.8 0h-.965a4.023 4.023 0 0 1-3.9 3 4.023 4.023 0 0 1-3.898-3z" fill-opacity=".5"/>
+    <path d="M8.036 11a2.994 2.994 0 0 0 2.987-3c0-1.657-1.338-3-2.987-3a2.994 2.994 0 0 0-2.988 3c0 1.657 1.338 3 2.988 3zm0-1c1.11 0 2.011-.895 2.011-2s-.9-2-2.011-2c-1.111 0-2.012.895-2.012 2s.9 2 2.012 2z"/>
+    <path d="M10.354 6.354l4-4a.5.5 0 0 0-.708-.708l-4 4a.5.5 0 1 0 .708.708z"/>
   </g>
 </svg>
--- a/devtools/client/themes/images/pseudo-class.svg
+++ b/devtools/client/themes/images/pseudo-class.svg
@@ -1,20 +1,7 @@
 <!-- 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 width="16" height="16" viewBox="0 0 16 16" color="#babec3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-  <defs>
-    <rect id="class-block-maskBG" width="8" height="8" fill="#fff"/>
-    <rect id="class-block" width="8" height="8" rx="1" ry="1"/>
-    <mask id="mask-block-solid">
-      <use xlink:href="#class-block-maskBG"/>
-      <use xlink:href="#class-block" transform="translate(3 3)" fill="#000"/>
-    </mask>
-  </defs>
-  <rect x=".5" y=".5" width="7" height="7" rx="1" ry="1" mask="url(#mask-block-solid)" fill="none" stroke="currentColor" stroke-width="1"/>
-  <use xlink:href="#class-block" mask="url(#mask-block-solid)" fill="currentColor" fill-opacity=".4"/>
-  <use xlink:href="#class-block" mask="url(#mask-block-solid)" fill="currentColor" transform="translate(4 4)"/>
-  <g transform="translate(8 8)" fill="currentColor">
-    <path d="M2.5,0C2.2,0,2,0.2,2,0.5C2,0.8,2.2,1,2.5,1C2.8,1,3,0.8,3,0.5 C3,0.2,2.8,0,2.5,0z M4.5,0C4.2,0,4,0.2,4,0.5C4,0.8,4.2,1,4.5,1C4.8,1,5,0.8,5,0.5C5,0.2,4.8,0,4.5,0z M0.5,6C0.8,6,1,5.8,1,5.5 C1,5.2,0.8,5,0.5,5C0.2,5,0,5.2,0,5.5C0,5.8,0.2,6,0.5,6z M0.5,4C0.8,4,1,3.8,1,3.5C1,3.2,0.8,3,0.5,3C0.2,3,0,3.2,0,3.5 C0,3.8,0.2,4,0.5,4z M7.5,2C7.2,2,7,2.2,7,2.5C7,2.8,7.2,3,7.5,3C7.8,3,8,2.8,8,2.5C8,2.2,7.8,2,7.5,2z M7.5,4C7.2,4,7,4.2,7,4.5 C7,4.8,7.2,5,7.5,5C7.8,5,8,4.8,8,4.5C8,4.2,7.8,4,7.5,4z M5.5,7C5.2,7,5,7.2,5,7.5C5,7.8,5.2,8,5.5,8C5.8,8,6,7.8,6,7.5 C6,7.2,5.8,7,5.5,7z M3.5,7C3.2,7,3,7.2,3,7.5C3,7.8,3.2,8,3.5,8C3.8,8,4,7.8,4,7.5C4,7.2,3.8,7,3.5,7z M0.5,2C0.8,2,1,1.8,1,1.5v-1 C1,0.2,0.8,0,0.5,0C0.2,0,0,0.2,0,0.5v1C0,1.8,0.2,2,0.5,2z M8,0.5C8,0.2,7.8,0,7.5,0h-1C6.2,0,6,0.2,6,0.5C6,0.8,6.2,1,6.5,1h1 C7.8,1,8,0.8,8,0.5z M7.5,6C7.2,6,7,6.2,7,6.5v1C7,7.8,7.2,8,7.5,8C7.8,8,8,7.8,8,7.5v-1C8,6.2,7.8,6,7.5,6z M1.5,7h-1 C0.2,7,0,7.2,0,7.5C0,7.8,0.2,8,0.5,8h1C1.8,8,2,7.8,2,7.5C2,7.2,1.8,7,1.5,7z"/>
-    <use xlink:href="#class-block" fill-opacity=".2"/>
-  </g>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="whitesmoke">
+  <path d="M11 7V5.5c0-.3-.2-.5-.5-.5h-5c-.3 0-.5.2-.5.5v5c0 .3.2.5.5.5h1.9V7.5c0-.3.2-.5.5-.5H11zM3 7H.8c-.1 0-.6 0-.7-.7-.1-.2-.1-.4-.1-.6v-5C0 .5 0 .3.2.1.4 0 .6 0 .7 0h5.2c.3 0 .6 0 .8.2.2.1.3.3.3.5V3H3v4zM1 6h1V2.7c0-.2.1-.4.2-.5.3-.2.6-.2.8-.2h3V1H1v5z"/>
+  <path d="M9 9h1v1H9V9zm5 1h-1V9h1v1zm-2 0h-1V9h1v1zm3-1h1v1h-1V9zm1 5h-1v-1h1v1zm0-2h-1v-1h1v1zm-1 3h1v1h-1v-1zm-1 1h-1v-1h1v1zm-2 0h-1v-1h1v1zm-3-1h1v1H9v-1zm1-1H9v-1h1v1zm0-2H9v-1h1v1z"/>
 </svg>
deleted file mode 100644
index 3e8b4db747311a8ac511e3db2cb2dab6a5541ad7..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/rewind.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/. -->
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M13 2.5l-8 5 8 5v-10zm1 0v10a1 1 0 0 1-1.53.848l-8-5a1 1 0 0 1 0-1.696l8-5A1 1 0 0 1 14 2.5zM2 12.497l-.04-7.342-.01-1.658A.488.488 0 0 0 1.474 3 .488.488 0 0 0 1 3.503l.05 9a.488.488 0 0 0 .477.497.488.488 0 0 0 .473-.503z"/>
+</svg>
deleted file mode 100644
index c6fee45f3da3d5d4bdda03779dac7cbcd73337c9..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/devtools/client/themes/images/timeline-filter.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-<!-- 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" viewBox="0 0 16 16">
-  <path fill="#babec3" d="M2,2v3l5,4v6h2v-6l5,-4v-3L14,2z"/>
-</svg>
--- a/devtools/client/themes/images/tool-canvas.svg
+++ b/devtools/client/themes/images/tool-canvas.svg
@@ -1,7 +1,9 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path opacity="0.5" d="M3.7 3.6H2.1c-.1 0-.1.1-.1.1v1.6c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1V3.7c.1-.1 0-.1-.1-.1zM3.7 7.3H2.1c-.1 0-.1.1-.1.1V9c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1V7.5c.1-.1 0-.2-.1-.2zM3.7 11.1H2.1c-.1 0-.1.1-.1.1v1.6c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1v-1.6c.1 0 0-.1-.1-.1zM6.1 5.5H4.5c-.1 0-.1.1-.1.1v1.6c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1l-.1-1.7c.2 0 .1 0 0 0zM6.1 9.2H4.5c-.1 0-.1.1-.1.1V11c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1V9.4c.1-.1 0-.2-.1-.2zM8.3 3.6H6.7c-.1 0-.1.1-.1.1v1.6c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1V3.7c.1-.1 0-.1-.1-.1zM8.3 7.3H6.7c-.1 0-.1.1-.1.1V9c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1V7.5c.1-.1 0-.2-.1-.2zM8.3 11.1H6.7c-.1 0-.1.1-.1.1v1.6c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1v-1.6c.1 0 0-.1-.1-.1zM10.6 5.5H9c-.1 0-.1.1-.1.1v1.6c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1V5.6c0-.1 0-.1-.1-.1zM10.6 9.2H9c-.1 0-.1.1-.1.1V11c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1V9.4c0-.1 0-.2-.1-.2zM12.9 3.6h-1.6c-.1 0-.1.1-.1.1v1.6c0 .1.1.1.1.1h1.6c.1.1.1 0 .1-.1V3.7c0-.1 0-.1-.1-.1zM12.9 7.3h-1.6c-.1 0-.1.1-.1.1V9c0 .1.1.1.1.1h1.6c.1.1.1.1.1 0V7.5c0-.1 0-.2-.1-.2zM12.9 11.1h-1.6c-.1 0-.1.1-.1.1v1.6c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1v-1.6s0-.1-.1-.1z"/>
-  <path d="M13.2 2.8H1.8c-.4 0-.8.4-.8.9v9.2c0 .4.3.9.8.9h11.4c.4 0 .8-.4.8-.9V3.7c0-.7-.6-.9-.8-.9zm-.2 10H2v-9h11v9z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <g fill-rule="evenodd">
+    <path d="M1 2.007C1 1.45 1.45 1 2.007 1h11.986C14.55 1 15 1.45 15 2.007v11.986C15 14.55 14.55 15 13.993 15H2.007C1.45 15 1 14.55 1 13.993V2.007zM2 2h12v12H2V2z"/>
+    <path d="M3 3h2v2H3zM11 3h2v2h-2zM7 3h2v2H7zM3 7h2v2H3zM11 7h2v2h-2zM7 7h2v2H7zM5 5h2v2H5zM9 5h2v2H9zM3 11h2v2H3zM11 11h2v2h-2zM7 11h2v2H7zM5 9h2v2H5zM9 9h2v2H9z" opacity="0.5"/>
+  </g>
 </svg>
--- a/devtools/client/themes/images/tool-debugger-paused.svg
+++ b/devtools/client/themes/images/tool-debugger-paused.svg
@@ -1,7 +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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="#5FC749">
-  <path d="M8.2 1.7C4.8 1.7 2 4.4 2 7.9s2.7 6.3 6.3 6.3c3.5 0 6.3-2.7 6.3-6.3-.1-3.4-2.9-6.2-6.4-6.2zm0 11.4C5.3 13.1 3 10.8 3 7.9s2.3-5.2 5.2-5.2 5.2 2.3 5.2 5.2c0 3-2.2 5.2-5.2 5.2z"/>
-  <path d="M11.4 7.5L10 6.2c-.1-.1-.2-.1-.4-.1H5.7c-.1 0-.2.1-.2.2v3.2c0 .1.1.4.2.4h4c.1 0 .2-.1.2-.1l1.5-1.7c.2-.1.2-.4 0-.6z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#5FC749">
+  <path d="M2 5v6c0 .109.039.342.144.553.15.297.374.447.856.447h9l-.78.375 4-5v1.25l-4-5L12 4H3c-.482 0-.707.15-.856.447A1.403 1.403 0 0 0 2 5zM1 5s0-2 2-2h9l4 5-4 5H3c-2 0-2-2-2-2V5z"/>
 </svg>
--- a/devtools/client/themes/images/tool-debugger.svg
+++ b/devtools/client/themes/images/tool-debugger.svg
@@ -1,7 +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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M8.2 1.7C4.8 1.7 2 4.4 2 7.9s2.7 6.3 6.3 6.3c3.5 0 6.3-2.7 6.3-6.3-.1-3.4-2.9-6.2-6.4-6.2zm0 11.4C5.3 13.1 3 10.8 3 7.9 3 5 5.3 2.7 8.2 2.7c2.9 0 5.2 2.3 5.2 5.2 0 3-2.2 5.2-5.2 5.2z"/>
-  <path d="M6.5 5.3c-.2 0-.4.2-.4.4v4.5c0 .2.3.4.5.4s.5-.2.5-.4V5.7c0-.2-.3-.4-.5-.4M9.5 5.3c-.2 0-.4.2-.4.4v4.5c0 .2.3.4.5.4s.5-.2.5-.4V5.7c0-.2-.3-.4-.5-.4"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M2 5v6c0 .109.039.342.144.553.15.297.374.447.856.447h9l-.78.375 4-5v1.25l-4-5L12 4H3c-.482 0-.707.15-.856.447A1.403 1.403 0 0 0 2 5zM1 5s0-2 2-2h9l4 5-4 5H3c-2 0-2-2-2-2V5z"/>
 </svg>
--- a/devtools/client/themes/images/tool-dom.svg
+++ b/devtools/client/themes/images/tool-dom.svg
@@ -1,6 +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/. -->
-<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M.2 6.3l3.5 3.6c.1.1.3.2.5.2h.9c.3 0 .5-.2.6-.4.1-.2.1-.5-.1-.7l-3-3.1 3-2.9c.2-.2.3-.5.2-.7-.2-.4-.5-.5-.7-.5h-.9c-.2 0-.4 0-.5.2L.2 5.4c-.3.2-.3.6 0 .9M15.8 9.7l-3.5-3.6c-.1-.1-.3-.2-.5-.2h-.9c-.3 0-.5.2-.6.4-.1.2-.1.5.1.7l3 3.1-3 2.9c-.2.2-.3.5-.2.7.1.3.3.4.6.4h.9c.2 0 .3-.1.5-.2l3.5-3.4c.4-.1.4-.5.1-.8"/>
-</svg>
\ No newline at end of file
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M6.052 1.13L1.164 5.57a.5.5 0 0 0 0 .74l5 4.56a.5.5 0 0 0 .673-.74l-5-4.559v.74l4.887-4.44a.5.5 0 0 0-.672-.741zM10.948 14.87l4.888-4.44a.5.5 0 0 0 0-.74l-5-4.56a.5.5 0 1 0-.673.74l5 4.559v-.74l-4.887 4.44a.5.5 0 0 0 .672.741z"/>
+</svg>
--- a/devtools/client/themes/images/tool-inspector.svg
+++ b/devtools/client/themes/images/tool-inspector.svg
@@ -1,6 +1,7 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M14.3 7.7H13V4.8c0-.7-.6-.9-.8-.9H8.3V2.4c.1-.3-.2-.6-.5-.6s-.6.3-.6.6v1.5H2.9c-.5 0-.9.4-.9.9v3H.7c-.3 0-.6.3-.6.6s.3.6.6.6H2v3c0 .4.3.9.8.9h4.3v1.3c0 .3.3.6.6.6s.6-.3.6-.6v-1.3h3.9c.4 0 .8-.4.8-.9V8.9h1.2c.3 0 .6-.3.6-.6s-.2-.6-.5-.6zM12 11.9H3v-7h9v7z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M3 3.995v8.01c0-.01.005-.005.002-.005h9.996c-.001 0 .002-.003.002.005v-8.01c0 .01-.005.005-.002.005H3.002C3.003 4 3 4.003 3 3.995zm-1 0C2 3.445 2.456 3 3.002 3h9.996C13.55 3 14 3.456 14 3.995v8.01c0 .55-.456.995-1.002.995H3.002A1.005 1.005 0 0 1 2 12.005v-8.01z"/>
+  <path d="M8.5 3.5V2a.5.5 0 0 0-1 0v1.5a.5.5 0 0 0 1 0zM1 8.5h1a.5.5 0 0 0 0-1H1a.5.5 0 0 0 0 1zM14 8.5h1a.5.5 0 1 0 0-1h-1a.5.5 0 1 0 0 1zM8.5 14v-1.5a.5.5 0 1 0-1 0V14a.5.5 0 1 0 1 0z"/>
 </svg>
--- a/devtools/client/themes/images/tool-memory-active.svg
+++ b/devtools/client/themes/images/tool-memory-active.svg
@@ -1,7 +1,10 @@
 <!-- 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" viewBox="0 0 16 16" fill="#5FC749">
-  <path opacity="0.2" d="M5.2 2.9l5.7.1v9.4H5.2V2.9z"/>
-  <path d="M10.5 2.2h-5c-.4 0-.8.4-.8.9v9.2c0 .4.3.9.8.9h5c.4 0 .8-.4.8-.9V3.1s-.6-.9-.8-.9zm-.2 10H5.7v-9h4.6v9zM15.5 6.6c-.1 0-.3-.1-.4-.2l-1.5-1.7-.9.9c-.2.2-.5.2-.7 0-.2-.2-.2-.5 0-.7l1.3-1.3c.1-.1.2-.1.4-.1.1 0 .3.1.4.2l1.9 2c.2.2.2.5 0 .7-.3.2-.4.2-.5.2zM15.5 9.7c-.1 0-.3-.1-.4-.2l-1.5-1.7-.9.9c-.2.3-.5.3-.7.1-.2-.2-.2-.5 0-.7l1.3-1.2c.1-.1.2-.1.4-.1.1 0 .3.1.4.2L16 9c.2.2.2.5 0 .7h-.5zM15.5 12.7c-.1 0-.3-.1-.4-.2l-1.5-1.7-.9.9c-.2.2-.5.2-.7 0-.2-.2-.2-.5 0-.7l1.3-1.2c.1-.1.2-.1.4-.1.1 0 .3.1.4.2l1.9 2c.2.2.2.5 0 .7-.3.1-.4.1-.5.1zM.5 6.6c.1 0 .3-.1.4-.2l1.5-1.7.9.9c.2.2.5.2.7 0 .2-.2.2-.5 0-.7L2.7 3.7c-.1-.1-.2-.1-.3-.1-.2 0-.3 0-.4.1l-1.9 2c-.2.3-.1.6.1.8.1.1.2.1.3.1zM.5 9.7c.1 0 .3-.1.4-.2l1.5-1.7.9.9c.2.3.5.3.7.1.2-.2.2-.6 0-.8L2.7 6.8c-.1-.1-.2-.1-.3-.1-.2 0-.3 0-.4.1l-1.9 2c-.2.2-.2.5 0 .7.2.2.3.2.4.2zM.5 12.7c.1 0 .3-.1.4-.2l1.5-1.7.9.9c.2.3.5.3.7.1.2-.2.2-.5 0-.7L2.7 9.8c-.1-.1-.2-.1-.3-.1-.2 0-.3 0-.4.1l-1.9 2c-.2.2-.2.5 0 .7.2.2.3.2.4.2z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="#5FC749">
+  <path d="M4.727 8.055l-1.96-1a.5.5 0 0 0-.573.083L.655 8.602a.5.5 0 1 0 .69.725l1.539-1.465-.572.083 1.96 1a.5.5 0 1 0 .455-.89z"/>
+  <path d="M4.727 10.055l-1.96-1a.5.5 0 0 0-.573.083L.655 10.602a.5.5 0 1 0 .69.725l1.539-1.465-.572.083 1.96 1a.5.5 0 1 0 .455-.89zM11.727 10.945l1.961-1-.572-.083 1.54 1.465a.5.5 0 1 0 .689-.725l-1.54-1.464a.5.5 0 0 0-.571-.083l-1.961 1a.5.5 0 1 0 .454.89z"/>
+  <path d="M11.727 8.945l1.961-1-.572-.083 1.54 1.465a.5.5 0 1 0 .689-.725l-1.54-1.464a.5.5 0 0 0-.571-.083l-1.961 1a.5.5 0 1 0 .454.89z"/>
+  <path d="M11.727 6.945l1.961-1-.572-.083 1.54 1.465a.5.5 0 1 0 .689-.725l-1.54-1.464a.5.5 0 0 0-.571-.083l-1.961 1a.5.5 0 1 0 .454.89zM4.727 6.055l-1.96-1a.5.5 0 0 0-.573.083L.655 6.602a.5.5 0 1 0 .69.725l1.539-1.465-.572.083 1.96 1a.5.5 0 1 0 .455-.89z"/>
+  <path d="M5 3.002v9.996c0-.001.003.002-.003.002h6.006c-.006 0-.003-.003-.003-.002V3.002c0 .001-.003-.002.003-.002H4.997c.006 0 .003.003.003.002zm-1 0C4 2.45 4.453 2 4.997 2h6.006c.55 0 .997.456.997 1.002v9.996c0 .553-.453 1.002-.997 1.002H4.997C4.447 14 4 13.544 4 12.998V3.002z"/>
 </svg>
--- a/devtools/client/themes/images/tool-memory.svg
+++ b/devtools/client/themes/images/tool-memory.svg
@@ -1,7 +1,10 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path opacity="0.2" d="M5.2 2.9l5.7.1v9.4H5.2V2.9z"/>
-  <path d="M10.5 2.2h-5c-.4 0-.8.4-.8.9v9.2c0 .4.3.9.8.9h5c.4 0 .8-.4.8-.9V3.1s-.6-.9-.8-.9zm-.2 10H5.7v-9h4.6v9zM15.5 6.6c-.1 0-.3-.1-.4-.2l-1.5-1.7-.9.9c-.2.2-.5.2-.7 0-.2-.2-.2-.5 0-.7l1.3-1.3c.1-.1.2-.1.4-.1.1 0 .3.1.4.2l1.9 2c.2.2.2.5 0 .7-.3.2-.4.2-.5.2zM15.5 9.7c-.1 0-.3-.1-.4-.2l-1.5-1.7-.9.9c-.2.3-.5.3-.7.1-.2-.2-.2-.5 0-.7l1.3-1.2c.1-.1.2-.1.4-.1.1 0 .3.1.4.2L16 9c.2.2.2.5 0 .7h-.5zM15.5 12.7c-.1 0-.3-.1-.4-.2l-1.5-1.7-.9.9c-.2.2-.5.2-.7 0-.2-.2-.2-.5 0-.7l1.3-1.2c.1-.1.2-.1.4-.1.1 0 .3.1.4.2l1.9 2c.2.2.2.5 0 .7-.3.1-.4.1-.5.1zM.5 6.6c.1 0 .3-.1.4-.2l1.5-1.7.9.9c.2.2.5.2.7 0 .2-.2.2-.5 0-.7L2.7 3.7c-.1-.1-.2-.1-.3-.1-.2 0-.3 0-.4.1l-1.9 2c-.2.3-.1.6.1.8.1.1.2.1.3.1zM.5 9.7c.1 0 .3-.1.4-.2l1.5-1.7.9.9c.2.3.5.3.7.1.2-.2.2-.6 0-.8L2.7 6.8c-.1-.1-.2-.1-.3-.1-.2 0-.3 0-.4.1l-1.9 2c-.2.2-.2.5 0 .7.2.2.3.2.4.2zM.5 12.7c.1 0 .3-.1.4-.2l1.5-1.7.9.9c.2.3.5.3.7.1.2-.2.2-.5 0-.7L2.7 9.8c-.1-.1-.2-.1-.3-.1-.2 0-.3 0-.4.1l-1.9 2c-.2.2-.2.5 0 .7.2.2.3.2.4.2z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M4.727 8.055l-1.96-1a.5.5 0 0 0-.573.083L.655 8.602a.5.5 0 1 0 .69.725l1.539-1.465-.572.083 1.96 1a.5.5 0 1 0 .455-.89z"/>
+  <path d="M4.727 10.055l-1.96-1a.5.5 0 0 0-.573.083L.655 10.602a.5.5 0 1 0 .69.725l1.539-1.465-.572.083 1.96 1a.5.5 0 1 0 .455-.89zM11.727 10.945l1.961-1-.572-.083 1.54 1.465a.5.5 0 1 0 .689-.725l-1.54-1.464a.5.5 0 0 0-.571-.083l-1.961 1a.5.5 0 1 0 .454.89z"/>
+  <path d="M11.727 8.945l1.961-1-.572-.083 1.54 1.465a.5.5 0 1 0 .689-.725l-1.54-1.464a.5.5 0 0 0-.571-.083l-1.961 1a.5.5 0 1 0 .454.89z"/>
+  <path d="M11.727 6.945l1.961-1-.572-.083 1.54 1.465a.5.5 0 1 0 .689-.725l-1.54-1.464a.5.5 0 0 0-.571-.083l-1.961 1a.5.5 0 1 0 .454.89zM4.727 6.055l-1.96-1a.5.5 0 0 0-.573.083L.655 6.602a.5.5 0 1 0 .69.725l1.539-1.465-.572.083 1.96 1a.5.5 0 1 0 .455-.89z"/>
+  <path d="M5 3.002v9.996c0-.001.003.002-.003.002h6.006c-.006 0-.003-.003-.003-.002V3.002c0 .001-.003-.002.003-.002H4.997c.006 0 .003.003.003.002zm-1 0C4 2.45 4.453 2 4.997 2h6.006c.55 0 .997.456.997 1.002v9.996c0 .553-.453 1.002-.997 1.002H4.997C4.447 14 4 13.544 4 12.998V3.002z"/>
 </svg>
--- a/devtools/client/themes/images/tool-network.svg
+++ b/devtools/client/themes/images/tool-network.svg
@@ -1,9 +1,9 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path opacity="0.5" d="M10.7 3.3c0-.2-.2-.4-.4-.4H2.8c-.2 0-.4.3-.4.5s.2.5.4.5h7.5c.2 0 .4-.3.4-.5"/>
-  <path d="M12.7 6.4c0-.2-.2-.4-.4-.4H4.8c-.2 0-.4.3-.4.5s.2.5.4.5h7.5c.2 0 .4-.3.4-.5"/>
-  <path opacity="0.5" d="M10.7 9.3c0-.2-.2-.4-.4-.4H3.8c-.2 0-.4.3-.4.5s.2.5.4.5h6.5c.2 0 .4-.3.4-.5"/>
-  <path d="M6.7 12.3c0-.2-.2-.4-.4-.4H2.8c-.2 0-.4.3-.4.5s.2.5.4.5h3.5c.2 0 .4-.3.4-.5"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <rect fill-opacity=".5" x="2" y="3" width="8" height="1" rx=".5"/>
+  <rect x="6" y="6" width="8" height="1" rx=".5"/>
+  <rect fill-opacity=".5" x="4" y="9" width="8" height="1" rx=".5"/>
+  <rect x="2" y="12" width="5" height="1" rx=".5"/>
 </svg>
--- a/devtools/client/themes/images/tool-options.svg
+++ b/devtools/client/themes/images/tool-options.svg
@@ -1,7 +1,7 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M12.3 10.8c.1-.2.2-.6.3-.6h2.1V6.8h-2.1c-.1 0-.2-.3-.3-.6l.6-.5c.2-.2.3-.3.3-.6 0-.2-.1-.4-.3-.6l-.7-.5c-.3-.3-.9-.3-1.2 0l-.4.6c-.3-.2-.6-.3-.6-.4V2.1H6.6v2.1c0 .1-.3.2-.6.3L5.6 4c-.4-.3-.9-.3-1.2 0l-.7.7c-.3.3-.3.9 0 1.2l.6.4c-.1.2-.2.5-.3.5H1.9v3.4H4c.1 0 .2.3.3.6L3 12l2 2 1-1.4c.2.1.6.2.6.3V15H10v-2.1c0-.1.3-.2.6-.3l.5.6c.3.3.9.3 1.2 0l.7-.7c.2-.2.2-.4.2-.6 0-.2-.1-.4-.3-.6l-.6-.5zm.1 1.1l-.7.7h-.1-.1l-.8-.8c-.1-.1-.3-.1-.5-.1-.3.2-.6.4-.9.4-.2 0-.2.2-.2.4V14H7.5v-1.5c0-.2 0-.3-.2-.4-.4-.1-.7-.2-1-.4-.1 0-.1-.1-.2-.1s-.2 0-.3.1l-.9 1h-.2L4 12v-.1-.1l1-1c.1-.1.1-.3.1-.5-.2-.1-.3-.3-.4-.7 0-.2-.2-.2-.4-.2H2.8V7.7h1.5c.2 0 .3 0 .4-.2.1-.4.2-.7.4-1 .1-.1.1-.3-.1-.4l-.7-.8v-.1l.7-.7h.2l.8.8c0 .1.2.1.4 0 .3-.2.5-.3.9-.4.2 0 .2-.2.2-.4V3h1.7v1.5c0 .2 0 .3.2.4.4.1.7.2 1 .4.1.1.3.1.4-.1l.8-.8h.1l.7.7v.2l-.8.8c-.1.1-.1.3-.1.5.2.3.4.6.4.9 0 .2.2.2.4.2h1.5v1.7h-1.5c-.2 0-.3 0-.4.2-.1.4-.2.7-.4 1-.1.2-.1.3.1.4l.8.8v.1z"/>
-  <path d="M8.3 6C6.9 6 5.7 7.1 5.7 8.6s1.2 2.6 2.6 2.6 2.6-1.2 2.6-2.6S9.7 6 8.3 6zm0 4.4c-1 0-1.8-.8-1.8-1.8s.8-1.8 1.8-1.8 1.8.8 1.8 1.8-.8 1.8-1.8 1.8z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M8.513 3.416v-.918A.502.502 0 0 0 8.012 2h-.999c-.273 0-.5.226-.5.498V4.07l-.6.262c-.274.12-.534.27-.775.449l-.527.39-.567-.328-.796-.46a.502.502 0 0 0-.682.185l-.5.864a.504.504 0 0 0 .182.683l.795.46.567.326-.073.65a4.055 4.055 0 0 0 0 .898l.073.65-.567.327-.795.459a.502.502 0 0 0-.181.683l.499.864c.137.237.446.32.682.185l.796-.46.567-.327.527.39c.24.177.5.328.775.448l.6.262v1.572c0 .272.225.498.5.498h.999c.273 0 .5-.226.5-.498V11.93l.6-.262c.274-.12.534-.27.775-.449l.527-.39.567.328.796.46a.502.502 0 0 0 .682-.185l.5-.864a.504.504 0 0 0-.182-.683l-.795-.46-.567-.326.073-.65a4.055 4.055 0 0 0 0-.898l-.073-.65.567-.327.795-.459a.502.502 0 0 0 .181-.683l-.499-.864a.504.504 0 0 0-.682-.185l-.796.46-.567.327-.527-.39c-.24-.177-.5-.328-.775-.448l-.6-.262v-.654zm1 0c.345.15.67.34.968.56l.796-.459a1.504 1.504 0 0 1 2.048.55l.5.865a1.502 1.502 0 0 1-.548 2.05l-.795.459a5.055 5.055 0 0 1 0 1.118l.795.46c.717.414.958 1.337.547 2.049l-.499.864a1.502 1.502 0 0 1-2.048.55l-.796-.458c-.299.22-.623.41-.968.56v.918c0 .827-.679 1.498-1.501 1.498h-.999c-.829 0-1.5-.675-1.5-1.498v-.918c-.345-.15-.67-.34-.97-.56l-.795.459a1.504 1.504 0 0 1-2.048-.55l-.5-.865a1.502 1.502 0 0 1 .548-2.05l.795-.459a5.055 5.055 0 0 1 0-1.118l-.795-.46A1.504 1.504 0 0 1 1.2 4.932l.499-.864a1.502 1.502 0 0 1 2.048-.55l.796.458c.299-.22.624-.41.969-.56v-.918c0-.827.678-1.498 1.5-1.498h.999c.829 0 1.5.675 1.5 1.498v.918z"/>
+    <path d="M7.5 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm0 1a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/>
 </svg>
--- a/devtools/client/themes/images/tool-profiler.svg
+++ b/devtools/client/themes/images/tool-profiler.svg
@@ -1,8 +1,9 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M6.9 7.8c-.3.1-.6.4-.6.7-.1.5.2 1 .7 1.1.3 0 .7-.1.9-.3l2.5-2.9-3.5 1.4z"/>
-  <path opacity="0.5" d="M4.7 10.6c.7 1.1 1.9 1.9 3.3 1.9s2.6-.8 3.3-1.9H4.7z"/>
-  <path d="M7.9 2.3C4.6 2.3 1.8 5 1.8 8.5s2.7 6.3 6.3 6.3c3.5 0 6.3-2.7 6.3-6.3-.2-3.4-3-6.2-6.5-6.2zm0 11.4c-2.9 0-5.2-2.3-5.2-5.2v-.4h1.8c.2 0 .4-.3.4-.5s-.2-.5-.4-.5H2.9C3.2 5.9 4 4.8 5 4.1l1.4 1.4c.2.1.6.1.7-.1.1-.1.2-.5.1-.6L6 3.7c.6-.2 1.2-.4 1.9-.4.8 0 1.5.2 2.2.5L9 4.8c-.1.1 0 .5.1.6.1.1.5.2.6.1L11 4.3c.9.7 1.6 1.7 1.9 2.8h-1.6c-.2 0-.4.3-.4.5s.2.5.4.5h1.8v.4c0 3-2.2 5.2-5.2 5.2z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke" fill-rule="evenodd">
+  <path d="M15 9.004C14.51 12.394 11.578 15 8.035 15 4.15 15 1 11.866 1 8s3.15-7 7.036-7c1.941 0 3.7.783 4.972 2.048l-.709.709A6.027 6.027 0 0 0 8.036 2c-3.33 0-6.03 2.686-6.03 6s2.7 6 6.03 6a6.023 6.023 0 0 0 5.946-4.993l1.017-.003z"/>
+  <path d="M4.137 9H3.1a5.002 5.002 0 0 0 9.8 0h-.965a4.023 4.023 0 0 1-3.9 3 4.023 4.023 0 0 1-3.898-3z" fill-opacity=".5"/>
+  <path d="M8.036 11a2.994 2.994 0 0 0 2.987-3c0-1.657-1.338-3-2.987-3a2.994 2.994 0 0 0-2.988 3c0 1.657 1.338 3 2.988 3zm0-1c1.11 0 2.011-.895 2.011-2s-.9-2-2.011-2c-1.111 0-2.012.895-2.012 2s.9 2 2.012 2z"/>
+  <path d="M10.354 6.354l4-4a.5.5 0 0 0-.708-.708l-4 4a.5.5 0 1 0 .708.708z"/>
 </svg>
--- a/devtools/client/themes/images/tool-scratchpad.svg
+++ b/devtools/client/themes/images/tool-scratchpad.svg
@@ -1,7 +1,7 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M13.2 3h-1.3v-.8c0-.3-.3-.6-.6-.6s-.6.3-.6.6V3H7.8v-.8c0-.3-.3-.6-.6-.6s-.7.2-.7.6V3H3.7v-.8c0-.3-.3-.6-.6-.6s-.6.2-.6.6V3H.8c-.4 0-.8.4-.8.9v10.2c0 .4.3.9.8.9h12.4c.4 0 .8-.4.8-.9V3.9s-.6-.9-.8-.9zM13 14H1V3.9h12V14z"/>
-  <path d="M8.7 12.1h-6c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h6c.3 0 .5.2.5.5s-.3.5-.5.5zM11.5 9.9H5.4c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h6.1c.3 0 .5.2.5.5s-.2.5-.5.5zM7.7 7.8H3c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h4.7c.3 0 .5.2.5.5s-.2.5-.5.5z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M5 1.5a.5.5 0 0 0-1 0v2a.5.5 0 0 0 1 0v-2zM8.5 3.5v-2a.5.5 0 0 0-1 0v2a.5.5 0 0 0 1 0zM12 3.5v-2a.5.5 0 1 0-1 0v2a.5.5 0 1 0 1 0zM5 7h4a.5.5 0 0 0 0-1H5a.5.5 0 0 0 0 1zM5 11h2a.5.5 0 1 0 0-1H5a.5.5 0 1 0 0 1zM6 9h5a.5.5 0 1 0 0-1H6a.5.5 0 0 0 0 1z"/>
+  <path d="M3 3.996v9.008c0-.003 0-.004.002-.004h9.996c-.001 0 .002-.003.002.004V3.996c0 .003 0 .004-.002.004H3.002C3.003 4 3 4.003 3 3.996zm-1 0C2 3.446 2.456 3 3.002 3h9.996A.998.998 0 0 1 14 3.996v9.008c0 .55-.456.996-1.002.996H3.002A.998.998 0 0 1 2 13.004V3.996z"/>
 </svg>
--- a/devtools/client/themes/images/tool-shadereditor.svg
+++ b/devtools/client/themes/images/tool-shadereditor.svg
@@ -1,14 +1,12 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path opacity="0.2" d="M2.5 3.9l11.1 8.3-11.4.1.3-8.4z"/>
-  <path d="M-8.6 9.8l-5.5-5.5c-.3-.3-.8-.3-1 0-.3.3-.3.8 0 1l5.5 5.5c.1.1.3.2.5.2s.4-.1.5-.2c.2-.3.2-.7 0-1z"/>
-  <path d="M-6 11.2V2.9c.5-.2.9-.7.9-1.3C-5.1.8-5.8.1-6.6.1-7.3.1-7.9.6-8 1.3h-8.1c-.1-.7-.7-1.2-1.4-1.2-.8 0-1.5.7-1.5 1.5 0 .6.4 1.2 1 1.4v8.2c-.6.2-1 .7-1 1.4 0 .8.7 1.5 1.5 1.5.5 0 1-.3 1.3-.7h8.5c.3.4.7.7 1.3.7.8 0 1.5-.7 1.5-1.5-.2-.6-.6-1.2-1.1-1.4zm-10-7.9h8v8h-8v-8z"/>
-  <circle cx="14.9" cy="2.6" r="1.1"/>
-  <circle cx="1.1" cy="2.6" r="1.1"/>
-  <circle cx="1.3" cy="13.5" r="1.1"/>
-  <circle cx="14.9" cy="13.2" r="1.1"/>
-  <path d="M13.4 2.9H2.6c-.4 0-.8.4-.8.9V12c0 .4.3.9.8.9h10.8c.4 0 .8-.4.8-.9V3.8c0-.7-.6-.9-.8-.9zm-.2 8.3L3.5 3.9h9.7v7.3zM2.8 4.6l9.6 7.3H2.8V4.6z"/>
-  <path opacity="0.2" d="M8.3 4.1H6.7c-.1 0-.1.1-.1.1v1.6c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1V4.2c.1-.1 0-.1-.1-.1zM10.6 5.8H9c-.1 0-.1.1-.1.1v1.6c0 .1.1.1.1.1h1.6c.1 0 .1-.1.1-.1V5.9c0-.1 0-.1-.1-.1zM12.7 4.2h-1.6c-.1 0-.1.1-.1.1v1.6c0 .1.1.1.1.1h1.6c.1.1.1 0 .1-.1V4.3c0-.1 0-.1-.1-.1zM12.8 7.7h-1.6c-.1 0-.1.1-.1.1v1.6c0 .1.1.1.1.1h1.6c.1.1.1.1.1 0V7.9c0-.1 0-.2-.1-.2z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M2 4v8h12V4H2zm0-1h12a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z"/>
+  <circle cx="1" cy="3" r="1"/>
+  <circle cx="1" cy="13" r="1"/>
+  <circle cx="15" cy="13" r="1"/>
+  <circle cx="15" cy="3" r="1"/>
+  <path d="M1.215 3.911l13 9 .411.285.57-.822-.411-.285-13-9-.411-.285-.57.822z"/>
+  <path fill-opacity=".3" d="M8 5h2v2H8zM8 8h2v2L9 8.711zM5 5.962V5h2v2h-.828l-.729-.368zM11 5h2v2h-2zM11 8h2v2h-2z"/>
 </svg>
--- a/devtools/client/themes/images/tool-storage.svg
+++ b/devtools/client/themes/images/tool-storage.svg
@@ -1,11 +1,7 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M6.2 28C.3 28 .3 26.2.3 25.8V25c0-1.7 3.6-2.1 5.9-2.1s6.1.4 6.1 2.1v.8c0 .4-.2 2.2-6.1 2.2z"/>
-  <path d="M6.2 30c-6 0-5.9-2.1-5.9-2.1v1.8c0 .4 0 2.3 5.8 2.3 6 0 6.2-1.9 6.2-2.3V28s0 2-6.1 2z"/>
-  <path d="M6.2 33.7c-6 0-5.9-2.1-5.9-2.1v1.8c0 .4 0 2.3 5.8 2.3 6 0 6.2-1.9 6.2-2.3v-1.6c0-.2 0 1.8-6.1 1.9z"/>
-  <path d="M-4.1-3.1h-2.6v-1.5c0-.8-1-1.5-1.8-1.5h-2.4c-.8 0-1.8.7-1.8 1.5v1.5h-2.6c-.8 0-1.4.6-1.4 1.4v6.5c0 .8.6 1.1 1.4 1.1h11.2c.8 0 1.4-.3 1.4-1.1v-6.5c-.1-.8-.7-1.4-1.4-1.4zm.3 8h-12v-7h4v-3h4v3h3.9l.1 7z"/>
-  <path d="M-9.8-1.3c-1.3 0-2.4 1.1-2.4 2.4s1.1 2.4 2.4 2.4 2.4-1.1 2.4-2.4c0-1.3-1-2.4-2.4-2.4zm0 3.5c-.7 0-1.2-.5-1.2-1.1S-10.4 0-9.8 0s1.2.5 1.2 1.1-.5 1.1-1.2 1.1z"/>
-  <path d="M14 6.9v-3c0-2.8-10.3-2.8-10.3 0V12.3c.1 1.4 2.7 2.1 5.2 2.1s5-.7 5.1-2.1V6.9c0 .1 0 0 0 0zM4.7 5.2c1 .5 2.6.8 4.2.8s3.1-.3 4.1-.8V7c-.1.4-1.4 1-4 1-2.5 0-4.2-.6-4.3-1V5.2zm4.2-2.4c2.7 0 4.1.8 4.1 1.1S11.6 5 8.9 5c-2.7 0-4.2-.8-4.2-1.1s1.5-1.1 4.2-1.1zM4.7 8.2c1 .5 2.7.8 4.3.8 1.5 0 3-.3 4-.8v1c-.1.4-1.4 1.1-4 1.1s-4.2-.7-4.3-1.1v-1zm4.2 5.2c-2.6 0-4.1-.7-4.2-1.1v-1.9c1 .6 2.7.8 4.3.8 1.6 0 3.1-.3 4-.8v1.9c-.1.3-1.6 1.1-4.1 1.1z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M7.5 7.556c3.006 0 5.5-1.136 5.5-2.778C13 3.136 10.506 2 7.5 2S2 3.136 2 4.778C2 6.42 4.494 7.556 7.5 7.556zm0-1c-2.517 0-4.5-.903-4.5-1.778S4.983 3 7.5 3s4.5.903 4.5 1.778-1.983 1.778-4.5 1.778zM7.5 14.445c3.006 0 5.5-1.137 5.5-2.778 0-.878-.595-1.606-1.657-2.081-.244-.11-.473-.107-.778-.033-.056.014-.565.158-.765.205-.626.148-1.342.231-2.3.231-.973 0-1.683-.082-2.273-.225a18.574 18.574 0 0 1-.673-.193c-.277-.076-.479-.089-.707-.005l-.035.014C2.638 10.064 2 10.756 2 11.667c0 1.641 2.494 2.778 5.5 2.778zm0-1c-2.517 0-4.5-.904-4.5-1.778 0-.432.354-.816 1.194-1.163h-.002c-.012.005.003.006.097.032-.056-.016.474.144.702.2.669.162 1.458.253 2.509.253 1.035 0 1.828-.092 2.53-.257.228-.054.74-.2.77-.207a.756.756 0 0 1 .134-.027c.734.329 1.066.735 1.066 1.169 0 .874-1.983 1.778-4.5 1.778z"/>
+  <path d="M7.5 10.945c3.006 0 5.5-1.137 5.5-2.778 0-.873-.62-1.601-1.693-2.082-.244-.109-.472-.106-.773-.032-.051.013-.551.158-.75.206-.615.147-1.326.23-2.284.23-.973 0-1.68-.082-2.265-.225a17.077 17.077 0 0 1-.66-.19c-.27-.076-.467-.092-.692-.015l-.054.02C2.65 6.568 2 7.259 2 8.168c0 1.641 2.494 2.778 5.5 2.778zm0-1C4.983 9.945 3 9.04 3 8.167c0-.426.364-.813 1.21-1.163l-.003.001c-.011.004.005.005.099.032-.079-.022.465.143.69.198.665.163 1.452.254 2.504.254 1.036 0 1.825-.092 2.517-.258.228-.054.733-.2.758-.207a.766.766 0 0 1 .124-.026c.748.335 1.101.75 1.101 1.169 0 .874-1.983 1.778-4.5 1.778z"/>
 </svg>
--- a/devtools/client/themes/images/tool-styleeditor.svg
+++ b/devtools/client/themes/images/tool-styleeditor.svg
@@ -1,6 +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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M-13 9.3h-.3c-1.6 0-2.5-.9-2.5-2.3 0-.4.1-.8.1-1.2.1-.4.2-.7.2-1.1 0-.3 0-.5-.6-.5-.3 0-.5-.2-.5-.5V3c0-.3.2-.5.5-.5.6 0 .6-.2.6-.5s-.1-.6-.1-1c-.1-.4-.2-.8-.2-1.2 0-1.4.9-2.3 2.5-2.3h.3c.3 0 .5.2.5.5v.7c0 .3-.2.5-.5.5h-.3c-.3.1-.5.1-.5.7 0 .2 0 .5.1.9 0 .3.1.7.1 1.1 0 .6-.2 1.1-.5 1.5.2.3.5.7.5 1.5 0 .3 0 .7-.1 1.1-.1.3-.1.6-.1.9 0 .6.2.7.6.7h.2c.3 0 .5.2.5.5v.7c0 .2-.2.5-.5.5zm7.1 0h-.3c-.3 0-.5-.2-.5-.5v-.7c0-.3.2-.5.5-.5h.2c.4 0 .6-.1.6-.7 0-.2 0-.4-.1-.7V6c0-.3-.1-.7-.1-1.1 0-.7.2-1.2.5-1.5-.3-.3-.5-.8-.5-1.5 0-.4.1-.7.1-1.1V.6c0-.2.1-.5.1-.7 0-.5-.1-.7-.6-.7h-.2c-.3 0-.5-.2-.5-.5V-2c0-.3.2-.5.5-.5h.3c1.5 0 2.5.8 2.5 2.3 0 .4-.1.8-.1 1.1-.1.5-.2.8-.2 1.1 0 .3 0 .5.6.5.3 0 .5.2.5.5v.7c0 .3-.2.5-.5.5-.5 0-.6.2-.6.5s0 .7.1 1 .1.8.1 1.2c.1 1.5-.8 2.4-2.4 2.4zM13.5 7.7c-.6-.1-1-.3-1.2-.6-.1-.2-.2-.5-.2-.9v-1c0-.8-.1-1.3-.4-1.7-.4-.6-1.2-1-2.2-1h-.3v1.2h.2c.5 0 .9.3 1.1.7.1.2.2.6.2 1.1V6c0 .6.1 1.1.2 1.5.2.4.4.7.8.8-.4.2-.6.5-.8.9-.2.3-.2.8-.2 1.4v.9c0 .5-.1.8-.3 1.1-.3.2-.6.4-1 .4h-.2v1.2h.3c.7-.1 1.3-.3 1.8-.6.5-.4.8-1 .8-1.9v-1.2c0-.4.1-.7.2-.9.2-.4.6-.6 1.2-.7h.2V7.8l-.2-.1zM2.8 7.5c.2-.4.2-.9.2-1.4v-.9c0-.5.1-.8.3-1.1.2-.2.5-.4.9-.4h.2V2.5h-.3c-.6.1-1.2.3-1.7.6-.5.4-.8 1.1-.8 1.9v1.2c0 .4-.1.7-.2.9-.2.4-.6.6-1.2.7H0v1.1h.2c.6.1 1 .3 1.2.6.1.2.2.5.2.9v1c0 .8.1 1.3.4 1.7.4.6 1.2 1 2.2 1h.3v-1.2h-.2c-.5 0-.9-.3-1.1-.7-.1 0-.2-.4-.2-.9v-.7c0-.6 0-1-.2-1.4-.2-.4-.4-.7-.8-.8.4-.2.6-.5.8-.9zM-2 16.6c-.4 0-.8-.1-.8-.8 0-.3.1-.7.2-1 .1-.4.2-.8.2-1.2 0-1.2-1.3-1.6-2.1-1.6-.7 0-1 .3-1 .5s.2.4.5.4h.2l.1-.2s.1-.1.3-.1c.3 0 .5.2.5.6 0 .4 0 .8-.1 1.1 0 .4-.1.8-.1 1.3 0 .6.4 1 .8 1.2-.5.2-.8.6-.8 1.2 0 .5 0 .9.1 1.3 0 .4.1.7.1 1.1 0 .3-.2.6-.5.6-.2 0-.3-.1-.3-.1l-.1-.2H-5c-.3.1-.5.2-.5.4s.3.5 1 .5c.8 0 2.1-.4 2.1-1.6 0-.4-.1-.8-.2-1.2-.1-.4-.2-.7-.2-1 0-.7.4-.8.8-.8h.5v-.7H-2z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke">
+  <path d="M5.5 2C3.565 2 2.806 3.12 3.065 4.587c.239 1.346.117 1.76-.435 2.39l-.054.06c-.252.288-.39.474-.523.74L1.94 8l.112.224c.132.265.27.45.523.739l.054.06c.552.63.674 1.044.435 2.39C2.802 12.904 3.527 14 5.5 14a.5.5 0 1 0 0-1c-1.291 0-1.614-.487-1.45-1.413.292-1.65.081-2.37-.669-3.223l-.053-.06c-.2-.229-.296-.357-.38-.528v.448c.084-.17.18-.299.38-.528l.053-.06c.75-.854.961-1.573.67-3.223C3.89 3.515 4.24 3 5.5 3a.5.5 0 1 0 0-1zM10.5 3c1.26 0 1.609.515 1.45 1.413-.292 1.65-.081 2.37.669 3.223l.053.06c.2.229.296.357.38.528v-.448c-.084.17-.18.299-.38.528l-.053.06c-.75.854-.961 1.573-.67 3.223.165.926-.158 1.413-1.449 1.413a.5.5 0 1 0 0 1c1.973 0 2.698-1.096 2.435-2.587-.239-1.346-.117-1.76.435-2.39l.054-.06c.252-.288.39-.474.523-.74L14.06 8l-.112-.224c-.132-.265-.27-.45-.523-.739l-.054-.06c-.552-.63-.674-1.044-.435-2.39C13.194 3.12 12.435 2 10.5 2a.5.5 0 0 0 0 1z"/>
 </svg>
--- a/devtools/client/themes/images/tool-webconsole.svg
+++ b/devtools/client/themes/images/tool-webconsole.svg
@@ -1,7 +1,7 @@
 <!-- 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" viewBox="0 0 16 16" fill="whitesmoke">
-  <path d="M6.8 9.3c0-.2 0-.3-.2-.4L4.9 7.2c-.3-.3-.7-.3-.9 0s-.3.6 0 .9l1.3 1.4L4 10.9c-.3.3-.3.6 0 .9s.6.3.9 0L6.7 10c.1-.2.2-.5.1-.7z"/>
-  <path d="M13.2 3H1.8c-.4 0-.8.4-.8.9v9.2s.3.9.8.9h11.4c.4 0 .8-.4.8-.9V3.9c0-.7-.6-.9-.8-.9zM13 13H2V6h11v7zm0-8H2V4h11v1z"/>
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="whitesmoke" fill-rule="evenodd">
+  <path d="M14 4V3H2v1h12zm0 1v8H2V5h12zM1 3.002C1 2.45 1.45 2 2.007 2h11.986A1.01 1.01 0 0 1 15 3.002v9.996C15 13.55 14.55 14 13.993 14H2.007A1.01 1.01 0 0 1 1 12.998V3.002z"/>
+  <path d="M4.09 7.859l2.062 2-.006-.713-2.061 2.062a.5.5 0 0 0 .707.707l2.062-2.061a.5.5 0 0 0-.006-.713l-2.061-2a.5.5 0 1 0-.697.718z"/>
 </svg>
--- a/devtools/client/themes/memory.css
+++ b/devtools/client/themes/memory.css
@@ -110,28 +110,26 @@ html, body, #app, #memory-tool {
 #clear-snapshots::before {
   background-image: url(chrome://devtools/skin/images/clear.svg);
 }
 
 #diff-snapshots::before {
   background-image: url(chrome://devtools/skin/images/diff.svg);
 }
 
+#import-snapshot::before {
+  background-image: url(chrome://devtools/skin/images/import.svg);
+}
+
 #record-allocation-stacks-label,
 #pop-view-button-label {
   border-inline-end: 1px solid var(--theme-splitter-color);
   padding-inline-end: 5px;
 }
 
-#import-snapshot,
-#clear-snapshots {
-  -moz-box-align: center;
-  flex-grow: 1;
-}
-
 .spacer {
   flex: 1;
 }
 
 #filter {
   align-self: stretch;
   margin: 2px;
 }
--- a/devtools/client/themes/performance.css
+++ b/devtools/client/themes/performance.css
@@ -5,26 +5,26 @@
 
 /* CSS Variables specific to this panel that aren't defined by the themes */
 .theme-dark {
   --cell-border-color: rgba(255,255,255,0.15);
   --cell-border-color-light: rgba(255,255,255,0.1);
   --focus-cell-border-color: rgba(255,255,255,0.5);
   --row-alt-background-color: rgba(86, 117, 185, 0.15);
   --row-hover-background-color: rgba(86, 117, 185, 0.25);
-  --filter-image: url(chrome://devtools/skin/images/timeline-filter.svg);
+  --filter-image: url(chrome://devtools/skin/images/filter.svg);
 }
 
 .theme-light {
   --cell-border-color: rgba(0,0,0,0.15);
   --cell-border-color-light: rgba(0,0,0,0.1);
   --focus-cell-border-color: rgba(0,0,0,0.3);
   --row-alt-background-color: rgba(76,158,217,0.1);
   --row-hover-background-color: rgba(76,158,217,0.2);
-  --filter-image: url(chrome://devtools/skin/images/timeline-filter.svg);
+  --filter-image: url(chrome://devtools/skin/images/filter.svg);
 }
 
 .theme-firebug {
   --cell-border-color: rgba(0,0,0,0.15);
   --cell-border-color-light: rgba(0,0,0,0.1);
   --focus-cell-border-color: rgba(0,0,0,0.3);
   --row-alt-background-color: rgba(76,158,217,0.1);
   --row-hover-background-color: rgba(76,158,217,0.2);
@@ -86,21 +86,25 @@
 }
 
 /* Recording buttons */
 
 #main-record-button {
   list-style-image: url(images/profiler-stopwatch.svg);
 }
 
-#main-record-button .button-icon {
+#import-button {
+  list-style-image: url(images/import.svg);
+}
+
+#main-record-button .button-icon, #import-button .button-icon {
   margin: 0;
 }
 
-#main-record-button .button-text {
+#main-record-button .button-text, #import-button .button-text {
   display: none;
 }
 
 .notice-container .record-button {
   padding: 5px !important;
 }
 
 .notice-container .record-button[checked],
--- a/devtools/client/themes/styleeditor.css
+++ b/devtools/client/themes/styleeditor.css
@@ -1,13 +1,21 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* 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/. */
 
+.style-editor-newButton {
+  list-style-image: url(images/add.svg);
+}
+
+.style-editor-importButton {
+  list-style-image: url(images/import.svg);
+}
+
 .stylesheet-title,
 .stylesheet-name {
   text-decoration: none;
 }
 
 .stylesheet-name {
   font-size: 13px;
 }
@@ -95,33 +103,27 @@
 
 .media-condition-unmatched {
   opacity: 0.4;
 }
 
 .stylesheet-enabled {
   padding: 8px 0;
   margin: 0 8px;
-  background-image: url(images/itemToggle.png);
+  background-image: url(images/itemToggle.svg);
   background-repeat: no-repeat;
   background-clip: content-box;
-  background-position: 0 8px;
-  background-size: 48px 24px;
+  background-position: center;
+  background-size: 16px;
   width: 24px;
   height: 40px;
 }
 
-@media (min-resolution: 1.1dppx) {
-  .stylesheet-enabled {
-    background-image: url(images/itemToggle@2x.png);
-  }
-}
-
 .disabled > .stylesheet-enabled {
-  background-position: -24px 8px;
+  opacity: 0.3;
 }
 
 /* Invert all toggle icons but the one in the active row for light theme */
 .theme-light .splitview-nav > li:not(.splitview-active) .stylesheet-enabled {
   filter: invert(1);
 }
 
 .splitview-nav > li > .stylesheet-enabled:focus,
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -748,74 +748,16 @@
 }
 
 .command-button:hover:active::before,
 .command-button[checked=true]::before,
 .command-button[open=true]::before {
   opacity: 1;
 }
 
-/* Toolbox command buttons */
-
-#command-button-paintflashing::before {
-  background-image: var(--command-paintflashing-image);
-}
-
-#command-button-screenshot::before {
-  background-image: var(--command-screenshot-image);
-}
-
-#command-button-responsive::before {
-  background-image: var(--command-responsive-image);
-}
-
-#command-button-scratchpad::before {
-  background-image: var(--command-scratchpad-image);
-}
-
-#command-button-pick::before {
-  background-image: var(--command-pick-image);
-}
-
-#command-button-splitconsole::before {
-  background-image: var(--command-splitconsole-image);
-}
-
-#command-button-noautohide::before {
-  background-image: var(--command-noautohide-image);
-}
-
-#command-button-eyedropper::before {
-  background-image: var(--command-eyedropper-image);
-}
-
-#command-button-rulers::before {
-  background-image: var(--command-rulers-image);
-}
-
-#command-button-measure::before {
-  background-image: var(--command-measure-image);
-}
-
-#command-button-frames::before {
-  background-image: var(--command-frames-image);
-}
-
-#command-button-frames {
-  background: url("chrome://devtools/skin/images/dropmarker.svg") no-repeat right;
-
-  /* Override background-size from the command-button.
-    The drop down arrow is smaller */
-  background-size: 8px 4px !important;
-}
-
-#command-button-frames:-moz-dir(rtl) {
-  background-position: left;
-}
-
 /* Tabs */
 
 .devtools-tabbar {
   -moz-appearance: none;
   min-height: 24px;
   border: 0px solid;
   border-bottom-width: 1px;
   padding: 0;
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/toolbox.css
@@ -0,0 +1,63 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+ /* Toolbox command buttons */
+
+#command-button-paintflashing::before {
+   background-image: var(--command-paintflashing-image);
+ }
+
+#command-button-screenshot::before {
+   background-image: var(--command-screenshot-image);
+ }
+
+#command-button-responsive::before {
+ background-image: var(--command-responsive-image);
+}
+
+#command-button-scratchpad::before {
+ background-image: var(--command-scratchpad-image);
+}
+
+#command-button-pick::before {
+ background-image: var(--command-pick-image);
+}
+
+#command-button-splitconsole::before {
+ background-image: var(--command-splitconsole-image);
+}
+
+#command-button-noautohide::before {
+ background-image: var(--command-noautohide-image);
+}
+
+#command-button-eyedropper::before {
+ background-image: var(--command-eyedropper-image);
+}
+
+#command-button-rulers::before {
+ background-image: var(--command-rulers-image);
+}
+
+#command-button-measure::before {
+ background-image: var(--command-measure-image);
+}
+
+#command-button-frames::before {
+ background-image: var(--command-frames-image);
+}
+
+#command-button-frames {
+ background: url("chrome://devtools/skin/images/dropmarker.svg") no-repeat right;
+
+ /* Override background-size from the command-button.
+   The drop down arrow is smaller */
+ background-size: 8px 4px !important;
+ min-width: 32px;
+}
+
+#command-button-frames:-moz-dir(rtl) {
+ background-position: left;
+}
--- a/devtools/client/webconsole/jsterm.js
+++ b/devtools/client/webconsole/jsterm.js
@@ -438,21 +438,34 @@ JSTerm.prototype = {
     }
 
     let selectedNodeActor = null;
     let inspectorSelection = this.hud.owner.getInspectorSelection();
     if (inspectorSelection && inspectorSelection.nodeFront) {
       selectedNodeActor = inspectorSelection.nodeFront.actorID;
     }
 
-    let message = new Messages.Simple(executeString, {
-      category: "input",
-      severity: "log",
-    });
-    this.hud.output.addMessage(message);
+    if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
+      const { ConsoleCommand } = require("devtools/client/webconsole/new-console-output/types");
+      let message = new ConsoleCommand({
+        messageText: executeString,
+        source: "javascript",
+        type: "command",
+        // @TODO remove category and severity
+        category: "input",
+        severity: "log",
+      });
+      this.hud.newConsoleOutput.dispatchMessageAdd(message);
+    } else {
+      let message = new Messages.Simple(executeString, {
+        category: "input",
+        severity: "log",
+      });
+      this.hud.output.addMessage(message);
+    }
     let onResult = this._executeResultCallback.bind(this, resultCallback);
 
     let options = {
       frame: this.SELECTED_FRAME,
       selectedNodeActor: selectedNodeActor,
     };
 
     this.requestEvaluation(executeString, options).then(onResult, onResult);
--- a/devtools/client/webconsole/new-console-output/components/message-container.js
+++ b/devtools/client/webconsole/new-console-output/components/message-container.js
@@ -8,43 +8,56 @@
 
 // React & Redux
 const {
   createClass,
   createFactory,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 
+const componentMap = new Map([
+  ["ConsoleApiCall", require("./message-types/console-api-call").ConsoleApiCall],
+  ["ConsoleCommand", require("./message-types/console-command").ConsoleCommand],
+  ["DefaultRenderer", require("./message-types/default-renderer").DefaultRenderer],
+  ["EvaluationResult", require("./message-types/evaluation-result").EvaluationResult],
+  ["PageError", require("./message-types/page-error").PageError]
+]);
+
 const MessageContainer = createClass({
+  displayName: "MessageContainer",
 
   propTypes: {
     message: PropTypes.object.isRequired
   },
 
-  displayName: "MessageContainer",
-
   render() {
     const { message } = this.props;
-    let MessageComponent = getMessageComponent(message.messageType);
+    let MessageComponent = createFactory(getMessageComponent(message));
     return MessageComponent({ message });
   }
 });
 
-function getMessageComponent(messageType) {
-  let MessageComponent;
-  switch (messageType) {
-    case "ConsoleApiCall":
-      MessageComponent = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call").ConsoleApiCall;
-      break;
-    case "EvaluationResult":
-      MessageComponent = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result").EvaluationResult;
-      break;
-    case "PageError":
-      MessageComponent = require("devtools/client/webconsole/new-console-output/components/message-types/page-error").PageError;
+function getMessageComponent(message) {
+  // @TODO Once packets have been converted to Chrome RDP structure, remove.
+  if (message.messageType) {
+    let {messageType} = message;
+    if (!componentMap.has(messageType)) {
+      return componentMap.get("DefaultRenderer");
+    }
+    return componentMap.get(messageType);
+  }
+
+  switch (message.source) {
+    case "javascript":
+      switch (message.type) {
+        case "command":
+          return componentMap.get("ConsoleCommand");
+      }
       break;
   }
-  return createFactory(MessageComponent);
+
+  return componentMap.get("DefaultRenderer");
 }
 
 module.exports.MessageContainer = MessageContainer;
 
 // Exported so we can test it with unit tests.
 module.exports.getMessageComponent = getMessageComponent;
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/components/message-types/console-command.js
@@ -0,0 +1,51 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+"use strict";
+
+// React & Redux
+const {
+  createFactory,
+  DOM: dom,
+  PropTypes
+} = require("devtools/client/shared/vendor/react");
+const { ConsoleCommand: ConsoleCommandType } = require("devtools/client/webconsole/new-console-output/types");
+const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
+
+ConsoleCommand.displayName = "ConsoleCommand";
+
+ConsoleCommand.propTypes = {
+  message: PropTypes.instanceOf(ConsoleCommandType).isRequired,
+};
+
+/**
+ * Displays input from the console.
+ */
+function ConsoleCommand(props) {
+  const { message } = props;
+
+  const icon = MessageIcon({severity: message.severity});
+
+  // @TODO Use of "is" is a temporary hack to get the category and severity
+  // attributes to be applied. There are targeted in webconsole's CSS rules,
+  // so if we remove this hack, we have to modify the CSS rules accordingly.
+  return dom.div({
+    class: "message",
+    ariaLive: "off",
+    is: "fdt-message",
+    category: message.category,
+    severity: message.severity
+  },
+    // @TODO add timestamp
+    // @TODO add indent if necessary
+    icon,
+    dom.span({className: "message-body-wrapper message-body devtools-monospace"},
+      dom.span({}, message.messageText)
+    )
+  );
+}
+
+module.exports.ConsoleCommand = ConsoleCommand;
--- a/devtools/client/webconsole/new-console-output/components/message-types/default-renderer.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/default-renderer.js
@@ -3,35 +3,23 @@
 /* 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/. */
 
 "use strict";
 
 // React & Redux
 const {
-  createFactory,
   DOM: dom,
 } = require("devtools/client/shared/vendor/react");
-const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
 
 DefaultRenderer.displayName = "DefaultRenderer";
 
 function DefaultRenderer(props) {
-  const { category, severity } = props;
-
-  const icon = MessageIcon({ severity });
-
   // @TODO Use of "is" is a temporary hack to get the category and severity
   // attributes to be applied. There are targeted in webconsole's CSS rules,
   // so if we remove this hack, we have to modify the CSS rules accordingly.
-  return dom.div({
-    class: "message cm-s-mozilla",
-    is: "fdt-message",
-    category: category,
-    severity: severity
-  },
-    icon,
-    "This evaluation result type is not supported yet."
+  return dom.div({},
+    "This message type is not supported yet."
   );
 }
 
 module.exports.DefaultRenderer = DefaultRenderer;
--- a/devtools/client/webconsole/new-console-output/components/message-types/moz.build
+++ b/devtools/client/webconsole/new-console-output/components/message-types/moz.build
@@ -1,11 +1,12 @@
 # vim: set filetype=python:
 # 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/.
 
 DevToolsModules(
     'console-api-call.js',
+    'console-command.js',
     'default-renderer.js',
     'evaluation-result.js',
     'page-error.js',
 )
--- a/devtools/client/webconsole/new-console-output/moz.build
+++ b/devtools/client/webconsole/new-console-output/moz.build
@@ -11,16 +11,17 @@ DIRS += [
     'utils',
 ]
 
 DevToolsModules(
     'constants.js',
     'main.js',
     'new-console-output-wrapper.js',
     'store.js',
+    'types.js',
 )
 
 MOCHITEST_CHROME_MANIFESTS += [
   'test/components/chrome.ini',
   'test/utils/chrome.ini'
 ]
 XPCSHELL_TESTS_MANIFESTS += [
   'test/actions/xpcshell.ini',
--- a/devtools/client/webconsole/new-console-output/reducers/messages.js
+++ b/devtools/client/webconsole/new-console-output/reducers/messages.js
@@ -8,25 +8,27 @@
 const Immutable = require("devtools/client/shared/vendor/immutable");
 const constants = require("devtools/client/webconsole/new-console-output/constants");
 
 function messages(state = Immutable.List(), action) {
   switch (action.type) {
     case constants.MESSAGE_ADD:
       let newMessage = action.message;
 
-      if (newMessage.data.level === "clear") {
+      // @TODO clean this up once we've switched to Chrome RDP packet structure.
+      if (newMessage.data && newMessage.data.level === "clear") {
         return Immutable.List([newMessage]);
       }
 
       if (newMessage.allowRepeating && state.size > 0) {
         let lastMessage = state.last();
         if (lastMessage.repeatId === newMessage.repeatId) {
-          newMessage.repeat = lastMessage.repeat + 1;
-          return state.pop().push(newMessage);
+          return state.pop().push(
+            newMessage.set("repeat", lastMessage.repeat + 1)
+          );
         }
       }
       return state.push(newMessage);
     case constants.MESSAGES_CLEAR:
       return Immutable.List();
   }
 
   return state;
--- a/devtools/client/webconsole/new-console-output/test/components/test_console-api-call_repeat.html
+++ b/devtools/client/webconsole/new-console-output/test/components/test_console-api-call_repeat.html
@@ -12,18 +12,17 @@
 <p>Test for ConsoleApiCall component with repeats</p>
 
 <script type="text/javascript;version=1.8">
 window.onload = Task.async(function* () {
   const { prepareMessage } = require("devtools/client/webconsole/new-console-output/utils/messages");
   const { ConsoleApiCall } = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call");
 
   const packet = yield getPacket("console.log('foobar', 'test')", "consoleAPICall");
-  const message = prepareMessage(packet);
-  message.repeat = 107;
+  const message = prepareMessage(packet).set("repeat", 107);
   const rendered = renderComponent(ConsoleApiCall, {message});
 
   const messageBodyPath = "span > span.message-flex-body > span.message-body.devtools-monospace";
   const messageBody = rendered.querySelectorAll(messageBodyPath);
   // @TODO Expected output should be: foobar test
   is(messageBody[0].textContent, "\"foobar\"\"test\"", "ConsoleApiCall outputs expected text for repeated message");
 
   const repeatPath = "span > span.message-flex-body > span.message-body.devtools-monospace + span.message-repeats";
--- a/devtools/client/webconsole/new-console-output/test/store/test_messages.js
+++ b/devtools/client/webconsole/new-console-output/test/store/test_messages.js
@@ -35,22 +35,27 @@ add_task(function* () {
  */
 add_task(function* () {
   const { getState, dispatch } = storeFactory();
 
   dispatch(actions.messageAdd(packet));
   dispatch(actions.messageAdd(packet));
   dispatch(actions.messageAdd(packet));
 
-  const expectedMessage = prepareMessage(packet);
-  expectedMessage.repeat = 3;
+  const messages = getAllMessages(getState());
+  equal(messages.size, 1,
+    "Repeated messages don't increase message list size");
+  equal(messages.first().repeat, 3, "Repeated message is updated as expected");
 
-  let messages = getAllMessages(getState());
-  deepEqual(messages.toArray(), [expectedMessage],
-    "Adding same message to the store three times results in repeated message");
+  let newPacket = Object.assign({}, packet);
+  newPacket.message.arguments = ["funny"];
+  dispatch(actions.messageAdd(newPacket));
+
+  equal(getAllMessages(getState()).size, 2,
+    "Non-repeated messages aren't clobbered");
 });
 
 /**
  * Test getRepeatId().
  */
 add_task(function* () {
   const message1 = prepareMessage(packet);
   const message2 = prepareMessage(packet);
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/types.js
@@ -0,0 +1,27 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* 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/. */
+"use strict";
+
+const Immutable = require("devtools/client/shared/vendor/immutable");
+
+exports.ConsoleCommand = Immutable.Record({
+  allowRepeating: false,
+  messageText: null,
+  source: null,
+  type: null,
+  category: null,
+  severity: null,
+});
+
+exports.ConsoleMessage = Immutable.Record({
+  allowRepeating: true,
+  category: "output",
+  severity: "log",
+  data: null,
+  messageType: null,
+  repeat: 1,
+  repeatId: null,
+});
--- a/devtools/client/webconsole/new-console-output/utils/messages.js
+++ b/devtools/client/webconsole/new-console-output/utils/messages.js
@@ -15,16 +15,86 @@ const {
   SEVERITY_CLASS_FRAGMENTS,
   SEVERITY_ERROR,
   SEVERITY_WARNING,
   SEVERITY_LOG,
 } = require("../constants");
 const WebConsoleUtils = require("devtools/shared/webconsole/utils").Utils;
 const STRINGS_URI = "chrome://devtools/locale/webconsole.properties";
 const l10n = new WebConsoleUtils.L10n(STRINGS_URI);
+const { ConsoleMessage } = require("../types");
+
+function prepareMessage(packet) {
+  if (packet.source) {
+    return packet;
+  }
+
+  if (packet._type) {
+    packet = convertCachedPacket(packet);
+  }
+
+  switch (packet.type) {
+    case "consoleAPICall": {
+      let data = Object.assign({}, packet.message);
+      if (data.level === "clear") {
+        data.arguments = [l10n.getStr("consoleCleared")];
+      }
+
+      return new ConsoleMessage({
+        category: CATEGORY_CLASS_FRAGMENTS[CATEGORY_WEBDEV],
+        data,
+        messageType: "ConsoleApiCall",
+        repeatId: getRepeatId(data),
+        severity: SEVERITY_CLASS_FRAGMENTS[LEVELS[data.level]] || "log",
+      });
+    }
+    case "pageError": {
+      let data = Object.assign({}, packet.pageError);
+      let severity = SEVERITY_CLASS_FRAGMENTS[SEVERITY_ERROR];
+      if (data.warning || data.strict) {
+        severity = SEVERITY_CLASS_FRAGMENTS[SEVERITY_WARNING];
+      } else if (data.info) {
+        severity = SEVERITY_CLASS_FRAGMENTS[SEVERITY_LOG];
+      }
+
+      return new ConsoleMessage({
+        category: CATEGORY_CLASS_FRAGMENTS[CATEGORY_JS],
+        data,
+        messageType: "PageError",
+        repeatId: getRepeatId(data),
+        severity,
+      });
+    }
+    case "evaluationResult":
+    default: {
+      let data;
+      if (typeof packet.result === "object") {
+        data = Object.assign({}, packet.result);
+      } else {
+        data = packet.result;
+      }
+
+      return new ConsoleMessage({
+        category: CATEGORY_CLASS_FRAGMENTS[CATEGORY_OUTPUT],
+        data,
+        messageType: "EvaluationResult",
+        repeatId: getRepeatId(data),
+        severity: SEVERITY_CLASS_FRAGMENTS[SEVERITY_LOG],
+      });
+    }
+  }
+}
+
+// Helpers
+function getRepeatId(message) {
+  let clonedMessage = JSON.parse(JSON.stringify(message));
+  delete clonedMessage.timeStamp;
+  delete clonedMessage.uniqueID;
+  return JSON.stringify(clonedMessage);
+}
 
 function convertCachedPacket(packet) {
   // The devtools server provides cached message packets in a different shape
   // from those of consoleApiCalls, so we prepare them for preparation here.
   let convertPacket = {};
   if (packet._type === "ConsoleAPI") {
     convertPacket.message = packet;
     convertPacket.type = "consoleAPICall";
@@ -32,91 +102,13 @@ function convertCachedPacket(packet) {
     convertPacket.pageError = packet;
     convertPacket.type = "pageError";
   } else {
     throw new Error("Unexpected packet type");
   }
   return convertPacket;
 }
 
-function prepareMessage(packet) {
-  // @TODO turn this into an Immutable Record.
-  let allowRepeating;
-  let category;
-  let data;
-  let messageType;
-  let repeat;
-  let repeatId;
-  let severity;
-
-  if (packet._type) {
-    packet = convertCachedPacket(packet);
-  }
-
-  switch (packet.type) {
-    case "consoleAPICall":
-      data = Object.assign({}, packet.message);
-
-      if (data.level === "clear") {
-        data.arguments = [l10n.getStr("consoleCleared")];
-      }
-
-      allowRepeating = true;
-      category = CATEGORY_CLASS_FRAGMENTS[CATEGORY_WEBDEV];
-      messageType = "ConsoleApiCall";
-      repeat = 1;
-      repeatId = getRepeatId(data);
-      severity = SEVERITY_CLASS_FRAGMENTS[LEVELS[data.level]] || "log";
-      break;
-    case "pageError":
-      data = Object.assign({}, packet.pageError);
-      allowRepeating = true;
-      category = CATEGORY_CLASS_FRAGMENTS[CATEGORY_JS];
-      messageType = "PageError";
-      repeat = 1;
-      repeatId = getRepeatId(data);
-
-      severity = SEVERITY_CLASS_FRAGMENTS[SEVERITY_ERROR];
-      if (data.warning || data.strict) {
-        severity = SEVERITY_CLASS_FRAGMENTS[SEVERITY_WARNING];
-      } else {
-        severity = SEVERITY_CLASS_FRAGMENTS[SEVERITY_LOG];
-      }
-      break;
-    case "evaluationResult":
-    default:
-      if (typeof packet.result === "object") {
-        data = Object.assign({}, packet.result);
-      } else {
-        data = packet.result;
-      }
-      allowRepeating = true;
-      category = CATEGORY_CLASS_FRAGMENTS[CATEGORY_OUTPUT];
-      messageType = "EvaluationResult";
-      repeat = 1;
-      repeatId = getRepeatId(data);
-      severity = SEVERITY_CLASS_FRAGMENTS[SEVERITY_LOG];
-      break;
-  }
-
-  return {
-    allowRepeating,
-    category,
-    data,
-    messageType,
-    repeat,
-    repeatId,
-    severity
-  };
-}
-
-function getRepeatId(message) {
-  let clonedMessage = JSON.parse(JSON.stringify(message));
-  delete clonedMessage.timeStamp;
-  delete clonedMessage.uniqueID;
-  return JSON.stringify(clonedMessage);
-}
-
 exports.prepareMessage = prepareMessage;
 // Export for use in testing.
 exports.getRepeatId = getRepeatId;
 
 exports.l10n = l10n;
deleted file mode 100644
--- a/dom/base/test/mochitest-child-permissions.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[test_messagemanager_assertpermission.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' #b2g(specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE) b2g-debug(specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE) b2g-desktop(specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE)
-[test_child_process_shutdown_message.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' #b2g(specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE) b2g-debug(specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE) b2g-desktop(specialpowers.wrap issue, NS_ERROR_XPC_GS_RETURNED_FAILURE)
--- a/dom/base/test/moz.build
+++ b/dom/base/test/moz.build
@@ -14,23 +14,16 @@ GeckoCppUnitTests([
     'TestNativeXMLHttpRequest',
     'TestPlainTextSerializer',
 ])
 
 MOCHITEST_MANIFESTS += [
     'mochitest.ini',
     'websocket_hybi/mochitest.ini',
 ]
-# OOP tests don't work on Windows (bug 763081) or native-fennec
-# (see Bug 774939). App permission checks are also disabled on
-# anything but B2G (Bug 900707).
-if CONFIG['MOZ_CHILD_PERMISSIONS']:
-    MOCHITEST_MANIFESTS += [
-        'mochitest-child-permissions.ini',
-    ]
 
 MOCHITEST_CHROME_MANIFESTS += [
     'chrome.ini',
     'chrome/chrome.ini',
 ]
 
 BROWSER_CHROME_MANIFESTS += [
     'browser.ini',
deleted file mode 100644
--- a/dom/base/test/test_child_process_shutdown_message.html
+++ /dev/null
@@ -1,171 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>Test that processes that are shutdown send a 'process-shutdown'
-         message to their process message manager.</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body onload="runTests();">
-<p id="display">
-</p>
-<div id="content" style="display: none">
-  
-</div>
-<pre id="test">
-<script class="testbody" type="application/javascript;version=1.8">
-
-const APP_URL = "http://example.org";
-const APP_MANIFEST = "http://example.org/manifest.webapp";
-const CHILD_PROCESS_SHUTDOWN_MESSAGE = "child-process-shutdown";
-
-let ppmm = SpecialPowers.Cc["@mozilla.org/parentprocessmessagemanager;1"]
-                        .getService(SpecialPowers.Ci.nsIMessageBroadcaster);
-let obs = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
-                       .getService(SpecialPowers.Ci.nsIObserverService);
-
-/**
- * Load the example.org site in an <iframe mozbrowser>
- *
- * @param isApp
- *        If true, the example.org site will be loaded as an app.
- */
-function loadBrowser(isApp, callback) {
-  let iframe = document.createElement("iframe");
-  if (isApp) {
-    iframe.setAttribute("mozapp", APP_MANIFEST);
-  }
-  SpecialPowers.wrap(iframe).mozbrowser = true;
-  iframe.src = APP_URL;
-  document.getElementById("content").appendChild(iframe);
-
-  iframe.addEventListener("mozbrowserloadend", function onloadend() {
-    iframe.removeEventListener("mozbrowserloadend", onloadend);
-    callback(iframe);
-  });
-}
-
-/**
- * Prepare the child process for an intentional crash. This is to keep
- * the leak automation tools happy.
- *
- * This also allows us to acquire the process message manaager that
- * corresponds to the process by sending a message to a frame script
- * in the content process and having it reply to us via the child
- * process message manager.
- */
-function prepareProcess(frameMM, callback) {
-  let frameScript = 'data:,\
-    privateNoteIntentionalCrash();\
-    var cpmm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]\
-                         .getService(Components.interfaces.nsISyncMessageSender);\
-    addMessageListener("TestChild:Ohai", function receiveMessage(msg) {\
-      cpmm.sendAsyncMessage("TestChild:Ohai");\
-    });';
-  frameMM.loadFrameScript(frameScript, false);
-  frameMM.sendAsyncMessage("TestChild:Ohai");
-  ppmm.addMessageListener("TestChild:Ohai", function receiveMessage(msg) {
-    ppmm.removeMessageListener("TestChild:Ohai", receiveMessage);
-    msg = SpecialPowers.wrap(msg);
-    callback(msg.target);
-  });
-}
-
-/**
- * Expects an OOP frame's process to shut down and report four
- * events/messages: an error event on the browser element, and a
- * 'child-process-shutdown' message on both the frame and process
- * message managers.
- */
-function expectFrameProcessShutdown(iframe, frameMM, processMM, callback) {
-  let msgCount = 0;
-  function countMessage() {
-    msgCount += 1;
-    if (msgCount == 4) {
-      ok(true, "Observed all four expected events.");
-      callback();
-    }
-  };
-
-  iframe.addEventListener("mozbrowsererror", function onerror(event) {
-    iframe.removeEventListener("mozbrowsererror", onerror);
-    is(event.detail.type, "fatal", "Observed expected event.");
-    countMessage();
-  });
-
-  processMM.addMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, function receiveMessage() {
-    processMM.removeMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, receiveMessage);
-    ok(true, "Received 'child-process-shutdown' message from process message manager.");
-    countMessage();
-  });
-
-  frameMM.addMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, function receiveMessage() {
-    frameMM.removeMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, receiveMessage);
-    ok(true, "Received 'child-process-shutdown' message from frame message manager.");
-    countMessage();
-  });
-
-  obs.addObserver(function observe(subject, type, data) {
-    if (subject == SpecialPowers.unwrap(processMM)) {
-      obs.removeObserver(observe, "message-manager-disconnect");
-      ok(true, "Received 'message-manager-disconnect' notification with " +
-               "frame message manager");
-      countMessage();
-    }
-  }, "message-manager-disconnect", false);
-}
-
-function setUp() {
-  SpecialPowers.addPermission("browser", true, window.document);
-  SpecialPowers.addPermission("embed-apps", true, window.document);
-  // TODO: remove network.disable.ipc.security in bug 820712
-  SpecialPowers.pushPrefEnv({
-    "set": [['dom.mozBrowserFramesEnabled', true],
-            ['dom.ipc.browser_frames.oop_by_default', true],
-            ['network.disable.ipc.security', true]]}, runNextTest);
-}
-
-function makeKillTest(isApp) {
-  return function testKill() {
-    loadBrowser(isApp, function (iframe) {
-      // We want to make sure we get notified on both the frame and
-      // process message managers.
-      let frameMM = SpecialPowers.getBrowserFrameMessageManager(iframe);
-      prepareProcess(frameMM, function (processMM) {
-        // Let's kill the content process by asking for a permission
-        // that it doesn't have.
-        ok(!processMM.assertPermission("frobnaz"),
-           "Content child should not have this permission");
-        expectFrameProcessShutdown(iframe, frameMM, processMM, function () {
-          iframe.parentNode.removeChild(iframe);
-          runNextTest();
-        });
-      });
-    });
-  };
-}
-
-function tearDown() {
-  SimpleTest.finish();
-}
-
-let _tests = [
-  setUp,
-  makeKillTest(false),
-  makeKillTest(true),
-  tearDown
-]
-function runNextTest() {
-  SimpleTest.executeSoon(_tests.shift());
-}
-
-function runTests() {
-  SimpleTest.waitForExplicitFinish();
-  runNextTest();
-}
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/base/test/test_messagemanager_assertpermission.html
+++ /dev/null
@@ -1,192 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>Test for the nsIProcessChecker part of Message Managers</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body onload="runTests();">
-<p id="display">
-</p>
-<div id="content" style="display: none">
-  
-</div>
-<pre id="test">
-<script class="testbody" type="application/javascript;version=1.8">
-
-const APP_URL = "http://example.org";
-const APP_MANIFEST = "http://example.org/manifest.webapp";
-const CHILD_PROCESS_SHUTDOWN_MESSAGE = "child-process-shutdown";
-
-let ppmm = SpecialPowers.Cc["@mozilla.org/parentprocessmessagemanager;1"]
-                        .getService(SpecialPowers.Ci.nsIMessageBroadcaster);
-let cpmm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"]
-                        .getService(SpecialPowers.Ci.nsISyncMessageSender);
-let gAppsService = SpecialPowers.Cc["@mozilla.org/AppsService;1"]
-                     .getService(SpecialPowers.Ci.nsIAppsService);
-
-function setUp() {
-  SpecialPowers.addPermission("browser", true, window.document);
-  SpecialPowers.addPermission("embed-apps", true, window.document);
-
-  let appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST);
-  SpecialPowers.addPermission("foobar", true, { url: APP_URL,
-                                                originAttributes: { appId: appId }
-                                              });
-  SpecialPowers.pushPrefEnv({"set":[['dom.mozBrowserFramesEnabled', true],
-                                    ['dom.ipc.browser_frames.oop_by_default', true]]}, runNextTest);
-}
-
-/**
- * Load the example.org app in an <iframe mozbrowser mozapp>
- */
-function loadApp(callback) {
-  let iframe = document.createElement("iframe");
-  iframe.setAttribute("mozapp", APP_MANIFEST);
-  SpecialPowers.wrap(iframe).mozbrowser = true;
-  iframe.src = APP_URL;
-  document.getElementById("content").appendChild(iframe);
-
-  iframe.addEventListener("mozbrowserloadend", function onloadend() {
-    iframe.removeEventListener("mozbrowserloadend", onloadend);
-    callback(iframe);
-  });
-}
-
-/**
- * Prepare the child process for an intentional crash. This is to keep
- * the leak automation tools happy.
- *
- * This also allows us to acquire the process message manaager that
- * corresponds to the process by sending a message to a frame script
- * in the content process and having it reply to us via the child
- * process message manager.
- */
-function prepareProcess(frameMM, callback) {
-  let frameScript = 'data:,\
-    privateNoteIntentionalCrash();\
-    var cpmm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]\
-                         .getService(Components.interfaces.nsISyncMessageSender);\
-    addMessageListener("TestChild:Ohai", function receiveMessage(msg) {\
-      cpmm.sendAsyncMessage("TestChild:Ohai");\
-    });';
-  frameMM.loadFrameScript(frameScript, false);
-  frameMM.sendAsyncMessage("TestChild:Ohai");
-  ppmm.addMessageListener("TestChild:Ohai", function receiveMessage(msg) {
-    ppmm.removeMessageListener("TestChild:Ohai", receiveMessage);
-    msg = SpecialPowers.wrap(msg);
-    callback(msg.target);
-  });
-}
-
-/**
- * Expects an OOP frame's process to shut down and report three
- * events/messages: an error event on the browser element, and a
- * 'child-process-shutdown' message on both the frame and process
- * message managers.
- */
-function expectFrameProcessShutdown(iframe, frameMM, processMM, callback) {
-  let msgCount = 0;
-  function countMessage() {
-    msgCount += 1;
-    if (msgCount == 3) {
-      ok(true, "Observed all three expected events.");
-      callback();
-    }
-  };
-
-  iframe.addEventListener("mozbrowsererror", function onerror(event) {
-    iframe.removeEventListener("mozbrowsererror", onerror);
-    is(event.detail.type, "fatal", "Observed expected event.");
-    countMessage();
-  });
-
-  processMM.addMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, function receiveMessage() {
-    processMM.removeMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, receiveMessage);
-    ok(true, "Received 'child-process-shutdown' message from process message manager.");
-    countMessage();
-  });
-
-  frameMM.addMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, function receiveMessage() {
-    frameMM.removeMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, receiveMessage);
-    ok(true, "Received 'child-process-shutdown' message from frame message manager.");
-    countMessage();
-  });
-}
-
-function testSameProcess() {
-  // Assert permissions on the in-process child process message manager.
-  // It always has all permissions, including ones that were never
-  // assigned to anybody.
-
-  cpmm.sendAsyncMessage("TestPermission:InProcess");
-  ppmm.addMessageListener("TestPermission:InProcess", function receiveMessage(msg) {
-    ppmm.removeMessageListener("TestPermission:InProcess", receiveMessage);
-    msg = SpecialPowers.wrap(msg);
-
-    ok(msg.target.assertPermission("frobnaz"), "in-process cpmm always has all capabilities");
-    runNextTest();
-  });
-}
-
-function testFrameMessageManager() {
-  // Assert permissions on the frame message manager.
-
-  loadApp(function (iframe) {
-    let frameMM = SpecialPowers.getBrowserFrameMessageManager(iframe);
-    prepareProcess(frameMM, function (processMM) {
-      ok(frameMM.assertPermission("foobar"),
-         "Frame mm has assigned permission.");
-      ok(!frameMM.assertPermission("frobnaz"),
-         "Frame mm doesn't have non-existing permission.");
-      expectFrameProcessShutdown(iframe, frameMM, processMM, function () {
-        iframe.parentNode.removeChild(iframe);
-        runNextTest();
-      });
-    });
-  });
-}
-
-function testChildProcessMessageManager() {
-  // Assert permissions on the child process message manager.
-
-  loadApp(function (iframe) {
-    let frameMM = SpecialPowers.getBrowserFrameMessageManager(iframe);
-    prepareProcess(frameMM, function (processMM) {
-      ok(processMM.assertPermission("foobar"),
-         "Process mm has assigned permission.");
-      ok(!processMM.assertPermission("frobnaz"),
-         "Process mm doesn't have non-existing permission.");
-      expectFrameProcessShutdown(iframe, frameMM, processMM, function () {
-        iframe.parentNode.removeChild(iframe);
-        runNextTest();
-      });
-    });
-  });
-}
-
-function tearDown() {
-  SimpleTest.finish();
-}
-
-let _tests = [
-  setUp,
-  testSameProcess,
-  testFrameMessageManager,
-  testChildProcessMessageManager,
-  tearDown
-]
-function runNextTest() {
-  SimpleTest.executeSoon(_tests.shift());
-}
-
-function runTests() {
-  SimpleTest.waitForExplicitFinish();
-  runNextTest();
-}
-
-</script>
-</pre>
-</body>
-</html>
--- a/dom/html/HTMLFormSubmission.cpp
+++ b/dom/html/HTMLFormSubmission.cpp
@@ -870,17 +870,17 @@ GetSubmitCharset(nsGenericHTMLElement* a
     // get charset from charsets one by one
     do {
       spPos = acceptCharsetValue.FindChar(char16_t(' '), offset);
       int32_t cnt = ((-1==spPos)?(charsetLen-offset):(spPos-offset));
       if (cnt > 0) {
         nsAutoString uCharset;
         acceptCharsetValue.Mid(uCharset, offset, cnt);
 
-        if (EncodingUtils::FindEncodingForLabel(uCharset, oCharset))
+        if (EncodingUtils::FindEncodingForLabelNoReplacement(uCharset, oCharset))
           return;
       }
       offset = spPos + 1;
     } while (spPos != -1);
   }
   // if there are no accept-charset or all the charset are not supported
   // Get the charset from document
   nsIDocument* doc = aForm->GetComposedDoc();
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -1742,33 +1742,17 @@ HTMLInputElement::GetValueInternal(nsASt
         mInputData.mState->GetValue(aValue, true);
       } else if (!aValue.Assign(mInputData.mValue, fallible)) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
       return NS_OK;
 
     case VALUE_MODE_FILENAME:
       if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
-#ifndef MOZ_CHILD_PERMISSIONS
         aValue.Assign(mFirstFilePath);
-#else
-        // XXX We'd love to assert that this can't happen, but some mochitests
-        // use SpecialPowers to circumvent our more sane security model.
-        if (!mFilesOrDirectories.IsEmpty()) {
-          ErrorResult rv;
-          GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], aValue, rv);
-          if (NS_WARN_IF(rv.Failed())) {
-            return rv.StealNSResult();
-          }
-          return NS_OK;
-        }
-        else {
-          aValue.Truncate();
-        }
-#endif
       } else {
         // Just return the leaf name
         if (mFilesOrDirectories.IsEmpty()) {
           aValue.Truncate();
         } else {
           GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue);
         }
       }
@@ -2933,32 +2917,30 @@ HTMLInputElement::AfterSetFilesOrDirecto
   // new value.  We just want the display to update as needed.
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
   if (formControlFrame) {
     nsAutoString readableValue;
     GetDisplayFileName(readableValue);
     formControlFrame->SetFormProperty(nsGkAtoms::value, readableValue);
   }
 
-#ifndef MOZ_CHILD_PERMISSIONS
   // Grab the full path here for any chrome callers who access our .value via a
   // CPOW. This path won't be called from a CPOW meaning the potential sync IPC
   // call under GetMozFullPath won't be rejected for not being urgent.
   // XXX Protected by the ifndef because the blob code doesn't allow us to send
   // this message in b2g.
   if (mFilesOrDirectories.IsEmpty()) {
     mFirstFilePath.Truncate();
   } else {
     ErrorResult rv;
     GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], mFirstFilePath, rv);
     if (NS_WARN_IF(rv.Failed())) {
       rv.SuppressException();
     }
   }
-#endif
 
   UpdateFileList();
 
   if (aSetValueChanged) {
     SetValueChanged(true);
   }
 
   UpdateAllValidityStates(true);
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -1386,22 +1386,20 @@ protected:
    * this member, never by the frame. Whenever the frame wants to change the
    * filename it has to call SetFilesOrDirectories to update this member.
    */
   nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
 
   RefPtr<GetFilesHelper> mGetFilesRecursiveHelper;
   RefPtr<GetFilesHelper> mGetFilesNonRecursiveHelper;
 
-#ifndef MOZ_CHILD_PERMISSIONS
   /**
    * Hack for bug 1086684: Stash the .value when we're a file picker.
    */
   nsString mFirstFilePath;
-#endif
 
   RefPtr<FileList>  mFileList;
   Sequence<RefPtr<Entry>> mEntries;
 
   nsString mStaticDocFileList;
 
   /**
    * The value of the input element when first initialized and it is updated
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1079,34 +1079,63 @@ void HTMLMediaElement::QueueSelectResour
   if (mHaveQueuedSelectResource)
     return;
   mHaveQueuedSelectResource = true;
   ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE);
   RefPtr<Runnable> r = NewRunnableMethod(this, &HTMLMediaElement::SelectResourceWrapper);
   RunInStableState(r);
 }
 
+static bool HasSourceChildren(nsIContent* aElement)
+{
+  for (nsIContent* child = aElement->GetFirstChild();
+       child;
+       child = child->GetNextSibling()) {
+    if (child->IsHTMLElement(nsGkAtoms::source))
+    {
+      return true;
+    }
+  }
+  return false;
+}
+
 NS_IMETHODIMP HTMLMediaElement::Load()
 {
+  LOG(LogLevel::Debug,
+      ("%p Load() hasSrcAttrStream=%d hasSrcAttr=%d hasSourceChildren=%d "
+       "handlingInput=%d isCallerChromeOrNative=%d",
+       this, !!mSrcAttrStream, HasAttr(kNameSpaceID_None, nsGkAtoms::src),
+       HasSourceChildren(this), EventStateManager::IsHandlingUserInput(),
+       nsContentUtils::LegacyIsCallerChromeOrNativeCode()));
+
   if (mIsRunningLoadMethod) {
     return NS_OK;
   }
 
   mIsDoingExplicitLoad = true;
   DoLoad();
 
   return NS_OK;
 }
 
 void HTMLMediaElement::DoLoad()
 {
   if (mIsRunningLoadMethod) {
     return;
   }
 
+  // Detect if user has interacted with element so that play will not be
+  // blocked when initiated by a script. This enables sites to capture user
+  // intent to play by calling load() in the click handler of a "catalog
+  // view" of a gallery of videos.
+  if (EventStateManager::IsHandlingUserInput() ||
+      nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
+    mHasUserInteraction = true;
+  }
+
   SetPlayedOrSeeked(false);
   mIsRunningLoadMethod = true;
   AbortExistingLoads();
   SetPlaybackRate(mDefaultPlaybackRate);
   QueueSelectResourceTask();
   ResetState();
   mIsRunningLoadMethod = false;
 }
@@ -1118,29 +1147,16 @@ void HTMLMediaElement::ResetState()
   // staled videoWidth and videoHeight. We have to call ForgetElement() here
   // such that the staled callbacks won't reach us.
   if (mVideoFrameContainer) {
     mVideoFrameContainer->ForgetElement();
     mVideoFrameContainer = nullptr;
   }
 }
 
-static bool HasSourceChildren(nsIContent* aElement)
-{
-  for (nsIContent* child = aElement->GetFirstChild();
-       child;
-       child = child->GetNextSibling()) {
-    if (child->IsHTMLElement(nsGkAtoms::source))
-    {
-      return true;
-    }
-  }
-  return false;
-}
-
 void HTMLMediaElement::SelectResourceWrapper()
 {
   SelectResource();
   mIsRunningSelectResource = false;
   mHaveQueuedSelectResource = false;
   mIsDoingExplicitLoad = false;
 }
 
@@ -3069,16 +3085,19 @@ HTMLMediaElement::ReportTelemetry()
           ("Reporting telemetry DROPPED_FRAMES_IN_VIDEO_PLAYBACK"));
       Telemetry::Accumulate(Telemetry::VIDEO_DROPPED_FRAMES_PROPORTION,
                             percentage);
     }
   }
 
   Telemetry::Accumulate(Telemetry::VIDEO_PLAY_TIME_MS, SECONDS_TO_MS(mPlayTime.Total()));
   LOG(LogLevel::Debug, ("%p VIDEO_PLAY_TIME_MS = %f", this, mPlayTime.Total()));
+
+  Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_MS, SECONDS_TO_MS(mHiddenPlayTime.Total()));
+  LOG(LogLevel::Debug, ("%p VIDEO_HIDDEN_PLAY_TIME_MS = %f", this, mHiddenPlayTime.Total()));
 }
 
 void HTMLMediaElement::UnbindFromTree(bool aDeep,
                                       bool aNullParent)
 {
   if (!mPaused && mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
     Pause();
   }
@@ -4556,20 +4575,25 @@ nsresult HTMLMediaElement::DispatchAsync
     return NS_OK;
   }
 
   nsCOMPtr<nsIRunnable> event = new nsAsyncEventRunner(aName, this);
   NS_DispatchToMainThread(event);
 
   if ((aName.EqualsLiteral("play") || aName.EqualsLiteral("playing"))) {
     mPlayTime.Start();
+    if (IsHidden()) {
+      mHiddenPlayTime.Start();
+    }
   } else if (aName.EqualsLiteral("waiting")) {
     mPlayTime.Pause();
+    mHiddenPlayTime.Pause();
   } else if (aName.EqualsLiteral("pause")) {
     mPlayTime.Pause();
+    mHiddenPlayTime.Pause();
   }
 
   return NS_OK;
 }
 
 nsresult HTMLMediaElement::DispatchPendingMediaEvents()
 {
   NS_ASSERTION(!mEventDeliveryPaused,
@@ -4756,18 +4780,27 @@ void HTMLMediaElement::NotifyOwnerDocume
       NotifyAudioChannelAgent(false);
     }
   }
 }
 
 bool
 HTMLMediaElement::NotifyOwnerDocumentActivityChangedInternal()
 {
+  bool visible = !IsHidden();
+  if (visible) {
+    // Visible -> Just pause hidden play time (no-op if already paused).
+    mHiddenPlayTime.Pause();
+  } else if (mPlayTime.IsStarted()) {
+    // Not visible, play time is running -> Start hidden play time if needed.
+    mHiddenPlayTime.Start();
+  }
+
   if (mDecoder && !IsBeingDestroyed()) {
-    mDecoder->NotifyOwnerActivityChanged(!IsHidden());
+    mDecoder->NotifyOwnerActivityChanged(visible);
   }
 
   bool pauseElement = !IsActive();
   SuspendOrResumeElement(pauseElement, !IsActive());
 
   if (!mPausedForInactiveDocumentOrChannel &&
       mPlayBlockedBecauseHidden &&
       !OwnerDoc()->Hidden()) {
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1611,16 +1611,19 @@ public:
     TimeStamp mStartTime;
     TimeDuration mSum;
     uint32_t mCount;
   };
 private:
   // Total time a video has spent playing.
   TimeDurationAccumulator mPlayTime;
 
+  // Total time a video has spent playing while hidden.
+  TimeDurationAccumulator mHiddenPlayTime;
+
   // Indicates if user has interacted with the element.
   // Used to block autoplay when disabled.
   bool mHasUserInteraction;
 
   // True if the first frame has been successfully loaded.
   bool mFirstFrameLoaded;
 
   // Media elements also have a default playback start position, which must
new file mode 100644
--- /dev/null
+++ b/dom/html/crashtests/1281972-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<body onload="window[0].document.forms[0].submit();">
+<iframe src="data:text/html;charset=UTF-8,<form accept-charset=HZ-GB-2312>"></iframe>
+</body>
--- a/dom/html/crashtests/crashtests.list
+++ b/dom/html/crashtests/crashtests.list
@@ -70,8 +70,9 @@ load 877910.html
 load 903106.html
 load 916322-1.html
 load 916322-2.html
 load 1032654.html
 pref(dom.image.srcset.enabled,true) load 1141260.html
 load 1228876.html
 load 1230110.html
 load 1237633.html
+load 1281972-1.html
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -20278,35 +20278,19 @@ FactoryOp::CheckPermission(ContentParent
       !isApp) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 #endif
 
   PermissionRequestBase::PermissionValue permission;
 
   if (QuotaManager::IsFirstPromptRequired(persistenceType, origin, isApp)) {
-#ifdef MOZ_CHILD_PERMISSIONS
-    if (aContentParent) {
-      if (NS_WARN_IF(!AssertAppPrincipal(aContentParent, principal))) {
-        IDB_REPORT_INTERNAL_ERR();
-        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-      }
-
-      uint32_t intPermission =
-        mozilla::CheckPermission(aContentParent, principal, IDB_PREFIX);
-
-      permission =
-        PermissionRequestBase::PermissionValueForIntPermission(intPermission);
-    } else
-#endif // MOZ_CHILD_PERMISSIONS
-    {
-      rv = PermissionRequestBase::GetCurrentPermission(principal, &permission);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
+    rv = PermissionRequestBase::GetCurrentPermission(principal, &permission);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
     }
   } else {
     permission = PermissionRequestBase::kPermissionAllowed;
   }
 
   if (permission != PermissionRequestBase::kPermissionDenied &&
       State::Initial == mState) {
     mSuffix = suffix;
@@ -20375,75 +20359,17 @@ FactoryOp::SendVersionChangeMessages(Dat
 bool
 FactoryOp::CheckAtLeastOneAppHasPermission(ContentParent* aContentParent,
                                            const nsACString& aPermissionString)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aContentParent);
   MOZ_ASSERT(!aPermissionString.IsEmpty());
 
-#ifdef MOZ_CHILD_PERMISSIONS
-  const ManagedContainer<PBrowserParent>& browsers =
-    aContentParent->ManagedPBrowserParent();
-
-  if (!browsers.IsEmpty()) {
-    nsCOMPtr<nsIAppsService> appsService =
-      do_GetService(APPS_SERVICE_CONTRACTID);
-    if (NS_WARN_IF(!appsService)) {
-      return false;
-    }
-
-    nsCOMPtr<nsIIOService> ioService = do_GetIOService();
-    if (NS_WARN_IF(!ioService)) {
-      return false;
-    }
-
-    nsCOMPtr<nsIPermissionManager> permMan =
-      mozilla::services::GetPermissionManager();
-    if (NS_WARN_IF(!permMan)) {
-      return false;
-    }
-
-    const nsPromiseFlatCString permissionString =
-      PromiseFlatCString(aPermissionString);
-
-    for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) {
-      uint32_t appId =
-        TabParent::GetFrom(iter.Get()->GetKey())->OwnOrContainingAppId();
-      MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID &&
-                 appId != nsIScriptSecurityManager::NO_APP_ID);
-
-      nsCOMPtr<mozIApplication> app;
-      nsresult rv = appsService->GetAppByLocalId(appId, getter_AddRefs(app));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return false;
-      }
-
-      nsCOMPtr<nsIPrincipal> principal;
-      app->GetPrincipal(getter_AddRefs(principal));
-      NS_ENSURE_TRUE(principal, false);
-
-      uint32_t permission;
-      rv = permMan->TestExactPermissionFromPrincipal(principal,
-                                                     permissionString.get(),
-                                                     &permission);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return false;
-      }
-
-      if (permission == nsIPermissionManager::ALLOW_ACTION) {
-        return true;
-      }
-    }
-  }
-
-  return false;
-#else
-  return true;
-#endif // MOZ_CHILD_PERMISSIONS
+  return true;
 }
 
 nsresult
 FactoryOp::FinishOpen()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::FinishOpen);
   MOZ_ASSERT(!mContentParent);
--- a/dom/inputmethod/MozKeyboard.js
+++ b/dom/inputmethod/MozKeyboard.js
@@ -690,21 +690,30 @@ MozInputMethod.prototype = {
       'indexes': indexes
     });
   },
 
   removeFocus: function() {
     cpmm.sendAsyncMessage('System:RemoveFocus', {});
   },
 
+  // Only the system app needs that, so instead of testing a permission which
+  // is allowed for all chrome:// url, we explicitly test that this is the
+  // system app's start URL.
   _hasInputManagePerm: function(win) {
-    let principal = win.document.nodePrincipal;
-    let perm = Services.perms.testExactPermissionFromPrincipal(principal,
-                                                               "input-manage");
-    return (perm === Ci.nsIPermissionManager.ALLOW_ACTION);
+    let url = win.location.href;
+    let systemAppIndex;
+    try {
+      systemAppIndex = Services.prefs.getCharPref('b2g.system_startup_url');
+    } catch(e) {
+      dump('MozKeyboard.jsm: no system app startup url set (pref is b2g.system_startup_url)');
+    }
+
+    dump(`MozKeyboard.jsm expecting ${systemAppIndex}\n`);
+    return url == systemAppIndex;
   }
 };
 
 /**
  * ==============================================
  * InputContextDOMRequestIpcHelper
  * ==============================================
  */
--- a/dom/inputmethod/mochitest/mochitest.ini
+++ b/dom/inputmethod/mochitest/mochitest.ini
@@ -1,11 +1,11 @@
 [DEFAULT]
-# Not supported on Android, bug 983015 for B2G emulator
-skip-if = (toolkit == 'android' || toolkit == 'gonk') || e10s
+# dom/inputmethod only makes sense on B2G
+run-if = (buildapp == 'b2g' && toolkit != 'gonk') || buildapp == 'mulet'
 support-files =
   inputmethod_common.js
   file_inputmethod.html
   file_blank.html
   file_test_app.html
   file_test_sendkey_cancel.html
   file_test_sms_app.html
   file_test_sms_app_1066515.html
--- a/dom/ipc/AppProcessChecker.cpp
+++ b/dom/ipc/AppProcessChecker.cpp
@@ -1,361 +1,33 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "AppProcessChecker.h"
 #include "nsIPermissionManager.h"
-#ifdef MOZ_CHILD_PERMISSIONS
-#include "ContentParent.h"
-#include "mozIApplication.h"
-#include "mozilla/hal_sandbox/PHalParent.h"
-#include "nsIAppsService.h"
-#include "nsIPrincipal.h"
-#include "nsPrintfCString.h"
-#include "nsIURI.h"
-#include "nsContentUtils.h"
-#include "nsNetUtil.h"
-#include "nsServiceManagerUtils.h"
-#include "TabParent.h"
 
-#include <algorithm>
-
-using namespace mozilla::dom;
-using namespace mozilla::hal_sandbox;
-using namespace mozilla::services;
-#else
 namespace mozilla {
 namespace dom {
 class PContentParent;
 } // namespace dom
 } // namespace mozilla
 
 class nsIPrincipal;
-#endif
 
 namespace mozilla {
 
 #if DEBUG
   #define LOG(...) printf_stderr(__VA_ARGS__)
 #else
   #define LOG(...)
 #endif
 
-#ifdef MOZ_CHILD_PERMISSIONS
-
-static bool
-CheckAppTypeHelper(mozIApplication* aApp,
-                   AssertAppProcessType aType,
-                   const char* aCapability,
-                   bool aIsBrowserElement)
-{
-  bool aValid = false;
-
-  // isBrowser frames inherit their app descriptor to identify their
-  // data storage, but they don't inherit the capability associated
-  // with that descriptor.
-  if (aApp && (aType == ASSERT_APP_HAS_PERMISSION || !aIsBrowserElement)) {
-    switch (aType) {
-      case ASSERT_APP_HAS_PERMISSION:
-      case ASSERT_APP_PROCESS_PERMISSION:
-        if (!NS_SUCCEEDED(aApp->HasPermission(aCapability, &aValid))) {
-          aValid = false;
-        }
-        break;
-      case ASSERT_APP_PROCESS_MANIFEST_URL: {
-        nsAutoString manifestURL;
-        if (NS_SUCCEEDED(aApp->GetManifestURL(manifestURL)) &&
-            manifestURL.EqualsASCII(aCapability)) {
-          aValid = true;
-        }
-        break;
-      }
-      default:
-        break;
-    }
-  }
-  return aValid;
-}
-
-bool
-AssertAppProcess(PBrowserParent* aActor,
-                 AssertAppProcessType aType,
-                 const char* aCapability)
-{
-  if (!aActor) {
-    NS_WARNING("Testing process capability for null actor");
-    return false;
-  }
-
-  TabParent* tab = TabParent::GetFrom(aActor);
-  nsCOMPtr<mozIApplication> app = tab->GetOwnOrContainingApp();
-
-  return CheckAppTypeHelper(app, aType, aCapability, tab->IsMozBrowserElement());
-}
-
-static bool
-CheckAppStatusHelper(mozIApplication* aApp,
-                     unsigned short aStatus)
-{
-  bool valid = false;
-
-  if (aApp) {
-    unsigned short appStatus = 0;
-    if (NS_SUCCEEDED(aApp->GetAppStatus(&appStatus))) {
-      valid = appStatus == aStatus;
-    }
-  }
-
-  return valid;
-}
-
-bool
-AssertAppStatus(PBrowserParent* aActor,
-                unsigned short aStatus)
-{
-  if (!aActor) {
-    NS_WARNING("Testing process capability for null actor");
-    return false;
-  }
-
-  TabParent* tab = TabParent::GetFrom(aActor);
-  nsCOMPtr<mozIApplication> app = tab->GetOwnOrContainingApp();
-
-  return CheckAppStatusHelper(app, aStatus);
-}
-
-// A general purpose helper function to check permission against the origin
-// rather than mozIApplication.
-static bool
-CheckOriginPermission(const nsACString& aOrigin, const char* aPermission)
-{
-  LOG("CheckOriginPermission: %s, %s\n", nsCString(aOrigin).get(), aPermission);
-
-  nsIScriptSecurityManager *securityManager =
-    nsContentUtils::GetSecurityManager();
-
-  nsCOMPtr<nsIPrincipal> principal;
-  securityManager->CreateCodebasePrincipalFromOrigin(aOrigin,
-                                                     getter_AddRefs(principal));
-
-  nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
-  NS_ENSURE_TRUE(permMgr, false);
-
-  uint32_t perm;
-  nsresult rv = permMgr->TestExactPermissionFromPrincipal(principal, aPermission, &perm);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  LOG("Permission %s for %s: %d\n", aPermission, nsCString(aOrigin).get(), perm);
-  return nsIPermissionManager::ALLOW_ACTION == perm;
-}
-
-bool
-AssertAppProcess(TabContext& aContext,
-                 AssertAppProcessType aType,
-                 const char* aCapability)
-{
-  const mozilla::DocShellOriginAttributes& attr = aContext.OriginAttributesRef();
-  nsCString suffix;
-  attr.CreateSuffix(suffix);
-
-  if (!aContext.SignedPkgOriginNoSuffix().IsEmpty()) {
-    LOG("TabContext owning signed package origin: %s, originAttr; %s\n",
-        nsCString(aContext.SignedPkgOriginNoSuffix()).get(),
-        suffix.get());
-  }
-
-  // Do a origin-based permission check if the TabContext owns a signed package.
-  if (!aContext.SignedPkgOriginNoSuffix().IsEmpty() &&
-      (ASSERT_APP_HAS_PERMISSION == aType || ASSERT_APP_PROCESS_PERMISSION == aType)) {
-    nsCString origin = aContext.SignedPkgOriginNoSuffix() + suffix;
-    return CheckOriginPermission(origin, aCapability);
-  }
-
-  nsCOMPtr<mozIApplication> app = aContext.GetOwnOrContainingApp();
-  return CheckAppTypeHelper(app, aType, aCapability, aContext.IsMozBrowserElement());
-}
-
-bool
-AssertAppStatus(TabContext& aContext,
-                unsigned short aStatus)
-{
-
-  nsCOMPtr<mozIApplication> app = aContext.GetOwnOrContainingApp();
-  return CheckAppStatusHelper(app, aStatus);
-}
-
-bool
-AssertAppProcess(PContentParent* aActor,
-                 AssertAppProcessType aType,
-                 const char* aCapability)
-{
-  nsTArray<TabContext> contextArray =
-    static_cast<ContentParent*>(aActor)->GetManagedTabContext();
-  for (uint32_t i = 0; i < contextArray.Length(); ++i) {
-    if (AssertAppProcess(contextArray[i], aType, aCapability)) {
-      return true;
-    }
-  }
-
-  NS_ERROR(
-    nsPrintfCString(
-      "Security problem: Content process does not have `%s'.  It will be killed.\n",
-      aCapability).get());
-
-  static_cast<ContentParent*>(aActor)->KillHard("AssertAppProcess");
-
-  return false;
-}
-
-bool
-AssertAppStatus(PContentParent* aActor,
-                unsigned short aStatus)
-{
-  nsTArray<TabContext> contextArray =
-    static_cast<ContentParent*>(aActor)->GetManagedTabContext();
-  for (uint32_t i = 0; i < contextArray.Length(); ++i) {
-    if (AssertAppStatus(contextArray[i], aStatus)) {
-      return true;
-    }
-  }
-
-  NS_ERROR(
-    nsPrintfCString(
-      "Security problem: Content process does not have `%d' status.  It will be killed.",
-      aStatus).get());
-
-  static_cast<ContentParent*>(aActor)->KillHard("AssertAppStatus");
-
-  return false;
-}
-
-bool
-AssertAppProcess(PHalParent* aActor,
-                 AssertAppProcessType aType,
-                 const char* aCapability)
-{
-  return AssertAppProcess(aActor->Manager(), aType, aCapability);
-}
-
-bool
-AssertAppPrincipal(PContentParent* aActor,
-                   nsIPrincipal* aPrincipal)
-{
-  if (!aPrincipal) {
-    NS_WARNING("Principal is invalid, killing app process");
-    static_cast<ContentParent*>(aActor)->KillHard("AssertAppPrincipal");
-    return false;
-  }
-
-  uint32_t principalAppId = aPrincipal->GetAppId();
-  bool inIsolatedBrowser = aPrincipal->GetIsInIsolatedMozBrowserElement();
-
-  // Check if the permission's appId matches a child we manage.
-  nsTArray<TabContext> contextArray =
-    static_cast<ContentParent*>(aActor)->GetManagedTabContext();
-  for (uint32_t i = 0; i < contextArray.Length(); ++i) {
-    if (contextArray[i].OwnOrContainingAppId() == principalAppId) {
-      // If the child only runs isolated browser content and the principal
-      // claims it's not in an isolated browser element, it's lying.
-      if (!contextArray[i].IsIsolatedMozBrowserElement() || inIsolatedBrowser) {
-        return true;
-      }
-      break;
-    }
-  }
-
-  NS_WARNING("Principal is invalid, killing app process");
-  static_cast<ContentParent*>(aActor)->KillHard("AssertAppPrincipal");
-  return false;
-}
-
-already_AddRefed<nsIPrincipal>
-GetAppPrincipal(uint32_t aAppId)
-{
-  nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
-
-  nsCOMPtr<mozIApplication> app;
-  nsresult rv = appsService->GetAppByLocalId(aAppId, getter_AddRefs(app));
-  NS_ENSURE_SUCCESS(rv, nullptr);
-
-  nsCOMPtr<nsIPrincipal> principal;
-  app->GetPrincipal(getter_AddRefs(principal));
-
-  return principal.forget();
-}
-
-uint32_t
-CheckPermission(PContentParent* aActor,
-                nsIPrincipal* aPrincipal,
-                const char* aPermission)
-{
-  if (!AssertAppPrincipal(aActor, aPrincipal)) {
-    return nsIPermissionManager::DENY_ACTION;
-  }
-
-  nsCOMPtr<nsIPermissionManager> pm =
-    services::GetPermissionManager();
-  NS_ENSURE_TRUE(pm, nsIPermissionManager::DENY_ACTION);
-
-  // Make sure that `aPermission' is an app permission before checking the origin.
-  nsCOMPtr<nsIPrincipal> appPrincipal = GetAppPrincipal(aPrincipal->GetAppId());
-  uint32_t appPerm = nsIPermissionManager::UNKNOWN_ACTION;
-  nsresult rv = pm->TestExactPermissionFromPrincipal(appPrincipal, aPermission, &appPerm);
-  NS_ENSURE_SUCCESS(rv, nsIPermissionManager::UNKNOWN_ACTION);
-  // Setting to "deny" in the settings UI should deny everywhere.
-  if (appPerm == nsIPermissionManager::UNKNOWN_ACTION ||
-      appPerm == nsIPermissionManager::DENY_ACTION) {
-    return appPerm;
-  }
-
-  uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
-  rv = pm->TestExactPermissionFromPrincipal(aPrincipal, aPermission, &permission);
-  NS_ENSURE_SUCCESS(rv, nsIPermissionManager::UNKNOWN_ACTION);
-  if (permission == nsIPermissionManager::UNKNOWN_ACTION ||
-      permission == nsIPermissionManager::DENY_ACTION) {
-    return permission;
-  }
-
-  // For browser content (and if the app hasn't explicitly denied this),
-  // consider the requesting origin, not the app.
-  // After bug 1238160, the principal no longer knows how to answer "is this a
-  // browser element", which is really what this code path wants. Currently,
-  // desktop is the only platform where we intend to disable isolation on a
-  // browser frame, so non-desktop should be able to assume that
-  // inIsolatedMozBrowser is true for all mozbrowser frames.  This code path is
-  // currently unused on desktop, since MOZ_CHILD_PERMISSIONS is only set for
-  // MOZ_B2G.  We use a release assertion in
-  // nsFrameLoader::OwnerIsIsolatedMozBrowserFrame so that platforms with apps
-  // can assume inIsolatedMozBrowser is true for all mozbrowser frames.
-  if (appPerm == nsIPermissionManager::PROMPT_ACTION &&
-      aPrincipal->GetIsInIsolatedMozBrowserElement()) {
-    return permission;
-  }
-
-  // Setting to "prompt" in the settings UI should prompt everywhere in
-  // non-browser content.
-  if (appPerm == nsIPermissionManager::PROMPT_ACTION ||
-      permission == nsIPermissionManager::PROMPT_ACTION) {
-    return nsIPermissionManager::PROMPT_ACTION;
-  }
-
-  if (appPerm == nsIPermissionManager::ALLOW_ACTION ||
-      permission == nsIPermissionManager::ALLOW_ACTION) {
-    return nsIPermissionManager::ALLOW_ACTION;
-  }
-
-  NS_RUNTIMEABORT("Invalid permission value");
-  return nsIPermissionManager::DENY_ACTION;
-}
-
-#else
-
 bool
 AssertAppProcess(mozilla::dom::PBrowserParent* aActor,
                  AssertAppProcessType aType,
                  const char* aCapability)
 {
   return true;
 }
 
@@ -415,11 +87,9 @@ AssertAppPrincipal(mozilla::dom::PConten
 uint32_t
 CheckPermission(mozilla::dom::PContentParent* aActor,
                 nsIPrincipal* aPrincipal,
                 const char* aPermission)
 {
   return nsIPermissionManager::ALLOW_ACTION;
 }
 
-#endif
-
 } // namespace mozilla
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -4385,23 +4385,16 @@ BlobParent::RecvGetFilePath(nsString* aF
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mBlobImpl);
   MOZ_ASSERT(!mRemoteBlobImpl);
   MOZ_ASSERT(mOwnsBlobImpl);
 
   // In desktop e10s the file picker code sends this message.
 
-#if defined(MOZ_CHILD_PERMISSIONS) && !defined(MOZ_GRAPHENE)
-  if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) {
-    ASSERT_UNLESS_FUZZING();
-    return false;
-  }
-#endif
-
   nsString filePath;
   ErrorResult rv;
   mBlobImpl->GetMozFullPathInternal(filePath, rv);
   if (NS_WARN_IF(rv.Failed())) {
     return false;
   }
 
   *aFilePath = filePath;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4336,24 +4336,16 @@ ContentParent::RecvExtProtocolChannelCon
   parent->SetParentListener(nullptr);
 
   return true;
 }
 
 bool
 ContentParent::HasNotificationPermission(const IPC::Principal& aPrincipal)
 {
-#ifdef MOZ_CHILD_PERMISSIONS
-  uint32_t permission = mozilla::CheckPermission(this, aPrincipal,
-                                                 "desktop-notification");
-  if (permission != nsIPermissionManager::ALLOW_ACTION) {
-    return false;
-  }
-#endif /* MOZ_CHILD_PERMISSIONS */
-
   return true;
 }
 
 bool
 ContentParent::RecvShowAlert(const AlertNotificationType& aAlert)
 {
   nsCOMPtr<nsIAlertNotification> alert(dont_AddRef(aAlert));
   if (NS_WARN_IF(!alert)) {
@@ -4478,26 +4470,16 @@ AddGeolocationListener(nsIDOMGeoPosition
   geo->WatchPosition(watcher, errorCallBack, options, &retval);
   return retval;
 }
 
 bool
 ContentParent::RecvAddGeolocationListener(const IPC::Principal& aPrincipal,
                                           const bool& aHighAccuracy)
 {
-#ifdef MOZ_CHILD_PERMISSIONS
-  if (!ContentParent::IgnoreIPCPrincipal()) {
-    uint32_t permission = mozilla::CheckPermission(this, aPrincipal,
-                                                   "geolocation");
-    if (permission != nsIPermissionManager::ALLOW_ACTION) {
-      return true;
-    }
-  }
-#endif /* MOZ_CHILD_PERMISSIONS */
-
   // To ensure no geolocation updates are skipped, we always force the
   // creation of a new listener.
   RecvRemoveGeolocationListener();
   mGeolocationWatchID = AddGeolocationListener(this, this, aHighAccuracy);
   return true;
 }
 
 bool
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -1620,20 +1620,27 @@ TrackBuffersManager::InsertFrames(TrackB
 
   // 15. Remove decoding dependencies of the coded frames removed in the previous step:
   // Remove all coded frames between the coded frames removed in the previous step and the next random access point after those removed frames.
 
   TimeIntervals intersection = trackBuffer.mBufferedRanges;
   intersection.Intersection(aIntervals);
 
   if (intersection.Length()) {
-    if (aSamples[0]->mKeyframe) {
+    if (aSamples[0]->mKeyframe &&
+        (mType.LowerCaseEqualsLiteral("video/webm") ||
+         mType.LowerCaseEqualsLiteral("audio/webm"))) {
       // We are starting a new GOP, we do not have to worry about breaking an
       // existing current coded frame group. Reset the next insertion index
       // so the search for when to start our frames removal can be exhaustive.
+      // This is a workaround for bug 1276184 and only until either bug 1277733
+      // or bug 1209386 is fixed.
+      // With the webm container, we can't always properly determine the
+      // duration of the last frame, which may cause the last frame of a cluster
+      // to overlap the following frame.
       trackBuffer.mNextInsertionIndex.reset();
     }
     size_t index =
       RemoveFrames(aIntervals, trackBuffer, trackBuffer.mNextInsertionIndex.refOr(0));
     if (index) {
       trackBuffer.mNextInsertionIndex = Some(index);
     }
   }
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -6657,17 +6657,20 @@ nsDisplaySVGEffects::PaintAsLayer(nsDisp
                                   LayerManager* aManager)
 {
   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
   nsSVGIntegrationUtils::PaintFramesParams params(*aCtx->ThebesContext(),
                                                   mFrame,  mVisibleRect,
                                                   borderArea, aBuilder,
                                                   aManager, mOpacityItemCreated);
 
-  nsSVGIntegrationUtils::PaintFramesWithEffects(params);
+  image::DrawResult result =
+    nsSVGIntegrationUtils::PaintFramesWithEffects(params);
+
+  nsDisplaySVGEffectsGeometry::UpdateDrawResult(this, result);
 }
 
 LayerState
 nsDisplaySVGEffects::GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters)
 {
   return LAYER_SVG_EFFECTS;
@@ -6797,16 +6800,21 @@ nsDisplaySVGEffects::ComputeInvalidation
     // Filter and mask output can depend on the location of the frame's user
     // space and on the frame's BBox. We need to invalidate if either of these
     // change relative to the reference frame.
     // Invalidations from our inactive layer manager are not enough to catch
     // some of these cases because filters can produce output even if there's
     // nothing in the filter input.
     aInvalidRegion->Or(bounds, geometry->mBounds);
   }
+
+  if (aBuilder->ShouldSyncDecodeImages() &&
+      geometry->ShouldInvalidateToSyncDecodeImages()) {
+    aInvalidRegion->Or(*aInvalidRegion, bounds);
+  }
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
 nsDisplaySVGEffects::PrintEffects(nsACString& aTo)
 {
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
--- a/layout/base/nsDisplayListInvalidation.cpp
+++ b/layout/base/nsDisplayListInvalidation.cpp
@@ -103,16 +103,17 @@ nsDisplayBoxShadowInnerGeometry::MoveBy(
 nsDisplayBoxShadowOuterGeometry::nsDisplayBoxShadowOuterGeometry(nsDisplayItem* aItem,
     nsDisplayListBuilder* aBuilder, float aOpacity)
   : nsDisplayItemGenericGeometry(aItem, aBuilder)
   , mOpacity(aOpacity)
 {}
 
 nsDisplaySVGEffectsGeometry::nsDisplaySVGEffectsGeometry(nsDisplaySVGEffects* aItem, nsDisplayListBuilder* aBuilder)
   : nsDisplayItemGeometry(aItem, aBuilder)
+  , nsImageGeometryMixin(aItem, aBuilder)
   , mBBox(aItem->BBoxInUserSpace())
   , mUserSpaceOffset(aItem->UserSpaceOffset())
   , mFrameOffsetToReferenceFrame(aItem->ToReferenceFrame())
 {}
 
 void
 nsDisplaySVGEffectsGeometry::MoveBy(const nsPoint& aOffset)
 {
--- a/layout/base/nsDisplayListInvalidation.h
+++ b/layout/base/nsDisplayListInvalidation.h
@@ -237,16 +237,17 @@ public:
     : nsDisplayItemBoundsGeometry(aItem, aBuilder)
     , mColor(aColor)
   { }
 
   nscolor mColor;
 };
 
 class nsDisplaySVGEffectsGeometry : public nsDisplayItemGeometry
+  , public nsImageGeometryMixin<nsDisplaySVGEffectsGeometry>
 {
 public:
   nsDisplaySVGEffectsGeometry(nsDisplaySVGEffects* aItem, nsDisplayListBuilder* aBuilder);
 
   virtual void MoveBy(const nsPoint& aOffset) override;
 
   gfxRect mBBox;
   gfxPoint mUserSpaceOffset;
--- a/layout/generic/nsBlockReflowState.h
+++ b/layout/generic/nsBlockReflowState.h
@@ -11,36 +11,74 @@
 #include "nsFloatManager.h"
 #include "nsLineBox.h"
 #include "nsHTMLReflowState.h"
 
 class nsBlockFrame;
 class nsFrameList;
 class nsOverflowContinuationTracker;
 
-  // block reflow state flags
+// Block reflow state flags.
+//
+// BRS_UNCONSTRAINEDBSIZE is set in the nsBlockReflowState constructor when the
+// frame being reflowed has been given NS_UNCONSTRAINEDSIZE as its available
+// BSize in the nsHTMLReflowState. If set, NS_UNCONSTRAINEDSIZE is passed to
+// nsLineLayout as the available BSize.
 #define BRS_UNCONSTRAINEDBSIZE    0x00000001
-#define BRS_ISBSTARTMARGINROOT    0x00000002  // Is this frame a root for block
-#define BRS_ISBENDMARGINROOT      0x00000004  //  direction start/end margin collapsing?
-#define BRS_APPLYBSTARTMARGIN     0x00000008  // See ShouldApplyTopMargin
+// BRS_ISBSTARTMARGINROOT is set in the nsBlockReflowState constructor when
+// reflowing a "block margin root" frame (i.e. a frame with the
+// NS_BLOCK_MARGIN_ROOT flag set, for which margins apply by default).
+//
+// The flag is also set when reflowing a frame whose computed BStart border
+// padding is non-zero.
+#define BRS_ISBSTARTMARGINROOT    0x00000002
+// BRS_ISBENDMARGINROOT is set in the nsBlockReflowState constructor when
+// reflowing a "block margin root" frame (i.e. a frame with the
+// NS_BLOCK_MARGIN_ROOT flag set, for which margins apply by default).
+//
+// The flag is also set when reflowing a frame whose computed BEnd border
+// padding is non-zero.
+#define BRS_ISBENDMARGINROOT      0x00000004
+// BRS_APPLYBSTARTMARGIN is set if the BStart margin should be considered when
+// placing a linebox that contains a block frame. It may be set as a side-effect
+// of calling nsBlockFrame::ShouldApplyBStartMargin(); once set,
+// ShouldApplyBStartMargin() uses it as a fast-path way to return whether the
+// BStart margin should apply.
+//
+// If the flag hasn't been set in the block reflow state, then
+// ShouldApplyBStartMargin() will crawl the line list to see if a block frame
+// precedes the specified frame. If so, the BStart margin should be applied, and
+// the flag is set to cache the result. (If not, the BStart margin will be
+// applied as a result of the generational margin collapsing logic in
+// nsBlockReflowContext::ComputeCollapsedBStartMargin(). In this case, the flag
+// won't be set, so subsequent calls to ShouldApplyBStartMargin() will continue
+// crawl the line list.)
+//
+// This flag is also set in the nsBlockReflowState constructor if
+// BRS_ISBSTARTMARGINROOT is set; that is, the frame being reflowed is a margin
+// root by default.
+#define BRS_APPLYBSTARTMARGIN     0x00000008
 #define BRS_ISFIRSTINFLOW         0x00000010
 // Set when mLineAdjacentToTop is valid
 #define BRS_HAVELINEADJACENTTOTOP 0x00000020
 // Set when the block has the equivalent of NS_BLOCK_FLOAT_MGR
 #define BRS_FLOAT_MGR             0x00000040
 // Set when nsLineLayout::LineIsEmpty was true at the end of reflowing
 // the current line
 #define BRS_LINE_LAYOUT_EMPTY     0x00000080
 #define BRS_ISOVERFLOWCONTAINER   0x00000100
 // Our mPushedFloats list is stored on the blocks' proptable
 #define BRS_PROPTABLE_FLOATCLIST  0x00000200
 // Set when the pref layout.float-fragments-inside-column.enabled is true.
 #define BRS_FLOAT_FRAGMENTS_INSIDE_COLUMN_ENABLED 0x00000400
 #define BRS_LASTFLAG              BRS_FLOAT_FRAGMENTS_INSIDE_COLUMN_ENABLED
 
+// nsBlockReflowState contains additional reflow state information that the
+// block frame uses along with nsHTMLReflowState. Like nsHTMLReflowState, this
+// is read-only data that is passed down from a parent frame to its children.
 class nsBlockReflowState {
 public:
   nsBlockReflowState(const nsHTMLReflowState& aReflowState,
                      nsPresContext* aPresContext,
                      nsBlockFrame* aFrame,
                      bool aBStartMarginRoot, bool aBEndMarginRoot,
                      bool aBlockNeedsFloatManager,
                      nscoord aConsumedBSize = NS_INTRINSICSIZE);
--- a/layout/generic/nsFrameStateBits.h
+++ b/layout/generic/nsFrameStateBits.h
@@ -46,24 +46,26 @@
 #define FRAME_STATE_BIT(group_, value_, name_) /* nothing */
 #define DEFINED_FRAME_STATE_BIT
 #endif
 
 // == Frame state bits that apply to all frames ===============================
 
 FRAME_STATE_GROUP(Generic, nsIFrame)
 
+// This bit is set when the frame is actively being reflowed. It is set in many
+// frames' Reflow() by calling MarkInReflow() and unset in DidReflow().
 FRAME_STATE_BIT(Generic, 0, NS_FRAME_IN_REFLOW)
 
 // This bit is set when a frame is created. After it has been reflowed
 // once (during the DidReflow with a finished state) the bit is
 // cleared.
 FRAME_STATE_BIT(Generic, 1, NS_FRAME_FIRST_REFLOW)
 
-// For a continuation frame, if this bit is set, then this a "fluid" 
+// For a continuation frame, if this bit is set, then this a "fluid"
 // continuation, i.e., across a line boundary. Otherwise it's a "hard"
 // continuation, e.g. a bidi continuation.
 FRAME_STATE_BIT(Generic, 2, NS_FRAME_IS_FLUID_CONTINUATION)
 
 // For nsIAnonymousContentCreator content that's created using ContentInfo.
 FRAME_STATE_BIT(Generic, 3, NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT)
 
 // If this bit is set, then a reference to the frame is being held
@@ -243,18 +245,18 @@ FRAME_STATE_BIT(Generic, 48, NS_FRAME_NE
 // Frame has a descendant frame that needs painting - This includes
 // cross-doc children.
 FRAME_STATE_BIT(Generic, 49, NS_FRAME_DESCENDANT_NEEDS_PAINT)
 
 // Frame is a descendant of a popup
 FRAME_STATE_BIT(Generic, 50, NS_FRAME_IN_POPUP)
 
 // Frame has only descendant frames that needs painting - This includes
-// cross-doc children. This guarantees that all descendents have 
-// NS_FRAME_NEEDS_PAINT and NS_FRAME_ALL_DESCENDANTS_NEED_PAINT, or they 
+// cross-doc children. This guarantees that all descendents have
+// NS_FRAME_NEEDS_PAINT and NS_FRAME_ALL_DESCENDANTS_NEED_PAINT, or they
 // have no display items.
 FRAME_STATE_BIT(Generic, 51, NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)
 
 // Frame is marked as NS_FRAME_NEEDS_PAINT and also has an explicit
 // rect stored to invalidate.
 FRAME_STATE_BIT(Generic, 52, NS_FRAME_HAS_INVALID_RECT)
 
 // Frame is not displayed directly due to it being, or being under, an SVG
@@ -355,17 +357,17 @@ FRAME_STATE_BIT(SVG, 22, NS_STATE_SVG_PO
 //
 // Percentage values beyond the number of addressable characters, however, do
 // not influence NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES.  For example,
 // NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES would be false for:
 //
 //   <text x="10 20 30 40%">abc</text>
 //
 // NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES is used to determine whether
-// to recompute mPositions when the viewport size changes.  So although the 
+// to recompute mPositions when the viewport size changes.  So although the
 // first example above shows that NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES
 // can be true even if a viewport size change will not affect mPositions,
 // determining a completley accurate value for
 // NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES would require extra work that is
 // probably not worth it.
 FRAME_STATE_BIT(SVG, 23, NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES)
 
 FRAME_STATE_BIT(SVG, 24, NS_STATE_SVG_TEXT_IN_REFLOW)
@@ -447,28 +449,42 @@ FRAME_STATE_BIT(Text, 62, TEXT_NO_RENDER
 // (OffsetToFrameProperty)
 FRAME_STATE_BIT(Text, 63, TEXT_IN_OFFSET_CACHE)
 
 
 // == Frame state bits that apply to block frames =============================
 
 FRAME_STATE_GROUP(Block, nsBlockFrame)
 
-// See the meanings at http://www-archive.mozilla.org/newlayout/doc/block-and-line.html
-
 // Something in the block has changed that requires Bidi resolution to be
-// performed on the block. This flag must be either set on all blocks in a 
+// performed on the block. This flag must be either set on all blocks in a
 // continuation chain or none of them.
 FRAME_STATE_BIT(Block, 20, NS_BLOCK_NEEDS_BIDI_RESOLUTION)
 
 FRAME_STATE_BIT(Block, 21, NS_BLOCK_HAS_PUSHED_FLOATS)
+
+// This indicates that this is a frame from which child margins can be
+// calculated. The absence of this flag implies that child margin calculations
+// should ignore the frame and look further up the parent chain. Used in
+// nsBlockReflowContext::ComputeCollapsedBStartMargin() via
+// nsBlockFrame::IsMarginRoot().
+//
+// This causes the nsBlockReflowState's constructor to set the
+// BRS_ISBSTARTMARGINROOT and BRS_ISBENDMARGINROOT flags.
 FRAME_STATE_BIT(Block, 22, NS_BLOCK_MARGIN_ROOT)
+
+// This indicates that a block frame should create its own float manager. This
+// is required by each block frame that can contain floats. The float manager is
+// used to reserve space for the floated frames.
 FRAME_STATE_BIT(Block, 23, NS_BLOCK_FLOAT_MGR)
+
 FRAME_STATE_BIT(Block, 24, NS_BLOCK_HAS_LINE_CURSOR)
+
 FRAME_STATE_BIT(Block, 25, NS_BLOCK_HAS_OVERFLOW_LINES)
+
 FRAME_STATE_BIT(Block, 26, NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)
 
 // Set on any block that has descendant frames in the normal
 // flow with 'clear' set to something other than 'none'
 // (including <BR CLEAR="..."> frames)
 FRAME_STATE_BIT(Block, 27, NS_BLOCK_HAS_CLEAR_CHILDREN)
 
 // NS_BLOCK_CLIP_PAGINATED_OVERFLOW is only set in paginated prescontexts, on
--- a/layout/generic/nsLineLayout.h
+++ b/layout/generic/nsLineLayout.h
@@ -494,23 +494,45 @@ protected:
     {
       return mJustificationInfo.mIsEndJustifiable;
     }
 
     bool ParticipatesInJustification() const;
   };
   PerFrameData* mFrameFreeList;
 
+  // In nsLineLayout, a "span" is a container inline frame, and a "frame" is one
+  // of its children.
+  //
+  // nsLineLayout::BeginLineReflow() creates the initial PerSpanData which is
+  // called the "root span". nsInlineFrame::ReflowFrames() creates a new
+  // PerSpanData when it calls nsLineLayout::BeginSpan(); at this time, the
+  // nsLineLayout object's mCurrentSpan is switched to the new span. The new
+  // span records the old mCurrentSpan as its parent. After reflowing the child
+  // inline frames, nsInlineFrame::ReflowFrames() calls nsLineLayout::EndSpan(),
+  // which pops the PerSpanData and re-sets mCurrentSpan.
   struct PerSpanData {
     union {
       PerSpanData* mParent;
       PerSpanData* mNextFreeSpan;
     };
+
+    // The PerFrameData of the inline frame that "owns" the span, or null if
+    // this is the root span. mFrame is initialized to the containing inline
+    // frame's PerFrameData when a new PerSpanData is pushed in
+    // nsLineLayout::BeginSpan().
     PerFrameData* mFrame;
+
+    // The first PerFrameData structure in the span.
     PerFrameData* mFirstFrame;
+
+    // The last PerFrameData structure in the span. PerFrameData structures are
+    // added to the span as they are reflowed. mLastFrame may also be directly
+    // manipulated if a line is split, or if frames are pushed from one line to
+    // the next.
     PerFrameData* mLastFrame;
 
     const nsHTMLReflowState* mReflowState;
     bool mNoWrap;
     mozilla::WritingMode mWritingMode;
     bool mContainsFloat;
     bool mHasNonemptyContent;
 
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2130,17 +2130,36 @@ already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetImageLayerImage(const nsStyleImageLayers& aLayers)
 {
   RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
 
   for (uint32_t i = 0, i_end = aLayers.mImageCount; i < i_end; ++i) {
     RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
 
     const nsStyleImage& image = aLayers.mLayers[i].mImage;
-    SetValueToStyleImage(image, val);
+    // Layer::mImage::GetType() returns eStyleImageType_Null in two conditions:
+    // 1. The value of mask-image/bg-image is 'none'.
+    //    Since this layer does not refer to any source, Layer::mSourceURI must
+    //    be nullptr too.
+    // 2. This layer refers to a local resource, e.g. mask-image:url(#mymask).
+    //    For local references, there is no need to download any external
+    //    resource, so Layer::mImage is not used.
+    //    Instead, we store the local URI in one place -- on Layer::mSourceURI.
+    //    Hence, we must serialize using mSourceURI (instead of
+    //    SetValueToStyleImage()/mImage) in this case.
+    bool isLocalURI = image.GetType() == eStyleImageType_Null &&
+                      aLayers.mLayers[i].mSourceURI;
+    if (isLocalURI) {
+      // This is how we represent a 'mask-image' reference for a local URI,
+      // such as 'mask-image:url(#mymask)' or 'mask:url(#mymask)'
+      val->SetURI(aLayers.mLayers[i].mSourceURI);
+    } else {
+      SetValueToStyleImage(image, val);
+    }
+
     valueList->AppendCSSValue(val.forget());
   }
 
   return valueList.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetImageLayerPosition(const nsStyleImageLayers& aLayers)
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -46,16 +46,17 @@
 #include "nsStyleStructInlines.h"
 #include <algorithm>
 #include <cmath>
 #include <limits>
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
+using namespace mozilla::image;
 
 // ============================================================================
 // Utility functions
 
 /**
  * Using the specified gfxSkipCharsIterator, converts an offset and length
  * in original char indexes to skipped char indexes.
  *
@@ -3144,16 +3145,20 @@ public:
   virtual void DisableComponentAlpha() override {
     mDisableSubpixelAA = true;
   }
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState,
                        nsTArray<nsIFrame*> *aOutFrames) override;
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) override;
+  nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
+  {
+    return new nsDisplayItemGenericImageGeometry(this, aBuilder);
+  }
   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override {
     bool snap;
     return GetBounds(aBuilder, &snap);
   }
 private:
   bool mDisableSubpixelAA;
 };
 
@@ -3194,17 +3199,18 @@ nsDisplaySVGText::Paint(nsDisplayListBui
   gfxPoint devPixelOffset =
     nsLayoutUtils::PointToGfxPoint(offset, appUnitsPerDevPixel);
 
   gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(mFrame) *
                    gfxMatrix::Translation(devPixelOffset);
 
   gfxContext* ctx = aCtx->ThebesContext();
   ctx->Save();
-  static_cast<SVGTextFrame*>(mFrame)->PaintSVG(*ctx, tm);
+  DrawResult result = static_cast<SVGTextFrame*>(mFrame)->PaintSVG(*ctx, tm);
+  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
   ctx->Restore();
 }
 
 // ---------------------------------------------------------------------
 // nsQueryFrame methods
 
 NS_QUERYFRAME_HEAD(SVGTextFrame)
   NS_QUERYFRAME_ENTRY(SVGTextFrame)
@@ -3630,53 +3636,53 @@ ShouldPaintCaret(const TextRenderedRun& 
       uint32_t(caretOffset) < aThisRun.mTextFrameContentOffset +
                                 aThisRun.mTextFrameContentLength) {
     return true;
   }
 
   return false;
 }
 
-nsresult
+DrawResult
 SVGTextFrame::PaintSVG(gfxContext& aContext,
                        const gfxMatrix& aTransform,
                        const nsIntRect *aDirtyRect)
 {
   DrawTarget& aDrawTarget = *aContext.GetDrawTarget();
 
   nsIFrame* kid = PrincipalChildList().FirstChild();
   if (!kid)
-    return NS_OK;
+    return DrawResult::SUCCESS;
 
   nsPresContext* presContext = PresContext();
 
   gfxMatrix initialMatrix = aContext.CurrentMatrix();
 
   if (mState & NS_FRAME_IS_NONDISPLAY) {
     // If we are in a canvas DrawWindow call that used the
     // DRAWWINDOW_DO_NOT_FLUSH flag, then we may still have out
     // of date frames.  Just don't paint anything if they are
     // dirty.
     if (presContext->PresShell()->InDrawWindowNotFlushing() &&
         NS_SUBTREE_DIRTY(this)) {
-      return NS_OK;
+      return DrawResult::SUCCESS;
     }
     // Text frames inside <clipPath>, <mask>, etc. will never have had
     // ReflowSVG called on them, so call UpdateGlyphPositioning to do this now.
     UpdateGlyphPositioning();
   } else if (NS_SUBTREE_DIRTY(this)) {
     // If we are asked to paint before reflow has recomputed mPositions etc.
     // directly via PaintSVG, rather than via a display list, then we need
     // to bail out here too.
-    return NS_OK;
+    return DrawResult::SUCCESS;
   }
 
   if (aTransform.IsSingular()) {
     NS_WARNING("Can't render text element!");
-    return NS_ERROR_FAILURE;
+    return DrawResult::BAD_ARGS;
   }
 
   gfxMatrix matrixForPaintServers = aTransform * initialMatrix;
 
   // Check if we need to draw anything.
   if (aDirtyRect) {
     NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
                  (mState & NS_FRAME_IS_NONDISPLAY),
@@ -3688,17 +3694,17 @@ SVGTextFrame::PaintSVG(gfxContext& aCont
     gfxRect frameRect(mRect.x / appUnitsPerDevPixel,
                       mRect.y / appUnitsPerDevPixel,
                       mRect.width / appUnitsPerDevPixel,
                       mRect.height / appUnitsPerDevPixel);
 
     nsRect canvasRect = nsLayoutUtils::RoundGfxRectToAppRect(
         GetCanvasTM().TransformBounds(frameRect), 1);
     if (!canvasRect.Intersects(dirtyRect)) {
-      return NS_OK;
+      return DrawResult::SUCCESS;
     }
   }
 
   // SVG frames' PaintSVG methods paint in CSS px, but normally frames paint in
   // dev pixels. Here we multiply a CSS-px-to-dev-pixel factor onto aTransform
   // so our non-SVG nsTextFrame children paint correctly.
   auto auPerDevPx = presContext->AppUnitsPerDevPixel();
   float cssPxPerDevPx = presContext->AppUnitsToFloatCSSPixels(auPerDevPx);
@@ -3775,17 +3781,17 @@ SVGTextFrame::PaintSVG(gfxContext& aCont
       // caret with, rather than using the color property?
       caret->PaintCaret(aDrawTarget, frame, nsPoint());
       aContext.NewPath();
     }
 
     run = it.Next();
   }
 
-  return NS_OK;
+  return DrawResult::SUCCESS;
 }
 
 nsIFrame*
 SVGTextFrame::GetFrameForPoint(const gfxPoint& aPoint)
 {
   NS_ASSERTION(PrincipalChildList().FirstChild(), "must have a child frame");
 
   if (mState & NS_FRAME_IS_NONDISPLAY) {
--- a/layout/svg/SVGTextFrame.h
+++ b/layout/svg/SVGTextFrame.h
@@ -258,16 +258,17 @@ class SVGTextFrame final : public nsSVGD
   friend class MutationObserver;
   friend class nsDisplaySVGText;
 
   typedef gfxTextRun::Range Range;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Path Path;
   typedef mozilla::gfx::Point Point;
   typedef mozilla::SVGTextContextPaint SVGTextContextPaint;
+  typedef mozilla::image::DrawResult DrawResult;
 
 protected:
   explicit SVGTextFrame(nsStyleContext* aContext)
     : nsSVGDisplayContainerFrame(aContext)
     , mFontSizeScaleFactor(1.0f)
     , mLastContextScale(1.0f)
     , mLengthAdjustScaleFactor(1.0f)
   {
@@ -320,19 +321,19 @@ public:
    */
   virtual void FindCloserFrameForSelection(nsPoint aPoint,
                                           FrameWithDistance* aCurrentBestFrame) override;
 
 
 
   // nsISVGChildFrame interface:
   virtual void NotifySVGChanged(uint32_t aFlags) override;
-  virtual nsresult PaintSVG(gfxContext& aContext,
-                            const gfxMatrix& aTransform,
-                            const nsIntRect* aDirtyRect = nullptr) override;
+  virtual DrawResult PaintSVG(gfxContext& aContext,
+                              const gfxMatrix& aTransform,
+                              const nsIntRect* aDirtyRect = nullptr) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
   virtual void ReflowSVG() override;
   virtual nsRect GetCoveredRegion() override;
   virtual SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace,
                                       uint32_t aFlags) override;
 
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM() override;
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -445,22 +445,23 @@ nsFilterInstance::BuildSourceImage(DrawT
     return NS_ERROR_FAILURE;
   }
   RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(offscreenDT);
   MOZ_ASSERT(ctx); // already checked the draw target above
   ctx->SetMatrix(
     ctx->CurrentMatrix().Translate(-neededRect.TopLeft()).
                          PreMultiply(deviceToFilterSpace));
 
-  mPaintCallback->Paint(*ctx, mTargetFrame, mPaintTransform, &dirty);
+  DrawResult result =
+    mPaintCallback->Paint(*ctx, mTargetFrame, mPaintTransform, &dirty);
 
   mSourceGraphic.mSourceSurface = offscreenDT->Snapshot();
   mSourceGraphic.mSurfaceRect = neededRect;
 
-  return NS_OK;
+  return (result == DrawResult::SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
 nsFilterInstance::Render(DrawTarget* aDrawTarget)
 {
   MOZ_ASSERT(mTargetFrame, "Need a frame for rendering");
 
   nsIntRect filterRect =
--- a/layout/svg/nsISVGChildFrame.h
+++ b/layout/svg/nsISVGChildFrame.h
@@ -38,16 +38,17 @@ class Matrix;
 class nsISVGChildFrame : public nsQueryFrame
 {
 public:
   typedef mozilla::SVGAnimatedNumberList SVGAnimatedNumberList;
   typedef mozilla::SVGNumberList SVGNumberList;
   typedef mozilla::SVGAnimatedLengthList SVGAnimatedLengthList;
   typedef mozilla::SVGLengthList SVGLengthList;
   typedef mozilla::SVGUserUnitList SVGUserUnitList;
+  typedef mozilla::image::DrawResult DrawResult;
 
   NS_DECL_QUERYFRAME_TARGET(nsISVGChildFrame)
 
   /**
    * Paint this frame.
    *
    * SVG is painted using a combination of display lists (trees of
    * nsDisplayItem built by BuildDisplayList() implementations) and recursive
@@ -68,19 +69,19 @@ public:
    *   the DrawTarget when possible and instead just pass a transform down to
    *   their children.  This is preferable because changing the transform is
    *   very expensive for certain DrawTarget backends so it is best to minimize
    *   the number of transform changes.
    *
    * @param aDirtyRect The area being redrawn, in frame offset pixel
    *   coordinates.
    */
-  virtual nsresult PaintSVG(gfxContext& aContext,
-                            const gfxMatrix& aTransform,
-                            const nsIntRect* aDirtyRect = nullptr) = 0;
+  virtual DrawResult PaintSVG(gfxContext& aContext,
+                              const gfxMatrix& aTransform,
+                              const nsIntRect* aDirtyRect = nullptr) = 0;
 
   /**
    * Returns the frame that should handle pointer events at aPoint.  aPoint is
    * expected to be in the SVG user space of the frame on which this method is
    * called.  The frame returned may be the frame on which this method is
    * called, any of its descendants or else nullptr.
    */
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) = 0;
--- a/layout/svg/nsSVGClipPathFrame.cpp
+++ b/layout/svg/nsSVGClipPathFrame.cpp
@@ -82,20 +82,24 @@ nsSVGClipPathFrame::ApplyClipPath(gfxCon
 }
 
 already_AddRefed<SourceSurface>
 nsSVGClipPathFrame::GetClipMask(gfxContext& aReferenceContext,
                                 nsIFrame* aClippedFrame,
                                 const gfxMatrix& aMatrix,
                                 Matrix* aMaskTransform,
                                 SourceSurface* aExtraMask,
-                                const Matrix& aExtraMasksTransform)
+                                const Matrix& aExtraMasksTransform,
+                                DrawResult* aResult)
 {
   MOZ_ASSERT(!IsTrivial(), "Caller needs to use ApplyClipPath");
 
+  if (aResult) {
+    *aResult = DrawResult::SUCCESS;
+  }
   DrawTarget& aReferenceDT = *aReferenceContext.GetDrawTarget();
 
   // A clipPath can reference another clipPath.  We re-enter this method for
   // each clipPath in a reference chain, so here we limit chain length:
   static int16_t sRefChainLengthCounter = AutoReferenceLimiter::notReferencing;
   AutoReferenceLimiter
     refChainLengthLimiter(&sRefChainLengthCounter,
                           MAX_SVG_CLIP_PATH_REFERENCE_CHAIN_LENGTH);
@@ -207,17 +211,20 @@ nsSVGClipPathFrame::GetClipMask(gfxConte
           toChildsUserSpace =
             static_cast<const nsSVGElement*>(childContent)->
               PrependLocalTransformsTo(mMatrixForChildren, eUserSpaceToParent);
         }
 
         // Our children have NS_STATE_SVG_CLIPPATH_CHILD set on them, and
         // nsSVGPathGeometryFrame::Render checks for that state bit and paints
         // only the geometry (opaque black) if set.
-        SVGFrame->PaintSVG(*ctx, toChildsUserSpace);
+        DrawResult result = SVGFrame->PaintSVG(*ctx, toChildsUserSpace);
+        if (aResult) {
+          *aResult &= result;
+        }
 
         if (clipPathThatClipsChild) {
           if (childsClipPathRequiresMasking) {
             ctx->PopGroupAndBlend();
           }
           ctx->Restore();
         }
       }
--- a/layout/svg/nsSVGClipPathFrame.h
+++ b/layout/svg/nsSVGClipPathFrame.h
@@ -17,16 +17,17 @@ class nsISVGChildFrame;
 
 class nsSVGClipPathFrame : public nsSVGContainerFrame
 {
   friend nsIFrame*
   NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
   typedef mozilla::gfx::Matrix Matrix;
   typedef mozilla::gfx::SourceSurface SourceSurface;
+  typedef mozilla::image::DrawResult DrawResult;
 
 protected:
   explicit nsSVGClipPathFrame(nsStyleContext* aContext)
     : nsSVGContainerFrame(aContext)
     , mReferencing(mozilla::AutoReferenceLimiter::notReferencing)
   {
     AddStateBits(NS_FRAME_IS_NONDISPLAY);
   }
@@ -71,22 +72,24 @@ public:
    * @param aMatrix The transform from aClippedFrame's user space to aContext's
    *   current transform.
    * @param [out] aMaskTransform The transform to use with the returned
    *   surface.
    * @param [in, optional] aExtraMask An extra surface that the returned
    *   surface should be masked with.
    * @param [in, optional] aExtraMasksTransform The transform to use with
    *   aExtraMask. Should be passed when aExtraMask is passed.
+   * @param [out, optional] aResult returns the result of drawing action.
    */
   already_AddRefed<SourceSurface>
     GetClipMask(gfxContext& aReferenceContext, nsIFrame* aClippedFrame,
                 const gfxMatrix& aMatrix, Matrix* aMaskTransform,
                 SourceSurface* aExtraMask = nullptr,
-                const Matrix& aExtraMasksTransform = Matrix());
+                const Matrix& aExtraMasksTransform = Matrix(),
+                DrawResult* aResult = nullptr);
 
   /**
    * aPoint is expected to be in aClippedFrame's SVG user space.
    */
   bool PointIsInsideClipPath(nsIFrame* aClippedFrame, const gfxPoint &aPoint);
 
   // Check if this clipPath is made up of more than one geometry object.
   // If so, the clipping API in cairo isn't enough and we need to use
--- a/layout/svg/nsSVGContainerFrame.cpp
+++ b/layout/svg/nsSVGContainerFrame.cpp
@@ -243,60 +243,64 @@ nsSVGDisplayContainerFrame::IsSVGTransfo
     }
   }
   return foundTransform;
 }
 
 //----------------------------------------------------------------------
 // nsISVGChildFrame methods
 
-nsresult
+DrawResult
 nsSVGDisplayContainerFrame::PaintSVG(gfxContext& aContext,
                                      const gfxMatrix& aTransform,
                                      const nsIntRect *aDirtyRect)
 {
   NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
                (mState & NS_FRAME_IS_NONDISPLAY) ||
                PresContext()->IsGlyph(),
                "If display lists are enabled, only painting of non-display "
                "SVG should take this code path");
 
   if (StyleEffects()->mOpacity == 0.0) {
-    return NS_OK;
+    return DrawResult::SUCCESS;
   }
 
   gfxMatrix matrix = aTransform;
   if (GetContent()->IsSVGElement()) { // must check before cast
     matrix = static_cast<const nsSVGElement*>(GetContent())->
                PrependLocalTransformsTo(matrix, eChildToUserSpace);
     if (matrix.IsSingular()) {
-      return NS_OK;
+      return DrawResult::SUCCESS;
     }
   }
 
+  DrawResult result = DrawResult::SUCCESS;
   for (nsIFrame* kid = mFrames.FirstChild(); kid;
        kid = kid->GetNextSibling()) {
     gfxMatrix m = matrix;
     // PaintFrameWithEffects() expects the transform that is passed to it to
     // include the transform to the passed frame's user space, so add it:
     const nsIContent* content = kid->GetContent();
     if (content->IsSVGElement()) { // must check before cast
       const nsSVGElement* element = static_cast<const nsSVGElement*>(content);
       if (!element->HasValidDimensions()) {
         continue; // nothing to paint for kid
       }
       m = element->PrependLocalTransformsTo(m, eUserSpaceToParent);
       if (m.IsSingular()) {
         continue;
       }
     }
-    nsSVGUtils::PaintFrameWithEffects(kid, aContext, m, aDirtyRect);
+    result = nsSVGUtils::PaintFrameWithEffects(kid, aContext, m, aDirtyRect);
+    if (result != DrawResult::SUCCESS) {
+      return result;
+    }
   }
 
-  return NS_OK;
+  return result;
 }
 
 nsIFrame*
 nsSVGDisplayContainerFrame::GetFrameForPoint(const gfxPoint& aPoint)
 {
   NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
                (mState & NS_FRAME_IS_NONDISPLAY),
                "If display lists are enabled, only hit-testing of a "
--- a/layout/svg/nsSVGContainerFrame.h
+++ b/layout/svg/nsSVGContainerFrame.h
@@ -135,19 +135,19 @@ public:
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) override;
 
   virtual bool IsSVGTransformed(Matrix *aOwnTransform = nullptr,
                                 Matrix *aFromParentTransform = nullptr) const override;
 
   // nsISVGChildFrame interface:
-  virtual nsresult PaintSVG(gfxContext& aContext,
-                            const gfxMatrix& aTransform,
-                            const nsIntRect *aDirtyRect = nullptr) override;
+  virtual DrawResult PaintSVG(gfxContext& aContext,
+                              const gfxMatrix& aTransform,
+                              const nsIntRect *aDirtyRect = nullptr) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
   virtual nsRect GetCoveredRegion() override;
   virtual void ReflowSVG() override;
   virtual void NotifySVGChanged(uint32_t aFlags) override;
   virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                       uint32_t aFlags) override;
   virtual bool IsDisplayContainer() override { return true; }
 };
--- a/layout/svg/nsSVGFilterPaintCallback.h
+++ b/layout/svg/nsSVGFilterPaintCallback.h
@@ -8,26 +8,28 @@
 
 #include "nsRect.h"
 
 class nsIFrame;
 class gfxContext;
 
 class nsSVGFilterPaintCallback {
 public:
+  typedef mozilla::image::DrawResult DrawResult;
+
   /**
    * Paint the frame contents.
    * SVG frames will have had matrix propagation set to false already.
    * Non-SVG frames have to do their own thing.
    * The caller will do a Save()/Restore() as necessary so feel free
    * to mess with context state.
    * The context will be configured to use the "user space" coordinate
    * system.
    * @param aDirtyRect the dirty rect *in user space pixels*
    * @param aTransformRoot the outermost frame whose transform should be taken
    *                       into account when painting an SVG glyph
    */
-  virtual void Paint(gfxContext& aContext, nsIFrame *aTarget,
-                     const gfxMatrix& aTransform,
-                     const nsIntRect *aDirtyRect) = 0;
+  virtual DrawResult Paint(gfxContext& aContext, nsIFrame *aTarget,
+                           const gfxMatrix& aTransform,
+                           const nsIntRect *aDirtyRect) = 0;
 };
 
 #endif
--- a/layout/svg/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/nsSVGForeignObjectFrame.cpp
@@ -193,36 +193,36 @@ nsSVGForeignObjectFrame::IsSVGTransforme
                                        gfxMatrix(),
                                        eUserSpaceToParent));
     }
     foundTransform = true;
   }
   return foundTransform;
 }
 
-nsresult
+DrawResult
 nsSVGForeignObjectFrame::PaintSVG(gfxContext& aContext,
                                   const gfxMatrix& aTransform,
                                   const nsIntRect* aDirtyRect)
 {
   NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
                (mState & NS_FRAME_IS_NONDISPLAY),
                "If display lists are enabled, only painting of non-display "
                "SVG should take this code path");
 
   if (IsDisabled())
-    return NS_OK;
+    return DrawResult::SUCCESS;
 
   nsIFrame* kid = PrincipalChildList().FirstChild();
   if (!kid)
-    return NS_OK;
+    return DrawResult::SUCCESS;
 
   if (aTransform.IsSingular()) {
     NS_WARNING("Can't render foreignObject element!");
-    return NS_ERROR_FAILURE;
+    return DrawResult::BAD_ARGS;
   }
 
   nsRect kidDirtyRect = kid->GetVisualOverflowRect();
 
   /* Check if we need to draw anything. */
   if (aDirtyRect) {
     NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
                  (mState & NS_FRAME_IS_NONDISPLAY),
@@ -240,17 +240,17 @@ nsSVGForeignObjectFrame::PaintSVG(gfxCon
       nsLayoutUtils::RoundGfxRectToAppRect(transDirtyRect,
                        PresContext()->AppUnitsPerCSSPixel()));
 
     // XXX after bug 614732 is fixed, we will compare mRect with aDirtyRect,
     // not with kidDirtyRect. I.e.
     // int32_t appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
     // mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect)
     if (kidDirtyRect.IsEmpty())
-      return NS_OK;
+      return DrawResult::SUCCESS;
   }
 
   aContext.Save();
 
   if (StyleDisplay()->IsScrollableOverflow()) {
     float x, y, width, height;
     static_cast<nsSVGElement*>(mContent)->
       GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
@@ -272,23 +272,23 @@ nsSVGForeignObjectFrame::PaintSVG(gfxCon
 
   using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
   PaintFrameFlags flags = PaintFrameFlags::PAINT_IN_TRANSFORM;
   if (SVGAutoRenderState::IsPaintingToWindow(aContext.GetDrawTarget())) {
     flags |= PaintFrameFlags::PAINT_TO_WINDOW;
   }
   nsRenderingContext rendCtx(&aContext);
   nsresult rv = nsLayoutUtils::PaintFrame(&rendCtx, kid, nsRegion(kidDirtyRect),
-                                          NS_RGBA(0,0,0,0),
-                                          nsDisplayListBuilderMode::PAINTING,
-                                          flags);
+                                         NS_RGBA(0,0,0,0),
+                                         nsDisplayListBuilderMode::PAINTING,
+                                         flags);
 
   aContext.Restore();
 
-  return rv;
+  return NS_FAILED(rv) ? DrawResult::BAD_ARGS : DrawResult::SUCCESS;
 }
 
 nsIFrame*
 nsSVGForeignObjectFrame::GetFrameForPoint(const gfxPoint& aPoint)
 {
   NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
                (mState & NS_FRAME_IS_NONDISPLAY),
                "If display lists are enabled, only hit-testing of a "
--- a/layout/svg/nsSVGForeignObjectFrame.h
+++ b/layout/svg/nsSVGForeignObjectFrame.h
@@ -69,19 +69,19 @@ public:
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override
   {
     return MakeFrameName(NS_LITERAL_STRING("SVGForeignObject"), aResult);
   }
 #endif
 
   // nsISVGChildFrame interface:
-  virtual nsresult PaintSVG(gfxContext& aContext,
-                            const gfxMatrix& aTransform,
-                            const nsIntRect* aDirtyRect = nullptr) override;
+  virtual DrawResult PaintSVG(gfxContext& aContext,
+                              const gfxMatrix& aTransform,
+                              const nsIntRect* aDirtyRect = nullptr) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
   virtual nsRect GetCoveredRegion() override;
   virtual void ReflowSVG() override;
   virtual void NotifySVGChanged(uint32_t aFlags) override;
   virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                       uint32_t aFlags) override;
   virtual bool IsDisplayContainer() override { return true; }
 
--- a/layout/svg/nsSVGImageFrame.cpp
+++ b/layout/svg/nsSVGImageFrame.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/dom/SVGImageElement.h"
 #include "nsContentUtils.h"
 #include "nsIReflowCallback.h"
 #include "mozilla/unused.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
+using namespace mozilla::image;
 
 class nsSVGImageFrame;
 
 class nsSVGImageListener final : public imgINotificationObserver
 {
 public:
   explicit nsSVGImageListener(nsSVGImageFrame *aFrame);
 
@@ -60,19 +61,19 @@ protected:
   }
 
   virtual ~nsSVGImageFrame();
 
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   // nsISVGChildFrame interface:
-  virtual nsresult PaintSVG(gfxContext& aContext,
-                            const gfxMatrix& aTransform,
-                            const nsIntRect* aDirtyRect = nullptr) override;
+  virtual DrawResult PaintSVG(gfxContext& aContext,
+                              const gfxMatrix& aTransform,
+                              const nsIntRect* aDirtyRect = nullptr) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
   virtual void ReflowSVG() override;
 
   // nsSVGPathGeometryFrame methods:
   virtual uint16_t GetHitTestFlags() override;
 
   // nsIFrame interface:
   virtual nsresult  AttributeChanged(int32_t         aNameSpaceID,
@@ -319,25 +320,23 @@ nsSVGImageFrame::TransformContextForPain
   }
 
   aGfxContext->Multiply(ThebesMatrix(imageTransform));
   return true;
 }
 
 //----------------------------------------------------------------------
 // nsISVGChildFrame methods:
-nsresult
+DrawResult
 nsSVGImageFrame::PaintSVG(gfxContext& aContext,
                           const gfxMatrix& aTransform,
                           const nsIntRect *aDirtyRect)
 {
-  nsresult rv = NS_OK;
-
   if (!StyleVisibility()->IsVisible())
-    return NS_OK;
+    return DrawResult::SUCCESS;
 
   float x, y, width, height;
   SVGImageElement *imgElem = static_cast<SVGImageElement*>(mContent);
   imgElem->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
   NS_ASSERTION(width > 0 && height > 0,
                "Should only be painting things with valid width/height");
 
   if (!mImageContainer) {
@@ -346,27 +345,28 @@ nsSVGImageFrame::PaintSVG(gfxContext& aC
     if (imageLoader)
       imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
                               getter_AddRefs(currentRequest));
 
     if (currentRequest)
       currentRequest->GetImage(getter_AddRefs(mImageContainer));
   }
 
+  DrawResult result = DrawResult::SUCCESS;
   if (mImageContainer) {
     gfxContextAutoSaveRestore autoRestorer(&aContext);
 
     if (StyleDisplay()->IsScrollableOverflow()) {
       gfxRect clipRect = nsSVGUtils::GetClipRectForFrame(this, x, y,
                                                          width, height);
       nsSVGUtils::SetClipRect(&aContext, aTransform, clipRect);
     }
 
     if (!TransformContextForPainting(&aContext, aTransform)) {
-      return NS_ERROR_FAILURE;
+      return DrawResult::SUCCESS;
     }
 
     // fill-opacity doesn't affect <image>, so if we're allowed to
     // optimize group opacity, the opacity used for compositing the
     // image into the current canvas is just the group opacity.
     float opacity = 1.0f;
     if (nsSVGUtils::CanOptimizeOpacity(this)) {
       opacity = StyleEffects()->mOpacity;
@@ -412,45 +412,43 @@ nsSVGImageFrame::PaintSVG(gfxContext& aC
       LayoutDeviceSize devPxSize(width, height);
       nsRect destRect(nsPoint(),
                       LayoutDevicePixel::ToAppUnits(devPxSize,
                                                     appUnitsPerDevPx));
 
       // Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case.
       // That method needs our image to have a fixed native width & height,
       // and that's not always true for TYPE_VECTOR images.
-      // FIXME We should use the return value, see bug 1258510.
-      Unused << nsLayoutUtils::DrawSingleImage(
+      result = nsLayoutUtils::DrawSingleImage(
         aContext,
         PresContext(),
         mImageContainer,
         nsLayoutUtils::GetSamplingFilterForFrame(this),
         destRect,
         aDirtyRect ? dirtyRect : destRect,
         &context,
         drawFlags);
     } else { // mImageContainer->GetType() == TYPE_RASTER
-      // FIXME We should use the return value, see bug 1258510.
-      Unused << nsLayoutUtils::DrawSingleUnscaledImage(
+      result = nsLayoutUtils::DrawSingleUnscaledImage(
         aContext,
         PresContext(),
         mImageContainer,
         nsLayoutUtils::GetSamplingFilterForFrame(this),
         nsPoint(0, 0),
         aDirtyRect ? &dirtyRect : nullptr,
         drawFlags);
     }
 
     if (opacity != 1.0f || StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
       aContext.PopGroupAndBlend();
     }
     // gfxContextAutoSaveRestore goes out of scope & cleans up our gfxContext
   }
 
-  return rv;
+  return result;
 }
 
 nsIFrame*
 nsSVGImageFrame::GetFrameForPoint(const gfxPoint& aPoint)
 {
   if (!(GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) && !GetHitTestFlags()) {
     return nullptr;
   }
--- a/layout/svg/nsSVGInnerSVGFrame.cpp
+++ b/layout/svg/nsSVGInnerSVGFrame.cpp
@@ -13,16 +13,17 @@
 #include "nsISVGChildFrame.h"
 #include "nsSVGContainerFrame.h"
 #include "nsSVGIntegrationUtils.h"
 #include "mozilla/dom/SVGSVGElement.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
+using namespace mozilla::image;
 
 nsIFrame*
 NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsSVGInnerSVGFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSVGInnerSVGFrame)
@@ -52,17 +53,17 @@ nsIAtom *
 nsSVGInnerSVGFrame::GetType() const
 {
   return nsGkAtoms::svgInnerSVGFrame;
 }
 
 //----------------------------------------------------------------------
 // nsISVGChildFrame methods
 
-nsresult
+DrawResult
 nsSVGInnerSVGFrame::PaintSVG(gfxContext& aContext,
                              const gfxMatrix& aTransform,
                              const nsIntRect *aDirtyRect)
 {
   NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
                (mState & NS_FRAME_IS_NONDISPLAY),
                "If display lists are enabled, only painting of non-display "
                "SVG should take this code path");
@@ -70,17 +71,17 @@ nsSVGInnerSVGFrame::PaintSVG(gfxContext&
   gfxContextAutoSaveRestore autoSR;
 
   if (StyleDisplay()->IsScrollableOverflow()) {
     float x, y, width, height;
     static_cast<SVGSVGElement*>(mContent)->
       GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
 
     if (width <= 0 || height <= 0) {
-      return NS_OK;
+      return DrawResult::SUCCESS;
     }
 
     autoSR.SetContext(&aContext);
     gfxRect clipRect =
       nsSVGUtils::GetClipRectForFrame(this, x, y, width, height);
     nsSVGUtils::SetClipRect(&aContext, aTransform, clipRect);
   }
 
--- a/layout/svg/nsSVGInnerSVGFrame.h
+++ b/layout/svg/nsSVGInnerSVGFrame.h
@@ -47,19 +47,19 @@ public:
   }
 #endif
 
   virtual nsresult  AttributeChanged(int32_t         aNameSpaceID,
                                      nsIAtom*        aAttribute,
                                      int32_t         aModType) override;
 
   // nsISVGChildFrame interface:
-  virtual nsresult PaintSVG(gfxContext& aContext,
-                            const gfxMatrix& aTransform,
-                            const nsIntRect *aDirtyRect = nullptr) override;
+  virtual DrawResult PaintSVG(gfxContext& aContext,
+                              const gfxMatrix& aTransform,
+                              const nsIntRect *aDirtyRect = nullptr) override;
   virtual nsRect GetCoveredRegion() override;
   virtual void ReflowSVG() override;
   virtual void NotifySVGChanged(uint32_t aFlags) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
 
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM() override;
 
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -25,16 +25,17 @@
 #include "BasicLayers.h"
 #include "mozilla/gfx/Point.h"
 #include "nsCSSRendering.h"
 #include "mozilla/unused.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
+using namespace mozilla::image;
 
 // ----------------------------------------------------------------------
 
 /**
  * This class is used to get the pre-effects visual overflow rect of a frame,
  * or, in the case of a frame with continuations, to collect the union of the
  * pre-effects visual overflow rects of all the continuations. The result is
  * relative to the origin (top left corner of the border box) of the frame, or,
@@ -380,31 +381,32 @@ class RegularFramePaintCallback : public
 {
 public:
   RegularFramePaintCallback(nsDisplayListBuilder* aBuilder,
                             LayerManager* aManager,
                             const nsPoint& aOffset)
     : mBuilder(aBuilder), mLayerManager(aManager),
       mOffset(aOffset) {}
 
-  virtual void Paint(gfxContext& aContext, nsIFrame *aTarget,
-                     const gfxMatrix& aTransform,
-                     const nsIntRect* aDirtyRect) override
+  virtual DrawResult Paint(gfxContext& aContext, nsIFrame *aTarget,
+                           const gfxMatrix& aTransform,
+                           const nsIntRect* aDirtyRect) override
   {
     BasicLayerManager* basic = static_cast<BasicLayerManager*>(mLayerManager);
     basic->SetTarget(&aContext);
 
     gfxPoint devPixelOffset =
       nsLayoutUtils::PointToGfxPoint(-mOffset,
                                      aTarget->PresContext()->AppUnitsPerDevPixel());
 
     gfxContextMatrixAutoSaveRestore autoSR(&aContext);
     aContext.SetMatrix(aContext.CurrentMatrix().Translate(devPixelOffset));
 
     mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, mBuilder);
+    return DrawResult::SUCCESS;
   }
 
 private:
   nsDisplayListBuilder* mBuilder;
   LayerManager* mLayerManager;
   nsPoint mOffset;
 };
 
@@ -469,17 +471,17 @@ ComputeMaskGeometry(const nsSVGIntegrati
   ctx.Restore();
 
   IntRect result;
   ToRect(clippedFrameSurfaceRect).ToIntRect(&result);
   return mozilla::gfx::Factory::CheckSurfaceSize(result.Size()) ? result
                                                                 : IntRect();
 }
 
-static void
+static DrawResult
 GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
                     float aOpacity, nsStyleContext* aSC,
                     const nsTArray<nsSVGMaskFrame *>& aMaskFrames,
                     const nsPoint& aOffsetToUserSpace,
                     Matrix& aOutMaskTransform,
                     RefPtr<SourceSurface>& aOutMaskSurface)
 {
   const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
@@ -492,38 +494,38 @@ GenerateMaskSurface(const nsSVGIntegrati
 
   // There is only one SVG mask.
   if (((aMaskFrames.Length() == 1) && aMaskFrames[0])) {
     aOutMaskSurface =
       aMaskFrames[0]->GetMaskForMaskedFrame(&ctx, aParams.frame,
                                             cssPxToDevPxMatrix, aOpacity,
                                             &aOutMaskTransform,
                                             svgReset->mMask.mLayers[0].mMaskMode);
-    return;
+    return DrawResult::SUCCESS;
   }
 
   IntRect maskSurfaceRect = ComputeMaskGeometry(aParams, svgReset,
                                                 aOffsetToUserSpace,
                                                 aMaskFrames);
   if (maskSurfaceRect.IsEmpty()) {
-    return;
+    return DrawResult::SUCCESS;
   }
 
   // Mask composition result on CoreGraphic::A8 surface is not correct
   // when mask-mode is not add(source over). Switch to skia when CG backend
   // detected.
   RefPtr<DrawTarget> maskDT =
     (ctx.GetDrawTarget()->GetBackendType() == BackendType::COREGRAPHICS ||
      ctx.GetDrawTarget()->GetBackendType() == BackendType::DIRECT2D1_1)
     ? Factory::CreateDrawTarget(BackendType::SKIA, maskSurfaceRect.Size(),
                                 SurfaceFormat::A8)
     : ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(),
                                                    SurfaceFormat::A8);
   if (!maskDT || !maskDT->IsValid()) {
-    return;
+    return DrawResult::TEMPORARY_ERROR;
   }
 
   RefPtr<gfxContext> maskContext = gfxContext::CreateOrNull(maskDT);
   MOZ_ASSERT(maskContext);
 
   nsPresContext* presContext = aParams.frame->PresContext();
   gfxPoint devPixelOffsetToUserSpace =
     nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace,
@@ -570,31 +572,35 @@ GenerateMaskSurface(const nsSVGIntegrati
         nsCSSRendering::PaintBGParams::ForSingleLayer(*presContext,
                                                       rc, aParams.dirtyRect,
                                                       aParams.borderArea,
                                                       aParams.frame,
                                                       aParams.builder->GetBackgroundPaintFlags() |
                                                       nsCSSRendering::PAINTBG_MASK_IMAGE,
                                                       i, compositionOp);
 
-      // FIXME We should use the return value, see bug 1258510.
-      Unused << nsCSSRendering::PaintBackgroundWithSC(params, aSC,
-                                                      *aParams.frame->StyleBorder());
+      DrawResult result =
+        nsCSSRendering::PaintBackgroundWithSC(params, aSC,
+                                              *aParams.frame->StyleBorder());
+      if (result != DrawResult::SUCCESS) {
+        return result;
+      }
     }
   }
 
   aOutMaskTransform = ToMatrix(maskSurfaceMatrix);
   if (!aOutMaskTransform.Invert()) {
-    return;
+    return DrawResult::SUCCESS;
   }
 
   aOutMaskSurface = maskDT->Snapshot();
+  return DrawResult::SUCCESS;
 }
 
-void
+DrawResult
 nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams)
 {
 #ifdef DEBUG
   NS_ASSERTION(!(aParams.frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
                (NS_SVGDisplayListPaintingEnabled() &&
                 !(aParams.frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)),
                "Should not use nsSVGIntegrationUtils on this SVG frame");
 #endif
@@ -614,26 +620,26 @@ nsSVGIntegrationUtils::PaintFramesWithEf
    */
   nsIFrame* frame = aParams.frame;
   const nsIContent* content = frame->GetContent();
   bool hasSVGLayout = (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
   if (hasSVGLayout) {
     nsISVGChildFrame *svgChildFrame = do_QueryFrame(frame);
     if (!svgChildFrame || !frame->GetContent()->IsSVGElement()) {
       NS_ASSERTION(false, "why?");
-      return;
+      return DrawResult::BAD_ARGS;
     }
     if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
-      return; // The SVG spec says not to draw _anything_
+      return DrawResult::SUCCESS; // The SVG spec says not to draw _anything_
     }
   }
 
   float opacity = frame->StyleEffects()->mOpacity;
   if (opacity == 0.0f) {
-    return;
+    return DrawResult::SUCCESS;
   }
   if (opacity != 1.0f &&
       (nsSVGUtils::CanOptimizeOpacity(frame) ||
        aParams.callerPaintsOpacity)) {
     opacity = 1.0f;
   }
   MOZ_ASSERT(!nsSVGUtils::CanOptimizeOpacity(frame) || !aParams.callerPaintsOpacity,
              "How can we be optimizing the opacity into the svg as well as having the caller paint it?");
@@ -704,42 +710,43 @@ nsSVGIntegrationUtils::PaintFramesWithEf
                                  ? maskFrames.Length() == 1 && maskFrames[0]
                                  : maskFrames.Length() > 0;
 
   // These are used if we require a temporary surface for a custom blend mode.
   RefPtr<gfxContext> target = &aParams.ctx;
   IntPoint targetOffset;
 
   bool complexEffects = false;
+  DrawResult result = DrawResult::SUCCESS;
   /* Check if we need to do additional operations on this child's
    * rendering, which necessitates rendering into another surface. */
   if (opacity != 1.0f ||  (clipPathFrame && !isTrivialClip)
       || frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL
       || shouldGenerateMaskLayer) {
     complexEffects = true;
 
     context.Save();
     nsRect clipRect =
       frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
     context.Clip(NSRectToSnappedRect(clipRect,
                                   frame->PresContext()->AppUnitsPerDevPixel(),
                                   *drawTarget));
     Matrix maskTransform;
     RefPtr<SourceSurface> maskSurface;
-
     if (shouldGenerateMaskLayer) {
-      GenerateMaskSurface(aParams, opacity, firstFrame->StyleContext(),
-                          maskFrames, offsetToUserSpace,
-                          maskTransform, maskSurface);
+      result = GenerateMaskSurface(aParams, opacity,
+                                  firstFrame->StyleContext(),
+                                  maskFrames, offsetToUserSpace,
+                                  maskTransform, maskSurface);
     }
 
     if (shouldGenerateMaskLayer && !maskSurface) {
       // Entire surface is clipped out.
       context.Restore();
-      return;
+      return result;
     }
 
     if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
       // Create a temporary context to draw to so we can blend it back with
       // another operator.
       gfxRect clipRect;
       {
         gfxContextMatrixAutoSaveRestore matRestore(&context);
@@ -748,28 +755,30 @@ nsSVGIntegrationUtils::PaintFramesWithEf
         clipRect = context.GetClipExtents();
       }
 
       IntRect drawRect = RoundedOut(ToRect(clipRect));
 
       RefPtr<DrawTarget> targetDT = context.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8);
       if (!targetDT || !targetDT->IsValid()) {
         context.Restore();
-        return;
+        return result;
       }
       target = gfxContext::CreateOrNull(targetDT);
       MOZ_ASSERT(target); // already checked the draw target above
       target->SetMatrix(context.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft()));
       targetOffset = drawRect.TopLeft();
     }
 
     if (clipPathFrame && !isTrivialClip) {
       Matrix clippedMaskTransform;
-      RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix,
-                                                                         &clippedMaskTransform, maskSurface, maskTransform);
+      RefPtr<SourceSurface> clipMaskSurface =
+        clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix,
+                                   &clippedMaskTransform, maskSurface,
+                                   maskTransform, &result);
 
       if (clipMaskSurface) {
         maskSurface = clipMaskSurface;
         maskTransform = clippedMaskTransform;
       }
     }
 
     if (opacity != 1.0f || shouldGenerateMaskLayer ||
@@ -810,17 +819,17 @@ nsSVGIntegrationUtils::PaintFramesWithEf
 
   if ((clipPathFrame && isTrivialClip) ||
       (!clipPathFrame && svgReset->HasClipPath())) {
     context.Restore();
   }
 
   /* No more effects, we're done. */
   if (!complexEffects) {
-    return;
+    return result;
   }
 
   if (opacity != 1.0f || shouldGenerateMaskLayer ||
       (clipPathFrame && !isTrivialClip)) {
     target->PopGroupAndBlend();
   }
 
   if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
@@ -830,16 +839,17 @@ nsSVGIntegrationUtils::PaintFramesWithEf
 
     context.SetMatrix(gfxMatrix()); // This will be restored right after.
     RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y));
     context.SetPattern(pattern);
     context.Paint();
   }
 
   context.Restore();
+  return result;
 }
 
 gfxMatrix
 nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame)
 {
   int32_t appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
   float devPxPerCSSPx =
     1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
--- a/layout/svg/nsSVGIntegrationUtils.h
+++ b/layout/svg/nsSVGIntegrationUtils.h
@@ -32,16 +32,17 @@ struct nsSize;
 
 /**
  * Integration of SVG effects (clipPath clipping, masking and filters) into
  * regular display list based painting and hit-testing.
  */
 class nsSVGIntegrationUtils final
 {
   typedef mozilla::gfx::DrawTarget DrawTarget;
+  typedef mozilla::image::DrawResult DrawResult;
 
 public:
   /**
    * Returns true if SVG effects are currently applied to this frame.
    */
   static bool
   UsingEffectsForFrame(const nsIFrame* aFrame);
 
@@ -140,17 +141,17 @@ public:
         borderArea(aBorderArea), builder(aBuilder),
         layerManager(aLayerManager), callerPaintsOpacity(aCallerPaintsOpacity)
     { }
   };
 
   /**
    * Paint non-SVG frame with SVG effects.
    */
-  static void
+  static DrawResult
   PaintFramesWithEffects(const PaintFramesParams& aParams);
 
   /**
    * SVG frames expect to paint in SVG user units, which are equal to CSS px
    * units. This method provides a transform matrix to multiply onto a
    * gfxContext's current transform to convert the context's current units from
    * its usual dev pixels to SVG user units/CSS px to keep the SVG code happy.
    */
--- a/layout/svg/nsSVGMarkerFrame.cpp
+++ b/layout/svg/nsSVGMarkerFrame.cpp
@@ -149,22 +149,22 @@ nsSVGMarkerFrame::PaintMark(gfxContext& 
     nsSVGUtils::SetClipRect(&aContext, markTM, clipRect);
   }
 
 
   nsIFrame* kid = GetAnonymousChildFrame(this);
   nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
   // The CTM of each frame referencing us may be different.
   SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
-  nsSVGUtils::PaintFrameWithEffects(kid, aContext, markTM);
+  DrawResult result = nsSVGUtils::PaintFrameWithEffects(kid, aContext, markTM);
 
   if (StyleDisplay()->IsScrollableOverflow())
     aContext.Restore();
 
-  return NS_OK;
+  return (result == DrawResult::SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 SVGBBox
 nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix &aToBBoxUserspace,
                                           uint32_t aFlags,
                                           nsSVGPathGeometryFrame *aMarkedFrame,
                                           const nsSVGMark *aMark,
                                           float aStrokeWidth)
--- a/layout/svg/nsSVGMaskFrame.cpp
+++ b/layout/svg/nsSVGMaskFrame.cpp
@@ -261,17 +261,20 @@ nsSVGMaskFrame::GetMaskForMaskedFrame(gf
     if (SVGFrame) {
       SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
     }
     gfxMatrix m = mMatrixForChildren;
     if (kid->GetContent()->IsSVGElement()) {
       m = static_cast<nsSVGElement*>(kid->GetContent())->
             PrependLocalTransformsTo(m);
     }
-    nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m);
+    DrawResult result = nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m);
+    if (result != DrawResult::SUCCESS) {
+      return nullptr;
+    }
   }
 
   RefPtr<SourceSurface> maskSnapshot = maskDT->Snapshot();
   if (!maskSnapshot) {
     return nullptr;
   }
   RefPtr<DataSourceSurface> maskSurface = maskSnapshot->GetDataSurface();
   DataSourceSurface::MappedSurface map;
--- a/layout/svg/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/nsSVGOuterSVGFrame.cpp
@@ -554,16 +554,21 @@ public:
                        nsTArray<nsIFrame*> *aOutFrames) override;
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) override;
 
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion* aInvalidRegion) override;
 
+  nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
+  {
+    return new nsDisplayItemGenericImageGeometry(this, aBuilder);
+  }
+
   NS_DISPLAY_DECL_NAME("SVGOuterSVG", TYPE_SVG_OUTER_SVG)
 };
 
 void
 nsDisplayOuterSVG::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                            HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
 {
   nsSVGOuterSVGFrame *outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
@@ -619,17 +624,20 @@ nsDisplayOuterSVG::Paint(nsDisplayListBu
   gfxPoint devPixelOffset =
     nsLayoutUtils::PointToGfxPoint(viewportRect.TopLeft(), appUnitsPerDevPixel);
 
   aContext->ThebesContext()->Save();
   // We include the offset of our frame and a scale from device pixels to user
   // units (i.e. CSS px) in the matrix that we pass to our children):
   gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(mFrame) *
                    gfxMatrix::Translation(devPixelOffset);
-  nsSVGUtils::PaintFrameWithEffects(mFrame, *aContext->ThebesContext(), tm, &contentAreaDirtyRect);
+  DrawResult result =
+    nsSVGUtils::PaintFrameWithEffects(mFrame, *aContext->ThebesContext(), tm,
+                                      &contentAreaDirtyRect);
+  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
   aContext->ThebesContext()->Restore();
 
 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
   PRTime end = PR_Now();
   printf("SVG Paint Timing: %f ms\n", (end-start)/1000.0);
 #endif
 }
 
@@ -654,16 +662,25 @@ nsDisplayOuterSVG::ComputeInvalidationRe
   frame->InvalidateSVG(frame->FindInvalidatedForeignObjectFrameChildren(frame));
 
   nsRegion result = frame->GetInvalidRegion();
   result.MoveBy(ToReferenceFrame());
   frame->ClearInvalidRegion();
 
   nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
   aInvalidRegion->Or(*aInvalidRegion, result);
+
+  auto geometry =
+    static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
+
+  if (aBuilder->ShouldSyncDecodeImages() &&
+    geometry->ShouldInvalidateToSyncDecodeImages()) {
+    bool snap;
+    aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
+  }
 }
 
 nsresult
 nsSVGOuterSVGFrame::AttributeChanged(int32_t  aNameSpaceID,
                                      nsIAtom* aAttribute,
                                      int32_t  aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
@@ -818,17 +835,17 @@ nsSVGOuterSVGFrame::NotifyViewportOrTran
   }
 
   nsSVGUtils::NotifyChildrenOfSVGChange(PrincipalChildList().FirstChild(), aFlags);
 }
 
 //----------------------------------------------------------------------
 // nsISVGChildFrame methods:
 
-nsresult
+DrawResult
 nsSVGOuterSVGFrame::PaintSVG(gfxContext& aContext,
                              const gfxMatrix& aTransform,
                              const nsIntRect* aDirtyRect)
 {
   NS_ASSERTION(PrincipalChildList().FirstChild()->GetType() ==
                  nsGkAtoms::svgOuterSVGAnonChildFrame &&
                !PrincipalChildList().FirstChild()->GetNextSibling(),
                "We should have a single, anonymous, child");
--- a/layout/svg/nsSVGOuterSVGFrame.h
+++ b/layout/svg/nsSVGOuterSVGFrame.h
@@ -109,19 +109,19 @@ public:
     // themselves.
     return PrincipalChildList().FirstChild()->IsSVGTransformed();
   }
 
   // nsISVGSVGFrame interface:
   virtual void NotifyViewportOrTransformChanged(uint32_t aFlags) override;
 
   // nsISVGChildFrame methods:
-  virtual nsresult PaintSVG(gfxContext& aContext,
-                            const gfxMatrix& aTransform,
-                            const nsIntRect* aDirtyRect = nullptr) override;
+  virtual DrawResult PaintSVG(gfxContext& aContext,
+                              const gfxMatrix& aTransform,
+                              const nsIntRect* aDirtyRect = nullptr) override;
   virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                       uint32_t aFlags) override;
 
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM() override;
 
   /* Methods to allow descendant nsSVGForeignObjectFrame frames to register and
    * unregister themselves with their nearest nsSVGOuterSVGFrame ancestor. This
--- a/layout/svg/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/nsSVGPathGeometryFrame.cpp
@@ -26,16 +26,17 @@
 #include "nsSVGUtils.h"
 #include "mozilla/ArrayUtils.h"
 #include "SVGAnimatedTransformList.h"
 #include "SVGContentUtils.h"
 #include "SVGGraphicsElement.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
+using namespace mozilla::image;
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsIFrame*
 NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell,
                            nsStyleContext* aContext)
 {
@@ -64,23 +65,32 @@ public:
     MOZ_COUNT_CTOR(nsDisplaySVGPathGeometry);
     MOZ_ASSERT(aFrame, "Must have a frame!");
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplaySVGPathGeometry() {
     MOZ_COUNT_DTOR(nsDisplaySVGPathGeometry);
   }
 #endif
- 
+
   NS_DISPLAY_DECL_NAME("nsDisplaySVGPathGeometry", TYPE_SVG_PATH_GEOMETRY)
 
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override;
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) override;
+
+  nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
+  {
+    return new nsDisplayItemGenericImageGeometry(this, aBuilder);
+  }
+
+  void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
+                                 const nsDisplayItemGeometry* aGeometry,
+                                 nsRegion *aInvalidRegion) override;
 };
 
 void
 nsDisplaySVGPathGeometry::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                                   HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
 {
   nsSVGPathGeometryFrame *frame = static_cast<nsSVGPathGeometryFrame*>(mFrame);
   nsPoint pointRelativeToReferenceFrame = aRect.Center();
@@ -106,17 +116,38 @@ nsDisplaySVGPathGeometry::Paint(nsDispla
   // here.
   nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
 
   gfxPoint devPixelOffset =
     nsLayoutUtils::PointToGfxPoint(offset, appUnitsPerDevPixel);
 
   gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(mFrame) *
                    gfxMatrix::Translation(devPixelOffset);
-  static_cast<nsSVGPathGeometryFrame*>(mFrame)->PaintSVG(*aCtx->ThebesContext(), tm);
+  DrawResult result =
+    static_cast<nsSVGPathGeometryFrame*>(mFrame)->PaintSVG(*aCtx->ThebesContext(), tm);
+
+  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
+}
+
+void
+nsDisplaySVGPathGeometry::ComputeInvalidationRegion(
+  nsDisplayListBuilder* aBuilder,
+  const nsDisplayItemGeometry* aGeometry,
+  nsRegion* aInvalidRegion)
+{
+  auto geometry =
+    static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
+
+  if (aBuilder->ShouldSyncDecodeImages() &&
+      geometry->ShouldInvalidateToSyncDecodeImages()) {
+    bool snap;
+    aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
+  }
+
+  nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
 }
 
 //----------------------------------------------------------------------
 // nsIFrame methods
 
 void
 nsSVGPathGeometryFrame::Init(nsIContent*       aContent,
                              nsContainerFrame* aParent,
@@ -237,29 +268,29 @@ nsSVGPathGeometryFrame::BuildDisplayList
   }
   aLists.Content()->AppendNewToTop(
     new (aBuilder) nsDisplaySVGPathGeometry(aBuilder, this));
 }
 
 //----------------------------------------------------------------------
 // nsISVGChildFrame methods
 
-nsresult
+DrawResult
 nsSVGPathGeometryFrame::PaintSVG(gfxContext& aContext,
                                  const gfxMatrix& aTransform,
                                  const nsIntRect* aDirtyRect)
 {
   if (!StyleVisibility()->IsVisible())
-    return NS_OK;
+    return DrawResult::SUCCESS;
 
   // Matrix to the geometry's user space:
   gfxMatrix newMatrix =
     aContext.CurrentMatrix().PreMultiply(aTransform).NudgeToIntegers();
   if (newMatrix.IsSingular()) {
-    return NS_OK;
+    return DrawResult::BAD_ARGS;
   }
 
   uint32_t paintOrder = StyleSVG()->mPaintOrder;
   if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
     Render(&aContext, eRenderFill | eRenderStroke, newMatrix);
     PaintMarkers(aContext, aTransform);
   } else {
     while (paintOrder) {
@@ -275,17 +306,17 @@ nsSVGPathGeometryFrame::PaintSVG(gfxCont
         case NS_STYLE_PAINT_ORDER_MARKERS:
           PaintMarkers(aContext, aTransform);
           break;
       }
       paintOrder >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
     }
   }
 
-  return NS_OK;
+  return DrawResult::SUCCESS;
 }
 
 nsIFrame*
 nsSVGPathGeometryFrame::GetFrameForPoint(const gfxPoint& aPoint)
 {
   FillRule fillRule;
   uint16_t hitTestFlags;
   if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
--- a/layout/svg/nsSVGPathGeometryFrame.h
+++ b/layout/svg/nsSVGPathGeometryFrame.h
@@ -90,19 +90,19 @@ public:
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) override;
 
   // nsSVGPathGeometryFrame methods
   gfxMatrix GetCanvasTM();
 protected:
   // nsISVGChildFrame interface:
-  virtual nsresult PaintSVG(gfxContext& aContext,
-                            const gfxMatrix& aTransform,
-                            const nsIntRect* aDirtyRect = nullptr) override;
+  virtual DrawResult PaintSVG(gfxContext& aContext,
+                              const gfxMatrix& aTransform,
+                              const nsIntRect* aDirtyRect = nullptr) override;
   virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
   virtual nsRect GetCoveredRegion() override;
   virtual void ReflowSVG() override;
   virtual void NotifySVGChanged(uint32_t aFlags) override;
   virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                       uint32_t aFlags) override;
   virtual bool IsDisplayContainer() override { return false; }
 
--- a/layout/svg/nsSVGPatternFrame.cpp
+++ b/layout/svg/nsSVGPatternFrame.cpp
@@ -405,17 +405,20 @@ nsSVGPatternFrame::PaintPattern(const Dr
       if (SVGFrame) {
         SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
       }
       gfxMatrix tm = *(patternWithChildren->mCTM);
       if (kid->GetContent()->IsSVGElement()) {
         tm = static_cast<nsSVGElement*>(kid->GetContent())->
                PrependLocalTransformsTo(tm, eUserSpaceToParent);
       }
-      nsSVGUtils::PaintFrameWithEffects(kid, *gfx, tm);
+      DrawResult result = nsSVGUtils::PaintFrameWithEffects(kid, *gfx, tm);
+      if (result != DrawResult::SUCCESS) {
+        return nullptr;
+      }
     }
     patternWithChildren->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
   }
 
   patternWithChildren->mSource = nullptr;
 
   if (aGraphicOpacity != 1.0f) {
     gfx->PopGroupAndBlend();
--- a/layout/svg/nsSVGSwitchFrame.cpp
+++ b/layout/svg/nsSVGSwitchFrame.cpp
@@ -43,19 +43,19 @@ public:
   }
 #endif
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) override;
 
   // nsISVGChildFrame interface:
-  virtual nsresult PaintSVG(gfxContext& aContext,
-                            const gfxMatrix& aTransform,
-                            const nsIntRect* aDirtyRect = nullptr) override;
+  virtual DrawResult PaintSVG(gfxContext& aContext,
+                              const gfxMatrix& aTransform,
+                              const nsIntRect* aDirtyRect = nullptr) override;
   nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
   nsRect GetCoveredRegion() override;
   virtual void ReflowSVG() override;
   virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                       uint32_t aFlags) override;
 
 private:
   nsIFrame *GetActiveChildFrame();
@@ -97,39 +97,40 @@ nsSVGSwitchFrame::BuildDisplayList(nsDis
                                    const nsDisplayListSet& aLists)
 {
   nsIFrame* kid = GetActiveChildFrame();
   if (kid) {
     BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
   }
 }
 
-nsresult
+DrawResult
 nsSVGSwitchFrame::PaintSVG(gfxContext& aContext,
                            const gfxMatrix& aTransform,
                            const nsIntRect* aDirtyRect)
 {
   NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
                (mState & NS_FRAME_IS_NONDISPLAY),
                "If display lists are enabled, only painting of non-display "
                "SVG should take this code path");
 
   if (StyleEffects()->mOpacity == 0.0)
-    return NS_OK;
+    return DrawResult::SUCCESS;
 
+  DrawResult result = DrawResult::SUCCESS;
   nsIFrame *kid = GetActiveChildFrame();
   if (kid) {
     gfxMatrix tm = aTransform;
     if (kid->GetContent()->IsSVGElement()) {
       tm = static_cast<nsSVGElement*>(kid->GetContent())->
              PrependLocalTransformsTo(tm, eUserSpaceToParent);
     }
-    nsSVGUtils::PaintFrameWithEffects(kid, aContext, tm, aDirtyRect);
+    result = nsSVGUtils::PaintFrameWithEffects(kid, aContext, tm, aDirtyRect);
   }
-  return NS_OK;
+  return result;
 }
 
 
 nsIFrame*
 nsSVGSwitchFrame::GetFrameForPoint(const gfxPoint& aPoint)
 {
   NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
                (mState & NS_FRAME_IS_NONDISPLAY),
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -452,69 +452,69 @@ nsSVGUtils::NotifyChildrenOfSVGChange(ns
   }
 }
 
 // ************************************************************
 
 class SVGPaintCallback : public nsSVGFilterPaintCallback
 {
 public:
-  virtual void Paint(gfxContext& aContext, nsIFrame *aTarget,
-                     const gfxMatrix& aTransform,
-                     const nsIntRect* aDirtyRect) override
+  virtual DrawResult Paint(gfxContext& aContext, nsIFrame *aTarget,
+                           const gfxMatrix& aTransform,
+                           const nsIntRect* aDirtyRect) override
   {
     nsISVGChildFrame *svgChildFrame = do_QueryFrame(aTarget);
     NS_ASSERTION(svgChildFrame, "Expected SVG frame here");
 
     nsIntRect* dirtyRect = nullptr;
     nsIntRect tmpDirtyRect;
 
     // aDirtyRect is in user-space pixels, we need to convert to
     // outer-SVG-frame-relative device pixels.
     if (aDirtyRect) {
       gfxMatrix userToDeviceSpace = aTransform;
       if (userToDeviceSpace.IsSingular()) {
-        return;
+        return DrawResult::SUCCESS;
       }
       gfxRect dirtyBounds = userToDeviceSpace.TransformBounds(
         gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
       dirtyBounds.RoundOut();
       if (gfxUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect)) {
         dirtyRect = &tmpDirtyRect;
       }
     }
 
-    svgChildFrame->PaintSVG(aContext, aTransform, dirtyRect);
+    return svgChildFrame->PaintSVG(aContext, aTransform, dirtyRect);
   }
 };
 
-void
+DrawResult
 nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame,
                                   gfxContext& aContext,
                                   const gfxMatrix& aTransform,
                                   const nsIntRect *aDirtyRect)
 {
   NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
                (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
                aFrame->PresContext()->IsGlyph(),
                "If display lists are enabled, only painting of non-display "
                "SVG should take this code path");
 
   nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
   if (!svgChildFrame)
-    return;
+    return DrawResult::SUCCESS;
 
   float opacity = aFrame->StyleEffects()->mOpacity;
   if (opacity == 0.0f)
-    return;
+    return DrawResult::SUCCESS;
 
   const nsIContent* content = aFrame->GetContent();
   if (content->IsSVGElement() &&
       !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
-    return;
+    return DrawResult::SUCCESS;
   }
 
   /* Properties are added lazily and may have been removed by a restyle,
      so make sure all applicable ones are set again. */
 
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(aFrame);
 
@@ -536,26 +536,26 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
     int32_t appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel();
     gfxMatrix tm = aTransform;
     if (aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
       gfx::Matrix childrenOnlyTM;
       if (static_cast<nsSVGContainerFrame*>(aFrame)->
             HasChildrenOnlyTransform(&childrenOnlyTM)) {
         // Undo the children-only transform:
         if (!childrenOnlyTM.Invert()) {
-          return;
+          return DrawResult::SUCCESS;
         }
         tm = ThebesMatrix(childrenOnlyTM) * tm;
       }
     }
     nsIntRect bounds = TransformFrameRectToOuterSVG(overflowRect,
                          tm, aFrame->PresContext()).
                            ToOutsidePixels(appUnitsPerDevPx);
     if (!aDirtyRect->Intersects(bounds)) {
-      return;
+      return DrawResult::SUCCESS;
     }
   }
 
   /* SVG defines the following rendering model:
    *
    *  1. Render fill
    *  2. Render stroke
    *  3. Render markers
@@ -578,17 +578,17 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
 
   nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
   nsSVGMaskFrame *maskFrame = effectProperties.GetFirstMaskFrame(&isOK);
 
   bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
 
   if (!isOK) {
     // Some resource is invalid. We shouldn't paint anything.
-    return;
+    return DrawResult::SUCCESS;
   }
 
   // These are used if we require a temporary surface for a custom blend mode.
   RefPtr<gfxContext> target = &aContext;
   IntPoint targetOffset;
 
   /* Check if we need to do additional operations on this child's
    * rendering, which necessitates rendering into another surface. */
@@ -599,17 +599,17 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
     Matrix maskTransform;
     RefPtr<SourceSurface> maskSurface =
       maskFrame ? maskFrame->GetMaskForMaskedFrame(&aContext,
                                                     aFrame, aTransform, opacity, &maskTransform)
                 : nullptr;
 
     if (maskFrame && !maskSurface) {
       // Entire surface is clipped out.
-      return;
+      return DrawResult::SUCCESS;
     }
 
     aContext.Save();
     if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
       // aFrame has a valid visual overflow rect, so clip to it before calling
       // PushGroup() to minimize the size of the surfaces we'll composite:
       gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&aContext);
       aContext.Multiply(aTransform);
@@ -637,17 +637,17 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
       }
 
       IntRect drawRect = RoundedOut(ToRect(clipRect));
 
       RefPtr<DrawTarget> targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8);
       target = gfxContext::CreateOrNull(targetDT);
       if (!target) {
         gfxDevCrash(LogReason::InvalidContext) << "SVGPaintWithEffects context problem " << gfx::hexa(targetDT);
-        return;
+        return DrawResult::TEMPORARY_ERROR;
       }
       target->SetMatrix(aContext.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft()));
       targetOffset = drawRect.TopLeft();
     }
 
     if (clipPathFrame && !isTrivialClip) {
       Matrix clippedMaskTransform;
       RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(aContext, aFrame, aTransform,
@@ -667,26 +667,28 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
   /* If this frame has only a trivial clipPath, set up cairo's clipping now so
    * we can just do normal painting and get it clipped appropriately.
    */
   if (clipPathFrame && isTrivialClip) {
     aContext.Save();
     clipPathFrame->ApplyClipPath(aContext, aFrame, aTransform);
   }
 
+  DrawResult result = DrawResult::SUCCESS;
+
   /* Paint the child */
   if (effectProperties.HasValidFilter()) {
     nsRegion* dirtyRegion = nullptr;
     nsRegion tmpDirtyRegion;
     if (aDirtyRect) {
       // aDirtyRect is in outer-<svg> device pixels, but the filter code needs
       // it in frame space.
       gfxMatrix userToDeviceSpace = GetUserToCanvasTM(aFrame);
       if (userToDeviceSpace.IsSingular()) {
-        return;
+        return DrawResult::SUCCESS;
       }
       gfxMatrix deviceToUserSpace = userToDeviceSpace;
       deviceToUserSpace.Invert();
       gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(
                               gfxRect(aDirtyRect->x, aDirtyRect->y,
                                       aDirtyRect->width, aDirtyRect->height));
       tmpDirtyRegion =
         nsLayoutUtils::RoundGfxRectToAppRect(
@@ -694,43 +696,44 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
         aFrame->GetPosition();
       dirtyRegion = &tmpDirtyRegion;
     }
     SVGPaintCallback paintCallback;
     nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(),
                                          aTransform, &paintCallback,
                                          dirtyRegion);
   } else {
-    svgChildFrame->PaintSVG(*target, aTransform, aDirtyRect);
+    result = svgChildFrame->PaintSVG(*target, aTransform, aDirtyRect);
   }
 
   if (clipPathFrame && isTrivialClip) {
     aContext.Restore();
   }
 
   /* No more effects, we're done. */
   if (!complexEffects)
-    return;
-  
+    return result;
+
   if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) {
     target->PopGroupAndBlend();
   }
 
   if (aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
     RefPtr<DrawTarget> targetDT = target->GetDrawTarget();
     target = nullptr;
     RefPtr<SourceSurface> targetSurf = targetDT->Snapshot();
 
     aContext.SetMatrix(gfxMatrix()); // This will be restored right after.
     RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y));
     aContext.SetPattern(pattern);
     aContext.Paint();
   }
 
   aContext.Restore();
+  return result;
 }
 
 bool
 nsSVGUtils::HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint)
 {
   nsSVGEffects::EffectProperties props =
     nsSVGEffects::GetEffectProperties(aFrame);
   if (!props.mClipPath) {
@@ -1780,18 +1783,18 @@ nsSVGUtils::PaintSVGGlyph(Element* aElem
                                          aContextPaint, nullptr);
   gfxMatrix m;
   if (frame->GetContent()->IsSVGElement()) {
     // PaintSVG() expects the passed transform to be the transform to its own
     // SVG user space, so we need to account for any 'transform' attribute:
     m = static_cast<nsSVGElement*>(frame->GetContent())->
           PrependLocalTransformsTo(gfxMatrix(), eUserSpaceToParent);
   }
-  nsresult rv = svgFrame->PaintSVG(*aContext, m);
-  return NS_SUCCEEDED(rv);
+  DrawResult result = svgFrame->PaintSVG(*aContext, m);
+  return (result == DrawResult::SUCCESS);
 }
 
 bool
 nsSVGUtils::GetSVGGlyphExtents(Element* aElement,
                                const gfxMatrix& aSVGToAppSpace,
                                gfxRect* aResult)
 {
   nsIFrame* frame = aElement->GetPrimaryFrame();
--- a/layout/svg/nsSVGUtils.h
+++ b/layout/svg/nsSVGUtils.h
@@ -180,16 +180,17 @@ class nsSVGUtils
 public:
   typedef mozilla::dom::Element Element;
   typedef mozilla::gfx::AntialiasMode AntialiasMode;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::FillRule FillRule;
   typedef mozilla::gfx::GeneralPattern GeneralPattern;
   typedef mozilla::gfx::Size Size;
   typedef mozilla::SVGTextContextPaint SVGTextContextPaint;
+  typedef mozilla::image::DrawResult DrawResult;
 
   static void Init();
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(ObjectBoundingBoxProperty, gfxRect)
 
   /**
    * Gets the nearest nsSVGInnerSVGFrame or nsSVGOuterSVGFrame frame. aFrame
    * must be an SVG frame. If aFrame is of type nsGkAtoms::svgOuterSVGFrame,
@@ -277,17 +278,17 @@ public:
    * @param aRect gets a rectangle in app units
    * @return the outer SVG frame which aRect is relative to
    */
   static nsIFrame*
   GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect);
 
   /* Paint SVG frame with SVG effects - aDirtyRect is the area being
    * redrawn, in device pixel coordinates relative to the outer svg */
-  static void
+  static DrawResult
   PaintFrameWithEffects(nsIFrame *aFrame,
                         gfxContext& aContext,
                         const gfxMatrix& aTransform,
                         const nsIntRect *aDirtyRect = nullptr);
 
   /* Hit testing - check if point hits the clipPath of indicated
    * frame.  Returns true if no clipPath set. */
   static bool
--- a/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
@@ -7,30 +7,31 @@ package org.mozilla.gecko;
 
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.customtabs.CustomTabsIntent;
 
 import org.mozilla.gecko.customtabs.CustomTabsActivity;
 import org.mozilla.gecko.db.BrowserContract;
+import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.tabqueue.TabQueueHelper;
 import org.mozilla.gecko.tabqueue.TabQueueService;
 
 /**
  * Activity that receives incoming Intents and dispatches them to the appropriate activities (e.g. browser, custom tabs, web app).
  */
 public class LauncherActivity extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         GeckoAppShell.ensureCrashHandling();
 
-        if (AppConstants.MOZ_ANDROID_CUSTOM_TABS && isCustomTabsIntent()) {
+        if (AppConstants.MOZ_ANDROID_CUSTOM_TABS && isCustomTabsIntent() && isCustomTabsEnabled()) {
             dispatchCustomTabsIntent();
         } else if (isViewIntentWithURL()) {
             dispatchViewIntent();
         } else {
             dispatchNormalIntent();
         }
 
         finish();
@@ -79,9 +80,13 @@ public class LauncherActivity extends Ac
         return Intent.ACTION_VIEW.equals(intent.getAction())
                 && intent.getDataString() != null;
     }
 
     private boolean isCustomTabsIntent() {
         return isViewIntentWithURL()
                 && getIntent().hasExtra(CustomTabsIntent.EXTRA_SESSION);
     }
+
+    private boolean isCustomTabsEnabled() {
+        return GeckoSharedPrefs.forApp(this).getBoolean(GeckoPreferences.PREFS_CUSTOM_TABS, false);
+    }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java
@@ -795,28 +795,26 @@ public class BrowserProvider extends Sha
                 " LEFT OUTER JOIN " +
                 " (SELECT position FROM numbers WHERE position NOT IN (SELECT " + Bookmarks.POSITION + " " + pinnedSitesFromClause + ")) AS free_ids" +
                 " ON numbers.position > free_ids.position" +
                 " GROUP BY numbers.position" +
                 " ORDER BY numbers.position ASC" +
                 " LIMIT " + suggestedGridLimit;
 
         // Filter out: unvisited pages (history_id == -1) pinned (and other special) sites, deleted sites,
-        // pages which weren't visited locally, and about: pages.
+        // and about: pages.
         final String ignoreForTopSitesWhereClause =
                 "(" + Combined.HISTORY_ID + " IS NOT -1)" +
                 " AND " +
                 Combined.URL + " NOT IN (SELECT " +
                 Bookmarks.URL + " FROM bookmarks WHERE " +
                 DBUtils.qualifyColumn("bookmarks", Bookmarks.PARENT) + " < " + Bookmarks.FIXED_ROOT_ID + " AND " +
                 DBUtils.qualifyColumn("bookmarks", Bookmarks.IS_DELETED) + " == 0)" +
                 " AND " +
-                "(" + Combined.URL + " NOT LIKE ?)" +
-                " AND " +
-                "(" + Combined.LOCAL_VISITS_COUNT + " > 0)";
+                "(" + Combined.URL + " NOT LIKE ?)";
 
         final String[] ignoreForTopSitesArgs = new String[] {
                 AboutPages.URL_FILTER
         };
 
         // Stuff the suggested sites into SQL: this allows us to filter pinned and topsites out of the suggested
         // sites list as part of the final query (as opposed to walking cursors in java)
         final SuggestedSites suggestedSites = GeckoProfile.get(getContext(), uri.getQueryParameter(BrowserContract.PARAM_PROFILE)).getDB().getSuggestedSites();
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
@@ -159,16 +159,17 @@ OnSharedPreferenceChangeListener
     private static final String PREFS_FAQ_LINK = NON_PREF_PREFIX + "faq.link";
     private static final String PREFS_FEEDBACK_LINK = NON_PREF_PREFIX + "feedback.link";
     public static final String PREFS_NOTIFICATIONS_CONTENT = NON_PREF_PREFIX + "notifications.content";
     public static final String PREFS_NOTIFICATIONS_CONTENT_LEARN_MORE = NON_PREF_PREFIX + "notifications.content.learn_more";
     public static final String PREFS_NOTIFICATIONS_WHATS_NEW = NON_PREF_PREFIX + "notifications.whats_new";
     public static final String PREFS_APP_UPDATE_LAST_BUILD_ID = "app.update.last_build_id";
     public static final String PREFS_READ_PARTNER_CUSTOMIZATIONS_PROVIDER = NON_PREF_PREFIX + "distribution.read_partner_customizations_provider";
     public static final String PREFS_READ_PARTNER_BOOKMARKS_PROVIDER = NON_PREF_PREFIX + "distribution.read_partner_bookmarks_provider";
+    public static final String PREFS_CUSTOM_TABS = NON_PREF_PREFIX + "customtabs";
 
     private static final String ACTION_STUMBLER_UPLOAD_PREF = "STUMBLER_PREF";
 
 
     // This isn't a Gecko pref, even if it looks like one.
     private static final String PREFS_BROWSER_LOCALE = "locale";
 
     public static final String PREFS_RESTORE_SESSION = NON_PREF_PREFIX + "restoreSession3";
@@ -876,16 +877,20 @@ OnSharedPreferenceChangeListener
                     }
                 } else if (PREFS_NOTIFICATIONS_CONTENT.equals(key) ||
                         PREFS_NOTIFICATIONS_CONTENT_LEARN_MORE.equals(key)) {
                     if (!FeedService.isInExperiment(this)) {
                         preferences.removePreference(pref);
                         i--;
                         continue;
                     }
+                } else if (PREFS_CUSTOM_TABS.equals(key) && !AppConstants.MOZ_ANDROID_CUSTOM_TABS) {
+                    preferences.removePreference(pref);
+                    i--;
+                    continue;
                 }
 
                 // Some Preference UI elements are not actually preferences,
                 // but they require a key to work correctly. For example,
                 // "Clear private data" requires a key for its state to be
                 // saved when the orientation changes. It uses the
                 // "android.not_a_preference.privacy.clear" key - which doesn't
                 // exist in Gecko - to satisfy this requirement.
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -267,16 +267,22 @@
 
 <!ENTITY pref_tracking_protection_enabled "Enabled">
 <!ENTITY pref_tracking_protection_enabled_pb "Enabled in Private Browsing">
 <!ENTITY pref_tracking_protection_disabled "Disabled">
 
 <!ENTITY pref_whats_new_notification "What\'s new in &brandShortName;">
 <!ENTITY pref_whats_new_notification_summary "Learn about new features after an update">
 
+<!-- Custom Tabs is an Android API for allowing third-party apps to open URLs in a customized UI.
+     Instead of switching to the browser it appears as if the user stays in the third-party app.
+     For more see: https://developer.chrome.com/multidevice/android/customtabs -->
+<!ENTITY pref_custom_tabs "Custom Tabs">
+<!ENTITY pref_custom_tabs_summary "Allow third-party apps to open URLs with a customized look and feel. ">
+
 <!ENTITY tracking_protection_prompt_title "Now with Tracking Protection">
 <!ENTITY tracking_protection_prompt_text "Actively block tracking elements so you don\'t have to worry.">
 <!ENTITY tracking_protection_prompt_tip_text "Visit Privacy settings to learn more">
 <!ENTITY tracking_protection_prompt_action_button "Got it!">
 
 <!ENTITY tab_queue_toast_message3 "Tab saved in &brandShortName;">
 <!ENTITY tab_queue_toast_action "Open now">
 <!ENTITY tab_queue_prompt_title "Opening multiple links?">
--- a/mobile/android/base/resources/xml/preferences_advanced.xml
+++ b/mobile/android/base/resources/xml/preferences_advanced.xml
@@ -33,16 +33,21 @@
                     android:persistent="true" />
 
     <ListPreference android:key="browser.menu.showCharacterEncoding"
                     android:title="@string/pref_char_encoding"
                     android:entries="@array/pref_char_encoding_entries"
                     android:entryValues="@array/pref_char_encoding_values"
                     android:persistent="false" />
 
+    <SwitchPreference android:key="android.not_a_preference.customtabs"
+                      android:title="@string/pref_custom_tabs"
+                      android:summary="@string/pref_custom_tabs_summary"
+                      android:defaultValue="false" />
+
     <PreferenceCategory android:title="@string/pref_category_data_saver">
 
         <ListPreference android:key="browser.image_blocking"
                         android:title="@string/pref_tap_to_load_images_title2"
                         android:entries="@array/pref_browser_image_blocking_entries"
                         android:entryValues="@array/pref_browser_image_blocking_values"
                         android:persistent="false" />
 
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -229,16 +229,19 @@
 
   <string name="pref_tracking_protection_enabled">&pref_tracking_protection_enabled;</string>
   <string name="pref_tracking_protection_enabled_pb">&pref_tracking_protection_enabled_pb;</string>
   <string name="pref_tracking_protection_disabled">&pref_tracking_protection_disabled;</string>
 
   <string name="pref_whats_new_notification">&pref_whats_new_notification;</string>
   <string name="pref_whats_new_notification_summary">&pref_whats_new_notification_summary;</string>
 
+  <string name="pref_custom_tabs">&pref_custom_tabs;</string>
+  <string name="pref_custom_tabs_summary">&pref_custom_tabs_summary;</string>
+
   <string name="pref_char_encoding">&pref_char_encoding;</string>
   <string name="pref_char_encoding_on">&pref_char_encoding_on;</string>
   <string name="pref_char_encoding_off">&pref_char_encoding_off;</string>
   <string name="pref_clear_private_data_now">&pref_clear_private_data2;</string>
   <string name="pref_clear_private_data_now_tablet">&pref_clear_private_data_now_tablet;</string>
   <string name="pref_clear_on_exit_title">&pref_clear_on_exit_title3;</string>
   <string name="pref_clear_on_exit_summary2">&pref_clear_on_exit_summary2;</string>
   <string name="pref_clear_on_exit_dialog_title">&pref_clear_on_exit_dialog_title;</string>
--- a/mobile/android/tests/browser/chrome/web_channel.html
+++ b/mobile/android/tests/browser/chrome/web_channel.html
@@ -19,71 +19,71 @@
       case "multichannel":
         test_multichannel();
         break;
     }
   };
 
   function test_generic() {
     var event = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: "generic",
         message: {
           something: {
             nested: "hello",
           },
         }
-      }
+      })
     });
 
     window.dispatchEvent(event);
   }
 
   function test_twoWay() {
     var firstMessage = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: "twoway",
         message: {
           command: "one",
         },
-      }
+      })
     });
 
     window.addEventListener("WebChannelMessageToContent", function(e) {
       var secondMessage = new window.CustomEvent("WebChannelMessageToChrome", {
-        detail: {
+        detail: JSON.stringify({
           id: "twoway",
           message: {
             command: "two",
             detail: e.detail.message,
           },
-        },
+        }),
       });
 
       if (!e.detail.message.error) {
         window.dispatchEvent(secondMessage);
       }
     }, true);
 
     window.dispatchEvent(firstMessage);
   }
 
   function test_multichannel() {
     var event1 = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: "wrongchannel",
         message: {},
-      }
+      })
     });
 
     var event2 = new window.CustomEvent("WebChannelMessageToChrome", {
-      detail: {
+      detail: JSON.stringify({
         id: "multichannel",
         message: {},
-      }
+      })
     });
 
     window.dispatchEvent(event1);
     window.dispatchEvent(event2);
   }
 </script>
 </body>
 </html>
--- a/old-configure.in
+++ b/old-configure.in
@@ -3796,29 +3796,16 @@ MOZ_ARG_DISABLE_BOOL(permissions,
 )
 
 AC_SUBST(MOZ_PERMISSIONS)
 if test -n "$MOZ_PERMISSIONS"; then
     AC_DEFINE(MOZ_PERMISSIONS)
 fi
 
 dnl ========================================================
-dnl Child permissions, currently only used for b2g
-dnl ========================================================
-if test -n "$MOZ_B2G"; then
-    if test -n "$MOZ_PERMISSIONS"; then
-        MOZ_CHILD_PERMISSIONS=1
-        AC_DEFINE(MOZ_CHILD_PERMISSIONS)
-    else
-        AC_MSG_ERROR([You need to enable MOZ_PERMISSIONS for MOZ_CHILD_PERMISSIONS])
-    fi
-fi
-AC_SUBST(MOZ_CHILD_PERMISSIONS)
-
-dnl ========================================================
 dnl NegotiateAuth
 dnl ========================================================
 
 MOZ_ARG_DISABLE_BOOL(negotiateauth,
 [  --disable-negotiateauth Disable GSS-API negotiation ],
     MOZ_AUTH_EXTENSION=,
     MOZ_AUTH_EXTENSION=1 )
 
--- a/taskcluster/docs/taskgraph.rst
+++ b/taskcluster/docs/taskgraph.rst
@@ -121,16 +121,36 @@ another, equivalent task, so it generate
 that to search for a matching, existing task.
 
 In some cases, such as try pushes, tasks in the target task set have been
 explicitly requested and are thus excluded from optimization. In other cases,
 the target task set is almost the entire task graph, so targetted tasks are
 considered for optimization.  This behavior is controlled with the
 ``optimize_target_tasks`` parameter.
 
+Action Tasks
+------------
+
+Action Tasks are tasks which help you to schedule new jobs via Treeherder's
+"Add New Jobs" feature. The Decision Task creates a YAML file named
+``action.yml`` which can be used to schedule Action Tasks after suitably replacing
+``{{decision_task_id}}`` and ``{{task_labels}}``, which correspond to the decision
+task ID of the push and a comma separated list of task labels which need to be
+scheduled.
+
+This task invokes ``mach taskgraph action-task`` which builds up a task graph of
+the requested tasks. This graph is optimized using the tasks running initially in
+the same push, due to the decision task.
+
+So for instance, if you had already requested a build task in the ``try`` command,
+and you wish to add a test which depends on this build, the original build task
+is re-used.
+
+This feature is only present on ``try`` pushes for now.
+
 Mach commands
 -------------
 
 A number of mach subcommands are available aside from ``mach taskgraph
 decision`` to make this complex system more accesssible to those trying to
 understand or modify it.  They allow you to run portions of the
 graph-generation process and output the results.
 
@@ -155,16 +175,21 @@ such a file on every run, and that is ge
 parameter file.  The parameter keys and values are described in
 :doc:`parameters`.
 
 Finally, the ``mach taskgraph decision`` subcommand performs the entire
 task-graph generation process, then creates the tasks.  This command should
 only be used within a decision task, as it assumes it is running in that
 context.
 
+The ``mach taskgraph action-task`` subcommand is used by Action Tasks to
+create a task graph of the requested jobs and its non-optimized dependencies.
+Action Tasks are currently scheduled by
+[pulse_actions](https://github.com/mozilla/pulse_actions)
+
 Taskgraph JSON Format
 ---------------------
 
 Task graphs -- both the graph artifacts produced by the decision task and those
 output by the ``--json`` option to the ``mach taskgraph`` commands -- are JSON
 objects, keyed by label, or for optimized task graphs, by taskId.  For
 convenience, the decision task also writes out ``label-to-taskid.json``
 containing a mapping from label to taskId.  Each task in the graph is
--- a/taskcluster/mach_commands.py
+++ b/taskcluster/mach_commands.py
@@ -148,16 +148,40 @@ class MachCommands(MachCommandBase):
         import taskgraph.decision
         try:
             self.setup_logging()
             return taskgraph.decision.taskgraph_decision(options)
         except Exception:
             traceback.print_exc()
             sys.exit(1)
 
+    @SubCommand('taskgraph', 'action-task',
+                description="Run the action task")
+    @CommandArgument('--root', '-r',
+                     default='taskcluster/ci',
+                     help="root of the taskgraph definition relative to topsrcdir")
+    @CommandArgument('--decision-id',
+                     required=True,
+                     help="Decision Task ID of the reference decision task")
+    @CommandArgument('--task-labels',
+                     required=True,
+                     help='Comma separated list of task labels to be scheduled')
+    def taskgraph_action(self, **options):
+        """Run the action task: Generates a task graph using the set of labels
+        provided in the task-labels parameter. It uses the full-task file of
+        the gecko decision task."""
+
+        import taskgraph.action
+        try:
+            self.setup_logging()
+            return taskgraph.action.taskgraph_action(options)
+        except Exception:
+            traceback.print_exc()
+            sys.exit(1)
+
     def setup_logging(self, quiet=False, verbose=True):
         """
         Set up Python logging for all loggers, sending results to stderr (so
         that command output can be redirected easily) and adding the typical
         mach timestamp.
         """
         # remove the old terminal handler
         self.log_manager.replace_terminal_handler(None)
new file mode 100644
--- /dev/null
+++ b/taskcluster/taskgraph/action.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+
+# 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/.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import json
+import logging
+import requests
+
+from .create import create_tasks
+from .decision import write_artifact
+from .parameters import Parameters
+from .optimize import optimize_task_graph
+from .taskgraph import TaskGraph
+
+logger = logging.getLogger(__name__)
+TASKCLUSTER_QUEUE_URL = "https://queue.taskcluster.net/v1/task/"
+
+
+def taskgraph_action(options):
+    """
+    Run the action task.  This function implements `mach taskgraph action-task`,
+    and is responsible for
+
+     * creating taskgraph of tasks asked for in parameters with respect to
+     a given gecko decision task and schedule these jobs.
+    """
+
+    parameters = get_action_parameters(options)
+    decision_task_id = parameters['decision_id']
+    # read in the full graph for reference
+    full_task_json = get_artifact(decision_task_id, "public/full-task-graph.json")
+    all_tasks, full_task_graph = TaskGraph.from_json(full_task_json, options['root'])
+
+    target_tasks = set(parameters['task_labels'].split(','))
+    target_graph = full_task_graph.graph.transitive_closure(target_tasks)
+    target_task_graph = TaskGraph(
+        {l: all_tasks[l] for l in target_graph.nodes},
+        target_graph)
+
+    existing_tasks = get_artifact(decision_task_id, "public/label-to-taskid.json")
+
+    # We don't want to optimize target tasks since they have been requested by user
+    # Hence we put `target_tasks under` `do_not_optimize`
+    optimized_graph, label_to_taskid = optimize_task_graph(target_task_graph=target_task_graph,
+                                                           do_not_optimize=target_tasks,
+                                                           existing_tasks=existing_tasks)
+
+    # write out the optimized task graph to describe what will actually happen,
+    # and the map of labels to taskids
+    write_artifact('task-graph.json', optimized_graph.to_json())
+    write_artifact('label-to-taskid.json', label_to_taskid)
+    # actually create the graph
+    create_tasks(optimized_graph, label_to_taskid)
+
+
+def get_action_parameters(options):
+    """
+    Load parameters from the command-line options for 'taskgraph action'.
+    """
+    parameters = {n: options[n] for n in [
+        'decision_id',
+        'task_labels',
+    ] if n in options}
+
+    return Parameters(parameters)
+
+
+def get_artifact(task_id, path):
+    url = TASKCLUSTER_QUEUE_URL + task_id + "/artifacts/" + path
+    resp = requests.get(url=url)
+    artifact = json.loads(resp.text)
+    return artifact
new file mode 100644
--- /dev/null
+++ b/taskcluster/taskgraph/action.yml
@@ -0,0 +1,72 @@
+---
+created: '{{now}}'
+deadline: '{{#from_now}}1 day{{/from_now}}'
+expires: '{{#from_now}}14 day{{/from_now}}'
+metadata:
+  owner: mozilla-taskcluster-maintenance@mozilla.com
+  source: 'https://hg.mozilla.org/{{project}}/file/{{head_rev}}/taskcluster/taskgraph/action.yml'
+  name: "[tc] Action Task"
+  description: Helps schedule new jobs without new push
+
+workerType: "gecko-decision"
+provisionerId: "aws-provisioner-v1"
+
+tags:
+  createdForUser: {{owner}}
+
+scopes:
+  # Bug 1269443: cache scopes, etc. must be listed explicitly
+  - "docker-worker:cache:level-1-*"
+  - "docker-worker:cache:tooltool-cache"
+  - "secrets:get:project/taskcluster/gecko/hgfingerprint"
+  - "assume:repo:hg.mozilla.org/try:*"
+
+routes:
+  - "tc-treeherder.v2.{{project}}.{{head_rev}}.{{pushlog_id}}"
+  - "tc-treeherder-stage.v2.{{project}}.{{head_rev}}.{{pushlog_id}}"
+
+payload:
+  env:
+    GECKO_BASE_REPOSITORY: 'https://hg.mozilla.org/mozilla-central'
+    GECKO_HEAD_REPOSITORY: '{{{head_repository}}}'
+    GECKO_HEAD_REF: '{{head_ref}}'
+    GECKO_HEAD_REV: '{{head_rev}}'
+
+  cache:
+    level-{{level}}-{{project}}-tc-vcs-public-sources: /home/worker/.tc-vcs/
+    level-{{level}}-{{project}}-gecko-decision: /home/worker/workspace
+
+  features:
+    taskclusterProxy: true
+
+  # Note: This task is built server side without the context or tooling that
+  # exist in tree so we must hard code the version
+  image: 'taskcluster/decision:0.1.0'
+
+  # Virtually no network or other potentially risky operations happen as part
+  # of the task timeout aside from the initial clone. We intentionally have
+  # set this to a lower value _all_ decision tasks should use a root
+  # repository which is cached.
+  maxRunTime: 1800
+
+  command:
+    - /bin/bash
+    - -cx
+    - >
+      mkdir -p /home/worker/artifacts &&
+      checkout-gecko workspace &&
+      cd workspace/gecko &&
+      ln -s /home/worker/artifacts artifacts &&
+      ./mach taskgraph action-task
+      --decision-id='{{decision_task_id}}'
+      --task-labels='{{task_labels}}'
+
+  artifacts:
+    'public':
+      type: 'directory'
+      path: '/home/worker/artifacts'
+      expires: '{{#from_now}}7 days{{/from_now}}'
+
+extra:
+  treeherder:
+    symbol: A
--- a/taskcluster/taskgraph/decision.py
+++ b/taskcluster/taskgraph/decision.py
@@ -11,21 +11,27 @@ import json
 import logging
 import yaml
 
 from .generator import TaskGraphGenerator
 from .create import create_tasks
 from .parameters import Parameters
 from .target_tasks import get_method
 
-logger = logging.getLogger(__name__)
-ARTIFACTS_DIR = 'artifacts'
+from taskgraph.util.templates import Templates
+from taskgraph.util.time import (
+    json_time_from_now,
+    current_json_time,
+)
 
 logger = logging.getLogger(__name__)
 
+ARTIFACTS_DIR = 'artifacts'
+GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..'))
+
 # For each project, this gives a set of parameters specific to the project.
 # See `taskcluster/docs/parameters.rst` for information on parameters.
 PER_PROJECT_PARAMETERS = {
     'try': {
         'target_tasks_method': 'try_option_syntax',
         # for try, if a task was specified as a target, it should
         # not be optimized away
         'optimize_target_tasks': False,
@@ -59,16 +65,19 @@ def taskgraph_decision(options):
     tgg = TaskGraphGenerator(
         root_dir=options['root'],
         parameters=parameters,
         target_tasks_method=target_tasks_method)
 
     # write out the parameters used to generate this graph
     write_artifact('parameters.yml', dict(**parameters))
 
+    # write out the yml file for action tasks
+    write_artifact('action.yml', get_action_yml(parameters))
+
     # write out the full graph for reference
     write_artifact('full-task-graph.json', tgg.full_task_graph.to_json())
 
     # write out the target task set to allow reproducing this as input
     write_artifact('target-tasks.json', tgg.target_task_set.tasks.keys())
 
     # write out the optimized task graph to describe what will actually happen,
     # and the map of labels to taskids
@@ -118,8 +127,20 @@ def write_artifact(filename, data):
     if filename.endswith('.yml'):
         with open(path, 'w') as f:
             yaml.safe_dump(data, f, allow_unicode=True, default_flow_style=False)
     elif filename.endswith('.json'):
         with open(path, 'w') as f:
             json.dump(data, f, sort_keys=True, indent=2, separators=(',', ': '))
     else:
         raise TypeError("Don't know how to write to {}".format(filename))
+
+
+def get_action_yml(parameters):
+    templates = Templates(os.path.join(GECKO, "taskcluster/taskgraph"))
+    action_parameters = parameters.copy()
+    action_parameters.update({
+        "decision_task_id": "{{decision_task_id}}",
+        "task_labels": "{{task_labels}}",
+        "from_now": json_time_from_now,
+        "now": current_json_time()
+    })
+    return templates.load('action.yml', action_parameters)
--- a/taskcluster/taskgraph/optimize.py
+++ b/taskcluster/taskgraph/optimize.py
@@ -9,30 +9,34 @@ import re
 from .graph import Graph
 from .taskgraph import TaskGraph
 from slugid import nice as slugid
 
 logger = logging.getLogger(__name__)
 TASK_REFERENCE_PATTERN = re.compile('<([^>]+)>')
 
 
-def optimize_task_graph(target_task_graph, do_not_optimize):
+def optimize_task_graph(target_task_graph, do_not_optimize, existing_tasks=None):
     """
     Perform task optimization, without optimizing tasks named in
     do_not_optimize.
     """
     named_links_dict = target_task_graph.graph.named_links_dict()
     label_to_taskid = {}
 
     # This proceeds in two phases.  First, mark all optimized tasks (those
     # which will be removed from the graph) as such, including a replacement
     # taskId where applicable.  Second, generate a new task graph containing
     # only the non-optimized tasks, with all task labels resolved to taskIds
     # and with task['dependencies'] populated.
-    annotate_task_graph(target_task_graph, do_not_optimize, named_links_dict, label_to_taskid)
+    annotate_task_graph(target_task_graph=target_task_graph,
+                        do_not_optimize=do_not_optimize,
+                        named_links_dict=named_links_dict,
+                        label_to_taskid=label_to_taskid,
+                        existing_tasks=existing_tasks)
     return get_subgraph(target_task_graph, named_links_dict, label_to_taskid), label_to_taskid
 
 
 def resolve_task_references(label, task_def, taskid_for_edge_name):
     def repl(match):
         key = match.group(1)
         try:
             return taskid_for_edge_name[key]
@@ -50,17 +54,18 @@ def resolve_task_references(label, task_
                 return TASK_REFERENCE_PATTERN.sub(repl, val['task-reference'])
             else:
                 return {k: recurse(v) for k, v in val.iteritems()}
         else:
             return val
     return recurse(task_def)
 
 
-def annotate_task_graph(target_task_graph, do_not_optimize, named_links_dict, label_to_taskid):
+def annotate_task_graph(target_task_graph, do_not_optimize,
+                        named_links_dict, label_to_taskid, existing_tasks):
     """
     Annotate each task in the graph with .optimized (boolean) and .task_id
     (possibly None), following the rules for optimization and calling the task
     kinds' `optimize_task` method.
 
     As a side effect, label_to_taskid is updated with labels for all optimized
     tasks that are replaced with existing tasks.
     """
@@ -81,16 +86,20 @@ def annotate_task_graph(target_task_grap
 
         # if this task is blacklisted, don't even consider optimizing
         replacement_task_id = None
         if label in do_not_optimize:
             optimized = False
         # if any dependencies can't be optimized, this task can't, either
         elif any(not t.optimized for t in dependencies):
             optimized = False
+        # Let's check whether this task has been created before
+        elif existing_tasks is not None and label in existing_tasks:
+            optimized = True
+            replacement_task_id = existing_tasks[label]
         # otherwise, examine the task itself (which may be an expensive operation)
         else:
             optimized, replacement_task_id = task.optimize()
 
         task.optimized = optimized
         task.task_id = replacement_task_id
         if replacement_task_id:
             label_to_taskid[label] = replacement_task_id
--- a/taskcluster/taskgraph/test/test_optimize.py
+++ b/taskcluster/taskgraph/test/test_optimize.py
@@ -88,58 +88,58 @@ class TestOptimize(unittest.TestCase):
         OptimizingTask.optimize = lambda self: (False, None)
         graph = self.make_graph(
             self.make_task('task1'),
             self.make_task('task2'),
             self.make_task('task3'),
             ('task2', 'task1', 'build'),
             ('task2', 'task3', 'image'),
         )
-        annotate_task_graph(graph, set(), graph.graph.named_links_dict(), {})
+        annotate_task_graph(graph, set(), graph.graph.named_links_dict(), {}, None)
         self.assert_annotations(
             graph,
             task1=(False, None),
             task2=(False, None),
             task3=(False, None)
         )
 
     def test_annotate_task_graph_taskid_without_optimize(self):
         "raises exception if kind returns a taskid without optimizing"
         OptimizingTask.optimize = lambda self: (False, 'some-taskid')
         graph = self.make_graph(self.make_task('task1'))
         self.assertRaises(
             Exception,
-            lambda: annotate_task_graph(graph, set(), graph.graph.named_links_dict(), {})
+            lambda: annotate_task_graph(graph, set(), graph.graph.named_links_dict(), {}, None)
         )
 
     def test_annotate_task_graph_optimize_away_dependency(self):
         "raises exception if kind optimizes away a task on which another depends"
         OptimizingTask.optimize = \
             lambda self: (True, None) if self.label == 'task1' else (False, None)
         graph = self.make_graph(
             self.make_task('task1'),
             self.make_task('task2'),
             ('task2', 'task1', 'build'),
         )
         self.assertRaises(
             Exception,
-            lambda: annotate_task_graph(graph, set(), graph.graph.named_links_dict(), {})
+            lambda: annotate_task_graph(graph, set(), graph.graph.named_links_dict(), {}, None)
         )
 
     def test_annotate_task_graph_do_not_optimize(self):
         "annotating marks everything as un-optimized if in do_not_optimize"
         OptimizingTask.optimize = lambda self: (True, 'taskid')
         graph = self.make_graph(
             self.make_task('task1'),
             self.make_task('task2'),
             ('task2', 'task1', 'build'),
         )
         label_to_taskid = {}
         annotate_task_graph(graph, {'task1', 'task2'},
-                            graph.graph.named_links_dict(), label_to_taskid)
+                            graph.graph.named_links_dict(), label_to_taskid, None)
         self.assert_annotations(
             graph,
             task1=(False, None),
             task2=(False, None)
         )
         self.assertEqual
 
     def test_annotate_task_graph_nos_propagate(self):
@@ -149,17 +149,17 @@ class TestOptimize(unittest.TestCase):
         graph = self.make_graph(
             self.make_task('task1'),
             self.make_task('task2'),
             self.make_task('task3'),
             ('task2', 'task1', 'build'),
             ('task2', 'task3', 'image'),
         )
         annotate_task_graph(graph, set(),
-                            graph.graph.named_links_dict(), {})
+                            graph.graph.named_links_dict(), {}, None)
         self.assert_annotations(
             graph,
             task1=(False, None),
             task2=(False, None),  # kind would have returned (True, 'taskid') here
             task3=(True, 'taskid')
         )
 
     def test_get_subgraph_single_dep(self):
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -381,16 +381,22 @@ class ExtensionContext extends BaseConte
 
       this.sandbox = Cu.Sandbox(prin, {
         metadata,
         sandboxPrototype: contentWindow,
         wantXrays: true,
         isWebExtensionContentScript: true,
         wantGlobalProperties: ["XMLHttpRequest", "fetch"],
       });
+
+      Cu.evalInSandbox(`
+        window.JSON = JSON;
+        window.XMLHttpRequest = XMLHttpRequest;
+        window.fetch = fetch;
+      `, this.sandbox);
     }
 
     let delegate = {
       getSender(context, target, sender) {
         // Nothing to do here.
       },
     };
 
--- a/toolkit/components/extensions/test/mochitest/test_ext_permission_xhr.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_permission_xhr.html
@@ -12,24 +12,26 @@
 
 <script type="text/javascript">
 "use strict";
 
 /* eslint-disable mozilla/balanced-listeners */
 
 add_task(function* test_simple() {
   function runTests(cx) {
-    function xhr(url) {
-      return new Promise((resolve, reject) => {
-        let req = new XMLHttpRequest();
-        req.open("GET", url);
-        req.addEventListener("load", resolve);
-        req.addEventListener("error", reject);
-        req.send();
-      });
+    function xhr(XMLHttpRequest) {
+      return (url) => {
+        return new Promise((resolve, reject) => {
+          let req = new XMLHttpRequest();
+          req.open("GET", url);
+          req.addEventListener("load", resolve);
+          req.addEventListener("error", reject);
+          req.send();
+        });
+      };
     }
 
     function run(shouldFail, fetch) {
       function passListener() {
         browser.test.succeed(`${cx}.${fetch.name} pass listener`);
       }
 
       function failListener() {
@@ -38,20 +40,24 @@ add_task(function* test_simple() {
 
       if (shouldFail) {
         return fetch("http://example.org/example.txt").then(failListener, passListener);
       } else {
         return fetch("http://example.com/example.txt").then(passListener, failListener);
       }
     }
 
-    return run(true, xhr)
-      .then(() => run(false, xhr))
+    return run(true, xhr(XMLHttpRequest))
+      .then(() => run(false, xhr(XMLHttpRequest)))
+      .then(() => run(true, xhr(window.XMLHttpRequest)))
+      .then(() => run(false, xhr(window.XMLHttpRequest)))
       .then(() => run(true, fetch))
       .then(() => run(false, fetch))
+      .then(() => run(true, window.fetch))
+      .then(() => run(false, window.fetch))
       .catch(err => {
         browser.test.fail(`Error: ${err} :: ${err.stack}`);
         browser.test.notifyFail("permission_xhr");
       });
   }
 
   function background(runTests) {
     runTests("bg").then(() => {
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8225,16 +8225,25 @@
     "alert_emails": ["ajones@mozilla.com"],
     "expires_in_version": "55",
     "description": "Total time spent playing video in milliseconds. This reports the total play time for an HTML Media Element whenever it is suspended or resumed, such as when the page is unloaded, or when the mute status changes when the AudioChannelAPI pref is set.",
     "kind": "exponential",
     "high": 7200000,
     "n_buckets": 100,
     "bug_numbers": [1261955, 1127646]
   },
+  "VIDEO_HIDDEN_PLAY_TIME_MS" : {
+    "alert_emails": ["ajones@mozilla.com", "gsquelart@mozilla.com"],
+    "expires_in_version": "55",
+    "description": "Total time spent playing video while element is hidden, in milliseconds. This reports the total hidden play time for an HTML Media Element whenever it is suspended or resumed, such as when the page is unloaded, or when the mute status changes when the AudioChannelAPI pref is set.",
+    "kind": "exponential",
+    "high": 7200000,
+    "n_buckets": 100,
+    "bug_numbers": [1285419]
+  },
   "VIDEO_UNLOAD_STATE": {
     "alert_emails": ["ajones@mozilla.com"],
     "expires_in_version": "55",
     "kind": "enumerated",
     "n_values": 5,
     "description": "HTML Media Element state when unloading. ended = 0, paused = 1, stalled = 2, seeking = 3, other = 4",
     "bug_numbers": [1261955, 1261955]
   },
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -809,27 +809,66 @@ var FindBar = {
 
   _onMouseup(event) {
     if (this._findMode != this.FIND_NORMAL)
       sendAsyncMessage("Findbar:Mouseup");
   },
 };
 FindBar.init();
 
-// An event listener for custom "WebChannelMessageToChrome" events on pages.
-addEventListener("WebChannelMessageToChrome", function (e) {
-  // If target is window then we want the document principal, otherwise fallback to target itself.
-  let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal;
+let WebChannelMessageToChromeListener = {
+  // Preference containing the list (space separated) of origins that are
+  // allowed to send non-string values through a WebChannel, mainly for
+  // backwards compatability. See bug 1238128 for more information.
+  URL_WHITELIST_PREF: "webchannel.allowObject.urlWhitelist",
+
+  // Cached list of whitelisted principals, we avoid constructing this if the
+  // value in `_lastWhitelistValue` hasn't changed since we constructed it last.
+  _cachedWhitelist: [],
+  _lastWhitelistValue: "",
+
+  init() {
+    addEventListener("WebChannelMessageToChrome", e => {
+      this._onMessageToChrome(e);
+    }, true, true);
+  },
 
-  if (e.detail) {
-    sendAsyncMessage("WebChannelMessageToChrome", e.detail, { eventTarget: e.target }, principal);
-  } else  {
-    Cu.reportError("WebChannel message failed. No message detail.");
+  _getWhitelistedPrincipals() {
+    let whitelist = Services.prefs.getCharPref(this.URL_WHITELIST_PREF);
+    if (whitelist != this._lastWhitelistValue) {
+      let urls = whitelist.split(/\s+/);
+      this._cachedWhitelist = urls.map(origin =>
+        Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin));
+    }
+    return this._cachedWhitelist;
+  },
+
+  _onMessageToChrome(e) {
+    // If target is window then we want the document principal, otherwise fallback to target itself.
+    let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal;
+
+    if (e.detail) {
+      if (typeof e.detail != 'string') {
+        // Check if the principal is one of the ones that's allowed to send
+        // non-string values for e.detail.
+        let objectsAllowed = this._getWhitelistedPrincipals().some(whitelisted =>
+          principal.originNoSuffix == whitelisted.originNoSuffix);
+        if (!objectsAllowed) {
+          Cu.reportError("WebChannelMessageToChrome sent with an object from a non-whitelisted principal");
+          return;
+        }
+      }
+      sendAsyncMessage("WebChannelMessageToChrome", e.detail, { eventTarget: e.target }, principal);
+    } else  {
+      Cu.reportError("WebChannel message failed. No message detail.");
+    }
   }
-}, true, true);
+};
+
+WebChannelMessageToChromeListener.init();
 
 // This should be kept in sync with /browser/base/content.js.
 // Add message listener for "WebChannelMessageToContent" messages from chrome scripts.
 addMessageListener("WebChannelMessageToContent", function (e) {
   if (e.data) {
     // e.objects.eventTarget will be defined if sending a response to
     // a WebChannelMessageToChrome event. An unsolicited send
     // may not have an eventTarget defined, in this case send to the
--- a/toolkit/modules/WebChannel.jsm
+++ b/toolkit/modules/WebChannel.jsm
@@ -66,16 +66,25 @@ var WebChannelBroker = Object.create({
    */
   _listener: function (event) {
     let data = event.data;
     let sendingContext = {
       browser: event.target,
       eventTarget: event.objects.eventTarget,
       principal: event.principal,
     };
+    // data must be a string except for a few legacy origins allowed by browser-content.js.
+    if (typeof data == "string") {
+      try {
+        data = JSON.parse(data);
+      } catch (e) {
+        Cu.reportError("Failed to parse WebChannel data as a JSON object");
+        return;
+      }
+    }
 
     if (data && data.id) {
       if (!event.principal) {
         this._sendErrorEventToContent(data.id, sendingContext, "Message principal missing");
       } else {
         let validChannelFound = false;
         data.message = data.message || {};
 
--- a/widget/ContentCache.cpp
+++ b/widget/ContentCache.cpp
@@ -64,16 +64,44 @@ public:
       Assign("Vertical (LTR)");
       return;
     }
     Assign("Vertical (RTL)");
   }
   virtual ~GetWritingModeName() {}
 };
 
+class GetEscapedUTF8String final : public NS_ConvertUTF16toUTF8
+{
+public:
+  explicit GetEscapedUTF8String(const nsAString& aString)
+    : NS_ConvertUTF16toUTF8(aString)
+  {
+    Escape();
+  }
+  explicit GetEscapedUTF8String(const char16ptr_t aString)
+    : NS_ConvertUTF16toUTF8(aString)
+  {
+    Escape();
+  }
+  GetEscapedUTF8String(const char16ptr_t aString, uint32_t aLength)
+    : NS_ConvertUTF16toUTF8(aString, aLength)
+  {
+    Escape();
+  }
+
+private:
+  void Escape()
+  {
+    ReplaceSubstring("\r", "\\r");
+    ReplaceSubstring("\n", "\\n");
+    ReplaceSubstring("\t", "\\t");
+  }
+};
+
 /*****************************************************************************
  * mozilla::ContentCache
  *****************************************************************************/
 
 LazyLogModule sContentCacheLog("ContentCacheWidgets");
 
 ContentCache::ContentCache()
   : mCompositionStart(UINT32_MAX)
@@ -543,17 +571,17 @@ ContentCacheInParent::HandleQueryContent
       aEvent.mReply.mReversed = mSelection.Reversed();
       aEvent.mReply.mHasSelection = true;
       aEvent.mReply.mWritingMode = mSelection.mWritingMode;
       MOZ_LOG(sContentCacheLog, LogLevel::Info,
         ("0x%p HandleQueryContentEvent(), "
          "Succeeded, aEvent={ mReply={ mOffset=%u, mString=\"%s\", "
          "mReversed=%s, mHasSelection=%s, mWritingMode=%s } }",
          this, aEvent.mReply.mOffset,
-         NS_ConvertUTF16toUTF8(aEvent.mReply.mString).get(),
+         GetEscapedUTF8String(aEvent.mReply.mString).get(),
          GetBoolName(aEvent.mReply.mReversed),
          GetBoolName(aEvent.mReply.mHasSelection),
          GetWritingModeName(aEvent.mReply.mWritingMode).get()));
       break;
     case eQueryTextContent: {
       MOZ_LOG(sContentCacheLog, LogLevel::Info,
         ("0x%p HandleQueryContentEvent("
          "aEvent={ mMessage=eQueryTextContent, mInput={ mOffset=%u, "
@@ -631,17 +659,17 @@ ContentCacheInParent::HandleQueryContent
       aEvent.mReply.mOffset = aEvent.mInput.mOffset;
       // XXX This may be wrong if storing range isn't in the selection range.
       aEvent.mReply.mWritingMode = mSelection.mWritingMode;
       MOZ_LOG(sContentCacheLog, LogLevel::Info,
         ("0x%p HandleQueryContentEvent(), "
          "Succeeded, aEvent={ mReply={ mOffset=%u, mString=\"%s\", "
          "mWritingMode=%s, mRect=%s } }",
          this, aEvent.mReply.mOffset,
-         NS_ConvertUTF16toUTF8(aEvent.mReply.mString).get(),
+         GetEscapedUTF8String(aEvent.mReply.mString).get(),
          GetWritingModeName(aEvent.mReply.mWritingMode).get(),
          GetRectText(aEvent.mReply.mRect).get()));
       break;
     case eQueryCaretRect:
       MOZ_LOG(sContentCacheLog, LogLevel::Info,
         ("0x%p HandleQueryContentEvent("
          "aEvent={ mMessage=eQueryCaretRect, mInput={ mOffset=%u } }, "
          "aWidget=0x%p), mText.Length()=%u",
@@ -881,17 +909,17 @@ bool
 ContentCacheInParent::OnCompositionEvent(const WidgetCompositionEvent& aEvent)
 {
   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     ("0x%p OnCompositionEvent(aEvent={ "
      "mMessage=%s, mData=\"%s\" (Length()=%u), mRanges->Length()=%u }), "
      "mPendingEventsNeedingAck=%u, mIsComposing=%s, "
      "mCommitStringByRequest=0x%p",
      this, ToChar(aEvent.mMessage),
-     NS_ConvertUTF16toUTF8(aEvent.mData).get(), aEvent.mData.Length(),
+     GetEscapedUTF8String(aEvent.mData).get(), aEvent.mData.Length(),
      aEvent.mRanges ? aEvent.mRanges->Length() : 0, mPendingEventsNeedingAck,
      GetBoolName(mIsComposing), mCommitStringByRequest));
 
   // We must be able to simulate the selection because
   // we might not receive selection updates in time
   if (!mIsComposing) {
     if (aEvent.mWidget && aEvent.mWidget->PluginHasFocus()) {
       // If focus is on plugin, we cannot get selection range
--- a/widget/windows/TSFTextStore.cpp
+++ b/widget/windows/TSFTextStore.cpp
@@ -658,16 +658,44 @@ public:
       AssignLiteral("Vertical (LR)");
       return;
     }
     AssignLiteral("Vertical (RL)");
   }
   virtual ~GetWritingModeName() {}
 };
 
+class GetEscapedUTF8String final : public NS_ConvertUTF16toUTF8
+{
+public:
+  explicit GetEscapedUTF8String(const nsAString& aString)
+    : NS_ConvertUTF16toUTF8(aString)
+  {
+    Escape();
+  }
+  explicit GetEscapedUTF8String(const char16ptr_t aString)
+    : NS_ConvertUTF16toUTF8(aString)
+  {
+    Escape();
+  }
+  GetEscapedUTF8String(const char16ptr_t aString, uint32_t aLength)
+    : NS_ConvertUTF16toUTF8(aString, aLength)
+  {
+    Escape();
+  }
+
+private:
+  void Escape()
+  {
+    ReplaceSubstring("\r", "\\r");
+    ReplaceSubstring("\n", "\\n");
+    ReplaceSubstring("\t", "\\t");
+  }
+};
+
 /******************************************************************/
 /* InputScopeImpl                                                 */
 /******************************************************************/
 
 class InputScopeImpl final : public ITfInputScope
 {
   ~InputScopeImpl() {}
 
@@ -1685,17 +1713,17 @@ TSFTextStore::FlushPendingActions()
         }
         break;
       }
       case PendingAction::COMPOSITION_UPDATE: {
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
           ("0x%p   TSFTextStore::FlushPendingActions() "
            "flushing COMPOSITION_UPDATE={ mData=\"%s\", "
            "mRanges=0x%p, mRanges->Length()=%d }",
-           this, NS_ConvertUTF16toUTF8(action.mData).get(),
+           this, GetEscapedUTF8String(action.mData).get(),
            action.mRanges.get(),
            action.mRanges ? action.mRanges->Length() : 0));
 
         // eCompositionChange causes a DOM text event, the IME will be notified
         // of NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED.  In this case, we
         // should not clear mContentForTSF until we notify the IME of the
         // composition update.
         mDeferClearingContentForTSF = true;
@@ -1727,17 +1755,17 @@ TSFTextStore::FlushPendingActions()
           // Be aware, the mWidget might already have been destroyed.
         }
         break;
       }
       case PendingAction::COMPOSITION_END: {
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
           ("0x%p   TSFTextStore::FlushPendingActions() "
            "flushing COMPOSITION_END={ mData=\"%s\" }",
-           this, NS_ConvertUTF16toUTF8(action.mData).get()));
+           this, GetEscapedUTF8String(action.mData).get()));
 
         // Dispatching eCompositionCommit causes a DOM text event, then,
         // the IME will be notified of NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED.
         // In this case, we should not clear mContentForTSFuntil we notify
         // the IME of the composition update.
         mDeferClearingContentForTSF = true;
 
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
@@ -2051,20 +2079,20 @@ TSFTextStore::ContentForTSFRef()
     mDeferClearingContentForTSF = false;
   }
 
   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
     ("0x%p   TSFTextStore::ContentForTSFRef(): "
      "mContentForTSF={ mText=\"%s\" (Length()=%u), "
      "mLastCompositionString=\"%s\" (Length()=%u), "
      "mMinTextModifiedOffset=%u }",
-     this, mContentForTSF.Text().Length() <= 20 ?
-       NS_ConvertUTF16toUTF8(mContentForTSF.Text()).get() : "<omitted>",
+     this, mContentForTSF.Text().Length() <= 40 ?
+       GetEscapedUTF8String(mContentForTSF.Text()).get() : "<omitted>",
      mContentForTSF.Text().Length(),
-     NS_ConvertUTF16toUTF8(mContentForTSF.LastCompositionString()).get(),
+     GetEscapedUTF8String(mContentForTSF.LastCompositionString()).get(),
      mContentForTSF.LastCompositionString().Length(),
      mContentForTSF.MinTextModifiedOffset()));
 
   return mContentForTSF;
 }
 
 bool
 TSFTextStore::GetCurrentText(nsAString& aTextContent)
@@ -2450,17 +2478,17 @@ GetLineStyle(TF_DA_LINESTYLE aTSFLineSty
 HRESULT
 TSFTextStore::RecordCompositionUpdateAction()
 {
   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
     ("0x%p   TSFTextStore::RecordCompositionUpdateAction(), "
      "mComposition={ mView=0x%p, mStart=%d, mString=\"%s\" "
      "(Length()=%d) }",
      this, mComposition.mView.get(), mComposition.mStart,
-     NS_ConvertUTF16toUTF8(mComposition.mString).get(),
+     GetEscapedUTF8String(mComposition.mString).get(),
      mComposition.mString.Length()));
 
   if (!mComposition.IsComposing()) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::RecordCompositionUpdateAction() FAILED "
        "due to no composition view", this));
     return E_FAIL;
   }
@@ -2926,17 +2954,17 @@ TSFTextStore::SetText(DWORD dwFlags,
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("0x%p TSFTextStore::SetText(dwFlags=%s, acpStart=%ld, "
      "acpEnd=%ld, pchText=0x%p \"%s\", cch=%lu, pChange=0x%p), "
      "mComposition.IsComposing()=%s",
      this, dwFlags == TS_ST_CORRECTION ? "TS_ST_CORRECTION" :
                                          "not-specified",
      acpStart, acpEnd, pchText,
      pchText && cch ?
-       NS_ConvertUTF16toUTF8(pchText, cch).get() : "",
+       GetEscapedUTF8String(pchText, cch).get() : "",
      cch, pChange, GetBoolName(mComposition.IsComposing())));
 
   // Per SDK documentation, and since we don't have better
   // ways to do this, this method acts as a helper to
   // call SetSelection followed by InsertTextAtSelection
   if (!IsReadWriteLocked()) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::SetText() FAILED due to "
@@ -3889,17 +3917,17 @@ TSFTextStore::InsertTextAtSelection(DWOR
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("0x%p TSFTextStore::InsertTextAtSelection(dwFlags=%s, "
      "pchText=0x%p \"%s\", cch=%lu, pacpStart=0x%p, pacpEnd=0x%p, "
      "pChange=0x%p), IsComposing()=%s",
      this, dwFlags == 0 ? "0" :
            dwFlags == TF_IAS_NOQUERY ? "TF_IAS_NOQUERY" :
            dwFlags == TF_IAS_QUERYONLY ? "TF_IAS_QUERYONLY" : "Unknown",
      pchText,
-     pchText && cch ? NS_ConvertUTF16toUTF8(pchText, cch).get() : "",
+     pchText && cch ? GetEscapedUTF8String(pchText, cch).get() : "",
      cch, pacpStart, pacpEnd, pChange,
      GetBoolName(mComposition.IsComposing())));
 
   if (cch && !pchText) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::InsertTextAtSelection() FAILED due to "
        "null pchText", this));
     return E_INVALIDARG;
@@ -3985,17 +4013,17 @@ TSFTextStore::InsertTextAtSelection(DWOR
 
 bool
 TSFTextStore::InsertTextAtSelectionInternal(const nsAString& aInsertStr,
                                             TS_TEXTCHANGE* aTextChange)
 {
   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
     ("0x%p   TSFTextStore::InsertTextAtSelectionInternal("
      "aInsertStr=\"%s\", aTextChange=0x%p), IsComposing=%s",
-     this, NS_ConvertUTF16toUTF8(aInsertStr).get(), aTextChange,
+     this, GetEscapedUTF8String(aInsertStr).get(), aTextChange,
      GetBoolName(mComposition.IsComposing())));
 
   Content& contentForTSF = ContentForTSFRef();
   if (!contentForTSF.IsInitialized()) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::InsertTextAtSelectionInternal() failed "
        "due to ContentForTSFRef() failure()", this));
     return false;
@@ -4018,17 +4046,17 @@ TSFTextStore::InsertTextAtSelectionInter
     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
       ("0x%p   TSFTextStore::InsertTextAtSelectionInternal() "
        "appending pending compositionstart and compositionend... "
        "PendingCompositionStart={ mSelectionStart=%d, "
        "mSelectionLength=%d }, PendingCompositionEnd={ mData=\"%s\" "
        "(Length()=%u) }",
        this, compositionStart->mSelectionStart,
        compositionStart->mSelectionLength,
-       NS_ConvertUTF16toUTF8(compositionEnd->mData).get(),
+       GetEscapedUTF8String(compositionEnd->mData).get(),
        compositionEnd->mData.Length()));
   }
 
   contentForTSF.ReplaceSelectedTextWith(aInsertStr);
 
   if (aTextChange) {
     aTextChange->acpStart = oldSelection.acpStart;
     aTextChange->acpOldEnd = oldSelection.acpEnd;
@@ -4178,17 +4206,17 @@ TSFTextStore::RecordCompositionStartActi
 
 HRESULT
 TSFTextStore::RecordCompositionEndAction()
 {
   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
     ("0x%p   TSFTextStore::RecordCompositionEndAction(), "
      "mComposition={ mView=0x%p, mString=\"%s\" }",
      this, mComposition.mView.get(),
-     NS_ConvertUTF16toUTF8(mComposition.mString).get()));
+     GetEscapedUTF8String(mComposition.mString).get()));
 
   MOZ_ASSERT(mComposition.IsComposing());
 
   CompleteLastActionIfStillIncomplete();
   PendingAction* action = mPendingActions.AppendElement();
   action->mType = PendingAction::COMPOSITION_END;
   action->mData = mComposition.mString;
 
@@ -4341,31 +4369,31 @@ TSFTextStore::OnUpdateComposition(ITfCom
          "SelectionForTSFRef() failure", this));
       return E_FAIL;
     }
     MOZ_LOG(sTextStoreLog, LogLevel::Info,
       ("0x%p   TSFTextStore::OnUpdateComposition() succeeded: "
        "mComposition={ mStart=%ld, mString=\"%s\" }, "
        "SelectionForTSFRef()={ acpStart=%ld, acpEnd=%ld, style.ase=%s }",
        this, mComposition.mStart,
-       NS_ConvertUTF16toUTF8(mComposition.mString).get(),
+       GetEscapedUTF8String(mComposition.mString).get(),
        selectionForTSF.StartOffset(), selectionForTSF.EndOffset(),
        GetActiveSelEndName(selectionForTSF.ActiveSelEnd())));
   }
   return S_OK;
 }
 
 STDMETHODIMP
 TSFTextStore::OnEndComposition(ITfCompositionView* pComposition)
 {
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("0x%p TSFTextStore::OnEndComposition(pComposition=0x%p), "
      "mComposition={ mView=0x%p, mString=\"%s\" }",
      this, pComposition, mComposition.mView.get(),
-     NS_ConvertUTF16toUTF8(mComposition.mString).get()));
+     GetEscapedUTF8String(mComposition.mString).get()));
 
   AutoPendingActionAndContentFlusher flusher(this);
 
   if (!mComposition.IsComposing()) {
     MOZ_LOG(sTextStoreLog, LogLevel::Error,
       ("0x%p   TSFTextStore::OnEndComposition() FAILED due to "
        "no active composition", this));
     return E_UNEXPECTED;
@@ -5198,17 +5226,17 @@ void
 TSFTextStore::CommitCompositionInternal(bool aDiscard)
 {
   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
     ("0x%p   TSFTextStore::CommitCompositionInternal(aDiscard=%s), "
      "mSink=0x%p, mContext=0x%p, mComposition.mView=0x%p, "
      "mComposition.mString=\"%s\"",
      this, GetBoolName(aDiscard), mSink.get(), mContext.get(),
      mComposition.mView.get(),
-     NS_ConvertUTF16toUTF8(mComposition.mString).get()));
+     GetEscapedUTF8String(mComposition.mString).get()));
 
   // If the document is locked, TSF will fail to commit composition since
   // TSF needs another document lock.  So, let's put off the request.
   // Note that TextComposition will commit composition in the focused editor
   // with the latest composition string for web apps and waits asynchronous
   // committing messages.  Therefore, we can and need to perform this
   // asynchronously.
   if (IsReadLocked()) {
@@ -5823,19 +5851,19 @@ TSFTextStore::Content::ReplaceTextWith(L
         mMinTextModifiedOffset = firstDifferentOffset =
           mComposition.EndOffset();
       }
       MOZ_LOG(sTextStoreLog, LogLevel::Debug,
         ("0x%p   TSFTextStore::Content::ReplaceTextWith(aStart=%d, "
          "aLength=%d, aReplaceString=\"%s\"), mComposition={ mStart=%d, "
          "mString=\"%s\" }, mLastCompositionString=\"%s\", "
          "mMinTextModifiedOffset=%u, firstDifferentOffset=%u",
-         this, aStart, aLength, NS_ConvertUTF16toUTF8(aReplaceString).get(),
-         mComposition.mStart, NS_ConvertUTF16toUTF8(mComposition.mString).get(),
-         NS_ConvertUTF16toUTF8(mLastCompositionString).get(),
+         this, aStart, aLength, GetEscapedUTF8String(aReplaceString).get(),
+         mComposition.mStart, GetEscapedUTF8String(mComposition.mString).get(),
+         GetEscapedUTF8String(mLastCompositionString).get(),
          mMinTextModifiedOffset, firstDifferentOffset));
     } else {
       firstDifferentOffset =
         static_cast<uint32_t>(aStart) +
           FirstDifferentCharOffset(aReplaceString, replacedString);
     }
     mMinTextModifiedOffset =
       std::min(mMinTextModifiedOffset, firstDifferentOffset);
--- a/xpcom/base/nsSystemInfo.cpp
+++ b/xpcom/base/nsSystemInfo.cpp
@@ -811,16 +811,25 @@ nsSystemInfo::Init()
                       sandInfo.CanSandboxMedia());
   }
 #endif // XP_LINUX && MOZ_SANDBOX
 
   return NS_OK;
 }
 
 #ifdef MOZ_WIDGET_ANDROID
+// Prerelease versions of Android use a letter instead of version numbers.
+// Unfortunately this breaks websites due to the user agent.
+// Chrome works around this by hardcoding an Android version when a
+// numeric version can't be obtained. We're doing the same.
+// This version will need to be updated whenever there is a new official
+// Android release.
+// See: https://cs.chromium.org/chromium/src/base/sys_info_android.cc?l=61
+#define DEFAULT_ANDROID_VERSION "6.0.99"
+
 /* static */
 void
 nsSystemInfo::GetAndroidSystemInfo(AndroidSystemInfo* aInfo)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   if (!mozilla::AndroidBridge::Bridge()) {
     aInfo->sdk_version() = 0;
@@ -833,17 +842,25 @@ nsSystemInfo::GetAndroidSystemInfo(Andro
     aInfo->device() = str;
   }
   if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
       "android/os/Build", "MANUFACTURER", str)) {
     aInfo->manufacturer() = str;
   }
   if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
       "android/os/Build$VERSION", "RELEASE", str)) {
-    aInfo->release_version() = str;
+    int major_version;
+    int minor_version;
+    int bugfix_version;
+    int num_read = sscanf(NS_ConvertUTF16toUTF8(str).get(), "%d.%d.%d", &major_version, &minor_version, &bugfix_version);
+    if (num_read == 0) {
+      aInfo->release_version() = NS_LITERAL_STRING(DEFAULT_ANDROID_VERSION);
+    } else {
+      aInfo->release_version() = str;
+    }
   }
   if (mozilla::AndroidBridge::Bridge()->GetStaticStringField(
       "android/os/Build", "HARDWARE", str)) {
     aInfo->hardware() = str;
   }
   int32_t sdk_version;
   if (!mozilla::AndroidBridge::Bridge()->GetStaticIntField(
       "android/os/Build$VERSION", "SDK_INT", &sdk_version)) {