Merge m-c to graphics
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 17 Mar 2017 11:03:02 -0400
changeset 349221 193e63383eff99fb919d0e4a69d549791a8d82b7
parent 349220 9cf2ea8fb1b7ecfd8d6cb630155d0c1d08ec73cd (current diff)
parent 348200 9a95ade0ec97a871f97c397261fc7297e897331f (diff)
child 349222 ceebf99c92ad17ebc9213942fff5fca5694c49e9
push id88400
push userkwierso@gmail.com
push dateThu, 23 Mar 2017 23:56:15 +0000
treeherdermozilla-inbound@dfe3771711d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone55.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 m-c to graphics MozReview-Commit-ID: 5m04vXUTlsy
browser/base/content/test/general/browser_selectpopup.js
devtools/server/tests/unit/test_profiler_getsharedlibraryinformation.js
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
security/nss/fuzz/tls_client_socket.cc
security/nss/fuzz/tls_client_socket.h
testing/web-platform/meta/webvtt/webvtt-file-format-parsing/webvtt-cue-text-parsing-rules/tests/entities.html.ini
testing/web-platform/meta/webvtt/webvtt-file-format-parsing/webvtt-cue-text-parsing-rules/tests/tags.html.ini
third_party/rust/encoding-index-japanese/.cargo-checksum.json
third_party/rust/encoding-index-japanese/.cargo-ok
third_party/rust/encoding-index-japanese/Cargo.toml
third_party/rust/encoding-index-japanese/jis0208.rs
third_party/rust/encoding-index-japanese/jis0212.rs
third_party/rust/encoding-index-japanese/lib.rs
third_party/rust/encoding-index-korean/.cargo-checksum.json
third_party/rust/encoding-index-korean/.cargo-ok
third_party/rust/encoding-index-korean/Cargo.toml
third_party/rust/encoding-index-korean/euc_kr.rs
third_party/rust/encoding-index-korean/lib.rs
third_party/rust/encoding-index-simpchinese/.cargo-checksum.json
third_party/rust/encoding-index-simpchinese/.cargo-ok
third_party/rust/encoding-index-simpchinese/Cargo.toml
third_party/rust/encoding-index-simpchinese/gb18030.rs
third_party/rust/encoding-index-simpchinese/gb18030_ranges.rs
third_party/rust/encoding-index-simpchinese/lib.rs
third_party/rust/encoding-index-singlebyte/.cargo-checksum.json
third_party/rust/encoding-index-singlebyte/.cargo-ok
third_party/rust/encoding-index-singlebyte/Cargo.toml
third_party/rust/encoding-index-singlebyte/ibm866.rs
third_party/rust/encoding-index-singlebyte/iso_8859_10.rs
third_party/rust/encoding-index-singlebyte/iso_8859_13.rs
third_party/rust/encoding-index-singlebyte/iso_8859_14.rs
third_party/rust/encoding-index-singlebyte/iso_8859_15.rs
third_party/rust/encoding-index-singlebyte/iso_8859_16.rs
third_party/rust/encoding-index-singlebyte/iso_8859_2.rs
third_party/rust/encoding-index-singlebyte/iso_8859_3.rs
third_party/rust/encoding-index-singlebyte/iso_8859_4.rs
third_party/rust/encoding-index-singlebyte/iso_8859_5.rs
third_party/rust/encoding-index-singlebyte/iso_8859_6.rs
third_party/rust/encoding-index-singlebyte/iso_8859_7.rs
third_party/rust/encoding-index-singlebyte/iso_8859_8.rs
third_party/rust/encoding-index-singlebyte/koi8_r.rs
third_party/rust/encoding-index-singlebyte/koi8_u.rs
third_party/rust/encoding-index-singlebyte/lib.rs
third_party/rust/encoding-index-singlebyte/macintosh.rs
third_party/rust/encoding-index-singlebyte/windows_1250.rs
third_party/rust/encoding-index-singlebyte/windows_1251.rs
third_party/rust/encoding-index-singlebyte/windows_1252.rs
third_party/rust/encoding-index-singlebyte/windows_1253.rs
third_party/rust/encoding-index-singlebyte/windows_1254.rs
third_party/rust/encoding-index-singlebyte/windows_1255.rs
third_party/rust/encoding-index-singlebyte/windows_1256.rs
third_party/rust/encoding-index-singlebyte/windows_1257.rs
third_party/rust/encoding-index-singlebyte/windows_1258.rs
third_party/rust/encoding-index-singlebyte/windows_874.rs
third_party/rust/encoding-index-singlebyte/x_mac_cyrillic.rs
third_party/rust/encoding-index-tradchinese/.cargo-checksum.json
third_party/rust/encoding-index-tradchinese/.cargo-ok
third_party/rust/encoding-index-tradchinese/Cargo.toml
third_party/rust/encoding-index-tradchinese/big5.rs
third_party/rust/encoding-index-tradchinese/lib.rs
third_party/rust/encoding/.cargo-checksum.json
third_party/rust/encoding/.cargo-ok
third_party/rust/encoding/.gitignore
third_party/rust/encoding/.travis.yml
third_party/rust/encoding/AUTHORS.txt
third_party/rust/encoding/Cargo.toml
third_party/rust/encoding/LICENSE.txt
third_party/rust/encoding/Makefile
third_party/rust/encoding/README.md
third_party/rust/encoding/examples/recode.rs
third_party/rust/encoding/src/all.rs
third_party/rust/encoding/src/codec/ascii.rs
third_party/rust/encoding/src/codec/error.rs
third_party/rust/encoding/src/codec/japanese.rs
third_party/rust/encoding/src/codec/korean.rs
third_party/rust/encoding/src/codec/simpchinese.rs
third_party/rust/encoding/src/codec/singlebyte.rs
third_party/rust/encoding/src/codec/tradchinese.rs
third_party/rust/encoding/src/codec/utf_16.rs
third_party/rust/encoding/src/codec/utf_8.rs
third_party/rust/encoding/src/codec/whatwg.rs
third_party/rust/encoding/src/examples/UTF-8-test.txt
third_party/rust/encoding/src/examples/outer-space-treaty.html
third_party/rust/encoding/src/index/gen_index.py
third_party/rust/encoding/src/label.rs
third_party/rust/encoding/src/lib.rs
third_party/rust/encoding/src/testutils.rs
third_party/rust/encoding/src/types.rs
third_party/rust/encoding/src/util.rs
third_party/rust/encoding_index_tests/.cargo-checksum.json
third_party/rust/encoding_index_tests/.cargo-ok
third_party/rust/encoding_index_tests/Cargo.toml
third_party/rust/encoding_index_tests/index_tests.rs
toolkit/mozapps/extensions/test/xpcshell/test_dss.js
tools/profiler/public/ProfilerBacktrace.h
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,11 +1,15 @@
 # Always ignore node_modules.
 **/node_modules/**/*.*
 
+# Include these to speed up ESLint, see bug 1347906.
+.hg
+.git
+
 # Exclude expected objdirs.
 obj*/**
 
 # We ignore all these directories by default, until we get them enabled.
 # If you are enabling a directory, please add directory specific exclusions
 # below.
 addon-sdk/**
 build/**
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -312,26 +312,23 @@ nsAccessibilityService::ListenersChanged
           listenerName != nsGkAtoms::onmousedown &&
           listenerName != nsGkAtoms::onmouseup) {
         continue;
       }
 
       nsIDocument* ownerDoc = node->OwnerDoc();
       DocAccessible* document = GetExistingDocAccessible(ownerDoc);
 
-      // Always recreate for onclick changes.
-      if (document) {
-        if (nsCoreUtils::HasClickListener(node)) {
-          if (!document->GetAccessible(node)) {
-            document->RecreateAccessible(node);
-          }
-        } else {
-          if (document->GetAccessible(node)) {
-            document->RecreateAccessible(node);
-          }
+      // Create an accessible for a inaccessible element having click event
+      // handler.
+      if (document && !document->HasAccessible(node) &&
+          nsCoreUtils::HasClickListener(node)) {
+        nsIContent* parentEl = node->GetFlattenedTreeParent();
+        if (parentEl) {
+          document->ContentInserted(parentEl, node, node->GetNextSibling());
         }
         break;
       }
     }
   }
   return NS_OK;
 }
 
--- a/accessible/tests/browser/e10s/browser_treeupdate_listener.js
+++ b/accessible/tests/browser/e10s/browser_treeupdate_listener.js
@@ -21,23 +21,9 @@ addAccessibleTask('<span id="parent"><sp
       content.window.dummyListener = () => {};
       content.document.getElementById('parent').addEventListener(
         'click', content.window.dummyListener);
     });
     yield onReorder;
 
     let tree = { TEXT: [] };
     testAccessibleTree(findAccessibleChildByID(accDoc, 'parent'), tree);
-
-    onReorder = waitForEvent(EVENT_REORDER, 'body');
-    // Remove an event listener from parent.
-    yield ContentTask.spawn(browser, {}, () => {
-      content.document.getElementById('parent').removeEventListener(
-        'click', content.window.dummyListener);
-      delete content.window.dummyListener;
-    });
-    yield onReorder;
-
-    is(findAccessibleChildByID(accDoc, 'parent'), null,
-      'Check that parent is not accessible.');
-    is(findAccessibleChildByID(accDoc, 'child'), null,
-      'Check that child is not accessible.');
   });
--- a/accessible/tests/mochitest/treeupdate/test_bug1175913.html
+++ b/accessible/tests/mochitest/treeupdate/test_bug1175913.html
@@ -42,28 +42,30 @@
       {
         return "Test that show event is sent when click listener is added";
       }
     }
 
     function testRemoveListener()
     {
       this.eventSeq = [
-        new invokerChecker(EVENT_HIDE, getNode("parent")),
+        new unexpectedInvokerChecker(EVENT_HIDE, getNode("parent")),
       ];
 
       this.invoke = function testRemoveListener_invoke()
       {
         getNode("parent").removeEventListener("click", dummyListener);
       }
 
       this.finalCheck = function testRemoveListener_finalCheck()
       {
-        is(getAccessible("parent", null, null, DONOTFAIL_IF_NO_ACC), null, "Check that parent is not accessible.");
-        is(getAccessible("child", null, null, DONOTFAIL_IF_NO_ACC), null, "Check that child is not accessible.");
+        ok(getAccessible("parent", null, null, DONOTFAIL_IF_NO_ACC),
+           "Parent stays accessible after click event listener is removed");
+        ok(!getAccessible("child", null, null, DONOTFAIL_IF_NO_ACC),
+           "Child stays inaccessible");
       }
 
       this.getID = function testRemoveListener_getID()
       {
         return "Test that hide event is sent when click listener is removed";
       }
     }
 
--- a/accessible/tests/mochitest/treeupdate/test_textleaf.html
+++ b/accessible/tests/mochitest/treeupdate/test_textleaf.html
@@ -38,32 +38,26 @@
       }
     }
 
     function setOnClickAttr(aID)
     {
       var node = getNode(aID);
       node.setAttribute("onclick", "alert(3);");
       var textLeaf = getAccessible(node).firstChild;
-      is(textLeaf.actionCount, 1, "Wrong action numbers!");
+      is(textLeaf.actionCount, 1, "setOnClickAttr: wrong action numbers!");
     }
 
     function removeOnClickAttr(aID)
     {
-      this.__proto__ = new textLeafUpdate(aID, false);
-
-      this.invoke = function removeOnClickAttr_invoke()
-      {
-        this.node.removeAttribute("onclick");
-      }
-
-      this.getID = function removeOnClickAttr_getID()
-      {
-        return "unmake " + prettyName(aID) + " linkable";
-      }
+      var node = getNode(aID);
+      node.removeAttribute("onclick");
+      var textLeaf = getAccessible(node).firstChild;
+      is(textLeaf.actionCount, 0,
+         "removeOnClickAttr: wrong action numbers!");
     }
 
     function setOnClickNRoleAttrs(aID)
     {
       this.__proto__ = new textLeafUpdate(aID, true);
 
       this.invoke = function setOnClickAttr_invoke()
       {
@@ -124,20 +118,21 @@
     //gA11yEventDumpToConsole = true;
 
     var gQueue = null;
 
     function doTest()
     {
       // adds onclick on element, text leaf should inherit its action
       setOnClickAttr("div");
+      // remove onclick attribute, text leaf shouldn't have any action
+      removeOnClickAttr("div");
+
       // Call rest of event tests.
       gQueue = new eventQueue();
-      // remove onclick attribute, text leaf shouldn't have any action
-      gQueue.push(new removeOnClickAttr("div"));
 
       // set onclick attribute making span accessible, it's inserted into tree
       // and adopts text leaf accessible, text leaf should have an action
       gQueue.push(new setOnClickNRoleAttrs("span"));
 
       // text data removal of text node should remove its text accessible
       gQueue.push(new removeTextData("p", ROLE_PARAGRAPH));
       gQueue.push(new removeTextData("pre", ROLE_TEXT_CONTAINER));
--- a/addon-sdk/source/test/test-clipboard.js
+++ b/addon-sdk/source/test/test-clipboard.js
@@ -2,85 +2,32 @@
  * 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";
 
 require("sdk/clipboard");
 
 const { Cc, Ci } = require("chrome");
 
-const imageTools = Cc["@mozilla.org/image/tools;1"].
-                    getService(Ci.imgITools);
+const imageTools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools);
+const io = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].getService(Ci.nsIAppShellService);
 
-const io = Cc["@mozilla.org/network/io-service;1"].
-                    getService(Ci.nsIIOService);
-
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
 const base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" +
                   "AABzenr0AAAASUlEQVRYhe3O0QkAIAwD0eyqe3Q993AQ3cBSUKpygfsNTy" +
                   "N5ugbQpK0BAADgP0BRDWXWlwEAAAAAgPsA3rzDaAAAAHgPcGrpgAnzQ2FG" +
                   "bWRR9AAAAABJRU5ErkJggg%3D%3D";
 
 const { base64jpeg } = require("./fixtures");
 
 const { platform } = require("sdk/system");
 // For Windows, Mac and Linux, platform returns the following: winnt, darwin and linux.
 var isWindows = platform.toLowerCase().indexOf("win") == 0;
 
-const canvasHTML = "data:text/html," + encodeURIComponent(
-  "<html>\
-    <body>\
-      <canvas width='32' height='32'></canvas>\
-    </body>\
-  </html>"
-);
-
-function comparePixelImages(imageA, imageB, callback) {
-  let tabs = require("sdk/tabs");
-
-  tabs.open({
-    url: canvasHTML,
-
-    onReady: function onReady(tab) {
-      let worker = tab.attach({
-        contentScript: "new " + function() {
-          let canvas = document.querySelector("canvas");
-          let context = canvas.getContext("2d");
-
-          self.port.on("draw-image", function(imageURI) {
-            let img = new Image();
-
-            img.onload = function() {
-              context.drawImage(this, 0, 0);
-
-              let pixels = Array.join(context.getImageData(0, 0, 32, 32).data);
-              self.port.emit("image-pixels", pixels);
-            }
-
-            img.src = imageURI;
-          });
-        }
-      });
-
-      let compared = "";
-
-      worker.port.on("image-pixels", function (pixels) {
-        if (!compared) {
-          compared = pixels;
-          this.emit("draw-image", imageB);
-        } else {
-          tab.close(callback.bind(null, compared === pixels))
-        }
-      });
-
-      worker.port.emit("draw-image", imageA);
-    }
-  });
-}
-
-
 // Test the typical use case, setting & getting with no flavors specified
 exports["test With No Flavor"] = function(assert) {
   var contents = "hello there";
   var flavor = "text";
   var fullFlavor = "text/unicode";
   var clip = require("sdk/clipboard");
 
   // Confirm we set the clipboard
@@ -155,30 +102,49 @@ exports["test Set Image"] = function(ass
   var clip = require("sdk/clipboard");
   var flavor = "image";
   var fullFlavor = "image/png";
 
   assert.ok(clip.set(base64png, flavor), "clipboard set");
   assert.equal(clip.currentFlavors[0], flavor, "flavor is set");
 };
 
-exports["test Get Image"] = function(assert, done) {
+exports["test Get Image"] = function* (assert) {
   var clip = require("sdk/clipboard");
 
   clip.set(base64png, "image");
 
   var contents = clip.get();
+  const hiddenWindow = appShellService.hiddenDOMWindow;
+  const Image = hiddenWindow.Image;
+  const canvas = hiddenWindow.document.createElementNS(XHTML_NS, "canvas");
+  let context = canvas.getContext("2d");
 
-  comparePixelImages(base64png, contents, function (areEquals) {
-    assert.ok(areEquals,
-      "Image gets from clipboard equals to image sets to the clipboard");
+  const imageURLToPixels = (imageURL) => {
+    return new Promise((resolve) => {
+      let img = new Image();
+
+      img.onload = function() {
+        context.drawImage(this, 0, 0);
+
+        let pixels = Array.join(context.getImageData(0, 0, 32, 32).data);
+        resolve(pixels);
+      };
 
-    done();
-  });
-}
+      img.src = imageURL;
+    });
+  };
+
+  let [base64pngPixels, clipboardPixels] = yield Promise.all([
+    imageURLToPixels(base64png), imageURLToPixels(contents),
+  ]);
+
+  assert.ok(base64pngPixels === clipboardPixels,
+            "Image gets from clipboard equals to image sets to the clipboard");
+};
 
 exports["test Set Image Type Not Supported"] = function(assert) {
   var clip = require("sdk/clipboard");
   var flavor = "image";
 
   assert.throws(function () {
     clip.set(base64jpeg, flavor);
   }, "Invalid flavor for image/jpeg");
--- a/addon-sdk/source/test/test-ui-action-button.js
+++ b/addon-sdk/source/test/test-ui-action-button.js
@@ -303,17 +303,17 @@ exports['test button global state update
   // Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it
   // was removed or it's not in the UX build yet
 
   let { node, id: widgetId } = getWidget(button.id);
 
   // check read-only properties
 
   assert.throws(() => button.id = 'another-id',
-    /^setting a property that has only a getter/,
+    /^setting getter-only property/,
     'id cannot be set at runtime');
 
   assert.equal(button.id, 'my-button-4',
     'id is unchanged');
   assert.equal(node.id, widgetId,
     'node id is unchanged');
 
   // check writable properties
--- a/addon-sdk/source/test/test-ui-sidebar.js
+++ b/addon-sdk/source/test/test-ui-sidebar.js
@@ -1045,17 +1045,17 @@ exports.testSidebarGettersAndSettersAfte
     url: url
   });
 
   sidebar.destroy();
 
   assert.equal(sidebar.id, undefined, 'sidebar after destroy has no id');
 
   assert.throws(() => sidebar.id = 'foo-tang',
-    /^setting a property that has only a getter/,
+    /^setting getter-only property/,
     'id cannot be set at runtime');
 
   assert.equal(sidebar.id, undefined, 'sidebar after destroy has no id');
 
   assert.equal(sidebar.title, undefined, 'sidebar after destroy has no title');
   sidebar.title = 'boo-tang';
   assert.equal(sidebar.title, undefined, 'sidebar after destroy has no title');
 
--- a/addon-sdk/source/test/test-ui-toggle-button.js
+++ b/addon-sdk/source/test/test-ui-toggle-button.js
@@ -297,17 +297,17 @@ exports['test button global state update
   // Tried to use `getWidgetIdsInArea` but seems undefined, not sure if it
   // was removed or it's not in the UX build yet
 
   let { node, id: widgetId } = getWidget(button.id);
 
   // check read-only properties
 
   assert.throws(() => button.id = 'another-id',
-    /^setting a property that has only a getter/,
+    /^setting getter-only property/,
     'id cannot be set at runtime');
 
   assert.equal(button.id, 'my-button-4',
     'id is unchanged');
   assert.equal(node.id, widgetId,
     'node id is unchanged');
 
   // check writable properties
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -884,19 +884,16 @@ pref("dom.apps.reviewer_paths", "/review
 
 // New implementation to unify touch-caret and selection-carets.
 pref("layout.accessiblecaret.enabled", true);
 
 // Show the selection bars at the two ends of the selection highlight. Required
 // by the spec in bug 921965.
 pref("layout.accessiblecaret.bar.enabled", true);
 
-// Hide the caret in cursor mode after 3 seconds.
-pref("layout.accessiblecaret.timeout_ms", 3000);
-
 // Hide carets and text selection dialog during scrolling.
 pref("layout.accessiblecaret.always_show_when_scrolling", false);
 
 // Enable sync with Firefox Accounts.
 pref("services.sync.fxaccounts.enabled", true);
 pref("identity.fxaccounts.enabled", true);
 
 pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
--- a/b2g/config/aries/releng-aries.manifest
+++ b/b2g/config/aries/releng-aries.manifest
@@ -1,27 +1,27 @@
 [
-{
-"version": "Android NDK r11b for B2G",
-"algorithm": "sha512",
-"visibility": "internal",
-"filename": "android-ndk-b2g.tar.xz",
-"unpack": true,
-"digest": "bc37c6b2e38f4ff19e3326786312d8f893600e155d35dfba45163bd909e022db852b9c6920863cb498bbe7da8b86a6a387fa024bc9444ce3a8d1715cf2c24b21",
-"size": 292442020
-},
-{
-"algorithm": "sha512",
-"visibility": "internal",
-"filename": "backup-aries_23.0.1.A.5.77.tar.xz",
-"unpack": true,
-"digest": "79c8e390e88cc4765ff7f5f29f3d5337c9037b7eb9414006947d38d34acefdbcf7090c18a366948c682b1c2c9d9ef51012e7be44daa28fdde7b837ade647c257",
-"size": 227555180
-},
-{
-"version": "gcc 4.8.5 + PR64905",
-"size": 80160264,
-"digest": "c1a9dc9da289b8528874d16300b9d13a997cec99195bb0bc46ff665216d8535d6d6cb5af6b4b1f2749af6815dab12e703fdb3849014e5c23a70eff351a0baf4e",
-"algorithm": "sha512",
-"filename": "gcc.tar.xz",
-"unpack": "True"
-}
+  {
+    "version": "Android NDK r11b for B2G",
+    "algorithm": "sha512",
+    "visibility": "internal",
+    "filename": "android-ndk-b2g.tar.xz",
+    "unpack": true,
+    "digest": "bc37c6b2e38f4ff19e3326786312d8f893600e155d35dfba45163bd909e022db852b9c6920863cb498bbe7da8b86a6a387fa024bc9444ce3a8d1715cf2c24b21",
+    "size": 292442020
+  },
+  {
+    "algorithm": "sha512",
+    "visibility": "internal",
+    "filename": "backup-aries_23.0.1.A.5.77.tar.xz",
+    "unpack": true,
+    "digest": "79c8e390e88cc4765ff7f5f29f3d5337c9037b7eb9414006947d38d34acefdbcf7090c18a366948c682b1c2c9d9ef51012e7be44daa28fdde7b837ade647c257",
+    "size": 227555180
+  },
+  {
+    "version": "gcc 4.8.5 + PR64905",
+    "size": 80160264,
+    "digest": "c1a9dc9da289b8528874d16300b9d13a997cec99195bb0bc46ff665216d8535d6d6cb5af6b4b1f2749af6815dab12e703fdb3849014e5c23a70eff351a0baf4e",
+    "algorithm": "sha512",
+    "filename": "gcc.tar.xz",
+    "unpack": "True"
+  }
 ]
--- a/b2g/config/nexus-5-l/releng-nexus5.manifest
+++ b/b2g/config/nexus-5-l/releng-nexus5.manifest
@@ -1,19 +1,19 @@
 [
-{
-"version": "Android NDK r11b for B2G",
-"algorithm": "sha512",
-"visibility": "internal",
-"filename": "android-ndk-b2g.tar.xz",
-"unpack": true,
-"digest": "bc37c6b2e38f4ff19e3326786312d8f893600e155d35dfba45163bd909e022db852b9c6920863cb498bbe7da8b86a6a387fa024bc9444ce3a8d1715cf2c24b21",
-"size": 292442020
-},
-{
-"version": "gcc 4.8.5 + PR64905",
-"size": 80160264,
-"digest": "c1a9dc9da289b8528874d16300b9d13a997cec99195bb0bc46ff665216d8535d6d6cb5af6b4b1f2749af6815dab12e703fdb3849014e5c23a70eff351a0baf4e",
-"algorithm": "sha512",
-"filename": "gcc.tar.xz",
-"unpack": "True"
-}
+  {
+    "version": "Android NDK r11b for B2G",
+    "algorithm": "sha512",
+    "visibility": "internal",
+    "filename": "android-ndk-b2g.tar.xz",
+    "unpack": true,
+    "digest": "bc37c6b2e38f4ff19e3326786312d8f893600e155d35dfba45163bd909e022db852b9c6920863cb498bbe7da8b86a6a387fa024bc9444ce3a8d1715cf2c24b21",
+    "size": 292442020
+  },
+  {
+    "version": "gcc 4.8.5 + PR64905",
+    "size": 80160264,
+    "digest": "c1a9dc9da289b8528874d16300b9d13a997cec99195bb0bc46ff665216d8535d6d6cb5af6b4b1f2749af6815dab12e703fdb3849014e5c23a70eff351a0baf4e",
+    "algorithm": "sha512",
+    "filename": "gcc.tar.xz",
+    "unpack": "True"
+  }
 ]
--- a/b2g/config/tooltool-manifests/linux32/releng.manifest
+++ b/b2g/config/tooltool-manifests/linux32/releng.manifest
@@ -1,32 +1,32 @@
 [
-{
-"version": "gcc 4.8.5 + PR64905",
-"size": 80160264,
-"digest": "c1a9dc9da289b8528874d16300b9d13a997cec99195bb0bc46ff665216d8535d6d6cb5af6b4b1f2749af6815dab12e703fdb3849014e5c23a70eff351a0baf4e",
-"algorithm": "sha512",
-"filename": "gcc.tar.xz",
-"unpack": true
-},
-{
-"size": 11189216,
-"digest": "18bc52b0599b1308b667e282abb45f47597bfc98a5140cfcab8da71dacf89dd76d0dee22a04ce26fe7ad1f04e2d6596991f9e5b01fd2aaaab5542965f596b0e6",
-"algorithm": "sha512",
-"filename": "gtk3.tar.xz",
-"setup": "setup.sh",
-"unpack": true
-},
-{
-"size": 167175,
-"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
-"algorithm": "sha512",
-"filename": "sccache.tar.bz2",
-"unpack": true
-},
-{
-"size": 31078810,
-"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
-"algorithm": "sha512",
-"filename": "moz-tt.tar.bz2",
-"unpack": true
-}
+  {
+    "version": "gcc 4.8.5 + PR64905",
+    "size": 80160264,
+    "digest": "c1a9dc9da289b8528874d16300b9d13a997cec99195bb0bc46ff665216d8535d6d6cb5af6b4b1f2749af6815dab12e703fdb3849014e5c23a70eff351a0baf4e",
+    "algorithm": "sha512",
+    "filename": "gcc.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 11189216,
+    "digest": "18bc52b0599b1308b667e282abb45f47597bfc98a5140cfcab8da71dacf89dd76d0dee22a04ce26fe7ad1f04e2d6596991f9e5b01fd2aaaab5542965f596b0e6",
+    "algorithm": "sha512",
+    "filename": "gtk3.tar.xz",
+    "setup": "setup.sh",
+    "unpack": true
+  },
+  {
+    "size": 167175,
+    "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+    "algorithm": "sha512",
+    "filename": "sccache.tar.bz2",
+    "unpack": true
+  },
+  {
+    "size": 31078810,
+    "digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
+    "algorithm": "sha512",
+    "filename": "moz-tt.tar.bz2",
+    "unpack": true
+  }
 ]
--- a/b2g/config/tooltool-manifests/macosx64/releng.manifest
+++ b/b2g/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,24 +1,24 @@
 [
-{
-"version": "clang 3.8.0",
-"size": 133060926,
-"digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
-"algorithm": "sha512",
-"filename": "clang.tar.bz2",
-"unpack": true
-},
-{
-"size": 167175,
-"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
-"algorithm": "sha512",
-"filename": "sccache.tar.bz2",
-"unpack": true
-},
-{
-"size": 31078810,
-"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
-"algorithm": "sha512",
-"filename": "moz-tt.tar.bz2",
-"unpack": true
-}
+  {
+    "version": "clang 3.8.0",
+    "size": 133060926,
+    "digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
+    "algorithm": "sha512",
+    "filename": "clang.tar.bz2",
+    "unpack": true
+  },
+  {
+    "size": 167175,
+    "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+    "algorithm": "sha512",
+    "filename": "sccache.tar.bz2",
+    "unpack": true
+  },
+  {
+    "size": 31078810,
+    "digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
+    "algorithm": "sha512",
+    "filename": "moz-tt.tar.bz2",
+    "unpack": true
+  }
 ]
--- a/b2g/config/tooltool-manifests/win32/releng.manifest
+++ b/b2g/config/tooltool-manifests/win32/releng.manifest
@@ -1,22 +1,22 @@
 [
-{
-"size": 266240,
-"digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
-"algorithm": "sha512",
-"filename": "mozmake.exe"
-},
-{
-"size": 31078810,
-"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
-"algorithm": "sha512",
-"filename": "moz-tt.tar.bz2",
-"unpack": true
-},
-{
-"size": 167175,
-"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
-"algorithm": "sha512",
-"filename": "sccache.tar.bz2",
-"unpack": true
-}
+  {
+    "size": 266240,
+    "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
+    "algorithm": "sha512",
+    "filename": "mozmake.exe"
+  },
+  {
+    "size": 31078810,
+    "digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
+    "algorithm": "sha512",
+    "filename": "moz-tt.tar.bz2",
+    "unpack": true
+  },
+  {
+    "size": 167175,
+    "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+    "algorithm": "sha512",
+    "filename": "sccache.tar.bz2",
+    "unpack": true
+  }
 ]
--- a/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest
+++ b/b2g/dev/config/tooltool-manifests/linux64/hazard.manifest
@@ -1,48 +1,48 @@
 [
-{
-"size": 102421980,
-"digest": "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921",
-"version": "gcc 4.9.3",
-"unpack": true,
-"filename": "gcc.tar.xz",
-"algorithm": "sha512"
-},
-{
-"unpack": true,
-"algorithm": "sha512",
-"filename": "sixgill.tar.xz",
-"hg_id": "8cb9c3fb039a+ tip",
-"digest": "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029",
-"size": 2631908
-},
-{
-"algorithm": "sha512",
-"filename": "gtk3.tar.xz",
-"setup": "setup.sh",
-"unpack": true,
-"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-"size": 12072532
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 110077036,
-"digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
-"algorithm": "sha512",
-"filename": "rustc.tar.xz",
-"unpack": true
-},
-{
-"algorithm": "sha512",
-"filename": "sccache.tar.bz2",
-"unpack": true,
-"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
-"size": 167175
-},
-{
-"filename": "moz-tt.tar.bz2",
-"algorithm": "sha512",
-"unpack": true,
-"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
-"size": 31078810
-}
+  {
+    "size": 102421980,
+    "digest": "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921",
+    "version": "gcc 4.9.3",
+    "unpack": true,
+    "filename": "gcc.tar.xz",
+    "algorithm": "sha512"
+  },
+  {
+    "unpack": true,
+    "algorithm": "sha512",
+    "filename": "sixgill.tar.xz",
+    "hg_id": "8cb9c3fb039a+ tip",
+    "digest": "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029",
+    "size": 2631908
+  },
+  {
+    "algorithm": "sha512",
+    "filename": "gtk3.tar.xz",
+    "setup": "setup.sh",
+    "unpack": true,
+    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
+    "size": 12072532
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 110077036,
+    "digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.xz",
+    "unpack": true
+  },
+  {
+    "algorithm": "sha512",
+    "filename": "sccache.tar.bz2",
+    "unpack": true,
+    "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+    "size": 167175
+  },
+  {
+    "filename": "moz-tt.tar.bz2",
+    "algorithm": "sha512",
+    "unpack": true,
+    "digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
+    "size": 31078810
+  }
 ]
--- a/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
@@ -1,40 +1,40 @@
 [
-{
-"version": "gcc 4.9.3",
-"size": 102421980,
-"digest": "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921",
-"algorithm": "sha512",
-"filename": "gcc.tar.xz",
-"unpack": true
-},
-{
-"size": 12072532,
-"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-"algorithm": "sha512",
-"filename": "gtk3.tar.xz",
-"setup": "setup.sh",
-"unpack": true
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 110077036,
-"digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
-"algorithm": "sha512",
-"filename": "rustc.tar.xz",
-"unpack": true
-},
-{
-"size": 167175,
-"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
-"algorithm": "sha512",
-"filename": "sccache.tar.bz2",
-"unpack": true
-},
-{
-"size": 31078810,
-"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
-"algorithm": "sha512",
-"filename": "moz-tt.tar.bz2",
-"unpack": true
-}
+  {
+    "version": "gcc 4.9.3",
+    "size": 102421980,
+    "digest": "f25292aa93dc449e0472eee511c0ac15b5f1a4272ab76cf53ce5d20dc57f29e83da49ae1a9d9e994192647f75e13ae60f75ba2ac3cb9d26d5f5d6cabf88de921",
+    "algorithm": "sha512",
+    "filename": "gcc.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 12072532,
+    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
+    "algorithm": "sha512",
+    "filename": "gtk3.tar.xz",
+    "setup": "setup.sh",
+    "unpack": true
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 110077036,
+    "digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 167175,
+    "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+    "algorithm": "sha512",
+    "filename": "sccache.tar.bz2",
+    "unpack": true
+  },
+  {
+    "size": 31078810,
+    "digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
+    "algorithm": "sha512",
+    "filename": "moz-tt.tar.bz2",
+    "unpack": true
+  }
 ]
--- a/b2g/dev/config/tooltool-manifests/macosx64/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,24 +1,24 @@
 [
-{
-"version": "clang 3.8.0",
-"size": 133060926,
-"digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
-"algorithm": "sha512",
-"filename": "clang.tar.bz2",
-"unpack": true
-},
-{
-"size": 167175,
-"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
-"algorithm": "sha512",
-"filename": "sccache.tar.bz2",
-"unpack": true
-},
-{
-"size": 31078810,
-"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
-"algorithm": "sha512",
-"filename": "moz-tt.tar.bz2",
-"unpack": true
-}
+  {
+    "version": "clang 3.8.0",
+    "size": 133060926,
+    "digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
+    "algorithm": "sha512",
+    "filename": "clang.tar.bz2",
+    "unpack": true
+  },
+  {
+    "size": 167175,
+    "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+    "algorithm": "sha512",
+    "filename": "sccache.tar.bz2",
+    "unpack": true
+  },
+  {
+    "size": 31078810,
+    "digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
+    "algorithm": "sha512",
+    "filename": "moz-tt.tar.bz2",
+    "unpack": true
+  }
 ]
--- a/b2g/dev/config/tooltool-manifests/win32/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/win32/releng.manifest
@@ -1,22 +1,22 @@
 [
-{
-"size": 266240,
-"digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
-"algorithm": "sha512",
-"filename": "mozmake.exe"
-},
-{
-"size": 167175,
-"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
-"algorithm": "sha512",
-"filename": "sccache.tar.bz2",
-"unpack": true
-},
-{
-"size": 31078810,
-"digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
-"algorithm": "sha512",
-"filename": "moz-tt.tar.bz2",
-"unpack": true
-}
+  {
+    "size": 266240,
+    "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
+    "algorithm": "sha512",
+    "filename": "mozmake.exe"
+  },
+  {
+    "size": 167175,
+    "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+    "algorithm": "sha512",
+    "filename": "sccache.tar.bz2",
+    "unpack": true
+  },
+  {
+    "size": 31078810,
+    "digest": "2dffe4e5419a0c0c9908dc52b01cc07379a42e2aa8481be7a26bb8750b586b95bbac3fe57e64f5d37b43e206516ea70ad938a2e45858fdcf1e28258e70ae8d8c",
+    "algorithm": "sha512",
+    "filename": "moz-tt.tar.bz2",
+    "unpack": true
+  }
 ]
--- a/b2g/test/emulator.manifest
+++ b/b2g/test/emulator.manifest
@@ -1,6 +1,8 @@
-[{
-"size": 746441603,
-"digest": "199236aefecc1657cdc1b791ec38c8184557ab9249aff9c63a74abf73edc1dc0ea36b19b558f34ca3b14f8a511b10bcf37408b19701929522b4dc22dbaddcbe9",
-"algorithm": "sha512",
-"filename": "emulator.zip"
-}]
+[
+  {
+    "size": 746441603,
+    "digest": "199236aefecc1657cdc1b791ec38c8184557ab9249aff9c63a74abf73edc1dc0ea36b19b558f34ca3b14f8a511b10bcf37408b19701929522b4dc22dbaddcbe9",
+    "algorithm": "sha512",
+    "filename": "emulator.zip"
+  }
+]
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1717,17 +1717,17 @@
     <emItem blockID="i720" id="FXqG@xeeR.net">
       <prefs>
         <pref>browser.startup.homepage</pref>
       </prefs>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <emItem blockID="d33f6d48-a555-49dd-96ff-8d75473403a8" id="mozilla_cc2@internetdownloadmanager.com">
       <prefs/>
-      <versionRange minVersion="0" maxVersion="6.26.11" severity="1">
+      <versionRange minVersion="0" maxVersion="6.26.11" severity="3">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="53.0a1"/>
         </targetApplication>
       </versionRange>
     </emItem>
     <emItem blockID="i493" id="12x3q@3244516.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -157,17 +157,16 @@ pref("app.update.service.enabled", true)
 //  .. etc ..
 //
 pref("extensions.update.enabled", true);
 pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
 pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
 pref("extensions.update.interval", 86400);  // Check for updates to Extensions and
                                             // Themes every day
 // Non-symmetric (not shared by extensions) extension-specific [update] preferences
-pref("extensions.dss.enabled", false);          // Dynamic Skin Switching
 pref("extensions.dss.switchPending", false);    // Non-dynamic switch pending after next
                                                 // restart.
 
 pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.name", "chrome://browser/locale/browser.properties");
 pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description", "chrome://browser/locale/browser.properties");
 
 pref("extensions.webextensions.themes.icons.buttons", "back,forward,reload,stop,bookmark_star,bookmark_menu,downloads,home,app_menu,cut,copy,paste,new_window,new_private_window,save_page,print,history,full_screen,find,options,addons,developer,synced_tabs,open_file,sidebars,share_page,subscribe,text_encoding,email_link,forget,pocket");
 
@@ -1251,20 +1250,20 @@ pref("plain_text.wrap_long_lines", true)
 
 // If this turns true, Moz*Gesture events are not called stopPropagation()
 // before content.
 pref("dom.debug.propagate_gesture_events_through_content", false);
 
 // All the Geolocation preferences are here.
 //
 
-// Geolocation preferences for the RELEASE channel.
+// Geolocation preferences for the RELEASE and "later" Beta channels.
 // Some of these prefs are specified even though they are redundant; they are
 // here for clarity and end-user experiments.
-#ifdef RELEASE
+#ifndef EARLY_BETA_OR_EARLIER
 pref("geo.wifi.uri", "https://www.googleapis.com/geolocation/v1/geolocate?key=%GOOGLE_API_KEY%");
 
 #ifdef XP_MACOSX
 pref("geo.provider.use_corelocation", false);
 #endif
 
 #ifdef XP_WIN
 pref("geo.provider.ms-windows-location", false);
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -250,17 +250,17 @@
 
         window.addEventListener("AboutNetErrorOptions", function(evt) {
         // Pinning errors are of type nssFailure2
           if (getErrorCode() == "nssFailure2") {
             document.getElementById("learnMoreContainer").style.display = "block";
             let learnMoreLink = document.getElementById("learnMoreLink");
             // nssFailure2 also gets us other non-overrideable errors. Choose
             // a "learn more" link based on description:
-            if (getDescription().includes("mozilla_pkix_error_key_pinning_failure")) {
+            if (getDescription().includes("MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE")) {
               learnMoreLink.href = "https://support.mozilla.org/kb/certificate-pinning-reports";
             }
 
             var options = JSON.parse(evt.detail);
             if (options && options.enabled) {
               var checkbox = document.getElementById("automaticallyReportInFuture");
               showCertificateErrorReporting();
               if (options.automatic) {
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -567,17 +567,17 @@ toolbar:not(#TabsToolbar) > #personal-bo
 #PopupAutoCompleteRichResult > richlistbox {
   transition: height 100ms;
 }
 
 #PopupAutoCompleteRichResult.showSearchSuggestionsNotification > richlistbox {
   transition: none;
 }
 
-#DateTimePickerPanel {
+#DateTimePickerPanel[active="true"] {
   -moz-binding: url("chrome://global/content/bindings/datetimepopup.xml#datetime-popup");
 }
 
 #urlbar[pageproxystate="invalid"] > #urlbar-icons > .urlbar-icon,
 #urlbar[pageproxystate="invalid"][focused="true"] > #urlbar-go-button ~ toolbarbutton,
 #urlbar[pageproxystate="valid"] > #urlbar-go-button,
 #urlbar:not([focused="true"]) > #urlbar-go-button {
   visibility: collapse;
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/forms/browser.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+support-files =
+  head.js
+
+[browser_selectpopup.js]
+skip-if = os == "linux" # Bug 1329991 - test fails intermittently on Linux builds
+[browser_selectpopup_colors.js]
+skip-if = os == "linux" # Bug 1329991 - test fails intermittently on Linux builds
rename from browser/base/content/test/general/browser_selectpopup.js
rename to browser/base/content/test/forms/browser_selectpopup.js
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/forms/browser_selectpopup.js
@@ -74,62 +74,16 @@ const PAGECONTENT_SOMEHIDDEN =
 const PAGECONTENT_TRANSLATED =
   "<html><body>" +
   "<div id='div'>" +
   "<iframe id='frame' width='320' height='295' style='border: none;'" +
   "        src='data:text/html,<select id=select autofocus><option>he he he</option><option>boo boo</option><option>baz baz</option></select>'" +
   "</iframe>" +
   "</div></body></html>";
 
-const PAGECONTENT_COLORS =
-  "<html><head><style>" +
-  "  .blue { color: #fff; background-color: #00f; }" +
-  "  .green { color: #800080; background-color: green; }" +
-  "  .defaultColor { color: -moz-ComboboxText; }" +
-  "  .defaultBackground { background-color: -moz-Combobox; }" +
-  "</style>" +
-  "<body><select id='one'>" +
-  '  <option value="One" style="color: #fff; background-color: #f00;">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgb(255, 0, 0)"}</option>' +
-  '  <option value="Two" class="blue">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgb(0, 0, 255)"}</option>' +
-  '  <option value="Three" class="green">{"color": "rgb(128, 0, 128)", "backgroundColor": "rgb(0, 128, 0)"}</option>' +
-  '  <option value="Four" class="defaultColor defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "rgba(0, 0, 0, 0)", "unstyled": "true"}</option>' +
-  '  <option value="Five" class="defaultColor">{"color": "-moz-ComboboxText", "backgroundColor": "rgba(0, 0, 0, 0)", "unstyled": "true"}</option>' +
-  '  <option value="Six" class="defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "rgba(0, 0, 0, 0)", "unstyled": "true"}</option>' +
-  '  <option value="Seven" selected="true">{"unstyled": "true"}</option>' +
-  "</select></body></html>";
-
-const PAGECONTENT_COLORS_ON_SELECT =
-  "<html><head><style>" +
-  "  #one { background-color: #7E3A3A; color: #fff }" +
-  "</style>" +
-  "<body><select id='one'>" +
-  '  <option value="One">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
-  '  <option value="Two">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
-  '  <option value="Three">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
-  '  <option value="Four" selected="true">{"end": "true"}</option>' +
-  "</select></body></html>";
-
-const TRANSPARENT_SELECT =
-  "<html><head><style>" +
-  "  #one { background-color: transparent; }" +
-  "</style>" +
-  "<body><select id='one'>" +
-  '  <option value="One">{"unstyled": "true"}</option>' +
-  '  <option value="Two" selected="true">{"end": "true"}</option>' +
-  "</select></body></html>";
-
-const OPTION_COLOR_EQUAL_TO_UABACKGROUND_COLOR_SELECT =
-  "<html><head><style>" +
-  "  #one { background-color: black; color: white; }" +
-  "</style>" +
-  "<body><select id='one'>" +
-  '  <option value="One" style="background-color: white; color: black;">{"color": "rgb(0, 0, 0)", "backgroundColor": "rgb(255, 255, 255)"}</option>' +
-  '  <option value="Two" selected="true">{"end": "true"}</option>' +
-  "</select></body></html>";
-
 function openSelectPopup(selectPopup, mode = "key", selector = "select", win = window) {
   let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
 
   if (mode == "click" || mode == "mousedown") {
     let mousePromise;
     if (mode == "click") {
       mousePromise = BrowserTestUtils.synthesizeMouseAtCenter(selector, { }, win.gBrowser.selectedBrowser);
     } else {
@@ -138,34 +92,16 @@ function openSelectPopup(selectPopup, mo
 
     return Promise.all([popupShownPromise, mousePromise]);
   }
 
   EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true, code: "ArrowDown" }, win);
   return popupShownPromise;
 }
 
-function hideSelectPopup(selectPopup, mode = "enter", win = window) {
-  let browser = win.gBrowser.selectedBrowser;
-  let selectClosedPromise = ContentTask.spawn(browser, null, function*() {
-    Cu.import("resource://gre/modules/SelectContentHelper.jsm");
-    return ContentTaskUtils.waitForCondition(() => !SelectContentHelper.open);
-  });
-
-  if (mode == "escape") {
-    EventUtils.synthesizeKey("KEY_Escape", { code: "Escape" }, win);
-  } else if (mode == "enter") {
-    EventUtils.synthesizeKey("KEY_Enter", { code: "Enter" }, win);
-  } else if (mode == "click") {
-    EventUtils.synthesizeMouseAtCenter(selectPopup.lastChild, { }, win);
-  }
-
-  return selectClosedPromise;
-}
-
 function getInputEvents() {
   return ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
     return content.wrappedJSObject.gInputEvents;
   });
 }
 
 function getChangeEvents() {
   return ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
@@ -174,54 +110,16 @@ function getChangeEvents() {
 }
 
 function getClickEvents() {
   return ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
     return content.wrappedJSObject.gClickEvents;
   });
 }
 
-function getSystemColor(color) {
-  // Need to convert system color to RGB color.
-  let textarea = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea");
-  textarea.style.color = color;
-  return getComputedStyle(textarea).color;
-}
-
-function testOptionColors(index, item, menulist) {
-  // The label contains a JSON string of the expected colors for
-  // `color` and `background-color`.
-  let expected = JSON.parse(item.label);
-
-  for (let color of Object.keys(expected)) {
-    if (color.toLowerCase().includes("color") &&
-        !expected[color].startsWith("rgb")) {
-      expected[color] = getSystemColor(expected[color]);
-    }
-  }
-
-  // Press Down to move the selected item to the next item in the
-  // list and check the colors of this item when it's not selected.
-  EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
-
-  if (expected.end) {
-    return;
-  }
-
-  if (expected.unstyled) {
-    ok(!item.hasAttribute("customoptionstyling"),
-      `Item ${index} should not have any custom option styling`);
-  } else {
-    is(getComputedStyle(item).color, expected.color,
-       "Item " + (index) + " has correct foreground color");
-    is(getComputedStyle(item).backgroundColor, expected.backgroundColor,
-       "Item " + (index) + " has correct background color");
-  }
-}
-
 function* doSelectTests(contentType, dtd) {
   const pageUrl = "data:" + contentType + "," + escape(dtd + "\n" + PAGECONTENT);
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
 
   let menulist = document.getElementById("ContentSelectDropdown");
   let selectPopup = menulist.menupopup;
 
   yield openSelectPopup(selectPopup);
@@ -825,111 +723,16 @@ add_task(function* test_somehidden() {
        "Item " + (idx++) + " is visible");
     child = child.nextSibling;
   }
 
   yield hideSelectPopup(selectPopup, "escape");
   yield BrowserTestUtils.removeTab(tab);
 });
 
-// This test checks when a <select> element has styles applied to <option>s within it.
-add_task(function* test_colors_applied_to_popup_items() {
-  const pageUrl = "data:text/html," + escape(PAGECONTENT_COLORS);
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
-
-  let menulist = document.getElementById("ContentSelectDropdown");
-  let selectPopup = menulist.menupopup;
-
-  let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
-  yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
-  yield popupShownPromise;
-
-  // The label contains a JSON string of the expected colors for
-  // `color` and `background-color`.
-  is(selectPopup.parentNode.itemCount, 7, "Correct number of items");
-  let child = selectPopup.firstChild;
-  let idx = 1;
-
-  ok(!child.selected, "The first child should not be selected");
-  while (child) {
-    testOptionColors(idx, child, menulist);
-    idx++;
-    child = child.nextSibling;
-  }
-
-  yield hideSelectPopup(selectPopup, "escape");
-  yield BrowserTestUtils.removeTab(tab);
-});
-
-// This test checks when a <select> element has styles applied to itself.
-add_task(function* test_colors_applied_to_popup() {
-  const pageUrl = "data:text/html," + escape(PAGECONTENT_COLORS_ON_SELECT);
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
-
-  let menulist = document.getElementById("ContentSelectDropdown");
-  let selectPopup = menulist.menupopup;
-
-  let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
-  yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
-  yield popupShownPromise;
-
-  is(selectPopup.parentNode.itemCount, 4, "Correct number of items");
-  let child = selectPopup.firstChild;
-  let idx = 1;
-
-  is(getComputedStyle(selectPopup).color, "rgb(255, 255, 255)",
-    "popup has expected foreground color");
-  is(getComputedStyle(selectPopup).backgroundColor, "rgb(126, 58, 58)",
-    "popup has expected background color");
-
-  ok(!child.selected, "The first child should not be selected");
-  while (child) {
-    testOptionColors(idx, child, menulist);
-    idx++;
-    child = child.nextSibling;
-  }
-
-  yield hideSelectPopup(selectPopup, "escape");
-
-  yield BrowserTestUtils.removeTab(tab);
-});
-
-// This test checks when a <select> element has a transparent background applied to itself.
-add_task(function* test_transparent_applied_to_popup() {
-  const pageUrl = "data:text/html," + escape(TRANSPARENT_SELECT);
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
-
-  let menulist = document.getElementById("ContentSelectDropdown");
-  let selectPopup = menulist.menupopup;
-
-  let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
-  yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
-  yield popupShownPromise;
-
-  is(selectPopup.parentNode.itemCount, 2, "Correct number of items");
-  let child = selectPopup.firstChild;
-  let idx = 1;
-
-  is(getComputedStyle(selectPopup).color, getSystemColor("-moz-ComboboxText"),
-    "popup has expected foreground color");
-  is(getComputedStyle(selectPopup).backgroundColor, getSystemColor("-moz-Combobox"),
-    "popup has expected background color");
-
-  ok(!child.selected, "The first child should not be selected");
-  while (child) {
-    testOptionColors(idx, child, menulist);
-    idx++;
-    child = child.nextSibling;
-  }
-
-  yield hideSelectPopup(selectPopup, "escape");
-
-  yield BrowserTestUtils.removeTab(tab);
-});
-
 // This test checks that the popup is closed when the select element is blurred.
 add_task(function* test_blur_hides_popup() {
   const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
 
   yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
     content.addEventListener("blur", function(event) {
       event.preventDefault();
@@ -951,46 +754,8 @@ add_task(function* test_blur_hides_popup
   });
 
   yield popupHiddenPromise;
 
   ok(true, "Blur closed popup");
 
   yield BrowserTestUtils.removeTab(tab);
 });
-
-// This test checks when a <select> element has a background set, and the
-// options have their own background set which is equal to the default
-// user-agent background color, but should be used because the select
-// background color has been changed.
-add_task(function* test_options_inverted_from_select_background() {
-  const pageUrl = "data:text/html," + escape(OPTION_COLOR_EQUAL_TO_UABACKGROUND_COLOR_SELECT);
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
-
-  let menulist = document.getElementById("ContentSelectDropdown");
-  let selectPopup = menulist.menupopup;
-
-  let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
-  yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
-  yield popupShownPromise;
-
-  is(selectPopup.parentNode.itemCount, 2, "Correct number of items");
-  let child = selectPopup.firstChild;
-  let idx = 1;
-
-  // The popup has a black background and white text, but the
-  // options inside of it have flipped the colors.
-  is(getComputedStyle(selectPopup).color, "rgb(255, 255, 255)",
-    "popup has expected foreground color");
-  is(getComputedStyle(selectPopup).backgroundColor, "rgb(0, 0, 0)",
-    "popup has expected background color");
-
-  ok(!child.selected, "The first child should not be selected");
-  while (child) {
-    testOptionColors(idx, child, menulist);
-    idx++;
-    child = child.nextSibling;
-  }
-
-  yield hideSelectPopup(selectPopup, "escape");
-
-  yield BrowserTestUtils.removeTab(tab);
-});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/forms/browser_selectpopup_colors.js
@@ -0,0 +1,236 @@
+const PAGECONTENT_COLORS =
+  "<html><head><style>" +
+  "  .blue { color: #fff; background-color: #00f; }" +
+  "  .green { color: #800080; background-color: green; }" +
+  "  .defaultColor { color: -moz-ComboboxText; }" +
+  "  .defaultBackground { background-color: -moz-Combobox; }" +
+  "</style>" +
+  "<body><select id='one'>" +
+  '  <option value="One" style="color: #fff; background-color: #f00;">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgb(255, 0, 0)"}</option>' +
+  '  <option value="Two" class="blue">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgb(0, 0, 255)"}</option>' +
+  '  <option value="Three" class="green">{"color": "rgb(128, 0, 128)", "backgroundColor": "rgb(0, 128, 0)"}</option>' +
+  '  <option value="Four" class="defaultColor defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "rgba(0, 0, 0, 0)", "unstyled": "true"}</option>' +
+  '  <option value="Five" class="defaultColor">{"color": "-moz-ComboboxText", "backgroundColor": "rgba(0, 0, 0, 0)", "unstyled": "true"}</option>' +
+  '  <option value="Six" class="defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "rgba(0, 0, 0, 0)", "unstyled": "true"}</option>' +
+  '  <option value="Seven" selected="true">{"unstyled": "true"}</option>' +
+  "</select></body></html>";
+
+const PAGECONTENT_COLORS_ON_SELECT =
+  "<html><head><style>" +
+  "  #one { background-color: #7E3A3A; color: #fff }" +
+  "</style>" +
+  "<body><select id='one'>" +
+  '  <option value="One">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '  <option value="Two">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '  <option value="Three">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '  <option value="Four" selected="true">{"end": "true"}</option>' +
+  "</select></body></html>";
+
+const TRANSPARENT_SELECT =
+  "<html><head><style>" +
+  "  #one { background-color: transparent; }" +
+  "</style>" +
+  "<body><select id='one'>" +
+  '  <option value="One">{"unstyled": "true"}</option>' +
+  '  <option value="Two" selected="true">{"end": "true"}</option>' +
+  "</select></body></html>";
+
+const OPTION_COLOR_EQUAL_TO_UABACKGROUND_COLOR_SELECT =
+  "<html><head><style>" +
+  "  #one { background-color: black; color: white; }" +
+  "</style>" +
+  "<body><select id='one'>" +
+  '  <option value="One" style="background-color: white; color: black;">{"color": "rgb(0, 0, 0)", "backgroundColor": "rgb(255, 255, 255)"}</option>' +
+  '  <option value="Two" selected="true">{"end": "true"}</option>' +
+  "</select></body></html>";
+
+const GENERIC_OPTION_STYLED_AS_IMPORTANT =
+  "<html><head><style>" +
+  "  option { background-color: black !important; color: white !important; }" +
+  "</style>" +
+  "<body><select id='one'>" +
+  '  <option value="One">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgb(0, 0, 0)"}</option>' +
+  '  <option value="Two" selected="true">{"end": "true"}</option>' +
+  "</select></body></html>";
+
+const TRANSLUCENT_SELECT_BECOMES_OPAQUE =
+  "<html><head>" +
+  "<body><select id='one' style='background-color: rgba(255,255,255,.55);'>" +
+  '  <option value="One">{"color": "rgb(0, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '  <option value="Two" selected="true">{"end": "true"}</option>' +
+  "</select></body></html>";
+
+const DISABLED_OPTGROUP_AND_OPTIONS =
+  "<html><head>" +
+  "<body><select id='one'>" +
+  "  <optgroup label='{\"color\": \"rgb(0, 0, 0)\", \"backgroundColor\": \"buttonface\"}'>" +
+  '    <option disabled="">{"color": "GrayText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '    <option>{"color": "rgb(0, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '    <option disabled="">{"color": "GrayText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '    <option>{"color": "rgb(0, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '    <option>{"color": "rgb(0, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '    <option>{"color": "rgb(0, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '    <option>{"color": "rgb(0, 0, 0)", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  "  </optgroup>" +
+  "  <optgroup label='{\"color\": \"GrayText\", \"backgroundColor\": \"buttonface\"}' disabled=''>" +
+  '    <option>{"color": "GrayText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '    <option>{"color": "GrayText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '    <option>{"color": "GrayText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '    <option>{"color": "GrayText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '    <option>{"color": "GrayText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '    <option>{"color": "GrayText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  '    <option>{"color": "GrayText", "backgroundColor": "rgba(0, 0, 0, 0)"}</option>' +
+  "  </optgroup>" +
+  '  <option value="Two" selected="true">{"end": "true"}</option>' +
+  "</select></body></html>";
+
+function getSystemColor(color) {
+  // Need to convert system color to RGB color.
+  let textarea = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea");
+  textarea.style.color = color;
+  return getComputedStyle(textarea).color;
+}
+
+function testOptionColors(index, item, menulist) {
+  // The label contains a JSON string of the expected colors for
+  // `color` and `background-color`.
+  let expected = JSON.parse(item.label);
+
+  for (let color of Object.keys(expected)) {
+    if (color.toLowerCase().includes("color") &&
+        !expected[color].startsWith("rgb")) {
+      expected[color] = getSystemColor(expected[color]);
+    }
+  }
+
+  // Press Down to move the selected item to the next item in the
+  // list and check the colors of this item when it's not selected.
+  EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
+
+  if (expected.end) {
+    return;
+  }
+
+  if (expected.unstyled) {
+    ok(!item.hasAttribute("customoptionstyling"),
+      `Item ${index} should not have any custom option styling`);
+  } else {
+    is(getComputedStyle(item).color, expected.color,
+       "Item " + (index) + " has correct foreground color");
+    is(getComputedStyle(item).backgroundColor, expected.backgroundColor,
+       "Item " + (index) + " has correct background color");
+  }
+}
+
+function* testSelectColors(select, itemCount, options) {
+  const pageUrl = "data:text/html," + escape(select);
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+
+  let menulist = document.getElementById("ContentSelectDropdown");
+  let selectPopup = menulist.menupopup;
+
+  let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
+  yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
+  yield popupShownPromise;
+
+  is(selectPopup.parentNode.itemCount, itemCount, "Correct number of items");
+  let child = selectPopup.firstChild;
+  let idx = 1;
+
+  if (!options.skipSelectColorTest) {
+    is(getComputedStyle(selectPopup).color, options.selectColor,
+      "popup has expected foreground color");
+    is(getComputedStyle(selectPopup).backgroundColor, options.selectBgColor,
+      "popup has expected background color");
+  }
+
+  ok(!child.selected, "The first child should not be selected");
+  while (child) {
+    testOptionColors(idx, child, menulist);
+    idx++;
+    child = child.nextSibling;
+  }
+
+  yield hideSelectPopup(selectPopup, "escape");
+
+  yield BrowserTestUtils.removeTab(tab);
+}
+
+add_task(function* setup() {
+  yield SpecialPowers.pushPrefEnv({
+    "set": [
+      ["dom.select_popup_in_parent.enabled", true],
+      ["dom.forms.select.customstyling", true]
+    ]
+  });
+});
+
+// This test checks when a <select> element has styles applied to <option>s within it.
+add_task(function* test_colors_applied_to_popup_items() {
+  yield testSelectColors(PAGECONTENT_COLORS, 7,
+                         {skipSelectColorTest: true});
+});
+
+// This test checks when a <select> element has styles applied to itself.
+add_task(function* test_colors_applied_to_popup() {
+  let options = {
+    selectColor: "rgb(255, 255, 255)",
+    selectBgColor: "rgb(126, 58, 58)"
+  };
+  yield testSelectColors(PAGECONTENT_COLORS_ON_SELECT, 4, options);
+});
+
+// This test checks when a <select> element has a transparent background applied to itself.
+add_task(function* test_transparent_applied_to_popup() {
+  let options = {
+    selectColor: getSystemColor("-moz-ComboboxText"),
+    selectBgColor: getSystemColor("-moz-Combobox")
+  };
+  yield testSelectColors(TRANSPARENT_SELECT, 2, options);
+});
+
+// This test checks when a <select> element has a background set, and the
+// options have their own background set which is equal to the default
+// user-agent background color, but should be used because the select
+// background color has been changed.
+add_task(function* test_options_inverted_from_select_background() {
+  // The popup has a black background and white text, but the
+  // options inside of it have flipped the colors.
+  let options = {
+    selectColor: "rgb(255, 255, 255)",
+    selectBgColor: "rgb(0, 0, 0)"
+  };
+  yield testSelectColors(OPTION_COLOR_EQUAL_TO_UABACKGROUND_COLOR_SELECT,
+                         2, options);
+});
+
+// This test checks when a <select> element has a background set using !important,
+// which was affecting how we calculated the user-agent styling.
+add_task(function* test_select_background_using_important() {
+  yield testSelectColors(GENERIC_OPTION_STYLED_AS_IMPORTANT, 2,
+                         {skipSelectColorTest: true});
+});
+
+// This test checks when a <select> element has a background set, and the
+// options have their own background set which is equal to the default
+// user-agent background color, but should be used because the select
+// background color has been changed.
+add_task(function* test_translucent_select_becomes_opaque() {
+  // The popup is requested to show a translucent background
+  // but we force the alpha channel to 1 on the popup.
+  let options = {
+    selectColor: "rgb(0, 0, 0)",
+    selectBgColor: "rgb(255, 255, 255)"
+  };
+  yield testSelectColors(TRANSLUCENT_SELECT_BECOMES_OPAQUE, 2, options);
+});
+
+add_task(function* test_disabled_optgroup_and_options() {
+  // The colors used by this test are platform-specific.
+  if (AppConstants.platform != "win") {
+    return;
+  }
+
+  yield testSelectColors(DISABLED_OPTGROUP_AND_OPTIONS, 17,
+                         {skipSelectColorTest: true});
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/forms/head.js
@@ -0,0 +1,19 @@
+/* eslint-env mozilla/frame-script */
+
+function hideSelectPopup(selectPopup, mode = "enter", win = window) {
+  let browser = win.gBrowser.selectedBrowser;
+  let selectClosedPromise = ContentTask.spawn(browser, null, function*() {
+    Cu.import("resource://gre/modules/SelectContentHelper.jsm");
+    return ContentTaskUtils.waitForCondition(() => !SelectContentHelper.open);
+  });
+
+  if (mode == "escape") {
+    EventUtils.synthesizeKey("KEY_Escape", { code: "Escape" }, win);
+  } else if (mode == "enter") {
+    EventUtils.synthesizeKey("KEY_Enter", { code: "Enter" }, win);
+  } else if (mode == "click") {
+    EventUtils.synthesizeMouseAtCenter(selectPopup.lastChild, { }, win);
+  }
+
+  return selectClosedPromise;
+}
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -319,18 +319,16 @@ skip-if = e10s && debug && os == "win" #
 [browser_save_link_when_window_navigates.js]
 [browser_save_video.js]
 [browser_save_video_frame.js]
 [browser_scope.js]
 [browser_contentSearchUI.js]
 support-files =
   contentSearchUI.html
   contentSearchUI.js
-[browser_selectpopup.js]
-skip-if = os == "linux" # Bug 1329991 - test fails intermittently on Linux builds
 [browser_selectTabAtIndex.js]
 [browser_ssl_error_reports.js]
 [browser_star_hsts.js]
 [browser_storagePressure_notification.js]
 [browser_subframe_favicons_not_used.js]
 [browser_syncui.js]
 skip-if = os == 'linux' # Bug 1304272
 [browser_tab_close_dependent_window.js]
--- a/browser/base/content/test/general/browser_aboutCertError.js
+++ b/browser/base/content/test/general/browser_aboutCertError.js
@@ -14,90 +14,90 @@ const ss = Cc["@mozilla.org/browser/sess
 
 add_task(function* checkReturnToAboutHome() {
   info("Loading a bad cert page directly and making sure 'return to previous page' goes to about:home");
   let browser;
   let certErrorLoaded;
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     gBrowser.selectedTab = gBrowser.addTab(BAD_CERT);
     browser = gBrowser.selectedBrowser;
-    certErrorLoaded = waitForCertErrorLoad(browser);
+    certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
   }, false);
 
   info("Loading and waiting for the cert error");
   yield certErrorLoaded;
 
   is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack");
   is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward");
 
   // Populate the shistory entries manually, since it happens asynchronously
   // and the following tests will be too soon otherwise.
   yield TabStateFlusher.flush(browser);
   let {entries} = JSON.parse(ss.getTabState(tab));
   is(entries.length, 1, "there is one shistory entry");
 
   info("Clicking the go back button on about:certerror");
-  let pageshowPromise = promiseWaitForEvent(browser, "pageshow");
   yield ContentTask.spawn(browser, null, function* () {
     let doc = content.document;
     let returnButton = doc.getElementById("returnButton");
     is(returnButton.getAttribute("autofocus"), "true", "returnButton has autofocus");
     returnButton.click();
+
+    yield ContentTaskUtils.waitForEvent(this, "pageshow", true);
   });
-  yield pageshowPromise;
 
   is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack");
   is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward");
   is(gBrowser.currentURI.spec, "about:home", "Went back");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(function* checkReturnToPreviousPage() {
   info("Loading a bad cert page and making sure 'return to previous page' goes back");
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
   let browser = gBrowser.selectedBrowser;
 
   info("Loading and waiting for the cert error");
-  let certErrorLoaded = waitForCertErrorLoad(browser);
+  let certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
   BrowserTestUtils.loadURI(browser, BAD_CERT);
   yield certErrorLoaded;
 
   is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack");
   is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward");
 
   // Populate the shistory entries manually, since it happens asynchronously
   // and the following tests will be too soon otherwise.
   yield TabStateFlusher.flush(browser);
   let {entries} = JSON.parse(ss.getTabState(tab));
   is(entries.length, 2, "there are two shistory entries");
 
   info("Clicking the go back button on about:certerror");
-  let pageshowPromise = promiseWaitForEvent(browser, "pageshow");
   yield ContentTask.spawn(browser, null, function* () {
     let doc = content.document;
     let returnButton = doc.getElementById("returnButton");
     returnButton.click();
+
+    yield ContentTaskUtils.waitForEvent(this, "pageshow", true);
   });
-  yield pageshowPromise;
 
   is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack");
   is(browser.webNavigation.canGoForward, true, "webNavigation.canGoForward");
   is(gBrowser.currentURI.spec, GOOD_PAGE, "Went back");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(function* checkBadStsCert() {
   info("Loading a badStsCert and making sure exception button doesn't show up");
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
   let browser = gBrowser.selectedBrowser;
 
   info("Loading and waiting for the cert error");
-  let certErrorLoaded = waitForCertErrorLoad(browser);
+  let certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
   BrowserTestUtils.loadURI(browser, BAD_STS_CERT);
   yield certErrorLoaded;
 
   let exceptionButtonHidden = yield ContentTask.spawn(browser, null, function* () {
     let doc = content.document;
     let exceptionButton = doc.getElementById("exceptionDialogButton");
     return exceptionButton.hidden;
   });
@@ -123,17 +123,17 @@ const PREF_BLOCKLIST_CLOCK_SKEW_SECONDS 
 
 add_task(function* checkWrongSystemTimeWarning() {
   function* setUpPage() {
     let browser;
     let certErrorLoaded;
     yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
       gBrowser.selectedTab = gBrowser.addTab(BAD_CERT);
       browser = gBrowser.selectedBrowser;
-      certErrorLoaded = waitForCertErrorLoad(browser);
+      certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
     }, false);
 
     info("Loading and waiting for the cert error");
     yield certErrorLoaded;
 
     return yield ContentTask.spawn(browser, null, function* () {
       let doc = content.document;
       let div = doc.getElementById("wrongSystemTimePanel");
@@ -219,17 +219,17 @@ add_task(function* checkWrongSystemTimeW
 
 add_task(function* checkAdvancedDetails() {
   info("Loading a bad cert page and verifying the main error and advanced details section");
   let browser;
   let certErrorLoaded;
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     gBrowser.selectedTab = gBrowser.addTab(BAD_CERT);
     browser = gBrowser.selectedBrowser;
-    certErrorLoaded = waitForCertErrorLoad(browser);
+    certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
   }, false);
 
   info("Loading and waiting for the cert error");
   yield certErrorLoaded;
 
   let message = yield ContentTask.spawn(browser, null, function* () {
     let doc = content.document;
     let shortDescText = doc.getElementById("errorShortDescText");
@@ -281,17 +281,17 @@ add_task(function* checkAdvancedDetails(
 
 add_task(function* checkAdvancedDetailsForHSTS() {
   info("Loading a bad STS cert page and verifying the advanced details section");
   let browser;
   let certErrorLoaded;
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     gBrowser.selectedTab = gBrowser.addTab(BAD_STS_CERT);
     browser = gBrowser.selectedBrowser;
-    certErrorLoaded = waitForCertErrorLoad(browser);
+    certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
   }, false);
 
   info("Loading and waiting for the cert error");
   yield certErrorLoaded;
 
   let message = yield ContentTask.spawn(browser, null, function* () {
     let doc = content.document;
     let advancedButton = doc.getElementById("advancedButton");
@@ -350,41 +350,31 @@ add_task(function* checkAdvancedDetailsF
 
 add_task(function* checkUnknownIssuerLearnMoreLink() {
   info("Loading a cert error for self-signed pages and checking the correct link is shown");
   let browser;
   let certErrorLoaded;
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     gBrowser.selectedTab = gBrowser.addTab(UNKNOWN_ISSUER);
     browser = gBrowser.selectedBrowser;
-    certErrorLoaded = waitForCertErrorLoad(browser);
+    certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
   }, false);
 
   info("Loading and waiting for the cert error");
   yield certErrorLoaded;
 
   let href = yield ContentTask.spawn(browser, null, function* () {
     let learnMoreLink = content.document.getElementById("learnMoreLink");
     return learnMoreLink.href;
   });
   ok(href.endsWith("security-error"), "security-error in the Learn More URL");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
-function waitForCertErrorLoad(browser) {
-  return new Promise(resolve => {
-    info("Waiting for DOMContentLoaded event");
-    browser.addEventListener("DOMContentLoaded", function load() {
-      browser.removeEventListener("DOMContentLoaded", load, false, true);
-      resolve();
-    }, false, true);
-  });
-}
-
 function getCertChain(securityInfoAsString) {
   let certChain = "";
   const serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
                        .getService(Ci.nsISerializationHelper);
   let securityInfo = serhelper.deserializeObject(securityInfoAsString);
   securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
   let certs = securityInfo.failedCertChain.getEnumerator();
   while (certs.hasMoreElements()) {
--- a/browser/base/content/test/general/browser_aboutNetError.js
+++ b/browser/base/content/test/general/browser_aboutNetError.js
@@ -19,26 +19,29 @@ add_task(function* checkReturnToPrevious
     gBrowser.selectedTab = gBrowser.addTab(LOW_TLS_VERSION);
     browser = gBrowser.selectedBrowser;
     pageLoaded = BrowserTestUtils.waitForErrorPage(browser);
   }, false);
 
   info("Loading and waiting for the net error");
   yield pageLoaded;
 
-  Assert.ok(content.document.getElementById("prefResetButton").getBoundingClientRect().left >= 0,
-    "Should have a visible button");
+  // NB: This code assumes that the error page and the test page load in the
+  // same process. If this test starts to fail, it could be because they load
+  // in different processes.
+  yield ContentTask.spawn(browser, LOW_TLS_VERSION, function* (LOW_TLS_VERSION_) {
+    ok(content.document.getElementById("prefResetButton").getBoundingClientRect().left >= 0,
+      "Should have a visible button");
 
-  Assert.ok(content.document.documentURI.startsWith("about:neterror"), "Should be showing error page");
+    ok(content.document.documentURI.startsWith("about:neterror"), "Should be showing error page");
 
-  let pageshowPromise = promiseWaitForEvent(browser, "pageshow");
-  yield ContentTask.spawn(browser, null, function* () {
     let doc = content.document;
     let prefResetButton = doc.getElementById("prefResetButton");
-    Assert.equal(prefResetButton.getAttribute("autofocus"), "true", "prefResetButton has autofocus");
+    is(prefResetButton.getAttribute("autofocus"), "true", "prefResetButton has autofocus");
     prefResetButton.click();
+
+    yield ContentTaskUtils.waitForEvent(this, "pageshow", true);
+
+    is(content.document.documentURI, LOW_TLS_VERSION_, "Should not be showing page");
   });
-  yield pageshowPromise;
-
-  Assert.equal(content.document.documentURI, LOW_TLS_VERSION, "Should not be showing page");
 
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
--- a/browser/base/content/test/general/browser_blockHPKP.js
+++ b/browser/base/content/test/general/browser_blockHPKP.js
@@ -58,17 +58,17 @@ function loadPinningPage() {
 }
 
 // After the site is pinned try to load with a subdomain site that should
 // fail to validate
 var successfulPinningPageListener = {
   handleEvent() {
     gBrowser.selectedBrowser.removeEventListener("load", this, true);
     BrowserTestUtils.loadURI(gBrowser.selectedBrowser, "https://" + kBadPinningDomain).then(function() {
-      return promiseErrorPageLoaded(gBrowser.selectedBrowser);
+      return BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser);
     }).then(errorPageLoaded);
   }
 };
 
 // The browser should load about:neterror, when this happens, proceed
 // to load the pinning domain again, this time removing the pinning information
 function errorPageLoaded() {
   ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
--- a/browser/base/content/test/general/browser_bug578534.js
+++ b/browser/base/content/test/general/browser_bug578534.js
@@ -1,28 +1,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/. */
 
-function test() {
+add_task(function* test() {
   let uriString = "http://example.com/";
   let cookieBehavior = "network.cookie.cookieBehavior";
   let uriObj = Services.io.newURI(uriString)
   let cp = Components.classes["@mozilla.org/cookie/permission;1"]
                      .getService(Components.interfaces.nsICookiePermission);
 
-  Services.prefs.setIntPref(cookieBehavior, 2);
-
+  yield SpecialPowers.pushPrefEnv({ set: [[ cookieBehavior, 2 ]] });
   cp.setAccess(uriObj, cp.ACCESS_ALLOW);
-  gBrowser.selectedTab = gBrowser.addTab(uriString);
-  waitForExplicitFinish();
-  BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(onTabLoaded);
 
-  function onTabLoaded() {
-    is(gBrowser.selectedBrowser.contentWindow.navigator.cookieEnabled, true,
-       "navigator.cookieEnabled should be true");
-    // Clean up
-    gBrowser.removeTab(gBrowser.selectedTab);
-    Services.prefs.setIntPref(cookieBehavior, 0);
-    cp.setAccess(uriObj, cp.ACCESS_DEFAULT);
-    finish();
-  }
-}
+  yield BrowserTestUtils.withNewTab({ gBrowser, url: uriString }, function* (browser) {
+    yield ContentTask.spawn(browser, null, function() {
+      is(content.navigator.cookieEnabled, true,
+         "navigator.cookieEnabled should be true");
+    });
+  });
+
+  cp.setAccess(uriObj, cp.ACCESS_DEFAULT);
+});
--- a/browser/base/content/test/general/browser_bug592338.js
+++ b/browser/base/content/test/general/browser_bug592338.js
@@ -65,71 +65,28 @@ function test_install_lwtheme() {
         LightweightThemeManager.currentTheme = null;
         gBrowser.removeTab(gBrowser.selectedTab);
         Services.perms.remove(makeURI("http://example.com/"), "install");
 
         runNextTest();
       }
     );
   });
-},
-
-function test_lwtheme_switch_theme() {
-  is(LightweightThemeManager.currentTheme, null, "Should be no lightweight theme selected");
-
-  AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(aAddon) {
-    aAddon.userDisabled = false;
-    ok(aAddon.isActive, "Theme should have immediately enabled");
-    Services.prefs.setBoolPref("extensions.dss.enabled", false);
-
-    var pm = Services.perms;
-    pm.add(makeURI("https://example.com/"), "install", pm.ALLOW_ACTION);
-
-    gBrowser.selectedTab = gBrowser.addTab("https://example.com/browser/browser/base/content/test/general/bug592338.html");
-    gBrowser.selectedBrowser.addEventListener("pageshow", function() {
-      if (gBrowser.contentDocument.location.href == "about:blank")
-        return;
-
-      gBrowser.selectedBrowser.removeEventListener("pageshow", arguments.callee);
-
-      executeSoon(function() {
-        wait_for_notification(function(aPanel) {
-          is(LightweightThemeManager.currentTheme, null, "Should not have installed the test lwtheme");
-          ok(aAddon.isActive, "Test theme should still be active");
-
-          let notification = aPanel.childNodes[0];
-          is(notification.button.label, "Restart Now", "Should have seen the right button");
-
-          ok(aAddon.userDisabled, "Should be waiting to disable the test theme");
-          aAddon.userDisabled = false;
-          Services.prefs.setBoolPref("extensions.dss.enabled", true);
-
-          gBrowser.removeTab(gBrowser.selectedTab);
-
-          Services.perms.remove(makeURI("http://example.com"), "install");
-
-          runNextTest();
-        });
-        BrowserTestUtils.synthesizeMouse("#theme-install", 2, 2, {}, gBrowser.selectedBrowser);
-      });
-    });
-  });
 }
 ];
 
 function runNextTest() {
   AddonManager.getAllInstalls(function(aInstalls) {
     is(aInstalls.length, 0, "Should be no active installs");
 
     if (TESTS.length == 0) {
       AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(aAddon) {
         aAddon.uninstall();
 
         Services.prefs.setBoolPref("extensions.logging.enabled", false);
-        Services.prefs.setBoolPref("extensions.dss.enabled", false);
 
         finish();
       });
       return;
     }
 
     info("Running " + TESTS[0].name);
     TESTS.shift()();
@@ -142,21 +99,16 @@ function test() {
   Services.prefs.setBoolPref("extensions.logging.enabled", true);
 
   AddonManager.getInstallForURL(TESTROOT + "theme.xpi", function(aInstall) {
     aInstall.addListener({
       onInstallEnded() {
         AddonManager.getAddonByID("theme-xpi@tests.mozilla.org", function(aAddon) {
           isnot(aAddon, null, "Should have installed the test theme.");
 
-          // In order to switch themes while the test is running we turn on dynamic
-          // theme switching. This means the test isn't exactly correct but should
-          // do some good
-          Services.prefs.setBoolPref("extensions.dss.enabled", true);
-
           runNextTest();
         });
       }
     });
 
     aInstall.install();
   }, "application/x-xpinstall");
 }
--- a/browser/base/content/test/general/browser_bug749738.js
+++ b/browser/base/content/test/general/browser_bug749738.js
@@ -7,29 +7,23 @@
 const DUMMY_PAGE = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
 
 function test() {
   waitForExplicitFinish();
 
   let tab = gBrowser.addTab();
   gBrowser.selectedTab = tab;
 
-  load(tab, DUMMY_PAGE, function() {
+  BrowserTestUtils.loadURI(tab.linkedBrowser, DUMMY_PAGE);
+  BrowserTestUtils.browserLoaded(tab.linkedBrowser).then(() => {
     gFindBar.onFindCommand();
     EventUtils.sendString("Dummy");
     gBrowser.removeTab(tab);
 
     try {
       gFindBar.close();
       ok(true, "findbar.close should not throw an exception");
     } catch (e) {
       ok(false, "findbar.close threw exception: " + e);
     }
     finish();
   });
 }
-
-function load(aTab, aUrl, aCallback) {
-  aTab.linkedBrowser.addEventListener("load", function(aEvent) {
-    waitForFocus(aCallback, content);
-  }, {capture: true, once: true});
-  aTab.linkedBrowser.loadURI(aUrl);
-}
--- a/browser/base/content/test/general/browser_bug882977.js
+++ b/browser/base/content/test/general/browser_bug882977.js
@@ -9,28 +9,21 @@ add_task(function*() {
   yield SpecialPowers.pushPrefEnv({
     "set": [
       ["browser.startup.homepage", homepage],
       ["browser.startup.page", 1],
     ]
   });
 
   let win = OpenBrowserWindow();
-  yield BrowserTestUtils.waitForEvent(win, "load");
+  yield BrowserTestUtils.firstBrowserLoaded(win, false);
 
   let browser = win.gBrowser.selectedBrowser;
-  // If we've finished loading about:home already, we can check
-  // right away - otherwise, we need to wait.
-  if (browser.contentDocument.readyState == "complete" &&
-      browser.currentURI.spec == homepage) {
-    checkIdentityMode(win);
-  } else {
-    yield BrowserTestUtils.browserLoaded(browser, false, homepage);
-    checkIdentityMode(win);
-  }
+  is(browser.currentURI.spec, homepage, "Loaded the correct homepage");
+  checkIdentityMode(win);
 
   yield BrowserTestUtils.closeWindow(win);
 });
 
 function checkIdentityMode(win) {
   let identityMode = win.document.getElementById("identity-box").className;
   is(identityMode, "chromeUI", "Identity state should be chromeUI for about:home in a new window");
 }
--- a/browser/base/content/test/plugins/browser_blocklist_content.js
+++ b/browser/base/content/test/plugins/browser_blocklist_content.js
@@ -67,17 +67,17 @@ add_task(function* () {
 add_task(function* () {
   yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html>GO!</html>");
 
   yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
 
   // Hack the planet! Load our blocklist shim, so we can mess with blocklist
   // return results in the content process. Active until we close our tab.
   let mm = gTestBrowser.messageManager;
-  info("test 3a: loading " + gChromeRoot + "blocklist_proxy.js" + "\n");
+  info("test 3a: loading " + gChromeRoot + "blocklist_proxy.js\n");
   mm.loadFrameScript(gChromeRoot + "blocklist_proxy.js", true);
 
   yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
 
   // Work around for delayed PluginBindingAttached
   yield promiseUpdatePluginBindings(gTestBrowser);
 
   yield ContentTask.spawn(gTestBrowser, {}, function* () {
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_3.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_3.js
@@ -238,31 +238,33 @@ var tests = [
       showNotification(this.notifyObj);
     },
     onShown(popup) {
       let self = this;
       let progressListener = {
         onLocationChange: function onLocationChange() {
           gBrowser.removeProgressListener(progressListener);
 
-	  executeSoon(() => {
+          executeSoon(() => {
             let notification = PopupNotifications.getNotification(self.notifyObj.id,
                                                                   self.notifyObj.browser);
             ok(notification != null, "Notification remained when subframe navigated");
             self.notifyObj.options.eventCallback = undefined;
 
             notification.remove();
-	  });
+          });
         },
       };
 
       info("Adding progress listener and performing navigation");
       gBrowser.addProgressListener(progressListener);
-      content.document.getElementById("iframe")
-                      .setAttribute("src", "http://example.org/");
+      ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+        content.document.getElementById("iframe")
+                        .setAttribute("src", "http://example.org/");
+      });
     },
     onHidden() {}
   },
   // Popup Notifications should catch exceptions from callbacks
   { id: "Test#10",
     run() {
       this.testNotif1 = new BasicNotification(this.id);
       this.testNotif1.message += " 1";
--- a/browser/base/content/test/siteIdentity/browser_bug1045809.js
+++ b/browser/base/content/test/siteIdentity/browser_bug1045809.js
@@ -30,31 +30,37 @@ add_task(function* () {
   // Test 3: mixed content must be blocked again
   yield promiseTabLoadEvent(tab);
   yield* test3(gBrowser.getBrowserForTab(tab));
 });
 
 function* test1(gTestBrowser) {
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
-  var x = content.document.getElementsByTagName("iframe")[0].contentDocument.getElementById("mixedContentContainer");
-  is(x, null, "Mixed Content is NOT to be found in Test1");
+  yield ContentTask.spawn(gTestBrowser, null, function() {
+    var x = content.document.getElementsByTagName("iframe")[0].contentDocument.getElementById("mixedContentContainer");
+    is(x, null, "Mixed Content is NOT to be found in Test1");
+  });
 
   // Disable Mixed Content Protection for the page (and reload)
   gIdentityHandler.disableMixedContentProtection();
 }
 
 function* test2(gTestBrowser) {
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: true, activeBlocked: false, passiveLoaded: false});
 
-  var x = content.document.getElementsByTagName("iframe")[0].contentDocument.getElementById("mixedContentContainer");
-  isnot(x, null, "Mixed Content is to be found in Test2");
+  yield ContentTask.spawn(gTestBrowser, null, function() {
+    var x = content.document.getElementsByTagName("iframe")[0].contentDocument.getElementById("mixedContentContainer");
+    isnot(x, null, "Mixed Content is to be found in Test2");
+  });
 
   // Re-enable Mixed Content Protection for the page (and reload)
   gIdentityHandler.enableMixedContentProtection();
 }
 
 function* test3(gTestBrowser) {
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
-  var x = content.document.getElementsByTagName("iframe")[0].contentDocument.getElementById("mixedContentContainer");
-  is(x, null, "Mixed Content is NOT to be found in Test3");
+  yield ContentTask.spawn(gTestBrowser, null, function() {
+    var x = content.document.getElementsByTagName("iframe")[0].contentDocument.getElementById("mixedContentContainer");
+    is(x, null, "Mixed Content is NOT to be found in Test3");
+  });
 }
--- a/browser/base/content/test/siteIdentity/browser_bug822367.js
+++ b/browser/base/content/test/siteIdentity/browser_bug822367.js
@@ -7,189 +7,181 @@ const PREF_DISPLAY = "security.mixed_con
 const PREF_ACTIVE = "security.mixed_content.block_active_content";
 
 // We alternate for even and odd test cases to simulate different hosts
 const HTTPS_TEST_ROOT = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com");
 const HTTPS_TEST_ROOT_2 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://test1.example.com");
 
 var gTestBrowser = null;
 
-registerCleanupFunction(function() {
-  // Set preferences back to their original values
-  Services.prefs.clearUserPref(PREF_DISPLAY);
-  Services.prefs.clearUserPref(PREF_ACTIVE);
-});
-
-function MixedTestsCompleted() {
-  gBrowser.removeCurrentTab();
-  window.focus();
-  finish();
-}
-
-function test() {
-  waitForExplicitFinish();
-
-  Services.prefs.setBoolPref(PREF_DISPLAY, true);
-  Services.prefs.setBoolPref(PREF_ACTIVE, true);
+add_task(function* test() {
+  yield SpecialPowers.pushPrefEnv({ set: [[ PREF_DISPLAY, true ],
+                                          [ PREF_ACTIVE, true  ]] });
 
   var newTab = gBrowser.addTab();
   gBrowser.selectedTab = newTab;
   gTestBrowser = gBrowser.selectedBrowser;
   newTab.linkedBrowser.stop()
 
   // Mixed Script Test
-  gTestBrowser.addEventListener("load", MixedTest1A, true);
   var url = HTTPS_TEST_ROOT + "file_bug822367_1.html";
-  gTestBrowser.contentWindow.location = url;
-}
+  BrowserTestUtils.loadURI(gTestBrowser, url);
+  yield BrowserTestUtils.browserLoaded(gTestBrowser);
+});
 
 // Mixed Script Test
-function MixedTest1A() {
-  gTestBrowser.removeEventListener("load", MixedTest1A, true);
-  gTestBrowser.addEventListener("load", MixedTest1B, true);
-
+add_task(function* MixedTest1A() {
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
   let {gIdentityHandler} = gTestBrowser.ownerGlobal;
   gIdentityHandler.disableMixedContentProtection();
-}
-function MixedTest1B() {
-  BrowserTestUtils.waitForCondition(() => content.document.getElementById("p1").innerHTML == "hello", "Waited too long for mixed script to run in Test 1").then(MixedTest1C);
-}
-function MixedTest1C() {
-  ok(content.document.getElementById("p1").innerHTML == "hello", "Mixed script didn't load in Test 1");
-  gTestBrowser.removeEventListener("load", MixedTest1B, true);
-  MixedTest2();
-}
+  yield BrowserTestUtils.browserLoaded(gTestBrowser);
+});
+
+add_task(function* MixedTest1B() {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
+    yield ContentTaskUtils.waitForCondition(
+      () => content.document.getElementById("p1").innerHTML == "hello",
+      "Waited too long for mixed script to run in Test 1");
+  });
+});
 
 // Mixed Display Test - Doorhanger should not appear
-function MixedTest2() {
-  gTestBrowser.addEventListener("load", MixedTest2A, true);
+add_task(function* MixedTest2() {
   var url = HTTPS_TEST_ROOT_2 + "file_bug822367_2.html";
-  gTestBrowser.contentWindow.location = url;
-}
+  BrowserTestUtils.loadURI(gTestBrowser, url);
+  yield BrowserTestUtils.browserLoaded(gTestBrowser);
 
-function MixedTest2A() {
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: false, passiveLoaded: false});
-  MixedTest3();
-}
+});
 
 // Mixed Script and Display Test - User Override should cause both the script and the image to load.
-function MixedTest3() {
-  gTestBrowser.removeEventListener("load", MixedTest2A, true);
-  gTestBrowser.addEventListener("load", MixedTest3A, true);
+add_task(function* MixedTest3() {
   var url = HTTPS_TEST_ROOT + "file_bug822367_3.html";
-  gTestBrowser.contentWindow.location = url;
-}
-function MixedTest3A() {
-  gTestBrowser.removeEventListener("load", MixedTest3A, true);
-  gTestBrowser.addEventListener("load", MixedTest3B, true);
+  BrowserTestUtils.loadURI(gTestBrowser, url);
+  yield BrowserTestUtils.browserLoaded(gTestBrowser);
+});
+
+add_task(function* MixedTest3A() {
+  assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
+
+  let {gIdentityHandler} = gTestBrowser.ownerGlobal;
+  gIdentityHandler.disableMixedContentProtection();
+  yield BrowserTestUtils.browserLoaded(gTestBrowser);
+});
 
+add_task(function* MixedTest3B() {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
+    let p1 = ContentTaskUtils.waitForCondition(
+      () => content.document.getElementById("p1").innerHTML == "hello",
+      "Waited too long for mixed script to run in Test 3");
+    let p2 = ContentTaskUtils.waitForCondition(
+      () => content.document.getElementById("p2").innerHTML == "bye",
+      "Waited too long for mixed image to load in Test 3");
+    yield Promise.all([ p1, p2 ]);
+  });
+
+  assertMixedContentBlockingState(gTestBrowser, {activeLoaded: true, activeBlocked: false, passiveLoaded: true});
+});
+
+// Location change - User override on one page doesn't propogate to another page after location change.
+add_task(function* MixedTest4() {
+  var url = HTTPS_TEST_ROOT_2 + "file_bug822367_4.html";
+  BrowserTestUtils.loadURI(gTestBrowser, url);
+  yield BrowserTestUtils.browserLoaded(gTestBrowser);
+});
+
+add_task(function* MixedTest4A() {
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
   let {gIdentityHandler} = gTestBrowser.ownerGlobal;
   gIdentityHandler.disableMixedContentProtection();
-}
-function MixedTest3B() {
-  BrowserTestUtils.waitForCondition(() => content.document.getElementById("p1").innerHTML == "hello", "Waited too long for mixed script to run in Test 3").then(MixedTest3C);
-}
-function MixedTest3C() {
-  BrowserTestUtils.waitForCondition(() => content.document.getElementById("p2").innerHTML == "bye", "Waited too long for mixed image to load in Test 3").then(MixedTest3D);
-}
-function MixedTest3D() {
-  ok(content.document.getElementById("p1").innerHTML == "hello", "Mixed script didn't load in Test 3");
-  ok(content.document.getElementById("p2").innerHTML == "bye", "Mixed image didn't load in Test 3");
-  assertMixedContentBlockingState(gTestBrowser, {activeLoaded: true, activeBlocked: false, passiveLoaded: true});
-  MixedTest4();
-}
+  yield BrowserTestUtils.browserLoaded(gTestBrowser);
+});
+
+add_task(function* MixedTest4B() {
+  let url = HTTPS_TEST_ROOT + "file_bug822367_4B.html";
+  yield ContentTask.spawn(gTestBrowser, url, function* (wantedUrl) {
+    yield ContentTaskUtils.waitForCondition(
+      () => content.document.location == wantedUrl,
+      "Waited too long for mixed script to run in Test 4");
+  });
+});
+
+add_task(function* MixedTest4C() {
+  assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
-// Location change - User override on one page doesn't propogate to another page after location change.
-function MixedTest4() {
-  gTestBrowser.removeEventListener("load", MixedTest3B, true);
-  gTestBrowser.addEventListener("load", MixedTest4A, true);
-  var url = HTTPS_TEST_ROOT_2 + "file_bug822367_4.html";
-  gTestBrowser.contentWindow.location = url;
-}
-function MixedTest4A() {
-  gTestBrowser.removeEventListener("load", MixedTest4A, true);
-  gTestBrowser.addEventListener("load", MixedTest4B, true);
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
+    yield ContentTaskUtils.waitForCondition(
+      () => content.document.getElementById("p1").innerHTML == "",
+      "Mixed script loaded in test 4 after location change!");
+  });
+});
 
+// Mixed script attempts to load in a document.open()
+add_task(function* MixedTest5() {
+  var url = HTTPS_TEST_ROOT + "file_bug822367_5.html";
+  BrowserTestUtils.loadURI(gTestBrowser, url);
+  yield BrowserTestUtils.browserLoaded(gTestBrowser);
+});
+
+add_task(function* MixedTest5A() {
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
   let {gIdentityHandler} = gTestBrowser.ownerGlobal;
   gIdentityHandler.disableMixedContentProtection();
-}
-function MixedTest4B() {
-  BrowserTestUtils.waitForCondition(() => content.document.location == HTTPS_TEST_ROOT + "file_bug822367_4B.html", "Waited too long for mixed script to run in Test 4").then(MixedTest4C);
-}
-function MixedTest4C() {
-  ok(content.document.location == HTTPS_TEST_ROOT + "file_bug822367_4B.html", "Location didn't change in test 4");
+  yield BrowserTestUtils.browserLoaded(gTestBrowser);
+});
 
-  assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
+add_task(function* MixedTest5B() {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
+    yield ContentTaskUtils.waitForCondition(
+      () => content.document.getElementById("p1").innerHTML == "hello",
+      "Waited too long for mixed script to run in Test 5");
+  });
+});
 
-  BrowserTestUtils.waitForCondition(() => content.document.getElementById("p1").innerHTML == "", "Mixed script loaded in test 4 after location change!").then(MixedTest4D);
-}
-function MixedTest4D() {
-  ok(content.document.getElementById("p1").innerHTML == "", "p1.innerHTML changed; mixed script loaded after location change in Test 4");
-  MixedTest5();
-}
+// Mixed script attempts to load in a document.open() that is within an iframe.
+add_task(function* MixedTest6() {
+  var url = HTTPS_TEST_ROOT_2 + "file_bug822367_6.html";
+  BrowserTestUtils.loadURI(gTestBrowser, url);
+  yield BrowserTestUtils.browserLoaded(gTestBrowser);
+});
 
-// Mixed script attempts to load in a document.open()
-function MixedTest5() {
-  gTestBrowser.removeEventListener("load", MixedTest4B, true);
-  gTestBrowser.addEventListener("load", MixedTest5A, true);
-  var url = HTTPS_TEST_ROOT + "file_bug822367_5.html";
-  gTestBrowser.contentWindow.location = url;
-}
-function MixedTest5A() {
-  gTestBrowser.removeEventListener("load", MixedTest5A, true);
-  gTestBrowser.addEventListener("load", MixedTest5B, true);
+add_task(function* MixedTest6A() {
+  gTestBrowser.removeEventListener("load", MixedTest6A, true);
+  let {gIdentityHandler} = gTestBrowser.ownerGlobal;
 
+  yield BrowserTestUtils.waitForCondition(
+    () => gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
+    "Waited too long for control center to get mixed active blocked state");
+});
+
+add_task(function* MixedTest6B() {
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
 
   let {gIdentityHandler} = gTestBrowser.ownerGlobal;
   gIdentityHandler.disableMixedContentProtection();
-}
-function MixedTest5B() {
-  BrowserTestUtils.waitForCondition(() => content.document.getElementById("p1").innerHTML == "hello", "Waited too long for mixed script to run in Test 5").then(MixedTest5C);
-}
-function MixedTest5C() {
-  ok(content.document.getElementById("p1").innerHTML == "hello", "Mixed script didn't load in Test 5");
-  MixedTest6();
-}
 
-// Mixed script attempts to load in a document.open() that is within an iframe.
-function MixedTest6() {
-  gTestBrowser.removeEventListener("load", MixedTest5B, true);
-  gTestBrowser.addEventListener("load", MixedTest6A, true);
-  var url = HTTPS_TEST_ROOT_2 + "file_bug822367_6.html";
-  gTestBrowser.contentWindow.location = url;
-}
-function MixedTest6A() {
-  gTestBrowser.removeEventListener("load", MixedTest6A, true);
-  let {gIdentityHandler} = gTestBrowser.ownerGlobal;
-  BrowserTestUtils.waitForCondition(() => gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"), "Waited too long for control center to get mixed active blocked state").then(MixedTest6B);
-}
+  yield BrowserTestUtils.browserLoaded(gTestBrowser);
+});
 
-function MixedTest6B() {
-  gTestBrowser.addEventListener("load", MixedTest6C, true);
-
-  assertMixedContentBlockingState(gTestBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
-
-  let {gIdentityHandler} = gTestBrowser.ownerGlobal;
-  gIdentityHandler.disableMixedContentProtection();
-}
+add_task(function* MixedTest6C() {
+  yield ContentTask.spawn(gTestBrowser, null, function* () {
+    function test() {
+      try {
+        return content.document.getElementById("f1").contentDocument.getElementById("p1").innerHTML == "hello";
+      } catch (e) {
+        return false;
+      }
+    }
 
-function MixedTest6C() {
-  gTestBrowser.removeEventListener("load", MixedTest6C, true);
-  BrowserTestUtils.waitForCondition(function() {
-    try {
-      return content.document.getElementById("f1").contentDocument.getElementById("p1").innerHTML == "hello";
-    } catch (e) {
-      return false;
-    }
-  }, "Waited too long for mixed script to run in Test 6").then(MixedTest6D);
-}
-function MixedTest6D() {
-  ok(content.document.getElementById("f1").contentDocument.getElementById("p1").innerHTML == "hello", "Mixed script didn't load in Test 6");
+    yield ContentTaskUtils.waitForCondition(test, "Waited too long for mixed script to run in Test 6");
+  });
+});
+
+add_task(function* MixedTest6D() {
   assertMixedContentBlockingState(gTestBrowser, {activeLoaded: true, activeBlocked: false, passiveLoaded: false});
-  MixedTestsCompleted();
-}
+});
+
+add_task(function* cleanup() {
+  gBrowser.removeCurrentTab();
+});
--- a/browser/base/content/test/webrtc/browser.ini
+++ b/browser/base/content/test/webrtc/browser.ini
@@ -4,16 +4,18 @@ support-files =
   get_user_media_in_frame.html
   get_user_media_content_script.js
   head.js
 
 [browser_devices_get_user_media.js]
 skip-if = (os == "linux" && debug) # linux: bug 976544
 [browser_devices_get_user_media_anim.js]
 [browser_devices_get_user_media_in_frame.js]
+[browser_devices_get_user_media_multi_process.js]
+skip-if = (e10s && debug) # bug 1347625
 [browser_devices_get_user_media_screen.js]
 skip-if = (e10s && debug) || (os == "linux") || (os == "win" && !debug) # bug 1320754 for e10s debug, and bug 1320994 for linux opt, bug 1338038 for windows and linux debug
 [browser_devices_get_user_media_tear_off_tab.js]
 [browser_devices_get_user_media_unprompted_access.js]
 [browser_devices_get_user_media_unprompted_access_in_frame.js]
 [browser_devices_get_user_media_unprompted_access_tear_off_tab.js]
 skip-if = (os == "linux") || (os == "win" && bits == 64) # linux: bug 1331616, win8: bug 1334752
 [browser_webrtc_hooks.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
@@ -0,0 +1,153 @@
+/* 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/. */
+
+registerCleanupFunction(function() {
+  gBrowser.removeCurrentTab();
+});
+
+var gTests = [
+
+{
+  desc: "getUserMedia audio in a first process + video in a second process",
+  run: function* checkMultiProcess() {
+    // The main purpose of this test is to ensure webrtc sharing indicators
+    // work with multiple content processes, but it makes sense to run this
+    // test without e10s too to ensure using webrtc devices in two different
+    // tabs is handled correctly.
+
+    // Request audio in the first tab.
+    let promise = promisePopupNotificationShown("webRTC-shareDevices");
+    yield promiseRequestDevice(true);
+    yield promise;
+    yield expectObserverCalled("getUserMedia:request");
+
+    checkDeviceSelectors(true);
+
+    let indicator = promiseIndicatorWindow();
+    yield promiseMessage("ok", () => {
+      PopupNotifications.panel.firstChild.button.click();
+    });
+    yield expectObserverCalled("getUserMedia:response:allow");
+    yield expectObserverCalled("recording-device-events");
+    Assert.deepEqual((yield getMediaCaptureState()), {audio: true},
+                     "expected microphone to be shared");
+
+    yield indicator;
+    yield checkSharingUI({audio: true});
+
+    ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
+    ok(!webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator hidden");
+    ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
+    is(webrtcUI.getActiveStreams(false, true).length, 1, "1 active audio stream");
+    is(webrtcUI.getActiveStreams(true, true, true).length, 1, "1 active stream");
+
+    yield expectNoObserverCalled();
+
+    // If we have reached the max process count already, increase it to ensure
+    // our new tab can have its own content process.
+    var ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
+                 .getService(Ci.nsIMessageBroadcaster);
+    ppmm.QueryInterface(Ci.nsIProcessScriptLoader);
+    let childCount = ppmm.childCount;
+    let maxContentProcess = Services.prefs.getIntPref("dom.ipc.processCount");
+    // The first check is because if we are on a branch where e10s-multi is
+    // disabled, we want to keep testing e10s with a single content process.
+    // The + 1 is because ppmm.childCount also counts the chrome process
+    // (which also runs process scripts).
+    if (maxContentProcess > 1 && childCount == maxContentProcess + 1) {
+      yield SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount",
+                                                childCount]]});
+    }
+
+    // Open a new tab with a different hostname.
+    let url = gBrowser.currentURI.spec.replace("https://example.com/",
+                                               "http://127.0.0.1:8888/");
+    let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+    tab.linkedBrowser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
+
+    // Request video.
+    promise = promisePopupNotificationShown("webRTC-shareDevices");
+    yield promiseRequestDevice(false, true);
+    yield promise;
+    yield expectObserverCalled("getUserMedia:request");
+
+    checkDeviceSelectors(false, true);
+
+    yield promiseMessage("ok", () => {
+      PopupNotifications.panel.firstChild.button.click();
+    });
+    yield expectObserverCalled("getUserMedia:response:allow");
+    yield expectObserverCalled("recording-device-events");
+
+    yield checkSharingUI({video: true}, window, {audio: true, video: true});
+
+    ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
+    ok(webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator shown");
+    ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
+    is(webrtcUI.getActiveStreams(false, true).length, 1, "1 active audio stream");
+    is(webrtcUI.getActiveStreams(true).length, 1, "1 active video stream");
+    is(webrtcUI.getActiveStreams(true, true, true).length, 2, "2 active streams");
+
+    info("removing the second tab");
+    yield BrowserTestUtils.removeTab(tab);
+
+    // Check that we still show the sharing indicators for the first tab's stream.
+    yield promiseWaitForCondition(() => !webrtcUI.showCameraIndicator);
+    ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
+    ok(!webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator hidden");
+    ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
+    is(webrtcUI.getActiveStreams(false, true).length, 1, "1 active audio stream");
+    is(webrtcUI.getActiveStreams(true, true, true).length, 1, "1 active stream");
+
+    yield checkSharingUI({audio: true});
+
+    // When both tabs use the same content process, the frame script for the
+    // first tab receives observer notifications for things happening in the
+    // second tab, so let's clear the observer call counts before we cleanup
+    // in the first tab.
+    yield ignoreObserversCalled();
+
+    // Close the first tab's stream and verify that all indicators are removed.
+    yield closeStream();
+
+    ok(!webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator hidden");
+    is(webrtcUI.getActiveStreams(true, true, true).length, 0, "0 active streams");
+  }
+},
+
+];
+
+function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab();
+  gBrowser.selectedTab = tab;
+  let browser = tab.linkedBrowser;
+
+  browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
+
+  browser.addEventListener("load", function() {
+    is(PopupNotifications._currentNotifications.length, 0,
+       "should start the test without any prior popup notification");
+    ok(gIdentityHandler._identityPopup.hidden,
+       "should start the test with the control center hidden");
+
+    Task.spawn(function* () {
+      yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
+
+      for (let testCase of gTests) {
+        info(testCase.desc);
+        yield testCase.run();
+      }
+    }).then(finish, ex => {
+     Cu.reportError(ex);
+     ok(false, "Unexpected Exception: " + ex);
+     finish();
+    });
+  }, {capture: true, once: true});
+  let rootDir = getRootDirectory(gTestPath);
+  rootDir = rootDir.replace("chrome://mochitests/content/",
+                            "https://example.com/");
+  content.location = rootDir + "get_user_media.html";
+}
--- a/browser/base/content/test/webrtc/head.js
+++ b/browser/base/content/test/webrtc/head.js
@@ -236,16 +236,28 @@ function expectNoObserverCalled(aIgnoreD
         }
       }
       resolve();
     });
     mm.sendAsyncMessage("Test:ExpectNoObserverCalled");
   });
 }
 
+function ignoreObserversCalled() {
+  return new Promise(resolve => {
+    let mm = _mm();
+    mm.addMessageListener("Test:ExpectNoObserverCalled:Reply",
+                          function listener() {
+      mm.removeMessageListener("Test:ExpectNoObserverCalled:Reply", listener);
+      resolve();
+    });
+    mm.sendAsyncMessage("Test:ExpectNoObserverCalled");
+  });
+}
+
 function promiseMessageReceived() {
   return new Promise((resolve, reject) => {
     let mm = _mm();
     mm.addMessageListener("Test:MessageReceived", function listener({data}) {
       mm.removeMessageListener("Test:MessageReceived", listener);
       resolve(data);
     });
     mm.sendAsyncMessage("Test:WaitForMessage");
@@ -434,17 +446,19 @@ function checkDeviceSelectors(aAudio, aV
 
   let screenSelector = document.getElementById("webRTC-selectWindowOrScreen");
   if (aScreen)
     ok(!screenSelector.hidden, "screen selector visible");
   else
     ok(screenSelector.hidden, "screen selector hidden");
 }
 
-function* checkSharingUI(aExpected, aWin = window) {
+// aExpected is for the current tab,
+// aExpectedGlobal is for all tabs.
+function* checkSharingUI(aExpected, aWin = window, aExpectedGlobal = null) {
   let doc = aWin.document;
   // First check the icon above the control center (i) icon.
   let identityBox = doc.getElementById("identity-box");
   ok(identityBox.hasAttribute("sharing"), "sharing attribute is set");
   let sharing = identityBox.getAttribute("sharing");
   if (aExpected.screen)
     is(sharing, "screen", "showing screen icon on the control center icon");
   else if (aExpected.video)
@@ -478,17 +492,17 @@ function* checkSharingUI(aExpected, aWin
       ok(!icon[0].classList.contains("in-use"),
          "if shown, the " + id + " icon should not have the in-use class");
       is(icon.length, 1, "should not show more than 1 " + id + " icon");
     }
   }
   aWin.gIdentityHandler._identityPopup.hidden = true;
 
   // Check the global indicators.
-  yield* assertWebRTCIndicatorStatus(aExpected);
+  yield* assertWebRTCIndicatorStatus(aExpectedGlobal || aExpected);
 }
 
 function* checkNotSharing() {
   Assert.deepEqual((yield getMediaCaptureState()), {},
                    "expected nothing to be shared");
 
   ok(!document.getElementById("identity-box").hasAttribute("sharing"),
      "no sharing indicator on the control center icon");
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -12,16 +12,17 @@ MOCHITEST_MANIFESTS += [
 
 MOCHITEST_CHROME_MANIFESTS += [
     'content/test/chrome/chrome.ini',
 ]
 
 BROWSER_CHROME_MANIFESTS += [
     'content/test/alerts/browser.ini',
     'content/test/captivePortal/browser.ini',
+    'content/test/forms/browser.ini',
     'content/test/general/browser.ini',
     'content/test/newtab/browser.ini',
     'content/test/pageinfo/browser.ini',
     'content/test/plugins/browser.ini',
     'content/test/popupNotifications/browser.ini',
     'content/test/referrer/browser.ini',
     'content/test/siteIdentity/browser.ini',
     'content/test/social/browser.ini',
new file mode 100644
--- /dev/null
+++ b/browser/components/.eslintrc.js
@@ -0,0 +1,9 @@
+"use strict";
+
+module.exports = {
+  rules: {
+    // XXX Bug 1326071 - This should be reduced down - probably to 20 or to
+    // be removed & synced with the toolkit/.eslintrc.js value.
+    "complexity": ["error", {"max": 69}],
+  }
+};
--- a/browser/components/contextualidentity/test/browser/browser_usercontext.js
+++ b/browser/components/contextualidentity/test/browser/browser_usercontext.js
@@ -64,17 +64,17 @@ add_task(function* test() {
 
     let tab = openTabInUserContext(BASE_URI, userContextId);
 
     // wait for load
     let browser = gBrowser.getBrowserForTab(tab);
     yield BrowserTestUtils.browserLoaded(browser);
 
     // get the title
-    let title = browser.contentTitle.trim().split("|");
+    let title = browser.contentDocument.title.trim().split("|");
 
     // check each item in the title and validate it meets expectatations
     for (let part of title) {
       let [storageMethodName, value] = part.split("=");
       is(value, expectedContext,
             "the title reflects the expected contextual identity of " +
             expectedContext + " for method " + storageMethodName + ": " + value);
     }
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -721,19 +721,18 @@ const CustomizableWidgets = [
           zoomFactor = Math.round(window.ZoomManager.zoom * 100);
         } catch (e) {}
         zoomResetButton.setAttribute("label", CustomizableUI.getLocalizedProperty(
           buttons[1], "label", [updateDisplay ? zoomFactor : 100]
         ));
       }
 
       // Register ourselves with the service so we know when the zoom prefs change.
-      Services.obs.addObserver(updateZoomResetButton, "browser-fullZoom:zoomChange", false);
-      Services.obs.addObserver(updateZoomResetButton, "browser-fullZoom:zoomReset", false);
       Services.obs.addObserver(updateZoomResetButton, "browser-fullZoom:location-change", false);
+      window.addEventListener("FullZoomChange", updateZoomResetButton);
 
       if (inPanel) {
         let panel = aDocument.getElementById(kPanelId);
         panel.addEventListener("popupshowing", updateZoomResetButton);
       } else {
         if (inToolbar) {
           let container = window.gBrowser.tabContainer;
           container.addEventListener("TabSelect", updateZoomResetButton);
@@ -800,19 +799,18 @@ const CustomizableWidgets = [
           updateZoomResetButton();
         }.bind(this),
 
         onWidgetInstanceRemoved: function(aWidgetId, aDoc) {
           if (aWidgetId != this.id || aDoc != aDocument)
             return;
 
           CustomizableUI.removeListener(listener);
-          Services.obs.removeObserver(updateZoomResetButton, "browser-fullZoom:zoomChange");
-          Services.obs.removeObserver(updateZoomResetButton, "browser-fullZoom:zoomReset");
           Services.obs.removeObserver(updateZoomResetButton, "browser-fullZoom:location-change");
+          window.removeEventListener("FullZoomChange", updateZoomResetButton);
           let panel = aDoc.getElementById(kPanelId);
           panel.removeEventListener("popupshowing", updateZoomResetButton);
           let container = aDoc.defaultView.gBrowser.tabContainer;
           container.removeEventListener("TabSelect", updateZoomResetButton);
         }.bind(this),
 
         onCustomizeStart(aWindow) {
           if (aWindow.document == aDocument) {
--- a/browser/components/customizableui/test/browser_1087303_button_preferences.js
+++ b/browser/components/customizableui/test/browser_1087303_button_preferences.js
@@ -37,14 +37,14 @@ function waitForPageLoad(aTab) {
   let timeoutId = setTimeout(() => {
     aTab.linkedBrowser.removeEventListener("load", onTabLoad, true);
     deferred.reject("Page didn't load within " + 20000 + "ms");
   }, 20000);
 
   function onTabLoad(event) {
     clearTimeout(timeoutId);
     aTab.linkedBrowser.removeEventListener("load", onTabLoad, true);
-    info("Tab event received: " + "load");
+    info("Tab event received: load");
     deferred.resolve();
  }
   aTab.linkedBrowser.addEventListener("load", onTabLoad, true, true);
   return deferred.promise;
 }
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -81,16 +81,18 @@ support-files =
 [browser_ext_runtime_openOptionsPage_uninstall.js]
 [browser_ext_runtime_setUninstallURL.js]
 [browser_ext_sessions_getRecentlyClosed.js]
 [browser_ext_sessions_getRecentlyClosed_private.js]
 [browser_ext_sessions_getRecentlyClosed_tabs.js]
 [browser_ext_sessions_restore.js]
 [browser_ext_sidebarAction.js]
 [browser_ext_sidebarAction_context.js]
+[browser_ext_sidebarAction_tabs.js]
+[browser_ext_sidebarAction_windows.js]
 [browser_ext_simple.js]
 [browser_ext_tab_runtimeConnect.js]
 [browser_ext_tabs_audio.js]
 [browser_ext_tabs_captureVisibleTab.js]
 [browser_ext_tabs_create.js]
 [browser_ext_tabs_create_invalid_url.js]
 [browser_ext_tabs_detectLanguage.js]
 [browser_ext_tabs_duplicate.js]
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
@@ -80,96 +80,23 @@ add_task(function* sidebar_two_sidebar_a
   yield extension3.unload();
 
   // We just close the sidebar on uninstall of the current sidebar.
   ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
 
   yield extension2.unload();
 });
 
-add_task(function* sidebar_windows() {
-  let extension = ExtensionTestUtils.loadExtension(extData);
-  yield extension.startup();
-  // Test sidebar is opened on install
-  yield extension.awaitMessage("sidebar");
-  ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible in first window");
-  // Check that the menuitem has our image styling.
-  let elements = document.getElementsByClassName("webextension-menuitem");
-  is(elements.length, 1, "have one menuitem");
-  let style = elements[0].getAttribute("style");
-  ok(style.includes("webextension-menuitem-image"), "this menu has style");
-
-  let secondSidebar = extension.awaitMessage("sidebar");
-
-  // SidebarUI relies on window.opener being set, which is normal behavior when
-  // using menu or key commands to open a new browser window.
-  let win = yield BrowserTestUtils.openNewBrowserWindow({opener: window});
-
-  yield secondSidebar;
-  ok(!win.document.getElementById("sidebar-box").hidden, "sidebar box is visible in second window");
-  // Check that the menuitem has our image styling.
-  elements = win.document.getElementsByClassName("webextension-menuitem");
-  is(elements.length, 1, "have one menuitem");
-  style = elements[0].getAttribute("style");
-  ok(style.includes("webextension-menuitem-image"), "this menu has style");
-
-  yield extension.unload();
-  yield BrowserTestUtils.closeWindow(win);
-});
-
 add_task(function* sidebar_empty_panel() {
   let extension = ExtensionTestUtils.loadExtension(extData);
   yield extension.startup();
   // Test sidebar is opened on install
   yield extension.awaitMessage("sidebar");
   ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible in first window");
   extension.sendMessage("set-panel");
   yield extension.awaitFinish();
   yield extension.unload();
 });
 
-add_task(function* sidebar_tab_query_bug_1340739() {
-  let data = {
-    manifest: {
-      "permissions": [
-        "tabs",
-      ],
-      "sidebar_action": {
-        "default_panel": "sidebar.html",
-      },
-    },
-    useAddonManager: "temporary",
-    files: {
-      "sidebar.html": `
-        <!DOCTYPE html>
-        <html>
-        <head><meta charset="utf-8"/>
-        <script src="sidebar.js"></script>
-        </head>
-        <body>
-        A Test Sidebar
-        </body></html>
-      `,
-      "sidebar.js": function() {
-        Promise.all([
-          browser.tabs.query({}).then((tabs) => {
-            browser.test.assertEq(1, tabs.length, "got tab without currentWindow");
-          }),
-          browser.tabs.query({currentWindow: true}).then((tabs) => {
-            browser.test.assertEq(1, tabs.length, "got tab with currentWindow");
-          }),
-        ]).then(() => {
-          browser.test.sendMessage("sidebar");
-        });
-      },
-    },
-  };
-
-  let extension = ExtensionTestUtils.loadExtension(data);
-  yield extension.startup();
-  yield extension.awaitMessage("sidebar");
-  yield extension.unload();
-});
-
 add_task(function* cleanup() {
   // This is set on initial sidebar install.
   Services.prefs.clearUserPref("extensions.sidebar-button.shown");
 });
copy from browser/components/extensions/test/browser/browser_ext_sidebarAction.js
copy to browser/components/extensions/test/browser/browser_ext_sidebarAction_tabs.js
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_tabs.js
@@ -1,136 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-let extData = {
-  manifest: {
-    "sidebar_action": {
-      "default_panel": "sidebar.html",
-    },
-  },
-  useAddonManager: "temporary",
-
-  files: {
-    "sidebar.html": `
-      <!DOCTYPE html>
-      <html>
-      <head><meta charset="utf-8"/>
-      <script src="sidebar.js"></script>
-      </head>
-      <body>
-      A Test Sidebar
-      </body></html>
-    `,
-
-    "sidebar.js": function() {
-      window.onload = () => {
-        browser.test.sendMessage("sidebar");
-      };
-    },
-  },
-
-  background: function() {
-    browser.test.onMessage.addListener(msg => {
-      if (msg === "set-panel") {
-        browser.sidebarAction.setPanel({panel: ""}).then(() => {
-          browser.test.notifyFail("empty panel settable");
-        }).catch(() => {
-          browser.test.notifyPass("unable to set empty panel");
-        });
-      }
-    });
-  },
-};
-
-add_task(function* sidebar_initial_install() {
-  ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
-  let extension = ExtensionTestUtils.loadExtension(extData);
-  yield extension.startup();
-  // Test sidebar is opened on install
-  yield extension.awaitMessage("sidebar");
-  ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible");
-  // Test toolbar button is available
-  ok(document.getElementById("sidebar-button"), "sidebar button is in UI");
-
-  yield extension.unload();
-  // Test that the sidebar was closed on unload.
-  ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
-
-  // Move toolbar button back to customization.
-  CustomizableUI.removeWidgetFromArea("sidebar-button", CustomizableUI.AREA_NAVBAR);
-  ok(!document.getElementById("sidebar-button"), "sidebar button is not in UI");
-});
-
-
-add_task(function* sidebar_two_sidebar_addons() {
-  let extension2 = ExtensionTestUtils.loadExtension(extData);
-  yield extension2.startup();
-  // Test sidebar is opened on install
-  yield extension2.awaitMessage("sidebar");
-  ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible");
-  // Test toolbar button is NOT available after first install
-  ok(!document.getElementById("sidebar-button"), "sidebar button is not in UI");
-
-  // Test second sidebar install opens new sidebar
-  let extension3 = ExtensionTestUtils.loadExtension(extData);
-  yield extension3.startup();
-  // Test sidebar is opened on install
-  yield extension3.awaitMessage("sidebar");
-  ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible");
-  yield extension3.unload();
-
-  // We just close the sidebar on uninstall of the current sidebar.
-  ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
-
-  yield extension2.unload();
-});
-
-add_task(function* sidebar_windows() {
-  let extension = ExtensionTestUtils.loadExtension(extData);
-  yield extension.startup();
-  // Test sidebar is opened on install
-  yield extension.awaitMessage("sidebar");
-  ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible in first window");
-  // Check that the menuitem has our image styling.
-  let elements = document.getElementsByClassName("webextension-menuitem");
-  is(elements.length, 1, "have one menuitem");
-  let style = elements[0].getAttribute("style");
-  ok(style.includes("webextension-menuitem-image"), "this menu has style");
-
-  let secondSidebar = extension.awaitMessage("sidebar");
-
-  // SidebarUI relies on window.opener being set, which is normal behavior when
-  // using menu or key commands to open a new browser window.
-  let win = yield BrowserTestUtils.openNewBrowserWindow({opener: window});
-
-  yield secondSidebar;
-  ok(!win.document.getElementById("sidebar-box").hidden, "sidebar box is visible in second window");
-  // Check that the menuitem has our image styling.
-  elements = win.document.getElementsByClassName("webextension-menuitem");
-  is(elements.length, 1, "have one menuitem");
-  style = elements[0].getAttribute("style");
-  ok(style.includes("webextension-menuitem-image"), "this menu has style");
-
-  yield extension.unload();
-  yield BrowserTestUtils.closeWindow(win);
-});
-
-add_task(function* sidebar_empty_panel() {
-  let extension = ExtensionTestUtils.loadExtension(extData);
-  yield extension.startup();
-  // Test sidebar is opened on install
-  yield extension.awaitMessage("sidebar");
-  ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible in first window");
-  extension.sendMessage("set-panel");
-  yield extension.awaitFinish();
-  yield extension.unload();
-});
-
 add_task(function* sidebar_tab_query_bug_1340739() {
   let data = {
     manifest: {
       "permissions": [
         "tabs",
       ],
       "sidebar_action": {
         "default_panel": "sidebar.html",
@@ -162,14 +38,15 @@ add_task(function* sidebar_tab_query_bug
       },
     },
   };
 
   let extension = ExtensionTestUtils.loadExtension(data);
   yield extension.startup();
   yield extension.awaitMessage("sidebar");
   yield extension.unload();
-});
 
-add_task(function* cleanup() {
+  // Move toolbar button back to customization.
+  CustomizableUI.removeWidgetFromArea("sidebar-button", CustomizableUI.AREA_NAVBAR);
+  ok(!document.getElementById("sidebar-button"), "sidebar button is not in UI");
   // This is set on initial sidebar install.
   Services.prefs.clearUserPref("extensions.sidebar-button.shown");
 });
copy from browser/components/extensions/test/browser/browser_ext_sidebarAction.js
copy to browser/components/extensions/test/browser/browser_ext_sidebarAction_windows.js
--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction.js
+++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_windows.js
@@ -23,73 +23,18 @@ let extData = {
     `,
 
     "sidebar.js": function() {
       window.onload = () => {
         browser.test.sendMessage("sidebar");
       };
     },
   },
-
-  background: function() {
-    browser.test.onMessage.addListener(msg => {
-      if (msg === "set-panel") {
-        browser.sidebarAction.setPanel({panel: ""}).then(() => {
-          browser.test.notifyFail("empty panel settable");
-        }).catch(() => {
-          browser.test.notifyPass("unable to set empty panel");
-        });
-      }
-    });
-  },
 };
 
-add_task(function* sidebar_initial_install() {
-  ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
-  let extension = ExtensionTestUtils.loadExtension(extData);
-  yield extension.startup();
-  // Test sidebar is opened on install
-  yield extension.awaitMessage("sidebar");
-  ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible");
-  // Test toolbar button is available
-  ok(document.getElementById("sidebar-button"), "sidebar button is in UI");
-
-  yield extension.unload();
-  // Test that the sidebar was closed on unload.
-  ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
-
-  // Move toolbar button back to customization.
-  CustomizableUI.removeWidgetFromArea("sidebar-button", CustomizableUI.AREA_NAVBAR);
-  ok(!document.getElementById("sidebar-button"), "sidebar button is not in UI");
-});
-
-
-add_task(function* sidebar_two_sidebar_addons() {
-  let extension2 = ExtensionTestUtils.loadExtension(extData);
-  yield extension2.startup();
-  // Test sidebar is opened on install
-  yield extension2.awaitMessage("sidebar");
-  ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible");
-  // Test toolbar button is NOT available after first install
-  ok(!document.getElementById("sidebar-button"), "sidebar button is not in UI");
-
-  // Test second sidebar install opens new sidebar
-  let extension3 = ExtensionTestUtils.loadExtension(extData);
-  yield extension3.startup();
-  // Test sidebar is opened on install
-  yield extension3.awaitMessage("sidebar");
-  ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible");
-  yield extension3.unload();
-
-  // We just close the sidebar on uninstall of the current sidebar.
-  ok(document.getElementById("sidebar-box").hidden, "sidebar box is not visible");
-
-  yield extension2.unload();
-});
-
 add_task(function* sidebar_windows() {
   let extension = ExtensionTestUtils.loadExtension(extData);
   yield extension.startup();
   // Test sidebar is opened on install
   yield extension.awaitMessage("sidebar");
   ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible in first window");
   // Check that the menuitem has our image styling.
   let elements = document.getElementsByClassName("webextension-menuitem");
@@ -108,68 +53,15 @@ add_task(function* sidebar_windows() {
   // Check that the menuitem has our image styling.
   elements = win.document.getElementsByClassName("webextension-menuitem");
   is(elements.length, 1, "have one menuitem");
   style = elements[0].getAttribute("style");
   ok(style.includes("webextension-menuitem-image"), "this menu has style");
 
   yield extension.unload();
   yield BrowserTestUtils.closeWindow(win);
-});
 
-add_task(function* sidebar_empty_panel() {
-  let extension = ExtensionTestUtils.loadExtension(extData);
-  yield extension.startup();
-  // Test sidebar is opened on install
-  yield extension.awaitMessage("sidebar");
-  ok(!document.getElementById("sidebar-box").hidden, "sidebar box is visible in first window");
-  extension.sendMessage("set-panel");
-  yield extension.awaitFinish();
-  yield extension.unload();
-});
-
-add_task(function* sidebar_tab_query_bug_1340739() {
-  let data = {
-    manifest: {
-      "permissions": [
-        "tabs",
-      ],
-      "sidebar_action": {
-        "default_panel": "sidebar.html",
-      },
-    },
-    useAddonManager: "temporary",
-    files: {
-      "sidebar.html": `
-        <!DOCTYPE html>
-        <html>
-        <head><meta charset="utf-8"/>
-        <script src="sidebar.js"></script>
-        </head>
-        <body>
-        A Test Sidebar
-        </body></html>
-      `,
-      "sidebar.js": function() {
-        Promise.all([
-          browser.tabs.query({}).then((tabs) => {
-            browser.test.assertEq(1, tabs.length, "got tab without currentWindow");
-          }),
-          browser.tabs.query({currentWindow: true}).then((tabs) => {
-            browser.test.assertEq(1, tabs.length, "got tab with currentWindow");
-          }),
-        ]).then(() => {
-          browser.test.sendMessage("sidebar");
-        });
-      },
-    },
-  };
-
-  let extension = ExtensionTestUtils.loadExtension(data);
-  yield extension.startup();
-  yield extension.awaitMessage("sidebar");
-  yield extension.unload();
-});
-
-add_task(function* cleanup() {
+  // Move toolbar button back to customization.
+  CustomizableUI.removeWidgetFromArea("sidebar-button", CustomizableUI.AREA_NAVBAR);
+  ok(!document.getElementById("sidebar-button"), "sidebar button is not in UI");
   // This is set on initial sidebar install.
   Services.prefs.clearUserPref("extensions.sidebar-button.shown");
 });
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
@@ -137,16 +137,86 @@ add_task(function* testBadPermissions() 
       await browser.pageAction.show(tab.id);
     },
   });
 
   yield BrowserTestUtils.removeTab(tab2);
   yield BrowserTestUtils.removeTab(tab1);
 });
 
+add_task(function* testMatchDataURI() {
+  const target = ExtensionTestUtils.loadExtension({
+    files: {
+      "page.html": `<!DOCTYPE html>
+        <meta charset="utf-8">
+        <script src="page.js"></script>
+        <iframe id="inherited" src="data:text/html;charset=utf-8,inherited"></iframe>
+      `,
+      "page.js": function() {
+        browser.test.onMessage.addListener((msg, url) => {
+          window.location.href = url;
+        });
+      },
+    },
+    background() {
+      browser.tabs.create({active: true, url: browser.runtime.getURL("page.html")});
+    },
+  });
+
+  const scripts = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: ["<all_urls>", "webNavigation"],
+    },
+    background() {
+      browser.webNavigation.onCompleted.addListener(({url, frameId}) => {
+        browser.test.log(`Document loading complete: ${url}`);
+        if (frameId === 0) {
+          browser.test.sendMessage("tab-ready", url);
+        }
+      });
+
+      browser.test.onMessage.addListener(async msg => {
+        browser.test.assertRejects(
+          browser.tabs.executeScript({
+            code: "location.href;",
+            allFrames: true,
+          }),
+          /No window matching/,
+          "Should not execute in `data:` frame");
+
+        browser.test.sendMessage("done");
+      });
+    },
+  });
+
+  yield scripts.startup();
+  yield target.startup();
+
+  // Test extension page with a data: iframe.
+  const page = yield scripts.awaitMessage("tab-ready");
+  ok(page.endsWith("page.html"), "Extension page loaded into a tab");
+
+  scripts.sendMessage("execute");
+  yield scripts.awaitMessage("done");
+
+  // Test extension tab navigated to a data: URI.
+  const data = "data:text/html;charset=utf-8,also-inherits";
+  target.sendMessage("navigate", data);
+
+  const url = yield scripts.awaitMessage("tab-ready");
+  is(url, data, "Extension tab navigated to a data: URI");
+
+  scripts.sendMessage("execute");
+  yield scripts.awaitMessage("done");
+
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+  yield scripts.unload();
+  yield target.unload();
+});
+
 add_task(function* testBadURL() {
   async function background() {
     let promises = [
       new Promise(resolve => {
         browser.tabs.executeScript({
           file: "http://example.com/script.js",
         }, result => {
           browser.test.assertEq(undefined, result, "Result value");
--- a/browser/components/migration/.eslintrc.js
+++ b/browser/components/migration/.eslintrc.js
@@ -14,17 +14,17 @@ module.exports = { // eslint-disable-lin
   "env": { "browser": true },
 
   "rules": {
     "block-scoped-var": "error",
     // "brace-style": ["warn", "1tbs", {"allowSingleLine": true}],
     "comma-dangle": "off",
     "comma-spacing": ["warn", {"before": false, "after": true}],
     "comma-style": ["warn", "last"],
-    // "complexity": "warn",
+    "complexity": ["error", {"max": 21}],
     "consistent-return": "error",
     //"curly": "error",
     "dot-notation": "error",
     "eol-last": "error",
     "indent": ["warn", 2, {"SwitchCase": 1, "ArrayExpression": "first", "ObjectExpression": "first"}],
     // "key-spacing": ["warn", {"beforeColon": false, "afterColon": true}],
     "keyword-spacing": "warn",
     "max-nested-callbacks": ["error", 3],
@@ -74,9 +74,8 @@ module.exports = { // eslint-disable-lin
     "space-infix-ops": ["warn", {"int32Hint": true}],
     // "space-unary-ops": ["warn", { "words": true, "nonwords": false }],
     "strict": ["error", "global"],
     "use-isnan": "error",
     "valid-typeof": "error",
     "yoda": "error"
   }
 };
-
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ b/browser/components/migration/ChromeProfileMigrator.js
@@ -76,49 +76,46 @@ function chromeTimeToDate(aTime) {
  * @return  Chrome time
  * @note    For details on Chrome time, see chromeTimeToDate.
  */
 function dateToChromeTime(aDate) {
   return (aDate * 10000 + S100NS_FROM1601TO1970) / S100NS_PER_MS;
 }
 
 /**
- * Insert bookmark items into specific folder.
+ * Converts an array of chrome bookmark objects into one our own places code
+ * understands.
  *
- * @param   parentGuid
- *          GUID of the folder where items will be inserted
  * @param   items
- *          bookmark items to be inserted
+ *          bookmark items to be inserted on this parent
  * @param   errorAccumulator
  *          function that gets called with any errors thrown so we don't drop them on the floor.
  */
-function* insertBookmarkItems(parentGuid, items, errorAccumulator) {
+function convertBookmarks(items, errorAccumulator) {
+  let itemsToInsert = [];
   for (let item of items) {
     try {
       if (item.type == "url") {
         if (item.url.trim().startsWith("chrome:")) {
           // Skip invalid chrome URIs. Creating an actual URI always reports
           // messages to the console, so we avoid doing that.
           continue;
         }
-        yield MigrationUtils.insertBookmarkWrapper({
-          parentGuid, url: item.url, title: item.name
-        });
+        itemsToInsert.push({url: item.url, title: item.name});
       } else if (item.type == "folder") {
-        let newFolderGuid = (yield MigrationUtils.insertBookmarkWrapper({
-          parentGuid, type: PlacesUtils.bookmarks.TYPE_FOLDER, title: item.name
-        })).guid;
-
-        yield insertBookmarkItems(newFolderGuid, item.children, errorAccumulator);
+        let folderItem = {type: PlacesUtils.bookmarks.TYPE_FOLDER, title: item.name};
+        folderItem.children = convertBookmarks(item.children, errorAccumulator);
+        itemsToInsert.push(folderItem);
       }
-    } catch (e) {
-      Cu.reportError(e);
-      errorAccumulator(e);
+    } catch (ex) {
+      Cu.reportError(ex);
+      errorAccumulator(ex);
     }
   }
+  return itemsToInsert;
 }
 
 function ChromeProfileMigrator() {
   let chromeUserDataFolder =
     getDataFolder(["Google", "Chrome"], ["Google", "Chrome"], ["google-chrome"]);
   this._chromeUserDataFolder = chromeUserDataFolder.exists() ?
     chromeUserDataFolder : null;
 }
@@ -255,57 +252,44 @@ function GetBookmarksResource(aProfileFo
 
   return {
     type: MigrationUtils.resourceTypes.BOOKMARKS,
 
     migrate(aCallback) {
       return Task.spawn(function* () {
         let gotErrors = false;
         let errorGatherer = function() { gotErrors = true };
-        let jsonStream = yield new Promise((resolve, reject) => {
-          let options = {
-            uri: NetUtil.newURI(bookmarksFile),
-            loadUsingSystemPrincipal: true
-          };
-          NetUtil.asyncFetch(options, (inputStream, resultCode) => {
-            if (Components.isSuccessCode(resultCode)) {
-              resolve(inputStream);
-            } else {
-              reject(new Error("Could not read Bookmarks file"));
-            }
-          });
-        });
-
         // Parse Chrome bookmark file that is JSON format
-        let bookmarkJSON = NetUtil.readInputStreamToString(
-          jsonStream, jsonStream.available(), { charset : "UTF-8" });
+        let bookmarkJSON = yield OS.File.read(bookmarksFile.path, {encoding: "UTF-8"});
         let roots = JSON.parse(bookmarkJSON).roots;
 
         // Importing bookmark bar items
         if (roots.bookmark_bar.children &&
             roots.bookmark_bar.children.length > 0) {
           // Toolbar
           let parentGuid = PlacesUtils.bookmarks.toolbarGuid;
+          let bookmarks = convertBookmarks(roots.bookmark_bar.children, errorGatherer);
           if (!MigrationUtils.isStartupMigration) {
             parentGuid =
               yield MigrationUtils.createImportedBookmarksFolder("Chrome", parentGuid);
           }
-          yield insertBookmarkItems(parentGuid, roots.bookmark_bar.children, errorGatherer);
+          yield MigrationUtils.insertManyBookmarksWrapper(bookmarks, parentGuid);
         }
 
         // Importing bookmark menu items
         if (roots.other.children &&
             roots.other.children.length > 0) {
           // Bookmark menu
           let parentGuid = PlacesUtils.bookmarks.menuGuid;
+          let bookmarks = convertBookmarks(roots.other.children, errorGatherer);
           if (!MigrationUtils.isStartupMigration) {
-            parentGuid =
-              yield MigrationUtils.createImportedBookmarksFolder("Chrome", parentGuid);
+            parentGuid
+              = yield MigrationUtils.createImportedBookmarksFolder("Chrome", parentGuid);
           }
-          yield insertBookmarkItems(parentGuid, roots.other.children, errorGatherer);
+          yield MigrationUtils.insertManyBookmarksWrapper(bookmarks, parentGuid);
         }
         if (gotErrors) {
           throw new Error("The migration included errors.");
         }
       }).then(() => aCallback(true),
               () => aCallback(false));
     }
   };
--- a/browser/components/migration/ESEDBReader.jsm
+++ b/browser/components/migration/ESEDBReader.jsm
@@ -427,17 +427,17 @@ ESEDB.prototype = {
   },
 
   _convertResult(column, buffer, err) {
     if (err != 0) {
       if (err == 1004) {
         // Deal with null values:
         buffer = null;
       } else {
-        Cu.reportError("Unexpected JET error: " + err + ";" + " retrieving value for column " + column.name);
+        Cu.reportError("Unexpected JET error: " + err + "; retrieving value for column " + column.name);
         throw new Error(convertESEError(err));
       }
     }
     if (column.type == "string") {
       return buffer ? buffer.readString() : "";
     }
     if (column.type == "boolean") {
       return buffer ? (buffer.value == 255) : false;
--- a/browser/components/migration/EdgeProfileMigrator.js
+++ b/browser/components/migration/EdgeProfileMigrator.js
@@ -13,16 +13,18 @@ Cu.import("resource://gre/modules/Task.j
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm"); /* globals MigratorPrototype */
 Cu.import("resource:///modules/MSMigrationUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ESEDBReader",
                                   "resource:///modules/ESEDBReader.jsm");
 
+Cu.importGlobalProperties(["URL"]);
+
 const kEdgeRegistryRoot = "SOFTWARE\\Classes\\Local Settings\\Software\\" +
   "Microsoft\\Windows\\CurrentVersion\\AppContainer\\Storage\\" +
   "microsoft.microsoftedge_8wekyb3d8bbwe\\MicrosoftEdge";
 const kEdgeDatabasePath = "AC\\MicrosoftEdge\\User\\Default\\DataStore\\Data\\";
 
 XPCOMUtils.defineLazyGetter(this, "gEdgeDatabase", function() {
   let edgeDir = MSMigrationUtils.getEdgeLocalDataFolder();
   if (!edgeDir) {
@@ -189,31 +191,28 @@ EdgeReadingListMigrator.prototype = {
     };
 
     let readingListItems = readTableFromEdgeDB("ReadingList", columnFn, filterFn);
     if (!readingListItems.length) {
       return;
     }
 
     let destFolderGuid = yield this._ensureReadingListFolder(parentGuid);
-    let exceptionThrown;
+    let bookmarks = [];
     for (let item of readingListItems) {
       let dateAdded = item.AddedDate || new Date();
-      yield MigrationUtils.insertBookmarkWrapper({
-        parentGuid: destFolderGuid, url: item.URL, title: item.Title, dateAdded
-      }).catch(ex => {
-        if (!exceptionThrown) {
-          exceptionThrown = ex;
-        }
-        Cu.reportError(ex);
-      });
+      // Avoid including broken URLs:
+      try {
+        new URL(item.URL);
+      } catch (ex) {
+        continue;
+      }
+      bookmarks.push({ url: item.URL, title: item.Title, dateAdded });
     }
-    if (exceptionThrown) {
-      throw exceptionThrown;
-    }
+    yield MigrationUtils.insertManyBookmarksWrapper(readingListItems, destFolderGuid);
   }),
 
   _ensureReadingListFolder: Task.async(function*(parentGuid) {
     if (!this.__readingListFolderGuid) {
       let folderTitle = MigrationUtils.getLocalizedString("importedEdgeReadingList");
       let folderSpec = {type: PlacesUtils.bookmarks.TYPE_FOLDER, parentGuid, title: folderTitle};
       this.__readingListFolderGuid = (yield MigrationUtils.insertBookmarkWrapper(folderSpec)).guid;
     }
@@ -235,83 +234,40 @@ EdgeBookmarksMigrator.prototype = {
   get exists() {
     if (!("_exists" in this)) {
       this._exists = !!this.db;
     }
     return this._exists;
   },
 
   migrate(callback) {
-    this._migrateBookmarks(PlacesUtils.bookmarks.menuGuid).then(
+    this._migrateBookmarks().then(
       () => callback(true),
       ex => {
         Cu.reportError(ex);
         callback(false);
       }
     );
   },
 
-  _migrateBookmarks: Task.async(function*(rootGuid) {
-    let {bookmarks, folderMap} = this._fetchBookmarksFromDB();
-    if (!bookmarks.length) {
-      return;
-    }
-    yield this._importBookmarks(bookmarks, folderMap, rootGuid);
-  }),
-
-  _importBookmarks: Task.async(function*(bookmarks, folderMap, rootGuid) {
-    if (!MigrationUtils.isStartupMigration) {
-      rootGuid =
-        yield MigrationUtils.createImportedBookmarksFolder("Edge", rootGuid);
-    }
-
-    let exceptionThrown;
-    for (let bookmark of bookmarks) {
-      // If this is a folder, we might have created it already to put other bookmarks in.
-      if (bookmark.IsFolder && bookmark._guid) {
-        continue;
+  _migrateBookmarks: Task.async(function*() {
+    let {toplevelBMs, toolbarBMs} = this._fetchBookmarksFromDB();
+    if (toplevelBMs.length) {
+      let parentGuid = PlacesUtils.bookmarks.menuGuid;
+      if (!MigrationUtils.isStartupMigration) {
+        parentGuid = yield MigrationUtils.createImportedBookmarksFolder("Edge", parentGuid);
       }
-
-      // If this is a folder, just create folders up to and including that folder.
-      // Otherwise, create folders until we have a parent for this bookmark.
-      // This avoids duplicating logic for the bookmarks bar.
-      let folderId = bookmark.IsFolder ? bookmark.ItemId : bookmark.ParentId;
-      let parentGuid = yield this._getGuidForFolder(folderId, folderMap, rootGuid).catch(ex => {
-        if (!exceptionThrown) {
-          exceptionThrown = ex;
-        }
-        Cu.reportError(ex);
-      });
-
-      // If this was a folder, we're done with this item
-      if (bookmark.IsFolder) {
-        continue;
+      yield MigrationUtils.insertManyBookmarksWrapper(toplevelBMs, parentGuid);
+    }
+    if (toolbarBMs.length) {
+      let parentGuid = PlacesUtils.bookmarks.toolbarGuid;
+      if (!MigrationUtils.isStartupMigration) {
+        parentGuid = yield MigrationUtils.createImportedBookmarksFolder("Edge", parentGuid);
       }
-
-      if (!parentGuid) {
-        // If we couldn't sort out a parent, fall back to importing on the root:
-        parentGuid = rootGuid;
-      }
-      let placesInfo = {
-        parentGuid,
-        url: bookmark.URL,
-        dateAdded: bookmark.DateUpdated || new Date(),
-        title: bookmark.Title,
-      };
-
-      yield MigrationUtils.insertBookmarkWrapper(placesInfo).catch(ex => {
-        if (!exceptionThrown) {
-          exceptionThrown = ex;
-        }
-        Cu.reportError(ex);
-      });
-    }
-
-    if (exceptionThrown) {
-      throw exceptionThrown;
+      yield MigrationUtils.insertManyBookmarksWrapper(toolbarBMs, parentGuid);
     }
   }),
 
   _fetchBookmarksFromDB() {
     let folderMap = new Map();
     let columns = [
       {name: "URL", type: "string"},
       {name: "Title", type: "string"},
@@ -326,54 +282,64 @@ EdgeBookmarksMigrator.prototype = {
         return false;
       }
       if (row.IsFolder) {
         folderMap.set(row.ItemId, row);
       }
       return true;
     };
     let bookmarks = readTableFromEdgeDB(this.TABLE_NAME, columns, filterFn, this.db);
-    return {bookmarks, folderMap};
-  },
-
-  _getGuidForFolder: Task.async(function*(folderId, folderMap, rootGuid) {
-    // If the folderId is not known as a folder in the folder map, we assume
-    // we just need the root
-    if (!folderMap.has(folderId)) {
-      return rootGuid;
-    }
-    let folder = folderMap.get(folderId);
-    // If the folder already has a places guid, just return that.
-    if (folder._guid) {
-      return folder._guid;
-    }
+    let toplevelBMs = [], toolbarBMs = [];
+    for (let bookmark of bookmarks) {
+      let bmToInsert;
+      // Ignore invalid URLs:
+      if (!bookmark.IsFolder) {
+        try {
+          new URL(bookmark.URL);
+        } catch (ex) {
+          Cu.reportError(`Ignoring ${bookmark.URL} when importing from Edge because of exception: ${ex}`);
+          continue;
+        }
+        bmToInsert = {
+          dateAdded: bookmark.DateUpdated || new Date(),
+          title: bookmark.Title,
+          url: bookmark.URL,
+        };
+      } else /* bookmark.IsFolder */ {
+        // Ignore the favorites bar bookmark itself.
+        if (bookmark.Title == "_Favorites_Bar_") {
+          continue;
+        }
+        if (!bookmark._childrenRef) {
+          bookmark._childrenRef = [];
+        }
+        bmToInsert = {
+          title: bookmark.Title,
+          type: PlacesUtils.bookmarks.TYPE_FOLDER,
+          dateAdded: bookmark.DateUpdated || new Date(),
+          children: bookmark._childrenRef,
+        };
+      }
 
-    // Hacks! The bookmarks bar is special:
-    if (folder.Title == "_Favorites_Bar_") {
-      let toolbarGuid = PlacesUtils.bookmarks.toolbarGuid;
-      if (!MigrationUtils.isStartupMigration) {
-        toolbarGuid =
-          yield MigrationUtils.createImportedBookmarksFolder("Edge", toolbarGuid);
+      if (!folderMap.has(bookmark.ParentId)) {
+        toplevelBMs.push(bmToInsert);
+      } else {
+        let parent = folderMap.get(bookmark.ParentId);
+        if (parent.Title == "_Favorites_Bar_") {
+          toolbarBMs.push(bmToInsert);
+          continue;
+        }
+        if (!parent._childrenRef) {
+          parent._childrenRef = [];
+        }
+        parent._childrenRef.push(bmToInsert);
       }
-      folder._guid = toolbarGuid;
-      return folder._guid;
     }
-    // Otherwise, get the right parent guid recursively:
-    let parentGuid = yield this._getGuidForFolder(folder.ParentId, folderMap, rootGuid);
-    let folderInfo = {
-      title: folder.Title,
-      type: PlacesUtils.bookmarks.TYPE_FOLDER,
-      dateAdded: folder.DateUpdated || new Date(),
-      parentGuid,
-    };
-    // and add ourselves as a kid, and return the guid we got.
-    let parentBM = yield MigrationUtils.insertBookmarkWrapper(folderInfo);
-    folder._guid = parentBM.guid;
-    return folder._guid;
-  }),
+    return {toplevelBMs, toolbarBMs};
+  },
 };
 
 function EdgeProfileMigrator() {
   this.wrappedJSObject = this;
 }
 
 EdgeProfileMigrator.prototype = Object.create(MigratorPrototype);
 
--- a/browser/components/migration/MSMigrationUtils.jsm
+++ b/browser/components/migration/MSMigrationUtils.jsm
@@ -372,75 +372,71 @@ Bookmarks.prototype = {
           yield MigrationUtils.createImportedBookmarksFolder(this.importedAppLabel, folderGuid);
       }
       yield this._migrateFolder(this._favoritesFolder, folderGuid);
     }.bind(this)).then(() => aCallback(true),
                        e => { Cu.reportError(e); aCallback(false) });
   },
 
   _migrateFolder: Task.async(function* (aSourceFolder, aDestFolderGuid) {
+    let bookmarks = yield this._getBookmarksInFolder(aSourceFolder);
+    if (bookmarks.length) {
+      yield MigrationUtils.insertManyBookmarksWrapper(bookmarks, aDestFolderGuid);
+    }
+  }),
+
+  _getBookmarksInFolder: Task.async(function* (aSourceFolder) {
     // TODO (bug 741993): the favorites order is stored in the Registry, at
     // HCU\Software\Microsoft\Windows\CurrentVersion\Explorer\MenuOrder\Favorites
     // for IE, and in a similar location for Edge.
     // Until we support it, bookmarks are imported in alphabetical order.
     let entries = aSourceFolder.directoryEntries;
-    let succeeded = true;
+    let rv = [];
     while (entries.hasMoreElements()) {
       let entry = entries.getNext().QueryInterface(Ci.nsIFile);
       try {
         // Make sure that entry.path == entry.target to not follow .lnk folder
         // shortcuts which could lead to infinite cycles.
         // Don't use isSymlink(), since it would throw for invalid
         // lnk files pointing to URLs or to unresolvable paths.
         if (entry.path == entry.target && entry.isDirectory()) {
-          let folderGuid;
-          if (entry.leafName == this._toolbarFolderName &&
-              entry.parent.equals(this._favoritesFolder)) {
+          let isBookmarksFolder = entry.leafName == this._toolbarFolderName &&
+                                  entry.parent.equals(this._favoritesFolder);
+          if (isBookmarksFolder && entry.isReadable()) {
             // Import to the bookmarks toolbar.
-            folderGuid = PlacesUtils.bookmarks.toolbarGuid;
+            let folderGuid = PlacesUtils.bookmarks.toolbarGuid;
             if (!MigrationUtils.isStartupMigration) {
               folderGuid =
                 yield MigrationUtils.createImportedBookmarksFolder(this.importedAppLabel, folderGuid);
             }
-          } else {
-            // Import to a new folder.
-            folderGuid = (yield MigrationUtils.insertBookmarkWrapper({
+            yield this._migrateFolder(entry, folderGuid);
+          } else if (entry.isReadable()) {
+            let childBookmarks = yield this._getBookmarksInFolder(entry);
+            rv.push({
               type: PlacesUtils.bookmarks.TYPE_FOLDER,
-              parentGuid: aDestFolderGuid,
-              title: entry.leafName
-            })).guid;
-          }
-
-          if (entry.isReadable()) {
-            // Recursively import the folder.
-            yield this._migrateFolder(entry, folderGuid);
+              title: entry.leafName,
+              children: childBookmarks,
+            });
           }
         } else {
           // Strip the .url extension, to both check this is a valid link file,
           // and get the associated title.
           let matches = entry.leafName.match(/(.+)\.url$/i);
           if (matches) {
             let fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"].
                               getService(Ci.nsIFileProtocolHandler);
             let uri = fileHandler.readURLFile(entry);
-            let title = matches[1];
-
-            yield MigrationUtils.insertBookmarkWrapper({
-              parentGuid: aDestFolderGuid, url: uri, title
-            });
+            rv.push({url: uri, title: matches[1]});
           }
         }
       } catch (ex) {
         Components.utils.reportError("Unable to import " + this.importedAppLabel + " favorite (" + entry.leafName + "): " + ex);
-        succeeded = false;
       }
     }
-    if (!succeeded) {
-      throw new Error("Failed to import all bookmarks correctly.");
-    }
+    return rv;
   }),
 
 };
 
 function Cookies(migrationType) {
   this._migrationType = migrationType;
 }
 
--- a/browser/components/migration/MigrationUtils.jsm
+++ b/browser/components/migration/MigrationUtils.jsm
@@ -986,16 +986,30 @@ this.MigrationUtils = Object.freeze({
       let {guid, lastModified, type} = bm;
       gUndoData.get("bookmarks").push({
         parentGuid, guid, lastModified, type
       });
       return bm;
     });
   },
 
+  insertManyBookmarksWrapper(bookmarks, parent) {
+    let insertionPromise = PlacesUtils.bookmarks.insertTree({guid: parent, children: bookmarks});
+    return insertionPromise.then(insertedItems => {
+      this._importQuantities.bookmarks += insertedItems.length;
+      if (gKeepUndoData) {
+        let bmData = gUndoData.get("bookmarks");
+        for (let bm of insertedItems) {
+          let {parentGuid, guid, lastModified, type} = bm;
+          bmData.push({parentGuid, guid, lastModified, type});
+        }
+      }
+    }, ex => Cu.reportError(ex));
+  },
+
   insertVisitsWrapper(places, options) {
     this._importQuantities.history += places.length;
     if (gKeepUndoData) {
       this._updateHistoryUndo(places);
     }
     return PlacesUtils.asyncHistory.updatePlaces(places, options, true);
   },
 
--- a/browser/components/migration/SafariProfileMigrator.js
+++ b/browser/components/migration/SafariProfileMigrator.js
@@ -22,16 +22,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/PropertyListUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
                                   "resource://gre/modules/FormHistory.jsm");
 
+Cu.importGlobalProperties(["URL"]);
+
 function Bookmarks(aBookmarksFile) {
   this._file = aBookmarksFile;
 }
 Bookmarks.prototype = {
   type: MigrationUtils.resourceTypes.BOOKMARKS,
 
   migrate: function B_migrate(aCallback) {
     return Task.spawn(function* () {
@@ -142,43 +144,48 @@ Bookmarks.prototype = {
     if (folderGuid == -1)
       throw new Error("Invalid folder GUID");
 
     yield this._migrateEntries(entriesFiltered, folderGuid);
   }),
 
   // migrate the given array of safari bookmarks to the given places
   // folder.
-  _migrateEntries: Task.async(function* (entries, parentGuid) {
-    for (let entry of entries) {
+  _migrateEntries(entries, parentGuid) {
+    let convertedEntries = this._convertEntries(entries);
+    return MigrationUtils.insertManyBookmarksWrapper(convertedEntries, parentGuid);
+  },
+
+  _convertEntries(entries) {
+    return entries.map(function(entry) {
       let type = entry.get("WebBookmarkType");
       if (type == "WebBookmarkTypeList" && entry.has("Children")) {
-        let title = entry.get("Title");
-        let newFolderGuid = (yield MigrationUtils.insertBookmarkWrapper({
-          parentGuid, type: PlacesUtils.bookmarks.TYPE_FOLDER, title
-        })).guid;
-
-        // Empty folders may not have a children array.
-        if (entry.has("Children"))
-          yield this._migrateEntries(entry.get("Children"), newFolderGuid, false);
-      } else if (type == "WebBookmarkTypeLeaf" && entry.has("URLString")) {
+        return {
+          title: entry.get("Title"),
+          type: PlacesUtils.bookmarks.TYPE_FOLDER,
+          children: this._convertEntries(entry.get("Children")),
+        };
+      }
+      if (type == "WebBookmarkTypeLeaf" && entry.has("URLString")) {
+        // Check we understand this URL before adding it:
+        let url = entry.get("URLString");
+        try {
+          new URL(url);
+        } catch (ex) {
+          Cu.reportError(`Ignoring ${url} when importing from Safari because of exception: ${ex}`);
+          return null;
+        }
         let title;
         if (entry.has("URIDictionary"))
           title = entry.get("URIDictionary").get("title");
-
-        try {
-          yield MigrationUtils.insertBookmarkWrapper({
-            parentGuid, url: entry.get("URLString"), title
-          });
-        } catch (ex) {
-          Cu.reportError("Invalid Safari bookmark: " + ex);
-        }
+        return { url, title };
       }
-    }
-  })
+      return null;
+    }).filter(e => !!e);
+  },
 };
 
 function History(aHistoryFile) {
   this._file = aHistoryFile;
 }
 History.prototype = {
   type: MigrationUtils.resourceTypes.HISTORY,
 
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/test_Chrome_bookmarks.js
@@ -0,0 +1,105 @@
+"use strict";
+
+Cu.import("resource://gre/modules/AppConstants.jsm");
+
+add_task(function* () {
+  let rootDir = do_get_file("chromefiles/");
+  let pathId;
+  let subDirs = ["Google", "Chrome"];
+  if (AppConstants.platform == "macosx") {
+    subDirs.unshift("Application Support");
+    pathId = "ULibDir";
+  } else if (AppConstants.platform == "win") {
+    subDirs.push("User Data");
+    pathId = "LocalAppData";
+  } else {
+    subDirs = [".config", "google-chrome"];
+    pathId = "Home";
+  }
+  registerFakePath(pathId, rootDir);
+
+  let target = rootDir.clone();
+  // Pretend this is the default profile
+  subDirs.push("Default");
+  while (subDirs.length) {
+    target.append(subDirs.shift());
+  }
+  // We don't import osfile.jsm until after registering the fake path, because
+  // importing osfile will sometimes greedily fetch certain path identifiers
+  // from the dir service, which means they get cached, which means we can't
+  // register a fake path for them anymore.
+  Cu.import("resource://gre/modules/osfile.jsm"); /* globals OS */
+  yield OS.File.makeDir(target.path, {from: rootDir.parent.path, ignoreExisting: true});
+
+  target.append("Bookmarks");
+  yield OS.File.remove(target.path, {ignoreAbsent: true});
+
+  let bookmarksData = {roots: {bookmark_bar: {children: []}, other: {children: []}}};
+  const MAX_BMS = 100;
+  let barKids = bookmarksData.roots.bookmark_bar.children;
+  let menuKids = bookmarksData.roots.other.children;
+  let currentMenuKids = menuKids;
+  let currentBarKids = barKids;
+  for (let i = 0; i < MAX_BMS; i++) {
+    currentBarKids.push({
+      url: "https://www.chrome-bookmark-bar-bookmark" + i + ".com",
+      name: "bookmark " + i,
+      type: "url",
+    });
+    currentMenuKids.push({
+      url: "https://www.chrome-menu-bookmark" + i + ".com",
+      name: "bookmark for menu " + i,
+      type: "url",
+    });
+    if (i % 20 == 19) {
+      let nextFolder = {
+        name: "toolbar folder " + Math.ceil(i / 20),
+        type: "folder",
+        children: [],
+      };
+      currentBarKids.push(nextFolder);
+      currentBarKids = nextFolder.children;
+
+      nextFolder = {
+        name: "menu folder " + Math.ceil(i / 20),
+        type: "folder",
+        children: [],
+      };
+      currentMenuKids.push(nextFolder);
+      currentMenuKids = nextFolder.children;
+    }
+  }
+
+  yield OS.File.writeAtomic(target.path, JSON.stringify(bookmarksData), {encoding: "utf-8"});
+
+  let migrator = MigrationUtils.getMigrator("chrome");
+  // Sanity check for the source.
+  Assert.ok(migrator.sourceExists);
+
+  let itemsSeen = {bookmarks: 0, folders: 0};
+  let bmObserver = {
+    onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle) {
+      if (!aTitle.includes("Chrome")) {
+        itemsSeen[aItemType == PlacesUtils.bookmarks.TYPE_FOLDER ? "folders" : "bookmarks"]++;
+      }
+    },
+    onBeginUpdateBatch() {},
+    onEndUpdateBatch() {},
+    onItemRemoved() {},
+    onItemChanged() {},
+    onItemVisited() {},
+    onItemMoved() {},
+  };
+
+  PlacesUtils.bookmarks.addObserver(bmObserver, false);
+  const PROFILE = {
+    id: "Default",
+    name: "Default",
+  };
+  yield promiseMigration(migrator, MigrationUtils.resourceTypes.BOOKMARKS, PROFILE);
+  PlacesUtils.bookmarks.removeObserver(bmObserver);
+
+  Assert.equal(itemsSeen.bookmarks, 200, "Should have seen 200 bookmarks.");
+  Assert.equal(itemsSeen.folders, 10, "Should have seen 10 folders.");
+  Assert.equal(MigrationUtils._importQuantities.bookmarks, itemsSeen.bookmarks + itemsSeen.folders, "Telemetry reporting correct.");
+});
--- a/browser/components/migration/tests/unit/xpcshell.ini
+++ b/browser/components/migration/tests/unit/xpcshell.ini
@@ -2,16 +2,17 @@
 head = head_migration.js
 firefox-appdir = browser
 skip-if = toolkit == 'android'
 support-files =
   Library/**
   AppData/**
 
 [test_automigration.js]
+[test_Chrome_bookmarks.js]
 [test_Chrome_cookies.js]
 skip-if = os != "mac" # Relies on ULibDir
 [test_Chrome_passwords.js]
 skip-if = os != "win"
 [test_Edge_availability.js]
 [test_Edge_db_migration.js]
 skip-if = os != "win" || os_version == "5.1" || os_version == "5.2" # Relies on post-XP bits of ESEDB
 [test_fx_telemetry.js]
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js
@@ -25,17 +25,17 @@ add_task(function* test() {
     page_without_title = app_name;
     about_pb_title = "Open a private window?";
     pb_page_with_title = test_title + " - (Private Browsing)";
     pb_page_without_title = app_name + " - (Private Browsing)";
     pb_about_pb_title = "Private Browsing - (Private Browsing)";
   } else {
     page_with_title = test_title + " - " + app_name;
     page_without_title = app_name;
-    about_pb_title = "Open a private window?" + " - " + app_name;
+    about_pb_title = "Open a private window? - " + app_name;
     pb_page_with_title = test_title + " - " + app_name + " (Private Browsing)";
     pb_page_without_title = app_name + " (Private Browsing)";
     pb_about_pb_title = "Private Browsing - " + app_name + " (Private Browsing)";
   }
 
   async function testTabTitle(aWindow, url, insidePB, expected_title) {
     let tab = (await BrowserTestUtils.openNewForegroundTab(aWindow.gBrowser));
     await BrowserTestUtils.loadURI(tab.linkedBrowser, url);
--- a/browser/components/sessionstore/test/browser_500328.js
+++ b/browser/components/sessionstore/test/browser_500328.js
@@ -1,115 +1,120 @@
 /* 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/. */
 
-function checkState(tab) {
+let checkState = Task.async(function*(browser) {
   // Go back and then forward, and make sure that the state objects received
   // from the popState event are as we expect them to be.
   //
   // We also add a node to the document's body when after going back and make
   // sure it's still there after we go forward -- this is to test that the two
   // history entries correspond to the same document.
 
+  let deferred = {};
+  deferred.promise = new Promise(resolve => deferred.resolve = resolve);
+
   let popStateCount = 0;
 
-  tab.linkedBrowser.addEventListener("popstate", function(aEvent) {
-    let contentWindow = tab.linkedBrowser.contentWindow;
+  browser.addEventListener("popstate", function(aEvent) {
     if (popStateCount == 0) {
       popStateCount++;
 
-      is(tab.linkedBrowser.contentWindow.testState, "foo",
-         "testState after going back");
+      ok(aEvent.state, "Event should have a state property.");
 
-      ok(aEvent.state, "Event should have a state property.");
-      is(JSON.stringify(tab.linkedBrowser.contentWindow.history.state), JSON.stringify({obj1:1}),
-         "first popstate object.");
+      ContentTask.spawn(browser, null, function() {
+        is(content.testState, "foo",
+           "testState after going back");
+        is(JSON.stringify(content.history.state), JSON.stringify({obj1:1}),
+           "first popstate object.");
 
-      // Add a node with id "new-elem" to the document.
-      let doc = contentWindow.document;
-      ok(!doc.getElementById("new-elem"),
-         "doc shouldn't contain new-elem before we add it.");
-      let elem = doc.createElement("div");
-      elem.id = "new-elem";
-      doc.body.appendChild(elem);
-
-      tab.linkedBrowser.goForward();
+        // Add a node with id "new-elem" to the document.
+        let doc = content.document;
+        ok(!doc.getElementById("new-elem"),
+           "doc shouldn't contain new-elem before we add it.");
+        let elem = doc.createElement("div");
+        elem.id = "new-elem";
+        doc.body.appendChild(elem);
+      }).then(() => {
+        browser.goForward();
+      });
     } else if (popStateCount == 1) {
       popStateCount++;
       // When content fires a PopStateEvent and we observe it from a chrome event
       // listener (as we do here, and, thankfully, nowhere else in the tree), the
       // state object will be a cross-compartment wrapper to an object that was
       // deserialized in the content scope. And in this case, since RegExps are
       // not currently Xrayable (see bug 1014991), trying to pull |obj3| (a RegExp)
       // off of an Xrayed Object won't work. So we need to waive.
-      ContentTask.spawn(tab.linkedBrowser, aEvent.state, function(state) {
+      ContentTask.spawn(browser, aEvent.state, function(state) {
         Assert.equal(Cu.waiveXrays(state).obj3.toString(),
           "/^a$/", "second popstate object.");
-      }).then(function() {
+
         // Make sure that the new-elem node is present in the document.  If it's
         // not, then this history entry has a different doc identifier than the
         // previous entry, which is bad.
-        let doc = contentWindow.document;
+        let doc = content.document;
         let newElem = doc.getElementById("new-elem");
         ok(newElem, "doc should contain new-elem.");
         newElem.remove();
         ok(!doc.getElementById("new-elem"), "new-elem should be removed.");
-
-        tab.linkedBrowser.removeEventListener("popstate", arguments.callee, true);
-        gBrowser.removeTab(tab);
-        finish();
+      }).then(() => {
+        browser.removeEventListener("popstate", arguments.callee, true);
+        deferred.resolve();
       });
     }
   });
 
   // Set some state in the page's window.  When we go back(), the page should
   // be retrieved from bfcache, and this state should still be there.
-  tab.linkedBrowser.contentWindow.testState = "foo";
+  yield ContentTask.spawn(browser, null, function() {
+    content.testState = "foo";
+  });
 
   // Now go back.  This should trigger the popstate event handler above.
-  tab.linkedBrowser.goBack();
-}
+  browser.goBack();
 
-function test() {
+  yield deferred.promise;
+});
+
+add_task(function* test() {
   // Tests session restore functionality of history.pushState and
   // history.replaceState().  (Bug 500328)
 
-  waitForExplicitFinish();
-
   // We open a new blank window, let it load, and then load in
   // http://example.com.  We need to load the blank window first, otherwise the
   // docshell gets confused and doesn't have a current history entry.
-  let tab = gBrowser.addTab("about:blank");
-  let browser = tab.linkedBrowser;
-
-  promiseBrowserLoaded(browser).then(() => {
-    browser.loadURI("http://example.com", null, null);
+  let state;
+  yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* (browser) {
+    BrowserTestUtils.loadURI(browser, "http://example.com");
+    yield BrowserTestUtils.browserLoaded(browser);
 
-    promiseBrowserLoaded(browser).then(() => {
-      // After these push/replaceState calls, the window should have three
-      // history entries:
-      //   testURL        (state object: null)          <-- oldest
-      //   testURL        (state object: {obj1:1})
-      //   testURL?page2  (state object: {obj3:/^a$/})  <-- newest
-      function contentTest() {
-        let history = content.window.history;
-        history.pushState({obj1:1}, "title-obj1");
-        history.pushState({obj2:2}, "title-obj2", "?page2");
-        history.replaceState({obj3:/^a$/}, "title-obj3");
-      }
-      ContentTask.spawn(browser, null, contentTest).then(function() {
-        return TabStateFlusher.flush(tab.linkedBrowser);
-      }).then(() => {
-        let state = ss.getTabState(tab);
-        gBrowser.removeTab(tab);
+    // After these push/replaceState calls, the window should have three
+    // history entries:
+    //   testURL        (state object: null)          <-- oldest
+    //   testURL        (state object: {obj1:1})
+    //   testURL?page2  (state object: {obj3:/^a$/})  <-- newest
+    function contentTest() {
+      let history = content.window.history;
+      history.pushState({obj1:1}, "title-obj1");
+      history.pushState({obj2:2}, "title-obj2", "?page2");
+      history.replaceState({obj3:/^a$/}, "title-obj3");
+    }
+    yield ContentTask.spawn(browser, null, contentTest);
+    yield TabStateFlusher.flush(browser);
 
-        // Restore the state into a new tab.  Things don't work well when we
-        // restore into the old tab, but that's not a real use case anyway.
-        let tab2 = gBrowser.addTab("about:blank");
-        ss.setTabState(tab2, state, true);
+    state = ss.getTabState(gBrowser.getTabForBrowser(browser));
+  });
+
+  // Restore the state into a new tab.  Things don't work well when we
+  // restore into the old tab, but that's not a real use case anyway.
+  yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function* (browser) {
+    let tab2 = gBrowser.getTabForBrowser(browser);
 
-        // Run checkState() once the tab finishes loading its restored state.
-        promiseTabRestored(tab2).then(() => checkState(tab2));
-      });
-    });
+    let tabRestoredPromise = promiseTabRestored(tab2);
+    ss.setTabState(tab2, state, true);
+
+    // Run checkState() once the tab finishes loading its restored state.
+    yield tabRestoredPromise;
+    yield checkState(browser);
   });
-}
+});
--- a/browser/components/sessionstore/test/browser_687710_2.js
+++ b/browser/components/sessionstore/test/browser_687710_2.js
@@ -1,8 +1,9 @@
+/* eslint-env mozilla/frame-script */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that the fix for bug 687710 isn't too aggressive -- shentries which are
 // cousins should be able to share bfcache entries.
 
 var stateBackup = ss.getBrowserState();
 
@@ -20,52 +21,48 @@ var state = {entries:[
     url: "http://example.com?1#a",
     triggeringPrincipal_base64,
     children: [{ docIdentifier: 10,
                  url: "http://example.com?10#aa",
                  triggeringPrincipal_base64 }]
   }
 ]};
 
-function test() {
-  waitForExplicitFinish();
+add_task(function* test() {
+  let tab = gBrowser.addTab("about:blank");
+  yield promiseTabState(tab, state);
+  yield ContentTask.spawn(tab.linkedBrowser, null, function() {
+    function compareEntries(i, j, history) {
+      let e1 = history.getEntryAtIndex(i, false)
+                      .QueryInterface(Ci.nsISHEntry)
+                      .QueryInterface(Ci.nsISHContainer);
+
+      let e2 = history.getEntryAtIndex(j, false)
+                      .QueryInterface(Ci.nsISHEntry)
+                      .QueryInterface(Ci.nsISHContainer);
 
-  registerCleanupFunction(function() {
-    ss.setBrowserState(stateBackup);
-  });
+      ok(e1.sharesDocumentWith(e2),
+         `${i} should share doc with ${j}`);
+      is(e1.childCount, e2.childCount,
+         `Child count mismatch (${i}, ${j})`);
 
-  let tab = gBrowser.addTab("about:blank");
-  promiseTabState(tab, state).then(() => {
-    let history = tab.linkedBrowser.webNavigation.sessionHistory;
+      for (let c = 0; c < e1.childCount; c++) {
+        let c1 = e1.GetChildAt(c);
+        let c2 = e2.GetChildAt(c);
+
+        ok(c1.sharesDocumentWith(c2),
+           `Cousins should share documents. (${i}, ${j}, ${c})`);
+      }
+    }
+
+    let history = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsISHistory);
 
     is(history.count, 2, "history.count");
     for (let i = 0; i < history.count; i++) {
       for (let j = 0; j < history.count; j++) {
         compareEntries(i, j, history);
       }
     }
-
-    finish();
   });
-}
-
-function compareEntries(i, j, history) {
-  let e1 = history.getEntryAtIndex(i, false)
-                  .QueryInterface(Ci.nsISHEntry)
-                  .QueryInterface(Ci.nsISHContainer);
-
-  let e2 = history.getEntryAtIndex(j, false)
-                  .QueryInterface(Ci.nsISHEntry)
-                  .QueryInterface(Ci.nsISHContainer);
 
-  ok(e1.sharesDocumentWith(e2),
-     i + " should share doc with " + j);
-  is(e1.childCount, e2.childCount,
-     "Child count mismatch (" + i + ", " + j + ")");
-
-  for (let c = 0; c < e1.childCount; c++) {
-    let c1 = e1.GetChildAt(c);
-    let c2 = e2.GetChildAt(c);
-
-    ok(c1.sharesDocumentWith(c2),
-       "Cousins should share documents. (" + i + ", " + j + ", " + c + ")");
-  }
-}
+  ss.setBrowserState(stateBackup);
+});
--- a/browser/components/sessionstore/test/browser_739805.js
+++ b/browser/components/sessionstore/test/browser_739805.js
@@ -24,17 +24,18 @@ function test() {
 
     ss.setTabState(tab, JSON.stringify(tabState));
     is(browser.__SS_restoreState, TAB_STATE_NEEDS_RESTORE, "tab needs restoring");
 
     let {formdata} = JSON.parse(ss.getTabState(tab));
     is(formdata && formdata.id["foo"], "bar", "tab state's formdata is valid");
 
     promiseTabRestored(tab).then(() => {
-      let input = browser.contentDocument.getElementById("foo");
-      is(input.value, "bar", "formdata has been restored correctly");
-      finish();
+      ContentTask.spawn(browser, null, function() {
+        let input = content.document.getElementById("foo");
+        is(input.value, "bar", "formdata has been restored correctly");
+      }).then(() => { finish(); });
     });
 
     // Restore the tab by selecting it.
     gBrowser.selectedTab = tab;
   });
 }
--- a/browser/components/sessionstore/test/browser_911547.js
+++ b/browser/components/sessionstore/test/browser_911547.js
@@ -11,42 +11,53 @@ add_task(function* test() {
   let tab = gBrowser.selectedTab = gBrowser.addTab(testURL);
   gBrowser.selectedTab = tab;
 
   let browser = tab.linkedBrowser;
   yield promiseBrowserLoaded(browser);
 
   // this is a baseline to ensure CSP is active
   // attempt to inject and run a script via inline (pre-restore, allowed)
-  injectInlineScript(browser, 'document.getElementById("test_id").value = "fail";');
-  is(browser.contentDocument.getElementById("test_id").value, "ok",
-     "CSP should block the inline script that modifies test_id");
+  yield injectInlineScript(browser, `document.getElementById("test_id").value = "fail";`);
+
+  let loadedPromise = promiseBrowserLoaded(browser);
+  yield ContentTask.spawn(browser, null, function() {
+    is(content.document.getElementById("test_id").value, "ok",
+       "CSP should block the inline script that modifies test_id");
 
-  // attempt to click a link to a data: URI (will inherit the CSP of the
-  // origin document) and navigate to the data URI in the link.
-  browser.contentDocument.getElementById("test_data_link").click();
-  yield promiseBrowserLoaded(browser);
+    // attempt to click a link to a data: URI (will inherit the CSP of the
+    // origin document) and navigate to the data URI in the link.
+    content.document.getElementById("test_data_link").click();
+  });
 
-  is(browser.contentDocument.getElementById("test_id2").value, "ok",
-     "CSP should block the script loaded by the clicked data URI");
+  yield loadedPromise;
+
+  yield ContentTask.spawn(browser, null, function() {
+    is(content.document.getElementById("test_id2").value, "ok",
+       "CSP should block the script loaded by the clicked data URI");
+  });
 
   // close the tab
   yield promiseRemoveTab(tab);
 
   // open new tab and recover the state
   tab = ss.undoCloseTab(window, 0);
   yield promiseTabRestored(tab);
   browser = tab.linkedBrowser;
 
-  is(browser.contentDocument.getElementById("test_id2").value, "ok",
-     "CSP should block the script loaded by the clicked data URI after restore");
+  yield ContentTask.spawn(browser, null, function() {
+    is(content.document.getElementById("test_id2").value, "ok",
+       "CSP should block the script loaded by the clicked data URI after restore");
+  });
 
   // clean up
   gBrowser.removeTab(tab);
 });
 
 // injects an inline script element (with a text body)
 function injectInlineScript(browser, scriptText) {
-  let scriptElt = browser.contentDocument.createElement("script");
-  scriptElt.type = "text/javascript";
-  scriptElt.text = scriptText;
-  browser.contentDocument.body.appendChild(scriptElt);
+  return ContentTask.spawn(browser, scriptText, function(text) {
+    let scriptElt = content.document.createElement("script");
+    scriptElt.type = "text/javascript";
+    scriptElt.text = text;
+    content.document.body.appendChild(scriptElt);
+  });
 }
--- a/browser/components/sessionstore/test/browser_history_persist.js
+++ b/browser/components/sessionstore/test/browser_history_persist.js
@@ -1,8 +1,9 @@
+/* eslint-env mozilla/frame-script */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
  * Ensure that history entries that should not be persisted are restored in the
  * same state.
@@ -12,68 +13,81 @@ add_task(function* check_history_not_per
   let tab = gBrowser.addTab("about:blank");
   let browser = tab.linkedBrowser;
   yield promiseBrowserLoaded(browser);
 
   // Retrieve the tab state.
   yield TabStateFlusher.flush(browser);
   let state = JSON.parse(ss.getTabState(tab));
   ok(!state.entries[0].persist, "Should have collected the persistence state");
-  gBrowser.removeTab(tab);
+  yield promiseRemoveTab(tab);
   browser = null;
 
   // Open a new tab to restore into.
   tab = gBrowser.addTab("about:blank");
   browser = tab.linkedBrowser;
   yield promiseTabState(tab, state);
-  let sessionHistory = browser.sessionHistory;
 
-  is(sessionHistory.count, 1, "Should be a single history entry");
-  is(sessionHistory.getEntryAtIndex(0, false).URI.spec, "about:blank", "Should be the right URL");
+  yield ContentTask.spawn(browser, null, function() {
+    let sessionHistory = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                                 .getInterface(Ci.nsISHistory);
+
+    is(sessionHistory.count, 1, "Should be a single history entry");
+    is(sessionHistory.getEntryAtIndex(0, false).URI.spec, "about:blank", "Should be the right URL");
+  });
 
   // Load a new URL into the tab, it should replace the about:blank history entry
   browser.loadURI("about:robots");
   yield promiseBrowserLoaded(browser);
-  sessionHistory = browser.sessionHistory;
-  is(sessionHistory.count, 1, "Should be a single history entry");
-  is(sessionHistory.getEntryAtIndex(0, false).URI.spec, "about:robots", "Should be the right URL");
+  yield ContentTask.spawn(browser, null, function() {
+    let sessionHistory = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                                 .getInterface(Ci.nsISHistory);
+    is(sessionHistory.count, 1, "Should be a single history entry");
+    is(sessionHistory.getEntryAtIndex(0, false).URI.spec, "about:robots", "Should be the right URL");
+  });
 
   // Cleanup.
-  gBrowser.removeTab(tab);
+  yield promiseRemoveTab(tab);
 });
 
 /**
  * Check that entries default to being persisted when the attribute doesn't
  * exist
  */
 add_task(function* check_history_default_persisted() {
   // Create an about:blank tab
   let tab = gBrowser.addTab("about:blank");
   let browser = tab.linkedBrowser;
   yield promiseBrowserLoaded(browser);
 
   // Retrieve the tab state.
   yield TabStateFlusher.flush(browser);
   let state = JSON.parse(ss.getTabState(tab));
   delete state.entries[0].persist;
-  gBrowser.removeTab(tab);
+  yield promiseRemoveTab(tab);
   browser = null;
 
   // Open a new tab to restore into.
   tab = gBrowser.addTab("about:blank");
   browser = tab.linkedBrowser;
   yield promiseTabState(tab, state);
-  let sessionHistory = browser.sessionHistory;
+  yield ContentTask.spawn(browser, null, function() {
+    let sessionHistory = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                                 .getInterface(Ci.nsISHistory);
 
-  is(sessionHistory.count, 1, "Should be a single history entry");
-  is(sessionHistory.getEntryAtIndex(0, false).URI.spec, "about:blank", "Should be the right URL");
+    is(sessionHistory.count, 1, "Should be a single history entry");
+    is(sessionHistory.getEntryAtIndex(0, false).URI.spec, "about:blank", "Should be the right URL");
+  });
 
   // Load a new URL into the tab, it should replace the about:blank history entry
   browser.loadURI("about:robots");
   yield promiseBrowserLoaded(browser);
-  sessionHistory = browser.sessionHistory;
-  is(sessionHistory.count, 2, "Should be two history entries");
-  is(sessionHistory.getEntryAtIndex(0, false).URI.spec, "about:blank", "Should be the right URL");
-  is(sessionHistory.getEntryAtIndex(1, false).URI.spec, "about:robots", "Should be the right URL");
+  yield ContentTask.spawn(browser, null, function() {
+    let sessionHistory = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                                 .getInterface(Ci.nsISHistory);
+    is(sessionHistory.count, 2, "Should be two history entries");
+    is(sessionHistory.getEntryAtIndex(0, false).URI.spec, "about:blank", "Should be the right URL");
+    is(sessionHistory.getEntryAtIndex(1, false).URI.spec, "about:robots", "Should be the right URL");
+  });
 
   // Cleanup.
-  gBrowser.removeTab(tab);
+  yield promiseRemoveTab(tab);
 });
--- a/browser/config/tooltool-manifests/linux32/releng.manifest
+++ b/browser/config/tooltool-manifests/linux32/releng.manifest
@@ -1,35 +1,35 @@
 [
-{
-"version": "gcc 4.9.4 + PR64905",
-"size": 101297752,
-"digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
-"algorithm": "sha512",
-"filename": "gcc.tar.xz",
-"unpack": true
-},
-{
-"size": 11189216,
-"digest": "18bc52b0599b1308b667e282abb45f47597bfc98a5140cfcab8da71dacf89dd76d0dee22a04ce26fe7ad1f04e2d6596991f9e5b01fd2aaaab5542965f596b0e6",
-"algorithm": "sha512",
-"filename": "gtk3.tar.xz",
-"setup": "setup.sh",
-"unpack": true
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 110077036,
-"digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
-"algorithm": "sha512",
-"filename": "rustc.tar.xz",
-"unpack": true
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.xz",
-"unpack": true,
-"digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
-"size": 1020700
-}
+  {
+    "version": "gcc 4.9.4 + PR64905",
+    "size": 101297752,
+    "digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
+    "algorithm": "sha512",
+    "filename": "gcc.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 11189216,
+    "digest": "18bc52b0599b1308b667e282abb45f47597bfc98a5140cfcab8da71dacf89dd76d0dee22a04ce26fe7ad1f04e2d6596991f9e5b01fd2aaaab5542965f596b0e6",
+    "algorithm": "sha512",
+    "filename": "gtk3.tar.xz",
+    "setup": "setup.sh",
+    "unpack": true
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 110077036,
+    "digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.xz",
+    "unpack": true
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.xz",
+    "unpack": true,
+    "digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
+    "size": 1020700
+  }
 ]
--- a/browser/config/tooltool-manifests/linux64/asan.manifest
+++ b/browser/config/tooltool-manifests/linux64/asan.manifest
@@ -1,34 +1,34 @@
 [
-{
-"version": "gcc 4.9.4 + PR64905",
-"size": 101297752,
-"digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
-"algorithm": "sha512",
-"filename": "gcc.tar.xz",
-"unpack": true
-},
-{
-"version": "clang 3.9.0, libgcc 4.9.4",
-"size": 166261192,
-"digest": "52f3fc23f0f5c98050f8b0ac7c92a6752d067582a16f712a5a58074be98975d594f9e36249fc2be7f1cc2ca6d509c663faaf2bea66f949243cc1f41651638ba6",
-"algorithm": "sha512",
-"filename": "clang.tar.xz",
-"unpack": true
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 110077036,
-"digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
-"algorithm": "sha512",
-"filename": "rustc.tar.xz",
-"unpack": true
-},
-{
-"size": 12072532,
-"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-"algorithm": "sha512",
-"filename": "gtk3.tar.xz",
-"setup": "setup.sh",
-"unpack": true
-}
+  {
+    "version": "gcc 4.9.4 + PR64905",
+    "size": 101297752,
+    "digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
+    "algorithm": "sha512",
+    "filename": "gcc.tar.xz",
+    "unpack": true
+  },
+  {
+    "version": "clang 3.9.0, libgcc 4.9.4",
+    "size": 166261192,
+    "digest": "52f3fc23f0f5c98050f8b0ac7c92a6752d067582a16f712a5a58074be98975d594f9e36249fc2be7f1cc2ca6d509c663faaf2bea66f949243cc1f41651638ba6",
+    "algorithm": "sha512",
+    "filename": "clang.tar.xz",
+    "unpack": true
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 110077036,
+    "digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 12072532,
+    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
+    "algorithm": "sha512",
+    "filename": "gtk3.tar.xz",
+    "setup": "setup.sh",
+    "unpack": true
+  }
 ]
--- a/browser/config/tooltool-manifests/linux64/clang.manifest
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest
@@ -1,35 +1,35 @@
 [
-{
-"version": "clang 3.8.0, libgcc 4.8.5",
-"size": 140319580,
-"digest": "34e219d7e8eaffa81710631c34d21355563d06335b3c00851e94c1f42f9098788fded8463dd0f67dd699f77b47a0245dd7aff754943a7a03fb5fd145a808254f",
-"algorithm": "sha512",
-"filename": "clang.tar.xz",
-"unpack": true
-},
-{
-"size": 12072532,
-"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-"algorithm": "sha512",
-"filename": "gtk3.tar.xz",
-"setup": "setup.sh",
-"unpack": true
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 110077036,
-"digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
-"algorithm": "sha512",
-"filename": "rustc.tar.xz",
-"unpack": true
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.xz",
-"unpack": true,
-"digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
-"size": 1020700
-}
+  {
+    "version": "clang 3.8.0, libgcc 4.8.5",
+    "size": 140319580,
+    "digest": "34e219d7e8eaffa81710631c34d21355563d06335b3c00851e94c1f42f9098788fded8463dd0f67dd699f77b47a0245dd7aff754943a7a03fb5fd145a808254f",
+    "algorithm": "sha512",
+    "filename": "clang.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 12072532,
+    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
+    "algorithm": "sha512",
+    "filename": "gtk3.tar.xz",
+    "setup": "setup.sh",
+    "unpack": true
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 110077036,
+    "digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.xz",
+    "unpack": true
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.xz",
+    "unpack": true,
+    "digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
+    "size": 1020700
+  }
 ]
--- a/browser/config/tooltool-manifests/linux64/clang.manifest.centos6
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest.centos6
@@ -1,34 +1,34 @@
 [
-{
-"version": "clang 3.8.0, libgcc 4.8.5",
-"size": 140319580,
-"digest": "34e219d7e8eaffa81710631c34d21355563d06335b3c00851e94c1f42f9098788fded8463dd0f67dd699f77b47a0245dd7aff754943a7a03fb5fd145a808254f",
-"algorithm": "sha512",
-"filename": "clang.tar.xz",
-"unpack": true
-},
-{
-"size": 12072532,
-"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-"algorithm": "sha512",
-"filename": "gtk3.tar.xz",
-"setup": "setup.sh",
-"unpack": true
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 110077036,
-"digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
-"algorithm": "sha512",
-"filename": "rustc.tar.xz",
-"unpack": true
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"filename": "sccache2.tar.xz",
-"unpack": true,
-"digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
-"size": 1020700
-}
+  {
+    "version": "clang 3.8.0, libgcc 4.8.5",
+    "size": 140319580,
+    "digest": "34e219d7e8eaffa81710631c34d21355563d06335b3c00851e94c1f42f9098788fded8463dd0f67dd699f77b47a0245dd7aff754943a7a03fb5fd145a808254f",
+    "algorithm": "sha512",
+    "filename": "clang.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 12072532,
+    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
+    "algorithm": "sha512",
+    "filename": "gtk3.tar.xz",
+    "setup": "setup.sh",
+    "unpack": true
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 110077036,
+    "digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.xz",
+    "unpack": true
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "filename": "sccache2.tar.xz",
+    "unpack": true,
+    "digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
+    "size": 1020700
+  }
 ]
--- a/browser/config/tooltool-manifests/linux64/hazard.manifest
+++ b/browser/config/tooltool-manifests/linux64/hazard.manifest
@@ -1,43 +1,43 @@
 [
-{
-"version": "gcc 4.9.4 + PR64905",
-"size": 101297752,
-"digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
-"algorithm": "sha512",
-"filename": "gcc.tar.xz",
-"unpack": true
-},
-{
-"digest": "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029",
-"unpack": true,
-"algorithm": "sha512",
-"filename": "sixgill.tar.xz",
-"size": 2631908,
-"hg_id": "8cb9c3fb039a+ tip"
-},
-{
-"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-"unpack": true,
-"setup": "setup.sh",
-"algorithm": "sha512",
-"filename": "gtk3.tar.xz",
-"size": 12072532
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 110077036,
-"digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
-"algorithm": "sha512",
-"filename": "rustc.tar.xz",
-"unpack": true
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.xz",
-"unpack": true,
-"digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
-"size": 1020700
-}
+  {
+    "version": "gcc 4.9.4 + PR64905",
+    "size": 101297752,
+    "digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
+    "algorithm": "sha512",
+    "filename": "gcc.tar.xz",
+    "unpack": true
+  },
+  {
+    "digest": "36dc644e24c0aa824975ad8f5c15714445d5cb064d823000c3cb637e885199414d7df551e6b99233f0656dcf5760918192ef04113c486af37f3c489bb93ad029",
+    "unpack": true,
+    "algorithm": "sha512",
+    "filename": "sixgill.tar.xz",
+    "size": 2631908,
+    "hg_id": "8cb9c3fb039a+ tip"
+  },
+  {
+    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
+    "unpack": true,
+    "setup": "setup.sh",
+    "algorithm": "sha512",
+    "filename": "gtk3.tar.xz",
+    "size": 12072532
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 110077036,
+    "digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.xz",
+    "unpack": true
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.xz",
+    "unpack": true,
+    "digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
+    "size": 1020700
+  }
 ]
--- a/browser/config/tooltool-manifests/linux64/msan.manifest
+++ b/browser/config/tooltool-manifests/linux64/msan.manifest
@@ -1,34 +1,34 @@
 [
-{
-"version": "gcc 4.9.4 + PR64905",
-"size": 101297752,
-"digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
-"algorithm": "sha512",
-"filename": "gcc.tar.xz",
-"unpack": true
-},
-{
-"version": "clang 3.9.0, libgcc 4.9.4",
-"size": 166261192,
-"digest": "52f3fc23f0f5c98050f8b0ac7c92a6752d067582a16f712a5a58074be98975d594f9e36249fc2be7f1cc2ca6d509c663faaf2bea66f949243cc1f41651638ba6",
-"algorithm": "sha512",
-"filename": "clang.tar.xz",
-"unpack": true
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 110077036,
-"digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
-"algorithm": "sha512",
-"filename": "rustc.tar.xz",
-"unpack": true
-},
-{
-"size": 12072532,
-"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-"algorithm": "sha512",
-"filename": "gtk3.tar.xz",
-"setup": "setup.sh",
-"unpack": true
-}
+  {
+    "version": "gcc 4.9.4 + PR64905",
+    "size": 101297752,
+    "digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
+    "algorithm": "sha512",
+    "filename": "gcc.tar.xz",
+    "unpack": true
+  },
+  {
+    "version": "clang 3.9.0, libgcc 4.9.4",
+    "size": 166261192,
+    "digest": "52f3fc23f0f5c98050f8b0ac7c92a6752d067582a16f712a5a58074be98975d594f9e36249fc2be7f1cc2ca6d509c663faaf2bea66f949243cc1f41651638ba6",
+    "algorithm": "sha512",
+    "filename": "clang.tar.xz",
+    "unpack": true
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 110077036,
+    "digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 12072532,
+    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
+    "algorithm": "sha512",
+    "filename": "gtk3.tar.xz",
+    "setup": "setup.sh",
+    "unpack": true
+  }
 ]
--- a/browser/config/tooltool-manifests/linux64/releng.manifest
+++ b/browser/config/tooltool-manifests/linux64/releng.manifest
@@ -1,43 +1,43 @@
 [
-{
-"version": "gcc 4.9.4 + PR64905",
-"size": 101297752,
-"digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
-"algorithm": "sha512",
-"filename": "gcc.tar.xz",
-"unpack": true
-},
-{
-"size": 12072532,
-"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-"algorithm": "sha512",
-"filename": "gtk3.tar.xz",
-"setup": "setup.sh",
-"unpack": true
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 110077036,
-"digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
-"algorithm": "sha512",
-"filename": "rustc.tar.xz",
-"unpack": true
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.xz",
-"unpack": true,
-"digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
-"size": 1020700
-},
-{
-"version": "clang + llvm 3.9.0, built from SVN r290136",
-"size": 151724092,
-"digest": "4ab5ff2131e4ce4888d38c17feb192c19bc6ede83abef55af7d2f29e2446f6335dc860377fa25cbb0283b3958c0a3d377a3cfdc7705a85d4843e3ab357ddca7f",
-"algorithm": "sha512",
-"filename": "clang.tar.xz",
-"unpack": true
-}
+  {
+    "version": "gcc 4.9.4 + PR64905",
+    "size": 101297752,
+    "digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
+    "algorithm": "sha512",
+    "filename": "gcc.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 12072532,
+    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
+    "algorithm": "sha512",
+    "filename": "gtk3.tar.xz",
+    "setup": "setup.sh",
+    "unpack": true
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 110077036,
+    "digest": "8b99d058cc081f6ca2a3cc88c3ca9c15232961d2539774dacee35e2258955ad8fc4cb0af3b903a3e3f8a264ddecb3baae9256502ffc178a2823779284ace2bd8",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.xz",
+    "unpack": true
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.xz",
+    "unpack": true,
+    "digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
+    "size": 1020700
+  },
+  {
+    "version": "clang + llvm 3.9.0, built from SVN r290136",
+    "size": 151724092,
+    "digest": "4ab5ff2131e4ce4888d38c17feb192c19bc6ede83abef55af7d2f29e2446f6335dc860377fa25cbb0283b3958c0a3d377a3cfdc7705a85d4843e3ab357ddca7f",
+    "algorithm": "sha512",
+    "filename": "clang.tar.xz",
+    "unpack": true
+  }
 ]
--- a/browser/config/tooltool-manifests/linux64/tsan.manifest
+++ b/browser/config/tooltool-manifests/linux64/tsan.manifest
@@ -1,26 +1,26 @@
 [
-{
-"version": "gcc 4.9.4 + PR64905",
-"size": 101297752,
-"digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
-"algorithm": "sha512",
-"filename": "gcc.tar.xz",
-"unpack": true
-},
-{
-"version": "clang 3.9.0, libgcc 4.9.4",
-"size": 166261192,
-"digest": "52f3fc23f0f5c98050f8b0ac7c92a6752d067582a16f712a5a58074be98975d594f9e36249fc2be7f1cc2ca6d509c663faaf2bea66f949243cc1f41651638ba6",
-"algorithm": "sha512",
-"filename": "clang.tar.xz",
-"unpack": true
-},
-{
-"size": 12072532,
-"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-"algorithm": "sha512",
-"filename": "gtk3.tar.xz",
-"setup": "setup.sh",
-"unpack": true
-}
+  {
+    "version": "gcc 4.9.4 + PR64905",
+    "size": 101297752,
+    "digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
+    "algorithm": "sha512",
+    "filename": "gcc.tar.xz",
+    "unpack": true
+  },
+  {
+    "version": "clang 3.9.0, libgcc 4.9.4",
+    "size": 166261192,
+    "digest": "52f3fc23f0f5c98050f8b0ac7c92a6752d067582a16f712a5a58074be98975d594f9e36249fc2be7f1cc2ca6d509c663faaf2bea66f949243cc1f41651638ba6",
+    "algorithm": "sha512",
+    "filename": "clang.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 12072532,
+    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
+    "algorithm": "sha512",
+    "filename": "gtk3.tar.xz",
+    "setup": "setup.sh",
+    "unpack": true
+  }
 ]
--- a/browser/config/tooltool-manifests/macosx64/asan.manifest
+++ b/browser/config/tooltool-manifests/macosx64/asan.manifest
@@ -1,10 +1,10 @@
 [
-{
-"version": "clang 3.8.0",
-"size": 133060926,
-"digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
-"algorithm": "sha512",
-"filename": "clang.tar.bz2",
-"unpack": true
-}
+  {
+    "version": "clang 3.8.0",
+    "size": 133060926,
+    "digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
+    "algorithm": "sha512",
+    "filename": "clang.tar.bz2",
+    "unpack": true
+  }
 ]
--- a/browser/config/tooltool-manifests/macosx64/clang.manifest
+++ b/browser/config/tooltool-manifests/macosx64/clang.manifest
@@ -1,35 +1,35 @@
 [
-{
-"version": "clang 3.9.0",
-"size": 184678304,
-"digest": "cfde9a0f7f59823200f94422b4adb9a2fb5d4d07f240bbd1142c792434f6a1cbb4096d25c9853d77008fc40db0d827daa7003e78016f51241f621d6040ccc635",
-"algorithm": "sha512",
-"filename": "clang.tar.bz2",
-"unpack": true
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 122706608,
-"digest": "b9ae830cfe24430907490cd58f8215447667fb8f1f052d6e78c8ecd7aadfbc92f75a404774d4cc7b30a46b756275209c2fe2723473704b3bce7ffee908e353a8",
-"algorithm": "sha512",
-"filename": "rustc.tar.bz2",
-"unpack": true
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.bz2",
-"unpack": true,
-"digest": "a285c7c6468ad7438262dfec90f65981e84abf2adbb1aa075c0ec1759b4f98ce5d5f14a3d555274f970704210a00738ba7d95db2fc320f7780e6b99bcb0ffb6c",
-"size": 1143715
-},
-{
-"version": "cctools port from commit hash 84ce22dbb22a26ce7f392e9de0ee39c2efe6fd68",
-"size": 2174783,
-"digest": "8678348faff8f344b377075007975ae77a55a2a73488e36950a43c8ec27a79970cd8e34003e33e756a57d9cbf5c3e2e4461184102c6c03f793377a4d250a7f24",
-"algorithm": "sha512",
-"filename": "cctools.tar.bz2",
-"unpack": true
-}
+  {
+    "version": "clang 3.9.0",
+    "size": 184678304,
+    "digest": "cfde9a0f7f59823200f94422b4adb9a2fb5d4d07f240bbd1142c792434f6a1cbb4096d25c9853d77008fc40db0d827daa7003e78016f51241f621d6040ccc635",
+    "algorithm": "sha512",
+    "filename": "clang.tar.bz2",
+    "unpack": true
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 122706608,
+    "digest": "b9ae830cfe24430907490cd58f8215447667fb8f1f052d6e78c8ecd7aadfbc92f75a404774d4cc7b30a46b756275209c2fe2723473704b3bce7ffee908e353a8",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.bz2",
+    "unpack": true
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.bz2",
+    "unpack": true,
+    "digest": "a285c7c6468ad7438262dfec90f65981e84abf2adbb1aa075c0ec1759b4f98ce5d5f14a3d555274f970704210a00738ba7d95db2fc320f7780e6b99bcb0ffb6c",
+    "size": 1143715
+  },
+  {
+    "version": "cctools port from commit hash 84ce22dbb22a26ce7f392e9de0ee39c2efe6fd68",
+    "size": 2174783,
+    "digest": "8678348faff8f344b377075007975ae77a55a2a73488e36950a43c8ec27a79970cd8e34003e33e756a57d9cbf5c3e2e4461184102c6c03f793377a4d250a7f24",
+    "algorithm": "sha512",
+    "filename": "cctools.tar.bz2",
+    "unpack": true
+  }
 ]
--- a/browser/config/tooltool-manifests/macosx64/cross-clang.manifest
+++ b/browser/config/tooltool-manifests/macosx64/cross-clang.manifest
@@ -1,43 +1,43 @@
 [
-{
-"version": "clang 3.8.0, libgcc 4.9.4 + PR64905",
-"size": 155933720,
-"digest": "7a3a9a5ff455554d120596fe4bc06c19e41ecbd1e67b1e95e778c0b8ce6d9472f30fd4a27c66d48d9c047aac7a077f23f87397711dd6f33175f95cf4a2ada862",
-"algorithm": "sha512",
-"filename": "clang.tar.xz",
-"unpack": true
-},
-{
-"version": "gcc 4.9.4 + PR64905",
-"size": 101297752,
-"digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
-"algorithm": "sha512",
-"filename": "gcc.tar.xz",
-"unpack": true
-},
-{
-"size": 1349196,
-"visibility": "public",
-"digest": "438a36523a74cbc4a58226647e0501fa64a1b63f32931dc364a75d699df8accb45afdf26f4cb61c6ac7f58be530205acb6da22008bec19603c6f6fda3a12a8cc",
-"algorithm": "sha512",
-"unpack": true,
-"filename": "cctools.tar.xz"
-},
-{
-"size": 30823112,
-"visibility": "internal",
-"digest": "0c58e06a3ea8f4641c991a7406fc8733c574f0b4aa773bce0feaa5468d2b8440fa33cea056e16fad2b8a7ef4409ca7228113eb12adc87c3e115129d8a3b3b565",
-"algorithm": "sha512",
-"unpack": true,
-"filename": "MacOSX10.10.sdk.tar.xz"
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.xz",
-"unpack": true,
-"digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
-"size": 1020700
-}
+  {
+    "version": "clang 3.8.0, libgcc 4.9.4 + PR64905",
+    "size": 155933720,
+    "digest": "7a3a9a5ff455554d120596fe4bc06c19e41ecbd1e67b1e95e778c0b8ce6d9472f30fd4a27c66d48d9c047aac7a077f23f87397711dd6f33175f95cf4a2ada862",
+    "algorithm": "sha512",
+    "filename": "clang.tar.xz",
+    "unpack": true
+  },
+  {
+    "version": "gcc 4.9.4 + PR64905",
+    "size": 101297752,
+    "digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
+    "algorithm": "sha512",
+    "filename": "gcc.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 1349196,
+    "visibility": "public",
+    "digest": "438a36523a74cbc4a58226647e0501fa64a1b63f32931dc364a75d699df8accb45afdf26f4cb61c6ac7f58be530205acb6da22008bec19603c6f6fda3a12a8cc",
+    "algorithm": "sha512",
+    "unpack": true,
+    "filename": "cctools.tar.xz"
+  },
+  {
+    "size": 30823112,
+    "visibility": "internal",
+    "digest": "0c58e06a3ea8f4641c991a7406fc8733c574f0b4aa773bce0feaa5468d2b8440fa33cea056e16fad2b8a7ef4409ca7228113eb12adc87c3e115129d8a3b3b565",
+    "algorithm": "sha512",
+    "unpack": true,
+    "filename": "MacOSX10.10.sdk.tar.xz"
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.xz",
+    "unpack": true,
+    "digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
+    "size": 1020700
+  }
 ]
--- a/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
@@ -1,60 +1,60 @@
 [
-{
-"version": "clang 3.9.0",
-"size": 168062128,
-"digest": "2a5458a25792fcade86a56ff0f4acdfa284d2b62966991a7c34a92c2e8c0b4a162ce00512d4467754e7f74598d64c56e91517e1606ed3fba011f7c10e8ad3288",
-"algorithm": "sha512",
-"filename": "clang.tar.xz",
-"unpack": true
-},
-{
-"size": 6075028,
-"visibility": "public",
-"digest": "0b962ba55a5a2fbae44218683bdf6ea0dfe8165aba436173a065f7190976184586b9acf4c23478bc5b6d81a3e00f681bf16df0536c9c9718ad0570d064f69027",
-"algorithm": "sha512",
-"unpack": true,
-"filename": "cctools.tar.xz"
-},
-{
-"size": 35215976,
-"visibility": "internal",
-"digest": "8be736545ddab25ebded188458ce974d5c9a7e29f3c50d2ebfbcb878f6aff853dd2ff5a3528bdefc64396a10101a1b50fd2fe52000140df33643cebe1ea759da",
-"algorithm": "sha512",
-"unpack": true,
-"filename": "MacOSX10.7.sdk.tar.bz2"
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.xz",
-"unpack": true,
-"digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
-"size": 1020700
-},
-{
-"version": "https://github.com/andreas56/libdmg-hfsplus rev 81dd75fd1549b24bf8af9736ac25518b367e6b63",
-"size": 62032,
-"visibility": "public",
-"digest": "9073c41034784eb8823ec817aed42bbc65c8da63ad3fac572726fa48b36320ee302ca8f51b23576e7fdbeec6ab300610d0c58bbd9c52024577dfdb13d95aa2ec",
-"algorithm": "sha512",
-"unpack": true,
-"filename": "dmg.tar.xz"
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 140284816,
-"digest": "c97503ed2448af059a450792aac76f9b91ce8bc6daf683cdbc2d3fb49a6bd205a4ba854838281b1e3888ae15b5df585cc8fa56365fb1ec5acafc9e1b8871f3e3",
-"algorithm": "sha512",
-"filename": "rustc.tar.xz",
-"unpack": true
-},
-{
-"size": 281576,
-"visibility": "public",
-"digest": "71616564533d138fb12f08e761c2638d054814fdf9c9439638ec57b201e100445c364d73d8d7a4f0e3b784898d5fe6264e8242863fc5ac40163f1791468bbc46",
-"algorithm": "sha512",
-"filename": "hfsplus-tools.tar.xz",
-"unpack": true
-}
+  {
+    "version": "clang 3.9.0",
+    "size": 168062128,
+    "digest": "2a5458a25792fcade86a56ff0f4acdfa284d2b62966991a7c34a92c2e8c0b4a162ce00512d4467754e7f74598d64c56e91517e1606ed3fba011f7c10e8ad3288",
+    "algorithm": "sha512",
+    "filename": "clang.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 6075028,
+    "visibility": "public",
+    "digest": "0b962ba55a5a2fbae44218683bdf6ea0dfe8165aba436173a065f7190976184586b9acf4c23478bc5b6d81a3e00f681bf16df0536c9c9718ad0570d064f69027",
+    "algorithm": "sha512",
+    "unpack": true,
+    "filename": "cctools.tar.xz"
+  },
+  {
+    "size": 35215976,
+    "visibility": "internal",
+    "digest": "8be736545ddab25ebded188458ce974d5c9a7e29f3c50d2ebfbcb878f6aff853dd2ff5a3528bdefc64396a10101a1b50fd2fe52000140df33643cebe1ea759da",
+    "algorithm": "sha512",
+    "unpack": true,
+    "filename": "MacOSX10.7.sdk.tar.bz2"
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.xz",
+    "unpack": true,
+    "digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
+    "size": 1020700
+  },
+  {
+    "version": "https://github.com/andreas56/libdmg-hfsplus rev 81dd75fd1549b24bf8af9736ac25518b367e6b63",
+    "size": 62032,
+    "visibility": "public",
+    "digest": "9073c41034784eb8823ec817aed42bbc65c8da63ad3fac572726fa48b36320ee302ca8f51b23576e7fdbeec6ab300610d0c58bbd9c52024577dfdb13d95aa2ec",
+    "algorithm": "sha512",
+    "unpack": true,
+    "filename": "dmg.tar.xz"
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 140284816,
+    "digest": "c97503ed2448af059a450792aac76f9b91ce8bc6daf683cdbc2d3fb49a6bd205a4ba854838281b1e3888ae15b5df585cc8fa56365fb1ec5acafc9e1b8871f3e3",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.xz",
+    "unpack": true
+  },
+  {
+    "size": 281576,
+    "visibility": "public",
+    "digest": "71616564533d138fb12f08e761c2638d054814fdf9c9439638ec57b201e100445c364d73d8d7a4f0e3b784898d5fe6264e8242863fc5ac40163f1791468bbc46",
+    "algorithm": "sha512",
+    "filename": "hfsplus-tools.tar.xz",
+    "unpack": true
+  }
 ]
--- a/browser/config/tooltool-manifests/macosx64/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,35 +1,35 @@
 [
-{
-"version": "clang 3.8.0",
-"size": 133060926,
-"digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
-"algorithm": "sha512",
-"filename": "clang.tar.bz2",
-"unpack": true
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 122706608,
-"digest": "b9ae830cfe24430907490cd58f8215447667fb8f1f052d6e78c8ecd7aadfbc92f75a404774d4cc7b30a46b756275209c2fe2723473704b3bce7ffee908e353a8",
-"algorithm": "sha512",
-"filename": "rustc.tar.bz2",
-"unpack": true
-},
-{
-"version": "cctools port from commit hash db1f8d906cb28, ld only",
-"size": 634496,
-"digest": "037f31fcf29e7bb7fada0d2bdd5e95c7d4cb2692f2a5c98ed6f6a7561b9d81622d015f0d12b291d3667719655f1369e8ce8a0a4a4773aa0ee4753e04a8821173",
-"algorithm": "sha512",
-"filename": "cctools.tar.bz2",
-"unpack": true
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.bz2",
-"unpack": true,
-"digest": "a285c7c6468ad7438262dfec90f65981e84abf2adbb1aa075c0ec1759b4f98ce5d5f14a3d555274f970704210a00738ba7d95db2fc320f7780e6b99bcb0ffb6c",
-"size": 1143715
-}
+  {
+    "version": "clang 3.8.0",
+    "size": 133060926,
+    "digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
+    "algorithm": "sha512",
+    "filename": "clang.tar.bz2",
+    "unpack": true
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 122706608,
+    "digest": "b9ae830cfe24430907490cd58f8215447667fb8f1f052d6e78c8ecd7aadfbc92f75a404774d4cc7b30a46b756275209c2fe2723473704b3bce7ffee908e353a8",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.bz2",
+    "unpack": true
+  },
+  {
+    "version": "cctools port from commit hash db1f8d906cb28, ld only",
+    "size": 634496,
+    "digest": "037f31fcf29e7bb7fada0d2bdd5e95c7d4cb2692f2a5c98ed6f6a7561b9d81622d015f0d12b291d3667719655f1369e8ce8a0a4a4773aa0ee4753e04a8821173",
+    "algorithm": "sha512",
+    "filename": "cctools.tar.bz2",
+    "unpack": true
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.bz2",
+    "unpack": true,
+    "digest": "a285c7c6468ad7438262dfec90f65981e84abf2adbb1aa075c0ec1759b4f98ce5d5f14a3d555274f970704210a00738ba7d95db2fc320f7780e6b99bcb0ffb6c",
+    "size": 1143715
+  }
 ]
--- a/browser/config/tooltool-manifests/win32/build-clang-cl.manifest
+++ b/browser/config/tooltool-manifests/win32/build-clang-cl.manifest
@@ -1,57 +1,57 @@
 [
-{
-"size": 266240,
-"digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
-"algorithm": "sha512",
-"filename": "mozmake.exe"
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 67227255,
-"digest": "95164dc1abb2ab90ea18cf4f322d5c8390e0212e79a52b12578720d568eb6df62e55a9938c8d2a364cd3a6bd0f99608c2139d141a88ec43484b8d0651398532a",
-"algorithm": "sha512",
-"filename": "rustc.tar.bz2",
-"unpack": true
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.bz2",
-"unpack": true,
-"digest": "7dee5c5602b3830cb8ac45ebaa8542714bbac0e50eabbff58a06972a02ceeab75ed7c56ff22a23f760b8317ae8e9a01cdecfaf75a7acbd2a4cdd817967170d2e",
-"size": 1179901
-},
-{
-"version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
-"size": 326656969,
-"digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
-"algorithm": "sha512",
-"filename": "vs2015u3.zip",
-"unpack": true
-},
-{
-"version": "SVN 1.9.4, repacked from SlikSvn (https://sliksvn.com/download/)",
-"size": 3934520,
-"digest": "d3b8f74936857ecbf542e403ed6835938a31d65302985729cbfa7191bf2cf94138565cefcc2f31517098013fbfc51868348863a55b588250902f9dec214dbc42",
-"algorithm": "sha512",
-"filename": "svn194.zip",
-"unpack": true
-},
-{
-"version": "CMake 3.6.2 repack",
-"size": 19832889,
-"digest": "39b0508b60f655969d1b54c76753b14b5b2e92dab58613c835aed798a6aeb9077a7df78aebc011c2c753661fdc15007d3353c0c8773a53148380e2ec02afb629",
-"algorithm": "sha512",
-"filename": "cmake362.zip",
-"unpack": true
-},
-{
-"version": "Ninja 1.7.1",
-"size": 184821,
-"digest": "e4f9a1ae624a2630e75264ba37d396d9c7407d6e6aea3763056210ba6e1387908bd31cf4037a6a3661a418e86c4d2761e0c333e6a3bd0d66549d2b0d72d3f43b",
-"algorithm": "sha512",
-"filename": "ninja171.zip",
-"unpack": true
-}
+  {
+    "size": 266240,
+    "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
+    "algorithm": "sha512",
+    "filename": "mozmake.exe"
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 67227255,
+    "digest": "95164dc1abb2ab90ea18cf4f322d5c8390e0212e79a52b12578720d568eb6df62e55a9938c8d2a364cd3a6bd0f99608c2139d141a88ec43484b8d0651398532a",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.bz2",
+    "unpack": true
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.bz2",
+    "unpack": true,
+    "digest": "7dee5c5602b3830cb8ac45ebaa8542714bbac0e50eabbff58a06972a02ceeab75ed7c56ff22a23f760b8317ae8e9a01cdecfaf75a7acbd2a4cdd817967170d2e",
+    "size": 1179901
+  },
+  {
+    "version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
+    "size": 326656969,
+    "digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
+    "algorithm": "sha512",
+    "filename": "vs2015u3.zip",
+    "unpack": true
+  },
+  {
+    "version": "SVN 1.9.4, repacked from SlikSvn (https://sliksvn.com/download/)",
+    "size": 3934520,
+    "digest": "d3b8f74936857ecbf542e403ed6835938a31d65302985729cbfa7191bf2cf94138565cefcc2f31517098013fbfc51868348863a55b588250902f9dec214dbc42",
+    "algorithm": "sha512",
+    "filename": "svn194.zip",
+    "unpack": true
+  },
+  {
+    "version": "CMake 3.6.2 repack",
+    "size": 19832889,
+    "digest": "39b0508b60f655969d1b54c76753b14b5b2e92dab58613c835aed798a6aeb9077a7df78aebc011c2c753661fdc15007d3353c0c8773a53148380e2ec02afb629",
+    "algorithm": "sha512",
+    "filename": "cmake362.zip",
+    "unpack": true
+  },
+  {
+    "version": "Ninja 1.7.1",
+    "size": 184821,
+    "digest": "e4f9a1ae624a2630e75264ba37d396d9c7407d6e6aea3763056210ba6e1387908bd31cf4037a6a3661a418e86c4d2761e0c333e6a3bd0d66549d2b0d72d3f43b",
+    "algorithm": "sha512",
+    "filename": "ninja171.zip",
+    "unpack": true
+  }
 ]
--- a/browser/config/tooltool-manifests/win32/clang.manifest
+++ b/browser/config/tooltool-manifests/win32/clang.manifest
@@ -1,50 +1,50 @@
 [
-{
-"size": 266240,
-"digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
-"algorithm": "sha512",
-"filename": "mozmake.exe"
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 67227255,
-"digest": "95164dc1abb2ab90ea18cf4f322d5c8390e0212e79a52b12578720d568eb6df62e55a9938c8d2a364cd3a6bd0f99608c2139d141a88ec43484b8d0651398532a",
-"algorithm": "sha512",
-"filename": "rustc.tar.bz2",
-"unpack": true
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.bz2",
-"unpack": true,
-"digest": "7dee5c5602b3830cb8ac45ebaa8542714bbac0e50eabbff58a06972a02ceeab75ed7c56ff22a23f760b8317ae8e9a01cdecfaf75a7acbd2a4cdd817967170d2e",
-"size": 1179901
-},
-{
-"version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
-"size": 326656969,
-"digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
-"algorithm": "sha512",
-"filename": "vs2015u3.zip",
-"unpack": true
-},
-{
-"version": "clang 5.0pre/r293859",
-"size": 309009013,
-"digest": "cd3ed31acefd185f441632158dde73538c62bab7ebf2a8ec630985ab345938ec522983721ddb1bead1de22d5ac1571d50a958ae002364d739f2a78c6e7244222",
-"algorithm": "sha512",
-"filename": "clang.tar.bz2",
-"unpack": true
-},
-{
-"version": "makecab rev d2bc6797648b7a834782714a55d339d2fd4e58c8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "makecab.tar.bz2",
-"unpack": true,
-"digest": "196ac6a567c85559957dfe511c3d8654d23c94d5603259e19ccafe9d71e0e4ccee63ccc9a778f2699654b786cda54266108b7d4db543d01bb0b42545b4e6ec75",
-"size": 297118
-}
+  {
+    "size": 266240,
+    "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
+    "algorithm": "sha512",
+    "filename": "mozmake.exe"
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 67227255,
+    "digest": "95164dc1abb2ab90ea18cf4f322d5c8390e0212e79a52b12578720d568eb6df62e55a9938c8d2a364cd3a6bd0f99608c2139d141a88ec43484b8d0651398532a",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.bz2",
+    "unpack": true
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.bz2",
+    "unpack": true,
+    "digest": "7dee5c5602b3830cb8ac45ebaa8542714bbac0e50eabbff58a06972a02ceeab75ed7c56ff22a23f760b8317ae8e9a01cdecfaf75a7acbd2a4cdd817967170d2e",
+    "size": 1179901
+  },
+  {
+    "version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
+    "size": 326656969,
+    "digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
+    "algorithm": "sha512",
+    "filename": "vs2015u3.zip",
+    "unpack": true
+  },
+  {
+    "version": "clang 5.0pre/r293859",
+    "size": 309009013,
+    "digest": "cd3ed31acefd185f441632158dde73538c62bab7ebf2a8ec630985ab345938ec522983721ddb1bead1de22d5ac1571d50a958ae002364d739f2a78c6e7244222",
+    "algorithm": "sha512",
+    "filename": "clang.tar.bz2",
+    "unpack": true
+  },
+  {
+    "version": "makecab rev d2bc6797648b7a834782714a55d339d2fd4e58c8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "makecab.tar.bz2",
+    "unpack": true,
+    "digest": "196ac6a567c85559957dfe511c3d8654d23c94d5603259e19ccafe9d71e0e4ccee63ccc9a778f2699654b786cda54266108b7d4db543d01bb0b42545b4e6ec75",
+    "size": 297118
+  }
 ]
--- a/browser/config/tooltool-manifests/win32/l10n.manifest
+++ b/browser/config/tooltool-manifests/win32/l10n.manifest
@@ -1,8 +1,8 @@
 [
-{
-"size": 266240,
-"digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
-"algorithm": "sha512",
-"filename": "mozmake.exe"
-}
+  {
+    "size": 266240,
+    "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
+    "algorithm": "sha512",
+    "filename": "mozmake.exe"
+  }
 ]
--- a/browser/config/tooltool-manifests/win32/releng.manifest
+++ b/browser/config/tooltool-manifests/win32/releng.manifest
@@ -1,42 +1,42 @@
 [
-{
-"size": 266240,
-"digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
-"algorithm": "sha512",
-"filename": "mozmake.exe"
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 67227255,
-"digest": "95164dc1abb2ab90ea18cf4f322d5c8390e0212e79a52b12578720d568eb6df62e55a9938c8d2a364cd3a6bd0f99608c2139d141a88ec43484b8d0651398532a",
-"algorithm": "sha512",
-"filename": "rustc.tar.bz2",
-"unpack": true
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.bz2",
-"unpack": true,
-"digest": "7dee5c5602b3830cb8ac45ebaa8542714bbac0e50eabbff58a06972a02ceeab75ed7c56ff22a23f760b8317ae8e9a01cdecfaf75a7acbd2a4cdd817967170d2e",
-"size": 1179901
-},
-{
-"version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
-"size": 326656969,
-"digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
-"algorithm": "sha512",
-"filename": "vs2015u3.zip",
-"unpack": true
-},
-{
-"version": "makecab rev d2bc6797648b7a834782714a55d339d2fd4e58c8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "makecab.tar.bz2",
-"unpack": true,
-"digest": "196ac6a567c85559957dfe511c3d8654d23c94d5603259e19ccafe9d71e0e4ccee63ccc9a778f2699654b786cda54266108b7d4db543d01bb0b42545b4e6ec75",
-"size": 297118
-}
+  {
+    "size": 266240,
+    "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
+    "algorithm": "sha512",
+    "filename": "mozmake.exe"
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 67227255,
+    "digest": "95164dc1abb2ab90ea18cf4f322d5c8390e0212e79a52b12578720d568eb6df62e55a9938c8d2a364cd3a6bd0f99608c2139d141a88ec43484b8d0651398532a",
+    "algorithm": "sha512",
+    "filename": "rustc.tar.bz2",
+    "unpack": true
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.bz2",
+    "unpack": true,
+    "digest": "7dee5c5602b3830cb8ac45ebaa8542714bbac0e50eabbff58a06972a02ceeab75ed7c56ff22a23f760b8317ae8e9a01cdecfaf75a7acbd2a4cdd817967170d2e",
+    "size": 1179901
+  },
+  {
+    "version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
+    "size": 326656969,
+    "digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
+    "algorithm": "sha512",
+    "filename": "vs2015u3.zip",
+    "unpack": true
+  },
+  {
+    "version": "makecab rev d2bc6797648b7a834782714a55d339d2fd4e58c8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "makecab.tar.bz2",
+    "unpack": true,
+    "digest": "196ac6a567c85559957dfe511c3d8654d23c94d5603259e19ccafe9d71e0e4ccee63ccc9a778f2699654b786cda54266108b7d4db543d01bb0b42545b4e6ec75",
+    "size": 297118
+  }
 ]
--- a/browser/config/tooltool-manifests/win64/clang.manifest
+++ b/browser/config/tooltool-manifests/win64/clang.manifest
@@ -1,51 +1,51 @@
 [
-{
-"size": 266240,
-"digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
-"algorithm": "sha512",
-"filename": "mozmake.exe"
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 72782416,
-"digest": "bf9fb1a21de834d7b6992fe9d95dfe2ff19e3cbff5b00117889eef0e6c9d85442cd75640e3051f0c1788b1f629e79ffae5cc8a4106b0cad520c1f954ae2be6b0",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "rustc.tar.bz2",
-"unpack": true
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.bz2",
-"unpack": true,
-"digest": "7dee5c5602b3830cb8ac45ebaa8542714bbac0e50eabbff58a06972a02ceeab75ed7c56ff22a23f760b8317ae8e9a01cdecfaf75a7acbd2a4cdd817967170d2e",
-"size": 1179901
-},
-{
-"version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
-"size": 326656969,
-"digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
-"algorithm": "sha512",
-"filename": "vs2015u3.zip",
-"unpack": true
-},
-{
-"version": "clang 5.0pre/r293859",
-"size": 313862839,
-"digest": "44dee70d525ea93952af27f943d1cc773311970c31d971d2bc2e3437cce0c899f3a03ddd8e42e86f1b4fd9ab1c4bc1767cdb0406eb4b3934ae4fc272dab830dc",
-"algorithm": "sha512",
-"filename": "clang.tar.bz2",
-"unpack": true
-},
-{
-"version": "makecab rev d2bc6797648b7a834782714a55d339d2fd4e58c8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "makecab.tar.bz2",
-"unpack": true,
-"digest": "196ac6a567c85559957dfe511c3d8654d23c94d5603259e19ccafe9d71e0e4ccee63ccc9a778f2699654b786cda54266108b7d4db543d01bb0b42545b4e6ec75",
-"size": 297118
-}
+  {
+    "size": 266240,
+    "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
+    "algorithm": "sha512",
+    "filename": "mozmake.exe"
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 72782416,
+    "digest": "bf9fb1a21de834d7b6992fe9d95dfe2ff19e3cbff5b00117889eef0e6c9d85442cd75640e3051f0c1788b1f629e79ffae5cc8a4106b0cad520c1f954ae2be6b0",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "rustc.tar.bz2",
+    "unpack": true
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.bz2",
+    "unpack": true,
+    "digest": "7dee5c5602b3830cb8ac45ebaa8542714bbac0e50eabbff58a06972a02ceeab75ed7c56ff22a23f760b8317ae8e9a01cdecfaf75a7acbd2a4cdd817967170d2e",
+    "size": 1179901
+  },
+  {
+    "version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
+    "size": 326656969,
+    "digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
+    "algorithm": "sha512",
+    "filename": "vs2015u3.zip",
+    "unpack": true
+  },
+  {
+    "version": "clang 5.0pre/r293859",
+    "size": 313862839,
+    "digest": "44dee70d525ea93952af27f943d1cc773311970c31d971d2bc2e3437cce0c899f3a03ddd8e42e86f1b4fd9ab1c4bc1767cdb0406eb4b3934ae4fc272dab830dc",
+    "algorithm": "sha512",
+    "filename": "clang.tar.bz2",
+    "unpack": true
+  },
+  {
+    "version": "makecab rev d2bc6797648b7a834782714a55d339d2fd4e58c8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "makecab.tar.bz2",
+    "unpack": true,
+    "digest": "196ac6a567c85559957dfe511c3d8654d23c94d5603259e19ccafe9d71e0e4ccee63ccc9a778f2699654b786cda54266108b7d4db543d01bb0b42545b4e6ec75",
+    "size": 297118
+  }
 ]
--- a/browser/config/tooltool-manifests/win64/l10n.manifest
+++ b/browser/config/tooltool-manifests/win64/l10n.manifest
@@ -1,8 +1,8 @@
 [
-{
-"size": 266240,
-"digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
-"algorithm": "sha512",
-"filename": "mozmake.exe"
-}
+  {
+    "size": 266240,
+    "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
+    "algorithm": "sha512",
+    "filename": "mozmake.exe"
+  }
 ]
--- a/browser/config/tooltool-manifests/win64/releng.manifest
+++ b/browser/config/tooltool-manifests/win64/releng.manifest
@@ -1,43 +1,43 @@
 [
-{
-"size": 266240,
-"digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
-"algorithm": "sha512",
-"filename": "mozmake.exe"
-},
-{
-"version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
-"size": 72782416,
-"digest": "bf9fb1a21de834d7b6992fe9d95dfe2ff19e3cbff5b00117889eef0e6c9d85442cd75640e3051f0c1788b1f629e79ffae5cc8a4106b0cad520c1f954ae2be6b0",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "rustc.tar.bz2",
-"unpack": true
-},
-{
-"version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "sccache2.tar.bz2",
-"unpack": true,
-"digest": "7dee5c5602b3830cb8ac45ebaa8542714bbac0e50eabbff58a06972a02ceeab75ed7c56ff22a23f760b8317ae8e9a01cdecfaf75a7acbd2a4cdd817967170d2e",
-"size": 1179901
-},
-{
-"version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
-"size": 326656969,
-"digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
-"algorithm": "sha512",
-"filename": "vs2015u3.zip",
-"unpack": true
-},
-{
-"version": "makecab rev d2bc6797648b7a834782714a55d339d2fd4e58c8",
-"algorithm": "sha512",
-"visibility": "public",
-"filename": "makecab.tar.bz2",
-"unpack": true,
-"digest": "196ac6a567c85559957dfe511c3d8654d23c94d5603259e19ccafe9d71e0e4ccee63ccc9a778f2699654b786cda54266108b7d4db543d01bb0b42545b4e6ec75",
-"size": 297118
-}
+  {
+    "size": 266240,
+    "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
+    "algorithm": "sha512",
+    "filename": "mozmake.exe"
+  },
+  {
+    "version": "rustc 1.15.1 (021bd294c 2017-02-08) repack",
+    "size": 72782416,
+    "digest": "bf9fb1a21de834d7b6992fe9d95dfe2ff19e3cbff5b00117889eef0e6c9d85442cd75640e3051f0c1788b1f629e79ffae5cc8a4106b0cad520c1f954ae2be6b0",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "rustc.tar.bz2",
+    "unpack": true
+  },
+  {
+    "version": "sccache rev b21198a7183a2fe226ff49348b1c0b51bae9f4f8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "sccache2.tar.bz2",
+    "unpack": true,
+    "digest": "7dee5c5602b3830cb8ac45ebaa8542714bbac0e50eabbff58a06972a02ceeab75ed7c56ff22a23f760b8317ae8e9a01cdecfaf75a7acbd2a4cdd817967170d2e",
+    "size": 1179901
+  },
+  {
+    "version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
+    "size": 326656969,
+    "digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
+    "algorithm": "sha512",
+    "filename": "vs2015u3.zip",
+    "unpack": true
+  },
+  {
+    "version": "makecab rev d2bc6797648b7a834782714a55d339d2fd4e58c8",
+    "algorithm": "sha512",
+    "visibility": "public",
+    "filename": "makecab.tar.bz2",
+    "unpack": true,
+    "digest": "196ac6a567c85559957dfe511c3d8654d23c94d5603259e19ccafe9d71e0e4ccee63ccc9a778f2699654b786cda54266108b7d4db543d01bb0b42545b4e6ec75",
+    "size": 297118
+  }
 ]
--- a/browser/extensions/formautofill/.eslintrc.js
+++ b/browser/extensions/formautofill/.eslintrc.js
@@ -104,17 +104,17 @@ module.exports = { // eslint-disable-lin
     // since only let and const are used, see "no-var").
     "block-scoped-var": "error",
 
     // Allow trailing commas for easy list extension.  Having them does not
     // impair readability, but also not required either.
     "comma-dangle": ["error", "always-multiline"],
 
     // Warn about cyclomatic complexity in functions.
-    "complexity": "warn",
+    "complexity": ["error", {"max": 20}],
 
     // Don't warn for inconsistent naming when capturing this (not so important
     // with auto-binding fat arrow functions).
     // "consistent-this": ["error", "self"],
 
     // Enforce dots on the next line with property name.
     "dot-location": ["error", "property"],
 
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -86,17 +86,30 @@ AutofillProfileAutoCompleteSearch.protot
    * @param {string} searchParam
    * @param {Object} previousResult a previous result to use for faster searchinig
    * @param {Object} listener the listener to notify when the search is complete
    */
   startSearch(searchString, searchParam, previousResult, listener) {
     this.log.debug("startSearch: for", searchString, "with input", formFillController.focusedInput);
     let focusedInput = formFillController.focusedInput;
     this.forceStop = false;
-    let info = this._serializeInfo(FormAutofillContent.getInputDetails(focusedInput));
+    let info = FormAutofillContent.getInputDetails(focusedInput);
+
+    if (!FormAutofillContent.savedFieldNames.has(info.fieldName) ||
+        FormAutofillContent.getFormHandler(focusedInput).filledProfileGUID) {
+      let formHistory = Cc["@mozilla.org/autocomplete/search;1?name=form-history"]
+                          .createInstance(Ci.nsIAutoCompleteSearch);
+      formHistory.startSearch(searchString, searchParam, previousResult, {
+        onSearchResult: (search, result) => {
+          listener.onSearchResult(this, result);
+          ProfileAutocomplete.setProfileAutoCompleteResult(result);
+        },
+      });
+      return;
+    }
 
     this._getProfiles({info, searchString}).then((profiles) => {
       if (this.forceStop) {
         return;
       }
 
       let allFieldNames = FormAutofillContent.getAllFieldNames(focusedInput);
       let result = new ProfileAutoCompleteResult(searchString,
@@ -137,22 +150,16 @@ AutofillProfileAutoCompleteSearch.protot
       Services.cpmm.addMessageListener("FormAutofill:Profiles", function getResult(result) {
         Services.cpmm.removeMessageListener("FormAutofill:Profiles", getResult);
         resolve(result.data);
       });
 
       Services.cpmm.sendAsyncMessage("FormAutofill:GetProfiles", data);
     });
   },
-
-  _serializeInfo(detail) {
-    let info = Object.assign({}, detail);
-    delete info.element;
-    return info;
-  },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([AutofillProfileAutoCompleteSearch]);
 
 let ProfileAutocomplete = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   _lastAutoCompleteResult: null,
@@ -245,30 +252,49 @@ let ProfileAutocomplete = {
  * NOTE: Declares it by "var" to make it accessible in unit tests.
  */
 var FormAutofillContent = {
   /**
    * @type {WeakMap} mapping FormLike root HTML elements to FormAutofillHandler objects.
    */
   _formsDetails: new WeakMap(),
 
+  /**
+   * @type {Set} Set of the fields with usable values in any saved profile.
+   */
+  savedFieldNames: null,
+
   init() {
     FormAutofillUtils.defineLazyLogGetter(this, "FormAutofillContent");
 
-    Services.cpmm.addMessageListener("FormAutofill:enabledStatus", (result) => {
-      if (result.data) {
-        ProfileAutocomplete.ensureRegistered();
-      } else {
-        ProfileAutocomplete.ensureUnregistered();
-      }
-    });
+    Services.cpmm.addMessageListener("FormAutofill:enabledStatus", this);
+    Services.cpmm.addMessageListener("FormAutofill:savedFieldNames", this);
 
     if (Services.cpmm.initialProcessData.autofillEnabled) {
       ProfileAutocomplete.ensureRegistered();
     }
+
+    this.savedFieldNames =
+      Services.cpmm.initialProcessData.autofillSavedFieldNames || new Set();
+  },
+
+  receiveMessage({name, data}) {
+    switch (name) {
+      case "FormAutofill:enabledStatus": {
+        if (data) {
+          ProfileAutocomplete.ensureRegistered();
+        } else {
+          ProfileAutocomplete.ensureUnregistered();
+        }
+        break;
+      }
+      case "FormAutofill:savedFieldNames": {
+        this.savedFieldNames = data;
+      }
+    }
   },
 
   /**
    * Get the input's information from cache which is created after page identified.
    *
    * @param {HTMLInputElement} element Focused input which triggered profile searching
    * @returns {Object|null}
    *          Return target input's information that cloned from content cache
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -48,75 +48,70 @@ FormAutofillHandler.prototype = {
    * the same exact combination of these values.
    *
    * A direct reference to the associated element cannot be sent to the user
    * interface because processing may be done in the parent process.
    */
   fieldDetails: null,
 
   /**
-   * Returns information from the form about fields that can be autofilled, and
-   * populates the fieldDetails array on this object accordingly.
-   *
-   * @returns {Array<Object>} Serializable data structure that can be sent to the user
-   *          interface, or null if the operation failed because the constraints
-   *          on the allowed fields were not honored.
+   * String of the filled profile's guid.
+   */
+  filledProfileGUID: null,
+
+  /**
+   * Set fieldDetails from the form about fields that can be autofilled.
    */
   collectFormFields() {
-    let autofillData = [];
+    this.fieldDetails = [];
 
     for (let element of this.form.elements) {
       // Exclude elements to which no autocomplete field has been assigned.
       let info = FormAutofillHeuristics.getInfo(element);
       if (!info) {
         continue;
       }
 
       // Store the association between the field metadata and the element.
       if (this.fieldDetails.some(f => f.section == info.section &&
                                       f.addressType == info.addressType &&
                                       f.contactType == info.contactType &&
                                       f.fieldName == info.fieldName)) {
         // A field with the same identifier already exists.
         log.debug("Not collecting a field matching another with the same info:", info);
-        return null;
+        continue;
       }
 
-      let inputFormat = {
+      let formatWithElement = {
         section: info.section,
         addressType: info.addressType,
         contactType: info.contactType,
         fieldName: info.fieldName,
+        element, // TODO: Apply Cu.getWeakReference and use get API for strong ref.
       };
-      // Clone the inputFormat for caching the fields and elements together
-      let formatWithElement = Object.assign({}, inputFormat);
 
-      inputFormat.index = autofillData.length;
-      autofillData.push(inputFormat);
-
-      formatWithElement.element = element;
       this.fieldDetails.push(formatWithElement);
     }
 
-    log.debug("Collected details on", autofillData.length, "fields");
-
-    return autofillData;
+    log.debug("Collected details on", this.fieldDetails.length, "fields");
   },
 
   /**
    * Processes form fields that can be autofilled, and populates them with the
    * profile provided by backend.
    *
    * @param {Object} profile
    *        A profile to be filled in.
    * @param {Object} focusedInput
    *        A focused input element which is skipped for filling.
    */
   autofillFormFields(profile, focusedInput) {
     log.debug("profile in autofillFormFields:", profile);
+
+    this.filledProfileGUID = profile.guid;
     for (let fieldDetail of this.fieldDetails) {
       // Avoid filling field value in the following cases:
       // 1. the focused input which is filled in FormFillController.
       // 2. a non-empty input field
       // 3. the invalid value set
 
       if (fieldDetail.element === focusedInput ||
           fieldDetail.element.value) {
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -80,16 +80,17 @@ FormAutofillParent.prototype = {
 
     // Observing the pref and storage changes
     Services.prefs.addObserver(ENABLED_PREF, this, false);
     Services.obs.addObserver(this, "formautofill-storage-changed", false);
 
     // Force to trigger the onStatusChanged function for setting listeners properly
     // while initizlization
     this._setStatus(this._getStatus());
+    this._updateSavedFieldNames();
   },
 
   observe(subject, topic, data) {
     log.debug("observe:", topic, "with data:", data);
     switch (topic) {
       case "advanced-pane-loaded": {
         let formAutofillPreferences = new FormAutofillPreferences();
         let document = subject.document;
@@ -110,16 +111,17 @@ FormAutofillParent.prototype = {
       }
 
       case "formautofill-storage-changed": {
         // Early exit if the action is not "add" nor "remove"
         if (data != "add" && data != "remove") {
           break;
         }
 
+        this._updateSavedFieldNames();
         let currentStatus = this._getStatus();
         if (currentStatus !== this._enabled) {
           this._setStatus(currentStatus);
         }
         break;
       }
 
       default: {
@@ -239,9 +241,34 @@ FormAutofillParent.prototype = {
     if (info && info.fieldName) {
       profiles = this._profileStore.getByFilter({searchString, info});
     } else {
       profiles = this._profileStore.getAll();
     }
 
     target.sendAsyncMessage("FormAutofill:Profiles", profiles);
   },
+
+  _updateSavedFieldNames() {
+    if (!Services.ppmm.initialProcessData.autofillSavedFieldNames) {
+      Services.ppmm.initialProcessData.autofillSavedFieldNames = new Set();
+    } else {
+      Services.ppmm.initialProcessData.autofillSavedFieldNames.clear();
+    }
+
+    this._profileStore.getAll().forEach((profile) => {
+      Object.keys(profile).forEach((fieldName) => {
+        if (!profile[fieldName]) {
+          return;
+        }
+        Services.ppmm.initialProcessData.autofillSavedFieldNames.add(fieldName);
+      });
+    });
+
+    // Remove the internal guid and metadata fields.
+    this._profileStore.INTERNAL_FIELDS.forEach((fieldName) => {
+      Services.ppmm.initialProcessData.autofillSavedFieldNames.delete(fieldName);
+    });
+
+    Services.ppmm.broadcastAsyncMessage("FormAutofill:savedFieldNames",
+                                        Services.ppmm.initialProcessData.autofillSavedFieldNames);
+  },
 };
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -87,16 +87,19 @@ const MOCK_STORAGE = [{
   tel: "1-650-903-0800",
 }];
 
 function ProfileStorage(path) {
   this._path = path;
 }
 
 ProfileStorage.prototype = {
+  // These fields are defined internally for each profile.
+  INTERNAL_FIELDS:
+    ["guid", "timeCreated", "timeLastUsed", "timeLastModified", "timesUsed"],
   /**
    * Loads the profile data from file to memory.
    *
    * @returns {Promise}
    * @resolves When the operation finished successfully.
    * @rejects  JavaScript exception.
    */
   initialize() {
--- a/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
@@ -36,16 +36,17 @@ const TESTCASES = [
       {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "country", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "email", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "tel", "element": {}},
     ],
     profileData: {
+      "guid": "123",
       "street-address": "2 Harrison St",
       "address-level2": "San Francisco",
       "country": "US",
       "email": "foo@mozilla.com",
       "tel": "1234567",
     },
     expectedResult: {
       "street-addr": "2 Harrison St",
@@ -69,16 +70,17 @@ const TESTCASES = [
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
     ],
     profileData: {
+      "guid": "123",
       "street-address": "2 Harrison St",
       "address-level2": "San Francisco",
       "country": "US",
       "email": "foo@mozilla.com",
       "tel": "1234567",
     },
     expectedResult: {
       "street-addr": "2 Harrison St",
@@ -102,16 +104,17 @@ const TESTCASES = [
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
     ],
     profileData: {
+      "guid": "123",
       "street-address": "2 Harrison St",
       "address-level2": "San Francisco",
       "country": "US",
       "email": "",
       "tel": "",
     },
     expectedResult: {
       "street-addr": "2 Harrison St",
@@ -135,16 +138,17 @@ const TESTCASES = [
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
     ],
     profileData: {
+      "guid": "123",
       "street-address": "",
       "address-level2": "",
       "country": "",
       "email": "foo@mozilla.com",
       "tel": "1234567",
     },
     expectedResult: {
       "street-addr": "",
@@ -183,12 +187,15 @@ for (let tc of TESTCASES) {
             Assert.equal(element.value, testcase.expectedResult[id],
                         "Check the " + id + " fields were filled with correct data");
             resolve();
           }, {once: true});
         }));
       });
 
       handler.autofillFormFields(testcase.profileData);
+
+      Assert.equal(handler.filledProfileGUID, testcase.profileData.guid,
+                   "Check if filledProfileGUID is set correctly");
       yield Promise.all(onChangePromises);
     });
   })();
 }
--- a/browser/extensions/formautofill/test/unit/test_collectFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_collectFormFields.js
@@ -19,23 +19,16 @@ const TESTCASES = [
     description: "Form with autocomplete properties and 1 token",
     document: `<form><input id="given-name" autocomplete="given-name">
                <input id="family-name" autocomplete="family-name">
                <input id="street-addr" autocomplete="street-address">
                <input id="city" autocomplete="address-level2">
                <input id="country" autocomplete="country">
                <input id="email" autocomplete="email">
                <input id="tel" autocomplete="tel"></form>`,
-    returnedFormat: [
-      {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address", "index": 0},
-      {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2", "index": 1},
-      {"section": "", "addressType": "", "contactType": "", "fieldName": "country", "index": 2},
-      {"section": "", "addressType": "", "contactType": "", "fieldName": "email", "index": 3},
-      {"section": "", "addressType": "", "contactType": "", "fieldName": "tel", "index": 4},
-    ],
     fieldDetails: [
       {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "country", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "email", "element": {}},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "tel", "element": {}},
     ],
   },
@@ -43,23 +36,16 @@ const TESTCASES = [
     description: "Form with autocomplete properties and 2 tokens",
     document: `<form><input id="given-name" autocomplete="shipping given-name">
                <input id="family-name" autocomplete="shipping family-name">
                <input id="street-addr" autocomplete="shipping street-address">
                <input id="city" autocomplete="shipping address-level2">
                <input id="country" autocomplete="shipping country">
                <input id='email' autocomplete="shipping email">
                <input id="tel" autocomplete="shipping tel"></form>`,
-    returnedFormat: [
-      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "index": 0},
-      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "index": 1},
-      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "index": 2},
-      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "index": 3},
-      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "index": 4},
-    ],
     fieldDetails: [
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
     ],
   },
@@ -67,23 +53,16 @@ const TESTCASES = [
     description: "Form with autocomplete properties and profile is partly matched",
     document: `<form><input id="given-name" autocomplete="shipping given-name">
                <input id="family-name" autocomplete="shipping family-name">
                <input id="street-addr" autocomplete="shipping street-address">
                <input id="city" autocomplete="shipping address-level2">
                <input id="country" autocomplete="shipping country">
                <input id='email' autocomplete="shipping email">
                <input id="tel" autocomplete="shipping tel"></form>`,
-    returnedFormat: [
-      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "index": 0},
-      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "index": 1},
-      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "index": 2},
-      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "index": 3},
-      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "index": 4},
-    ],
     fieldDetails: [
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "email", "element": {}},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "tel", "element": {}},
     ],
   },
@@ -95,16 +74,15 @@ for (let tc of TESTCASES) {
     add_task(function* () {
       do_print("Starting testcase: " + testcase.description);
 
       let doc = MockDocument.createTestDocument("http://localhost:8080/test/",
                                                 testcase.document);
       let form = doc.querySelector("form");
       let handler = new FormAutofillHandler(form);
 
-      Assert.deepEqual(handler.collectFormFields(), testcase.returnedFormat,
-                         "Check the format of form autofill were returned correctly");
+      handler.collectFormFields();
 
       Assert.deepEqual(handler.fieldDetails, testcase.fieldDetails,
                          "Check the fieldDetails were set correctly");
     });
   })();
 }
--- a/browser/extensions/formautofill/test/unit/test_enabledStatus.js
+++ b/browser/extensions/formautofill/test/unit/test_enabledStatus.js
@@ -20,16 +20,17 @@ add_task(function* test_enabledStatus_in
 
   formAutofillParent._uninit();
 });
 
 add_task(function* test_enabledStatus_observe() {
   let formAutofillParent = new FormAutofillParent();
   sinon.stub(formAutofillParent, "_getStatus");
   sinon.spy(formAutofillParent, "_setStatus");
+  sinon.stub(formAutofillParent, "_updateSavedFieldNames");
 
   // _enabled = _getStatus() => No need to trigger onStatusChanged
   formAutofillParent._enabled = true;
   formAutofillParent._getStatus.returns(true);
   formAutofillParent.observe(null, "nsPref:changed", "browser.formautofill.enabled");
   do_check_eq(formAutofillParent._setStatus.called, false);
 
   // _enabled != _getStatus() => Need to trigger onStatusChanged
new file mode 100644
--- /dev/null
+++ b/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
@@ -0,0 +1,89 @@
+/*
+ * Test for keeping the valid fields information in initialProcessData.
+ */
+
+"use strict";
+
+Cu.import("resource://formautofill/FormAutofillParent.jsm");
+Cu.import("resource://formautofill/ProfileStorage.jsm");
+
+add_task(function* test_profileSavedFieldNames_init() {
+  let formAutofillParent = new FormAutofillParent();
+  sinon.stub(formAutofillParent, "_updateSavedFieldNames");
+
+  formAutofillParent.init();
+  do_check_eq(formAutofillParent._updateSavedFieldNames.called, true);
+
+  formAutofillParent._uninit();
+});
+
+add_task(function* test_profileSavedFieldNames_observe() {
+  let formAutofillParent = new FormAutofillParent();
+  sinon.stub(formAutofillParent, "_updateSavedFieldNames");
+
+  // profile added => Need to trigger updateValidFields
+  formAutofillParent.observe(null, "formautofill-storage-changed", "add");
+  do_check_eq(formAutofillParent._updateSavedFieldNames.called, true);
+
+  // profile removed => Need to trigger updateValidFields
+  formAutofillParent._updateSavedFieldNames.reset();
+  formAutofillParent.observe(null, "formautofill-storage-changed", "remove");
+  do_check_eq(formAutofillParent._updateSavedFieldNames.called, true);
+
+  // profile updated => no need to trigger updateValidFields
+  formAutofillParent._updateSavedFieldNames.reset();
+  formAutofillParent.observe(null, "formautofill-storage-changed", "update");
+  do_check_eq(formAutofillParent._updateSavedFieldNames.called, false);
+});
+
+add_task(function* test_profileSavedFieldNames_update() {
+  let formAutofillParent = new FormAutofillParent();
+  formAutofillParent.init();
+  do_register_cleanup(function cleanup() {
+    Services.prefs.clearUserPref("browser.formautofill.enabled");
+  });
+
+  sinon.stub(formAutofillParent._profileStore, "getAll");
+  formAutofillParent._profileStore.getAll.returns([]);
+
+  // The set is empty if there's no profile in the store.
+  formAutofillParent._updateSavedFieldNames();
+  do_check_eq(Services.ppmm.initialProcessData.autofillSavedFieldNames.size, 0);
+
+  // 2 profiles with 4 valid fields.
+  let fakeStorage = [{
+    guid: "test-guid-1",
+    organization: "Sesame Street",
+    "street-address": "123 Sesame Street.",
+    tel: "1-345-345-3456",
+    email: "",
+    timeCreated: 0,
+    timeLastUsed: 0,
+    timeLastModified: 0,
+    timesUsed: 0,
+  }, {
+    guid: "test-guid-2",
+    organization: "Mozilla",
+    "street-address": "331 E. Evelyn Avenue",
+    tel: "1-650-903-0800",
+    country: "US",
+    timeCreated: 0,
+    timeLastUsed: 0,
+    timeLastModified: 0,
+    timesUsed: 0,
+  }];
+  formAutofillParent._profileStore.getAll.returns(fakeStorage);
+  formAutofillParent._updateSavedFieldNames();
+
+  let autofillSavedFieldNames = Services.ppmm.initialProcessData.autofillSavedFieldNames;
+  do_check_eq(autofillSavedFieldNames.size, 4);
+  do_check_eq(autofillSavedFieldNames.has("organization"), true);
+  do_check_eq(autofillSavedFieldNames.has("street-address"), true);
+  do_check_eq(autofillSavedFieldNames.has("tel"), true);
+  do_check_eq(autofillSavedFieldNames.has("email"), false);
+  do_check_eq(autofillSavedFieldNames.has("guid"), false);
+  do_check_eq(autofillSavedFieldNames.has("timeCreated"), false);
+  do_check_eq(autofillSavedFieldNames.has("timeLastUsed"), false);
+  do_check_eq(autofillSavedFieldNames.has("timeLastModified"), false);
+  do_check_eq(autofillSavedFieldNames.has("timesUsed"), false);
+});
--- a/browser/extensions/formautofill/test/unit/xpcshell.ini
+++ b/browser/extensions/formautofill/test/unit/xpcshell.ini
@@ -5,8 +5,10 @@ support-files =
 
 [test_autofillFormFields.js]
 [test_collectFormFields.js]
 [test_enabledStatus.js]
 [test_getFormInputDetails.js]
 [test_markAsAutofillField.js]
 [test_profileAutocompleteResult.js]
 [test_profileStorage.js]
+[test_savedFieldNames.js]
+
--- a/browser/extensions/mortar/host/common/ppapi-runtime.jsm
+++ b/browser/extensions/mortar/host/common/ppapi-runtime.jsm
@@ -890,16 +890,19 @@ class Graphics extends PP_Resource {
     super(instance);
     this.canvas = instance.window.document.createElement("canvas");
   }
   destroy() {
     this.canvas.remove();
     super.destroy();
   }
   changeSize(width, height) {
+    let devicePixelRatio = this.instance.window.devicePixelRatio;
+    this.canvas.style.width = (width / devicePixelRatio) + "px";
+    this.canvas.style.height = (height / devicePixelRatio) + "px";
     this.canvas.width = width;
     this.canvas.height = height;
   }
 }
 class Graphics2DPaintOperation {
   constructor(imageData, x, y, dirtyRect=[]) {
     this.imageData = imageData;
     this.imageData.addRef();
@@ -1546,16 +1549,23 @@ class PPAPIInstance {
         type: "fullscreenChange",
         fullscreen: evt.data.fullscreen
       });
     });
 
     this.mm.addMessageListener("ppapipdf.js:hashchange", (evt) => {
       this.notifyHashChange(evt.data.url);
     });
+
+    this.mm.addMessageListener("ppapipdf.js:oncommand", (evt) => {
+      this.viewport.notify({
+        type: "command",
+        name: evt.data.name
+      });
+    });
   }
 
   notifyHashChange(url) {
     let location = new URL(url);
     if (location.hash) {
       this.viewport.notify({
         type: "hashChange",
         // substring(1) for getting rid of the first '#' character
@@ -5318,30 +5328,26 @@ dump(`callFromJSON: < ${JSON.stringify(c
       }
       return [PP_Bool.PP_TRUE, { rect: { point, size } }];
     },
 
     /**
      * float_t GetDeviceScale([in] PP_Resource resource);
      */
     PPB_View_GetDeviceScale: function(json) {
-      // FIXME Need to figure out how to get the ratio between device pixels
-      //       and DIPs.
       let view = PP_Resource.lookup(json.resource);
-      return 1; //view.instance.window.devicePixelRatio;
+      return view.instance.window.devicePixelRatio;
     },
 
     /**
      * float_t GetCSSScale([in] PP_Resource resource);
      */
     PPB_View_GetCSSScale: function(json) {
-      // FIXME Need to figure out how to get the ratio between CSS pixels
-      //       and DIPs.
       let view = PP_Resource.lookup(json.resource);
-      return view.instance.window.devicePixelRatio;
+      return 1;
     },
 
     /**
      * PP_Bool GetScrollOffset([in] PP_Resource resource,
      *                         [out] PP_Point offset);
      */
     PPB_View_GetScrollOffset: function(json) {
       let view = PP_Resource.lookup(json.resource);
--- a/browser/extensions/mortar/host/pdf/chrome/js/viewport.js
+++ b/browser/extensions/mortar/host/pdf/chrome/js/viewport.js
@@ -589,16 +589,31 @@ class Viewport {
   _handleHashChange(hash) {
     if (!this._documentDimensions) {
       this._initPosition = hash;
     } else {
       this._jumpToBookmark(hash);
     }
   }
 
+  _handleCommand(name) {
+    switch(name) {
+      case 'cmd_selectAll':
+        this._doAction({
+          type: 'selectAll'
+        });
+        break;
+      case 'cmd_copy':
+        this._doAction({
+          type: 'getSelectedText'
+        })
+        break;
+    }
+  }
+
   verifyPassword(password) {
     this._doAction({
       type: 'getPasswordComplete',
       password: password
     });
   }
 
   handleEvent(evt) {
@@ -609,22 +624,16 @@ class Viewport {
       case 'scroll':
         this._nextPosition = null;
         let position = this.getScrollOffset();
         if (this._runtimePosition.x != position.x ||
             this._runtimePosition.y != position.y) {
           this._refresh();
         }
         break;
-      case 'copy':
-        this._doAction({
-          type: 'getSelectedText'
-        })
-        evt.preventDefault();
-        break;
     }
   }
 
   invokeResize() {
     if (this._fullscreenStatus == 'changing') {
       return;
     }
     this._resize();
@@ -751,11 +760,14 @@ class Viewport {
       case 'getSelectedTextReply':
         // For now this message is used only by text copy so we handle just
         // that case.
         this._copyToClipboard(message.selectedText);
         break;
       case 'hashChange':
         this._handleHashChange(message.hash);
         break;
+      case 'command':
+        this._handleCommand(message.name);
+        break;
     }
   }
 }
--- a/browser/extensions/mortar/host/pdf/ppapi-content-sandbox.js
+++ b/browser/extensions/mortar/host/pdf/ppapi-content-sandbox.js
@@ -178,9 +178,40 @@ mm.addMessageListener("ppapipdf.js:save"
           .onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
       }
     };
 
     channel.asyncOpen2(listener);
   });
 });
 
+// This class is created to transfer global XUL commands event we needed.
+// The main reason we need to transfer it from sandbox side is that commands
+// triggered from menu bar targets only the outmost iframe (which is sandbox
+// itself) so we need to propagate it manually into the plugin's iframe.
+class CommandController {
+  constructor() {
+    this.SUPPORTED_COMMANDS = ['cmd_copy', 'cmd_selectAll'];
+    containerWindow.controllers.insertControllerAt(0, this);
+    containerWindow.addEventListener('unload', this.terminate.bind(this));
+  }
+
+  terminate() {
+    containerWindow.controllers.removeController(this);
+  }
+
+  supportsCommand(cmd) {
+    return this.SUPPORTED_COMMANDS.includes(cmd);
+  }
+
+  isCommandEnabled(cmd) {
+    return this.SUPPORTED_COMMANDS.includes(cmd);
+  }
+
+  doCommand(cmd) {
+    mm.sendAsyncMessage("ppapipdf.js:oncommand", {name: cmd});
+  }
+
+  onEvent(evt) {}
+};
+var commandController = new CommandController();
+
 mm.loadFrameScript("resource://ppapi.js/ppapi-instance.js", true);
--- a/browser/extensions/pdfjs/test/browser_pdfjs_zoom.js
+++ b/browser/extensions/pdfjs/test/browser_pdfjs_zoom.js
@@ -67,17 +67,17 @@ add_task(function* test() {
      "pdf handler defaults to always-ask is false");
   is(handlerInfo.preferredAction, Ci.nsIHandlerInfo.handleInternally,
     "pdf handler defaults to internal");
 
   info("Pref action: " + handlerInfo.preferredAction);
 
   yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" },
     function* (newTabBrowser) {
-      yield waitForPdfJS(newTabBrowser, TESTROOT + "file_pdfjs_test.pdf" + "#zoom=100");
+      yield waitForPdfJS(newTabBrowser, TESTROOT + "file_pdfjs_test.pdf#zoom=100");
 
       yield ContentTask.spawn(newTabBrowser, TESTS, function* (contentTESTS) {
         let document = content.document;
 
         function waitForRender() {
           return new Promise((resolve) => {
             document.addEventListener("pagerendered", function onPageRendered(e) {
               if (e.detail.pageNumber !== 1) {
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -635,21 +635,21 @@ getUserMedia.shareApplicationWindowCount
 #                    getUserMedia.dontAllow.label):
 # These two buttons are the possible answers to the various prompts in the
 # "getUserMedia.share{device}.message" strings.
 getUserMedia.allow.label = Allow
 getUserMedia.allow.accesskey = A
 getUserMedia.dontAllow.label = Don’t Allow
 getUserMedia.dontAllow.accesskey = D
 getUserMedia.remember=Remember this decision
-# LOCALIZATION NOTE (getUserMedia.reasonForNoPermanentAllow.screen2,
+# LOCALIZATION NOTE (getUserMedia.reasonForNoPermanentAllow.screen3,
 #                    getUserMedia.reasonForNoPermanentAllow.audio,
 #                    getUserMedia.reasonForNoPermanentAllow.insecure):
 # %S is brandShortName
-getUserMedia.reasonForNoPermanentAllow.screen2=%S can not allow permanent access to your screen without asking which one to share.
+getUserMedia.reasonForNoPermanentAllow.screen3=%S can not allow permanent access to your screen.
 getUserMedia.reasonForNoPermanentAllow.audio=%S can not allow permanent access to your tab’s audio without asking which tab to share.
 getUserMedia.reasonForNoPermanentAllow.insecure=Your connection to this site is not secure. To protect you, %S will only allow access for this session.
 
 getUserMedia.sharingMenu.label = Tabs sharing devices
 getUserMedia.sharingMenu.accesskey = d
 # LOCALIZATION NOTE (getUserMedia.sharingMenuCamera
 #                    getUserMedia.sharingMenuMicrophone,
 #                    getUserMedia.sharingMenuAudioCapture,
--- a/browser/modules/URLBarZoom.jsm
+++ b/browser/modules/URLBarZoom.jsm
@@ -7,81 +7,85 @@
 
 this.EXPORTED_SYMBOLS = [ "URLBarZoom" ];
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 var URLBarZoom = {
   init(aWindow) {
     aWindow.addEventListener("EndSwapDocShells", onEndSwapDocShells, true);
+    aWindow.addEventListener("FullZoomChange", onFullZoomChange);
     aWindow.addEventListener("unload", () => {
       aWindow.removeEventListener("EndSwapDocShells", onEndSwapDocShells, true);
+      aWindow.removeEventListener("FullZoomChange", onFullZoomChange);
     }, {once: true});
   },
 }
 
-function fullZoomObserver(aSubject, aTopic) {
+function fullZoomLocationChangeObserver(aSubject, aTopic) {
   // If the tab was the last one in its window and has been dragged to another
   // window, the original browser's window will be unavailable here. Since that
   // window is closing, we can just ignore this notification.
   if (!aSubject.ownerGlobal) {
     return;
   }
 
-  let animate = (aTopic != "browser-fullZoom:location-change");
-  updateZoomButton(aSubject, animate);
+  updateZoomButton(aSubject, false);
 }
 
 function onEndSwapDocShells(event) {
   updateZoomButton(event.originalTarget);
 }
 
+function onFullZoomChange(event) {
+  let browser;
+  if (event.target.nodeType == event.target.DOCUMENT_NODE) {
+    // In non-e10s, the event is dispatched on the contentDocument
+    // so we need to jump through some hoops to get to the <xul:browser>.
+    let gBrowser = event.currentTarget.gBrowser;
+    let topDoc = event.target.defaultView.top.document;
+    browser = gBrowser.getBrowserForDocument(topDoc);
+  } else {
+    browser = event.originalTarget;
+  }
+  updateZoomButton(browser, true);
+}
+
   /**
    * Updates the zoom button in the location bar.
    *
    * @param {object} aBrowser The browser that the zoomed content resides in.
    * @param {boolean} aAnimate Should be True for all cases unless the zoom
    *   change is related to tab switching. Optional
-   * @param {number} aValue The value that should be used for the zoom control.
-   *   If not provided then the value will be read from the window. Useful
-   *   if the data on the window may be stale.
    */
-function updateZoomButton(aBrowser, aAnimate = false, aValue = undefined) {
+function updateZoomButton(aBrowser, aAnimate = false) {
   let win = aBrowser.ownerGlobal;
   if (aBrowser != win.gBrowser.selectedBrowser) {
     return;
   }
 
   let customizableZoomControls = win.document.getElementById("zoom-controls");
   let zoomResetButton = win.document.getElementById("urlbar-zoom-button");
 
   // Ensure that zoom controls haven't already been added to browser in Customize Mode
   if (customizableZoomControls &&
       customizableZoomControls.getAttribute("cui-areatype") == "toolbar") {
     zoomResetButton.hidden = true;
     return;
   }
 
-  let zoomFactor = Math.round((aValue || win.ZoomManager.zoom) * 100);
+  let zoomFactor = Math.round(win.ZoomManager.zoom * 100);
   if (zoomFactor != 100) {
-    // Check if zoom button is visible and update label if it is
-    if (zoomResetButton.hidden) {
-      zoomResetButton.hidden = false;
-    }
+    zoomResetButton.hidden = false;
     if (aAnimate) {
       zoomResetButton.setAttribute("animate", "true");
     } else {
       zoomResetButton.removeAttribute("animate");
     }
     zoomResetButton.setAttribute("label",
         win.gNavigatorBundle.getFormattedString("urlbar-zoom-button.label", [zoomFactor]));
   } else {
     // Hide button if zoom is at 100%
     zoomResetButton.hidden = true;
   }
 }
 
-Services.obs.addObserver(fullZoomObserver, "browser-fullZoom:zoomChange", false);
-Services.obs.addObserver(fullZoomObserver, "browser-fullZoom:zoomReset", false);
-Services.obs.addObserver(fullZoomObserver, "browser-fullZoom:location-change", false);
-Services.mm.addMessageListener("SyntheticDocument:ZoomChange", function(aMessage) {
-  updateZoomButton(aMessage.target, true, aMessage.data.value);
-});
+Services.obs.addObserver(fullZoomLocationChangeObserver, "browser-fullZoom:location-change", false);
--- a/browser/modules/test/browser/browser_urlBar_zoom.js
+++ b/browser/modules/test/browser/browser_urlBar_zoom.js
@@ -19,55 +19,38 @@ add_task(function* () {
   yield labelUpdatePromise;
   info("Zoom increased to " + Math.floor(ZoomManager.zoom * 100) + "%");
   is(zoomResetButton.hidden, false, "Zoom reset button is now visible");
   let pageZoomLevel = Math.floor(ZoomManager.zoom * 100);
   let expectedZoomLevel = 110;
   let buttonZoomLevel = parseInt(zoomResetButton.getAttribute("label"), 10);
   is(buttonZoomLevel, expectedZoomLevel, ("Button label updated successfully to " + Math.floor(ZoomManager.zoom * 100) + "%"));
 
-  let zoomResetPromise = promiseObserverNotification("browser-fullZoom:zoomReset");
+  let zoomResetPromise = BrowserTestUtils.waitForEvent(window, "FullZoomChange");
   zoomResetButton.click();
   yield zoomResetPromise;
   pageZoomLevel = Math.floor(ZoomManager.zoom * 100);
   expectedZoomLevel = 100;
   is(pageZoomLevel, expectedZoomLevel, "Clicking zoom button successfully resets browser zoom to 100%");
   is(zoomResetButton.hidden, true, "Zoom reset button returns to being hidden");
-
 });
 
 add_task(function* () {
   info("Confirm that URL bar zoom button doesn't appear when customizable zoom widget is added to toolbar");
   CustomizableUI.addWidgetToArea("zoom-controls", CustomizableUI.AREA_NAVBAR);
   let zoomCustomizableWidget = document.getElementById("zoom-reset-button");
   let zoomResetButton = document.getElementById("urlbar-zoom-button");
-  let zoomChangePromise = promiseObserverNotification("browser-fullZoom:zoomChange");
+  let zoomChangePromise = BrowserTestUtils.waitForEvent(window, "FullZoomChange");
   FullZoom.enlarge();
   yield zoomChangePromise;
   is(zoomResetButton.hidden, true, "URL zoom button remains hidden despite zoom increase");
   is(parseInt(zoomCustomizableWidget.label, 10), 110, "Customizable zoom widget's label has updated to " + zoomCustomizableWidget.label);
 });
 
 add_task(function* asyncCleanup() {
   // reset zoom level and customizable widget
   ZoomManager.zoom = initialPageZoom;
   is(ZoomManager.zoom, 1, "Zoom level was restored");
   if (document.getElementById("zoom-controls")) {
     CustomizableUI.removeWidgetFromArea("zoom-controls", CustomizableUI.AREA_NAVBAR);
     ok(!document.getElementById("zoom-controls"), "Customizable zoom widget removed from toolbar");
   }
-
 });
-
-function promiseObserverNotification(aObserver) {
-  let deferred = Promise.defer();
-  function notificationCallback(e) {
-    Services.obs.removeObserver(notificationCallback, aObserver);
-    clearTimeout(timeoutId);
-    deferred.resolve();
-  }
-  let timeoutId = setTimeout(() => {
-    Services.obs.removeObserver(notificationCallback, aObserver);
-    deferred.reject("Notification '" + aObserver + "' did not happen within 20 seconds.");
-  }, kTimeoutInMS);
-  Services.obs.addObserver(notificationCallback, aObserver, false);
-  return deferred.promise;
-}
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -149,16 +149,23 @@ this.webrtcUI = {
     webrtcUI.activePerms.delete(aBrowser.outerWindowID);
   },
 
   forgetStreamsFromBrowser(aBrowser) {
     this._streams = this._streams.filter(stream => stream.browser != aBrowser);
     webrtcUI.forgetActivePermissionsFromBrowser(aBrowser);
   },
 
+  forgetStreamsFromProcess(aProcessMM) {
+    // stream.processMM is null when e10s is disabled.
+    this._streams =
+      this._streams.filter(stream => stream.processMM &&
+                                     stream.processMM != aProcessMM);
+  },
+
   showSharingDoorhanger(aActiveStream) {
     let browserWindow = aActiveStream.browser.ownerGlobal;
     if (aActiveStream.tab) {
       browserWindow.gBrowser.selectedTab = aActiveStream.tab;
     } else {
       aActiveStream.browser.focus();
     }
     browserWindow.focus();
@@ -274,40 +281,50 @@ this.webrtcUI = {
         break;
       case "webrtc:StopRecording":
         stopRecording(aMessage.target, aMessage.data);
         break;
       case "webrtc:CancelRequest":
         removePrompt(aMessage.target, aMessage.data);
         break;
       case "webrtc:UpdatingIndicators":
-        webrtcUI._streams = [];
+        webrtcUI.forgetStreamsFromProcess(aMessage.target);
         break;
       case "webrtc:UpdateGlobalIndicators":
         updateIndicators(aMessage.data, aMessage.target);
         break;
       case "webrtc:UpdateBrowserIndicators":
         let id = aMessage.data.windowId;
+        aMessage.targetFrameLoader.QueryInterface(Ci.nsIFrameLoader);
+        let processMM =
+          aMessage.targetFrameLoader.messageManager.processMessageManager;
         let index;
         for (index = 0; index < webrtcUI._streams.length; ++index) {
-          if (webrtcUI._streams[index].state.windowId == id)
+          let stream = webrtcUI._streams[index];
+          if (stream.state.windowId == id && stream.processMM == processMM)
             break;
         }
         // If there's no documentURI, the update is actually a removal of the
         // stream, triggered by the recording-window-ended notification.
-        if (!aMessage.data.documentURI && index < webrtcUI._streams.length)
+        if (!aMessage.data.documentURI && index < webrtcUI._streams.length) {
           webrtcUI._streams.splice(index, 1);
-        else
-          webrtcUI._streams[index] = {browser: aMessage.target, state: aMessage.data};
+        } else {
+          webrtcUI._streams[index] = {
+            browser: aMessage.target,
+            processMM,
+            state: aMessage.data
+          };
+        }
         let tabbrowser = aMessage.target.ownerGlobal.gBrowser;
         if (tabbrowser)
           tabbrowser.setBrowserSharing(aMessage.target, aMessage.data);
         break;
       case "child-process-shutdown":
         webrtcUI.processIndicators.delete(aMessage.target);
+        webrtcUI.forgetStreamsFromProcess(aMessage.target);
         updateIndicators(null, null);
         break;
     }
   }
 };
 
 function getBrowserForWindow(aContentWindow) {
   return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -434,17 +451,17 @@ function prompt(aBrowser, aRequest) {
 
   let productName = gBrandBundle.GetStringFromName("brandShortName");
 
   // Disable the permanent 'Allow' action if the connection isn't secure, or for
   // screen/audio sharing (because we can't guess which window the user wants to
   // share without prompting).
   let reasonForNoPermanentAllow = "";
   if (sharingScreen) {
-    reasonForNoPermanentAllow = "getUserMedia.reasonForNoPermanentAllow.screen2";
+    reasonForNoPermanentAllow = "getUserMedia.reasonForNoPermanentAllow.screen3";
   } else if (sharingAudio) {
     reasonForNoPermanentAllow = "getUserMedia.reasonForNoPermanentAllow.audio";
   } else if (!aRequest.secure) {
     reasonForNoPermanentAllow = "getUserMedia.reasonForNoPermanentAllow.insecure";
   }
 
   let options = {
     persistent: true,
@@ -722,18 +739,18 @@ function prompt(aBrowser, aRequest) {
 
       this.mainAction.callback = function(aState) {
         let remember = aState && aState.checkboxChecked;
         let allowedDevices = [];
         let perms = Services.perms;
         if (videoDevices.length) {
           let listId = "webRTC-select" + (sharingScreen ? "Window" : "Camera") + "-menulist";
           let videoDeviceIndex = doc.getElementById(listId).value;
-          let allowCamera = videoDeviceIndex != "-1";
-          if (allowCamera) {
+          let allowVideoDevice = videoDeviceIndex != "-1";
+          if (allowVideoDevice) {
             allowedDevices.push(videoDeviceIndex);
             // Session permission will be removed after use
             // (it's really one-shot, not for the entire session)
             perms.add(uri, "MediaManagerVideo", perms.ALLOW_ACTION,
                       perms.EXPIRE_SESSION);
             if (!webrtcUI.activePerms.has(aBrowser.outerWindowID)) {
               webrtcUI.activePerms.set(aBrowser.outerWindowID, new Set());
             }
@@ -742,19 +759,16 @@ function prompt(aBrowser, aRequest) {
               if (device.deviceIndex == videoDeviceIndex) {
                 webrtcUI.activePerms.get(aBrowser.outerWindowID)
                         .add(aRequest.windowID + device.mediaSource + device.id);
                 break;
               }
             }
             if (remember)
               SitePermissions.set(uri, "camera", SitePermissions.ALLOW);
-          } else {
-            let scope = remember ? SitePermissions.SCOPE_PERSISTENT : SitePermissions.SCOPE_TEMPORARY;
-            SitePermissions.set(uri, "camera", SitePermissions.BLOCK, scope, aBrowser);
           }
         }
         if (audioDevices.length) {
           if (!sharingAudio) {
             let audioDeviceIndex = doc.getElementById("webRTC-selectMicrophone-menulist").value;
             let allowMic = audioDeviceIndex != "-1";
             if (allowMic) {
               allowedDevices.push(audioDeviceIndex);
@@ -766,19 +780,16 @@ function prompt(aBrowser, aRequest) {
                 if (device.deviceIndex == audioDeviceIndex) {
                   webrtcUI.activePerms.get(aBrowser.outerWindowID)
                           .add(aRequest.windowID + device.mediaSource + device.id);
                   break;
                 }
               }
               if (remember)
                 SitePermissions.set(uri, "microphone", SitePermissions.ALLOW);
-            } else {
-                let scope = remember ? SitePermissions.SCOPE_PERSISTENT : SitePermissions.SCOPE_TEMPORARY;
-                SitePermissions.set(uri, "microphone", SitePermissions.BLOCK, scope, aBrowser);
             }
           } else {
             // Only one device possible for audio capture.
             allowedDevices.push(0);
           }
         }
 
         if (!allowedDevices.length) {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -2904,18 +2904,17 @@ toolbarbutton.chevron > .toolbarbutton-m
   height: 2px;
   margin-inline-end: -4em;
   background-color: Highlight;
 }
 
 %include ../shared/notification-icons.inc.css
 
 .notification-anchor-icon:-moz-focusring {
-  box-shadow: 0 0 2px 1px -moz-mac-focusring inset,
-              0 0 3px 2px -moz-mac-focusring;
+  box-shadow: var(--focus-ring-box-shadow);
 }
 
 /* Translation */
 
 %include ../shared/translation/infobar.inc.css
 
 notification[value="translation"] {
   color: #484848;
--- a/browser/themes/osx/controlcenter/panel.css
+++ b/browser/themes/osx/controlcenter/panel.css
@@ -46,10 +46,8 @@
 }
 
 #tracking-action-block:-moz-focusring,
 #tracking-action-unblock:-moz-focusring,
 #tracking-action-unblock-private:-moz-focusring,
 #identity-popup-securityView-body > button:-moz-focusring {
   box-shadow: var(--focus-ring-box-shadow);
 }
-
-
--- a/browser/themes/shared/aboutNetError.css
+++ b/browser/themes/shared/aboutNetError.css
@@ -158,8 +158,13 @@ span#hostname {
   padding: 1em 17.5%;
 }
 
 #certificateErrorText {
   font-family: monospace;
   white-space: pre-wrap;
   padding: 1em 0;
 }
+
+#cert_domain_link:not([href]) {
+  color: var(--in-content-page-color);
+  text-decoration: none;
+}
\ No newline at end of file
--- a/build/gen_test_packages_manifest.py
+++ b/build/gen_test_packages_manifest.py
@@ -13,26 +13,28 @@ ALL_HARNESSES = [
     'mochitest',
     'reftest',
     'xpcshell',
     'cppunittest',
     'jittest',
     'mozbase',
     'web-platform',
     'talos',
+    'awsy',
     'gtest',
 ]
 
 PACKAGE_SPECIFIED_HARNESSES = [
     'cppunittest',
     'mochitest',
     'reftest',
     'xpcshell',
     'web-platform',
     'talos',
+    'awsy',
 ]
 
 # These packages are not present for every build configuration.
 OPTIONAL_PACKAGES = [
     'gtest',
 ]
 
 
--- a/build/valgrind/cross-architecture.sup
+++ b/build/valgrind/cross-architecture.sup
@@ -10,33 +10,33 @@
    ...
    fun:_ZL9SaveToEnvPKc
    ...
 }
 {
    PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 793549.)
    Memcheck:Leak
    ...
-   fun:_ZL13SaveWordToEnvPKcRK19nsACString_internal
+   fun:_ZL13SaveWordToEnvPKcRK10nsACString
    ...
 }
 {
    PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 944133.)
    Memcheck:Leak
    ...
    fun:_ZN13CrashReporter14SetRestartArgsEiPPc
    ...
 }
 {
    PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 793548.)
    Memcheck:Leak
    fun:malloc
    ...
-   fun:_Z12ToNewCStringRK19nsACString_internal
-   fun:_ZN13CrashReporter14SetupExtraDataEP7nsIFileRK19nsACString_internal
+   fun:_Z12ToNewCStringRK10nsACString
+   fun:_ZN13CrashReporter14SetupExtraDataEP7nsIFileRK10nsACString
    ...
 }
 {
    We purposely leak the StatisticsReporter object
    Memcheck:Leak
    fun:malloc
    fun:moz_xmalloc
    fun:operator new
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -94,56 +94,56 @@ OriginAttributes::SetFirstPartyDomain(co
   }
 
   mFirstPartyDomain = NS_ConvertUTF8toUTF16(aDomain);
 }
 
 void
 OriginAttributes::CreateSuffix(nsACString& aStr) const
 {
-  UniquePtr<URLParams> params(new URLParams());
+  URLParams params;
   nsAutoString value;
 
   //
   // Important: While serializing any string-valued attributes, perform a
   // release-mode assertion to make sure that they don't contain characters that
   // will break the quota manager when it uses the serialization for file
   // naming.
   //
 
   if (mAppId != nsIScriptSecurityManager::NO_APP_ID) {
     value.AppendInt(mAppId);
-    params->Set(NS_LITERAL_STRING("appId"), value);
+    params.Set(NS_LITERAL_STRING("appId"), value);
   }
 
   if (mInIsolatedMozBrowser) {
-    params->Set(NS_LITERAL_STRING("inBrowser"), NS_LITERAL_STRING("1"));
+    params.Set(NS_LITERAL_STRING("inBrowser"), NS_LITERAL_STRING("1"));
   }
 
   if (mUserContextId != nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID) {
     value.Truncate();
     value.AppendInt(mUserContextId);
-    params->Set(NS_LITERAL_STRING("userContextId"), value);
+    params.Set(NS_LITERAL_STRING("userContextId"), value);
   }
 
 
   if (mPrivateBrowsingId) {
     value.Truncate();
     value.AppendInt(mPrivateBrowsingId);
-    params->Set(NS_LITERAL_STRING("privateBrowsingId"), value);
+    params.Set(NS_LITERAL_STRING("privateBrowsingId"), value);
   }
 
   if (!mFirstPartyDomain.IsEmpty()) {
     MOZ_RELEASE_ASSERT(mFirstPartyDomain.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) == kNotFound);
-    params->Set(NS_LITERAL_STRING("firstPartyDomain"), mFirstPartyDomain);
+    params.Set(NS_LITERAL_STRING("firstPartyDomain"), mFirstPartyDomain);
   }
 
   aStr.Truncate();
 
-  params->Serialize(value);
+  params.Serialize(value);
   if (!value.IsEmpty()) {
     aStr.AppendLiteral("^");
     aStr.Append(NS_ConvertUTF16toUTF8(value));
   }
 
 // In debug builds, check the whole string for illegal characters too (just in case).
 #ifdef DEBUG
   nsAutoCString str;
@@ -250,21 +250,21 @@ OriginAttributes::PopulateFromSuffix(con
   if (aStr.IsEmpty()) {
     return true;
   }
 
   if (aStr[0] != '^') {
     return false;
   }
 
-  UniquePtr<URLParams> params(new URLParams());
-  params->ParseInput(Substring(aStr, 1, aStr.Length() - 1));
+  URLParams params;
+  params.ParseInput(Substring(aStr, 1, aStr.Length() - 1));
 
   PopulateFromSuffixIterator iterator(this);
-  return params->ForEach(iterator);
+  return params.ForEach(iterator);
 }
 
 bool
 OriginAttributes::PopulateFromOrigin(const nsACString& aOrigin,
                                      nsACString& aOriginNoSuffix)
 {
   // RFindChar is only available on nsCString.
   nsCString origin(aOrigin);
--- a/devtools/client/aboutdebugging/aboutdebugging.css
+++ b/devtools/client/aboutdebugging/aboutdebugging.css
@@ -146,40 +146,58 @@ button {
   text-overflow: ellipsis;
 }
 
 .addons-controls {
   display: flex;
   flex-direction: row;
 }
 
-.addons-install-error {
-  background-color: #f3b0b0;
+.addons-install-error,
+.service-worker-multi-process {
   padding: 5px 10px;
   margin-top: 5px;
   margin-inline-end: 4px;
 }
 
-.service-worker-disabled .warning,
-.addons-install-error .warning {
+.addons-install-error {
+  background-color: #f3b0b0;
+}
+
+.service-worker-multi-process {
+  background-color: #ffeebb;
+  line-height: 1.5em;
+}
+
+.service-worker-multi-process .update-button {
+  margin: 5px 0;
+}
+
+.warning {
   background-image: url(chrome://devtools/skin/images/alerticon-warning.png);
   background-size: 13px 12px;
-  margin-inline-end: 10px;
   display: inline-block;
   width: 13px;
   height: 12px;
+  margin-inline-end: 10px;
 }
 
 @media (min-resolution: 1.1dppx) {
-  .service-worker-disabled .warning,
-  .addons-install-error .warning {
+  .warning {
     background-image: url(chrome://devtools/skin/images/alerticon-warning@2x.png);
   }
 }
 
+.addons-install-error .warning,
+.service-worker-multi-process .warning {
+  /* The warning icon can be hard to see on red / yellow backgrounds, this turns the icon
+  to a black icon. */
+  filter: brightness(0%);
+}
+
 .addons-options {
   flex: 1;
 }
 
 .addons-debugging-label {
   display: inline-block;
   margin-inline-end: 1ch;
 }
--- a/devtools/client/aboutdebugging/components/workers/moz.build
+++ b/devtools/client/aboutdebugging/components/workers/moz.build
@@ -1,9 +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/.
 
 DevToolsModules(
+    'multi-e10s-warning.js',
     'panel.js',
     'service-worker-target.js',
     'target.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/components/workers/multi-e10s-warning.js
@@ -0,0 +1,60 @@
+/* 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/. */
+
+/* eslint-env browser */
+
+"use strict";
+
+loader.lazyImporter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
+const { createClass, DOM: dom } =
+  require("devtools/client/shared/vendor/react");
+const Services = require("Services");
+const { Ci } = require("chrome");
+
+loader.lazyImporter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+loader.lazyRequireGetter(this, "DebuggerClient",
+  "devtools/shared/client/main", true);
+
+const Strings = Services.strings.createBundle("chrome://devtools/locale/aboutdebugging.properties");
+const PROCESS_COUNT_PREF = "dom.ipc.processCount";
+
+module.exports = createClass({
+  displayName: "multiE10SWarning",
+
+  onUpdatePreferenceClick() {
+    let message = Strings.GetStringFromName("multiProcessWarningConfirmUpdate");
+    if (window.confirm(message)) {
+      Services.prefs.setIntPref(PROCESS_COUNT_PREF, 1);
+      // Restart the browser.
+      Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
+    }
+  },
+
+  render() {
+    return dom.div(
+      {
+        className: "service-worker-multi-process"
+      },
+      dom.div(
+        {},
+        dom.div({ className: "warning" }),
+        dom.b({}, Strings.GetStringFromName("multiProcessWarningTitle"))
+      ),
+      dom.div(
+        {},
+        Strings.GetStringFromName("multiProcessWarningMessage")
+      ),
+      dom.button(
+        {
+          className: "update-button",
+          onClick: this.onUpdatePreferenceClick,
+        },
+        Strings.GetStringFromName("multiProcessWarningUpdateLink")
+      )
+    );
+  },
+});
--- a/devtools/client/aboutdebugging/components/workers/panel.js
+++ b/devtools/client/aboutdebugging/components/workers/panel.js
@@ -11,67 +11,80 @@ const { Ci } = require("chrome");
 const { createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 const { getWorkerForms } = require("../../modules/worker");
 const Services = require("Services");
 
 const PanelHeader = createFactory(require("../panel-header"));
 const TargetList = createFactory(require("../target-list"));
 const WorkerTarget = createFactory(require("./target"));
+const MultiE10SWarning = createFactory(require("./multi-e10s-warning"));
 const ServiceWorkerTarget = createFactory(require("./service-worker-target"));
 
 loader.lazyImporter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const WorkerIcon = "chrome://devtools/skin/images/debugging-workers.svg";
 const MORE_INFO_URL = "https://developer.mozilla.org/en-US/docs/Tools/about%3Adebugging";
+const PROCESS_COUNT_PREF = "dom.ipc.processCount";
 
 module.exports = createClass({
   displayName: "WorkersPanel",
 
   propTypes: {
     client: PropTypes.instanceOf(DebuggerClient).isRequired,
     id: PropTypes.string.isRequired
   },
 
   getInitialState() {
     return {
       workers: {
         service: [],
         shared: [],
         other: []
-      }
+      },
+      processCount: 1,
     };
   },
 
   componentDidMount() {
     let client = this.props.client;
-    client.addListener("workerListChanged", this.update);
-    client.addListener("serviceWorkerRegistrationListChanged", this.update);
-    client.addListener("processListChanged", this.update);
-    client.addListener("registration-changed", this.update);
+    client.addListener("workerListChanged", this.updateWorkers);
+    client.addListener("serviceWorkerRegistrationListChanged", this.updateWorkers);
+    client.addListener("processListChanged", this.updateWorkers);
+    client.addListener("registration-changed", this.updateWorkers);
 
-    this.update();
+    Services.prefs.addObserver(PROCESS_COUNT_PREF, this.updateMultiE10S, false);
+
+    this.updateMultiE10S();
+    this.updateWorkers();
   },
 
   componentWillUnmount() {
     let client = this.props.client;
-    client.removeListener("processListChanged", this.update);
-    client.removeListener("serviceWorkerRegistrationListChanged", this.update);
-    client.removeListener("workerListChanged", this.update);
-    client.removeListener("registration-changed", this.update);
+    client.removeListener("processListChanged", this.updateWorkers);
+    client.removeListener("serviceWorkerRegistrationListChanged", this.updateWorkers);
+    client.removeListener("workerListChanged", this.updateWorkers);
+    client.removeListener("registration-changed", this.updateWorkers);
+
+    Services.prefs.removeObserver(PROCESS_COUNT_PREF, this.updateMultiE10S);
   },
 
-  update() {
+  updateMultiE10S() {
+    let processCount = Services.prefs.getIntPref(PROCESS_COUNT_PREF);
+    this.setState({ processCount });
+  },
+
+  updateWorkers() {
     let workers = this.getInitialState().workers;
 
     getWorkerForms(this.props.client).then(forms => {
       forms.registrations.forEach(form => {
         workers.service.push({
           icon: WorkerIcon,
           name: form.url,
           url: form.url,
@@ -131,66 +144,94 @@ module.exports = createClass({
     for (let registration of registrations) {
       if (registration.scope === form.scope) {
         return registration;
       }
     }
     return null;
   },
 
-  render() {
-    let { client, id } = this.props;
-    let { workers } = this.state;
+  isE10S() {
+    return Services.appinfo.browserTabsRemoteAutostart;
+  },
 
+  renderServiceWorkersError() {
     let isWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(window);
     let isPrivateBrowsingMode = PrivateBrowsingUtils.permanentPrivateBrowsing;
     let isServiceWorkerDisabled = !Services.prefs
                                     .getBoolPref("dom.serviceWorkers.enabled");
-    let errorMsg = isWindowPrivate || isPrivateBrowsingMode ||
-           isServiceWorkerDisabled ?
-      dom.p({ className: "service-worker-disabled" },
-        dom.div({ className: "warning" }),
-        Strings.GetStringFromName("configurationIsNotCompatible"),
-        " (",
-        dom.a({ href: MORE_INFO_URL, target: "_blank" },
-          Strings.GetStringFromName("moreInfo")),
-        ")"
-      ) : "";
+
+    let isDisabled = isWindowPrivate || isPrivateBrowsingMode || isServiceWorkerDisabled;
+    if (!isDisabled) {
+      return "";
+    }
+    return dom.p(
+      {
+        className: "service-worker-disabled"
+      },
+      dom.div({ className: "warning" }),
+      Strings.GetStringFromName("configurationIsNotCompatible"),
+      " (",
+      dom.a(
+        {
+          href: MORE_INFO_URL,
+          target: "_blank"
+        },
+        Strings.GetStringFromName("moreInfo")
+      ),
+      ")"
+    );
+  },
+
+  render() {
+    let { client, id } = this.props;
+    let { workers, processCount } = this.state;
+
+    let isE10S = Services.appinfo.browserTabsRemoteAutostart;
+    let isMultiE10S = isE10S && processCount > 1;
 
-    return dom.div({
-      id: id + "-panel",
-      className: "panel",
-      role: "tabpanel",
-      "aria-labelledby": id + "-header"
-    },
-    PanelHeader({
-      id: id + "-header",
-      name: Strings.GetStringFromName("workers")
-    }),
-    dom.div({ id: "workers", className: "inverted-icons" },
-      TargetList({
-        client,
-        error: errorMsg,
-        id: "service-workers",
-        name: Strings.GetStringFromName("serviceWorkers"),
-        sort: true,
-        targetClass: ServiceWorkerTarget,
-        targets: workers.service
+    return dom.div(
+      {
+        id: id + "-panel",
+        className: "panel",
+        role: "tabpanel",
+        "aria-labelledby": id + "-header"
+      },
+      PanelHeader({
+        id: id + "-header",
+        name: Strings.GetStringFromName("workers")
       }),
-      TargetList({
-        client,
-        id: "shared-workers",
-        name: Strings.GetStringFromName("sharedWorkers"),
-        sort: true,
-        targetClass: WorkerTarget,
-        targets: workers.shared
-      }),
-      TargetList({
-        client,
-        id: "other-workers",
-        name: Strings.GetStringFromName("otherWorkers"),
-        sort: true,
-        targetClass: WorkerTarget,
-        targets: workers.other
-      })
-    ));
+      isMultiE10S ? MultiE10SWarning() : "",
+      dom.div(
+        {
+          id: "workers",
+          className: "inverted-icons"
+        },
+        TargetList({
+          client,
+          debugDisabled: isMultiE10S,
+          error: this.renderServiceWorkersError(),
+          id: "service-workers",
+          name: Strings.GetStringFromName("serviceWorkers"),
+          sort: true,
+          targetClass: ServiceWorkerTarget,
+          targets: workers.service
+        }),
+        TargetList({
+          client,
+          id: "shared-workers",
+          name: Strings.GetStringFromName("sharedWorkers"),
+          sort: true,
+          targetClass: WorkerTarget,
+          targets: workers.shared
+        }),
+        TargetList({
+          client,
+          id: "other-workers",
+          name: Strings.GetStringFromName("otherWorkers"),
+          sort: true,
+          targetClass: WorkerTarget,
+          targets: workers.other
+        })
+      )
+    );
   }
 });
--- a/devtools/client/aboutdebugging/components/workers/service-worker-target.js
+++ b/devtools/client/aboutdebugging/components/workers/service-worker-target.js
@@ -150,28 +150,30 @@ module.exports = createClass({
     // ACTIVE state. Unable to know the actual state ("installing", "waiting"), we
     // display a custom state "registering" for now. See Bug 1153292.
     return "registering";
   },
 
   renderButtons() {
     let pushButton = dom.button({
       className: "push-button",
-      onClick: this.push
+      onClick: this.push,
+      disabled: this.props.debugDisabled
     }, Strings.GetStringFromName("push"));
 
     let debugButton = dom.button({
       className: "debug-button",
       onClick: this.debug,
       disabled: this.props.debugDisabled
     }, Strings.GetStringFromName("debug"));
 
     let startButton = dom.button({
       className: "start-button",
       onClick: this.start,
+      disabled: this.props.debugDisabled
     }, Strings.GetStringFromName("start"));
 
     if (this.isRunning()) {
       if (this.isActive()) {
         return [pushButton, debugButton];
       }
       // Only debug button is available if the service worker is not active.
       return debugButton;
@@ -182,17 +184,17 @@ module.exports = createClass({
   renderUnregisterLink() {
     if (!this.isActive()) {
       // If not active, there might be no registrationActor available.
       return null;
     }
 
     return dom.a({
       onClick: this.unregister,
-      className: "unregister-link"
+      className: "unregister-link",
     }, Strings.GetStringFromName("unregister"));
   },
 
   render() {
     let { target } = this.props;
     let { pushSubscription } = this.state;
     let status = this.getServiceWorkerStatus();
 
--- a/devtools/client/aboutdebugging/test/browser.ini
+++ b/devtools/client/aboutdebugging/test/browser.ini
@@ -30,16 +30,18 @@ tags = webextensions
 tags = webextensions
 [browser_addons_debugging_initial_state.js]
 [browser_addons_install.js]
 [browser_addons_reload.js]
 [browser_addons_toggle_debug.js]
 [browser_page_not_found.js]
 [browser_service_workers.js]
 [browser_service_workers_fetch_flag.js]
+[browser_service_workers_multi_content_process.js]
+skip-if = !e10s # This test is only valid in e10s
 [browser_service_workers_not_compatible.js]
 [browser_service_workers_push.js]
 [browser_service_workers_push_service.js]
 [browser_service_workers_start.js]
 [browser_service_workers_status.js]
 [browser_service_workers_timeout.js]
 skip-if = true # Bug 1232931
 [browser_service_workers_unregister.js]
--- a/devtools/client/aboutdebugging/test/browser_service_workers.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers.js
@@ -4,23 +4,17 @@
 "use strict";
 
 // Service workers can't be loaded from chrome://,
 // but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
 const SERVICE_WORKER = URL_ROOT + "service-workers/empty-sw.js";
 const TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
 
 add_task(function* () {
-  yield new Promise(done => {
-    let options = {"set": [
-      ["dom.serviceWorkers.enabled", true],
-      ["dom.serviceWorkers.testing.enabled", true],
-    ]};
-    SpecialPowers.pushPrefEnv(options, done);
-  });
+  yield enableServiceWorkerDebugging();
 
   let { tab, document } = yield openAboutDebugging("workers");
 
   let swTab = yield addTab(TAB_URL);
 
   let serviceWorkersElement = getServiceWorkerList(document);
 
   yield waitForMutation(serviceWorkersElement, { childList: true });
--- a/devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_fetch_flag.js
@@ -4,24 +4,17 @@
 "use strict";
 
 // Service workers can't be loaded from chrome://,
 // but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
 const EMPTY_SW_TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
 const FETCH_SW_TAB_URL = URL_ROOT + "service-workers/fetch-sw.html";
 
 function* testBody(url, expecting) {
-  yield new Promise(done => {
-    let options = {"set": [
-      ["dom.serviceWorkers.enabled", true],
-      ["dom.serviceWorkers.testing.enabled", true],
-    ]};
-    SpecialPowers.pushPrefEnv(options, done);
-  });
-
+  yield enableServiceWorkerDebugging();
   let { tab, document } = yield openAboutDebugging("workers");
 
   let swTab = yield addTab(url);
 
   let serviceWorkersElement = getServiceWorkerList(document);
   yield waitForMutation(serviceWorkersElement, { childList: true });
 
   let fetchFlags =
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_multi_content_process.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Service worker debugging is unavailable when multi-e10s is enabled.
+// Check that the appropriate warning panel is displayed when there are more than 1
+// content process available.
+
+const SERVICE_WORKER = URL_ROOT + "service-workers/empty-sw.js";
+const TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
+
+add_task(function* () {
+  yield enableServiceWorkerDebugging();
+  info("Force two content processes");
+  yield pushPref("dom.ipc.processCount", 2);
+
+  let { tab, document } = yield openAboutDebugging("workers");
+
+  let warningSection = document.querySelector(".service-worker-multi-process");
+  let img = warningSection.querySelector(".warning");
+  ok(img, "warning message is rendered");
+
+  let swTab = yield addTab(TAB_URL, { background: true });
+  let serviceWorkersElement = getServiceWorkerList(document);
+
+  yield waitForMutation(serviceWorkersElement, { childList: true });
+
+  info("Check that service worker buttons are disabled.");
+  // Check that the service worker appears in the UI
+  let serviceWorkerContainer = getServiceWorkerContainer(SERVICE_WORKER, document);
+  let debugButton = serviceWorkerContainer.querySelector(".debug-button");
+  ok(debugButton.disabled, "Start/Debug button is disabled");
+
+  info("Update the preference to 1");
+  let onWarningCleared = waitUntil(() => {
+    return document.querySelector(".service-worker-multi-process");
+  });
+  yield pushPref("dom.ipc.processCount", 1);
+  yield onWarningCleared;
+  ok(!debugButton.disabled, "Debug button is enabled.");
+
+  info("Update the preference back to 2");
+  let onWarningRestored = waitUntil(() => {
+    return document.querySelector(".service-worker-multi-process");
+  });
+  yield pushPref("dom.ipc.processCount", 2);
+  yield onWarningRestored;
+  ok(debugButton.disabled, "Debug button is disabled again.");
+
+  info("Unregister service worker");
+  try {
+    yield unregisterServiceWorker(swTab, serviceWorkersElement);
+    ok(true, "Service worker registration unregistered");
+  } catch (e) {
+    ok(false, "SW not unregistered; " + e);
+  }
+
+  yield removeTab(swTab);
+  yield closeAboutDebugging(tab);
+});
--- a/devtools/client/aboutdebugging/test/browser_service_workers_push.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_push.js
@@ -10,25 +10,17 @@
 // It should trigger a "push" notification in the worker.
 
 // Service workers can't be loaded from chrome://, but http:// is ok with
 // dom.serviceWorkers.testing.enabled turned on.
 const SERVICE_WORKER = URL_ROOT + "service-workers/push-sw.js";
 const TAB_URL = URL_ROOT + "service-workers/push-sw.html";
 
 add_task(function* () {
-  info("Turn on workers via mochitest http.");
-  yield new Promise(done => {
-    let options = { "set": [
-      // Accept workers from mochitest's http.
-      ["dom.serviceWorkers.testing.enabled", true],
-    ]};
-    SpecialPowers.pushPrefEnv(options, done);
-  });
-
+  yield enableServiceWorkerDebugging();
   let { tab, document } = yield openAboutDebugging("workers");
 
   // Listen for mutations in the service-workers list.
   let serviceWorkersElement = getServiceWorkerList(document);
   let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
 
   // Open a tab that registers a push service worker.
   let swTab = yield addTab(TAB_URL);
--- a/devtools/client/aboutdebugging/test/browser_service_workers_push_service.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_push_service.js
@@ -13,24 +13,19 @@ const TAB_URL = URL_ROOT + "service-work
 
 const FAKE_ENDPOINT = "https://fake/endpoint";
 
 const PushService = Cc["@mozilla.org/push/Service;1"]
   .getService(Ci.nsIPushService).wrappedJSObject;
 
 add_task(function* () {
   info("Turn on workers via mochitest http.");
-  yield SpecialPowers.pushPrefEnv({
-    "set": [
-      // Accept workers from mochitest's http.
-      ["dom.serviceWorkers.testing.enabled", true],
-      // Enable the push service.
-      ["dom.push.connection.enabled", true],
-    ]
-  });
+  yield enableServiceWorkerDebugging();
+  // Enable the push service.
+  yield pushPref("dom.push.connection.enabled", true);
 
   info("Mock the push service");
   PushService.service = {
     _registrations: new Map(),
     _notify(scope) {
       Services.obs.notifyObservers(
         null,
         PushService.subscriptionModifiedTopic,
--- a/devtools/client/aboutdebugging/test/browser_service_workers_start.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_start.js
@@ -10,27 +10,19 @@
 // Service workers can't be loaded from chrome://, but http:// is ok with
 // dom.serviceWorkers.testing.enabled turned on.
 const SERVICE_WORKER = URL_ROOT + "service-workers/empty-sw.js";
 const TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
 
 const SW_TIMEOUT = 1000;
 
 add_task(function* () {
-  info("Turn on workers via mochitest http.");
-  yield new Promise(done => {
-    let options = { "set": [
-      // Accept workers from mochitest's http.
-      ["dom.serviceWorkers.testing.enabled", true],
-      // Reduce the timeout to accelerate service worker freezing
-      ["dom.serviceWorkers.idle_timeout", SW_TIMEOUT],
-      ["dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT],
-    ]};
-    SpecialPowers.pushPrefEnv(options, done);
-  });
+  yield enableServiceWorkerDebugging();
+  yield pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
+  yield pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
 
   let { tab, document } = yield openAboutDebugging("workers");
 
   // Listen for mutations in the service-workers list.
   let serviceWorkersElement = getServiceWorkerList(document);
   let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
 
   // Open a tab that registers an empty service worker.
--- a/devtools/client/aboutdebugging/test/browser_service_workers_status.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_status.js
@@ -7,25 +7,19 @@
 // but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
 const SERVICE_WORKER = URL_ROOT + "service-workers/delay-sw.js";
 const TAB_URL = URL_ROOT + "service-workers/delay-sw.html";
 const SW_TIMEOUT = 2000;
 
 requestLongerTimeout(2);
 
 add_task(function* () {
-  yield SpecialPowers.pushPrefEnv({
-    "set": [
-      // Accept workers from mochitest's http.
-      ["dom.serviceWorkers.testing.enabled", true],
-      ["dom.serviceWorkers.idle_timeout", SW_TIMEOUT],
-      ["dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT],
-      ["dom.ipc.processCount", 1],
-    ]
-  });
+  yield enableServiceWorkerDebugging();
+  yield pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
+  yield pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
 
   let { tab, document } = yield openAboutDebugging("workers");
 
   // Listen for mutations in the service-workers list.
   let serviceWorkersElement = getServiceWorkerList(document);
   let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
 
   let swTab = yield addTab(TAB_URL);
--- a/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js
@@ -6,27 +6,19 @@
 // Service workers can't be loaded from chrome://,
 // but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
 const SERVICE_WORKER = URL_ROOT + "service-workers/empty-sw.js";
 const TAB_URL = URL_ROOT + "service-workers/empty-sw.html";
 
 const SW_TIMEOUT = 1000;
 
 add_task(function* () {
-  yield new Promise(done => {
-    let options = {"set": [
-      // Accept workers from mochitest's http
-      ["dom.serviceWorkers.testing.enabled", true],
-      // Reduce the timeout to expose issues when service worker
-      // freezing is broken
-      ["dom.serviceWorkers.idle_timeout", SW_TIMEOUT],
-      ["dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT],
-    ]};
-    SpecialPowers.pushPrefEnv(options, done);
-  });
+  yield enableServiceWorkerDebugging();
+  yield pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
+  yield pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
 
   let { tab, document } = yield openAboutDebugging("workers");
 
   let swTab = yield addTab(TAB_URL);
 
   let serviceWorkersElement = getServiceWorkerList(document);
   yield waitForMutation(serviceWorkersElement, { childList: true });
 
--- a/devtools/client/aboutdebugging/test/browser_service_workers_unregister.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers_unregister.js
@@ -10,25 +10,17 @@
 
 // Service workers can't be loaded from chrome://, but http:// is ok with
 // dom.serviceWorkers.testing.enabled turned on.
 const SCOPE = URL_ROOT + "service-workers/";
 const SERVICE_WORKER = SCOPE + "empty-sw.js";
 const TAB_URL = SCOPE + "empty-sw.html";
 
 add_task(function* () {
-  info("Turn on workers via mochitest http.");
-  yield new Promise(done => {
-    let options = { "set": [
-      // Accept workers from mochitest's http.
-      ["dom.serviceWorkers.testing.enabled", true],
-      ["dom.ipc.processCount", 1],
-    ]};
-    SpecialPowers.pushPrefEnv(options, done);
-  });
+  yield enableServiceWorkerDebugging();
 
   let { tab, document } = yield openAboutDebugging("workers");
 
   // Listen for mutations in the service-workers list.
   let serviceWorkersElement = getServiceWorkerList(document);
   let onMutation = waitForMutation(serviceWorkersElement, { childList: true });
 
   // Open a tab that registers an empty service worker.
--- a/devtools/client/aboutdebugging/test/head.js
+++ b/devtools/client/aboutdebugging/test/head.js
@@ -2,17 +2,18 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /* eslint-env browser */
 /* exported openAboutDebugging, changeAboutDebuggingHash, closeAboutDebugging,
    installAddon, uninstallAddon, waitForMutation, waitForContentMutation, assertHasTarget,
    getServiceWorkerList, getTabList, openPanel, waitForInitialAddonList,
    waitForServiceWorkerRegistered, unregisterServiceWorker,
    waitForDelayedStartupFinished, setupTestAboutDebuggingWebExtension,
-   waitForServiceWorkerActivation */
+   waitForServiceWorkerActivation, enableServiceWorkerDebugging,
+   getServiceWorkerContainer */
 /* import-globals-from ../../framework/test/shared-head.js */
 
 "use strict";
 
 // Load the shared-head file first.
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
@@ -127,16 +128,36 @@ function getInstalledAddonNames(document
  * @return {DOMNode}                 target list or container element
  */
 function getServiceWorkerList(document) {
   return document.querySelector("#service-workers .target-list") ||
     document.querySelector("#service-workers.targets");
 }
 
 /**
+ * Retrieve the container element for the service worker corresponding to the provided
+ * name.
+ *
+ * @param  {String} name
+ *         expected service worker name
+ * @param  {DOMDocument} document
+ *         #service-workers section container document
+ * @return {DOMNode} container element
+ */
+function getServiceWorkerContainer(name, document) {
+  let nameElements = [...document.querySelectorAll("#service-workers .target-name")];
+  let nameElement = nameElements.filter(element => element.textContent === name)[0];
+  if (nameElement) {
+    return nameElement.closest(".target-container");
+  }
+
+  return null;
+}
+
+/**
  * Depending on whether there are tabs opened, return either a
  * target list element or its container.
  * @param  {DOMDocument}  document   #tabs section container document
  * @return {DOMNode}                 target list or container element
  */
 function getTabList(document) {
   return document.querySelector("#tabs .target-list") ||
     document.querySelector("#tabs.targets");
@@ -392,8 +413,25 @@ function* waitForServiceWorkerActivation
 
   let targetElement = name.parentNode.parentNode;
   let targetStatus = targetElement.querySelector(".target-status");
   while (targetStatus.textContent === "Registering") {
     // Wait for the status to leave the "registering" stage.
     yield waitForMutation(serviceWorkersElement, { childList: true, subtree: true });
   }
 }
+
+/**
+ * Set all preferences needed to enable service worker debugging and testing.
+ */
+function enableServiceWorkerDebugging() {
+  return new Promise(done => {
+    let options = { "set": [
+      // Enable service workers.
+      ["dom.serviceWorkers.enabled", true],
+      // Accept workers from mochitest's http.
+      ["dom.serviceWorkers.testing.enabled", true],
+      // Force single content process.
+      ["dom.ipc.processCount", 1],
+    ]};
+    SpecialPowers.pushPrefEnv(options, done);
+  });
+}
--- a/devtools/client/debugger/new/debugger.css
+++ b/devtools/client/debugger/new/debugger.css
@@ -197,17 +197,16 @@
   color: var(--theme-selection-color);
   cursor: pointer;
 }
 
 .landing-page .sidebar li:hover a,
 .landing-page .sidebar li:focus a {
   color: inherit;
 }
-
 :root.theme-light,
 :root .theme-light {
   --theme-search-overlays-semitransparent: rgba(221, 225, 228, 0.66);
 }
 
 * {
   box-sizing: border-box;
 }
@@ -1258,21 +1257,16 @@ menuseparator {
   font-weight: normal;
   white-space: pre-wrap;
 }
 
 .theme-dark .caption,
 .theme-light .caption {
   font-weight: normal;
 }
-/* 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/. */
 
 .search-container {
   position: absolute;
   top: 30px;
   left: 0;
   width: calc(100% - 1px);
   height: calc(100% - 31px);
   display: flex;
@@ -1355,50 +1349,51 @@ html[dir="rtl"] .arrow svg {
 html .arrow.expanded svg {
   transform: rotate(0deg);
 }
 
 .arrow.hidden {
   visibility: hidden;
 }
 .close-btn path {
-  fill: var(--theme-body-color);
+  fill: var(--theme-comment-alt);
 }
 
 .close-btn .close {
   cursor: pointer;
   width: 14px;
   height: 14px;
   padding: 2px;
   text-align: center;
   margin-top: 2px;
   line-height: 7px;
   transition: all 0.25s easeinout;
 }
 
 .close-btn .close svg {
-  width: 6px;
+  width: 8px;
 }
 
 .close-btn:hover {
   display: block;
 }
 
 .close-btn:hover .close {
   background: var(--theme-selection-background);
   border-radius: 2px;
 }
 
 .close-btn:hover .close path {
   fill: white;
 }
 
 .close-btn-big {
-  padding: 13px;
-  width: 40px;
+  padding: 11px;
+  margin-right: 7px;
+  width: 27px;
   height: 40px;
 }
 
 .close-btn-big .close {
   cursor: pointer;
   display: inline-block;
   padding: 2px;
   text-align: center;
@@ -1409,121 +1404,195 @@ html .arrow.expanded svg {
 }
 
 .close-btn-big .close svg {
   width: 9px;
   height: 9px;
 }
 
 .close-btn-big .close:hover {
-  background: var(--theme-selection-background);
   border-radius: 2px;
 }
 
-.close-btn-big .close:hover path {
-  fill: white;
-}
 .search-field {
   width: calc(100% - 1px);
-  height: 40px;
-  background-color: var(--theme-body-background);
+  height: 27px;
+  background-color: var(--theme-toolbar-background);
   border-bottom: 1px solid var(--theme-splitter-color);
+  padding-right: 10px;
   display: flex;
   flex-shrink: 0;
 }
 
+.search-field.big {
+  height: 40px;
+  font-size: 14px;
+}
+
+.search-field.big input {
+  font-size: 14px;
+}
+
 .search-field i {
   display: block;
-  padding: 13px;
-  width: 40px;
+  padding: 0;
+  width: 16px;
 }
 
 .search-field i svg {
   width: 16px;
 }
 
+.search-field.big input {
+  line-height: 40px;
+}
+
 .search-field input {
   border: none;
   line-height: 30px;
-  font-size: 14px;
-  background-color: var(--theme-body-background);
-  color: var(--theme-comment-alt);
+  background-color: var(--theme-toolbar-background);
+  color: var(--theme-body-color-inactive);
   width: calc(100% - 38px);
   flex: 1;
 }
 
 .theme-dark .search-field input {
-  color: var(--theme-comment-alt);
-}
-
-.search-field .magnifying-glass {
-  background-color: var(--theme-body-background);
+  color: var(--theme-body-color-inactive);
+}
+
+.search-field i.magnifying-glass,
+.search-field i.sad-face {
+  padding: 6px;
+  width: 24px;
+}
+
+.search-field.big i.magnifying-glass,
+.search-field.big i.sad-face {
+  padding: 14px;
+  width: 40px;
 }
 
 .search-field .magnifying-glass path,
 .search-field .magnifying-glass ellipse {
   stroke: var(--theme-splitter-color);
 }
 
+.search-field ::-webkit-input-placeholder {
+  color: var(--theme-body-color-inactive);
+}
+
 .search-field input::placeholder {
-  color: var(--theme-comment-alt);
+  color: var(--theme-body-color-inactive);
 }
 
 .search-field input:focus {
   outline-width: 0;
 }
 
 .search-field input.empty {
   color: var(--theme-highlight-orange);
 }
 
+.search-field.big .summary {
+  line-height: 40px;
+}
+
 .search-field .summary {
-  line-height: 40px;
+  line-height: 27px;
   padding-right: 10px;
   color: var(--theme-body-color-inactive);
 }
+
 .result-list {
   list-style: none;
   width: 100%;
-  max-height: calc(100% - 32px);
+  background-color: var(--theme-toolbar-background);
   margin: 0px;
   padding: 0px;
   overflow: auto;
 }
 
+.result-list.big {
+  max-height: calc(100% - 32px);
+}
+
 .result-list * {
   -moz-user-select: none;
   user-select: none;
 }
 
 .result-list li {
-  border: 2px solid var(--theme-splitter-color);
+  color: var(--theme-body-color);
   background-color: var(--theme-tab-toolbar-background);
+  padding: 4px 13px;
+  display: flex;
+  justify-content: space-between;
+}
+
+.result-list li:first-child {
+  border-top: none;
+}
+
+.result-list.big li {
   padding: 10px;
-  margin: 10px;
+  flex-direction: column;
+  border-bottom: 1px solid var(--theme-splitter-color);
 }
 
 .result-list li:hover {
   background: var(--theme-tab-toolbar-background);
   cursor: pointer;
 }
 
 .result-list li.selected {
-  border: 2px solid var(--theme-selection-background);
+  border: 1px solid var(--theme-selection-background);
+}
+
+.result-list.big li.selected {
+  padding-left: 9px;
+  padding-top: 9px;
+}
+
+.search-bar .result-list li.selected {
+  background-color: var(--theme-selection-background);
+  color: white;
 }
 
 .result-list li .title {
   line-height: 1.5em;
   word-break: break-all;
 }
 
-.result-list li .subtitle {
+.result-list li.selected .title {
+  color: white;
+}
+
+.result-list.big li.selected .title {
+  color: var(--theme-body-color);
+}
+
+.result-list.big li .subtitle {
+  word-break: break-all;
+  color: var(--theme-body-color-inactive);
+}
+
+.result-list.big li .subtitle {
   line-height: 1.5em;
-  color: grey;
-  word-break: break-all;
+}
+
+.search-bar .result-list li.selected .subtitle {
+  color: white;
+}
+
+.search-bar .result-list {
+  border-bottom: 1px solid var(--theme-splitter-color);
+}
+
+.theme-dark .result-list {
+  background-color: var(--theme-body-background);
 }
 
 .autocomplete {
   flex: 1;
   width: 100%;
 }
 
 .autocomplete .no-result-msg {
@@ -1637,66 +1706,79 @@ html[dir="rtl"] .tree .node > div {
   flex: 1;
   display: flex;
   overflow: hidden;
 }
 
 .theme-dark .sources-list .tree .node:not(.focused) svg {
   fill: var(--theme-comment);
 }
+
+.theme-dark .source-list .tree .node.focused {
+  background-color: var(--theme-tab-toolbar-background);
+}
 .toggle-button-start,
 .toggle-button-end {
   transform: translate(0, 2px);
   transition: transform 0.25s ease-in-out;
   cursor: pointer;
   padding: 4px 2px;
 }
 
 .toggle-button-start svg,
 .toggle-button-end svg {
-  width: 14px;
+  width: 16px;
   fill: var(--theme-comment);
 }
 
 .theme-dark .toggle-button-start svg,
 .theme-dark .toggle-button-end svg {
   fill: var(--theme-comment-alt);
 }
 
 .toggle-button-end {
   margin-left: auto;
+  margin-right: 5px;
+}
+
+.toggle-button-start {
+  margin-left: 5px;
 }
 
 html:not([dir="rtl"]) .toggle-button-end svg,
 html[dir="rtl"] .toggle-button-start svg {
   transform: rotate(180deg);
 }
 
 html .toggle-button-end.vertical svg {
   transform: rotate(-90deg);
 }
 
+.toggle-button-end.vertical {
+  margin-bottom: 2px;
+}
+
 .toggle-button-start.collapsed,
 .toggle-button-end.collapsed {
   transform: rotate(180deg);
 }
 
 .source-footer {
-  background: var(--theme-body-background);
+  background: var(--theme-toolbar-background);
   border-top: 1px solid var(--theme-splitter-color);
   position: absolute;
   display: flex;
   bottom: 0;
   left: 0;
   right: 1px;
   opacity: 1;
   z-index: 100;
   -moz-user-select: none;
   user-select: none;
-  height: 30px;
+  height: 27px;
   box-sizing: border-box;
 }
 
 .source-footer .commands {
   display: flex;
 }
 
 .source-footer .commands * {
@@ -1739,34 +1821,43 @@ html .toggle-button-end.vertical svg {
   color: var(--theme-body-color);
 }
 
 .coverage-on .source-footer .commands .coverage {
   color: var(--theme-highlight-blue);
   border: 1px solid var(--theme-body-color-inactive);
   border-radius: 2px;
 }
+
 .search-bar {
   display: flex;
   flex-direction: column;
   max-height: 50%;
 }
 
+.search-bar .search-field {
+  padding-left: 7px;
+}
+
+.search-bar .close-btn {
+  padding: 6px;
+}
+
 .search-bottom-bar * {
   -moz-user-select: none;
   user-select: none;
 }
 
 .search-bottom-bar {
   display: flex;
   flex-shrink: 0;
   justify-content: space-between;
   width: calc(100% - 1px);
-  height: 40px;
-  background-color: var(--theme-body-background);
+  height: 27px;
+  background-color: var(--theme-toolbar-background);
   border-bottom: 1px solid var(--theme-splitter-color);
   padding: 0 13px;
 }
 
 .search-bottom-bar button:focus {
   outline: none;
 }
 
@@ -1820,18 +1911,19 @@ html .toggle-button-end.vertical svg {
 }
 
 .search-bottom-bar .search-type-toggles {
   display: flex;
   align-items: center;
 }
 
 .search-bottom-bar .search-type-toggles .search-toggle-title {
-  color: var(--theme-comment-alt);
-  font-size: 12px;
+  color: var(--theme-body-color-inactive);
+  font-size: 11px;
+  font-weight: normal;
   margin: 0;
 }
 
 .search-bottom-bar .search-type-toggles .search-type-btn {
   margin: 0 6px;
   border: none;
   background: transparent;
   color: var(--theme-comment-alt);
@@ -1849,54 +1941,17 @@ html .toggle-button-end.vertical svg {
   color: var(--theme-selection-background);
 }
 
 .theme-dark .search-bottom-bar .search-type-toggles .search-type-btn.active {
   color: white;
 }
 
 .search-bar .result-list {
-  max-height: calc(100% - 80px);
-  border-bottom: 1px solid var(--theme-splitter-color);
-  background-color: var(--theme-body-background);
-}
-
-.search-bar .result-list li {
-  border: none;
-  color: var(--theme-body-color);
-  border-bottom: 1px solid var(--theme-splitter-color);
-  padding: 4px 13px;
-  margin: 0;
-  display: flex;
-  justify-content: space-between;
-}
-
-.search-bar .result-list li:first-child {
-  border-top: none;
-}
-
-.search-bar .result-list li:last-child {
-  border-bottom: none;
-}
-
-.search-bar .result-list li.selected {
-  background-color: var(--theme-selection-background);
-  color: white;
-}
-
-.search-bar .result-list li .subtitle {
-  font-weight: lighter;
-}
-
-.search-bar .result-list li.selected .subtitle {
-  color: white;
-}
-
-.theme-dark .result-list {
-  background-color: var(--theme-body-background);
+  max-height: 230px;
 }
 .popover {
   position: fixed;
   z-index: 4;
 }
 
 .popover-gap {
   height: 10px;
@@ -2171,23 +2226,23 @@ html[dir="rtl"] .editor-mount {
   font-size: 12px;
   padding: 0px 20px;
   color: var(--theme-highlight-blue);
 }
 
 .input-expression::-webkit-input-placeholder {
   text-align: center;
   font-style: italic;
-  color: var(--theme-body-color-inactive);
+  color: var(--theme-comment-alt);
 }
 
 .input-expression::placeholder {
   text-align: center;
   font-style: italic;
-  color: var(--theme-body-color-inactive);
+  color: var(--theme-comment-alt);
   opacity: 1;
 }
 
 .input-expression:focus {
   outline: none;
   cursor: text;
 }
 
@@ -2589,16 +2644,17 @@ html .breakpoints-list .breakpoint.pause
   flex: 0 0 30px;
   border-bottom: 1px solid var(--theme-splitter-color);
   display: flex;
   height: 30px;
   overflow: hidden;
   position: sticky;
   top: 0;
   z-index: 1;
+  background-color: var(--theme-body-background);
 }
 
 .theme-dark .command-bar {
   background-color: var(--theme-tab-toolbar-background);
 }
 
 .command-bar > button {
   -webkit-appearance: none;
@@ -2812,17 +2868,17 @@ html[dir="rtl"] .dropdown {
   height: 30px;
   display: inline-flex;
   align-items: center;
   position: relative;
   transition: all 0.25s ease;
   min-width: 40px;
   overflow: hidden;
   padding: 6px;
-  margin-inline-start: 8px;
+  margin-inline-start: 3px;
   margin-top: 2px;
 }
 
 .source-tab:hover {
   background-color: var(--theme-toolbar-background-alt);
   border-color: var(--theme-splitter-color);
   cursor: pointer;
 }
@@ -2874,14 +2930,11 @@ html[dir="rtl"] .dropdown {
 
 .source-tab:hover .close-btn {
   visibility: visible;
 }
 
 .source-tab .close-btn .close {
   padding: 0;
   margin-top: 0;
-  line-height: 0;
-  line-height: 13px;
-  display: inline-block;
-}
-
-/*# sourceMappingURL=debugger.css.map*/
+  display: inline-flex;
+  justify-content: center;
+}
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -6,147 +6,147 @@
 	else {
 		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react"), require("Services"), require("devtools/client/shared/vendor/react-dom"), require("devtools/shared/flags")) : factory(root["devtools/client/shared/vendor/react"], root["Services"], root["devtools/client/shared/vendor/react-dom"], root["devtools/shared/flags"]);
 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
 	}
 })(this, function(__WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_29__, __WEBPACK_EXTERNAL_MODULE_31__, __WEBPACK_EXTERNAL_MODULE_121__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
-/******/
+
 /******/ 	// The require function
 /******/ 	function __webpack_require__(moduleId) {
-/******/
+
 /******/ 		// Check if module is in cache
 /******/ 		if(installedModules[moduleId])
 /******/ 			return installedModules[moduleId].exports;
-/******/
+
 /******/ 		// Create a new module (and put it into the cache)
 /******/ 		var module = installedModules[moduleId] = {
 /******/ 			exports: {},
 /******/ 			id: moduleId,
 /******/ 			loaded: false
 /******/ 		};
-/******/
+
 /******/ 		// Execute the module function
 /******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
-/******/
+
 /******/ 		// Flag the module as loaded
 /******/ 		module.loaded = true;
-/******/
+
 /******/ 		// Return the exports of the module
 /******/ 		return module.exports;
 /******/ 	}
-/******/
-/******/
+
+
 /******/ 	// expose the modules object (__webpack_modules__)
 /******/ 	__webpack_require__.m = modules;
-/******/
+
 /******/ 	// expose the module cache
 /******/ 	__webpack_require__.c = installedModules;
-/******/
+
 /******/ 	// __webpack_public_path__
 /******/ 	__webpack_require__.p = "/assets/build";
-/******/
+
 /******/ 	// Load entry module and return exports
 /******/ 	return __webpack_require__(0);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ function(module, exports, __webpack_require__) {
 
 	module.exports = __webpack_require__(1);
 
 
 /***/ },
 /* 1 */
 /***/ function(module, exports, __webpack_require__) {
 
 	"use strict";
-	
+
 	var React = __webpack_require__(2);
-	
+
 	var _require = __webpack_require__(3),
 	    bindActionCreators = _require.bindActionCreators,
 	    combineReducers = _require.combineReducers;
-