Merge inbound to mozilla-central. a=merge
authorBogdan Tara <btara@mozilla.com>
Wed, 05 Sep 2018 02:24:27 +0300
changeset 483034 5b55659fa99f1318b48e1edc8cffdb4b56a45c0d
parent 483033 c3734e0f634e10ad994112e49d1849817c1688ff (current diff)
parent 482958 9be13bbe1877d736a656f682b25b49f2b75b69d0 (diff)
child 483035 295b49332ca1079fc66cef1e8e5ee674491d0e85
child 483039 bb0febbdbb250756bbaab3ebf525874fdaa0e170
child 483070 da67b43defd6c695aea49ba90978a931c9d58304
push id232
push userfmarier@mozilla.com
push dateWed, 05 Sep 2018 20:45:54 +0000
reviewersmerge
milestone64.0a1
Merge inbound to mozilla-central. a=merge
devtools/client/responsive.html/actions/display-pixel-ratio.js
devtools/client/responsive.html/actions/location.js
devtools/client/responsive.html/actions/touch-simulation.js
devtools/client/responsive.html/reducers/display-pixel-ratio.js
devtools/client/responsive.html/reducers/location.js
devtools/client/responsive.html/reducers/touch-simulation.js
devtools/client/responsive.html/test/unit/test_change_location.js
dom/bindings/Errors.msg
js/src/vm/BytecodeUtil.cpp
js/src/wasm/WasmTextToBinary.cpp
js/src/wasm/WasmValidate.h
testing/web-platform/meta/webdriver/tests/actions/mouse_dblclick.py.ini
testing/web-platform/meta/webdriver/tests/element_clear/clear.py.ini
testing/web-platform/meta/webdriver/tests/get_element_property/user_prompts.py.ini
testing/web-platform/meta/webdriver/tests/get_element_tag_name/user_prompts.py.ini
testing/web-platform/meta/webdriver/tests/get_title/user_prompts.py.ini
testing/web-platform/meta/webdriver/tests/get_window_rect/user_prompts.py.ini
testing/web-platform/meta/webdriver/tests/send_alert_text/send.py.ini
testing/web-platform/meta/webdriver/tests/set_window_rect/set.py.ini
--- a/browser/base/content/browser-contentblocking.js
+++ b/browser/base/content/browser-contentblocking.js
@@ -10,16 +10,20 @@ var FastBlock = {
     delete this.categoryItem;
     return this.categoryItem = document.getElementById("identity-popup-content-blocking-category-fastblock");
   },
 
   init() {
     XPCOMUtils.defineLazyPreferenceGetter(this, "enabled", this.PREF_ENABLED, false);
     XPCOMUtils.defineLazyPreferenceGetter(this, "visible", this.PREF_UI_ENABLED, false);
   },
+
+  isBlockerActivated(state) {
+    return state & Ci.nsIWebProgressListener.STATE_BLOCKED_SLOW_TRACKING_CONTENT;
+  },
 };
 
 var TrackingProtection = {
   PREF_ENABLED_GLOBALLY: "privacy.trackingprotection.enabled",
   PREF_ENABLED_IN_PRIVATE_WINDOWS: "privacy.trackingprotection.pbmode.enabled",
   PREF_UI_ENABLED: "browser.contentblocking.trackingprotection.control-center.ui.enabled",
   enabledGlobally: false,
   enabledInPrivateWindows: false,
@@ -123,16 +127,20 @@ var TrackingProtection = {
       } else {
         appMenuButton.setAttribute("tooltiptext", this.enabledGlobally ?
           this.strings.disableTooltip : this.strings.enableTooltip);
         appMenuButton.setAttribute("enabled", this.enabledGlobally);
         appMenuButton.setAttribute("aria-pressed", this.enabledGlobally);
       }
     }
   },
+
+  isBlockerActivated(state) {
+    return state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
+  },
 };
 
 var ThirdPartyCookies = {
   PREF_ENABLED: "network.cookie.cookieBehavior",
   PREF_ENABLED_VALUES: [
     // These values match the ones exposed under the Content Blocking section
     // of the Preferences UI.
     Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN,  // Block all third-party cookies
@@ -149,16 +157,21 @@ var ThirdPartyCookies = {
   init() {
     XPCOMUtils.defineLazyPreferenceGetter(this, "behaviorPref", this.PREF_ENABLED,
                                           Ci.nsICookieService.BEHAVIOR_ACCEPT);
     XPCOMUtils.defineLazyPreferenceGetter(this, "visible", this.PREF_UI_ENABLED, false);
   },
   get enabled() {
     return this.PREF_ENABLED_VALUES.includes(this.behaviorPref);
   },
+
+  isBlockerActivated(state) {
+    return (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_TRACKER) != 0 ||
+           (state & Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_FOREIGN) != 0;
+  },
 };
 
 
 var ContentBlocking = {
   // If the user ignores the doorhanger, we stop showing it after some time.
   MAX_INTROS: 20,
   PREF_ENABLED: "browser.contentblocking.enabled",
   PREF_UI_ENABLED: "browser.contentblocking.ui.enabled",
@@ -429,33 +442,31 @@ var ContentBlocking = {
 
     // The user might have navigated before the shield animation
     // finished. In this case, reset the animation to be able to
     // play it in full again and avoid choppiness.
     if (webProgress.isTopLevel) {
       this.iconBox.removeAttribute("animate");
     }
 
-    let isBlocking = state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT;
-    let isAllowing = state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT;
-    let detected = isBlocking || isAllowing;
-
-    let anyBlockerEnabled = false;
+    let anyBlockerActivated = false;
 
     for (let blocker of this.blockers) {
       blocker.categoryItem.classList.toggle("blocked", this.enabled && blocker.enabled);
       blocker.categoryItem.hidden = !blocker.visible;
-      anyBlockerEnabled = anyBlockerEnabled || blocker.enabled;
+      anyBlockerActivated = anyBlockerActivated || blocker.isBlockerActivated(state);
     }
 
-    // We consider the shield state "active" when any kind of blocking-related
-    // activity occurs on the page (blocking or allowing) and at least one blocker
-    // is enabled.
+    // We consider the shield state "active" when some kind of blocking activity
+    // occurs on the page.  Note that merely allowing the loading of content that
+    // we could have blocked does not trigger the appearance of the shield.
     // This state will be overriden later if there's an exception set for this site.
-    let active = this.enabled && detected && anyBlockerEnabled;
+    let active = this.enabled && anyBlockerActivated;
+    let isAllowing = state & Ci.nsIWebProgressListener.STATE_LOADED_TRACKING_CONTENT;
+    let detected = anyBlockerActivated || isAllowing;
 
     let isBrowserPrivate = PrivateBrowsingUtils.isBrowserPrivate(gBrowser.selectedBrowser);
 
     // Check whether the user has added an exception for this site.
     let type =  isBrowserPrivate ? "trackingprotection-pb" : "trackingprotection";
     let hasException = Services.perms.testExactPermission(baseURI, type) ==
       Services.perms.ALLOW_ACTION;
 
--- a/browser/base/content/test/trackingUI/browser.ini
+++ b/browser/base/content/test/trackingUI/browser.ini
@@ -1,13 +1,15 @@
 [DEFAULT]
 tags = trackingprotection
 support-files =
   head.js
   benignPage.html
+  cookiePage.html
+  cookieServer.sjs
   trackingPage.html
 
 [browser_trackingUI_3.js]
 [browser_trackingUI_animation.js]
 [browser_trackingUI_animation_2.js]
 [browser_trackingUI_appMenu.js]
 [browser_trackingUI_appMenu_toggle.js]
 [browser_trackingUI_fetch.js]
--- a/browser/base/content/test/trackingUI/browser_trackingUI_pbmode_exceptions.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_pbmode_exceptions.js
@@ -70,17 +70,17 @@ function testTrackingPage(window) {
   }
 }
 
 function testTrackingPageUnblocked() {
   info("Tracking content must be white-listed and not blocked");
   ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected");
   ok(ContentBlocking.content.hasAttribute("hasException"), "content shows exception");
 
-  ok(ContentBlocking.iconBox.hasAttribute("active"), "shield is active");
+  ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is active");
   ok(ContentBlocking.iconBox.hasAttribute("hasException"), "shield shows exception");
   is(ContentBlocking.iconBox.getAttribute("tooltiptext"),
      gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip"), "correct tooltip");
 
   ok(BrowserTestUtils.is_visible(ContentBlocking.iconBox), "icon box is visible");
   ok(!hidden("#tracking-action-block"), "blockButton is visible");
   ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
   ok(!hidden("#identity-popup-content-blocking-disabled-label"), "disabled label is visible");
--- a/browser/base/content/test/trackingUI/browser_trackingUI_state.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_state.js
@@ -13,28 +13,39 @@
  *
  * See also Bugs 1175327, 1043801, 1178985
  */
 
 const CB_PREF = "browser.contentblocking.enabled";
 const CB_UI_PREF = "browser.contentblocking.ui.enabled";
 const TP_PREF = "privacy.trackingprotection.enabled";
 const TP_PB_PREF = "privacy.trackingprotection.pbmode.enabled";
+const FB_PREF = "browser.fastblock.enabled";
+const FB_TIMEOUT_PREF = "browser.fastblock.timeout";
+const TPC_PREF = "network.cookie.cookieBehavior";
 const BENIGN_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/benignPage.html";
 const TRACKING_PAGE = "http://tracking.example.org/browser/browser/base/content/test/trackingUI/trackingPage.html";
+const COOKIE_PAGE = "http://not-tracking.example.com/browser/browser/base/content/test/trackingUI/cookiePage.html";
 var ContentBlocking = null;
+var FastBlock = null;
 var TrackingProtection = null;
+var ThirdPartyCookies = null;
 var tabbrowser = null;
+var gTrackingPageURL = TRACKING_PAGE;
 
 registerCleanupFunction(function() {
-  TrackingProtection = ContentBlocking = tabbrowser = null;
+  TrackingProtection = ContentBlocking = FastBlock =
+    ThirdPartyCookies = tabbrowser = null;
   UrlClassifierTestUtils.cleanupTestTrackers();
   Services.prefs.clearUserPref(TP_PREF);
   Services.prefs.clearUserPref(TP_PB_PREF);
   Services.prefs.clearUserPref(CB_PREF);
+  Services.prefs.clearUserPref(FB_PREF);
+  Services.prefs.clearUserPref(FB_TIMEOUT_PREF);
+  Services.prefs.clearUserPref(TPC_PREF);
 });
 
 // This is a special version of "hidden" that doesn't check for item
 // visibility and just asserts the display and opacity attributes.
 // That way we can test elements even when their panel is hidden...
 function hidden(sel) {
   let win = tabbrowser.ownerGlobal;
   let el = win.document.querySelector(sel);
@@ -93,71 +104,114 @@ function testBenignPageWithException() {
   ok(!hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is visible");
   ok(hidden("#identity-popup-content-blocking-detected"), "blocking detected label is hidden");
 
   if (Services.prefs.getBoolPref(CB_UI_PREF)) {
     ok(hidden("#identity-popup-content-blocking-category-list"), "category list is hidden");
   }
 }
 
+function areTrackersBlocked(isPrivateBrowsing) {
+  let cbEnabled = Services.prefs.getBoolPref(CB_PREF);
+  let blockedByTP = cbEnabled &&
+                    Services.prefs.getBoolPref(isPrivateBrowsing ? TP_PB_PREF : TP_PREF);
+  let blockedByFB = cbEnabled &&
+                    Services.prefs.getBoolPref(FB_PREF) &&
+                    // The timeout pref is only checked for completeness,
+                    // checking it is technically unneeded for this test.
+                    Services.prefs.getIntPref(FB_TIMEOUT_PREF) == 0;
+  let blockedByTPC = cbEnabled &&
+                     Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
+  return blockedByTP || blockedByFB || blockedByTPC;
+}
+
 function testTrackingPage(window) {
   info("Tracking content must be blocked");
   ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected");
   ok(!ContentBlocking.content.hasAttribute("hasException"), "content shows no exception");
 
-  ok(BrowserTestUtils.is_visible(ContentBlocking.iconBox), "icon box is visible");
-  ok(ContentBlocking.iconBox.hasAttribute("active"), "shield is active");
+  let isPrivateBrowsing = PrivateBrowsingUtils.isWindowPrivate(window);
+  let blockedByTP = areTrackersBlocked(isPrivateBrowsing);
+  is(BrowserTestUtils.is_visible(ContentBlocking.iconBox), blockedByTP,
+     "icon box is" + (blockedByTP ? "" : " not") + " visible");
+  is(ContentBlocking.iconBox.hasAttribute("active"), blockedByTP,
+      "shield is" + (blockedByTP ? "" : " not") + " active");
   ok(!ContentBlocking.iconBox.hasAttribute("hasException"), "icon box shows no exception");
   is(ContentBlocking.iconBox.getAttribute("tooltiptext"),
-     gNavigatorBundle.getString("trackingProtection.icon.activeTooltip"), "correct tooltip");
+     blockedByTP ? gNavigatorBundle.getString("trackingProtection.icon.activeTooltip") : "",
+     "correct tooltip");
 
   ok(hidden("#tracking-action-block"), "blockButton is hidden");
 
+  let cbEnabled = Services.prefs.getBoolPref(CB_PREF);
   if (PrivateBrowsingUtils.isWindowPrivate(window)) {
     ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
-    ok(!hidden("#tracking-action-unblock-private"), "unblockButtonPrivate is visible");
+    is(hidden("#tracking-action-unblock-private"), !cbEnabled,
+       "unblockButtonPrivate is" + (cbEnabled ? "" : " not") + " visible");
   } else {
     ok(!hidden("#tracking-action-unblock"), "unblockButton is visible");
-    ok(hidden("#tracking-action-unblock-private"), "unblockButtonPrivate is hidden");
+    is(hidden("#tracking-action-unblock-private"), cbEnabled,
+       "unblockButtonPrivate is" + (cbEnabled ? "" : " not") + " hidden");
   }
 
   ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden");
   ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible");
 
   if (Services.prefs.getBoolPref(CB_UI_PREF)) {
     ok(!hidden("#identity-popup-content-blocking-category-list"), "category list is visible");
-    ok(hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-add-blocking"),
-      "TP category item is not showing add blocking");
-    ok(!hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-state-label"),
-      "TP category item is set to blocked");
+    let category;
+    if (Services.prefs.getBoolPref(FB_PREF)) {
+      category = "#identity-popup-content-blocking-category-fastblock";
+    } else {
+      category = Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER ?
+                   "#identity-popup-content-blocking-category-3rdpartycookies" :
+                   "#identity-popup-content-blocking-category-tracking-protection";
+    }
+    is(hidden(category + " > .identity-popup-content-blocking-category-add-blocking"), blockedByTP,
+      "Category item is" + (blockedByTP ? " not" : "") + " showing add blocking");
+    is(hidden(category + " > .identity-popup-content-blocking-category-state-label"), !blockedByTP,
+      "Category item is" + (blockedByTP ? "" : " not") + " set to blocked");
   }
 }
 
-function testTrackingPageUnblocked() {
+function testTrackingPageUnblocked(blockedByTP) {
   info("Tracking content must be white-listed and not blocked");
   ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected");
   ok(ContentBlocking.content.hasAttribute("hasException"), "content shows exception");
 
-  ok(ContentBlocking.iconBox.hasAttribute("active"), "shield is active");
-  ok(ContentBlocking.iconBox.hasAttribute("hasException"), "shield shows exception");
+  let cbEnabled = Services.prefs.getBoolPref(CB_PREF);
+  ok(!ContentBlocking.iconBox.hasAttribute("active"), "shield is active");
+  is(ContentBlocking.iconBox.hasAttribute("hasException"), cbEnabled,
+     "shield" + (cbEnabled ? " shows" : " doesn't show") + " exception");
   is(ContentBlocking.iconBox.getAttribute("tooltiptext"),
      gNavigatorBundle.getString("trackingProtection.icon.disabledTooltip"), "correct tooltip");
 
-  ok(BrowserTestUtils.is_visible(ContentBlocking.iconBox), "icon box is visible");
-  ok(!hidden("#tracking-action-block"), "blockButton is visible");
+  is(BrowserTestUtils.is_visible(ContentBlocking.iconBox), cbEnabled,
+     "icon box is" + (cbEnabled ? "" : " not") + " visible");
+  is(hidden("#tracking-action-block"), !cbEnabled,
+     "blockButton is" + (cbEnabled ? " not" : "") + " visible");
   ok(hidden("#tracking-action-unblock"), "unblockButton is hidden");
   ok(!hidden("#identity-popup-content-blocking-disabled-label"), "disabled label is visible");
 
   ok(hidden("#identity-popup-content-blocking-not-detected"), "blocking not detected label is hidden");
   ok(!hidden("#identity-popup-content-blocking-detected"), "blocking detected label is visible");
 
   if (Services.prefs.getBoolPref(CB_UI_PREF)) {
     ok(!hidden("#identity-popup-content-blocking-category-list"), "category list is visible");
-    ok(hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-add-blocking"),
-      "TP category item is not showing add blocking");
+    let category;
+    if (Services.prefs.getBoolPref(FB_PREF)) {
+      category = "#identity-popup-content-blocking-category-fastblock";
+    } else {
+      category = Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER ?
+                   "#identity-popup-content-blocking-category-3rdpartycookies" :
+                   "#identity-popup-content-blocking-category-tracking-protection";
+    }
+    is(hidden(category + " > .identity-popup-content-blocking-category-add-blocking"), blockedByTP,
+      "Category item is" + (blockedByTP ? " not" : "") + " showing add blocking");
+    // Always hidden no matter if blockedByTP or not, since we have an exception.
     ok(hidden("#identity-popup-content-blocking-category-tracking-protection > .identity-popup-content-blocking-category-state-label"),
       "TP category item is not set to blocked");
   }
 }
 
 function testTrackingPageWithCBDisabled() {
   info("Tracking content must be white-listed and not blocked");
   ok(ContentBlocking.content.hasAttribute("detected"), "trackers are detected");
@@ -205,24 +259,25 @@ async function testContentBlockingEnable
 
   if (isPrivateBrowsing) {
     PrivateBrowsingUtils.removeFromTrackingAllowlist(uri);
   } else {
     Services.perms.remove(uri, "trackingprotection");
   }
 
   info("Load a test page containing tracking elements");
-  await promiseTabLoadEvent(tab, TRACKING_PAGE);
+  await promiseTabLoadEvent(tab, gTrackingPageURL);
   testTrackingPage(tab.ownerGlobal);
 
   info("Disable CB for the page (which reloads the page)");
   let tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-unblock");
   await tabReloadPromise;
-  testTrackingPageUnblocked();
+  let blockedByTP = areTrackersBlocked(isPrivateBrowsing);
+  testTrackingPageUnblocked(blockedByTP);
 
   info("Re-enable TP for the page (which reloads the page)");
   tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-block");
   await tabReloadPromise;
   testTrackingPage(tab.ownerGlobal);
 }
 
@@ -247,33 +302,47 @@ async function testContentBlockingDisabl
 
   if (isPrivateBrowsing) {
     PrivateBrowsingUtils.removeFromTrackingAllowlist(uri);
   } else {
     Services.perms.remove(uri, "trackingprotection");
   }
 
   info("Load a test page containing tracking elements");
-  await promiseTabLoadEvent(tab, TRACKING_PAGE);
+  await promiseTabLoadEvent(tab, gTrackingPageURL);
   testTrackingPageWithCBDisabled();
 }
 
 add_task(async function testNormalBrowsing() {
   await UrlClassifierTestUtils.addTestTrackers();
 
   tabbrowser = gBrowser;
   let tab = tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser);
 
   ContentBlocking = gBrowser.ownerGlobal.ContentBlocking;
   ok(ContentBlocking, "CB is attached to the browser window");
   TrackingProtection = gBrowser.ownerGlobal.TrackingProtection;
   ok(TrackingProtection, "TP is attached to the browser window");
   is(TrackingProtection.enabled, Services.prefs.getBoolPref(TP_PREF),
      "TP.enabled is based on the original pref value");
 
+  Services.prefs.setBoolPref(FB_PREF, false);
+
+  await testContentBlockingEnabled(tab);
+
+  if (Services.prefs.getBoolPref(CB_UI_PREF)) {
+    Services.prefs.setBoolPref(CB_PREF, false);
+    ok(!ContentBlocking.enabled, "CB is disabled after setting the pref");
+  } else {
+    Services.prefs.setBoolPref(TP_PREF, false);
+    ok(!TrackingProtection.enabled, "TP is disabled after setting the pref");
+  }
+
+  await testContentBlockingDisabled(tab);
+
   Services.prefs.setBoolPref(TP_PREF, true);
   ok(TrackingProtection.enabled, "TP is enabled after setting the pref");
   Services.prefs.setBoolPref(CB_PREF, true);
   ok(ContentBlocking.enabled, "CB is enabled after setting the pref");
 
   await testContentBlockingEnabled(tab);
 
   if (Services.prefs.getBoolPref(CB_UI_PREF)) {
@@ -282,33 +351,49 @@ add_task(async function testNormalBrowsi
   } else {
     Services.prefs.setBoolPref(TP_PREF, false);
     ok(!TrackingProtection.enabled, "TP is disabled after setting the pref");
   }
 
   await testContentBlockingDisabled(tab);
 
   gBrowser.removeCurrentTab();
+
+  Services.prefs.clearUserPref(FB_PREF);
 });
 
 add_task(async function testPrivateBrowsing() {
   let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
   tabbrowser = privateWin.gBrowser;
   let tab = tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser);
 
   // Set the normal mode pref to false to check the pbmode pref.
   Services.prefs.setBoolPref(TP_PREF, false);
 
+  Services.prefs.setBoolPref(FB_PREF, false);
+
   ContentBlocking = tabbrowser.ownerGlobal.ContentBlocking;
   ok(ContentBlocking, "CB is attached to the private window");
   TrackingProtection = tabbrowser.ownerGlobal.TrackingProtection;
   ok(TrackingProtection, "TP is attached to the private window");
   is(TrackingProtection.enabled, Services.prefs.getBoolPref(TP_PB_PREF),
      "TP.enabled is based on the pb pref value");
 
+  await testContentBlockingEnabled(tab);
+
+  if (Services.prefs.getBoolPref(CB_UI_PREF)) {
+    Services.prefs.setBoolPref(CB_PREF, false);
+    ok(!ContentBlocking.enabled, "CB is disabled after setting the pref");
+  } else {
+    Services.prefs.setBoolPref(TP_PREF, false);
+    ok(!TrackingProtection.enabled, "TP is disabled after setting the pref");
+  }
+
+  await testContentBlockingDisabled(tab);
+
   Services.prefs.setBoolPref(TP_PB_PREF, true);
   ok(TrackingProtection.enabled, "TP is enabled after setting the pref");
   Services.prefs.setBoolPref(CB_PREF, true);
   ok(TrackingProtection.enabled, "CB is enabled after setting the pref");
 
   await testContentBlockingEnabled(tab);
 
   if (Services.prefs.getBoolPref(CB_UI_PREF)) {
@@ -317,9 +402,110 @@ add_task(async function testPrivateBrows
   } else {
     Services.prefs.setBoolPref(TP_PB_PREF, false);
     ok(!TrackingProtection.enabled, "TP is disabled after setting the pref");
   }
 
   await testContentBlockingDisabled(tab);
 
   privateWin.close();
+
+  Services.prefs.clearUserPref(FB_PREF);
 });
+
+add_task(async function testFastBlock() {
+  if (!SpecialPowers.getBoolPref(CB_UI_PREF)) {
+    info("The FastBlock test is disabled when the Content Blocking UI is disabled");
+    return;
+  }
+
+  await UrlClassifierTestUtils.addTestTrackers();
+
+  tabbrowser = gBrowser;
+  let tab = tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser);
+
+  Services.prefs.setBoolPref(FB_PREF, false);
+
+  ContentBlocking = gBrowser.ownerGlobal.ContentBlocking;
+  ok(ContentBlocking, "CB is attached to the browser window");
+  FastBlock = gBrowser.ownerGlobal.FastBlock;
+  ok(FastBlock, "TP is attached to the browser window");
+  is(FastBlock.enabled, Services.prefs.getBoolPref(FB_PREF),
+     "FB.enabled is based on the original pref value");
+  Services.prefs.setBoolPref(CB_PREF, true);
+  ok(ContentBlocking.enabled, "CB is enabled after setting the pref");
+
+  await testContentBlockingEnabled(tab);
+
+  ok(Services.prefs.getBoolPref(CB_UI_PREF), "CB UI must be enabled here");
+  Services.prefs.setBoolPref(CB_PREF, false);
+  ok(!ContentBlocking.enabled, "CB is disabled after setting the pref");
+
+  await testContentBlockingDisabled(tab);
+
+  Services.prefs.setBoolPref(FB_PREF, true);
+  Services.prefs.setIntPref(FB_TIMEOUT_PREF, 0);
+  ok(FastBlock.enabled, "FB is enabled after setting the pref");
+  Services.prefs.setBoolPref(CB_PREF, true);
+  ok(ContentBlocking.enabled, "CB is enabled after setting the pref");
+
+  await testContentBlockingEnabled(tab);
+
+  ok(Services.prefs.getBoolPref(CB_UI_PREF), "CB UI must be enabled here");
+  Services.prefs.setBoolPref(CB_PREF, false);
+  ok(!ContentBlocking.enabled, "CB is disabled after setting the pref");
+
+  await testContentBlockingDisabled(tab);
+
+  Services.prefs.clearUserPref(FB_PREF);
+  Services.prefs.clearUserPref(FB_TIMEOUT_PREF);
+  gBrowser.removeCurrentTab();
+});
+
+add_task(async function testThirdPartyCookies() {
+  if (!SpecialPowers.getBoolPref(CB_UI_PREF)) {
+    info("The ThirdPartyCookies test is disabled when the Content Blocking UI is disabled");
+    return;
+  }
+
+  await UrlClassifierTestUtils.addTestTrackers();
+  gTrackingPageURL = COOKIE_PAGE;
+
+  Services.prefs.setBoolPref(FB_PREF, false);
+
+  tabbrowser = gBrowser;
+  let tab = tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser);
+
+  ContentBlocking = gBrowser.ownerGlobal.ContentBlocking;
+  ok(ContentBlocking, "CB is attached to the browser window");
+  ThirdPartyCookies = gBrowser.ownerGlobal.ThirdPartyCookies;
+  ok(ThirdPartyCookies, "TP is attached to the browser window");
+  is(ThirdPartyCookies.enabled,
+     Services.prefs.getIntPref(TPC_PREF) == Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER,
+     "TPC.enabled is based on the original pref value");
+  Services.prefs.setBoolPref(CB_PREF, true);
+  ok(ContentBlocking.enabled, "CB is enabled after setting the pref");
+
+  await testContentBlockingEnabled(tab);
+
+  ok(Services.prefs.getBoolPref(CB_UI_PREF), "CB UI must be enabled here");
+  Services.prefs.setBoolPref(CB_PREF, false);
+  ok(!ContentBlocking.enabled, "CB is disabled after setting the pref");
+
+  await testContentBlockingDisabled(tab);
+
+  Services.prefs.setIntPref(TPC_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER);
+  ok(ThirdPartyCookies.enabled, "TPC is enabled after setting the pref");
+  Services.prefs.setBoolPref(CB_PREF, true);
+  ok(ContentBlocking.enabled, "CB is enabled after setting the pref");
+
+  await testContentBlockingEnabled(tab);
+
+  ok(Services.prefs.getBoolPref(CB_UI_PREF), "CB UI must be enabled here");
+  Services.prefs.setBoolPref(CB_PREF, false);
+  ok(!ContentBlocking.enabled, "CB is disabled after setting the pref");
+
+  await testContentBlockingDisabled(tab);
+
+  Services.prefs.clearUserPref(FB_PREF);
+  Services.prefs.clearUserPref(TPC_PREF);
+  gBrowser.removeCurrentTab();
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/trackingUI/cookiePage.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<!-- 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/. -->
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+  <head>
+    <meta charset="utf8">
+  </head>
+  <body>
+    <iframe src="http://trackertest.org/browser/browser/base/content/test/trackingUI/cookieServer.sjs"></iframe>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/trackingUI/cookieServer.sjs
@@ -0,0 +1,9 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function handleRequest(request, response) {
+  response.setStatusLine(request.httpVersion, 200);
+  response.setHeader("Set-Cookie", "foopy=1");
+  response.write("cookie served");
+}
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -365,17 +365,16 @@
                           aria-labelledby="trackingProtectionMenuDesc">
                 <radio value="private"
                        data-l10n-id="content-blocking-tracking-protection-option-private"
                        flex="1" />
                 <radio value="always"
                        data-l10n-id="content-blocking-tracking-protection-option-always"
                        flex="1" />
               </radiogroup>
-              <label id="changeBlockListLink" data-l10n-id="content-blocking-tracking-protection-change-block-list" class="text-link"/>
               <label id="changeBlockListLink"
                      data-l10n-id="content-blocking-tracking-protection-change-block-list"
                      class="text-link"
                      search-l10n-ids="blocklist-window.title, blocklist-desc, blocklist-button-cancel.label, blocklist-button-ok.label"/>
             </vbox>
           </vbox>
         </hbox>
       </vbox>
--- a/build/macosx/cross-mozconfig.common
+++ b/build/macosx/cross-mozconfig.common
@@ -5,16 +5,19 @@
 MOZ_AUTOMATION_L10N_CHECK=0
 
 if [ "x$IS_NIGHTLY" = "xyes" ]; then
   # Some nightlies (eg: Mulet) don't want these set.
   MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1}
 fi
 . "$topsrcdir/build/mozconfig.common"
 
+# Rust requires dsymutil into PATH
+mk_add_options "export PATH=$topsrcdir/llvm-dsymutil/bin:$PATH"
+
 # ld needs libLTO.so from llvm
 mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/clang/lib"
 
 CROSS_CCTOOLS_PATH=$topsrcdir/cctools
 # This SDK was copied from a local XCode install and uploaded to tooltool.
 # Generate the tarball by running this command with the proper SDK version:
 #   sdk_path=$(xcrun --sdk macosx10.12 --show-sdk-path)
 #   tar -C $(dirname ${sdk_path}) -cHjf /tmp/$(basename ${sdk_path}).tar.bz2 $(basename ${sdk_path})
--- a/build/unix/mozconfig.linux32
+++ b/build/unix/mozconfig.linux32
@@ -1,15 +1,8 @@
 . "$topsrcdir/build/unix/mozconfig.linux"
 
 export MOZ_LINUX_32_SSE2_STARTUP_ERROR=1
 
 CFLAGS="$CFLAGS -march=pentium-m -msse -msse2 -mfpmath=sse"
 CXXFLAGS="$CXXFLAGS -march=pentium-m -msse -msse2 -mfpmath=sse"
 
 ac_add_options --target=i686-pc-linux
-
-# The linux32 builds are effectively cross-compilations, and geckodriver
-# is not enabled by default on cross-compilations, because in most cases,
-# the target is not something we can run binaries of, and geckodriver
-# is a target binary instead of a host binary. But for linux32, we actually
-# can run the target binaries, so we can manually enable geckodriver.
-ac_add_options --enable-geckodriver
--- a/config/external/icu/data/genicudata.py
+++ b/config/external/icu/data/genicudata.py
@@ -1,14 +1,14 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
 def main(output, data_file, data_symbol):
-    output.write('''    AREA .rdata,ALIGN=4,DATA,READONLY
-    EXPORT _{data_symbol}[DATA]
-_{data_symbol}
+    output.write('''    AREA |.rdata|,ALIGN=4,DATA,READONLY
+    EXPORT |{data_symbol}|[DATA]
+|{data_symbol}|
     INCBIN {data_file}
     END
 '''.format(data_file=data_file, data_symbol=data_symbol))
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_flex_display_badge.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the flex display badge toggles on the flexbox highlighter.
+
+const TEST_URI = `
+  <style type="text/css">
+    #flex {
+      display: flex;
+    }
+  </style>
+  <div id="flex"></div>
+`;
+
+const HIGHLIGHTER_TYPE = "FlexboxHighlighter";
+
+add_task(async function() {
+  await pushPref("devtools.inspector.flexboxHighlighter.enabled", true);
+  await pushPref("devtools.flexboxinspector.enabled", true);
+  await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  const { inspector } = await openLayoutView();
+  const { highlighters, store } = inspector;
+
+  info("Check the flex display badge is shown and not active.");
+  await selectNode("#flex", inspector);
+  const flexContainer = await getContainerForSelector("#flex", inspector);
+  const flexDisplayBadge = flexContainer.elt.querySelector(".markup-badge[data-display]");
+  ok(!flexDisplayBadge.classList.contains("active"), "flex display badge is not active.");
+
+  info("Check the initial state of the flex highlighter.");
+  ok(!highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "No flexbox highlighter exists in the highlighters overlay.");
+  ok(!highlighters.flexboxHighlighterShown, "No flexbox highlighter is shown.");
+
+  info("Toggling ON the flexbox highlighter from the flex display badge.");
+  const onHighlighterShown = highlighters.once("flexbox-highlighter-shown");
+  let onCheckboxChange = waitUntilState(store, state => state.flexbox.highlighted);
+  flexDisplayBadge.click();
+  await onHighlighterShown;
+  await onCheckboxChange;
+
+  info("Check the flexbox highlighter is created and flex display badge state.");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "Flexbox highlighter is created in the highlighters overlay.");
+  ok(highlighters.flexboxHighlighterShown, "Flexbox highlighter is shown.");
+  ok(flexDisplayBadge.classList.contains("active"), "flex display badge is active.");
+
+  info("Toggling OFF the flexbox highlighter from the flex display badge.");
+  const onHighlighterHidden = highlighters.once("flexbox-highlighter-hidden");
+  onCheckboxChange = waitUntilState(store, state => !state.flexbox.highlighted);
+  flexDisplayBadge.click();
+  await onHighlighterHidden;
+  await onCheckboxChange;
+
+  ok(!flexDisplayBadge.classList.contains("active"), "flex display badge is not active.");
+});
--- a/devtools/client/locales/en-US/responsive.properties
+++ b/devtools/client/locales/en-US/responsive.properties
@@ -119,20 +119,19 @@ responsive.devicePixelRatioOption=DPR: %
 # LOCALIZATION NOTE (responsive.reloadConditions.touchSimulation): Label on checkbox used
 # to select whether to reload when touch simulation is toggled.
 responsive.reloadConditions.touchSimulation=Reload when touch simulation is toggled
 
 # LOCALIZATION NOTE (responsive.reloadConditions.userAgent): Label on checkbox used
 # to select whether to reload when user agent is changed.
 responsive.reloadConditions.userAgent=Reload when user agent is changed
 
-# LOCALIZATION NOTE (responsive.reloadNotification.description): Text in notification bar
-# shown on first open to clarify that some features need a reload to apply.  %1$S is the
-# label on the reload conditions menu (responsive.reloadConditions.label).
-responsive.reloadNotification.description=Device simulation changes require a reload to fully apply.  Automatic reloads are disabled by default to avoid losing any changes in DevTools.  You can enable reloading via the “%1$S” menu.
+# LOCALIZATION NOTE (responsive.reloadNotification.description2): Text in notification bar
+# shown on first open to clarify that some features need a reload to apply.
+responsive.reloadNotification.description2=Device simulation changes require a reload to fully apply. Automatic reloads are disabled by default to avoid losing any changes in DevTools. You can enable reloading via the Settings menu.
 
 # LOCALIZATION NOTE (responsive.leftAlignViewport): Label on checkbox used in the settings
 # menu.
 responsive.leftAlignViewport = Left-align Viewport
 
 # LOCALIZATION NOTE (responsive.settingOnboarding.content): This is the content shown in
 # the setting onboarding tooltip that is displayed below the settings menu button in
 # Responsive Design Mode.
--- a/devtools/client/responsive.html/actions/devices.js
+++ b/devtools/client/responsive.html/actions/devices.js
@@ -1,29 +1,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const Services = require("Services");
+
 const {
   ADD_DEVICE,
   ADD_DEVICE_TYPE,
   LOAD_DEVICE_LIST_START,
   LOAD_DEVICE_LIST_ERROR,
   LOAD_DEVICE_LIST_END,
   REMOVE_DEVICE,
   UPDATE_DEVICE_DISPLAYED,
   UPDATE_DEVICE_MODAL,
 } = require("./index");
 const { removeDeviceAssociation } = require("./viewports");
 
 const { addDevice, getDevices, removeDevice } = require("devtools/client/shared/devices");
 
-const Services = require("Services");
 const DISPLAYED_DEVICES_PREF = "devtools.responsive.html.displayedDeviceList";
 
 /**
  * Returns an object containing the user preference of displayed devices.
  *
  * @return {Object} containing two Sets:
  * - added: Names of the devices that were explicitly enabled by the user
  * - removed: Names of the devices that were explicitly removed by the user
deleted file mode 100644
--- a/devtools/client/responsive.html/actions/display-pixel-ratio.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { CHANGE_DISPLAY_PIXEL_RATIO } = require("./index");
-
-module.exports = {
-
-  /**
-   * The pixel ratio of the display has changed. This may be triggered by the user
-   * when changing the monitor resolution, or when the window is dragged to a different
-   * display with a different pixel ratio.
-   */
-  changeDisplayPixelRatio(displayPixelRatio) {
-    return {
-      type: CHANGE_DISPLAY_PIXEL_RATIO,
-      displayPixelRatio,
-    };
-  },
-
-};
--- a/devtools/client/responsive.html/actions/index.js
+++ b/devtools/client/responsive.html/actions/index.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-// This file lists all of the actions available in responsive design.  This
+// This file lists all of the actions available in responsive design. This
 // central list of constants makes it easy to see all possible action names at
-// a glance.  Please add a comment with each new action type.
+// a glance. Please add a comment with each new action type.
 
 const { createEnum } = require("devtools/client/shared/enum");
 
 const {
   CHANGE_NETWORK_THROTTLING,
 } = require("devtools/client/shared/components/throttling/actions");
 
 createEnum([
@@ -23,17 +23,17 @@ createEnum([
   "ADD_DEVICE_TYPE",
 
   // Add an additional viewport to display the document.
   "ADD_VIEWPORT",
 
   // Change the device displayed in the viewport.
   "CHANGE_DEVICE",
 
-  // Change the location of the page.  This may be triggered by the user
+  // Change the location of the page. This may be triggered by the user
   // directly entering a new URL, navigating with links, etc.
   "CHANGE_LOCATION",
 
   // The pixel ratio of the display has changed. This may be triggered by the user
   // when changing the monitor resolution, or when the window is dragged to a different
   // display with a different pixel ratio.
   "CHANGE_DISPLAY_PIXEL_RATIO",
 
@@ -43,19 +43,16 @@ createEnum([
   // The pixel ratio of the viewport has changed. This may be triggered by the user
   // when changing the device displayed in the viewport, or when a pixel ratio is
   // selected from the device pixel ratio dropdown.
   "CHANGE_PIXEL_RATIO",
 
   // Change one of the reload conditions.
   "CHANGE_RELOAD_CONDITION",
 
-  // Change the touch simulation state.
-  "CHANGE_TOUCH_SIMULATION",
-
   // Indicates that the device list is being loaded.
   "LOAD_DEVICE_LIST_START",
 
   // Indicates that the device list loading action threw an error.
   "LOAD_DEVICE_LIST_ERROR",
 
   // Indicates that the device list has been loaded successfully.
   "LOAD_DEVICE_LIST_END",
@@ -79,15 +76,18 @@ createEnum([
   "TAKE_SCREENSHOT_START",
 
   // Indicates when the screenshot action ends.
   "TAKE_SCREENSHOT_END",
 
   // Toggles the left alignment of the viewports.
   "TOGGLE_LEFT_ALIGNMENT",
 
+  // Toggles the touch simulation state of the viewports.
+  "TOGGLE_TOUCH_SIMULATION",
+
   // Update the device display state in the device selector.
   "UPDATE_DEVICE_DISPLAYED",
 
   // Update the device modal state.
   "UPDATE_DEVICE_MODAL",
 
 ], module.exports);
deleted file mode 100644
--- a/devtools/client/responsive.html/actions/location.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { CHANGE_LOCATION } = require("./index");
-
-module.exports = {
-
-  /**
-   * The location of the page has changed.  This may be triggered by the user
-   * directly entering a new URL, navigating with links, etc.
-   */
-  changeLocation(location) {
-    return {
-      type: CHANGE_LOCATION,
-      location,
-    };
-  },
-
-};
--- a/devtools/client/responsive.html/actions/moz.build
+++ b/devtools/client/responsive.html/actions/moz.build
@@ -1,17 +1,14 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'devices.js',
-    'display-pixel-ratio.js',
     'index.js',
-    'location.js',
     'reload-conditions.js',
     'screenshot.js',
-    'touch-simulation.js',
     'ui.js',
     'viewports.js',
 )
--- a/devtools/client/responsive.html/actions/reload-conditions.js
+++ b/devtools/client/responsive.html/actions/reload-conditions.js
@@ -1,21 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+const Services = require("Services");
+
 const {
   CHANGE_RELOAD_CONDITION,
   LOAD_RELOAD_CONDITIONS_END,
 } = require("./index");
 
 const Types = require("../types");
-const Services = require("Services");
 
 const PREF_PREFIX = "devtools.responsive.reloadConditions.";
 
 module.exports = {
 
   changeReloadCondition(id, value) {
     return dispatch => {
       const pref = PREF_PREFIX + id;
--- a/devtools/client/responsive.html/actions/screenshot.js
+++ b/devtools/client/responsive.html/actions/screenshot.js
@@ -1,25 +1,26 @@
 /* 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";
 
+const Services = require("Services");
+
 const {
   TAKE_SCREENSHOT_START,
   TAKE_SCREENSHOT_END,
 } = require("./index");
 
 const { getFormatStr } = require("../utils/l10n");
 const { getTopLevelWindow } = require("../utils/window");
 const e10s = require("../utils/e10s");
-const Services = require("Services");
 
 const CAMERA_AUDIO_URL = "resource://devtools/client/themes/audio/shutter.wav";
 
 const animationFrame = () => new Promise(resolve => {
   window.requestAnimationFrame(resolve);
 });
 
 function getFileName() {
deleted file mode 100644
--- a/devtools/client/responsive.html/actions/touch-simulation.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/* eslint-env browser */
-
-"use strict";
-
-const {
-  CHANGE_TOUCH_SIMULATION
-} = require("./index");
-
-module.exports = {
-
-  changeTouchSimulation(enabled) {
-    return {
-      type: CHANGE_TOUCH_SIMULATION,
-      enabled,
-    };
-  },
-
-};
--- a/devtools/client/responsive.html/actions/ui.js
+++ b/devtools/client/responsive.html/actions/ui.js
@@ -1,20 +1,41 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {
+  CHANGE_DISPLAY_PIXEL_RATIO,
   TOGGLE_LEFT_ALIGNMENT,
+  TOGGLE_TOUCH_SIMULATION,
 } = require("./index");
 
 module.exports = {
 
+  /**
+   * The pixel ratio of the display has changed. This may be triggered by the user
+   * when changing the monitor resolution, or when the window is dragged to a different
+   * display with a different pixel ratio.
+   */
+  changeDisplayPixelRatio(displayPixelRatio) {
+    return {
+      type: CHANGE_DISPLAY_PIXEL_RATIO,
+      displayPixelRatio,
+    };
+  },
+
   toggleLeftAlignment(enabled) {
     return {
       type: TOGGLE_LEFT_ALIGNMENT,
       enabled,
     };
   },
 
+  toggleTouchSimulation(enabled) {
+    return {
+      type: TOGGLE_TOUCH_SIMULATION,
+      enabled,
+    };
+  },
+
 };
--- a/devtools/client/responsive.html/browser/swap.js
+++ b/devtools/client/responsive.html/browser/swap.js
@@ -1,18 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Ci } = require("chrome");
+const Services = require("Services");
 const { E10SUtils } = require("resource://gre/modules/E10SUtils.jsm");
 const { tunnelToInnerBrowser } = require("./tunnel");
-const Services = require("Services");
 
 function debug(msg) {
   // console.log(`RDM swap: ${msg}`);
 }
 
 /**
  * Swap page content from an existing tab into a new browser within a container
  * page.  Page state is preserved by using `swapFrameLoaders`, just like when
--- a/devtools/client/responsive.html/browser/web-navigation.js
+++ b/devtools/client/responsive.html/browser/web-navigation.js
@@ -5,16 +5,17 @@
 "use strict";
 
 const { Ci, Cu, Cr } = require("chrome");
 const ChromeUtils = require("ChromeUtils");
 const Services = require("Services");
 const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
 const { Utils } = require("resource://gre/modules/sessionstore/Utils.jsm");
 const Telemetry = require("devtools/client/shared/telemetry");
+
 const telemetry = new Telemetry();
 
 function readInputStreamToString(stream) {
   return NetUtil.readInputStreamToString(stream, stream.available());
 }
 
 /**
  * This object aims to provide the nsIWebNavigation interface for mozbrowser elements.
--- a/devtools/client/responsive.html/components/App.js
+++ b/devtools/client/responsive.html/components/App.js
@@ -1,17 +1,17 @@
 /* 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";
 
-const { Component, createFactory } = require("devtools/client/shared/vendor/react");
+const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const Toolbar = createFactory(require("./Toolbar"));
 const Viewports = createFactory(require("./Viewports"));
 
 loader.lazyGetter(this, "DeviceModal",
@@ -22,38 +22,38 @@ const {
   addCustomDevice,
   removeCustomDevice,
   updateDeviceDisplayed,
   updateDeviceModal,
   updatePreferredDevices,
 } = require("../actions/devices");
 const { changeReloadCondition } = require("../actions/reload-conditions");
 const { takeScreenshot } = require("../actions/screenshot");
-const { changeTouchSimulation } = require("../actions/touch-simulation");
-const { toggleLeftAlignment } = require("../actions/ui");
+const {
+  toggleTouchSimulation,
+  toggleLeftAlignment,
+} = require("../actions/ui");
 const {
   changeDevice,
   changePixelRatio,
   removeDeviceAssociation,
   resizeViewport,
   rotateViewport,
 } = require("../actions/viewports");
 
 const Types = require("../types");
 
-class App extends Component {
+class App extends PureComponent {
   static get propTypes() {
     return {
       devices: PropTypes.shape(Types.devices).isRequired,
       dispatch: PropTypes.func.isRequired,
-      displayPixelRatio: Types.pixelRatio.value.isRequired,
       networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
       reloadConditions: PropTypes.shape(Types.reloadConditions).isRequired,
       screenshot: PropTypes.shape(Types.screenshot).isRequired,
-      touchSimulation: PropTypes.shape(Types.touchSimulation).isRequired,
       viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.onAddCustomDevice = this.onAddCustomDevice.bind(this);
@@ -88,17 +88,17 @@ class App extends Component {
     // TODO: Bug 1332754: Move messaging and logic into the action creator so that the
     // message is sent from the action creator and device property changes are sent from
     // there instead of this function.
     window.postMessage({
       type: "change-device",
       device,
     }, "*");
     this.props.dispatch(changeDevice(id, device.name, deviceType));
-    this.props.dispatch(changeTouchSimulation(device.touch));
+    this.props.dispatch(toggleTouchSimulation(device.touch));
     this.props.dispatch(changePixelRatio(id, device.pixelRatio));
   }
 
   onChangeNetworkThrottling(enabled, profile) {
     window.postMessage({
       type: "change-network-throttling",
       enabled,
       profile,
@@ -118,17 +118,17 @@ class App extends Component {
     this.props.dispatch(changeReloadCondition(id, value));
   }
 
   onChangeTouchSimulation(enabled) {
     window.postMessage({
       type: "change-touch-simulation",
       enabled,
     }, "*");
-    this.props.dispatch(changeTouchSimulation(enabled));
+    this.props.dispatch(toggleTouchSimulation(enabled));
   }
 
   onContentResize({ width, height }) {
     window.postMessage({
       type: "content-resize",
       width,
       height,
     }, "*");
@@ -145,17 +145,17 @@ class App extends Component {
   onRemoveCustomDevice(device) {
     this.props.dispatch(removeCustomDevice(device));
   }
 
   onRemoveDeviceAssociation(id) {
     // TODO: Bug 1332754: Move messaging and logic into the action creator so that device
     // property changes are sent from there instead of this function.
     this.props.dispatch(removeDeviceAssociation(id));
-    this.props.dispatch(changeTouchSimulation(false));
+    this.props.dispatch(toggleTouchSimulation(false));
     this.props.dispatch(changePixelRatio(id, 0));
   }
 
   onResizeViewport(id, width, height) {
     this.props.dispatch(resizeViewport(id, width, height));
   }
 
   onRotateViewport(id) {
@@ -176,21 +176,19 @@ class App extends Component {
 
   onUpdateDeviceModal(isOpen, modalOpenedFromViewport) {
     this.props.dispatch(updateDeviceModal(isOpen, modalOpenedFromViewport));
   }
 
   render() {
     const {
       devices,
-      displayPixelRatio,
       networkThrottling,
       reloadConditions,
       screenshot,
-      touchSimulation,
       viewports,
     } = this.props;
 
     const {
       onAddCustomDevice,
       onBrowserMounted,
       onChangeDevice,
       onChangeNetworkThrottling,
@@ -221,23 +219,21 @@ class App extends Component {
     if (devices.modalOpenedFromViewport !== null) {
       deviceAdderViewportTemplate = viewports[devices.modalOpenedFromViewport];
     }
 
     return (
       dom.div({ id: "app" },
         Toolbar({
           devices,
-          displayPixelRatio,
           networkThrottling,
           reloadConditions,
           screenshot,
           selectedDevice,
           selectedPixelRatio,
-          touchSimulation,
           viewport: viewports[0],
           onChangeDevice,
           onChangeNetworkThrottling,
           onChangePixelRatio,
           onChangeReloadCondition,
           onChangeTouchSimulation,
           onExit,
           onRemoveDeviceAssociation,
--- a/devtools/client/responsive.html/components/DevicePixelRatioMenu.js
+++ b/devtools/client/responsive.html/components/DevicePixelRatioMenu.js
@@ -16,20 +16,20 @@ const Types = require("../types");
 loader.lazyRequireGetter(this, "showMenu", "devtools/client/shared/components/menu/utils", true);
 
 const PIXEL_RATIO_PRESET = [1, 2, 3];
 
 class DevicePixelRatioMenu extends PureComponent {
   static get propTypes() {
     return {
       devices: PropTypes.shape(Types.devices).isRequired,
-      displayPixelRatio: Types.pixelRatio.value.isRequired,
+      displayPixelRatio: PropTypes.number.isRequired,
       onChangePixelRatio: PropTypes.func.isRequired,
       selectedDevice: PropTypes.string.isRequired,
-      selectedPixelRatio: PropTypes.shape(Types.pixelRatio).isRequired,
+      selectedPixelRatio: PropTypes.number.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
     this.onShowDevicePixelMenu = this.onShowDevicePixelMenu.bind(this);
   }
 
@@ -39,18 +39,18 @@ class DevicePixelRatioMenu extends PureC
       onChangePixelRatio,
       selectedPixelRatio,
     } = this.props;
 
     const menuItems = PIXEL_RATIO_PRESET.map(value => {
       return {
         label: getFormatStr("responsive.devicePixelRatioOption", value),
         type: "checkbox",
-        checked: selectedPixelRatio.value > 0 ?
-          selectedPixelRatio.value === value :
+        checked: selectedPixelRatio > 0 ?
+          selectedPixelRatio === value :
           displayPixelRatio === value,
         click: () => onChangePixelRatio(+value),
       };
     });
 
     showMenu(menuItems, {
       button: event.target,
       useTopLevelWindow: true,
@@ -81,16 +81,16 @@ class DevicePixelRatioMenu extends PureC
           id: "device-pixel-ratio-menu",
           className: "devtools-button devtools-dropdown-button",
           disabled: isDisabled,
           title,
           onClick: this.onShowDevicePixelMenu,
         },
         dom.span({ className: "title" },
           getFormatStr("responsive.devicePixelRatioOption",
-            selectedPixelRatio.value || displayPixelRatio)
+            selectedPixelRatio || displayPixelRatio)
         )
       )
     );
   }
 }
 
 module.exports = DevicePixelRatioMenu;
--- a/devtools/client/responsive.html/components/ResizableViewport.js
+++ b/devtools/client/responsive.html/components/ResizableViewport.js
@@ -1,29 +1,29 @@
 /* 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/. */
 
 /* global window */
 
 "use strict";
 
-const { Component, createFactory } = require("devtools/client/shared/vendor/react");
+const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const Browser = createFactory(require("./Browser"));
 
 const Constants = require("../constants");
 const Types = require("../types");
 
 const VIEWPORT_MIN_WIDTH = Constants.MIN_VIEWPORT_DIMENSION;
 const VIEWPORT_MIN_HEIGHT = Constants.MIN_VIEWPORT_DIMENSION;
 
-class ResizableViewport extends Component {
+class ResizableViewport extends PureComponent {
   static get propTypes() {
     return {
       leftAlignmentEnabled: PropTypes.bool.isRequired,
       onBrowserMounted: PropTypes.func.isRequired,
       onContentResize: PropTypes.func.isRequired,
       onRemoveDeviceAssociation: PropTypes.func.isRequired,
       onResizeViewport: PropTypes.func.isRequired,
       screenshot: PropTypes.shape(Types.screenshot).isRequired,
--- a/devtools/client/responsive.html/components/Toolbar.js
+++ b/devtools/client/responsive.html/components/Toolbar.js
@@ -17,17 +17,17 @@ const ViewportDimension = createFactory(
 
 const { getStr } = require("../utils/l10n");
 const Types = require("../types");
 
 class Toolbar extends PureComponent {
   static get propTypes() {
     return {
       devices: PropTypes.shape(Types.devices).isRequired,
-      displayPixelRatio: Types.pixelRatio.value.isRequired,
+      displayPixelRatio: PropTypes.number.isRequired,
       leftAlignmentEnabled: PropTypes.bool.isRequired,
       networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
       onChangeDevice: PropTypes.func.isRequired,
       onChangeNetworkThrottling: PropTypes.func.isRequired,
       onChangePixelRatio: PropTypes.func.isRequired,
       onChangeReloadCondition: PropTypes.func.isRequired,
       onChangeTouchSimulation: PropTypes.func.isRequired,
       onExit: PropTypes.func.isRequired,
@@ -35,18 +35,18 @@ class Toolbar extends PureComponent {
       onResizeViewport: PropTypes.func.isRequired,
       onRotateViewport: PropTypes.func.isRequired,
       onScreenshot: PropTypes.func.isRequired,
       onToggleLeftAlignment: PropTypes.func.isRequired,
       onUpdateDeviceModal: PropTypes.func.isRequired,
       reloadConditions: PropTypes.shape(Types.reloadConditions).isRequired,
       screenshot: PropTypes.shape(Types.screenshot).isRequired,
       selectedDevice: PropTypes.string.isRequired,
-      selectedPixelRatio: PropTypes.shape(Types.pixelRatio).isRequired,
-      touchSimulation: PropTypes.shape(Types.touchSimulation).isRequired,
+      selectedPixelRatio: PropTypes.number.isRequired,
+      touchSimulationEnabled: PropTypes.bool.isRequired,
       viewport: PropTypes.shape(Types.viewport).isRequired,
     };
   }
 
   render() {
     const {
       devices,
       displayPixelRatio,
@@ -63,17 +63,17 @@ class Toolbar extends PureComponent {
       onRotateViewport,
       onScreenshot,
       onToggleLeftAlignment,
       onUpdateDeviceModal,
       reloadConditions,
       screenshot,
       selectedDevice,
       selectedPixelRatio,
-      touchSimulation,
+      touchSimulationEnabled,
       viewport,
     } = this.props;
 
     return (
       dom.header(
         {
           id: "toolbar",
           className: leftAlignmentEnabled ? "left-aligned" : "",
@@ -116,20 +116,20 @@ class Toolbar extends PureComponent {
             networkThrottling,
             onChangeNetworkThrottling,
             useTopLevelWindow: true,
           }),
           dom.div({ className: "devtools-separator" }),
           dom.button({
             id: "touch-simulation-button",
             className: "devtools-button" +
-                       (touchSimulation.enabled ? " checked" : ""),
-            title: (touchSimulation.enabled ?
+                       (touchSimulationEnabled ? " checked" : ""),
+            title: (touchSimulationEnabled ?
               getStr("responsive.disableTouch") : getStr("responsive.enableTouch")),
-            onClick: () => onChangeTouchSimulation(!touchSimulation.enabled),
+            onClick: () => onChangeTouchSimulation(!touchSimulationEnabled),
           })
         ),
         dom.div(
           { id: "toolbar-end-controls" },
           dom.button({
             id: "screenshot-button",
             className: "devtools-button",
             title: getStr("responsive.screenshot"),
@@ -151,13 +151,15 @@ class Toolbar extends PureComponent {
         )
       )
     );
   }
 }
 
 const mapStateToProps = state => {
   return {
+    displayPixelRatio: state.ui.displayPixelRatio,
     leftAlignmentEnabled: state.ui.leftAlignmentEnabled,
+    touchSimulationEnabled: state.ui.touchSimulationEnabled,
   };
 };
 
 module.exports = connect(mapStateToProps)(Toolbar);
--- a/devtools/client/responsive.html/components/ViewportDimension.js
+++ b/devtools/client/responsive.html/components/ViewportDimension.js
@@ -1,23 +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/. */
 
 "use strict";
 
-const { Component } = require("devtools/client/shared/vendor/react");
+const { PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const { isKeyIn } = require("../utils/key");
 const { MIN_VIEWPORT_DIMENSION } = require("../constants");
 const Types = require("../types");
 
-class ViewportDimension extends Component {
+class ViewportDimension extends PureComponent {
   static get propTypes() {
     return {
       onResizeViewport: PropTypes.func.isRequired,
       onRemoveDeviceAssociation: PropTypes.func.isRequired,
       viewport: PropTypes.shape(Types.viewport).isRequired,
     };
   }
 
--- a/devtools/client/responsive.html/components/Viewports.js
+++ b/devtools/client/responsive.html/components/Viewports.js
@@ -1,24 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { connect } = require("devtools/client/shared/vendor/react-redux");
-const { Component, createFactory } = require("devtools/client/shared/vendor/react");
+const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const ResizableViewport = createFactory(require("./ResizableViewport"));
 
 const Types = require("../types");
 
-class Viewports extends Component {
+class Viewports extends PureComponent {
   static get propTypes() {
     return {
       leftAlignmentEnabled: PropTypes.bool.isRequired,
       onBrowserMounted: PropTypes.func.isRequired,
       onContentResize: PropTypes.func.isRequired,
       onRemoveDeviceAssociation: PropTypes.func.isRequired,
       onResizeViewport: PropTypes.func.isRequired,
       screenshot: PropTypes.shape(Types.screenshot).isRequired,
--- a/devtools/client/responsive.html/index.js
+++ b/devtools/client/responsive.html/index.js
@@ -18,20 +18,19 @@ const { createFactory, createElement } =
   require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
 const message = require("./utils/message");
 const App = createFactory(require("./components/App"));
 const Store = require("./store");
 const { loadDevices } = require("./actions/devices");
-const { changeDisplayPixelRatio } = require("./actions/display-pixel-ratio");
-const { changeLocation } = require("./actions/location");
 const { loadReloadConditions } = require("./actions/reload-conditions");
 const { addViewport, resizeViewport } = require("./actions/viewports");
+const { changeDisplayPixelRatio } = require("./actions/ui");
 
 // Exposed for use by tests
 window.require = require;
 
 const bootstrap = {
 
   telemetry: new Telemetry(),
 
@@ -112,17 +111,16 @@ function onDevicePixelRatioChange() {
 }
 
 /**
  * Called by manager.js to add the initial viewport based on the original page.
  */
 window.addInitialViewport = ({ uri, userContextId }) => {
   try {
     onDevicePixelRatioChange();
-    bootstrap.dispatch(changeLocation(uri));
     bootstrap.dispatch(changeDisplayPixelRatio(window.devicePixelRatio));
     bootstrap.dispatch(addViewport(userContextId));
   } catch (e) {
     console.error(e);
   }
 };
 
 /**
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -486,18 +486,17 @@ ResponsiveUI.prototype = {
   },
 
   /**
    * Show one-time notification about reloads for emulation.
    */
   showReloadNotification() {
     if (Services.prefs.getBoolPref(RELOAD_NOTIFICATION_PREF, false)) {
       showNotification(this.browserWindow, this.tab, {
-        msg: l10n.getFormatStr("responsive.reloadNotification.description",
-                               l10n.getStr("responsive.reloadConditions.label")),
+        msg: l10n.getFormatStr("responsive.reloadNotification.description2"),
       });
       Services.prefs.setBoolPref(RELOAD_NOTIFICATION_PREF, false);
     }
   },
 
   reloadOnChange(id) {
     this.showReloadNotification();
     const pref = RELOAD_CONDITION_PREF_PREFIX + id;
@@ -576,17 +575,17 @@ ResponsiveUI.prototype = {
   onChangePixelRatio(event) {
     const { pixelRatio } = event.data;
     this.updateDPPX(pixelRatio);
   },
 
   async onChangeTouchSimulation(event) {
     const { enabled } = event.data;
     const reloadNeeded = await this.updateTouchSimulation(enabled) &&
-                       this.reloadOnChange("touchSimulation");
+                         this.reloadOnChange("touchSimulation");
     if (reloadNeeded) {
       this.getViewportBrowser().reload();
     }
     // Used by tests
     this.emit("touch-simulation-changed");
   },
 
   onContentResize(event) {
--- a/devtools/client/responsive.html/reducers.js
+++ b/devtools/client/responsive.html/reducers.js
@@ -1,15 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 exports.devices = require("./reducers/devices");
-exports.displayPixelRatio = require("./reducers/display-pixel-ratio");
-exports.location = require("./reducers/location");
 exports.networkThrottling = require("devtools/client/shared/components/throttling/reducer");
 exports.reloadConditions = require("./reducers/reload-conditions");
 exports.screenshot = require("./reducers/screenshot");
-exports.touchSimulation = require("./reducers/touch-simulation");
 exports.ui = require("./reducers/ui");
 exports.viewports = require("./reducers/viewports");
--- a/devtools/client/responsive.html/reducers/devices.js
+++ b/devtools/client/responsive.html/reducers/devices.js
@@ -13,20 +13,20 @@ const {
   REMOVE_DEVICE,
   UPDATE_DEVICE_DISPLAYED,
   UPDATE_DEVICE_MODAL,
 } = require("../actions/index");
 
 const Types = require("../types");
 
 const INITIAL_DEVICES = {
+  isModalOpen: false,
+  listState: Types.loadableState.INITIALIZED,
+  modalOpenedFromViewport: null,
   types: [],
-  isModalOpen: false,
-  modalOpenedFromViewport: null,
-  listState: Types.loadableState.INITIALIZED,
 };
 
 const reducers = {
 
   [ADD_DEVICE](devices, { device, deviceType }) {
     return Object.assign({}, devices, {
       [deviceType]: [...devices[deviceType], device],
     });
deleted file mode 100644
--- a/devtools/client/responsive.html/reducers/display-pixel-ratio.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/* eslint-env browser */
-
-"use strict";
-
-const { CHANGE_DISPLAY_PIXEL_RATIO } = require("../actions/index");
-const INITIAL_DISPLAY_PIXEL_RATIO = 0;
-
-const reducers = {
-
-  [CHANGE_DISPLAY_PIXEL_RATIO](_, action) {
-    return action.displayPixelRatio;
-  },
-
-};
-
-module.exports = function(displayPixelRatio = INITIAL_DISPLAY_PIXEL_RATIO, action) {
-  const reducer = reducers[action.type];
-  if (!reducer) {
-    return displayPixelRatio;
-  }
-  return reducer(displayPixelRatio, action);
-};
deleted file mode 100644
--- a/devtools/client/responsive.html/reducers/location.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { CHANGE_LOCATION } = require("../actions/index");
-
-const INITIAL_LOCATION = "about:blank";
-
-const reducers = {
-
-  [CHANGE_LOCATION](_, action) {
-    return action.location;
-  },
-
-};
-
-module.exports = function(location = INITIAL_LOCATION, action) {
-  const reducer = reducers[action.type];
-  if (!reducer) {
-    return location;
-  }
-  return reducer(location, action);
-};
--- a/devtools/client/responsive.html/reducers/moz.build
+++ b/devtools/client/responsive.html/reducers/moz.build
@@ -1,16 +1,13 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'devices.js',
-    'display-pixel-ratio.js',
-    'location.js',
     'reload-conditions.js',
     'screenshot.js',
-    'touch-simulation.js',
     'ui.js',
     'viewports.js',
 )
--- a/devtools/client/responsive.html/reducers/screenshot.js
+++ b/devtools/client/responsive.html/reducers/screenshot.js
@@ -4,17 +4,19 @@
 
 "use strict";
 
 const {
   TAKE_SCREENSHOT_END,
   TAKE_SCREENSHOT_START,
 } = require("../actions/index");
 
-const INITIAL_SCREENSHOT = { isCapturing: false };
+const INITIAL_SCREENSHOT = {
+  isCapturing: false,
+};
 
 const reducers = {
 
   [TAKE_SCREENSHOT_END](screenshot, action) {
     return Object.assign({}, screenshot, { isCapturing: false });
   },
 
   [TAKE_SCREENSHOT_START](screenshot, action) {
deleted file mode 100644
--- a/devtools/client/responsive.html/reducers/touch-simulation.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {
-  CHANGE_TOUCH_SIMULATION,
-} = require("../actions/index");
-
-const INITIAL_TOUCH_SIMULATION = {
-  enabled: false,
-};
-
-const reducers = {
-
-  [CHANGE_TOUCH_SIMULATION](touchSimulation, { enabled }) {
-    return Object.assign({}, touchSimulation, {
-      enabled,
-    });
-  },
-
-};
-
-module.exports = function(touchSimulation = INITIAL_TOUCH_SIMULATION, action) {
-  const reducer = reducers[action.type];
-  if (!reducer) {
-    return touchSimulation;
-  }
-  return reducer(touchSimulation, action);
-};
--- a/devtools/client/responsive.html/reducers/ui.js
+++ b/devtools/client/responsive.html/reducers/ui.js
@@ -2,39 +2,57 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 
 const {
+  CHANGE_DISPLAY_PIXEL_RATIO,
   TOGGLE_LEFT_ALIGNMENT,
+  TOGGLE_TOUCH_SIMULATION,
 } = require("../actions/index");
 
 const LEFT_ALIGNMENT_ENABLED = "devtools.responsive.leftAlignViewport.enabled";
 
 const INITIAL_UI = {
+  // The pixel ratio of the display.
+  displayPixelRatio: 0,
   // Whether or not the viewports are left aligned.
   leftAlignmentEnabled: Services.prefs.getBoolPref(LEFT_ALIGNMENT_ENABLED),
+  // Whether or not touch simulation is enabled.
+  touchSimulationEnabled: false,
 };
 
 const reducers = {
 
+  [CHANGE_DISPLAY_PIXEL_RATIO](ui, { displayPixelRatio }) {
+    return Object.assign({}, ui, {
+      displayPixelRatio,
+    });
+  },
+
   [TOGGLE_LEFT_ALIGNMENT](ui, { enabled }) {
     const leftAlignmentEnabled = enabled !== undefined ?
       enabled : !ui.leftAlignmentEnabled;
 
     Services.prefs.setBoolPref(LEFT_ALIGNMENT_ENABLED, leftAlignmentEnabled);
 
     return Object.assign({}, ui, {
       leftAlignmentEnabled,
     });
   },
 
+  [TOGGLE_TOUCH_SIMULATION](ui, { enabled }) {
+    return Object.assign({}, ui, {
+      touchSimulationEnabled: enabled,
+    });
+  },
+
 };
 
 module.exports = function(ui = INITIAL_UI, action) {
   const reducer = reducers[action.type];
   if (!reducer) {
     return ui;
   }
   return reducer(ui, action);
--- a/devtools/client/responsive.html/reducers/viewports.js
+++ b/devtools/client/responsive.html/reducers/viewports.js
@@ -17,19 +17,17 @@ let nextViewportId = 0;
 
 const INITIAL_VIEWPORTS = [];
 const INITIAL_VIEWPORT = {
   id: nextViewportId++,
   device: "",
   deviceType: "",
   width: 320,
   height: 480,
-  pixelRatio: {
-    value: 0,
-  },
+  pixelRatio: 0,
   userContextId: 0,
 };
 
 const reducers = {
 
   [ADD_VIEWPORT](viewports, { userContextId }) {
     // For the moment, there can be at most one viewport.
     if (viewports.length === 1) {
@@ -55,19 +53,17 @@ const reducers = {
 
   [CHANGE_PIXEL_RATIO](viewports, { id, pixelRatio }) {
     return viewports.map(viewport => {
       if (viewport.id !== id) {
         return viewport;
       }
 
       return Object.assign({}, viewport, {
-        pixelRatio: {
-          value: pixelRatio
-        },
+        pixelRatio,
       });
     });
   },
 
   [REMOVE_DEVICE_ASSOCIATION](viewports, { id }) {
     return viewports.map(viewport => {
       if (viewport.id !== id) {
         return viewport;
--- a/devtools/client/responsive.html/test/unit/test_change_display_pixel_ratio.js
+++ b/devtools/client/responsive.html/test/unit/test_change_display_pixel_ratio.js
@@ -1,22 +1,22 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test changing the display pixel ratio.
 
-const { changeDisplayPixelRatio } =
-  require("devtools/client/responsive.html/actions/display-pixel-ratio");
+const { changeDisplayPixelRatio } = require("devtools/client/responsive.html/actions/ui");
+
 const NEW_PIXEL_RATIO = 5.5;
 
 add_task(async function() {
   const store = Store();
   const { getState, dispatch } = store;
 
-  equal(getState().displayPixelRatio, 0,
+  equal(getState().ui.displayPixelRatio, 0,
         "Defaults to 0 at startup");
 
   dispatch(changeDisplayPixelRatio(NEW_PIXEL_RATIO));
-  equal(getState().displayPixelRatio, NEW_PIXEL_RATIO,
+  equal(getState().ui.displayPixelRatio, NEW_PIXEL_RATIO,
     `Display Pixel Ratio changed to ${NEW_PIXEL_RATIO}`);
 });
deleted file mode 100644
--- a/devtools/client/responsive.html/test/unit/test_change_location.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// Test changing the location of the displayed page.
-
-const { changeLocation } =
-  require("devtools/client/responsive.html/actions/location");
-
-const TEST_URL = "http://example.com";
-
-add_task(async function() {
-  const store = Store();
-  const { getState, dispatch } = store;
-
-  equal(getState().location, "about:blank",
-        "Defaults to about:blank at startup");
-
-  dispatch(changeLocation(TEST_URL));
-  equal(getState().location, TEST_URL, "Location changed to TEST_URL");
-});
--- a/devtools/client/responsive.html/test/unit/test_change_pixel_ratio.js
+++ b/devtools/client/responsive.html/test/unit/test_change_pixel_ratio.js
@@ -12,11 +12,11 @@ const NEW_PIXEL_RATIO = 5.5;
 add_task(async function() {
   const store = Store();
   const { getState, dispatch } = store;
 
   dispatch(addViewport());
   dispatch(changePixelRatio(0, NEW_PIXEL_RATIO));
 
   const viewport = getState().viewports[0];
-  equal(viewport.pixelRatio.value, NEW_PIXEL_RATIO,
+  equal(viewport.pixelRatio, NEW_PIXEL_RATIO,
     `Viewport's pixel ratio changed to ${NEW_PIXEL_RATIO}`);
 });
--- a/devtools/client/responsive.html/test/unit/test_update_touch_simulation_enabled.js
+++ b/devtools/client/responsive.html/test/unit/test_update_touch_simulation_enabled.js
@@ -1,23 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test updating the touch simulation `enabled` property
 
-const {
-  changeTouchSimulation,
-} = require("devtools/client/responsive.html/actions/touch-simulation");
+const { toggleTouchSimulation } = require("devtools/client/responsive.html/actions/ui");
 
 add_task(async function() {
   const store = Store();
   const { getState, dispatch } = store;
 
-  ok(!getState().touchSimulation.enabled,
+  ok(!getState().ui.touchSimulationEnabled,
     "Touch simulation is disabled by default.");
 
-  dispatch(changeTouchSimulation(true));
+  dispatch(toggleTouchSimulation(true));
 
-  ok(getState().touchSimulation.enabled,
+  ok(getState().ui.touchSimulationEnabled,
     "Touch simulation is enabled.");
 });
--- a/devtools/client/responsive.html/test/unit/xpcshell.ini
+++ b/devtools/client/responsive.html/test/unit/xpcshell.ini
@@ -3,15 +3,14 @@ tags = devtools
 head = head.js ../../../shared/test/shared-redux-head.js
 firefox-appdir = browser
 
 [test_add_device.js]
 [test_add_device_type.js]
 [test_add_viewport.js]
 [test_change_device.js]
 [test_change_display_pixel_ratio.js]
-[test_change_location.js]
 [test_change_network_throttling.js]
 [test_change_pixel_ratio.js]
 [test_resize_viewport.js]
 [test_rotate_viewport.js]
 [test_update_device_displayed.js]
 [test_update_touch_simulation_enabled.js]
--- a/devtools/client/responsive.html/types.js
+++ b/devtools/client/responsive.html/types.js
@@ -20,21 +20,16 @@ exports.loadableState = createEnum([
   "LOADING",
   "LOADED",
   "ERROR",
 ]);
 
 /* GLOBAL */
 
 /**
- * The location of the document displayed in the viewport(s).
- */
-exports.location = PropTypes.string;
-
-/**
  * Whether to reload the page automatically when certain actions occur.
  */
 exports.reloadConditions = {
 
   // Whether to reload when touch simulation is toggled
   touchSimulation: PropTypes.bool,
 
   // Whether to reload when user agent is changed
@@ -126,26 +121,16 @@ exports.networkThrottling = {
   enabled: PropTypes.bool,
 
   // Name of the selected throttling profile
   profile: PropTypes.string,
 
 };
 
 /**
- * Device pixel ratio for a given viewport.
- */
-const pixelRatio = exports.pixelRatio = {
-
-  // The device pixel ratio value
-  value: PropTypes.number,
-
-};
-
-/**
  * Touch simulation state for a given viewport.
  */
 exports.touchSimulation = {
 
   // Whether or not touch simulation is enabled
   enabled: PropTypes.bool,
 
 };
@@ -166,17 +151,17 @@ exports.viewport = {
 
   // The width of the viewport
   width: PropTypes.number,
 
   // The height of the viewport
   height: PropTypes.number,
 
   // The device pixel ratio of the viewport
-  pixelRatio: PropTypes.shape(pixelRatio),
+  pixelRatio: PropTypes.number,
 
   // The user context (container) ID for the viewport
   // Defaults to 0 meaning the default context
   userContextId: PropTypes.number,
 
 };
 
 /* ACTIONS IN PROGRESS */
--- a/devtools/client/webaudioeditor/test/doc_bug_1112378.html
+++ b/devtools/client/webaudioeditor/test/doc_bug_1112378.html
@@ -9,37 +9,37 @@
   </head>
 
   <body>
 
     <script type="text/javascript">
       "use strict";
 
       const ctx = new AudioContext();
-      const osc = ctx.createStereoPanner();
+      const panner = ctx.createStereoPanner();
 
       function throwError() {
         try {
-          osc.connect({});
+          panner.connect({});
         } catch (e) {
           return {
             lineNumber: e.lineNumber,
             fileName: e.fileName,
             columnNumber: e.columnNumber,
             message: e.message,
             instanceof: e instanceof TypeError,
             stringified: e.toString(),
             name: e.name
           };
         }
       }
 
       function throwDOMException() {
         try {
-          osc.channelCount = 3;
+          panner.channelCount = 3;
         } catch (e) {
           return {
             lineNumber: e.lineNumber,
             columnNumber: e.columnNumber,
             filename: e.filename,
             message: e.message,
             code: e.code,
             result: e.result,
--- a/devtools/server/tests/unit/test_objectgrips-21.js
+++ b/devtools/server/tests/unit/test_objectgrips-21.js
@@ -173,18 +173,18 @@ const nullPrincipalTests = [{
     var obj = new Proxy([], new Proxy({}, {get: (_, trap) => {
       trapDidRun = true;
       throw new Error("proxy trap '" + trap + "' was called.");
     }}));
   `,
   property: descriptor({value: "Error"}),
   afterTest: `trapDidRun === false`,
 }, {
-  // Like the previous test, but now the object has a Function class.
-  string: "[object Function]",
+  // Like the previous test, but now the object is a callable Proxy.
+  string: "function () {\n    [native code]\n}",
   isFunction: true,
   hasPreview: false,
   code: `
     var trapDidRun = false;
     var obj = new Proxy(function(){}, new Proxy({}, {get: (_, trap) => {
       trapDidRun = true;
       throw new Error("proxy trap '" + trap + "' was called.");
     }}));
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -140,17 +140,17 @@
 #include "nsIWebBrowserChromeFocus.h"
 #include "nsIWebBrowserFind.h"
 #include "nsIWebProgress.h"
 #include "nsIWidget.h"
 #include "nsIWindowWatcher.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsIWyciwygChannel.h"
 
-#include "nsPICommandUpdater.h"
+#include "nsCommandManager.h"
 #include "nsPIDOMWindow.h"
 #include "nsPILoadGroupInternal.h"
 #include "nsPIWindowRoot.h"
 
 #include "IHistory.h"
 #include "IUrlClassifierUITelemetry.h"
 
 #include "mozIThirdPartyUtil.h"
@@ -1546,24 +1546,64 @@ NS_IMETHODIMP
 nsDocShell::GetHasTrackingContentBlocked(bool* aHasTrackingContentBlocked)
 {
   nsCOMPtr<nsIDocument> doc(GetDocument());
   *aHasTrackingContentBlocked = doc && doc->GetHasTrackingContentBlocked();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShell::GetHasSlowTrackingContentBlocked(bool* aHasSlowTrackingContentBlocked)
+{
+  nsCOMPtr<nsIDocument> doc(GetDocument());
+  *aHasSlowTrackingContentBlocked = doc && doc->GetHasSlowTrackingContentBlocked();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::GetHasTrackingContentLoaded(bool* aHasTrackingContentLoaded)
 {
   nsCOMPtr<nsIDocument> doc(GetDocument());
   *aHasTrackingContentLoaded = doc && doc->GetHasTrackingContentLoaded();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShell::GetHasCookiesBlockedByPermission(bool* aHasCookiesBlockedByPermission)
+{
+  nsCOMPtr<nsIDocument> doc(GetDocument());
+  *aHasCookiesBlockedByPermission = doc && doc->GetHasCookiesBlockedByPermission();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasCookiesBlockedDueToTrackers(bool* aHasCookiesBlockedDueToTrackers)
+{
+  nsCOMPtr<nsIDocument> doc(GetDocument());
+  *aHasCookiesBlockedDueToTrackers = doc && doc->GetHasTrackingCookiesBlocked();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasAllCookiesBeenBlocked(bool* aHasAllCookiesBeenBlocked)
+{
+  nsCOMPtr<nsIDocument> doc(GetDocument());
+  *aHasAllCookiesBeenBlocked = doc && doc->GetHasAllCookiesBlocked();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetHasForeignCookiesBeenBlocked(bool* aHasForeignCookiesBeenBlocked)
+{
+  nsCOMPtr<nsIDocument> doc(GetDocument());
+  *aHasForeignCookiesBeenBlocked = doc && doc->GetHasForeignCookiesBlocked();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::GetAllowPlugins(bool* aAllowPlugins)
 {
   NS_ENSURE_ARG_POINTER(aAllowPlugins);
 
   *aAllowPlugins = mAllowPlugins;
   return NS_OK;
 }
 
@@ -13240,21 +13280,17 @@ nsDocShell::DoCommandWithParams(const ch
 
   return commandController->DoCommandWithParams(aCommand, aParams);
 }
 
 nsresult
 nsDocShell::EnsureCommandHandler()
 {
   if (!mCommandManager) {
-    nsCOMPtr<nsPICommandUpdater> commandUpdater =
-      do_CreateInstance("@mozilla.org/embedcomp/command-manager;1");
-    if (!commandUpdater) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
+    nsCOMPtr<nsPICommandUpdater> commandUpdater = new nsCommandManager();
 
     nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow();
     nsresult rv = commandUpdater->Init(domWindow);
     if (NS_SUCCEEDED(rv)) {
       mCommandManager = do_QueryInterface(commandUpdater);
     }
   }
 
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -620,16 +620,46 @@ interface nsIDocShell : nsIDocShellTreeI
 
    /**
    * This attribute determines whether Tracking Content is loaded on the
    * document. When it is true, tracking content was not blocked and has
    * loaded (or is about to load) on the page.
    */
   [infallible] readonly attribute boolean hasTrackingContentLoaded;
 
+   /**
+   * This attribute determines whether a document has Slow Tracking Content
+   * that has been blocked from loading.
+   */
+   [infallible] readonly attribute boolean hasSlowTrackingContentBlocked;
+
+   /**
+   * This attribute determines whether a document seen cookies or storage
+   * blocked due to a site permission being denied.
+   */
+   [infallible] readonly attribute boolean hasCookiesBlockedByPermission;
+
+   /**
+   * This attribute determines whether a document seen cookies or storage
+   * blocked due to a the request being made by a tracker.
+   */
+   [infallible] readonly attribute boolean hasCookiesBlockedDueToTrackers;
+
+   /**
+   * This attribute determines whether a document seen cookies or storage
+   * blocked due to cookie behavior settings blocking all cookies.
+   */
+   [infallible] readonly attribute boolean hasAllCookiesBeenBlocked;
+
+   /**
+   * This attribute determines whether a document seen cookies or storage
+   * blocked due to cookie behavior settings blocking all third-party cookies.
+   */
+   [infallible] readonly attribute boolean hasForeignCookiesBeenBlocked;
+
   /**
    * Disconnects this docshell's editor from its window, and stores the
    * editor data in the open document's session history entry.  This
    * should be called only during page transitions.
    */
   [noscript, notxpcom] void DetachEditorFromWindow();
 
   /**
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -2039,33 +2039,64 @@ Element::AttrValueIs(int32_t aNameSpaceI
 
   const nsAttrValue* val = mAttrs.GetAttr(aName, aNameSpaceID);
   return val && val->Equals(aValue, aCaseSensitive);
 }
 
 } // namespace dom
 } // namespace mozilla
 
-inline mozilla::dom::Element* nsINode::AsElement()
+inline mozilla::dom::Element*
+nsINode::AsElement()
 {
   MOZ_ASSERT(IsElement());
   return static_cast<mozilla::dom::Element*>(this);
 }
 
-inline const mozilla::dom::Element* nsINode::AsElement() const
+inline const mozilla::dom::Element*
+nsINode::AsElement() const
 {
   MOZ_ASSERT(IsElement());
   return static_cast<const mozilla::dom::Element*>(this);
 }
 
-inline mozilla::dom::Element* nsINode::GetParentElement() const
+inline mozilla::dom::Element*
+nsINode::GetParentElement() const
 {
   return mozilla::dom::Element::FromNodeOrNull(mParent);
 }
 
+inline mozilla::dom::Element*
+nsINode::GetPreviousElementSibling() const
+{
+  nsIContent* previousSibling = GetPreviousSibling();
+  while (previousSibling) {
+    if (previousSibling->IsElement()) {
+      return previousSibling->AsElement();
+    }
+    previousSibling = previousSibling->GetPreviousSibling();
+  }
+
+  return nullptr;
+}
+
+inline mozilla::dom::Element*
+nsINode::GetNextElementSibling() const
+{
+  nsIContent* nextSibling = GetNextSibling();
+  while (nextSibling) {
+    if (nextSibling->IsElement()) {
+      return nextSibling->AsElement();
+    }
+    nextSibling = nextSibling->GetNextSibling();
+  }
+
+  return nullptr;
+}
+
 /**
  * Macros to implement Clone(). _elementName is the class for which to implement
  * Clone.
  */
 #define NS_IMPL_ELEMENT_CLONE(_elementName)                                 \
 nsresult                                                                    \
 _elementName::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const \
 {                                                                           \
--- a/dom/base/Link.cpp
+++ b/dom/base/Link.cpp
@@ -4,20 +4,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Link.h"
 
 #include "mozilla/EventStates.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Element.h"
-#ifdef ANDROID
+#if defined(MOZ_PLACES)
+#include "mozilla/places/History.h"
+#else
 #include "mozilla/IHistory.h"
-#else
-#include "mozilla/places/History.h"
 #endif
 #include "nsIURL.h"
 #include "nsIURIMutator.h"
 #include "nsISizeOf.h"
 #include "nsIDocShell.h"
 #include "nsIPrefetchService.h"
 #include "nsCPrefetchService.h"
 #include "nsStyleLinkElement.h"
@@ -30,17 +30,17 @@
 
 #include "mozilla/Services.h"
 #include "nsAttrValueInlines.h"
 #include "HTMLLinkElement.h"
 
 namespace mozilla {
 namespace dom {
 
-#ifndef ANDROID
+#if defined(MOZ_PLACES)
 using places::History;
 #endif
 
 Link::Link(Element *aElement)
   : mElement(aElement)
   , mLinkState(eLinkState_NotLink)
   , mNeedsRegistration(false)
   , mRegistered(false)
@@ -376,18 +376,20 @@ Link::LinkState() const
     // Assume that we are not visited until we are told otherwise.
     self->mLinkState = eLinkState_Unvisited;
 
     // Make sure the href attribute has a valid link (bug 23209).
     // If we have a good href, register with History if available.
     if (mHistory && hrefURI) {
 #ifdef ANDROID
       nsCOMPtr<IHistory> history = services::GetHistoryService();
+#elif defined(MOZ_PLACES)
+      History* history = History::GetService();
 #else
-      History* history = History::GetService();
+      nsCOMPtr<IHistory> history;
 #endif
       if (history) {
         nsresult rv = history->RegisterVisitedCallback(hrefURI, self);
         if (NS_SUCCEEDED(rv)) {
           self->mRegistered = true;
 
           // And make sure we are in the document's link map.
           element->GetComposedDoc()->AddStyleRelevantLink(self);
@@ -841,18 +843,20 @@ Link::UnregisterFromHistory()
   if (!mRegistered) {
     return;
   }
 
   // And tell History to stop tracking us.
   if (mHistory && mCachedURI) {
 #ifdef ANDROID
     nsCOMPtr<IHistory> history = services::GetHistoryService();
+#elif defined(MOZ_PLACES)
+    History* history = History::GetService();
 #else
-    History* history = History::GetService();
+    nsCOMPtr<IHistory> history;
 #endif
     if (history) {
       nsresult rv = history->UnregisterVisitedCallback(mCachedURI, this);
       NS_ASSERTION(NS_SUCCEEDED(rv), "This should only fail if we misuse the API!");
       if (NS_SUCCEEDED(rv)) {
         mRegistered = false;
       }
     }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1410,16 +1410,21 @@ nsIDocument::nsIDocument()
     mHasMixedActiveContentBlocked(false),
     mHasMixedDisplayContentLoaded(false),
     mHasMixedDisplayContentBlocked(false),
     mHasMixedContentObjectSubrequest(false),
     mHasCSP(false),
     mHasUnsafeEvalCSP(false),
     mHasUnsafeInlineCSP(false),
     mHasTrackingContentBlocked(false),
+    mHasSlowTrackingContentBlocked(false),
+    mHasAllCookiesBlocked(false),
+    mHasTrackingCookiesBlocked(false),
+    mHasForeignCookiesBlocked(false),
+    mHasCookiesBlockedByPermission(false),
     mHasTrackingContentLoaded(false),
     mBFCacheDisallowed(false),
     mHasHadDefaultView(false),
     mStyleSheetChangeEventsEnabled(false),
     mIsSrcdocDocument(false),
     mDidDocumentOpen(false),
     mHasDisplayDocument(false),
     mFontFaceSetDirty(true),
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -5286,18 +5286,26 @@ nsGlobalWindowOuter::NotifyContentBlocki
   nsCOMPtr<nsISecureBrowserUI> securityUI;
   docShell->GetSecurityUI(getter_AddRefs(securityUI));
   if (!securityUI) {
     return;
   }
   securityUI->GetState(&state);
   if (aState == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
     doc->SetHasTrackingContentBlocked(true);
+  } else if (aState == nsIWebProgressListener::STATE_BLOCKED_SLOW_TRACKING_CONTENT) {
+    doc->SetHasSlowTrackingContentBlocked(true);
+  } else if (aState == nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION) {
+    doc->SetHasCookiesBlockedByPermission(true);
   } else if (aState == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
     doc->SetHasTrackingCookiesBlocked(true);
+  } else if (aState == nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL) {
+    doc->SetHasAllCookiesBlocked(true);
+  } else if (aState == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
+    doc->SetHasForeignCookiesBlocked(true);
   } else {
     // Ignore nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
   }
   state |= aState;
 
   eventSink->OnSecurityChange(aChannel, state);
 }
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -973,40 +973,104 @@ public:
    * Get tracking content blocked flag for this document.
    */
   bool GetHasTrackingContentBlocked()
   {
     return mHasTrackingContentBlocked;
   }
 
   /**
+   * Get slow tracking content blocked flag for this document.
+   */
+  bool GetHasSlowTrackingContentBlocked()
+  {
+    return mHasSlowTrackingContentBlocked;
+  }
+
+  /**
+   * Get all cookies blocked flag for this document.
+   */
+  bool GetHasAllCookiesBlocked()
+  {
+    return mHasAllCookiesBlocked;
+  }
+
+  /**
    * Get tracking cookies blocked flag for this document.
    */
   bool GetHasTrackingCookiesBlocked()
   {
     return mHasTrackingCookiesBlocked;
   }
 
   /**
+   * Get third-party cookies blocked flag for this document.
+   */
+  bool GetHasForeignCookiesBlocked()
+  {
+    return mHasForeignCookiesBlocked;
+  }
+
+  /**
+   * Get cookies blocked by site permission flag for this document.
+   */
+  bool GetHasCookiesBlockedByPermission()
+  {
+    return mHasCookiesBlockedByPermission;
+  }
+
+  /**
    * Set the tracking content blocked flag for this document.
    */
   void SetHasTrackingContentBlocked(bool aHasTrackingContentBlocked)
   {
     mHasTrackingContentBlocked = aHasTrackingContentBlocked;
   }
 
   /**
+   * Set the slow tracking content blocked flag for this document.
+   */
+  void SetHasSlowTrackingContentBlocked(bool aHasSlowTrackingContentBlocked)
+  {
+    mHasSlowTrackingContentBlocked = aHasSlowTrackingContentBlocked;
+  }
+
+  /**
+   * Set the all cookies blocked flag for this document.
+   */
+  void SetHasAllCookiesBlocked(bool aHasAllCookiesBlocked)
+  {
+    mHasAllCookiesBlocked = aHasAllCookiesBlocked;
+  }
+
+  /**
    * Set the tracking cookies blocked flag for this document.
    */
   void SetHasTrackingCookiesBlocked(bool aHasTrackingCookiesBlocked)
   {
     mHasTrackingCookiesBlocked = aHasTrackingCookiesBlocked;
   }
 
   /**
+   * Set the third-party cookies blocked flag for this document.
+   */
+  void SetHasForeignCookiesBlocked(bool aHasForeignCookiesBlocked)
+  {
+    mHasForeignCookiesBlocked = aHasForeignCookiesBlocked;
+  }
+
+  /**
+   * Set the cookies blocked by site permission flag for this document.
+   */
+  void SetHasCookiesBlockedByPermission(bool aHasCookiesBlockedByPermission)
+  {
+    mHasCookiesBlockedByPermission = aHasCookiesBlockedByPermission;
+  }
+
+  /**
    * Get tracking content loaded flag for this document.
    */
   bool GetHasTrackingContentLoaded()
   {
     return mHasTrackingContentLoaded;
   }
 
   /**
@@ -3998,19 +4062,31 @@ protected:
   bool mHasUnsafeEvalCSP : 1;
 
   // True if a document load has a CSP with unsafe-inline attached.
   bool mHasUnsafeInlineCSP : 1;
 
   // True if a document has blocked Tracking Content
   bool mHasTrackingContentBlocked : 1;
 
+  // True if a document has blocked Slow Tracking Content
+  bool mHasSlowTrackingContentBlocked : 1;
+
+  // True if a document has blocked All Cookies
+  bool mHasAllCookiesBlocked : 1;
+
   // True if a document has blocked Tracking Cookies
   bool mHasTrackingCookiesBlocked : 1;
 
+  // True if a document has blocked Foreign Cookies
+  bool mHasForeignCookiesBlocked : 1;
+
+  // True if a document has blocked Cookies By Site Permission
+  bool mHasCookiesBlockedByPermission : 1;
+
   // True if a document has loaded Tracking Content
   bool mHasTrackingContentLoaded : 1;
 
   // True if DisallowBFCaching has been called on this document.
   bool mBFCacheDisallowed : 1;
 
   bool mHasHadDefaultView : 1;
 
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -1566,44 +1566,16 @@ nsINode::ComputeIndexOf(const nsINode* a
     }
     current = current->GetNextSibling();
     ++index;
   }
 
   return -1;
 }
 
-Element*
-nsINode::GetPreviousElementSibling() const
-{
-  nsIContent* previousSibling = GetPreviousSibling();
-  while (previousSibling) {
-    if (previousSibling->IsElement()) {
-      return previousSibling->AsElement();
-    }
-    previousSibling = previousSibling->GetPreviousSibling();
-  }
-
-  return nullptr;
-}
-
-Element*
-nsINode::GetNextElementSibling() const
-{
-  nsIContent* nextSibling = GetNextSibling();
-  while (nextSibling) {
-    if (nextSibling->IsElement()) {
-      return nextSibling->AsElement();
-    }
-    nextSibling = nextSibling->GetNextSibling();
-  }
-
-  return nullptr;
-}
-
 static already_AddRefed<nsINode>
 GetNodeFromNodeOrString(const OwningNodeOrString& aNode,
                         nsIDocument* aDocument)
 {
   if (aNode.IsNode()) {
     nsCOMPtr<nsINode> node = aNode.GetAsNode();
     return node.forget();
   }
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -1831,18 +1831,18 @@ public:
   {
     nsINode* parent = GetParentNode();
     mozilla::ErrorResult rv;
     parent->RemoveChild(*this, rv);
     return rv.StealNSResult();
   }
 
   // ChildNode methods
-  mozilla::dom::Element* GetPreviousElementSibling() const;
-  mozilla::dom::Element* GetNextElementSibling() const;
+  inline mozilla::dom::Element* GetPreviousElementSibling() const;
+  inline mozilla::dom::Element* GetNextElementSibling() const;
 
   MOZ_CAN_RUN_SCRIPT void Before(const Sequence<OwningNodeOrString>& aNodes,
                                  ErrorResult& aRv);
   MOZ_CAN_RUN_SCRIPT void After(const Sequence<OwningNodeOrString>& aNodes,
                                 ErrorResult& aRv);
   MOZ_CAN_RUN_SCRIPT void ReplaceWith(const Sequence<OwningNodeOrString>& aNodes,
                                       ErrorResult& aRv);
   /**
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -115,16 +115,21 @@ const size_t gStackSize = 8192;
 #define NS_INTERSLICE_GC_DELAY      100 // ms
 
 // The amount of time we wait between a request to CC (after GC ran)
 // and doing the actual CC.
 #define NS_CC_DELAY                 6000 // ms
 
 #define NS_CC_SKIPPABLE_DELAY       250 // ms
 
+// In case the cycle collector isn't run at all, we don't want
+// forget skippables to run too often. So limit the forget skippable cycle to
+// start at earliest 2000 ms after the end of the previous cycle.
+#define NS_TIME_BETWEEN_FORGET_SKIPPABLE_CYCLES 2000 // ms
+
 // ForgetSkippable is usually fast, so we can use small budgets.
 // This isn't a real budget but a hint to IdleTaskRunner whether there
 // is enough time to call ForgetSkippable.
 static const int64_t kForgetSkippableSliceDuration = 2;
 
 // Maximum amount of time that should elapse between incremental CC slices
 static const int64_t kICCIntersliceDelay = 64; // ms
 
@@ -156,16 +161,18 @@ static nsITimer *sGCTimer;
 static nsITimer *sShrinkingGCTimer;
 static StaticRefPtr<IdleTaskRunner> sCCRunner;
 static StaticRefPtr<IdleTaskRunner> sICCRunner;
 static nsITimer *sFullGCTimer;
 static StaticRefPtr<IdleTaskRunner> sInterSliceGCRunner;
 
 static TimeStamp sLastCCEndTime;
 
+static TimeStamp sLastForgetSkippableCycleEndTime;
+
 static bool sCCLockedOut;
 static PRTime sCCLockedOutTime;
 
 static JS::GCSliceCallback sPrevGCSliceCallback;
 
 static bool sHasRunGC;
 
 // The number of currently pending document loads. This count isn't
@@ -2009,16 +2016,20 @@ CCRunnerFired(TimeStamp aDeadline)
 
   if (isLateTimerFire) {
     ccDelay = NS_CC_DELAY;
 
     // We have either just run the CC or decided we don't want to run the CC
     // next time, so kill the timer.
     sPreviousSuspectedCount = 0;
     nsJSContext::KillCCRunner();
+
+    if (!didDoWork) {
+      sLastForgetSkippableCycleEndTime = TimeStamp::Now();
+    }
   }
 
   return didDoWork;
 }
 
 // static
 uint32_t
 nsJSContext::CleanupsSinceLastGC()
@@ -2223,16 +2234,27 @@ nsJSContext::MaybePokeCC()
     return;
   }
 
   uint32_t sinceLastCCEnd = TimeUntilNow(sLastCCEndTime);
   if (sinceLastCCEnd && sinceLastCCEnd < NS_CC_DELAY) {
     return;
   }
 
+  // If GC hasn't run recently and forget skippable only cycle was run,
+  // don't start a new cycle too soon.
+  if (sCleanupsSinceLastGC > NS_MAJOR_FORGET_SKIPPABLE_CALLS) {
+    uint32_t sinceLastForgetSkippableCycle =
+      TimeUntilNow(sLastForgetSkippableCycleEndTime);
+    if (sinceLastForgetSkippableCycle &&
+        sinceLastForgetSkippableCycle < NS_TIME_BETWEEN_FORGET_SKIPPABLE_CYCLES) {
+      return;
+    }
+  }
+
   if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
     sCCRunnerFireCount = 0;
 
     // We can kill some objects before running forgetSkippable.
     nsCycleCollector_dispatchDeferredDeletion();
 
     sCCRunner =
       IdleTaskRunner::Create(CCRunnerFired,
@@ -2485,16 +2507,17 @@ nsJSContext::LikelyShortLivingObjectCrea
 void
 mozilla::dom::StartupJSEnvironment()
 {
   // initialize all our statics, so that we can restart XPCOM
   sGCTimer = sShrinkingGCTimer = sFullGCTimer = nullptr;
   sCCLockedOut = false;
   sCCLockedOutTime = 0;
   sLastCCEndTime = TimeStamp();
+  sLastForgetSkippableCycleEndTime = TimeStamp();
   sHasRunGC = false;
   sPendingLoadCount = 0;
   sLoadingInProgress = false;
   sCCollectedWaitingForGC = 0;
   sCCollectedZonesWaitingForGC = 0;
   sLikelyShortLivingObjectsNeedingGC = 0;
   sNeedsFullCC = false;
   sNeedsFullGC = true;
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -101,14 +101,14 @@ MSG_DEF(MSG_CACHE_STREAM_CLOSED, 0, JSEX
 MSG_DEF(MSG_TIME_VALUE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "{0} is outside the supported range for time values.")
 MSG_DEF(MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN, 1, JSEXN_TYPEERR, "Request mode '{0}' was used, but request cache mode 'only-if-cached' can only be used with request mode 'same-origin'.")
 MSG_DEF(MSG_THRESHOLD_RANGE_ERROR, 0, JSEXN_RANGEERR, "Threshold values must all be in the range [0, 1].")
 MSG_DEF(MSG_WORKER_THREAD_SHUTTING_DOWN, 0, JSEXN_TYPEERR, "The Worker thread is shutting down.")
 MSG_DEF(MSG_CACHE_OPEN_FAILED, 0, JSEXN_TYPEERR, "CacheStorage.open() failed to access the storage system.")
 MSG_DEF(MSG_MATRIX_INIT_LENGTH_WRONG, 1, JSEXN_TYPEERR, "Matrix init sequence must have a length of 6 or 16 (actual value: {0})")
 MSG_DEF(MSG_INVALID_MEDIA_VIDEO_CONFIGURATION, 0, JSEXN_TYPEERR, "Invalid VideoConfiguration.")
 MSG_DEF(MSG_INVALID_MEDIA_AUDIO_CONFIGURATION, 0, JSEXN_TYPEERR, "Invalid AudioConfiguration.")
-MSG_DEF(MSG_INVALID_CURVE_DURATION_ERROR, 0, JSEXN_RANGEERR, "The curve duration for SetValueCurve must be strictly positive.")
-MSG_DEF(MSG_INVALID_AUDIOPARAM_METHOD_START_TIME_ERROR, 0, JSEXN_RANGEERR, "The start time for an AudioParam method must be positive.")
-MSG_DEF(MSG_INVALID_AUDIOPARAM_METHOD_END_TIME_ERROR, 0, JSEXN_RANGEERR, "The end time for an AudioParam method must be positive.")
-MSG_DEF(MSG_INVALID_AUDIOPARAM_EXPONENTIAL_VALUE_ERROR, 0, JSEXN_RANGEERR, "The value passed to exponentialCurveToValueAtTime must be positive.")
-MSG_DEF(MSG_INVALID_AUDIOPARAM_EXPONENTIAL_CONSTANT_ERROR, 0, JSEXN_RANGEERR, "The exponential constant passed to setTargetAtTime must be positive.")
-MSG_DEF(MSG_VALUE_OUT_OF_RANGE, 1, JSEXN_RANGEERR, "The value for the {0} is outside the valid range.")
+MSG_DEF(MSG_INVALID_CURVE_DURATION_ERROR, 0, JSEXN_RANGEERR, "The curve duration for setValueCurveAtTime must be strictly positive.")
+MSG_DEF(MSG_INVALID_AUDIOPARAM_METHOD_START_TIME_ERROR, 0, JSEXN_RANGEERR, "The start time for an AudioParam method must be non-negative.")
+MSG_DEF(MSG_INVALID_AUDIOPARAM_METHOD_END_TIME_ERROR, 0, JSEXN_RANGEERR, "The end time for an AudioParam method must be non-negative.")
+MSG_DEF(MSG_INVALID_AUDIOPARAM_EXPONENTIAL_VALUE_ERROR, 0, JSEXN_RANGEERR, "The value passed to exponentialRampToValueAtTime must be positive.")
+MSG_DEF(MSG_INVALID_AUDIOPARAM_EXPONENTIAL_CONSTANT_ERROR, 0, JSEXN_RANGEERR, "The exponential constant passed to setTargetAtTime must be non-negative.")
+MSG_DEF(MSG_VALUE_OUT_OF_RANGE, 1, JSEXN_RANGEERR, "The value for the {0} is outside the valid range.")
\ No newline at end of file
--- a/dom/commandhandler/moz.build
+++ b/dom/commandhandler/moz.build
@@ -3,16 +3,18 @@
 # 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/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "Embedding: APIs")
 
 EXPORTS += [
+    'nsBaseCommandController.h',
+    'nsCommandManager.h',
     'nsCommandParams.h',
 ]
 
 XPIDL_SOURCES += [
     'nsICommandManager.idl',
     'nsICommandParams.idl',
     'nsIControllerCommand.idl',
     'nsIControllerCommandTable.idl',
--- a/dom/commandhandler/nsBaseCommandController.h
+++ b/dom/commandhandler/nsBaseCommandController.h
@@ -2,21 +2,16 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsBaseCommandController_h__
 #define nsBaseCommandController_h__
 
-#define NS_BASECOMMANDCONTROLLER_CID \
-  { 0xbf88b48c, 0xfd8e, 0x40b4, { 0xba, 0x36, 0xc7, 0xc3, 0xad, 0x6d, 0x8a, 0xc9 } }
-#define NS_BASECOMMANDCONTROLLER_CONTRACTID \
-  "@mozilla.org/embedcomp/base-command-controller;1"
-
 #include "nsIController.h"
 #include "nsIControllerContext.h"
 #include "nsIControllerCommandTable.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIWeakReferenceUtils.h"
 
 // The base editor controller is used for both text widgets, and all other text
 // and html editing
--- a/dom/commandhandler/nsICommandManager.idl
+++ b/dom/commandhandler/nsICommandManager.idl
@@ -100,19 +100,8 @@ interface nsICommandManager : nsISupport
 Arguments to observers "Observe" method are as follows:
 
   void Observe(   in nsISupports aSubject,          // The nsICommandManager calling this Observer
                   in string      aTopic,            // Name of the command
                   in wstring     aDummy );          // unused
 
 */
 
-// {64edb481-0c04-11d5-a73c-e964b968b0bc}
-%{C++
-#define NS_COMMAND_MANAGER_CID \
-{ 0x64edb481, 0x0c04, 0x11d5, { 0xa7, 0x3c, 0xe9, 0x64, 0xb9, 0x68, 0xb0, 0xbc } }
-
-#define NS_COMMAND_MANAGER_CONTRACTID \
- "@mozilla.org/embedcomp/command-manager;1"
-%}
-
-
-
--- a/dom/media/webaudio/AudioEventTimeline.cpp
+++ b/dom/media/webaudio/AudioEventTimeline.cpp
@@ -111,17 +111,17 @@ AudioTimelineEvent::~AudioTimelineEvent(
 template<class TimeType> void
 AudioEventTimeline::GetValuesAtTimeHelper(TimeType aTime, float* aBuffer,
                                           const size_t aSize)
 {
   MOZ_ASSERT(aBuffer);
   MOZ_ASSERT(aSize);
 
   auto TimeOf = [](const AudioTimelineEvent& aEvent) -> TimeType {
-    return aEvent.template Time<TimeType>();
+    return aEvent.Time<TimeType>();
   };
 
   size_t eventIndex = 0;
   const AudioTimelineEvent* previous = nullptr;
 
   // Let's remove old events except the last one: we need it to calculate some curves.
   CleanupEventsOlderThan(aTime);
 
@@ -181,17 +181,17 @@ AudioEventTimeline::GetValuesAtTimeHelpe
                                           const size_t aSize);
 template void
 AudioEventTimeline::GetValuesAtTimeHelper(int64_t aTime, float* aBuffer,
                                           const size_t aSize);
 
 template<class TimeType> float
 AudioEventTimeline::GetValueAtTimeOfEvent(const AudioTimelineEvent* aNext)
 {
-  TimeType time = aNext->template Time<TimeType>();
+  TimeType time = aNext->Time<TimeType>();
   switch (aNext->mType) {
     case AudioTimelineEvent::SetTarget:
       // SetTarget nodes can be handled no matter what their next node is
       // (if they have one).
       // Follow the curve, without regard to the next event, starting at
       // the last value of the last event.
       return ExponentialApproach(time,
                                  mLastComputedValue, aNext->mValue,
@@ -220,59 +220,65 @@ AudioEventTimeline::GetValuesAtTimeHelpe
   if (!aPrevious) {
      return mValue;
   }
 
   // If this event is a curve event, this returns the end time of the curve.
   // Otherwise, this returns the time of the event.
   auto TimeOf = [](const AudioTimelineEvent* aEvent) -> TimeType {
     if (aEvent->mType == AudioTimelineEvent::SetValueCurve) {
-      return aEvent->template Time<TimeType>() + aEvent->mDuration;
+      return aEvent->Time<TimeType>() + aEvent->mDuration;
     }
-    return aEvent->template Time<TimeType>();
+    return aEvent->Time<TimeType>();
   };
 
   // Value for an event. For a ValueCurve event, this is the value of the last
   // element of the curve.
   auto ValueOf = [](const AudioTimelineEvent* aEvent) -> float {
     if (aEvent->mType == AudioTimelineEvent::SetValueCurve) {
       return aEvent->mCurve[aEvent->mCurveLength - 1];
     }
     return aEvent->mValue;
   };
 
   // SetTarget nodes can be handled no matter what their next node is (if
   // they have one)
   if (aPrevious->mType == AudioTimelineEvent::SetTarget) {
     return ExponentialApproach(TimeOf(aPrevious),
-                               mLastComputedValue, ValueOf(aPrevious),
-                               aPrevious->mTimeConstant, aTime);
+                               mLastComputedValue,
+                               ValueOf(aPrevious),
+                               aPrevious->mTimeConstant,
+                               aTime);
   }
 
   // SetValueCurve events can be handled no matter what their next node is
   // (if they have one), when aTime is in the curve region.
   if (aPrevious->mType == AudioTimelineEvent::SetValueCurve &&
-      aTime <= aPrevious->template Time<TimeType>() + aPrevious->mDuration) {
-    return ExtractValueFromCurve(aPrevious->template Time<TimeType>(),
-                                 aPrevious->mCurve, aPrevious->mCurveLength,
-                                 aPrevious->mDuration, aTime);
+      aTime <= aPrevious->Time<TimeType>() + aPrevious->mDuration) {
+    return ExtractValueFromCurve(aPrevious->Time<TimeType>(),
+                                 aPrevious->mCurve,
+                                 aPrevious->mCurveLength,
+                                 aPrevious->mDuration,
+                                 aTime);
   }
 
   // If the requested time is after all of the existing events
   if (!aNext) {
     switch (aPrevious->mType) {
       case AudioTimelineEvent::SetValueAtTime:
       case AudioTimelineEvent::LinearRamp:
       case AudioTimelineEvent::ExponentialRamp:
         // The value will be constant after the last event
         return aPrevious->mValue;
       case AudioTimelineEvent::SetValueCurve:
-        return ExtractValueFromCurve(aPrevious->template Time<TimeType>(),
-                                     aPrevious->mCurve, aPrevious->mCurveLength,
-                                     aPrevious->mDuration, aTime);
+        return ExtractValueFromCurve(aPrevious->Time<TimeType>(),
+                                     aPrevious->mCurve,
+                                     aPrevious->mCurveLength,
+                                     aPrevious->mDuration,
+                                     aTime);
       case AudioTimelineEvent::SetTarget:
         MOZ_FALLTHROUGH_ASSERT("AudioTimelineEvent::SetTarget");
       case AudioTimelineEvent::SetValue:
       case AudioTimelineEvent::Cancel:
       case AudioTimelineEvent::Stream:
         MOZ_ASSERT(false, "Should have been handled earlier.");
     }
     MOZ_ASSERT(false, "unreached");
@@ -281,23 +287,25 @@ AudioEventTimeline::GetValuesAtTimeHelpe
   // Finally, handle the case where we have both a previous and a next event
 
   // First, handle the case where our range ends up in a ramp event
   switch (aNext->mType) {
   case AudioTimelineEvent::LinearRamp:
     return LinearInterpolate(TimeOf(aPrevious),
                              ValueOf(aPrevious),
                              TimeOf(aNext),
-                             ValueOf(aNext), aTime);
+                             ValueOf(aNext),
+                             aTime);
 
   case AudioTimelineEvent::ExponentialRamp:
     return ExponentialInterpolate(TimeOf(aPrevious),
                                   ValueOf(aPrevious),
                                   TimeOf(aNext),
-                                  ValueOf(aNext), aTime);
+                                  ValueOf(aNext),
+                                  aTime);
 
   case AudioTimelineEvent::SetValueAtTime:
   case AudioTimelineEvent::SetTarget:
   case AudioTimelineEvent::SetValueCurve:
     break;
   case AudioTimelineEvent::SetValue:
   case AudioTimelineEvent::Cancel:
   case AudioTimelineEvent::Stream:
@@ -308,19 +316,21 @@ AudioEventTimeline::GetValuesAtTimeHelpe
   switch (aPrevious->mType) {
   case AudioTimelineEvent::SetValueAtTime:
   case AudioTimelineEvent::LinearRamp:
   case AudioTimelineEvent::ExponentialRamp:
     // If the next event type is neither linear or exponential ramp, the
     // value is constant.
     return aPrevious->mValue;
   case AudioTimelineEvent::SetValueCurve:
-    return ExtractValueFromCurve(aPrevious->template Time<TimeType>(),
-                                 aPrevious->mCurve, aPrevious->mCurveLength,
-                                 aPrevious->mDuration, aTime);
+    return ExtractValueFromCurve(aPrevious->Time<TimeType>(),
+                                 aPrevious->mCurve,
+                                 aPrevious->mCurveLength,
+                                 aPrevious->mDuration,
+                                 aTime);
   case AudioTimelineEvent::SetTarget:
     MOZ_FALLTHROUGH_ASSERT("AudioTimelineEvent::SetTarget");
   case AudioTimelineEvent::SetValue:
   case AudioTimelineEvent::Cancel:
   case AudioTimelineEvent::Stream:
     MOZ_ASSERT(false, "Should have been handled earlier.");
   }
 
@@ -338,17 +348,17 @@ AudioEventTimeline::GetValuesAtTimeHelpe
 
 const AudioTimelineEvent*
 AudioEventTimeline::GetPreviousEvent(double aTime) const
 {
   const AudioTimelineEvent* previous = nullptr;
   const AudioTimelineEvent* next = nullptr;
 
   auto TimeOf = [](const AudioTimelineEvent& aEvent) -> double {
-    return aEvent.template Time<double>();
+    return aEvent.Time<double>();
   };
 
   bool bailOut = false;
   for (unsigned i = 0; !bailOut && i < mEvents.Length(); ++i) {
     switch (mEvents[i].mType) {
     case AudioTimelineEvent::SetValueAtTime:
     case AudioTimelineEvent::SetTarget:
     case AudioTimelineEvent::LinearRamp:
--- a/dom/media/webaudio/AudioEventTimeline.h
+++ b/dom/media/webaudio/AudioEventTimeline.h
@@ -126,42 +126,38 @@ public:
       mLastComputedValue(aDefaultValue)
   { }
 
   bool ValidateEvent(AudioTimelineEvent& aEvent, ErrorResult& aRv)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     auto TimeOf = [](const AudioTimelineEvent& aEvent) -> double {
-      return aEvent.template Time<double>();
+      return aEvent.Time<double>();
     };
 
     // Validate the event itself
     if (!WebAudioUtils::IsTimeValid(TimeOf(aEvent))) {
-      aRv.template ThrowRangeError<
+      aRv.ThrowRangeError<
         MSG_INVALID_AUDIOPARAM_METHOD_START_TIME_ERROR>();
       return false;
     }
     if (!WebAudioUtils::IsTimeValid(aEvent.mTimeConstant)) {
-      aRv.template ThrowRangeError<
+      aRv.ThrowRangeError<
         MSG_INVALID_AUDIOPARAM_EXPONENTIAL_CONSTANT_ERROR>();
       return false;
     }
 
     if (aEvent.mType == AudioTimelineEvent::SetValueCurve) {
-      if (!aEvent.mCurve || !aEvent.mCurveLength) {
-        aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-        return false;
-      }
-      if (aEvent.mCurveLength < 2) {
+      if (!aEvent.mCurve || aEvent.mCurveLength < 2) {
         aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
         return false;
       }
       if (aEvent.mDuration <= 0) {
-        aRv.template ThrowRangeError<MSG_INVALID_CURVE_DURATION_ERROR>();
+        aRv.ThrowRangeError<MSG_INVALID_CURVE_DURATION_ERROR>();
         return false;
       }
     }
 
     MOZ_ASSERT(IsValid(aEvent.mValue) && IsValid(aEvent.mDuration));
 
     // Make sure that new events don't fall within the duration of a
     // curve event.
@@ -169,32 +165,32 @@ public:
       if (mEvents[i].mType == AudioTimelineEvent::SetValueCurve &&
           TimeOf(mEvents[i]) <= TimeOf(aEvent) &&
           TimeOf(mEvents[i]) + mEvents[i].mDuration > TimeOf(aEvent)) {
         aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
         return false;
       }
     }
 
-    // Make sure that new curve events don't fall in a range which includes other
-    // events.
+    // Make sure that new curve events don't fall in a range which includes
+    // other events.
     if (aEvent.mType == AudioTimelineEvent::SetValueCurve) {
       for (unsigned i = 0; i < mEvents.Length(); ++i) {
         if (TimeOf(aEvent) < TimeOf(mEvents[i]) &&
-            TimeOf(aEvent) + aEvent.mDuration >= TimeOf(mEvents[i])) {
+            TimeOf(aEvent) + aEvent.mDuration > TimeOf(mEvents[i])) {
           aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
           return false;
         }
       }
     }
 
     // Make sure that invalid values are not used for exponential curves
     if (aEvent.mType == AudioTimelineEvent::ExponentialRamp) {
       if (aEvent.mValue <= 0.f) {
-        aRv.template ThrowRangeError<
+        aRv.ThrowRangeError<
           MSG_INVALID_AUDIOPARAM_EXPONENTIAL_VALUE_ERROR>();
         return false;
       }
       const AudioTimelineEvent* previousEvent = GetPreviousEvent(TimeOf(aEvent));
       if (previousEvent) {
         if (previousEvent->mValue <= 0.f) {
           aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
           return false;
@@ -208,28 +204,28 @@ public:
     }
     return true;
   }
 
   template<typename TimeType>
   void InsertEvent(const AudioTimelineEvent& aEvent)
   {
     for (unsigned i = 0; i < mEvents.Length(); ++i) {
-      if (aEvent.template Time<TimeType>() == mEvents[i].template Time<TimeType>()) {
-          // If two events happen at the same time, have them in chronological
-          // order of insertion.
-          do {
-            ++i;
-          } while (i < mEvents.Length() &&
-                   aEvent.mType != mEvents[i].mType &&
-                   aEvent.template Time<TimeType>() == mEvents[i].template Time<TimeType>());
-          mEvents.InsertElementAt(i, aEvent);
+      if (aEvent.Time<TimeType>() == mEvents[i].Time<TimeType>()) {
+        // If two events happen at the same time, have them in chronological
+        // order of insertion.
+        do {
+          ++i;
+        } while (i < mEvents.Length() && aEvent.mType != mEvents[i].mType &&
+                 aEvent.Time<TimeType>() == mEvents[i].Time<TimeType>());
+        mEvents.InsertElementAt(i, aEvent);
+        return;
       }
       // Otherwise, place the event right after the latest existing event
-      if (aEvent.template Time<TimeType>() < mEvents[i].template Time<TimeType>()) {
+      if (aEvent.Time<TimeType>() < mEvents[i].Time<TimeType>()) {
         mEvents.InsertElementAt(i, aEvent);
         return;
       }
     }
 
     // If we couldn't find a place for the event, just append it to the list
     mEvents.AppendElement(aEvent);
   }
@@ -303,22 +299,22 @@ public:
       InsertEvent<double>(event);
     }
   }
 
   template<typename TimeType>
   void CancelScheduledValues(TimeType aStartTime)
   {
     for (unsigned i = 0; i < mEvents.Length(); ++i) {
-      if (mEvents[i].template Time<TimeType>() >= aStartTime) {
+      if (mEvents[i].Time<TimeType>() >= aStartTime) {
 #ifdef DEBUG
         // Sanity check: the array should be sorted, so all of the following
         // events should have a time greater than aStartTime too.
         for (unsigned j = i + 1; j < mEvents.Length(); ++j) {
-          MOZ_ASSERT(mEvents[j].template Time<TimeType>() >= aStartTime);
+          MOZ_ASSERT(mEvents[j].Time<TimeType>() >= aStartTime);
         }
 #endif
         mEvents.TruncateLength(i);
         break;
       }
     }
   }
 
@@ -359,23 +355,21 @@ public:
   uint32_t GetEventCount() const
   {
     return mEvents.Length();
   }
 
   template<class TimeType>
   void CleanupEventsOlderThan(TimeType aTime)
   {
-    while (mEvents.Length() > 1 &&
-        aTime > mEvents[1].template Time<TimeType>()) {
+    while (mEvents.Length() > 1 && aTime > mEvents[1].Time<TimeType>()) {
 
       if (mEvents[1].mType == AudioTimelineEvent::SetTarget) {
         mLastComputedValue = GetValuesAtTimeHelperInternal(
-                                mEvents[1].template Time<TimeType>(),
-                                &mEvents[0], nullptr);
+          mEvents[1].Time<TimeType>(), &mEvents[0], nullptr);
       }
 
       mEvents.RemoveElementAt(0);
     }
   }
 
 private:
   template<class TimeType>
--- a/dom/media/webaudio/AudioParam.h
+++ b/dom/media/webaudio/AudioParam.h
@@ -46,24 +46,23 @@ public:
   // We override SetValueCurveAtTime to convert the Float32Array to the wrapper
   // object.
   AudioParam* SetValueCurveAtTime(const nsTArray<float>& aValues,
                                   double aStartTime,
                                   double aDuration,
                                   ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aStartTime)) {
-      aRv.template ThrowRangeError<
+      aRv.ThrowRangeError<
         MSG_INVALID_AUDIOPARAM_METHOD_START_TIME_ERROR>();
       return this;
     }
     aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
     EventInsertionHelper(aRv, AudioTimelineEvent::SetValueCurve,
-                         aStartTime, 0.0f,
-                         0.0f, aDuration, aValues.Elements(),
+                         aStartTime, 0.0f, 0.0f, aDuration, aValues.Elements(),
                          aValues.Length());
 
     return this;
   }
 
   void SetValue(float aValue)
   {
     AudioTimelineEvent event(AudioTimelineEvent::SetValue, 0.0f, aValue);
@@ -78,75 +77,75 @@ public:
     AudioParamTimeline::SetValue(aValue);
 
     SendEventToEngine(event);
   }
 
   AudioParam* SetValueAtTime(float aValue, double aStartTime, ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aStartTime)) {
-      aRv.template ThrowRangeError<
+      aRv.ThrowRangeError<
         MSG_INVALID_AUDIOPARAM_METHOD_START_TIME_ERROR>();
       return this;
     }
     aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
     EventInsertionHelper(aRv, AudioTimelineEvent::SetValueAtTime,
                          aStartTime, aValue);
 
     return this;
   }
 
   AudioParam* LinearRampToValueAtTime(float aValue, double aEndTime,
                                       ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aEndTime)) {
-      aRv.template ThrowRangeError<
+      aRv.ThrowRangeError<
         MSG_INVALID_AUDIOPARAM_METHOD_END_TIME_ERROR>();
       return this;
     }
     aEndTime = std::max(aEndTime, GetParentObject()->CurrentTime());
     EventInsertionHelper(aRv, AudioTimelineEvent::LinearRamp, aEndTime, aValue);
     return this;
   }
 
   AudioParam* ExponentialRampToValueAtTime(float aValue, double aEndTime,
                                            ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aEndTime)) {
-      aRv.template ThrowRangeError<
+      aRv.ThrowRangeError<
         MSG_INVALID_AUDIOPARAM_METHOD_END_TIME_ERROR>();
       return this;
     }
     aEndTime = std::max(aEndTime, GetParentObject()->CurrentTime());
     EventInsertionHelper(aRv, AudioTimelineEvent::ExponentialRamp,
                          aEndTime, aValue);
     return this;
   }
 
   AudioParam* SetTargetAtTime(float aTarget, double aStartTime,
                               double aTimeConstant, ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aStartTime) ||
         !WebAudioUtils::IsTimeValid(aTimeConstant)) {
-      aRv.template ThrowRangeError<
+      aRv.ThrowRangeError<
         MSG_INVALID_AUDIOPARAM_METHOD_START_TIME_ERROR>();
       return this;
     }
     aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
     EventInsertionHelper(aRv, AudioTimelineEvent::SetTarget,
                          aStartTime, aTarget,
                          aTimeConstant);
 
     return this;
   }
 
   AudioParam* CancelScheduledValues(double aStartTime, ErrorResult& aRv)
   {
     if (!WebAudioUtils::IsTimeValid(aStartTime)) {
-      aRv.template ThrowRangeError<
+      aRv.ThrowRangeError<
         MSG_INVALID_AUDIOPARAM_METHOD_START_TIME_ERROR>();
       return this;
     }
 
     aStartTime = std::max(aStartTime, GetParentObject()->CurrentTime());
 
     // Remove some events on the main thread copy.
     AudioEventTimeline::CancelScheduledValues(aStartTime);
--- a/dom/media/webaudio/AudioParamTimeline.h
+++ b/dom/media/webaudio/AudioParamTimeline.h
@@ -48,17 +48,17 @@ public:
   {
     return GetValueAtTime(aTime, 0);
   }
 
   template<typename TimeType>
   void InsertEvent(const AudioTimelineEvent& aEvent)
   {
     if (aEvent.mType == AudioTimelineEvent::Cancel) {
-      CancelScheduledValues(aEvent.template Time<TimeType>());
+      CancelScheduledValues(aEvent.Time<TimeType>());
       return;
     }
     if (aEvent.mType == AudioTimelineEvent::Stream) {
       mStream = aEvent.mStream;
       return;
     }
     if (aEvent.mType == AudioTimelineEvent::SetValue) {
       AudioEventTimeline::SetValue(aEvent.mValue);
--- a/editor/composer/nsComposerRegistration.cpp
+++ b/editor/composer/nsComposerRegistration.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/EditorSpellCheck.h"   // for NS_EDITORSPELLCHECK_CID, etc
 #include "mozilla/HTMLEditorController.h" // for HTMLEditorController, etc
 #include "mozilla/Module.h"             // for Module, Module::CIDEntry, etc
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/mozalloc.h"           // for operator new
 #include "nsCOMPtr.h"                   // for nsCOMPtr, getter_AddRefs, etc
+#include "nsBaseCommandController.h"    // for nsBaseCommandController
 #include "nsComponentManagerUtils.h"    // for do_CreateInstance
 #include "nsComposeTxtSrvFilter.h"      // for nsComposeTxtSrvFilter, etc
 #include "nsDebug.h"                    // for NS_ENSURE_SUCCESS
 #include "nsEditingSession.h"           // for NS_EDITINGSESSION_CID, etc
 #include "nsError.h"                    // for NS_ERROR_NO_AGGREGATION, etc
 #include "nsIController.h"              // for nsIController
 #include "nsIControllerCommandTable.h"  // for nsIControllerCommandTable, etc
 #include "nsIControllerContext.h"       // for nsIControllerContext
@@ -94,20 +95,19 @@ nsComposeTxtSrvFilterConstructorForMail(
 // command table, so that every controller shares a single command
 // table, for space-efficiency.
 //
 // The only reason to go via the service manager for the command table
 // is that it holds onto the singleton for us, avoiding static variables here.
 static nsresult
 CreateControllerWithSingletonCommandTable(const nsCID& inCommandTableCID, nsIController **aResult)
 {
+  nsCOMPtr<nsIController> controller = new nsBaseCommandController();
+
   nsresult rv;
-  nsCOMPtr<nsIController> controller = do_CreateInstance("@mozilla.org/embedcomp/base-command-controller;1", &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   nsCOMPtr<nsIControllerCommandTable> composerCommandTable = do_GetService(inCommandTableCID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // this guy is a singleton, so make it immutable
   composerCommandTable->MakeImmutable();
 
   nsCOMPtr<nsIControllerContext> controllerContext = do_QueryInterface(controller, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -286,17 +286,16 @@ GCRuntime::checkAllocatorState(JSContext
                   kind == AllocKind::FAT_INLINE_ATOM ||
                   kind == AllocKind::SYMBOL ||
                   kind == AllocKind::JITCODE ||
                   kind == AllocKind::SCOPE);
     MOZ_ASSERT_IF(!cx->zone()->isAtomsZone(),
                   kind != AllocKind::ATOM &&
                   kind != AllocKind::FAT_INLINE_ATOM);
     MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
-    MOZ_ASSERT(cx->isAllocAllowed());
 #endif
 
     // Crash if we perform a GC action when it is not safe.
     if (allowGC && !cx->suppressGC)
         cx->verifyIsSafeToGC();
 
     // For testing out of memory conditions
     if (js::oom::ShouldFailWithOOM()) {
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -7684,18 +7684,16 @@ GCRuntime::maybeDoCycleCollection()
 
 void
 GCRuntime::checkCanCallAPI()
 {
     MOZ_RELEASE_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
     /* If we attempt to invoke the GC while we are running in the GC, assert. */
     MOZ_RELEASE_ASSERT(!JS::RuntimeHeapIsBusy());
-
-    MOZ_ASSERT(rt->mainContextFromOwnThread()->isAllocAllowed());
 }
 
 bool
 GCRuntime::checkIfGCAllowedInCurrentState(JS::gcreason::Reason reason)
 {
     if (rt->mainContextFromOwnThread()->suppressGC)
         return false;
 
--- a/js/src/jit-test/tests/basic/bug807623.js
+++ b/js/src/jit-test/tests/basic/bug807623.js
@@ -1,17 +1,12 @@
 var objectProxy = new Proxy({}, {});
 var functionProxy = new Proxy(function() {}, {});
 
 assertEq(Object.prototype.toString.call(objectProxy), '[object Object]');
 assertEq(Object.prototype.toString.call(functionProxy), '[object Function]');
-try {
-  Function.prototype.toString.call(functionProxy);
-  assertEq(true, false);
-} catch (e) {
-  assertEq(!!/incompatible/.exec(e), true);
-}
+
 try {
   Function.prototype.toString.call(objectProxy);
   assertEq(true, false);
 } catch (e) {
   assertEq(!!/incompatible/.exec(e), true);
 }
--- a/js/src/jit-test/tests/debug/bug-1260725.js
+++ b/js/src/jit-test/tests/debug/bug-1260725.js
@@ -1,12 +1,10 @@
-// |jit-test| error: out of memory
-
 if (!('oomTest' in this))
-  throw new Error("out of memory");
+  quit()
 
 var dbg = new Debugger;
 dbg.onNewGlobalObject = function(global) {
   dbg.memory.takeCensus({});
 };
 oomTest(function() {
   newGlobal({sameZoneAs: this})
 });
--- a/js/src/jit-test/tests/debug/bug1251919.js
+++ b/js/src/jit-test/tests/debug/bug1251919.js
@@ -1,12 +1,10 @@
-// |jit-test| error: out of memory
-
 if (!('oomTest' in this))
-    throw new Error("out of memory");
+    quit();
 
 // jsfunfuzz-generated
 fullcompartmentchecks(true);
 // Adapted from randomly chosen test: js/src/jit-test/tests/debug/bug-1248162.js
 var dbg = new Debugger;
 dbg.onNewGlobalObject = function() {};
 oomTest(function() {
     newGlobal({sameZoneAs: this});
--- a/js/src/jit-test/tests/debug/bug1254123.js
+++ b/js/src/jit-test/tests/debug/bug1254123.js
@@ -1,12 +1,10 @@
-// |jit-test| error: boom
-
 if (!('oomTest' in this))
-    throw new Error("boom");
+  quit();
 
 evaluate(`
 function ERROR(msg) {
   throw new Error("boom");
 }
 for (var i = 0; i < 9; ++ i) {
   var dbg = new Debugger;
   dbg.onNewGlobalObject = ERROR;
--- a/js/src/jit-test/tests/debug/bug1254190.js
+++ b/js/src/jit-test/tests/debug/bug1254190.js
@@ -1,12 +1,12 @@
-// |jit-test| error: out of memory; slow;
+// |jit-test| slow
 
 if (!('oomTest' in this))
-    throw new Error("out of memory");
+    quit();
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 dbg.onNewScript = function (s) {
   log += dbg.findScripts({ source: s.source }).length;
 }
 log = "";
 oomTest(() => {
--- a/js/src/jit-test/tests/debug/bug1254578.js
+++ b/js/src/jit-test/tests/debug/bug1254578.js
@@ -1,12 +1,12 @@
-// |jit-test| error:ReferenceError; slow
+// |jit-test| slow
 
 if (!('oomTest' in this))
-  throw (new ReferenceError);
+    quit();
 
 var g = newGlobal();
 g.debuggeeGlobal = this;
 g.eval("(" + function() {
     dbg = new Debugger(debuggeeGlobal);
     dbg.onExceptionUnwind = function(frame, exc) {
         var s = '!';
         for (var f = frame; f; f = f.older)
--- a/js/src/jit-test/tests/debug/bug1264961.js
+++ b/js/src/jit-test/tests/debug/bug1264961.js
@@ -15,14 +15,12 @@ loadFile(`
   })
   for (var i=0; i<100; i++);
   assertEq(delete p.foo, true);
 `);
 function loadFile(lfVarx) {
     var k = 0;
     oomTest(function() {
         // In practice a crash occurs before iteration 4000.
-        if (k++ > 4000)
-          quit();
-        eval(lfVarx);
+        if (k++ <= 4000)
+          eval(lfVarx);
     })
 }
-
--- a/js/src/jit-test/tests/debug/bug1272908.js
+++ b/js/src/jit-test/tests/debug/bug1272908.js
@@ -1,17 +1,20 @@
-// |jit-test| error: out of memory; slow;
+// |jit-test| slow
+
+if (!('oomTest' in this))
+    quit();
 
 // Adapted from randomly chosen test: js/src/jit-test/tests/modules/bug-1233915.js
 g = newGlobal();
 g.parent = this;
 g.eval("(" + function() {
-    Debugger(parent).onExceptionUnwind = function(frame)
+    Debugger(parent).onExceptionUnwind = function(frame) {
     frame.eval("")
-} + ")()");
+    } } + ")()");
 // Adapted from randomly chosen test: js/src/jit-test/tests/debug/bug1254123.js
 function ERROR(msg) {
     throw new Error("boom");
 }
 var dbg = new Debugger;
 dbg.onNewGlobalObject = ERROR;
 oomTest(function() {
     newGlobal({sameZoneAs: this});
--- a/js/src/jit-test/tests/debug/bug1404710.js
+++ b/js/src/jit-test/tests/debug/bug1404710.js
@@ -1,11 +1,10 @@
-// |jit-test| error:InternalError
 if (!('stackTest' in this))
-    throw InternalError();
+    quit();
 stackTest(new Function(`
     var g = newGlobal();
     var dbg = new Debugger(g);
     dbg.onDebuggerStatement = function (frame) {
         frame.evalWithBindings("x", {x: 2}).return;
     };
     g.eval("function f(y) { debugger; }");
     g.f(3);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1484905.js
@@ -0,0 +1,4 @@
+// |jit-test| --ion-limit-script-size=off; --ion-gvn=off
+for (var i = 0; i < 1; ++i) {
+    "".replace(/x/, "").replace(/y/, "12");
+}
--- a/js/src/jit-test/tests/proxy/function-toString.js
+++ b/js/src/jit-test/tests/proxy/function-toString.js
@@ -1,10 +1,10 @@
 load(libdir + 'asserts.js');
 
-// Function.prototype.toString doesn't accept ES6 proxies.
+var nativeCode = "function () {\n    [native code]\n}";
 
 var proxy = new Proxy(function() {}, {});
-assertThrowsInstanceOf(() => Function.prototype.toString.call(proxy), TypeError);
+assertEq(Function.prototype.toString.call(proxy), nativeCode);
 var o = Proxy.revocable(function() {}, {});
-assertThrowsInstanceOf(() => Function.prototype.toString.call(o.proxy), TypeError);
+assertEq(Function.prototype.toString.call(o.proxy), nativeCode);
 o.revoke();
-assertThrowsInstanceOf(() => Function.prototype.toString.call(o.proxy), TypeError);
+assertEq(Function.prototype.toString.call(o.proxy), nativeCode);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7584,17 +7584,20 @@ class MRegExpInstanceOptimizable
 class MGetFirstDollarIndex
   : public MUnaryInstruction,
     public StringPolicy<0>::Data
 {
     explicit MGetFirstDollarIndex(MDefinition* str)
       : MUnaryInstruction(classOpcode, str)
     {
         setResultType(MIRType::Int32);
-        setMovable();
+
+        // Codegen assumes string length > 0. Don't allow LICM to move this
+        // before the .length > 1 check in RegExpReplace in RegExp.js.
+        MOZ_ASSERT(!isMovable());
     }
 
   public:
     INSTRUCTION_HEADER(GetFirstDollarIndex)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, str))
 
     AliasSet getAliasSet() const override {
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -305,18 +305,19 @@ BaseProxyHandler::className(JSContext* c
     return proxy->isCallable() ? "Function" : "Object";
 }
 
 JSString*
 BaseProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
 {
     if (proxy->isCallable())
         return JS_NewStringCopyZ(cx, "function () {\n    [native code]\n}");
-    RootedValue v(cx, ObjectValue(*proxy));
-    ReportIsNotFunction(cx, v);
+
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
+                              js_Function_str, js_toString_str, "object");
     return nullptr;
 }
 
 RegExpShared*
 BaseProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy) const
 {
     MOZ_CRASH("This should have been a wrapped regexp");
 }
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -1259,19 +1259,19 @@ ScriptedProxyHandler::className(JSContex
 {
     // Right now the caller is not prepared to handle failures.
     return BaseProxyHandler::className(cx, proxy);
 }
 
 JSString*
 ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
 {
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
-                              js_Function_str, js_toString_str, "object");
-    return nullptr;
+    // The BaseProxyHandler has the desired behavior: Throw for non-callable,
+    // otherwise return [native code].
+    return BaseProxyHandler::fun_toString(cx, proxy, isToSource);
 }
 
 RegExpShared*
 ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy) const
 {
     MOZ_CRASH("Should not end up in ScriptedProxyHandler::regexp_toShared");
 }
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -8238,22 +8238,29 @@ js::shell::AutoReportException::~AutoRep
 
     {
         JS::AutoSaveExceptionState savedExc(cx);
         if (!PrintStackTrace(cx, exn))
             fputs("(Unable to print stack trace)\n", fp);
         savedExc.restore();
     }
 
+    JS_ClearPendingException(cx);
+
+#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
+    // Don't quit the shell if an unhandled exception is reported during OOM
+    // testing.
+    if (cx->runningOOMTest)
+        return;
+#endif
+
     if (report.report()->errorNumber == JSMSG_OUT_OF_MEMORY)
         sc->exitCode = EXITCODE_OUT_OF_MEMORY;
     else
         sc->exitCode = EXITCODE_RUNTIME_ERROR;
-
-    JS_ClearPendingException(cx);
 }
 
 void
 js::shell::WarningReporter(JSContext* cx, JSErrorReport* report)
 {
     ShellContext* sc = GetShellContext(cx);
     FILE* fp = ErrorFilePointer();
 
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -409,29 +409,16 @@ skip script test262/language/expressions
 skip script test262/language/expressions/assignment/destructuring/iterator-destructuring-property-reference-target-evaluation-order.js
 
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1321616
 skip script test262/annexB/built-ins/Function/createdynfn-html-close-comment-params.js
 
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1334813
 skip script test262/built-ins/DataView/length.js
 
-# https://bugzilla.mozilla.org/show_bug.cgi?id=1440468
-skip script test262/built-ins/Function/prototype/toString/proxy.js
-skip script test262/built-ins/Function/prototype/toString/proxy-async-generator-function.js
-skip script test262/built-ins/Function/prototype/toString/proxy-class.js
-skip script test262/built-ins/Function/prototype/toString/proxy-async-method-definition.js
-skip script test262/built-ins/Function/prototype/toString/proxy-arrow-function.js
-skip script test262/built-ins/Function/prototype/toString/proxy-async-generator-method-definition.js
-skip script test262/built-ins/Function/prototype/toString/proxy-async-function.js
-skip script test262/built-ins/Function/prototype/toString/proxy-bound-function.js
-skip script test262/built-ins/Function/prototype/toString/proxy-method-definition.js
-skip script test262/built-ins/Function/prototype/toString/proxy-function-expression.js
-skip script test262/built-ins/Function/prototype/toString/proxy-generator-function.js
-
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1462741
 skip script test262/built-ins/Function/prototype/toString/well-known-intrinsic-object-functions.js
 
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1462745
 skip script test262/annexB/language/function-code/block-decl-nested-blocks-with-fun-decl.js
 
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1406171
 skip script test262/built-ins/Reflect/ownKeys/return-on-corresponding-order-large-index.js
--- a/js/src/vm/BytecodeUtil.cpp
+++ b/js/src/vm/BytecodeUtil.cpp
@@ -1145,21 +1145,17 @@ ToDisassemblySource(JSContext* cx, Handl
         if (!copy) {
             ReportOutOfMemory(cx);
             return false;
         }
         bytes->initBytes(std::move(copy));
         return true;
     }
 
-    if (JS::RuntimeHeapIsBusy()
-#ifdef DEBUG
-        || !cx->isAllocAllowed()
-#endif
-        ) {
+    if (JS::RuntimeHeapIsBusy()) {
         UniqueChars source = JS_smprintf("<value>");
         if (!source) {
             ReportOutOfMemory(cx);
             return false;
         }
         bytes->initBytes(std::move(source));
         return true;
     }
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -1288,17 +1288,16 @@ JSContext::JSContext(JSRuntime* runtime,
     suppressGC(0),
 #ifdef DEBUG
     ionCompiling(false),
     ionCompilingSafeForMinorGC(false),
     performingGC(false),
     gcSweeping(false),
     gcHelperStateThread(false),
     isTouchingGrayThings(false),
-    noGCOrAllocationCheck(0),
     noNurseryAllocationCheck(0),
     disableStrictProxyCheckingCount(0),
 #endif
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     runningOOMTest(false),
 #endif
     enableAccessValidation(false),
     inUnsafeRegion(0),
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -539,34 +539,26 @@ struct JSContext : public JS::RootingCon
 
     // Whether this thread is performing work in the background for a runtime's
     // GCHelperState.
     js::ThreadData<bool> gcHelperStateThread;
 
     // Whether this thread is currently manipulating possibly-gray GC things.
     js::ThreadData<size_t> isTouchingGrayThings;
 
-    js::ThreadData<size_t> noGCOrAllocationCheck;
     js::ThreadData<size_t> noNurseryAllocationCheck;
 
     /*
      * If this is 0, all cross-compartment proxies must be registered in the
      * wrapper map. This checking must be disabled temporarily while creating
      * new wrappers. When non-zero, this records the recursion depth of wrapper
      * creation.
      */
     js::ThreadData<uintptr_t> disableStrictProxyCheckingCount;
 
-    bool isAllocAllowed() { return noGCOrAllocationCheck == 0; }
-    void disallowAlloc() { ++noGCOrAllocationCheck; }
-    void allowAlloc() {
-        MOZ_ASSERT(!isAllocAllowed());
-        --noGCOrAllocationCheck;
-    }
-
     bool isNurseryAllocAllowed() { return noNurseryAllocationCheck == 0; }
     void disallowNurseryAlloc() { ++noNurseryAllocationCheck; }
     void allowNurseryAlloc() {
         MOZ_ASSERT(!isNurseryAllocAllowed());
         --noNurseryAllocationCheck;
     }
 
     bool isStrictProxyCheckingEnabled() { return disableStrictProxyCheckingCount == 0; }
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -1826,25 +1826,16 @@ JSObject::swap(JSContext* cx, HandleObje
         a->traceChildren(zone->barrierTracer());
         b->traceChildren(zone->barrierTracer());
     }
 
     NotifyGCPostSwap(a, b, r);
     return true;
 }
 
-static bool
-DefineStandardSlot(JSContext* cx, HandleObject obj, JSAtom* atom,
-                   HandleValue v, uint32_t attrs, bool& named)
-{
-    RootedId id(cx, AtomToId(atom));
-    named = DefineDataProperty(cx, obj, id, v, attrs);
-    return named;
-}
-
 static void
 SetClassObject(JSObject* obj, JSProtoKey key, JSObject* cobj, JSObject* proto)
 {
     if (!obj->is<GlobalObject>())
         return;
 
     obj->as<GlobalObject>().setConstructor(key, ObjectOrNullValue(cobj));
     obj->as<GlobalObject>().setPrototype(key, ObjectOrNullValue(proto));
@@ -1893,36 +1884,40 @@ DefineConstructorAndPrototype(JSContext*
     /*
      * Create the prototype object.  (GlobalObject::createBlankPrototype isn't
      * used because it won't let us use protoProto as the proto.
      */
     RootedNativeObject proto(cx, NewNativeObjectWithClassProto(cx, clasp, protoProto, SingletonObject));
     if (!proto)
         return nullptr;
 
+    /*
+     * Whether we need to define a constructor property on |obj| and which
+     * attributes to use.
+     */
+    bool defineConstructorProperty = false;
+    uint32_t propertyAttrs = 0;
+
     /* After this point, control must exit via label bad or out. */
     RootedNativeObject ctor(cx);
-    bool named = false;
     bool cached = false;
     if (!constructor) {
         /*
          * Lacking a constructor, name the prototype (e.g., Math) unless this
          * class (a) is anonymous, i.e. for internal use only; (b) the class
          * of obj (the global object) is has a reserved slot indexed by key;
          * and (c) key is not the null key.
          */
         if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) || !obj->is<GlobalObject>() ||
             key == JSProto_Null)
         {
-            uint32_t attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
-                           ? JSPROP_READONLY | JSPROP_PERMANENT
-                           : 0;
-            RootedValue value(cx, ObjectValue(*proto));
-            if (!DefineStandardSlot(cx, obj, atom, value, attrs, named))
-                goto bad;
+            defineConstructorProperty = true;
+            propertyAttrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
+                            ? JSPROP_READONLY | JSPROP_PERMANENT
+                            : 0;
         }
 
         ctor = proto;
     } else {
         RootedFunction fun(cx, NewNativeConstructor(cx, constructor, nargs, atom));
         if (!fun)
             goto bad;
 
@@ -1931,19 +1926,18 @@ DefineConstructorAndPrototype(JSContext*
          * inference may need to access these, and js::GetBuiltinPrototype will
          * fail if it tries to do a reentrant reconstruction of the class.
          */
         if (key != JSProto_Null) {
             SetClassObject(obj, key, fun, proto);
             cached = true;
         }
 
-        RootedValue value(cx, ObjectValue(*fun));
-        if (!DefineStandardSlot(cx, obj, atom, value, 0, named))
-            goto bad;
+        defineConstructorProperty = true;
+        propertyAttrs = 0;
 
         /*
          * Optionally construct the prototype object, before the class has
          * been fully initialized.  Allow the ctor to replace proto with a
          * different object, as is done for operator new.
          */
         ctor = fun;
         if (!LinkConstructorAndPrototype(cx, ctor, proto))
@@ -1956,32 +1950,32 @@ DefineConstructorAndPrototype(JSContext*
     }
 
     if (!DefinePropertiesAndFunctions(cx, proto, ps, fs) ||
         (ctor != proto && !DefinePropertiesAndFunctions(cx, ctor, static_ps, static_fs)))
     {
         goto bad;
     }
 
+    if (defineConstructorProperty) {
+        RootedId id(cx, AtomToId(atom));
+        RootedValue value(cx, ObjectValue(*ctor));
+        if (!DefineDataProperty(cx, obj, id, value, propertyAttrs))
+            goto bad;
+    }
+
     /* If this is a standard class, cache its prototype. */
     if (!cached && key != JSProto_Null)
         SetClassObject(obj, key, ctor, proto);
 
     if (ctorp)
         *ctorp = ctor;
     return proto;
 
 bad:
-    if (named) {
-        ObjectOpResult ignored;
-        RootedId id(cx, AtomToId(atom));
-
-        // XXX FIXME - absurd to call this here; instead define the property last.
-        DeleteProperty(cx, obj, id, ignored);
-    }
     if (cached)
         ClearClassObject(obj, key);
     return nullptr;
 }
 
 NativeObject*
 js::InitClass(JSContext* cx, HandleObject obj, HandleObject protoProto_,
               const Class* clasp, Native constructor, unsigned nargs,
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -150,16 +150,17 @@ static void Shutdown();
 #include "MediaManager.h"
 
 #include "GMPService.h"
 
 #include "mozilla/dom/PresentationDeviceManager.h"
 #include "mozilla/dom/PresentationTCPSessionTransport.h"
 
 #include "nsScriptError.h"
+#include "nsBaseCommandController.h"
 
 #include "mozilla/TextInputProcessor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::net;
 using mozilla::dom::power::PowerManagerService;
 using mozilla::dom::quota::QuotaManagerService;
@@ -601,22 +602,19 @@ CreateWindowCommandTableConstructor(nsIS
 
   return commandTable->QueryInterface(aIID, aResult);
 }
 
 static nsresult
 CreateWindowControllerWithSingletonCommandTable(nsISupports *aOuter,
                                       REFNSIID aIID, void **aResult)
 {
+  nsCOMPtr<nsIController> controller = new nsBaseCommandController();
+
   nsresult rv;
-  nsCOMPtr<nsIController> controller =
-       do_CreateInstance("@mozilla.org/embedcomp/base-command-controller;1", &rv);
-
- if (NS_FAILED(rv)) return rv;
-
   nsCOMPtr<nsIControllerCommandTable> windowCommandTable = do_GetService(kNS_WINDOWCOMMANDTABLE_CID, &rv);
   if (NS_FAILED(rv)) return rv;
 
   // this is a singleton; make it immutable
   windowCommandTable->MakeImmutable();
 
   nsCOMPtr<nsIControllerContext> controllerContext = do_QueryInterface(controller, &rv);
   if (NS_FAILED(rv)) return rv;
@@ -627,20 +625,19 @@ CreateWindowControllerWithSingletonComma
   return controller->QueryInterface(aIID, aResult);
 }
 
 // Constructor of a controller which is set up to use, internally, a
 // singleton command-table pre-filled with editor commands.
 static nsresult
 EditorControllerConstructor(nsISupports* aOuter, REFNSIID aIID, void** aResult)
 {
+  nsCOMPtr<nsIController> controller = new nsBaseCommandController();
+
   nsresult rv;
-  nsCOMPtr<nsIController> controller = do_CreateInstance("@mozilla.org/embedcomp/base-command-controller;1", &rv);
-  if (NS_FAILED(rv)) return rv;
-
   nsCOMPtr<nsIControllerCommandTable> editorCommandTable = do_GetService(kNS_EDITORCOMMANDTABLE_CID, &rv);
   if (NS_FAILED(rv)) return rv;
 
   // this guy is a singleton, so make it immutable
   editorCommandTable->MakeImmutable();
 
   nsCOMPtr<nsIControllerContext> controllerContext = do_QueryInterface(controller, &rv);
   if (NS_FAILED(rv)) return rv;
@@ -652,20 +649,19 @@ EditorControllerConstructor(nsISupports*
 }
 
 // Constructor of a controller which is set up to use, internally, a
 // singleton command-table pre-filled with editing commands.
 static nsresult
 nsEditingControllerConstructor(nsISupports *aOuter, REFNSIID aIID,
                                 void **aResult)
 {
+  nsCOMPtr<nsIController> controller = new nsBaseCommandController();
+
   nsresult rv;
-  nsCOMPtr<nsIController> controller = do_CreateInstance("@mozilla.org/embedcomp/base-command-controller;1", &rv);
-  if (NS_FAILED(rv)) return rv;
-
   nsCOMPtr<nsIControllerCommandTable> editingCommandTable = do_GetService(kNS_EDITINGCOMMANDTABLE_CID, &rv);
   if (NS_FAILED(rv)) return rv;
 
   // this guy is a singleton, so make it immutable
   editingCommandTable->MakeImmutable();
 
   nsCOMPtr<nsIControllerContext> controllerContext = do_QueryInterface(controller, &rv);
   if (NS_FAILED(rv)) return rv;
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -185,16 +185,36 @@ tasks.withType(org.jetbrains.kotlin.grad
     doFirst {
         logging.addStandardErrorListener(listener)
     }
     doLast {
         logging.removeStandardErrorListener(listener)
     }
 }
 
+tasks.withType(Javadoc) {
+    // Implement warning-as-error for javadoc.
+    def warnings = 0
+    def listener = {
+        if (it.contains(': warning')) {
+            warnings++
+        }
+    } as StandardOutputListener
+
+    doFirst {
+        logging.addStandardErrorListener(listener)
+    }
+    doLast {
+        logging.removeStandardErrorListener(listener)
+        if (warnings > 0) {
+            throw new GradleException("Treating $warnings javadoc warning(s) as error(s)")
+        }
+    }
+}
+
 dependencies {
     implementation "com.android.support:support-v4:$support_library_version"
     implementation "com.android.support:palette-v7:$support_library_version"
 
     testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
     testImplementation 'junit:junit:4.12'
     testImplementation 'org.robolectric:robolectric:3.8'
     testImplementation 'org.mockito:mockito-core:1.10.19'
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
@@ -246,34 +246,34 @@ public final class GeckoRuntimeSettings 
          */
         public @NonNull Builder displayDensityOverride(float density) {
             mSettings.mDisplayDensityOverride = density;
             return this;
         }
 
         /** Set whether or not known malware sites should be blocked.
          *
-         * Note: For each blocked site, {@link NavigationDelegate#onLoadError}
-         * with error category {@link NavigationDelegate#ERROR_CATEGORY_SAFEBROWSING}
+         * Note: For each blocked site, {@link GeckoSession.NavigationDelegate#onLoadError}
+         * with error category {@link GeckoSession.NavigationDelegate#ERROR_CATEGORY_SAFEBROWSING}
          * is called.
          *
          * @param enabled A flag determining whether or not to block malware
          *                sites.
          * @return The builder instance.
          */
         public @NonNull Builder blockMalware(boolean enabled) {
             mSettings.mSafebrowsingMalware.set(enabled);
             return this;
         }
 
         /**
          * Set whether or not known phishing sites should be blocked.
          *
-         * Note: For each blocked site, {@link NavigationDelegate#onLoadError}
-         * with error category {@link NavigationDelegate#ERROR_CATEGORY_SAFEBROWSING}
+         * Note: For each blocked site, {@link GeckoSession.NavigationDelegate#onLoadError}
+         * with error category {@link GeckoSession.NavigationDelegate#ERROR_CATEGORY_SAFEBROWSING}
          * is called.
          *
          * @param enabled A flag determining whether or not to block phishing
          *                sites.
          * @return The builder instance.
          */
         public @NonNull Builder blockPhishing(boolean enabled) {
             mSettings.mSafebrowsingPhishing.set(enabled);
@@ -527,17 +527,19 @@ public final class GeckoRuntimeSettings 
      *
      * @return True if Java crash reporting is enabled.
      */
     public boolean getJavaCrashReportingEnabled() {
         return mJavaCrashReporting;
     }
 
     /**
-     * Get the Job Id used on Oreo and later devices to manage crash reporting in background.
+     * Get the Job ID used on Oreo and later devices to manage crash reporting in background.
+     *
+     * @return Crash reporting service Job ID
      */
     public int getCrashReportingServiceJobId() {
         return mCrashReportingJobId;
     }
 
     /**
      * Gets whether the pause-for-debugger is enabled or not.
      *
@@ -719,18 +721,18 @@ public final class GeckoRuntimeSettings 
      */
     public boolean getConsoleOutputEnabled() {
         return mConsoleOutput.get();
     }
 
     /**
      * Set whether or not known malware sites should be blocked.
      *
-     * Note: For each blocked site, {@link NavigationDelegate#onLoadError}
-     * with error category {@link NavigationDelegate#ERROR_CATEGORY_SAFEBROWSING}
+     * Note: For each blocked site, {@link GeckoSession.NavigationDelegate#onLoadError}
+     * with error category {@link GeckoSession.NavigationDelegate#ERROR_CATEGORY_SAFEBROWSING}
      * is called.
      *
      * @param enabled A flag determining whether or not to block malware sites.
      * @return The GeckoRuntimeSettings instance.
      */
     public @NonNull GeckoRuntimeSettings setBlockMalware(boolean enabled) {
         mSafebrowsingMalware.set(enabled);
         return this;
@@ -743,18 +745,18 @@ public final class GeckoRuntimeSettings 
      */
     public boolean getBlockMalware() {
         return mSafebrowsingMalware.get();
     }
 
     /**
      * Set whether or not known phishing sites should be blocked.
      *
-     * Note: For each blocked site, {@link NavigationDelegate#onLoadError}
-     * with error category {@link NavigationDelegate#ERROR_CATEGORY_SAFEBROWSING}
+     * Note: For each blocked site, {@link GeckoSession.NavigationDelegate#onLoadError}
+     * with error category {@link GeckoSession.NavigationDelegate#ERROR_CATEGORY_SAFEBROWSING}
      * is called.
      *
      * @param enabled A flag determining whether or not to block phishing sites.
      * @return The GeckoRuntimeSettings instance.
      */
     public @NonNull GeckoRuntimeSettings setBlockPhishing(boolean enabled) {
         mSafebrowsingPhishing.set(enabled);
         return this;
--- a/netwerk/base/nsBaseChannel.cpp
+++ b/netwerk/base/nsBaseChannel.cpp
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsBaseChannel.h"
 #include "nsContentUtils.h"
 #include "nsURLHelper.h"
 #include "nsNetCID.h"
 #include "nsMimeTypes.h"
-#include "nsIContentSniffer.h"
+#include "nsUnknownDecoder.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsMimeTypes.h"
 #include "nsIHttpChannel.h"
 #include "nsIChannelEventSink.h"
 #include "nsIStreamConverterService.h"
 #include "nsChannelClassifier.h"
 #include "nsAsyncRedirectVerifyHelper.h"
 #include "nsProxyRelease.h"
@@ -819,20 +819,17 @@ CallTypeSniffers(void *aClosure, const u
   }
 }
 
 static void
 CallUnknownTypeSniffer(void *aClosure, const uint8_t *aData, uint32_t aCount)
 {
   nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
 
-  nsCOMPtr<nsIContentSniffer> sniffer =
-    do_CreateInstance(NS_GENERIC_CONTENT_SNIFFER);
-  if (!sniffer)
-    return;
+  RefPtr<nsUnknownDecoder> sniffer = new nsUnknownDecoder();
 
   nsAutoCString detected;
   nsresult rv = sniffer->GetMIMETypeFromContent(chan, aData, aCount, detected);
   if (NS_SUCCEEDED(rv))
     chan->SetContentType(detected);
 }
 
 NS_IMETHODIMP
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -948,24 +948,16 @@
 { /* 892FFEB0-3F80-11d3-A16C-0050041CAF44 */         \
     0x892ffeb0,                                      \
     0x3f80,                                          \
     0x11d3,                                          \
     {0xa1, 0x6c, 0x00, 0x50, 0x04, 0x1c, 0xaf, 0x44} \
 }
 
 /**
- * General-purpose content sniffer component. Use with CreateInstance.
- *
- * Implements nsIContentSniffer
- */
-#define NS_GENERIC_CONTENT_SNIFFER \
-    "@mozilla.org/network/content-sniffer;1"
-
-/**
  * Detector that can act as either an nsIStreamConverter or an
  * nsIContentSniffer to decide whether text/plain data is "really" text/plain
  * or APPLICATION_GUESS_FROM_EXT.  Use with CreateInstance.
  */
 #define NS_BINARYDETECTOR_CONTRACTID \
     "@mozilla.org/network/binary-detector;1"
 
 /******************************************************************************
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -943,17 +943,16 @@ static const mozilla::Module::ContractID
     { NS_DASHBOARD_CONTRACTID, &kNS_DASHBOARD_CID },
     { NS_ISTREAMCONVERTER_KEY FTP_TO_INDEX, &kNS_FTPDIRLISTINGCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY INDEX_TO_HTML, &kNS_NSINDEXEDTOHTMLCONVERTER_CID },
     { NS_DIRINDEXPARSER_CONTRACTID, &kNS_DIRINDEXPARSER_CID },
     { NS_ISTREAMCONVERTER_KEY MULTI_MIXED_X, &kNS_MULTIMIXEDCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY MULTI_BYTERANGES, &kNS_MULTIMIXEDCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY MULTI_MIXED, &kNS_MULTIMIXEDCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY UNKNOWN_CONTENT, &kNS_UNKNOWNDECODER_CID },
-    { NS_GENERIC_CONTENT_SNIFFER, &kNS_UNKNOWNDECODER_CID },
     { NS_BINARYDETECTOR_CONTRACTID, &kNS_BINARYDETECTOR_CID },
     { NS_ISTREAMCONVERTER_KEY GZIP_TO_UNCOMPRESSED, &kNS_HTTPCOMPRESSCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY XGZIP_TO_UNCOMPRESSED, &kNS_HTTPCOMPRESSCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY BROTLI_TO_UNCOMPRESSED, &kNS_HTTPCOMPRESSCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY COMPRESS_TO_UNCOMPRESSED, &kNS_HTTPCOMPRESSCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY XCOMPRESS_TO_UNCOMPRESSED, &kNS_HTTPCOMPRESSCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY DEFLATE_TO_UNCOMPRESSED, &kNS_HTTPCOMPRESSCONVERTER_CID },
     { MOZ_TXTTOHTMLCONV_CONTRACTID, &kMOZITXTTOHTMLCONV_CID },
--- a/netwerk/mime/nsMIMEHeaderParamImpl.cpp
+++ b/netwerk/mime/nsMIMEHeaderParamImpl.cpp
@@ -21,49 +21,49 @@
 #include "mozilla/Encoding.h"
 
 using mozilla::Encoding;
 
 // static functions declared below are moved from mailnews/mime/src/comi18n.cpp
 
 static char *DecodeQ(const char *, uint32_t);
 static bool Is7bitNonAsciiString(const char *, uint32_t);
-static void CopyRawHeader(const char *, uint32_t, const char *, nsACString &);
-static nsresult DecodeRFC2047Str(const char *, const char *, bool, nsACString&);
-static nsresult internalDecodeParameter(const nsACString&, const char*,
-                                        const char*, bool, bool, nsACString&);
+static void CopyRawHeader(const char *, uint32_t, const nsACString&, nsACString &);
+static nsresult DecodeRFC2047Str(const char *, const nsACString&, bool, nsACString&);
+static nsresult internalDecodeParameter(const nsACString&, const nsACString&,
+                                        const nsACString&, bool, bool, nsACString&);
 
 static nsresult
 ToUTF8(const nsACString& aString,
-       const char* aCharset,
+       const nsACString& aCharset,
        bool aAllowSubstitution,
        nsACString& aResult)
 {
-  if (!aCharset || !*aCharset)
+  if (aCharset.IsEmpty()) {
     return NS_ERROR_INVALID_ARG;
+  }
 
-  auto encoding = Encoding::ForLabelNoReplacement(
-                    mozilla::MakeStringSpan(aCharset));
+  auto encoding = Encoding::ForLabelNoReplacement(aCharset);
   if (!encoding) {
     return NS_ERROR_UCONV_NOCONV;
   }
   if (aAllowSubstitution) {
     nsresult rv = encoding->DecodeWithoutBOMHandling(aString, aResult);
     if (NS_SUCCEEDED(rv)) {
       return NS_OK;
     }
     return rv;
   }
   return encoding->DecodeWithoutBOMHandlingAndWithoutReplacement(aString,
                                                                  aResult);
 }
 
 static nsresult
 ConvertStringToUTF8(const nsACString& aString,
-                    const char* aCharset,
+                    const nsACString& aCharset,
                     bool aSkipCheck,
                     bool aAllowSubstitution,
                     nsACString& aUTF8String)
 {
   // return if ASCII only or valid UTF-8 providing that the ASCII/UTF-8
   // check is requested. It may not be asked for if a caller suspects
   // that the input is in non-ASCII 7bit charset (ISO-2022-xx, HZ) or
   // it's in a charset other than UTF-8 that can be mistaken for UTF-8.
@@ -156,29 +156,28 @@ nsMIMEHeaderParamImpl::DoGetParameter(co
                              getter_Copies(med));
     if (NS_FAILED(rv))
         return rv;
 
     // convert to UTF-8 after charset conversion and RFC 2047 decoding
     // if necessary.
 
     nsAutoCString str1;
-    rv = internalDecodeParameter(med, charset.get(), nullptr, false,
+    rv = internalDecodeParameter(med, charset, EmptyCString(), false,
                                  // was aDecoding == MIME_FIELD_ENCODING
                                  // see bug 875615
                                  true,
                                  str1);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!aFallbackCharset.IsEmpty())
     {
         const Encoding* encoding = Encoding::ForLabel(aFallbackCharset);
         nsAutoCString str2;
-        if (NS_SUCCEEDED(ConvertStringToUTF8(str1,
-                PromiseFlatCString(aFallbackCharset).get(), false,
+        if (NS_SUCCEEDED(ConvertStringToUTF8(str1, aFallbackCharset, false,
                                    encoding != UTF_8_ENCODING,
                                    str2))) {
           CopyUTF8toUTF16(str2, aResult);
           return NS_OK;
         }
     }
 
     if (IsUTF8(str1)) {
@@ -367,24 +366,23 @@ int32_t parseSegmentNumber(const char *a
     }
   }
 
   return segmentNumber;
 }
 
 // validate a given octet sequence for compliance with the specified
 // encoding
-bool IsValidOctetSequenceForCharset(nsACString& aCharset, const char *aOctets)
+bool IsValidOctetSequenceForCharset(const nsACString& aCharset, const char *aOctets)
 {
   nsAutoCString tmpRaw;
   tmpRaw.Assign(aOctets);
   nsAutoCString tmpDecoded;
 
-  nsresult rv = ConvertStringToUTF8(tmpRaw,
-                                    PromiseFlatCString(aCharset).get(),
+  nsresult rv = ConvertStringToUTF8(tmpRaw, aCharset,
                                     false, false, tmpDecoded);
 
   if (rv != NS_OK) {
     // we can't decode; charset may be unsupported, or the octet sequence
     // is broken (illegal or incomplete octet sequence contained)
     NS_WARNING("RFC2231/5987 parameter value does not decode according to specified charset\n");
     return false;
   }
@@ -775,32 +773,32 @@ increment_str:
       *(*aCharset + len) = 0;
     }
   }
 
   return *aResult ? NS_OK : NS_ERROR_INVALID_ARG;
 }
 
 nsresult
-internalDecodeRFC2047Header(const char* aHeaderVal, const char* aDefaultCharset,
+internalDecodeRFC2047Header(const char* aHeaderVal, const nsACString& aDefaultCharset,
                             bool aOverrideCharset, bool aEatContinuations,
                             nsACString& aResult)
 {
   aResult.Truncate();
   if (!aHeaderVal)
     return NS_ERROR_INVALID_ARG;
   if (!*aHeaderVal)
     return NS_OK;
 
 
   // If aHeaderVal is RFC 2047 encoded or is not a UTF-8 string  but
   // aDefaultCharset is specified, decodes RFC 2047 encoding and converts
   // to UTF-8. Otherwise, just strips away CRLF.
   if (PL_strstr(aHeaderVal, "=?") ||
-      (aDefaultCharset && (!IsUTF8(nsDependentCString(aHeaderVal)) ||
+      (!aDefaultCharset.IsEmpty() && (!IsUTF8(nsDependentCString(aHeaderVal)) ||
       Is7bitNonAsciiString(aHeaderVal, strlen(aHeaderVal))))) {
     DecodeRFC2047Str(aHeaderVal, aDefaultCharset, aOverrideCharset, aResult);
   } else if (aEatContinuations &&
              (PL_strchr(aHeaderVal, '\n') || PL_strchr(aHeaderVal, '\r'))) {
     aResult = aHeaderVal;
   } else {
     aEatContinuations = false;
     aResult = aHeaderVal;
@@ -819,17 +817,18 @@ internalDecodeRFC2047Header(const char* 
 
 NS_IMETHODIMP
 nsMIMEHeaderParamImpl::DecodeRFC2047Header(const char* aHeaderVal,
                                            const char* aDefaultCharset,
                                            bool aOverrideCharset,
                                            bool aEatContinuations,
                                            nsACString& aResult)
 {
-  return internalDecodeRFC2047Header(aHeaderVal, aDefaultCharset,
+  return internalDecodeRFC2047Header(aHeaderVal,
+                                     nsCString(aDefaultCharset),
                                      aOverrideCharset, aEatContinuations,
                                      aResult);
 }
 
 // true if the character is allowed in a RFC 5987 value
 // see RFC 5987, Section 3.2.1, "attr-char"
 bool IsRFC5987AttrChar(char aChar)
 {
@@ -928,32 +927,32 @@ nsMIMEHeaderParamImpl::DecodeRFC5987Para
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // return the encoding
   aLang.Assign(language);
 
   // finally convert octet sequence to UTF-8 and be done
   nsAutoCString utf8;
-  nsresult rv = ConvertStringToUTF8(value, charset.get(), true, false, utf8);
+  nsresult rv = ConvertStringToUTF8(value, charset, true, false, utf8);
   NS_ENSURE_SUCCESS(rv, rv);
 
   CopyUTF8toUTF16(utf8, aResult);
   return NS_OK;
 }
 
 nsresult
-internalDecodeParameter(const nsACString& aParamValue, const char* aCharset,
-                        const char* aDefaultCharset, bool aOverrideCharset,
+internalDecodeParameter(const nsACString& aParamValue, const nsACString& aCharset,
+                        const nsACString& aDefaultCharset, bool aOverrideCharset,
                         bool aDecode2047, nsACString& aResult)
 {
   aResult.Truncate();
   // If aCharset is given, aParamValue was obtained from RFC2231/5987
   // encoding and we're pretty sure that it's in aCharset.
-  if (aCharset && *aCharset)
+  if (!aCharset.IsEmpty())
   {
     return ConvertStringToUTF8(aParamValue, aCharset, true, true, aResult);
   }
 
   const nsCString& param = PromiseFlatCString(aParamValue);
   nsAutoCString unQuoted;
   nsACString::const_iterator s, e;
   param.BeginReading(s);
@@ -992,17 +991,18 @@ internalDecodeParameter(const nsACString
 
 NS_IMETHODIMP
 nsMIMEHeaderParamImpl::DecodeParameter(const nsACString& aParamValue,
                                        const char* aCharset,
                                        const char* aDefaultCharset,
                                        bool aOverrideCharset,
                                        nsACString& aResult)
 {
-  return internalDecodeParameter(aParamValue, aCharset, aDefaultCharset,
+  return internalDecodeParameter(aParamValue, nsCString(aCharset),
+                                 nsCString(aDefaultCharset),
                                  aOverrideCharset, true, aResult);
 }
 
 #define ISHEXCHAR(c) \
         ((0x30 <= uint8_t(c) && uint8_t(c) <= 0x39)  ||  \
          (0x41 <= uint8_t(c) && uint8_t(c) <= 0x46)  ||  \
          (0x61 <= uint8_t(c) && uint8_t(c) <= 0x66))
 
@@ -1106,22 +1106,22 @@ bool Is7bitNonAsciiString(const char *in
 // copy 'raw' sequences of octets in aInput to aOutput.
 // If aDefaultCharset is specified, the input is assumed to be in the
 // charset and converted to UTF-8. Otherwise, a blind copy is made.
 // If aDefaultCharset is specified, but the conversion to UTF-8
 // is not successful, each octet is replaced by Unicode replacement
 // chars. *aOutput is advanced by the number of output octets.
 // static
 void CopyRawHeader(const char *aInput, uint32_t aLen,
-                   const char *aDefaultCharset, nsACString &aOutput)
+                   const nsACString& aDefaultCharset, nsACString &aOutput)
 {
   int32_t c;
 
   // If aDefaultCharset is not specified, make a blind copy.
-  if (!aDefaultCharset || !*aDefaultCharset) {
+  if (aDefaultCharset.IsEmpty()) {
     aOutput.Append(aInput, aLen);
     return;
   }
 
   // Copy as long as it's US-ASCII.  An ESC may indicate ISO 2022
   // A ~ may indicate it is HZ
   while (aLen && (c = uint8_t(*aInput++)) != 0x1B && c != '~' && !(c & 0x80)) {
     aOutput.Append(char(c));
@@ -1130,38 +1130,38 @@ void CopyRawHeader(const char *aInput, u
   if (!aLen) {
     return;
   }
   aInput--;
 
   // skip ASCIIness/UTF8ness test if aInput is supected to be a 7bit non-ascii
   // string and aDefaultCharset is a 7bit non-ascii charset.
   bool skipCheck = (c == 0x1B || c == '~') &&
-                     IS_7BIT_NON_ASCII_CHARSET(aDefaultCharset);
+                     IS_7BIT_NON_ASCII_CHARSET(PromiseFlatCString(aDefaultCharset).get());
 
   // If not UTF-8, treat as default charset
   nsAutoCString utf8Text;
   if (NS_SUCCEEDED(
       ConvertStringToUTF8(Substring(aInput, aInput + aLen),
-                          aDefaultCharset, skipCheck, true,
-                          utf8Text))) {
+                          PromiseFlatCString(aDefaultCharset),
+                          skipCheck, true, utf8Text))) {
     aOutput.Append(utf8Text);
   } else { // replace each octet with Unicode replacement char in UTF-8.
     for (uint32_t i = 0; i < aLen; i++) {
       c = uint8_t(*aInput++);
       if (c & 0x80)
         aOutput.Append(REPLACEMENT_CHAR);
       else
         aOutput.Append(char(c));
     }
   }
 }
 
 nsresult DecodeQOrBase64Str(const char *aEncoded, size_t aLen, char aQOrBase64,
-                            const char *aCharset, nsACString &aResult)
+                            const nsACString& aCharset, nsACString &aResult)
 {
   char *decodedText;
   NS_ASSERTION(aQOrBase64 == 'Q' || aQOrBase64 == 'B', "Should be 'Q' or 'B'");
   if(aQOrBase64 == 'Q')
     decodedText = DecodeQ(aEncoded, aLen);
   else if (aQOrBase64 == 'B') {
     decodedText = PL_Base64Decode(aEncoded, aLen, nullptr);
   } else {
@@ -1171,17 +1171,17 @@ nsresult DecodeQOrBase64Str(const char *
   if (!decodedText) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsAutoCString utf8Text;
   // skip ASCIIness/UTF8ness test if aCharset is 7bit non-ascii charset.
   nsresult rv = ConvertStringToUTF8(nsDependentCString(decodedText),
                                     aCharset,
-                                    IS_7BIT_NON_ASCII_CHARSET(aCharset),
+                                    IS_7BIT_NON_ASCII_CHARSET(PromiseFlatCString(aCharset).get()),
                                     true, utf8Text);
   free(decodedText);
   if (NS_FAILED(rv)) {
     return rv;
   }
   aResult.Append(utf8Text);
 
   return NS_OK;
@@ -1190,17 +1190,17 @@ nsresult DecodeQOrBase64Str(const char *
 static const char especials[] = R"(()<>@,;:\"/[]?.=)";
 
 // |decode_mime_part2_str| taken from comi18n.c
 // Decode RFC2047-encoded words in the input and convert the result to UTF-8.
 // If aOverrideCharset is true, charset in RFC2047-encoded words is
 // ignored and aDefaultCharset is assumed, instead. aDefaultCharset
 // is also used to convert raw octets (without RFC 2047 encoding) to UTF-8.
 //static
-nsresult DecodeRFC2047Str(const char *aHeader, const char *aDefaultCharset,
+nsresult DecodeRFC2047Str(const char *aHeader, const nsACString& aDefaultCharset,
                           bool aOverrideCharset, nsACString &aResult)
 {
   const char *p, *q = nullptr, *r;
   const char *begin; // tracking pointer for where we are in the input buffer
   int32_t isLastEncodedWord = 0;
   const char *charsetStart, *charsetEnd;
   nsAutoCString prevCharset, curCharset;
   nsAutoCString encodedText;
@@ -1223,17 +1223,17 @@ nsresult DecodeRFC2047Str(const char *aH
       for (q = begin; q < p; ++q) {
         if (!PL_strchr(" \t\r\n", *q)) break;
       }
     }
 
     if (!isLastEncodedWord || q < p) {
       if (!encodedText.IsEmpty()) {
         rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(),
-                                prevEncoding, prevCharset.get(), aResult);
+                                prevEncoding, prevCharset, aResult);
         if (NS_FAILED(rv)) {
           aResult.Append(encodedText);
         }
         encodedText.Truncate();
         prevCharset.Truncate();
         prevEncoding = '\0';
       }
       // copy the part before the encoded-word
@@ -1281,17 +1281,17 @@ nsresult DecodeRFC2047Str(const char *aH
         isLastEncodedWord = 1;
         continue;
     }
 
     curCharset.Assign(charsetStart, charsetEnd - charsetStart);
     // Override charset if requested.  Never override labeled UTF-8.
     // Use default charset instead of UNKNOWN-8BIT
     if ((aOverrideCharset && 0 != nsCRT::strcasecmp(curCharset.get(), "UTF-8"))
-    || (aDefaultCharset && 0 == nsCRT::strcasecmp(curCharset.get(), "UNKNOWN-8BIT"))
+    || (!aDefaultCharset.IsEmpty() && 0 == nsCRT::strcasecmp(curCharset.get(), "UNKNOWN-8BIT"))
     ) {
       curCharset = aDefaultCharset;
     }
 
     const char *R;
     R = r;
     if (curEncoding == 'B') {
       // bug 227290. ignore an extraneous '=' at the end.
@@ -1316,55 +1316,55 @@ nsresult DecodeRFC2047Str(const char *aH
     bool bDecoded; // If the current line has been decoded.
     bDecoded = false;
     if (!encodedText.IsEmpty()) {
       if (curCharset == prevCharset && curEncoding == prevEncoding) {
         encodedText.Append(q + 2, R - (q + 2));
         bDecoded = true;
       }
       rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(),
-                              prevEncoding, prevCharset.get(), aResult);
+                              prevEncoding, prevCharset, aResult);
       if (NS_FAILED(rv)) {
         aResult.Append(encodedText);
       }
       encodedText.Truncate();
       prevCharset.Truncate();
       prevEncoding = '\0';
     }
     if (!bDecoded) {
       rv = DecodeQOrBase64Str(q + 2, R - (q + 2), curEncoding,
-                              curCharset.get(), aResult);
+                              curCharset, aResult);
       if (NS_FAILED(rv)) {
         aResult.Append(encodedText);
       }
     }
 
     begin = r + 2;
     isLastEncodedWord = 1;
     continue;
 
   badsyntax:
     if (!encodedText.IsEmpty()) {
       rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(),
-                              prevEncoding, prevCharset.get(), aResult);
+                              prevEncoding, prevCharset, aResult);
       if (NS_FAILED(rv)) {
         aResult.Append(encodedText);
       }
       encodedText.Truncate();
       prevCharset.Truncate();
     }
     // copy the part before the encoded-word
     aResult.Append(begin, p - begin);
     begin = p;
     isLastEncodedWord = 0;
   }
 
   if (!encodedText.IsEmpty()) {
     rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(),
-                            prevEncoding, prevCharset.get(), aResult);
+                            prevEncoding, prevCharset, aResult);
     if (NS_FAILED(rv)) {
       aResult.Append(encodedText);
     }
   }
 
   // put the tail back
   CopyRawHeader(begin, strlen(begin), aDefaultCharset, aResult);
 
--- a/netwerk/streamconv/converters/moz.build
+++ b/netwerk/streamconv/converters/moz.build
@@ -3,16 +3,20 @@
 # 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/.
 
 XPIDL_SOURCES += [
     'nsICompressConvStats.idl'
 ]
 
+EXPORTS += [
+    'nsUnknownDecoder.h',
+]
+
 XPIDL_MODULE = 'necko_http'
 
 UNIFIED_SOURCES += [
     'mozTXTToHTMLConv.cpp',
     'nsDirIndex.cpp',
     'nsDirIndexParser.cpp',
     'nsFTPDirListingConv.cpp',
     'nsHTTPCompressConv.cpp',
--- a/old-configure.in
+++ b/old-configure.in
@@ -2612,16 +2612,19 @@ esac
 # The DirectX SDK libraries are split into x86 and x64 sub-directories
 case "${target_cpu}" in
 i*86)
   MOZ_D3D_CPU_SUFFIX=x86
   ;;
 x86_64)
   MOZ_D3D_CPU_SUFFIX=x64
   ;;
+aarch64)
+  MOZ_D3D_CPU_SUFFIX=arm
+  ;;
 esac
 
 dnl ========================================================
 dnl D3D compiler DLL
 dnl ========================================================
 MOZ_FOUND_D3D_COMPILERS=
 
 if test -n "$MOZ_ANGLE_RENDERER"; then
@@ -2643,16 +2646,19 @@ if test -n "$MOZ_ANGLE_RENDERER"; then
 
   if test -n "$MOZ_D3DCOMPILER_VISTA_DLL"; then
     # We have a name, now track down the path.
     if test -n "$WINDOWSSDKDIR"; then
       MOZ_D3DCOMPILER_VISTA_DLL_PATH="$WINDOWSSDKDIR/Redist/D3D/$MOZ_D3D_CPU_SUFFIX/$MOZ_D3DCOMPILER_VISTA_DLL"
       if test -f "$MOZ_D3DCOMPILER_VISTA_DLL_PATH"; then
         AC_MSG_RESULT([Found MOZ_D3DCOMPILER_VISTA_DLL_PATH: $MOZ_D3DCOMPILER_VISTA_DLL_PATH])
         MOZ_HAS_WINSDK_WITH_D3D=1
+      elif test "$target_cpu" = "aarch64" -a "$MOZ_D3DCOMPILER_VISTA_DLL" = "d3dcompiler_47.dll"; then
+        AC_MSG_RESULT([AArch64 Windows includes d3dcompiler DLLs])
+        MOZ_D3DCOMPILER_VISTA_DLL_PATH=
       else
         AC_MSG_RESULT([MOZ_D3DCOMPILER_VISTA_DLL_PATH doesn't exist: $MOZ_D3DCOMPILER_VISTA_DLL_PATH])
         AC_MSG_ERROR([Windows SDK at "$WINDOWSSDKDIR" appears broken. Try updating to MozillaBuild 1.9 final or higher.])
         MOZ_D3DCOMPILER_VISTA_DLL_PATH=
       fi
     else
       AC_MSG_RESULT([Windows SDK not found.])
     fi
--- a/security/manager/ssl/nsSecureBrowserUIImpl.cpp
+++ b/security/manager/ssl/nsSecureBrowserUIImpl.cpp
@@ -260,19 +260,34 @@ nsSecureBrowserUIImpl::MapInternalToExte
 
   if (docShell->GetHasMixedDisplayContentBlocked())
     *aState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT;
 
   // Has Tracking Content been Blocked?
   if (docShell->GetHasTrackingContentBlocked())
     *aState |= nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
 
+  if (docShell->GetHasSlowTrackingContentBlocked())
+    *aState |= nsIWebProgressListener::STATE_BLOCKED_SLOW_TRACKING_CONTENT;
+
   if (docShell->GetHasTrackingContentLoaded())
     *aState |= nsIWebProgressListener::STATE_LOADED_TRACKING_CONTENT;
 
+  if (docShell->GetHasCookiesBlockedByPermission())
+    *aState |= nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION;
+
+  if (docShell->GetHasCookiesBlockedDueToTrackers())
+    *aState |= nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER;
+
+  if (docShell->GetHasForeignCookiesBeenBlocked())
+    *aState |= nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN;
+
+  if (docShell->GetHasAllCookiesBeenBlocked())
+    *aState |= nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL;
+
   // Copy forward any diagnostic flags for downstream use (e.g., warnings)
   if (mNewToplevelSecurityStateKnown &&
       mNewToplevelSecurityState & STATE_CERT_DISTRUST_IMMINENT) {
     *aState |= nsIWebProgressListener::STATE_CERT_DISTRUST_IMMINENT;
   }
 
   return NS_OK;
 }
--- a/testing/mozbase/rust/mozprofile/src/lib.rs
+++ b/testing/mozbase/rust/mozprofile/src/lib.rs
@@ -1,157 +1,148 @@
 extern crate tempdir;
 
-pub mod profile;
 pub mod preferences;
 pub mod prefreader;
-
+pub mod profile;
 
 #[cfg(test)]
 mod test {
-//    use std::fs::File;
-//    use profile::Profile;
-    use prefreader::{parse, tokenize, serialize};
-    use prefreader::{PrefToken, Position};
+    //    use std::fs::File;
+    //    use profile::Profile;
     use preferences::Pref;
+    use prefreader::{parse, serialize, tokenize};
+    use prefreader::{Position, PrefToken};
     use std::collections::BTreeMap;
     use std::error::Error;
     use std::io::Cursor;
     use std::str;
 
     #[test]
     fn tokenize_simple() {
-        let prefs = "  user_pref ( 'example.pref.string', 'value' )   ;\n pref(\"example.pref.int\", -123); sticky_pref('example.pref.bool',false);";
+        let prefs = "  user_pref ( 'example.pref.string', 'value' )   ;\n \
+                     pref(\"example.pref.int\", -123); sticky_pref('example.pref.bool',false);";
 
         let p = Position::new();
 
-        let expected = vec![PrefToken::UserPrefFunction(p),
-                            PrefToken::Paren('(', p),
-                            PrefToken::String("example.pref.string".into(), p),
-                            PrefToken::Comma(p),
-                            PrefToken::String("value".into(), p),
-                            PrefToken::Paren(')', p),
-                            PrefToken::Semicolon(p),
-                            PrefToken::PrefFunction(p),
-                            PrefToken::Paren('(', p),
-                            PrefToken::String("example.pref.int".into(), p),
-                            PrefToken::Comma(p),
-                            PrefToken::Int(-123, p),
-                            PrefToken::Paren(')', p),
-                            PrefToken::Semicolon(p),
-                            PrefToken::StickyPrefFunction(p),
-                            PrefToken::Paren('(', p),
-                            PrefToken::String("example.pref.bool".into(), p),
-                            PrefToken::Comma(p),
-                            PrefToken::Bool(false, p),
-                            PrefToken::Paren(')', p),
-                            PrefToken::Semicolon(p)];
+        let expected = vec![
+            PrefToken::UserPrefFunction(p),
+            PrefToken::Paren('(', p),
+            PrefToken::String("example.pref.string".into(), p),
+            PrefToken::Comma(p),
+            PrefToken::String("value".into(), p),
+            PrefToken::Paren(')', p),
+            PrefToken::Semicolon(p),
+            PrefToken::PrefFunction(p),
+            PrefToken::Paren('(', p),
+            PrefToken::String("example.pref.int".into(), p),
+            PrefToken::Comma(p),
+            PrefToken::Int(-123, p),
+            PrefToken::Paren(')', p),
+            PrefToken::Semicolon(p),
+            PrefToken::StickyPrefFunction(p),
+            PrefToken::Paren('(', p),
+            PrefToken::String("example.pref.bool".into(), p),
+            PrefToken::Comma(p),
+            PrefToken::Bool(false, p),
+            PrefToken::Paren(')', p),
+            PrefToken::Semicolon(p),
+        ];
 
         tokenize_test(prefs, &expected);
     }
 
     #[test]
     fn tokenize_comments() {
-        let prefs = "# bash style comment\n /*block comment*/ user_pref/*block comment*/(/*block comment*/ 'example.pref.string'  /*block comment*/,/*block comment*/ 'value'/*block comment*/ )// line comment";
+        let prefs = "# bash style comment\n /*block comment*/ user_pref/*block comment*/(/*block \
+                     comment*/ 'example.pref.string'  /*block comment*/,/*block comment*/ \
+                     'value'/*block comment*/ )// line comment";
 
         let p = Position::new();
 
-        let expected = vec![PrefToken::CommentBashLine(" bash style comment".into(), p),
-                            PrefToken::CommentBlock("block comment".into(), p),
-                            PrefToken::UserPrefFunction(p),
-                            PrefToken::CommentBlock("block comment".into(), p),
-                            PrefToken::Paren('(', p),
-                            PrefToken::CommentBlock("block comment".into(), p),
-                            PrefToken::String("example.pref.string".into(), p),
-                            PrefToken::CommentBlock("block comment".into(), p),
-                            PrefToken::Comma(p),
-                            PrefToken::CommentBlock("block comment".into(), p),
-                            PrefToken::String("value".into(), p),
-                            PrefToken::CommentBlock("block comment".into(), p),
-                            PrefToken::Paren(')', p),
-                            PrefToken::CommentLine(" line comment".into(), p)];
+        let expected = vec![
+            PrefToken::CommentBashLine(" bash style comment".into(), p),
+            PrefToken::CommentBlock("block comment".into(), p),
+            PrefToken::UserPrefFunction(p),
+            PrefToken::CommentBlock("block comment".into(), p),
+            PrefToken::Paren('(', p),
+            PrefToken::CommentBlock("block comment".into(), p),
+            PrefToken::String("example.pref.string".into(), p),
+            PrefToken::CommentBlock("block comment".into(), p),
+            PrefToken::Comma(p),
+            PrefToken::CommentBlock("block comment".into(), p),
+            PrefToken::String("value".into(), p),
+            PrefToken::CommentBlock("block comment".into(), p),
+            PrefToken::Paren(')', p),
+            PrefToken::CommentLine(" line comment".into(), p),
+        ];
 
         tokenize_test(prefs, &expected);
     }
 
     #[test]
     fn tokenize_escapes() {
         let prefs = r#"user_pref('example\x20pref', "\u0020\u2603\uD800\uDC96\"\'\n\r\\\w)"#;
 
         let p = Position::new();
 
-        let expected = vec![PrefToken::UserPrefFunction(p),
-                            PrefToken::Paren('(', p),
-                            PrefToken::String("example pref".into(), p),
-                            PrefToken::Comma(p),
-                            PrefToken::String(" ☃𐂖\"'\n\r\\\\w".into(), p),
-                            PrefToken::Paren(')', p)];
+        let expected = vec![
+            PrefToken::UserPrefFunction(p),
+            PrefToken::Paren('(', p),
+            PrefToken::String("example pref".into(), p),
+            PrefToken::Comma(p),
+            PrefToken::String(" ☃𐂖\"'\n\r\\\\w".into(), p),
+            PrefToken::Paren(')', p),
+        ];
 
         tokenize_test(prefs, &expected);
     }
 
     fn tokenize_test(prefs: &str, expected: &[PrefToken]) {
         println!("{}\n", prefs);
 
         for (e, a) in expected.iter().zip(tokenize(prefs.as_bytes())) {
             let success = match (e, &a) {
-                (&PrefToken::PrefFunction(_),
-                 &PrefToken::PrefFunction(_)) => true,
-                (&PrefToken::UserPrefFunction(_),
-                 &PrefToken::UserPrefFunction(_)) => true,
-                (&PrefToken::StickyPrefFunction(_),
-                 &PrefToken::StickyPrefFunction(_)) => true,
-                (&PrefToken::CommentBlock(ref data_e, _),
-                 &PrefToken::CommentBlock(ref data_a, _)) => {
-                    data_e == data_a
-                },
-                (&PrefToken::CommentLine(ref data_e, _),
-                 &PrefToken::CommentLine(ref data_a, _)) => {
-                    data_e == data_a
-                },
-                (&PrefToken::CommentBashLine(ref data_e, _),
-                 &PrefToken::CommentBashLine(ref data_a, _))  => {
-                    data_e == data_a
-                },
-                (&PrefToken::Paren(data_e, _),
-                 &PrefToken::Paren(data_a, _)) => {
+                (&PrefToken::PrefFunction(_), &PrefToken::PrefFunction(_)) => true,
+                (&PrefToken::UserPrefFunction(_), &PrefToken::UserPrefFunction(_)) => true,
+                (&PrefToken::StickyPrefFunction(_), &PrefToken::StickyPrefFunction(_)) => true,
+                (
+                    &PrefToken::CommentBlock(ref data_e, _),
+                    &PrefToken::CommentBlock(ref data_a, _),
+                ) => data_e == data_a,
+                (
+                    &PrefToken::CommentLine(ref data_e, _),
+                    &PrefToken::CommentLine(ref data_a, _),
+                ) => data_e == data_a,
+                (
+                    &PrefToken::CommentBashLine(ref data_e, _),
+                    &PrefToken::CommentBashLine(ref data_a, _),
+                ) => data_e == data_a,
+                (&PrefToken::Paren(data_e, _), &PrefToken::Paren(data_a, _)) => data_e == data_a,
+                (&PrefToken::Semicolon(_), &PrefToken::Semicolon(_)) => true,
+                (&PrefToken::Comma(_), &PrefToken::Comma(_)) => true,
+                (&PrefToken::String(ref data_e, _), &PrefToken::String(ref data_a, _)) => {
                     data_e == data_a
-                },
-                (&PrefToken::Semicolon(_),
-                 &PrefToken::Semicolon(_)) => true,
-                (&PrefToken::Comma(_),
-                 &PrefToken::Comma(_)) => true,
-                (&PrefToken::String(ref data_e, _),
-                 &PrefToken::String(ref data_a, _)) => {
-                    data_e == data_a
-                },
-                (&PrefToken::Int(data_e, _),
-                 &PrefToken::Int(data_a, _)) => {
-                    data_e == data_a
-                },
-                (&PrefToken::Bool(data_e, _),
-                 &PrefToken::Bool(data_a, _)) => {
-                    data_e == data_a
-                },
-                (&PrefToken::Error(data_e, _),
-                 &PrefToken::Error(data_a, _)) => {
-                    data_e == data_a
-                },
-                (_, _) => false
+                }
+                (&PrefToken::Int(data_e, _), &PrefToken::Int(data_a, _)) => data_e == data_a,
+                (&PrefToken::Bool(data_e, _), &PrefToken::Bool(data_a, _)) => data_e == data_a,
+                (&PrefToken::Error(data_e, _), &PrefToken::Error(data_a, _)) => data_e == data_a,
+                (_, _) => false,
             };
             if !success {
                 println!("Expected {:?}, got {:?}", e, a);
             }
             assert!(success);
         }
     }
 
     #[test]
     fn parse_simple() {
-        let input = "  user_pref /* block comment */ ( 'example.pref.string', 'value' )   ;\n pref(\"example.pref.int\", -123); sticky_pref('example.pref.bool',false)";
+        let input = "  user_pref /* block comment */ ( 'example.pref.string', 'value' )   ;\n \
+                     pref(\"example.pref.int\", -123); sticky_pref('example.pref.bool',false)";
 
         let mut expected: BTreeMap<String, Pref> = BTreeMap::new();
         expected.insert("example.pref.string".into(), Pref::new("value"));
         expected.insert("example.pref.int".into(), Pref::new(-123));
         expected.insert("example.pref.bool".into(), Pref::new_sticky(false));
 
         parse_test(input, expected);
     }
@@ -172,21 +163,22 @@ mod test {
                 println!("Expected:\n{:?}\nActual\n{:?}", expected, actual);
                 assert_eq!(actual, &expected);
             }
             Err(e) => {
                 println!("{}", e.description());
                 assert!(false)
             }
         }
-   }
+    }
 
     #[test]
     fn serialize_simple() {
-        let input = "  user_pref /* block comment */ ( 'example.pref.string', 'value' )   ;\n pref(\"example.pref.int\", -123); sticky_pref('example.pref.bool',false)";
+        let input = "  user_pref /* block comment */ ( 'example.pref.string', 'value' )   ;\n \
+                     pref(\"example.pref.int\", -123); sticky_pref('example.pref.bool',false)";
         let expected = "sticky_pref(\"example.pref.bool\", false);
 user_pref(\"example.pref.int\", -123);
 user_pref(\"example.pref.string\", \"value\");\n";
 
         serialize_test(input, expected);
     }
 
     #[test]
--- a/testing/mozbase/rust/mozprofile/src/preferences.rs
+++ b/testing/mozbase/rust/mozprofile/src/preferences.rs
@@ -107,17 +107,20 @@ mod test {
     #[test]
     fn test_bool() {
         assert_eq!(PrefValue::from(true), PrefValue::Bool(true));
     }
 
     #[test]
     fn test_string() {
         assert_eq!(PrefValue::from("foo"), PrefValue::String("foo".to_string()));
-        assert_eq!(PrefValue::from("foo".to_string()), PrefValue::String("foo".to_string()));
+        assert_eq!(
+            PrefValue::from("foo".to_string()),
+            PrefValue::String("foo".to_string())
+        );
     }
 
     #[test]
     fn test_int() {
         assert_eq!(PrefValue::from(42i8), PrefValue::Int(42i64));
         assert_eq!(PrefValue::from(42u8), PrefValue::Int(42i64));
         assert_eq!(PrefValue::from(42i16), PrefValue::Int(42i64));
         assert_eq!(PrefValue::from(42u16), PrefValue::Int(42i64));
--- a/testing/mozbase/rust/mozprofile/src/prefreader.rs
+++ b/testing/mozbase/rust/mozprofile/src/prefreader.rs
@@ -1,57 +1,60 @@
 use preferences::{Pref, PrefValue, Preferences};
 use std::borrow::Borrow;
 use std::borrow::Cow;
 use std::char;
 use std::error::Error;
 use std::fmt;
 use std::io::{self, Write};
 use std::iter::Iterator;
-use std::str;
 use std::mem;
 use std::ops::Deref;
+use std::str;
 
 impl PrefReaderError {
-    fn new(message: &'static str, position: Position, parent: Option<Box<Error>>) -> PrefReaderError {
+    fn new(
+        message: &'static str,
+        position: Position,
+        parent: Option<Box<Error>>,
+    ) -> PrefReaderError {
         PrefReaderError {
-            message: message,
-            position: position,
-            parent: parent
+            message,
+            position,
+            parent,
         }
     }
 }
 
-
 impl fmt::Display for PrefReaderError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{} at line {}, column {}",
-               self.message, self.position.line, self.position.column)
+        write!(
+            f,
+            "{} at line {}, column {}",
+            self.message, self.position.line, self.position.column
+        )
     }
 }
 
-
 impl Error for PrefReaderError {
     fn description(&self) -> &str {
         self.message
     }
 
     fn cause(&self) -> Option<&Error> {
         match self.parent {
             None => None,
-            Some(ref cause) => Some(cause.deref())
+            Some(ref cause) => Some(cause.deref()),
         }
     }
 }
 
 impl From<io::Error> for PrefReaderError {
     fn from(err: io::Error) -> PrefReaderError {
-        PrefReaderError::new("IOError",
-                             Position::new(),
-                             Some(err.into()))
+        PrefReaderError::new("IOError", Position::new(), Some(err.into()))
     }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 enum TokenizerState {
     Junk,
     CommentStart,
     CommentLine,
@@ -64,28 +67,25 @@ enum TokenizerState {
     SingleQuotedString,
     Number,
     Bool,
     AfterFunctionArg,
     AfterFunction,
     Error,
 }
 
-#[derive(Copy, Clone, Debug, PartialEq)]
+#[derive(Copy, Clone, Debug, Default, PartialEq)]
 pub struct Position {
     line: u32,
-    column: u32
+    column: u32,
 }
 
 impl Position {
     pub fn new() -> Position {
-        Position {
-            line: 1,
-            column: 0
-        }
+        Position { line: 1, column: 0 }
     }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum TokenType {
     None,
     PrefFunction,
     UserPrefFunction,
@@ -94,34 +94,34 @@ pub enum TokenType {
     CommentLine,
     CommentBashLine,
     Paren,
     Semicolon,
     Comma,
     String,
     Int,
     Bool,
-    Error
+    Error,
 }
 
 #[derive(Debug, PartialEq)]
 pub enum PrefToken<'a> {
     PrefFunction(Position),
     UserPrefFunction(Position),
     StickyPrefFunction(Position),
     CommentBlock(Cow<'a, str>, Position),
     CommentLine(Cow<'a, str>, Position),
     CommentBashLine(Cow<'a, str>, Position),
     Paren(char, Position),
     Semicolon(Position),
     Comma(Position),
     String(Cow<'a, str>, Position),
     Int(i64, Position),
     Bool(bool, Position),
-    Error(&'static str, Position)
+    Error(&'static str, Position),
 }
 
 impl<'a> PrefToken<'a> {
     fn position(&self) -> Position {
         match *self {
             PrefToken::PrefFunction(position) => position,
             PrefToken::UserPrefFunction(position) => position,
             PrefToken::StickyPrefFunction(position) => position,
@@ -129,44 +129,44 @@ impl<'a> PrefToken<'a> {
             PrefToken::CommentLine(_, position) => position,
             PrefToken::CommentBashLine(_, position) => position,
             PrefToken::Paren(_, position) => position,
             PrefToken::Semicolon(position) => position,
             PrefToken::Comma(position) => position,
             PrefToken::String(_, position) => position,
             PrefToken::Int(_, position) => position,
             PrefToken::Bool(_, position) => position,
-            PrefToken::Error(_, position) => position
+            PrefToken::Error(_, position) => position,
         }
     }
 }
 
 #[derive(Debug)]
 pub struct PrefReaderError {
     message: &'static str,
     position: Position,
-    parent: Option<Box<Error>>
+    parent: Option<Box<Error>>,
 }
 
 struct TokenData<'a> {
     token_type: TokenType,
     complete: bool,
     position: Position,
     data: Cow<'a, str>,
     start_pos: usize,
 }
 
 impl<'a> TokenData<'a> {
     fn new(token_type: TokenType, position: Position, start_pos: usize) -> TokenData<'a> {
         TokenData {
-            token_type: token_type,
+            token_type,
             complete: false,
-            position: position,
+            position,
             data: Cow::Borrowed(""),
-            start_pos: start_pos,
+            start_pos,
         }
     }
 
     fn start(&mut self, tokenizer: &PrefTokenizer, token_type: TokenType) {
         self.token_type = token_type;
         self.position = tokenizer.position;
         self.start_pos = tokenizer.pos;
     }
@@ -175,18 +175,21 @@ impl<'a> TokenData<'a> {
         self.complete = true;
         self.add_slice_to_token(buf, end_pos)
     }
 
     fn add_slice_to_token(&mut self, buf: &'a [u8], end_pos: usize) -> Result<(), PrefReaderError> {
         let data = match str::from_utf8(&buf[self.start_pos..end_pos]) {
             Ok(x) => x,
             Err(_) => {
-                return Err(PrefReaderError::new("Could not convert string to utf8",
-                                               self.position, None));
+                return Err(PrefReaderError::new(
+                    "Could not convert string to utf8",
+                    self.position,
+                    None,
+                ));
             }
         };
         if self.data != "" {
             self.data.to_mut().push_str(&data)
         } else {
             self.data = Cow::Borrowed(&data)
         };
         Ok(())
@@ -205,17 +208,17 @@ pub struct PrefTokenizer<'a> {
     position: Position,
     state: TokenizerState,
     next_state: Option<TokenizerState>,
 }
 
 impl<'a> PrefTokenizer<'a> {
     pub fn new(data: &'a [u8]) -> PrefTokenizer<'a> {
         PrefTokenizer {
-            data: data,
+            data,
             pos: 0,
             cur: None,
             position: Position::new(),
             state: TokenizerState::Junk,
             next_state: Some(TokenizerState::FunctionName),
         }
     }
 
@@ -230,41 +233,41 @@ impl<'a> PrefTokenizer<'a> {
             TokenType::CommentBlock => PrefToken::CommentBlock(buf, position),
             TokenType::CommentLine => PrefToken::CommentLine(buf, position),
             TokenType::CommentBashLine => PrefToken::CommentBashLine(buf, position),
             TokenType::Paren => {
                 if buf.len() != 1 {
                     panic!("Expected a buffer of length one");
                 }
                 PrefToken::Paren(buf.chars().next().unwrap(), position)
-            },
+            }
             TokenType::Semicolon => PrefToken::Semicolon(position),
             TokenType::Comma => PrefToken::Comma(position),
             TokenType::String => PrefToken::String(buf, position),
             TokenType::Int => {
-                let value = i64::from_str_radix(buf.borrow(), 10)
-                    .expect("Integer wasn't parsed as an i64");
+                let value =
+                    i64::from_str_radix(buf.borrow(), 10).expect("Integer wasn't parsed as an i64");
                 PrefToken::Int(value, position)
-            },
+            }
             TokenType::Bool => {
                 let value = match buf.borrow() {
                     "true" => true,
                     "false" => false,
-                    x => panic!(format!("Boolean wasn't 'true' or 'false' (was {})", x))
+                    x => panic!(format!("Boolean wasn't 'true' or 'false' (was {})", x)),
                 };
                 PrefToken::Bool(value, position)
-            },
-            TokenType::Error => panic!("make_token can't construct errors")
+            }
+            TokenType::Error => panic!("make_token can't construct errors"),
         }
     }
 
     fn get_char(&mut self) -> Option<char> {
         if self.pos >= self.data.len() - 1 {
             self.cur = None;
-            return None
+            return None;
         };
         if self.cur.is_some() {
             self.pos += 1;
         }
         let c = self.data[self.pos] as char;
         if self.cur == Some('\n') {
             self.position.line += 1;
             self.position.column = 0;
@@ -295,97 +298,119 @@ impl<'a> PrefTokenizer<'a> {
             self.cur = Some(c);
         }
         self.cur
     }
 
     fn is_space(c: char) -> bool {
         match c {
             ' ' | '\t' | '\r' | '\n' => true,
-            _ => false
+            _ => false,
         }
     }
 
     fn skip_whitespace(&mut self) -> Option<char> {
         while let Some(c) = self.cur {
             if PrefTokenizer::is_space(c) {
                 self.get_char();
             } else {
-                break
+                break;
             };
         }
         self.cur
     }
 
     fn consume_escape(&mut self, token_data: &mut TokenData<'a>) -> Result<(), PrefReaderError> {
         let pos = self.pos;
-        let escaped = try!(self.read_escape());
+        let escaped = self.read_escape()?;
         if let Some(escape_char) = escaped {
-            try!(token_data.add_slice_to_token(&self.data, pos));
+            token_data.add_slice_to_token(&self.data, pos)?;
             token_data.push_char(&self, escape_char);
         };
         Ok(())
     }
 
     fn read_escape(&mut self) -> Result<Option<char>, PrefReaderError> {
         let escape_char = match self.get_char() {
-            Some('u') => try!(self.read_hex_escape(4, true)),
-            Some('x') => try!(self.read_hex_escape(2, true)),
+            Some('u') => self.read_hex_escape(4, true)?,
+            Some('x') => self.read_hex_escape(2, true)?,
             Some('\\') => '\\' as u32,
             Some('"') => '"' as u32,
             Some('\'') => '\'' as u32,
             Some('r') => '\r' as u32,
             Some('n') => '\n' as u32,
             Some(_) => return Ok(None),
-            None => return Err(PrefReaderError::new("EOF in character escape",
-                                                   self.position, None))
+            None => {
+                return Err(PrefReaderError::new(
+                    "EOF in character escape",
+                    self.position,
+                    None,
+                ))
+            }
         };
-        Ok(Some(try!(char::from_u32(escape_char)
-                     .ok_or(PrefReaderError::new("Invalid codepoint decoded from escape",
-                                                 self.position, None)))))
+        Ok(Some(char::from_u32(escape_char).ok_or_else(|| {
+            PrefReaderError::new("Invalid codepoint decoded from escape", self.position, None)
+        })?))
     }
 
     fn read_hex_escape(&mut self, hex_chars: isize, first: bool) -> Result<u32, PrefReaderError> {
         let mut value = 0;
         for _ in 0..hex_chars {
             match self.get_char() {
                 Some(x) => {
                     value = value << 4;
                     match x {
                         '0'...'9' => value += x as u32 - '0' as u32,
                         'a'...'f' => value += x as u32 - 'a' as u32,
                         'A'...'F' => value += x as u32 - 'A' as u32,
-                        _ => return Err(PrefReaderError::new(
-                            "Unexpected character in escape", self.position, None))
+                        _ => {
+                            return Err(PrefReaderError::new(
+                                "Unexpected character in escape",
+                                self.position,
+                                None,
+                            ))
+                        }
                     }
-                },
-                None => return Err(PrefReaderError::new(
-                    "Unexpected EOF in escape", self.position, None))
+                }
+                None => {
+                    return Err(PrefReaderError::new(
+                        "Unexpected EOF in escape",
+                        self.position,
+                        None,
+                    ))
+                }
             }
         }
         if first && value >= 0xD800 && value <= 0xDBFF {
             // First part of a surrogate pair
             if self.get_char() != Some('\\') || self.get_char() != Some('u') {
-                return Err(PrefReaderError::new("Lone high surrogate in surrogate pair",
-                                               self.position, None))
+                return Err(PrefReaderError::new(
+                    "Lone high surrogate in surrogate pair",
+                    self.position,
+                    None,
+                ));
             }
             self.unget_char();
             let high_surrogate = value;
-            let low_surrogate = try!(self.read_hex_escape(4, false));
+            let low_surrogate = self.read_hex_escape(4, false)?;
             let high_value = (high_surrogate - 0xD800) << 10;
             let low_value = low_surrogate - 0xDC00;
             value = high_value + low_value + 0x10000;
         } else if first && value >= 0xDC00 && value <= 0xDFFF {
-            return Err(PrefReaderError::new("Lone low surrogate",
-                                            self.position,
-                                            None))
+            return Err(PrefReaderError::new(
+                "Lone low surrogate",
+                self.position,
+                None,
+            ));
         } else if !first && (value < 0xDC00 || value > 0xDFFF) {
-            return Err(PrefReaderError::new("Invalid low surrogate in surrogate pair",
-                                            self.position,
-                                            None));
+            return Err(PrefReaderError::new(
+                "Invalid low surrogate in surrogate pair",
+                self.position,
+                None,
+            ));
         }
         Ok(value)
     }
 
     fn get_match(&mut self, target: &str, separators: &str) -> bool {
         let initial_pos = self.pos;
         let mut matched = true;
         for c in target.chars() {
@@ -415,300 +440,303 @@ impl<'a> PrefTokenizer<'a> {
     }
 
     fn next_token(&mut self) -> Result<Option<TokenData<'a>>, PrefReaderError> {
         let mut token_data = TokenData::new(TokenType::None, Position::new(), 0);
 
         loop {
             let mut c = match self.get_char() {
                 Some(x) => x,
-                None => return Ok(None)
+                None => return Ok(None),
             };
 
             self.state = match self.state {
                 TokenizerState::Junk => {
                     c = match self.skip_whitespace() {
                         Some(x) => x,
-                        None => return Ok(None)
+                        None => return Ok(None),
                     };
                     match c {
                         '/' => TokenizerState::CommentStart,
                         '#' => {
                             token_data.start(&self, TokenType::CommentBashLine);
                             token_data.start_pos = self.pos + 1;
                             TokenizerState::CommentLine
-                        },
+                        }
                         _ => {
                             self.unget_char();
                             let next = match self.next_state {
                                 Some(x) => x,
                                 None => {
                                     return Err(PrefReaderError::new(
                                         "In Junk state without a next state defined",
                                         self.position,
-                                        None))
+                                        None,
+                                    ))
                                 }
                             };
                             self.next_state = None;
                             next
                         }
                     }
+                }
+                TokenizerState::CommentStart => match c {
+                    '*' => {
+                        token_data.start(&self, TokenType::CommentBlock);
+                        token_data.start_pos = self.pos + 1;
+                        TokenizerState::CommentBlock
+                    }
+                    '/' => {
+                        token_data.start(&self, TokenType::CommentLine);
+                        token_data.start_pos = self.pos + 1;
+                        TokenizerState::CommentLine
+                    }
+                    _ => {
+                        return Err(PrefReaderError::new(
+                            "Invalid character after /",
+                            self.position,
+                            None,
+                        ))
+                    }
                 },
-                TokenizerState::CommentStart => {
-                    match c {
-                        '*' => {
-                            token_data.start(&self, TokenType::CommentBlock);
-                            token_data.start_pos = self.pos + 1;
+                TokenizerState::CommentLine => match c {
+                    '\n' => {
+                        token_data.end(&self.data, self.pos)?;
+                        TokenizerState::Junk
+                    }
+                    _ => TokenizerState::CommentLine,
+                },
+                TokenizerState::CommentBlock => match c {
+                    '*' => {
+                        if self.get_char() == Some('/') {
+                            token_data.end(&self.data, self.pos - 1)?;
+                            TokenizerState::Junk
+                        } else {
                             TokenizerState::CommentBlock
-                        },
-                        '/' => {
-                            token_data.start(&self, TokenType::CommentLine);
-                            token_data.start_pos = self.pos + 1;
-                            TokenizerState::CommentLine
-                        },
-                        _ => {
-                            return Err(PrefReaderError::new(
-                                "Invalid character after /", self.position, None))
                         }
                     }
-
-                },
-                TokenizerState::CommentLine => {
-                    match c {
-                        '\n' => {
-                            try!(token_data.end(&self.data, self.pos));
-                            TokenizerState::Junk
-                        },
-                        _ => {
-                            TokenizerState::CommentLine
-                        }
-                    }
-                },
-                TokenizerState::CommentBlock => {
-                    match c {
-                        '*' => {
-                            if self.get_char() == Some('/') {
-                                try!(token_data.end(&self.data, self.pos - 1));
-                                TokenizerState::Junk
-                            } else {
-                                TokenizerState::CommentBlock
-                            }
-                        },
-                        _ => TokenizerState::CommentBlock
-                    }
+                    _ => TokenizerState::CommentBlock,
                 },
                 TokenizerState::FunctionName => {
                     let position = self.position;
                     let start_pos = self.pos;
                     match c {
                         'u' => {
                             if self.get_match("user_pref", "(") {
                                 token_data.start(&self, TokenType::UserPrefFunction);
                             }
-                        },
+                        }
                         's' => {
                             if self.get_match("sticky_pref", "(") {
                                 token_data.start(&self, TokenType::StickyPrefFunction);
                             }
                         }
                         'p' => {
                             if self.get_match("pref", "(") {
                                 token_data.start(&self, TokenType::PrefFunction);
                             }
-                        },
+                        }
                         _ => {}
                     };
                     if token_data.token_type == TokenType::None {
                         // We didn't match anything
                         return Err(PrefReaderError::new(
-                            "Expected a pref function name", position, None))
+                            "Expected a pref function name",
+                            position,
+                            None,
+                        ));
                     } else {
                         token_data.start_pos = start_pos;
                         token_data.position = position;
-                        try!(token_data.end(&self.data, self.pos + 1));
+                        token_data.end(&self.data, self.pos + 1)?;
                         self.next_state = Some(TokenizerState::AfterFunctionName);
                         TokenizerState::Junk
                     }
-                },
-                TokenizerState::AfterFunctionName => {
-                    match c {
-                        '(' => {
-                            self.next_state = Some(TokenizerState::FunctionArgs);
-                            token_data.start(&self, TokenType::Paren);
-                            try!(token_data.end(&self.data, self.pos + 1));
-                            self.next_state = Some(TokenizerState::FunctionArgs);
-                            TokenizerState::Junk
-                        },
-                        _ => {
-                            return Err(PrefReaderError::new(
-                                "Expected an opening paren", self.position, None))
-                        }
+                }
+                TokenizerState::AfterFunctionName => match c {
+                    '(' => {
+                        self.next_state = Some(TokenizerState::FunctionArgs);
+                        token_data.start(&self, TokenType::Paren);
+                        token_data.end(&self.data, self.pos + 1)?;
+                        self.next_state = Some(TokenizerState::FunctionArgs);
+                        TokenizerState::Junk
+                    }
+                    _ => {
+                        return Err(PrefReaderError::new(
+                            "Expected an opening paren",
+                            self.position,
+                            None,
+                        ))
                     }
                 },
-                TokenizerState::FunctionArgs => {
-                    match c {
-                        ')' => {
-                            token_data.start(&self, TokenType::Paren);
-                            try!(token_data.end(&self.data, self.pos + 1));
-                            self.next_state = Some(TokenizerState::AfterFunction);
-                            TokenizerState::Junk
-                        },
-                        _ => {
-                            self.unget_char();
-                            TokenizerState::FunctionArg
-                        }
+                TokenizerState::FunctionArgs => match c {
+                    ')' => {
+                        token_data.start(&self, TokenType::Paren);
+                        token_data.end(&self.data, self.pos + 1)?;
+                        self.next_state = Some(TokenizerState::AfterFunction);
+                        TokenizerState::Junk
+                    }
+                    _ => {
+                        self.unget_char();
+                        TokenizerState::FunctionArg
                     }
                 },
-                TokenizerState::FunctionArg => {
-                    match c {
-                        '"' => {
-                            token_data.start(&self, TokenType::String);
-                            token_data.start_pos = self.pos + 1;
-                            TokenizerState::DoubleQuotedString
-                        },
-                        '\'' => {
-                            token_data.start(&self, TokenType::String);
-                            token_data.start_pos = self.pos + 1;
-                            TokenizerState::SingleQuotedString
-                        },
-                        't' | 'f' => {
-                            self.unget_char();
-                            TokenizerState::Bool
-                        },
-                        '0'...'9' | '-' |'+' => {
-                            token_data.start(&self, TokenType::Int);
-                            TokenizerState::Number
-                        },
-                        _ => {
-                            return Err(PrefReaderError::new(
-                                "Invalid character at start of function argument",
-                                self.position, None))
-                        }
+                TokenizerState::FunctionArg => match c {
+                    '"' => {
+                        token_data.start(&self, TokenType::String);
+                        token_data.start_pos = self.pos + 1;
+                        TokenizerState::DoubleQuotedString
+                    }
+                    '\'' => {
+                        token_data.start(&self, TokenType::String);
+                        token_data.start_pos = self.pos + 1;
+                        TokenizerState::SingleQuotedString
+                    }
+                    't' | 'f' => {
+                        self.unget_char();
+                        TokenizerState::Bool
+                    }
+                    '0'...'9' | '-' | '+' => {
+                        token_data.start(&self, TokenType::Int);
+                        TokenizerState::Number
+                    }
+                    _ => {
+                        return Err(PrefReaderError::new(
+                            "Invalid character at start of function argument",
+                            self.position,
+                            None,
+                        ))
                     }
                 },
-                TokenizerState::DoubleQuotedString => {
-                    match c {
-                        '"' => {
-                            try!(token_data.end(&self.data, self.pos));
-                            self.next_state = Some(TokenizerState::AfterFunctionArg);
-                            TokenizerState::Junk
-
-                        },
-                        '\n' => {
-                            return Err(PrefReaderError::new(
-                                "EOL in double quoted string", self.position, None))
-                        },
-                        '\\' => {
-                            try!(self.consume_escape(&mut token_data));
-                            TokenizerState::DoubleQuotedString
-                        },
-                        _ => TokenizerState::DoubleQuotedString
+                TokenizerState::DoubleQuotedString => match c {
+                    '"' => {
+                        token_data.end(&self.data, self.pos)?;
+                        self.next_state = Some(TokenizerState::AfterFunctionArg);
+                        TokenizerState::Junk
                     }
+                    '\n' => {
+                        return Err(PrefReaderError::new(
+                            "EOL in double quoted string",
+                            self.position,
+                            None,
+                        ))
+                    }
+                    '\\' => {
+                        self.consume_escape(&mut token_data)?;
+                        TokenizerState::DoubleQuotedString
+                    }
+                    _ => TokenizerState::DoubleQuotedString,
                 },
-                TokenizerState::SingleQuotedString => {
-                    match c {
-                        '\'' => {
-                            try!(token_data.end(&self.data, self.pos));
-                            self.next_state = Some(TokenizerState::AfterFunctionArg);
-                            TokenizerState::Junk
-
-                        },
-                        '\n' => {
-                            return Err(PrefReaderError::new(
-                                "EOL in single quoted string", self.position, None))
-                        },
-                        '\\' => {
-                            try!(self.consume_escape(&mut token_data));
-                            TokenizerState::SingleQuotedString
-                        }
-                        _ => TokenizerState::SingleQuotedString
+                TokenizerState::SingleQuotedString => match c {
+                    '\'' => {
+                        token_data.end(&self.data, self.pos)?;
+                        self.next_state = Some(TokenizerState::AfterFunctionArg);
+                        TokenizerState::Junk
                     }
+                    '\n' => {
+                        return Err(PrefReaderError::new(
+                            "EOL in single quoted string",
+                            self.position,
+                            None,
+                        ))
+                    }
+                    '\\' => {
+                        self.consume_escape(&mut token_data)?;
+                        TokenizerState::SingleQuotedString
+                    }
+                    _ => TokenizerState::SingleQuotedString,
                 },
-                TokenizerState::Number => {
-                    match c {
-                        '0'...'9' => TokenizerState::Number,
-                        ')' | ',' => {
-                            try!(token_data.end(&self.data, self.pos));
-                            self.unget_char();
-                            self.next_state = Some(TokenizerState::AfterFunctionArg);
-                            TokenizerState::Junk
-                        },
-                        x if PrefTokenizer::is_space(x) => {
-                            try!(token_data.end(&self.data, self.pos));
-                            self.next_state = Some(TokenizerState::AfterFunctionArg);
-                            TokenizerState::Junk
-                        },
-                        _ => {
-                            return Err(PrefReaderError::new(
-                                "Invalid character in number literal", self.position, None))
-                        }
+                TokenizerState::Number => match c {
+                    '0'...'9' => TokenizerState::Number,
+                    ')' | ',' => {
+                        token_data.end(&self.data, self.pos)?;
+                        self.unget_char();
+                        self.next_state = Some(TokenizerState::AfterFunctionArg);
+                        TokenizerState::Junk
+                    }
+                    x if PrefTokenizer::is_space(x) => {
+                        token_data.end(&self.data, self.pos)?;
+                        self.next_state = Some(TokenizerState::AfterFunctionArg);
+                        TokenizerState::Junk
+                    }
+                    _ => {
+                        return Err(PrefReaderError::new(
+                            "Invalid character in number literal",
+                            self.position,
+                            None,
+                        ))
                     }
                 },
                 TokenizerState::Bool => {
                     let start_pos = self.pos;
                     let position = self.position;
                     match c {
                         't' => {
                             if self.get_match("true", ",)") {
                                 token_data.start(&self, TokenType::Bool)
                             }
-                        },
+                        }
                         'f' => {
                             if self.get_match("false", ",)") {
                                 token_data.start(&self, TokenType::Bool)
                             }
                         }
                         _ => {}
                     };
                     if token_data.token_type == TokenType::None {
                         return Err(PrefReaderError::new(
                             "Unexpected characters in function argument",
-                            position, None));
+                            position,
+                            None,
+                        ));
                     } else {
                         token_data.start_pos = start_pos;
                         token_data.position = position;
-                        try!(token_data.end(&self.data, self.pos + 1));
+                        token_data.end(&self.data, self.pos + 1)?;
                         self.next_state = Some(TokenizerState::AfterFunctionArg);
                         TokenizerState::Junk
                     }
-                },
-                TokenizerState::AfterFunctionArg => {
-                    match c {
-                        ',' => {
-                            token_data.start(&self, TokenType::Comma);
-                            try!(token_data.end(&self.data, self.pos + 1));
-                            self.next_state = Some(TokenizerState::FunctionArg);
-                            TokenizerState::Junk
-                        }
-                        ')' => {
-                            token_data.start(&self, TokenType::Paren);
-                            try!(token_data.end(&self.data, self.pos + 1));
-                            self.next_state = Some(TokenizerState::AfterFunction);
-                            TokenizerState::Junk
-                        }
-                        _ => return Err(PrefReaderError::new
-                                        ("Unexpected character after function argument",
-                                         self.position,
-                                         None))
+                }
+                TokenizerState::AfterFunctionArg => match c {
+                    ',' => {
+                        token_data.start(&self, TokenType::Comma);
+                        token_data.end(&self.data, self.pos + 1)?;
+                        self.next_state = Some(TokenizerState::FunctionArg);
+                        TokenizerState::Junk
+                    }
+                    ')' => {
+                        token_data.start(&self, TokenType::Paren);
+                        token_data.end(&self.data, self.pos + 1)?;
+                        self.next_state = Some(TokenizerState::AfterFunction);
+                        TokenizerState::Junk
+                    }
+                    _ => {
+                        return Err(PrefReaderError::new(
+                            "Unexpected character after function argument",
+                            self.position,
+                            None,
+                        ))
                     }
                 },
-                TokenizerState::AfterFunction => {
-                    match c {
-                        ';' => {
-                            token_data.start(&self, TokenType::Semicolon);
-                            try!(token_data.end(&self.data, self.pos));
-                            self.next_state = Some(TokenizerState::FunctionName);
-                            TokenizerState::Junk
-                        }
-                        _ => return Err(PrefReaderError::new(
+                TokenizerState::AfterFunction => match c {
+                    ';' => {
+                        token_data.start(&self, TokenType::Semicolon);
+                        token_data.end(&self.data, self.pos)?;
+                        self.next_state = Some(TokenizerState::FunctionName);
+                        TokenizerState::Junk
+                    }
+                    _ => {
+                        return Err(PrefReaderError::new(
                             "Unexpected character after function",
-                            self.position, None))
+                            self.position,
+                            None,
+                        ))
                     }
                 },
-                TokenizerState::Error => TokenizerState::Error
+                TokenizerState::Error => TokenizerState::Error,
             };
             if token_data.complete {
                 return Ok(Some(token_data));
             }
         }
     }
 }
 
@@ -717,294 +745,311 @@ impl<'a> Iterator for PrefTokenizer<'a> 
 
     fn next(&mut self) -> Option<PrefToken<'a>> {
         if let TokenizerState::Error = self.state {
             return None;
         }
         let token_data = match self.next_token() {
             Err(e) => {
                 self.state = TokenizerState::Error;
-                return Some(PrefToken::Error(e.message,
-                                             e.position))
-
-            },
+                return Some(PrefToken::Error(e.message, e.position));
+            }
             Ok(Some(token_data)) => token_data,
             Ok(None) => return None,
         };
         let token = self.make_token(token_data);
         Some(token)
     }
 }
 
-pub fn tokenize<'a>(data: &'a [u8]) -> PrefTokenizer<'a> {
+pub fn tokenize(data: &[u8]) -> PrefTokenizer {
     PrefTokenizer::new(data)
 }
 
 pub fn serialize_token<T: Write>(token: &PrefToken, output: &mut T) -> Result<(), PrefReaderError> {
     let mut data_buf = String::new();
 
-    let data = match token {
-        &PrefToken::PrefFunction(_) => "pref",
-        &PrefToken::UserPrefFunction(_) => "user_pref",
-        &PrefToken::StickyPrefFunction(_) => "sticky_pref",
-        &PrefToken::CommentBlock(ref data, _) => {
+    let data = match *token {
+        PrefToken::PrefFunction(_) => "pref",
+        PrefToken::UserPrefFunction(_) => "user_pref",
+        PrefToken::StickyPrefFunction(_) => "sticky_pref",
+        PrefToken::CommentBlock(ref data, _) => {
             data_buf.reserve(data.len() + 4);
             data_buf.push_str("/*");
             data_buf.push_str(data.borrow());
             data_buf.push_str("*");
             &*data_buf
-        },
-        &PrefToken::CommentLine(ref data, _) => {
+        }
+        PrefToken::CommentLine(ref data, _) => {
             data_buf.reserve(data.len() + 2);
             data_buf.push_str("//");
             data_buf.push_str(data.borrow());
             &*data_buf
-        },
-        &PrefToken::CommentBashLine(ref data, _) => {
+        }
+        PrefToken::CommentBashLine(ref data, _) => {
             data_buf.reserve(data.len() + 1);
             data_buf.push_str("#");
             data_buf.push_str(data.borrow());
             &*data_buf
-        },
-        &PrefToken::Paren(data, _) => {
+        }
+        PrefToken::Paren(data, _) => {
             data_buf.push(data);
             &*data_buf
-        },
-        &PrefToken::Comma(_) => ",",
-        &PrefToken::Semicolon(_) => ";\n",
-        &PrefToken::String(ref data, _) => {
+        }
+        PrefToken::Comma(_) => ",",
+        PrefToken::Semicolon(_) => ";\n",
+        PrefToken::String(ref data, _) => {
             data_buf.reserve(data.len() + 2);
             data_buf.push('"');
             data_buf.push_str(escape_quote(data.borrow()).borrow());
             data_buf.push('"');
             &*data_buf
-        },
-        &PrefToken::Int(data, _) => {
+        }
+        PrefToken::Int(data, _) => {
             data_buf.push_str(&*data.to_string());
             &*data_buf
-        },
-        &PrefToken::Bool(data, _) => {
-            if data {"true"} else {"false"}
-        },
-        &PrefToken::Error(data, pos) => return Err(PrefReaderError::new(data, pos, None))
+        }
+        PrefToken::Bool(data, _) => {
+            if data {
+                "true"
+            } else {
+                "false"
+            }
+        }
+        PrefToken::Error(data, pos) => return Err(PrefReaderError::new(data, pos, None)),
     };
-    try!(output.write(data.as_bytes()));
+    output.write_all(data.as_bytes())?;
     Ok(())
 }
 
 pub fn serialize_tokens<'a, I, W>(tokens: I, output: &mut W) -> Result<(), PrefReaderError>
-    where I: Iterator<Item=&'a PrefToken<'a>>, W: Write {
+where
+    I: Iterator<Item = &'a PrefToken<'a>>,
+    W: Write,
+{
     for token in tokens {
-        try!(serialize_token(token, output));
+        serialize_token(token, output)?;
     }
     Ok(())
 }
 
-fn escape_quote<'a>(data: &'a str) -> Cow<'a, str> {
+fn escape_quote(data: &str) -> Cow<str> {
     // Not very efficient…
-    if data.contains("\"") || data.contains("\\") {
-        let new_data = Cow::Owned(data
-            .replace(r#"\"#, r#"\\"#)
-            .replace(r#"""#, r#"\""#));
-        new_data
+    if data.contains('"') || data.contains('\\') {
+        Cow::Owned(data.replace(r#"\"#, r#"\\"#).replace(r#"""#, r#"\""#))
     } else {
         Cow::Borrowed(data)
     }
 }
 
 #[derive(Debug, PartialEq)]
 enum ParserState {
     Function,
     Key,
     Value,
 }
 
 struct PrefBuilder {
     key: Option<String>,
     value: Option<PrefValue>,
-    sticky: bool
+    sticky: bool,
 }
 
 impl PrefBuilder {
     fn new() -> PrefBuilder {
         PrefBuilder {
             key: None,
             value: None,
-            sticky: false
+            sticky: false,
         }
     }
 }
 
-
 fn skip_comments<'a>(tokenizer: &mut PrefTokenizer<'a>) -> Option<PrefToken<'a>> {
     loop {
         match tokenizer.next() {
-            Some(PrefToken::CommentBashLine(_, _)) |
-            Some(PrefToken::CommentBlock(_, _)) |
-            Some(PrefToken::CommentLine(_, _)) => {},
+            Some(PrefToken::CommentBashLine(_, _))
+            | Some(PrefToken::CommentBlock(_, _))
+            | Some(PrefToken::CommentLine(_, _)) => {}
             Some(x) => return Some(x),
             None => return None,
         }
     }
 }
 
-pub fn parse_tokens<'a>(tokenizer: &mut PrefTokenizer<'a>) -> Result<Preferences,
-                                                                     PrefReaderError> {
-
+pub fn parse_tokens<'a>(tokenizer: &mut PrefTokenizer<'a>) -> Result<Preferences, PrefReaderError> {
     let mut state = ParserState::Function;
     let mut current_pref = PrefBuilder::new();
     let mut rv = Preferences::new();
 
     loop {
         // Not just using a for loop here seems strange, but this restricts the
         // scope of the borrow
         let token = {
             match tokenizer.next() {
                 Some(x) => x,
-                None => break
+                None => break,
             }
         };
         // First deal with comments and errors
         match token {
             PrefToken::Error(msg, position) => {
-                return Err(PrefReaderError::new(msg.into(), position, None));
-            },
-            PrefToken::CommentBashLine(_, _) |
-            PrefToken::CommentLine(_, _) |
-            PrefToken::CommentBlock(_, _) => {
-                continue
-            },
+                return Err(PrefReaderError::new(msg, position, None));
+            }
+            PrefToken::CommentBashLine(_, _)
+            | PrefToken::CommentLine(_, _)
+            | PrefToken::CommentBlock(_, _) => continue,
             _ => {}
         }
         state = match state {
             ParserState::Function => {
                 match token {
                     PrefToken::PrefFunction(_) => {
                         current_pref.sticky = false;
                     }
                     PrefToken::UserPrefFunction(_) => {
                         current_pref.sticky = false;
-                    },
+                    }
                     PrefToken::StickyPrefFunction(_) => {
                         current_pref.sticky = true;
-                    },
+                    }
                     _ => {
-                        return Err(PrefReaderError::new("Expected pref function".into(),
-                                                        token.position(), None));
+                        return Err(PrefReaderError::new(
+                            "Expected pref function",
+                            token.position(),
+                            None,
+                        ));
                     }
                 }
                 let next = skip_comments(tokenizer);
                 match next {
                     Some(PrefToken::Paren('(', _)) => ParserState::Key,
-                    _ => return Err(PrefReaderError::new("Expected open paren".into(),
-                                                         next.map(|x| x.position())
-                                                         .unwrap_or(tokenizer.position),
-                                                         None))
+                    _ => {
+                        return Err(PrefReaderError::new(
+                            "Expected open paren",
+                            next.map(|x| x.position()).unwrap_or(tokenizer.position),
+                            None,
+                        ))
+                    }
                 }
-            },
+            }
             ParserState::Key => {
                 match token {
                     PrefToken::String(data, _) => current_pref.key = Some(data.into_owned()),
                     _ => {
-                        return Err(PrefReaderError::new("Expected string", token.position(), None));
+                        return Err(PrefReaderError::new(
+                            "Expected string",
+                            token.position(),
+                            None,
+                        ));
                     }
                 }
                 let next = skip_comments(tokenizer);
                 match next {
                     Some(PrefToken::Comma(_)) => ParserState::Value,
-                    _ => return Err(PrefReaderError::new("Expected comma",
-                                                         next.map(|x| x.position())
-                                                         .unwrap_or(tokenizer.position),
-                                                         None))
+                    _ => {
+                        return Err(PrefReaderError::new(
+                            "Expected comma",
+                            next.map(|x| x.position()).unwrap_or(tokenizer.position),
+                            None,
+                        ))
+                    }
                 }
-            },
+            }
             ParserState::Value => {
                 match token {
                     PrefToken::String(data, _) => {
                         current_pref.value = Some(PrefValue::String(data.into_owned()))
-                    },
-                    PrefToken::Int(data, _) => {
-                        current_pref.value = Some(PrefValue::Int(data))
                     }
-                    PrefToken::Bool(data, _) => {
-                        current_pref.value = Some(PrefValue::Bool(data))
-                    },
+                    PrefToken::Int(data, _) => current_pref.value = Some(PrefValue::Int(data)),
+                    PrefToken::Bool(data, _) => current_pref.value = Some(PrefValue::Bool(data)),
                     _ => {
-                        return Err(PrefReaderError::new("Expected value", token.position(),
-                                                        None))
+                        return Err(PrefReaderError::new(
+                            "Expected value",
+                            token.position(),
+                            None,
+                        ))
                     }
                 }
                 let next = skip_comments(tokenizer);
                 match next {
                     Some(PrefToken::Paren(')', _)) => {}
-                    _ => return Err(PrefReaderError::new("Expected close paren",
-                                                         next.map(|x| x.position())
-                                                         .unwrap_or(tokenizer.position),
-                                                         None))
+                    _ => {
+                        return Err(PrefReaderError::new(
+                            "Expected close paren",
+                            next.map(|x| x.position()).unwrap_or(tokenizer.position),
+                            None,
+                        ))
+                    }
                 }
                 let next = skip_comments(tokenizer);
                 match next {
-                    Some(PrefToken::Semicolon(_)) |
-                    None => {},
-                    _ => return Err(PrefReaderError::new("Expected semicolon",
-                                                         next.map(|x| x.position())
-                                                         .unwrap_or(tokenizer.position),
-                                                         None))
+                    Some(PrefToken::Semicolon(_)) | None => {}
+                    _ => {
+                        return Err(PrefReaderError::new(
+                            "Expected semicolon",
+                            next.map(|x| x.position()).unwrap_or(tokenizer.position),
+                            None,
+                        ))
+                    }
                 }
                 let key = mem::replace(&mut current_pref.key, None);
                 let value = mem::replace(&mut current_pref.value, None);
                 let pref = if current_pref.sticky {
                     Pref::new_sticky(value.unwrap())
                 } else {
                     Pref::new(value.unwrap())
                 };
                 rv.insert(key.unwrap(), pref);
                 current_pref.sticky = false;
                 ParserState::Function
-            },
+            }
         }
     }
     match state {
         ParserState::Key | ParserState::Value => {
-            return Err(PrefReaderError::new("EOF in middle of function",
-                                            tokenizer.position, None));
-        },
+            return Err(PrefReaderError::new(
+                "EOF in middle of function",
+                tokenizer.position,
+                None,
+            ));
+        }
         _ => {}
     }
     Ok(rv)
 }
 
 pub fn serialize<W: Write>(prefs: &Preferences, output: &mut W) -> io::Result<()> {
     let mut p: Vec<_> = prefs.iter().collect();
     p.sort_by(|a, b| a.0.cmp(&b.0));
-    for &(key, pref) in p.iter() {
+    for &(key, pref) in &p {
         let func = if pref.sticky {
             "sticky_pref("
         } else {
             "user_pref("
         }.as_bytes();
-        try!(output.write(func));
-        try!(output.write("\"".as_bytes()));
-        try!(output.write(escape_quote(key).as_bytes()));
-        try!(output.write("\"".as_bytes()));
-        try!(output.write(", ".as_bytes()));
+        output.write_all(func)?;
+        output.write_all(b"\"")?;
+        output.write_all(escape_quote(key).as_bytes())?;
+        output.write_all(b"\"")?;
+        output.write_all(b", ")?;
         match pref.value {
             PrefValue::Bool(x) => {
-                try!(output.write((if x {"true"} else {"false"}).as_bytes()));
-            },
+                output.write_all(if x { b"true" } else { b"false" })?;
+            }
             PrefValue::Int(x) => {
-                try!(output.write(x.to_string().as_bytes()));
-            },
+                output.write_all(x.to_string().as_bytes())?;
+            }
             PrefValue::String(ref x) => {
-                try!(output.write("\"".as_bytes()));
-                try!(output.write(escape_quote(x).as_bytes()));
-                try!(output.write("\"".as_bytes()));
+                output.write_all(b"\"")?;
+                output.write_all(escape_quote(x).as_bytes())?;
+                output.write_all(b"\"")?;
             }
         };
-        try!(output.write(");\n".as_bytes()));
-    };
+        output.write_all(b");\n")?;
+    }
     Ok(())
 }
 
-pub fn parse<'a>(data: &'a [u8]) -> Result<Preferences, PrefReaderError> {
+pub fn parse(data: &[u8]) -> Result<Preferences, PrefReaderError> {
     let mut tokenizer = tokenize(data);
     parse_tokens(&mut tokenizer)
 }
--- a/testing/mozbase/rust/mozprofile/src/profile.rs
+++ b/testing/mozbase/rust/mozprofile/src/profile.rs
@@ -1,14 +1,14 @@
-use preferences::{Preferences, Pref};
+use preferences::{Pref, Preferences};
 use prefreader::{parse, serialize, PrefReaderError};
 use std::collections::btree_map::Iter;
 use std::fs::File;
+use std::io::prelude::*;
 use std::io::Result as IoResult;
-use std::io::prelude::*;
 use std::path::{Path, PathBuf};
 use tempdir::TempDir;
 
 #[derive(Debug)]
 pub struct Profile {
     pub path: PathBuf,
     pub temp_dir: Option<TempDir>,
     prefs: Option<PrefFile>,
@@ -16,46 +16,46 @@ pub struct Profile {
 }
 
 impl Profile {
     pub fn new(opt_path: Option<&Path>) -> IoResult<Profile> {
         let mut temp_dir = None;
         let path = match opt_path {
             Some(p) => p.to_path_buf(),
             None => {
-                let dir = try!(TempDir::new("rust_mozprofile"));
+                let dir = TempDir::new("rust_mozprofile")?;
                 let temp_path = dir.path().to_path_buf();
                 temp_dir = Some(dir);
                 temp_path
             }
         };
 
         Ok(Profile {
-            path: path,
-            temp_dir: temp_dir,
+            path,
+            temp_dir,
             prefs: None,
-            user_prefs: None
+            user_prefs: None,
         })
     }
 
     pub fn prefs(&mut self) -> Result<&mut PrefFile, PrefReaderError> {
         if self.prefs.is_none() {
             let mut pref_path = PathBuf::from(&self.path);
             pref_path.push("prefs.js");
-            self.prefs = Some(try!(PrefFile::new(pref_path)))
+            self.prefs = Some(PrefFile::new(pref_path)?)
         };
         // This error handling doesn't make much sense
         Ok(self.prefs.as_mut().unwrap())
     }
 
     pub fn user_prefs(&mut self) -> Result<&mut PrefFile, PrefReaderError> {
         if self.user_prefs.is_none() {
             let mut pref_path = PathBuf::from(&self.path);
             pref_path.push("user.js");
-            self.user_prefs = Some(try!(PrefFile::new(pref_path)))
+            self.user_prefs = Some(PrefFile::new(pref_path)?)
         };
         // This error handling doesn't make much sense
         Ok(self.user_prefs.as_mut().unwrap())
     }
 }
 
 #[derive(Debug)]
 pub struct PrefFile {
@@ -63,42 +63,43 @@ pub struct PrefFile {
     pub prefs: Preferences,
 }
 
 impl PrefFile {
     pub fn new(path: PathBuf) -> Result<PrefFile, PrefReaderError> {
         let prefs = if !path.exists() {
             Preferences::new()
         } else {
-            let mut f = try!(File::open(&path));
+            let mut f = File::open(&path)?;
             let mut buf = String::with_capacity(4096);
-            try!(f.read_to_string(&mut buf));
-            try!(parse(buf.as_bytes()))
+            f.read_to_string(&mut buf)?;
+            parse(buf.as_bytes())?
         };
 
-        Ok(PrefFile {
-            path: path,
-            prefs: prefs
-        })
+        Ok(PrefFile { path, prefs })
     }
 
     pub fn write(&self) -> IoResult<()> {
-        let mut f = try!(File::create(&self.path));
+        let mut f = File::create(&self.path)?;
         serialize(&self.prefs, &mut f)
     }
 
     pub fn insert_slice<K>(&mut self, preferences: &[(K, Pref)])
-        where K: Into<String> + Clone {
+    where
+        K: Into<String> + Clone,
+    {
         for &(ref name, ref value) in preferences.iter() {
             self.insert((*name).clone(), (*value).clone());
         }
     }
 
     pub fn insert<K>(&mut self, key: K, value: Pref)
-        where K: Into<String> {
+    where
+        K: Into<String>,
+    {
         self.prefs.insert(key.into(), value);
     }
 
     pub fn remove(&mut self, key: &str) -> Option<Pref> {
         self.prefs.remove(key)
     }
 
     pub fn get(&mut self, key: &str) -> Option<&Pref> {
--- a/testing/mozbase/rust/mozversion/src/lib.rs
+++ b/testing/mozbase/rust/mozversion/src/lib.rs
@@ -1,15 +1,15 @@
 extern crate ini;
 extern crate regex;
 extern crate semver;
 
 use ini::Ini;
+use platform::ini_path;
 use regex::Regex;
-use platform::ini_path;
 use std::default::Default;
 use std::error;
 use std::fmt::{self, Display, Formatter};
 use std::path::Path;
 use std::str::FromStr;
 
 /// Details about the version of a Firefox build.
 #[derive(Clone, Default)]
@@ -84,92 +84,109 @@ pub struct Version {
     pub minor: u64,
     /// Patch version number (e.g. 2 in 55.1.2)
     pub patch: u64,
     /// Prerelase information (e.g. Some(("a", 1)) in 55.0a1)
     pub pre: Option<(String, u64)>,
 }
 
 impl Version {
-    pub fn from_str(version_string: &str) -> Result<Version, Error> {
-        let mut version: Version = Default::default();
-        let version_re = Regex::new(r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?(?:(?P<pre0>[a-z]+)(?P<pre1>\d*))?$").unwrap();
-        if let Some(captures) = version_re.captures(version_string) {
-            match captures.name("major")
-                .and_then(|x| u64::from_str(x.as_str()).ok()) {
-                    Some(x) => version.major = x,
-                    None => return Err(Error::VersionError("No major version number found".into()))
-                }
-            match captures.name("minor")
-                .and_then(|x| u64::from_str(x.as_str()).ok()) {
-                    Some(x) => version.minor = x,
-                    None => return Err(Error::VersionError("No minor version number found".into()))
-                }
-            match captures.name("patch")
-                .and_then(|x| u64::from_str(x.as_str()).ok()) {
-                    Some(x) => version.patch = x,
-                    None => {}
-                }
-            if let Some(pre_0) = captures.name("pre0").map(|x| x.as_str().to_string()) {
-                if captures.name("pre1").is_some() {
-                    if let Some(pre_1) = captures.name("pre1")
-                        .and_then(|x| u64::from_str(x.as_str()).ok()) {
-                            version.pre = Some((pre_0, pre_1))
-                        } else {
-                            return Err(Error::VersionError("Failed to convert prelease number to u64".into()));
-                        }
-                } else {
-                    return Err(Error::VersionError("Failed to convert prelease number to u64".into()));
-                }
-            }
-        } else {
-            return Err(Error::VersionError("Failed to parse input as version string".into()))
-        }
-        Ok(version)
-    }
-
     fn to_semver(&self) -> semver::Version {
         // The way the semver crate handles prereleases isn't what we want here
         // This should be fixed in the long term by implementing our own comparison
-        // operators, but for now just act as if prerelease metadata was missing, otherwise
-        // it is almost impossible to use this with nightly
+        // operators, but for now just act as if prerelease metadata was missing,
+        // otherwise it is almost impossible to use this with nightly
         semver::Version {
             major: self.major,
             minor: self.minor,
             patch: self.patch,
             pre: vec![],
             build: vec![],
         }
     }
 
     pub fn matches(&self, version_req: &str) -> Result<bool, Error> {
-        let req = try!(semver::VersionReq::parse(version_req));
+        let req = semver::VersionReq::parse(version_req)?;
         Ok(req.matches(&self.to_semver()))
     }
 }
 
+impl FromStr for Version {
+    type Err = Error;
+
+    fn from_str(version_string: &str) -> Result<Version, Error> {
+        let mut version: Version = Default::default();
+        let version_re = Regex::new(r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?(?:(?P<pre0>[a-z]+)(?P<pre1>\d*))?$").unwrap();
+        if let Some(captures) = version_re.captures(version_string) {
+            match captures
+                .name("major")
+                .and_then(|x| u64::from_str(x.as_str()).ok())
+            {
+                Some(x) => version.major = x,
+                None => return Err(Error::VersionError("No major version number found".into())),
+            }
+            match captures
+                .name("minor")
+                .and_then(|x| u64::from_str(x.as_str()).ok())
+            {
+                Some(x) => version.minor = x,
+                None => return Err(Error::VersionError("No minor version number found".into())),
+            }
+            if let Some(x) = captures
+                .name("patch")
+                .and_then(|x| u64::from_str(x.as_str()).ok())
+            {
+                version.patch = x
+            }
+            if let Some(pre_0) = captures.name("pre0").map(|x| x.as_str().to_string()) {
+                if captures.name("pre1").is_some() {
+                    if let Some(pre_1) = captures
+                        .name("pre1")
+                        .and_then(|x| u64::from_str(x.as_str()).ok())
+                    {
+                        version.pre = Some((pre_0, pre_1))
+                    } else {
+                        return Err(Error::VersionError(
+                            "Failed to convert prelease number to u64".into(),
+                        ));
+                    }
+                } else {
+                    return Err(Error::VersionError(
+                        "Failed to convert prelease number to u64".into(),
+                    ));
+                }
+            }
+        } else {
+            return Err(Error::VersionError(
+                "Failed to parse input as version string".into(),
+            ));
+        }
+        Ok(version)
+    }
+}
+
 impl Display for Version {
     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
         match self.patch {
-            0 => try!(write!(f, "{}.{}", self.major, self.minor)),
-            _ => try!(write!(f, "{}.{}.{}", self.major, self.minor, self.patch))
+            0 => write!(f, "{}.{}", self.major, self.minor)?,
+            _ => write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?,
         }
         if let Some(ref pre) = self.pre {
-            try!(write!(f, "{}{}", pre.0, pre.1));
+            write!(f, "{}{}", pre.0, pre.1)?;
         };
         Ok(())
     }
 }
 
 /// Determine the version of Firefox given the path to a binary.
 ///
 /// Given the path to a Firefox binary, read the associated application.ini
 /// and platform.ini files to extract information about the version of Firefox
 /// at that path.
-pub fn firefox_version(binary: &Path) -> Result<AppVersion, Error>  {
+pub fn firefox_version(binary: &Path) -> Result<AppVersion, Error> {
     let mut version = AppVersion::new();
     let mut updated = false;
 
     if let Some(dir) = ini_path(binary) {
         let mut application_ini = dir.clone();
         application_ini.push("application.ini");
 
         if Path::exists(&application_ini) {
@@ -187,104 +204,111 @@ pub fn firefox_version(binary: &Path) ->
             let ini_file = Ini::load_from_file(platform_ini).ok();
             if let Some(ini) = ini_file {
                 updated = true;
                 version.update_from_platform_ini(&ini);
             }
         }
 
         if !updated {
-            return Err(Error::MetadataError("Neither platform.ini nor application.ini found".into()))
+            return Err(Error::MetadataError(
+                "Neither platform.ini nor application.ini found".into(),
+            ));
         }
     } else {
-        return Err(Error::MetadataError("Invalid binary path".into()))
+        return Err(Error::MetadataError("Invalid binary path".into()));
     }
     Ok(version)
 }
 
 #[derive(Debug)]
 pub enum Error {
     /// Error parsing a version string
     VersionError(String),
     /// Error reading application metadata
     MetadataError(String),
     /// Error processing a string as a semver comparator
-    SemVerError(semver::ReqParseError)
+    SemVerError(semver::ReqParseError),
 }
 
 impl Display for Error {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            &Error::VersionError(ref x) => {
-                try!("VersionError: ".fmt(f));
+        match *self {
+            Error::VersionError(ref x) => {
+                "VersionError: ".fmt(f)?;
                 x.fmt(f)
-            },
-            &Error::MetadataError(ref x) => {
-                try!("MetadataError: ".fmt(f));
+            }
+            Error::MetadataError(ref x) => {
+                "MetadataError: ".fmt(f)?;
                 x.fmt(f)
-            },
-            &Error::SemVerError(ref e) => {
-                try!("SemVerError: ".fmt(f));
+            }
+            Error::SemVerError(ref e) => {
+                "SemVerError: ".fmt(f)?;
                 e.fmt(f)
             }
         }
     }
 }
 
 impl From<semver::ReqParseError> for Error {
     fn from(err: semver::ReqParseError) -> Error {
         Error::SemVerError(err)
     }
 }
 
 impl error::Error for Error {
     fn description(&self) -> &str {
-        match self {
-            &Error::VersionError(ref x) => &*x,
-            &Error::MetadataError(ref x) => &*x,
-            &Error::SemVerError(ref e) => e.description(),
+        match *self {
+            Error::VersionError(ref x) => &*x,
+            Error::MetadataError(ref x) => &*x,
+            Error::SemVerError(ref e) => e.description(),
         }
     }
 
     fn cause(&self) -> Option<&error::Error> {
-        match self {
-            &Error::SemVerError(ref e) => Some(e),
-            _ => None,
+        match *self {
+            Error::SemVerError(ref e) => Some(e),
+            Error::VersionError(_) | Error::MetadataError(_) => None,
         }
     }
 }
 
 #[cfg(target_os = "macos")]
 mod platform {
     use std::path::{Path, PathBuf};
 
     pub fn ini_path(binary: &Path) -> Option<PathBuf> {
-        binary.canonicalize().ok()
+        binary
+            .canonicalize()
+            .ok()
             .as_ref()
             .and_then(|dir| dir.parent())
             .and_then(|dir| dir.parent())
             .map(|dir| dir.join("Resources"))
     }
 }
 
 #[cfg(not(target_os = "macos"))]
 mod platform {
     use std::path::{Path, PathBuf};
 
     pub fn ini_path(binary: &Path) -> Option<PathBuf> {
-        binary.canonicalize().ok()
+        binary
+            .canonicalize()
+            .ok()
             .as_ref()
             .and_then(|dir| dir.parent())
             .map(|dir| dir.to_path_buf())
     }
 }
 
 #[cfg(test)]
 mod test {
-    use super::{Version};
+    use super::Version;
+    use std::str::FromStr;
 
     fn parse_version(input: &str) -> String {
         Version::from_str(input).unwrap().to_string()
     }
 
     fn compare(version: &str, comparison: &str) -> bool {
         let v = Version::from_str(version).unwrap();
         v.matches(comparison).unwrap()
@@ -308,12 +332,12 @@ mod test {
         assert!(compare("49.0", "<50"));
         assert!(compare("50.0", "<50.1"));
         assert!(compare("50.0.0", "<50.0.1"));
         assert!(!compare("50.1.0", ">50"));
         assert!(!compare("50.1.0", "<50"));
         assert!(compare("50.1.0", ">=50,<51"));
         assert!(compare("50.0a1", ">49.0"));
         assert!(compare("50.0a2", "=50"));
-        //This is the weird one
+        // This is the weird one
         assert!(!compare("50.0a2", ">50.0"));
     }
 }
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -650288,17 +650288,17 @@
    "faf00c007b375a365bbb6b00e4051d44852d0fdc",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/audioparam-setValueAtTime.html": [
    "ab2edfd009f1320fdb8e0a49dffb984baed36f05",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/audioparam-setValueCurve-exceptions.html": [
-   "16975f1c7c8f8079916e4fd4f166b63ad9fa686a",
+   "7990d0aee134cb33082e058b41bdaafc69bb507e",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/audioparam-setValueCurveAtTime.html": [
    "de8406244bc386981c0527629e3685f250c6a659",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/audioparam-summingjunction.html": [
    "9084942f7032cd00d9d57b8d4d1f6ba2742ab57f",
--- a/testing/web-platform/meta/content-security-policy/prefetch-src/prefetch-allowed.html.ini
+++ b/testing/web-platform/meta/content-security-policy/prefetch-src/prefetch-allowed.html.ini
@@ -1,5 +1,6 @@
 [prefetch-allowed.html]
   prefs: [security.mixed_content.upgrade_display_content:false]
   comment: prefetch-src is not supported via meta tag but the test is passing as the tests only tests that 'self' works
   disabled:
     if not debug and (os == "win") and (version == "6.1.7601"): https://bugzilla.mozilla.org/show_bug.cgi?id=1449374
+    if (os == "win") and (version == "10.0.15603") and (bits == 64): https://bugzilla.mozilla.org/show_bug.cgi?id=1449374
deleted file mode 100644
--- a/testing/web-platform/meta/webdriver/tests/actions/mouse_dblclick.py.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[mouse_dblclick.py]
-  disabled:
-    if webrender: bug 1425588
deleted file mode 100644
--- a/testing/web-platform/meta/webdriver/tests/element_clear/clear.py.ini
+++ /dev/null
@@ -1,49 +0,0 @@
-[clear.py]
-  [test_clear_content_editable_resettable_element[element0\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element1\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element2\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element3\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element4\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element5\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element6\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element7\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element8\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element9\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element10\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element11\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element12\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element13\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element14\]]
-    expected: FAIL
-
-  [test_clear_content_editable_resettable_element[element15\]]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/webdriver/tests/get_element_property/user_prompts.py.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[user_prompts.py]
-  disabled:
-    if webrender: bug 1425588
deleted file mode 100644
--- a/testing/web-platform/meta/webdriver/tests/get_element_tag_name/user_prompts.py.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[user_prompts.py]
-  disabled:
-    if webrender: bug 1425588
deleted file mode 100644
--- a/testing/web-platform/meta/webdriver/tests/get_title/user_prompts.py.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[user_prompts.py]
-  disabled:
-    if webrender: bug 1425588
deleted file mode 100644
--- a/testing/web-platform/meta/webdriver/tests/get_window_rect/user_prompts.py.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[user_prompts.py]
-  disabled:
-    if not debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): wpt-sync Bug 1446953
deleted file mode 100644
--- a/testing/web-platform/meta/webdriver/tests/send_alert_text/send.py.ini
+++ /dev/null
@@ -1,13 +0,0 @@
-[send.py]
-  [test_send_alert_text]
-    expected: FAIL
-
-  [test_confirm_element_not_interactable]
-    expected: FAIL
-
-  [test_alert_element_not_interactable]
-    expected: FAIL
-
-  [test_send_alert_text_with_whitespace]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/webdriver/tests/set_window_rect/set.py.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[set.py]
-  [test_restore_from_maximized]
-    disabled:
-      if debug and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): wpt-sync Bug 1444624
-
--- a/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/audioparam-setValueCurve-exceptions.html
+++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audioparam-interface/audioparam-setValueCurve-exceptions.html
@@ -292,17 +292,26 @@
         let g = context.createGain();
         let startTime = 5;
         let startTimeLater = 10;
         let startTimeEarlier = 2.5;
         let curveDuration = 10;
         let curveDurationShorter = 5;
         let curve = [1, 2, 3];
 
-        // An initial curve event
+        // Having an event that ends at time t and then starting a ramp at time
+        // t should work.
+        should(
+            () => {
+              g.gain.linearRampToValueAtTime(1.0, startTime);
+            },
+            `g.gain.linearRampToValueAtTime(1.0, ${startTime})`)
+            .notThrow();
+
+        // An initial curve event, starting from the end of the linear ramp.
         should(
             () => {
               g.gain.setValueCurveAtTime(curve, startTime, curveDuration);
             },
             `g.gain.setValueCurveAtTime([${curve}], ${startTime}, ${curveDuration})`)
             .notThrow();
 
         // Check that an exception is thrown when trying to overlap two curves,
@@ -317,31 +326,38 @@
             .throw('NotSupportedError');
         // Same start time, shorter end time
         should(
             () => {
               g.gain.setValueCurveAtTime(curve, startTime, curveDurationShorter);
             },
             `g.gain.setValueCurveAtTime([${curve}], ${startTime}, ${curveDurationShorter})`)
             .throw('NotSupportedError');
-        // Earlier start time, end time after the start time an another curve
+        // Earlier start time, end time after the start time of an another curve
         should(
             () => {
               g.gain.setValueCurveAtTime(curve, startTimeEarlier, curveDuration);
             },
             `g.gain.setValueCurveAtTime([${curve}], ${startTimeEarlier}, ${curveDuration})`)
             .throw('NotSupportedError');
-        // Start time after the start time of the other curve, but earlier that
+        // Start time after the start time of the other curve, but earlier than
         // its end.
         should(
             () => {
               g.gain.setValueCurveAtTime(curve, startTimeLater, curveDuration);
             },
             `g.gain.setValueCurveAtTime([${curve}], ${startTimeLater}, ${curveDuration})`)
             .throw('NotSupportedError');
+        // Setting an event exactly at the end of the curve should work.
+        should(
+            () => {
+              g.gain.setValueAtTime(1.0, startTime + curveDuration);
+            },
+            `g.gain.setValueAtTime(1.0, ${startTime + curveDuration})`)
+            .notThrow();
 
         task.done();
       });
 
       audit.define('curve lengths', (task, should) => {
         let context =
             new OfflineAudioContext(1, testDurationFrames, sampleRate);
         let g = context.createGain();
--- a/testing/webdriver/src/capabilities.rs
+++ b/testing/webdriver/src/capabilities.rs
@@ -116,17 +116,17 @@ impl SpecNewSessionParameters {
             .iter()
             .filter(|&(_, ref value)| **value == Value::Null)
             .map(|(k, _)| k.clone())
             .collect::<Vec<String>>();
         for key in null_entries {
             capabilities.remove(&key);
         }
 
-        for (key, value) in capabilities.iter() {
+        for (key, value) in &capabilities {
             match &**key {
                 x @ "acceptInsecureCerts" | x @ "setWindowRect" => if !value.is_boolean() {
                     return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
                         format!("{} is not boolean: {}", x, value),
                     ));
                 },
                 x @ "browserName" | x @ "browserVersion" | x @ "platformName" => {
@@ -158,17 +158,17 @@ impl SpecNewSessionParameters {
                 }
             }
         }
         Ok(capabilities)
     }
 
     fn validate_page_load_strategy(value: &Value) -> WebDriverResult<()> {
         match value {
-            &Value::String(ref x) => match &**x {
+            Value::String(x) => match &**x {
                 "normal" | "eager" | "none" => {}
                 x => {
                     return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
                         format!("Invalid page load strategy: {}", x),
                     ))
                 }
             },
@@ -184,17 +184,17 @@ impl SpecNewSessionParameters {
 
     fn validate_proxy(proxy_value: &Value) -> WebDriverResult<()> {
         let obj = try_opt!(
             proxy_value.as_object(),
             ErrorStatus::InvalidArgument,
             "proxy is not an object"
         );
 
-        for (key, value) in obj.iter() {
+        for (key, value) in obj {
             match &**key {
                 "proxyType" => match value.as_str() {
                     Some("pac") | Some("direct") | Some("autodetect") | Some("system")
                     | Some("manual") => {}
                     Some(x) => {
                         return Err(WebDriverError::new(
                             ErrorStatus::InvalidArgument,
                             format!("Invalid proxyType value: {}", x),
@@ -321,17 +321,17 @@ impl SpecNewSessionParameters {
 
     fn validate_timeouts(value: &Value) -> WebDriverResult<()> {
         let obj = try_opt!(
             value.as_object(),
             ErrorStatus::InvalidArgument,
             "timeouts capability is not an object"
         );
 
-        for (key, value) in obj.iter() {
+        for (key, value) in obj {
             match &**key {
                 x @ "script" | x @ "pageLoad" | x @ "implicit" => {
                     let timeout = try_opt!(
                         value.as_f64(),
                         ErrorStatus::InvalidArgument,
                         format!("{} timeouts value is not a number: {}", x, value)
                     );
                     if timeout < 0.0 || timeout.fract() != 0.0 {
@@ -388,49 +388,49 @@ impl SpecNewSessionParameters {
 }
 
 impl CapabilitiesMatching for SpecNewSessionParameters {
     fn match_browser<T: BrowserCapabilities>(
         &self,
         browser_capabilities: &mut T,
     ) -> WebDriverResult<Option<Capabilities>> {
         let default = vec![Map::new()];
-        let capabilities_list = if self.firstMatch.len() > 0 {
+        let capabilities_list = if self.firstMatch.is_empty() {
+            &default
+        } else {
             &self.firstMatch
-        } else {
-            &default
         };
 
         let merged_capabilities = capabilities_list
             .iter()
             .map(|first_match_entry| {
                 if first_match_entry
                     .keys()
                     .any(|k| self.alwaysMatch.contains_key(k))
                 {
                     return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
                         "firstMatch key shadowed a value in alwaysMatch",
                     ));
                 }
                 let mut merged = self.alwaysMatch.clone();
-                for (key, value) in first_match_entry.clone().into_iter() {
+                for (key, value) in first_match_entry.clone() {
                     merged.insert(key, value);
                 }
                 Ok(merged)
             })
             .map(|merged| merged.and_then(|x| self.validate(x, browser_capabilities)))
             .collect::<WebDriverResult<Vec<Capabilities>>>()?;
 
         let selected = merged_capabilities
             .iter()
             .filter_map(|merged| {
                 browser_capabilities.init(merged);
 
-                for (key, value) in merged.iter() {
+                for (key, value) in merged {
                     match &**key {
                         "browserName" => {
                             let browserValue = browser_capabilities
                                 .browser_name(merged)
                                 .ok()
                                 .and_then(|x| x);
 
                             if value.as_str() != browserValue.as_ref().map(|x| &**x) {
--- a/testing/webdriver/src/command.rs
+++ b/testing/webdriver/src/command.rs
@@ -466,18 +466,18 @@ impl<'de> Deserialize<'de> for NewSessio
 }
 
 impl CapabilitiesMatching for NewSessionParameters {
     fn match_browser<T: BrowserCapabilities>(
         &self,
         browser_capabilities: &mut T,
     ) -> WebDriverResult<Option<Capabilities>> {
         match self {
-            &NewSessionParameters::Spec(ref x) => x.match_browser(browser_capabilities),
-            &NewSessionParameters::Legacy(ref x) => x.match_browser(browser_capabilities),
+            NewSessionParameters::Spec(x) => x.match_browser(browser_capabilities),
+            NewSessionParameters::Legacy(x) => x.match_browser(browser_capabilities),
         }
     }
 }
 
 #[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct SendKeysParameters {
     pub text: String,
 }
--- a/testing/webdriver/src/httpapi.rs
+++ b/testing/webdriver/src/httpapi.rs
@@ -354,20 +354,20 @@ impl<U: WebDriverExtensionRoute> Request
 pub struct WebDriverHttpApi<U: WebDriverExtensionRoute> {
     routes: Vec<(Method, RequestMatcher<U>)>,
 }
 
 impl<U: WebDriverExtensionRoute> WebDriverHttpApi<U> {
     pub fn new(extension_routes: &[(Method, &str, U)]) -> WebDriverHttpApi<U> {
         let mut rv = WebDriverHttpApi::<U> { routes: vec![] };
         debug!("Creating routes");
-        for &(ref method, ref url, ref match_type) in standard_routes::<U>().iter() {
+        for &(ref method, ref url, ref match_type) in &standard_routes::<U>() {
             rv.add(method.clone(), *url, (*match_type).clone());
         }
-        for &(ref method, ref url, ref extension_route) in extension_routes.iter() {
+        for &(ref method, ref url, ref extension_route) in extension_routes {
             rv.add(
                 method.clone(),
                 *url,
                 Route::Extension(extension_route.clone()),
             );
         }
         rv
     }
@@ -379,17 +379,17 @@ impl<U: WebDriverExtensionRoute> WebDriv
 
     pub fn decode_request(
         &self,
         method: &Method,
         path: &str,
         body: &str,
     ) -> WebDriverResult<WebDriverMessage<U>> {
         let mut error = ErrorStatus::UnknownPath;
-        for &(ref match_method, ref matcher) in self.routes.iter() {
+        for &(ref match_method, ref matcher) in &self.routes {
             if method == *match_method {
                 let (method_match, captures) = matcher.get_match(method, path);
                 if captures.is_some() {
                     if method_match {
                         return WebDriverMessage::from_http(
                             &matcher.match_type,
                             &captures.unwrap(),
                             body,
--- a/testing/webdriver/src/server.rs
+++ b/testing/webdriver/src/server.rs
@@ -72,17 +72,17 @@ impl<T: WebDriverHandler<U>, U: WebDrive
                         Err(e) => Err(e),
                     };
 
                     match resp {
                         Ok(WebDriverResponse::NewSession(ref new_session)) => {
                             self.session = Some(Session::new(new_session.session_id.clone()));
                         }
                         Ok(WebDriverResponse::CloseWindow(CloseWindowResponse(ref handles))) => {
-                            if handles.len() == 0 {
+                            if handles.is_empty() {
                                 debug!("Last window was closed, deleting session");
                                 self.delete_session();
                             }
                         }
                         Ok(WebDriverResponse::DeleteSession) => self.delete_session(),
                         Err(ref x) if x.delete_session => self.delete_session(),
                         _ => {}
                     }
--- a/toolkit/content/tests/widgets/chrome.ini
+++ b/toolkit/content/tests/widgets/chrome.ini
@@ -1,23 +1,25 @@
 [DEFAULT]
 prefs =
   dom.ua_widget.enabled=true
   dom.webcomponents.shadowdom.enabled=true
 skip-if = os == 'android'
 support-files =
   tree_shared.js
   popup_shared.js
+  window_label_checkbox.xul
   window_menubar.xul
   seek_with_sound.ogg
 
 [test_contextmenu_nested.xul]
 skip-if = os == 'linux' # Bug 1116215
 [test_contextmenu_menugroup.xul]
 skip-if = os == 'linux' # Bug 1115088
 [test_editor_currentURI.xul]
+[test_label_checkbox.xul]
 [test_menubar.xul]
 skip-if = os == 'mac'
 [test_popupanchor.xul]
 skip-if = os == 'linux' || (verify && (os == 'win')) # Bug 1335894 perma-fail on linux 16.04
 [test_popupreflows.xul]
 [test_tree_column_reorder.xul]
 [test_videocontrols_onclickplay.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/widgets/test_label_checkbox.xul
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Label Checkbox Tests"
+  onload="onLoad()"
+  xmlns:html="http://www.w3.org/1999/xhtml"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <title>Label Checkbox Tests</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<script>
+ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+
+SimpleTest.waitForExplicitFinish();
+function onLoad()
+{
+  runTest();
+}
+
+function runTest()
+{
+  window.open("window_label_checkbox.xul", "_blank", "width=600,height=600");
+}
+
+onmessage = function onMessage()
+{
+  SimpleTest.finish();
+}
+</script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display">
+</p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/widgets/window_label_checkbox.xul
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window title="Label Checkbox Tests" width="200" height="200"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+<hbox>
+  <label control="checkbox" value="Label" id="label"/>
+  <checkbox id="checkbox"/>
+  <label control="radio2" value="Label" id="label2"/>
+  <radiogroup>
+    <radio/>
+    <radio id="radio2"/>
+  </radiogroup>
+</hbox>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+  let SimpleTest = opener.SimpleTest;
+  SimpleTest.waitForFocus(() => {
+    let ok = SimpleTest.ok;
+    let label = document.getElementById("label");
+    let checkbox = document.getElementById("checkbox");
+    let label2 = document.getElementById("label2");
+    let radio2 = document.getElementById("radio2");
+    checkbox.checked = true;
+    radio2.selected = false;
+    ok(checkbox.checked, "sanity check");
+    ok(!radio2.selected, "sanity check");
+    setTimeout(() => {
+      synthesizeMouseAtCenter(label, {});
+      ok(!checkbox.checked, "Checkbox should be unchecked");
+      synthesizeMouseAtCenter(label2, {});
+      ok(radio2.selected, "Radio2 should be selected");
+      opener.postMessage("done", "*");
+      window.close();
+    }, 0);
+  });
+
+]]>
+</script>
+
+</window>
--- a/toolkit/content/widgets/text.xml
+++ b/toolkit/content/widgets/text.xml
@@ -276,21 +276,36 @@
             return val;
           ]]>
         </setter>
       </property>
 
     </implementation>
 
     <handlers>
-      <handler event="click" action="if (this.disabled) return;
-                                     var controlElement = this.labeledControlElement;
-                                     if(controlElement)
-                                       controlElement.focus();
-                                    "/>
+      <handler event="click"><![CDATA[
+        if (this.disabled) {
+          return;
+        }
+        var controlElement = this.labeledControlElement;
+        if (!controlElement) {
+          return;
+        }
+        controlElement.focus();
+        const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+        if (controlElement.namespaceURI != XUL_NS) {
+          return;
+        }
+        if (controlElement.localName == "checkbox") {
+          controlElement.checked = !controlElement.checked;
+        } else if (controlElement.localName == "radio") {
+          controlElement.control.selectedItem = controlElement;
+        }
+      ]]></handler>
     </handlers>
   </binding>
 
   <binding id="text-link" extends="chrome://global/content/bindings/text.xml#text-label">
     <implementation>
       <property name="href" onget="return this.getAttribute('href');"
                             onset="this.setAttribute('href', val); return val;" />
       <method name="open">
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -902,19 +902,21 @@ set_config('ENABLE_MARIONETTE', marionet
 
 # geckodriver WebDriver implementation
 # ==============================================================
 option('--enable-geckodriver', help='Enable WebDriver implementation')
 
 @depends('--enable-geckodriver',
          'MOZ_AUTOMATION',
          compile_environment,
+         target,
          cross_compiling,
-         hazard_analysis)
-def geckodriver(enable, automation, compile_env, cross_compile, hazard):
+         hazard_analysis,
+         asan)
+def geckodriver(enable, automation, compile_env, target, cross_compile, hazard, asan):
     """
     geckodriver is implied on supported platforms when MOZ_AUTOMATION
     is set, but we also provide the --enable-geckodriver option for
     developers to use.
 
     At the present time, we want individual developers to be able to
     opt-in to building geckodriver locally, and for it to be enabled by
     default on supported CI build platforms.
@@ -922,17 +924,18 @@ def geckodriver(enable, automation, comp
     if enable:
         if not compile_env:
             die("--enable-geckodriver is not available without a compile "
                 "environment. A geckodriver binary will be downloaded during "
                 "an artifact build by default where available.")
         return True
 
     if enable.origin == 'default':
-        broken_platforms = cross_compile or hazard
+        # Bug 1441656 for Android.
+        broken_platforms = hazard or target.os == 'Android' or (asan and cross_compile)
 
         if automation and compile_env and not broken_platforms:
             return True
 
 set_config('ENABLE_GECKODRIVER', geckodriver)
 
 # WebRTC
 # ========================================================
--- a/toolkit/toolkit.mozbuild
+++ b/toolkit/toolkit.mozbuild
@@ -160,17 +160,17 @@ if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']
     DIRS += ['/toolkit/system/gnome']
 
 if CONFIG['ENABLE_MARIONETTE']:
     DIRS += [
         '/testing/firefox-ui',
         '/testing/marionette',
     ]
 
-if CONFIG['ENABLE_GECKODRIVER']:
+if CONFIG['ENABLE_GECKODRIVER'] and not CONFIG['MOZ_TSAN']:
     DIRS += ['/testing/geckodriver']
 
 DIRS += [
     '/tools/quitter',
     '/media/gmp-clearkey/0.1',
 ]
 
 if CONFIG['ENABLE_TESTS']:
--- a/toolkit/xre/nsEmbeddingModule.cpp
+++ b/toolkit/xre/nsEmbeddingModule.cpp
@@ -5,40 +5,36 @@
 
 #include "mozilla/ModuleUtils.h"
 #include "nsDialogParamBlock.h"
 #include "nsWindowWatcher.h"
 #include "nsAppStartupNotifier.h"
 #include "nsFind.h"
 #include "nsWebBrowserFind.h"
 #include "nsWebBrowserPersist.h"
-#include "nsCommandManager.h"
 #include "nsControllerCommandTable.h"
 #include "nsCommandParams.h"
 #include "nsCommandGroup.h"
-#include "nsBaseCommandController.h"
 #include "nsNetCID.h"
 #include "nsEmbedCID.h"
 
 #ifdef NS_PRINTING
 #include "nsPrintingPromptService.h"
 #include "nsPrintingProxy.h"
 #endif
 
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowWatcher, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsAppStartupNotifier)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFind)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebBrowserFind)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebBrowserPersist)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsControllerCommandTable)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsCommandManager)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCommandParams)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsControllerCommandGroup)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsBaseCommandController)
 
 #ifdef MOZ_XUL
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDialogParamBlock)
 #ifdef NS_PRINTING
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsPrintingPromptService,
                                          nsPrintingPromptService::GetSingleton)
 #ifdef PROXY_PRINTING
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsPrintingProxy,
@@ -54,20 +50,18 @@ NS_DEFINE_NAMED_CID(NS_PRINTINGPROMPTSER
 #endif
 #endif
 NS_DEFINE_NAMED_CID(NS_WINDOWWATCHER_CID);
 NS_DEFINE_NAMED_CID(NS_FIND_CID);
 NS_DEFINE_NAMED_CID(NS_WEB_BROWSER_FIND_CID);
 NS_DEFINE_NAMED_CID(NS_APPSTARTUPNOTIFIER_CID);
 NS_DEFINE_NAMED_CID(NS_WEBBROWSERPERSIST_CID);
 NS_DEFINE_NAMED_CID(NS_CONTROLLERCOMMANDTABLE_CID);
-NS_DEFINE_NAMED_CID(NS_COMMAND_MANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_COMMAND_PARAMS_CID);
 NS_DEFINE_NAMED_CID(NS_CONTROLLER_COMMAND_GROUP_CID);
-NS_DEFINE_NAMED_CID(NS_BASECOMMANDCONTROLLER_CID);
 
 static const mozilla::Module::CIDEntry kEmbeddingCIDs[] = {
 #ifdef MOZ_XUL
     { &kNS_DIALOGPARAMBLOCK_CID, false, nullptr, nsDialogParamBlockConstructor },
 #ifdef NS_PRINTING
 
 #ifdef PROXY_PRINTING
     { &kNS_PRINTINGPROMPTSERVICE_CID, false, nullptr, nsPrintingPromptServiceConstructor,
@@ -80,40 +74,36 @@ static const mozilla::Module::CIDEntry k
 #endif
 #endif
     { &kNS_WINDOWWATCHER_CID, false, nullptr, nsWindowWatcherConstructor },
     { &kNS_FIND_CID, false, nullptr, nsFindConstructor },
     { &kNS_WEB_BROWSER_FIND_CID, false, nullptr, nsWebBrowserFindConstructor },
     { &kNS_APPSTARTUPNOTIFIER_CID, false, nullptr, nsAppStartupNotifierConstructor },
     { &kNS_WEBBROWSERPERSIST_CID, false, nullptr, nsWebBrowserPersistConstructor },
     { &kNS_CONTROLLERCOMMANDTABLE_CID, false, nullptr, nsControllerCommandTableConstructor },
-    { &kNS_COMMAND_MANAGER_CID, false, nullptr, nsCommandManagerConstructor },
     { &kNS_COMMAND_PARAMS_CID, false, nullptr, nsCommandParamsConstructor },
     { &kNS_CONTROLLER_COMMAND_GROUP_CID, false, nullptr, nsControllerCommandGroupConstructor },
-    { &kNS_BASECOMMANDCONTROLLER_CID, false, nullptr, nsBaseCommandControllerConstructor },
     { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kEmbeddingContracts[] = {
 #ifdef MOZ_XUL
     { NS_DIALOGPARAMBLOCK_CONTRACTID, &kNS_DIALOGPARAMBLOCK_CID },
 #ifdef NS_PRINTING
     { NS_PRINTINGPROMPTSERVICE_CONTRACTID, &kNS_PRINTINGPROMPTSERVICE_CID },
 #endif
 #endif
     { NS_WINDOWWATCHER_CONTRACTID, &kNS_WINDOWWATCHER_CID },
     { NS_FIND_CONTRACTID, &kNS_FIND_CID },
     { NS_WEB_BROWSER_FIND_CONTRACTID, &kNS_WEB_BROWSER_FIND_CID },
     { NS_APPSTARTUPNOTIFIER_CONTRACTID, &kNS_APPSTARTUPNOTIFIER_CID },
     { NS_WEBBROWSERPERSIST_CONTRACTID, &kNS_WEBBROWSERPERSIST_CID },
     { NS_CONTROLLERCOMMANDTABLE_CONTRACTID, &kNS_CONTROLLERCOMMANDTABLE_CID },
-    { NS_COMMAND_MANAGER_CONTRACTID, &kNS_COMMAND_MANAGER_CID },
     { NS_COMMAND_PARAMS_CONTRACTID, &kNS_COMMAND_PARAMS_CID },
     { NS_CONTROLLER_COMMAND_GROUP_CONTRACTID, &kNS_CONTROLLER_COMMAND_GROUP_CID },
-    { NS_BASECOMMANDCONTROLLER_CONTRACTID, &kNS_BASECOMMANDCONTROLLER_CID },
     { nullptr }
 };
 
 static const mozilla::Module kEmbeddingModule = {
     mozilla::Module::kVersion,
     kEmbeddingCIDs,
     kEmbeddingContracts
 };
--- a/uriloader/base/nsIWebProgressListener.idl
+++ b/uriloader/base/nsIWebProgressListener.idl
@@ -172,46 +172,55 @@ interface nsIWebProgressListener : nsISu
    */
   const unsigned long STATE_IS_INSECURE     = 0x00000004;
   const unsigned long STATE_IS_BROKEN       = 0x00000001;
   const unsigned long STATE_IS_SECURE       = 0x00000002;
 
   /**
    * Mixed active content flags
    *
+   * NOTE: IF YOU ARE ADDING MORE OF THESE FLAGS, MAKE SURE TO EDIT
+   * nsSecureBrowserUIImpl::MapInternalToExternalState().
+   *
    * May be set in addition to the State Security Flags, to indicate that
    * mixed active content has been encountered.
    *
    * STATE_BLOCKED_MIXED_ACTIVE_CONTENT
    *   Mixed active content has been blocked from loading.
    *
    * STATE_LOADED_MIXED_ACTIVE_CONTENT
    *   Mixed active content has been loaded. State should be STATE_IS_BROKEN.
    */
   const unsigned long STATE_BLOCKED_MIXED_ACTIVE_CONTENT  = 0x00000010;
   const unsigned long STATE_LOADED_MIXED_ACTIVE_CONTENT   = 0x00000020;
 
   /**
    * Mixed display content flags
    *
+   * NOTE: IF YOU ARE ADDING MORE OF THESE FLAGS, MAKE SURE TO EDIT
+   * nsSecureBrowserUIImpl::MapInternalToExternalState().
+   *
    * May be set in addition to the State Security Flags, to indicate that
    * mixed display content has been encountered.
    *
    * STATE_BLOCKED_MIXED_DISPLAY_CONTENT
    *   Mixed display content has been blocked from loading.
    *
    * STATE_LOADED_MIXED_DISPLAY_CONTENT
    *   Mixed display content has been loaded. State should be STATE_IS_BROKEN.
    */
   const unsigned long STATE_BLOCKED_MIXED_DISPLAY_CONTENT = 0x00000100;
   const unsigned long STATE_LOADED_MIXED_DISPLAY_CONTENT  = 0x00000200;
 
    /**
    *  Safe Browsing blocking content flags
    *
+   * NOTE: IF YOU ARE ADDING MORE OF THESE FLAGS, MAKE SURE TO EDIT
+   * nsSecureBrowserUIImpl::MapInternalToExternalState().
+   *
    * May be set in addition to the State security Flags, to indicate that
    * tracking or unsafe content has been encountered.
    *
    * STATE_BLOCKED_TRACKING_CONTENT
    *   Tracking content has been blocked from loading.
    *
    * STATE_LOADED_TRACKING_CONTENT
    *   Tracking content has been loaded.
@@ -221,16 +230,19 @@ interface nsIWebProgressListener : nsISu
    */
   const unsigned long STATE_BLOCKED_TRACKING_CONTENT         = 0x00001000;
   const unsigned long STATE_LOADED_TRACKING_CONTENT          = 0x00002000;
   const unsigned long STATE_BLOCKED_UNSAFE_CONTENT           = 0x00004000;
 
   /**
    * Diagnostic flags
    *
+   * NOTE: IF YOU ARE ADDING MORE OF THESE FLAGS, MAKE SURE TO EDIT
+   * nsSecureBrowserUIImpl::MapInternalToExternalState().
+   *
    * May be set in addition to other security state flags to indicate that
    * some state is countered that deserves a warning or error, but does not
    * change the top level security state of the connection.
    *
    * STATE_CERT_DISTRUST_IMMINENT
    *   The certificate in use will be distrusted in the near future.
    */
   const unsigned long STATE_CERT_DISTRUST_IMMINENT    = 0x00010000;
@@ -289,16 +301,19 @@ interface nsIWebProgressListener : nsISu
     */
   const unsigned long STATE_USES_SSL_3                = 0x01000000;
   const unsigned long STATE_USES_WEAK_CRYPTO          = 0x02000000;
   const unsigned long STATE_CERT_USER_OVERRIDDEN      = 0x04000000;
 
   /**
    * Cookie Jar blocking
    *
+   * NOTE: IF YOU ARE ADDING MORE OF THESE FLAGS, MAKE SURE TO EDIT
+   * nsSecureBrowserUIImpl::MapInternalToExternalState().
+   *
    * These flags describe the reason of cookie jar rejection.
    *
    * STATE_COOKIES_BLOCKED_BY_PERMISSION
    *   Rejected for custom site permission.
    *
    * STATE_COOKIES_BLOCKED_TRACKER
    *   Rejected because the resource is a tracker and cookie policy doesn't
    *   allow its loading.