merge autoland to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Thu, 12 Oct 2017 11:34:05 +0200
changeset 679167 a32c32d9631c0bf7c7c283c57c299cd056e85814
parent 678974 2ca37e969460f5dfe50b26ba94d55a6182f70d81 (current diff)
parent 679166 4cc45d7c8cafd210b8efdd32b91194f81baf807e (diff)
child 679172 191c4f1b5992973a6910e4999081e46c5e5a2b56
push id84141
push userbmo:schien@mozilla.com
push dateThu, 12 Oct 2017 11:13:04 +0000
reviewersmerge, merge
milestone58.0a1
merge autoland to mozilla-central. r=merge a=merge MozReview-Commit-ID: JX8NRn7MQY4
ipc/chromium/src/base/child_privileges.h
layout/reftests/forms/input/checkbox/checkbox-clamp-ref.html
layout/reftests/forms/input/checkbox/checkbox-clamp.html
layout/reftests/forms/input/radio/radio-clamp-ref.html
layout/reftests/forms/input/radio/radio-clamp.html
memory/mozalloc/mozalloc.h
probes/moz.build
probes/mozilla-trace.d
probes/trace-gen.py
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -42,17 +42,17 @@ endif
 
 PROGRAMS_DEST = $(DIST)/bin
 
 include $(topsrcdir)/config/rules.mk
 
 ifneq (,$(filter-out WINNT,$(OS_ARCH)))
 
 ifdef COMPILE_ENVIRONMENT
-libs:: 
+libs::
 	cp -p $(MOZ_APP_NAME)$(BIN_SUFFIX) $(DIST)/bin/$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)
 endif
 
 GARBAGE += $(addprefix $(FINAL_TARGET)/defaults/pref/, firefox.js)
 
 endif
 
 # channel-prefs.js is handled separate from other prefs due to bug 756325
--- a/browser/app/macbuild/Contents/MacOS-files.in
+++ b/browser/app/macbuild/Contents/MacOS-files.in
@@ -1,12 +1,15 @@
 /*.app/***
 /*.dylib
 /certutil
 /firefox-bin
+#if defined(MOZ_GECKODRIVER)
+/geckodriver
+#endif
 /gtest/***
 #if defined(MOZ_ASAN) || defined(MOZ_TSAN)
 /llvm-symbolizer
 #endif
 /pingsender
 /pk12util
 /ssltunnel
 /webrtc-gtest
--- a/browser/app/moz.build
+++ b/browser/app/moz.build
@@ -57,16 +57,19 @@ LOCAL_INCLUDES += [
 ]
 
 if CONFIG['LIBFUZZER']:
     USE_LIBS += [ 'fuzzer' ]
     LOCAL_INCLUDES += [
         '/tools/fuzzing/libfuzzer',
     ]
 
+if CONFIG['ENABLE_GECKODRIVER']:
+    DEFINES['MOZ_GECKODRIVER'] = True
+
 if CONFIG['_MSC_VER']:
     # Always enter a Windows program through wmain, whether or not we're
     # a console application.
     WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     RCINCLUDE = 'splash.rc'
     DEFINES['MOZ_PHOENIX'] = True
--- a/browser/base/content/contentSearchUI.js
+++ b/browser/base/content/contentSearchUI.js
@@ -473,16 +473,18 @@ ContentSearchUIController.prototype = {
     }
     this.input.removeAttribute("keepfocus");
     this._hideSuggestions();
   },
 
   _onMousemove(event) {
     let idx = this._indexOfTableItem(event.target);
     if (idx >= this.numSuggestions) {
+      // Deselect any search suggestion that has been selected.
+      this.selectedIndex = -1;
       this.selectedButtonIndex = idx - this.numSuggestions;
       return;
     }
     this.selectedIndex = idx;
   },
 
   _onMouseup(event) {
     if (event.button == 2) {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1418,23 +1418,24 @@
           oldBrowser._urlbarFocused = (gURLBar && gURLBar.focused);
 
           if (this.isFindBarInitialized(oldTab)) {
             let findBar = this.getFindBar(oldTab);
             oldTab._findBarFocused = (!findBar.hidden &&
               findBar._findField.getAttribute("focused") == "true");
           }
 
-          // If focus is in the tab bar, retain it there.
-          if (document.activeElement == oldTab) {
-            // We need to explicitly focus the new tab, because
-            // tabbox.xml does this only in some cases.
+          let activeEl = document.activeElement;
+          // If focus is on the old tab, move it to the new tab.
+          if (activeEl == oldTab) {
             newTab.focus();
-          } else if (gMultiProcessBrowser && document.activeElement !== newBrowser) {
-
+          } else if (gMultiProcessBrowser && activeEl != newBrowser && activeEl != newTab) {
+            // In e10s, if focus isn't already in the tabstrip or on the new browser,
+            // and the new browser's previous focus wasn't in the url bar but focus is
+            // there now, we need to adjust focus further.
             let keepFocusOnUrlBar = newBrowser &&
                                     newBrowser._urlbarFocused &&
                                     gURLBar &&
                                     gURLBar.focused;
             if (!keepFocusOnUrlBar) {
               // Clear focus so that _adjustFocusAfterTabSwitch can detect if
               // some element has been focused and respect that.
               document.activeElement.blur();
--- a/browser/base/content/test/general/browser_contentSearchUI.js
+++ b/browser/base/content/test/general/browser_contentSearchUI.js
@@ -295,38 +295,38 @@ add_task(async function mouse() {
 
   state = await msg("mousemove", 0);
   checkState(state, "x", ["xfoo", "xbar"], 0);
 
   state = await msg("mousemove", 1);
   checkState(state, "x", ["xfoo", "xbar"], 1);
 
   state = await msg("mousemove", 2);
-  checkState(state, "x", ["xfoo", "xbar"], 1, 0);
+  checkState(state, "x", ["xfoo", "xbar"], 2, 0);
 
   state = await msg("mousemove", 3);
-  checkState(state, "x", ["xfoo", "xbar"], 1, 1);
+  checkState(state, "x", ["xfoo", "xbar"], 3, 1);
 
   state = await msg("mousemove", -1);
-  checkState(state, "x", ["xfoo", "xbar"], 1);
+  checkState(state, "x", ["xfoo", "xbar"], -1);
 
   await msg("reset");
   await setUp();
 
   state = await msg("key", { key: "x", waitForSuggestions: true });
   checkState(state, "x", ["xfoo", "xbar"], -1);
 
   state = await msg("mousemove", 0);
   checkState(state, "x", ["xfoo", "xbar"], 0);
 
   state = await msg("mousemove", 2);
-  checkState(state, "x", ["xfoo", "xbar"], 0, 0);
+  checkState(state, "x", ["xfoo", "xbar"], 2, 0);
 
   state = await msg("mousemove", -1);
-  checkState(state, "x", ["xfoo", "xbar"], 0);
+  checkState(state, "x", ["xfoo", "xbar"], -1);
 
   await msg("reset");
 });
 
 add_task(async function formHistory() {
   await setUp();
 
   // Type an X and add it to form history.
@@ -505,42 +505,26 @@ add_task(async function search() {
   eventData.engineName = TEST_ENGINE_PREFIX + " " + TEST_ENGINE_2_BASENAME;
   delete eventData.selection;
   SimpleTest.isDeeply(eventData, mesg, "Search event data");
 
   await promiseTab();
   await setUp();
 
   // Test selecting a suggestion, then clicking a one-off without deselecting the
-  // suggestion.
-  await msg("key", { key: "x", waitForSuggestions: true });
-  p = msg("waitForSearch");
-  await msg("mousemove", 1);
-  await msg("mousemove", 3);
-  await msg("click", { eltIdx: 3, modifiers });
-  mesg = await p;
-  eventData.searchString = "xfoo"
-  eventData.selection = {
-    index: 1,
-    kind: "mouse",
-  };
-  SimpleTest.isDeeply(eventData, mesg, "Search event data");
-
-  await promiseTab();
-  await setUp();
-
-  // Same as above, but with the keyboard.
+  // suggestion, using the keyboard.
   delete modifiers.button;
   await msg("key", { key: "x", waitForSuggestions: true });
   p = msg("waitForSearch");
   await msg("key", "VK_DOWN");
   await msg("key", "VK_DOWN");
   await msg("key", "VK_TAB");
   await msg("key", { key: "VK_RETURN", modifiers });
   mesg = await p;
+  eventData.searchString = "xfoo";
   eventData.selection = {
     index: 1,
     kind: "key",
   };
   SimpleTest.isDeeply(eventData, mesg, "Search event data");
 
   await promiseTab();
   await setUp();
--- a/browser/base/content/test/urlbar/browser_action_keyword_override.js
+++ b/browser/base/content/test/urlbar/browser_action_keyword_override.js
@@ -5,17 +5,17 @@ add_task(async function() {
   await PlacesUtils.keywords.insert({ keyword: "keyword",
                                       url: "http://example.com/?q=%s" })
 
   registerCleanupFunction(async function() {
     await PlacesUtils.bookmarks.remove(bm);
   });
 
   await promiseAutocompleteResultPopup("keyword search");
-  let result = gURLBar.popup.richlistbox.children[0];
+  let result = await waitForAutocompleteResultAt(0);
 
   info("Before override");
   let titleHbox = result._titleText.parentNode.parentNode;
   ok(titleHbox.classList.contains("ac-title"), "Title hbox element sanity check");
   is_element_visible(titleHbox, "Title element should be visible");
 
   let urlHbox = result._urlText.parentNode.parentNode;
   ok(urlHbox.classList.contains("ac-url"), "URL hbox element sanity check");
--- a/browser/base/content/test/urlbar/browser_action_searchengine.js
+++ b/browser/base/content/test/urlbar/browser_action_searchengine.js
@@ -1,35 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
 add_task(async function() {
   Services.search.addEngineWithDetails("MozSearch", "", "", "", "GET",
                                        "http://example.com/?q={searchTerms}");
   let engine = Services.search.getEngineByName("MozSearch");
   let originalEngine = Services.search.currentEngine;
   Services.search.currentEngine = engine;
 
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
 
-  registerCleanupFunction(() => {
+  registerCleanupFunction(async function() {
     Services.search.currentEngine = originalEngine;
     Services.search.removeEngine(engine);
-
     try {
-      gBrowser.removeTab(tab);
+      await BrowserTestUtils.removeTab(tab);
     } catch (ex) { /* tab may have already been closed in case of failure */ }
-
-    return PlacesTestUtils.clearHistory();
+    await PlacesUtils.history.clear();
   });
 
   await promiseAutocompleteResultPopup("open a search");
-  let result = gURLBar.popup.richlistbox.firstChild;
-
+  let result = await waitForAutocompleteResultAt(0);
   isnot(result, null, "Should have a result");
   is(result.getAttribute("url"),
      `moz-action:searchengine,{"engineName":"MozSearch","input":"open%20a%20search","searchQuery":"open%20a%20search"}`,
      "Result should be a moz-action: for the correct search engine");
   is(result.hasAttribute("image"), false, "Result shouldn't have an image attribute");
 
   let tabPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
   result.click();
   await tabPromise;
 
   is(gBrowser.selectedBrowser.currentURI.spec, "http://example.com/?q=open+a+search", "Correct URL should be loaded");
+
+  gURLBar.popup.hidePopup();
+  await promisePopupHidden(gURLBar.popup);
 });
--- a/browser/base/content/test/urlbar/browser_action_searchengine_alias.js
+++ b/browser/base/content/test/urlbar/browser_action_searchengine_alias.js
@@ -1,34 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
 add_task(async function() {
   let iconURI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC";
   Services.search.addEngineWithDetails("MozSearch", iconURI, "moz", "", "GET",
                                        "http://example.com/?q={searchTerms}");
   let engine = Services.search.getEngineByName("MozSearch");
   let originalEngine = Services.search.currentEngine;
   Services.search.currentEngine = engine;
 
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
 
-  registerCleanupFunction(() => {
+  registerCleanupFunction(async function() {
     Services.search.currentEngine = originalEngine;
     Services.search.removeEngine(engine);
-
     try {
-      gBrowser.removeTab(tab);
+      await BrowserTestUtils.removeTab(tab);
     } catch (ex) { /* tab may have already been closed in case of failure */ }
-
-    return PlacesTestUtils.clearHistory();
+    await PlacesUtils.history.clear();
   });
 
   await promiseAutocompleteResultPopup("moz open a search");
-
-  let result = gURLBar.popup.richlistbox.children[0];
+  let result = await waitForAutocompleteResultAt(0);
   ok(result.hasAttribute("image"), "Result should have an image attribute");
   ok(result.getAttribute("image") === engine.iconURI.spec,
      "Image attribute should have the search engine's icon");
 
   let tabPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
   EventUtils.synthesizeKey("VK_RETURN", { });
   await tabPromise;
 
   is(gBrowser.selectedBrowser.currentURI.spec, "http://example.com/?q=open+a+search");
+
+  gURLBar.popup.hidePopup();
+  await promisePopupHidden(gURLBar.popup);
 });
--- a/browser/base/content/test/urlbar/browser_autocomplete_a11y_label.js
+++ b/browser/base/content/test/urlbar/browser_autocomplete_a11y_label.js
@@ -4,19 +4,17 @@
 const SUGGEST_ALL_PREF = "browser.search.suggest.enabled";
 const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
 const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
 
 add_task(async function switchToTab() {
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:about");
 
   await promiseAutocompleteResultPopup("% about");
-
-  ok(gURLBar.popup.richlistbox.children.length > 1, "Should get at least 2 results");
-  let result = gURLBar.popup.richlistbox.children[1];
+  let result = await waitForAutocompleteResultAt(1);
   is(result.getAttribute("type"), "switchtab", "Expect right type attribute");
   is(result.label, "about:about about:about Tab", "Result a11y label should be: <title> <url> Tab");
 
   gURLBar.popup.hidePopup();
   await promisePopupHidden(gURLBar.popup);
   gBrowser.removeTab(tab);
 });
 
@@ -29,16 +27,17 @@ add_task(async function searchSuggestion
   Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, true);
   registerCleanupFunction(function() {
     Services.search.currentEngine = oldCurrentEngine;
     Services.prefs.clearUserPref(SUGGEST_ALL_PREF);
     Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, suggestionsEnabled);
   });
 
   await promiseAutocompleteResultPopup("foo");
+  await waitForAutocompleteResultAt(2);
   // Don't assume that the search doesn't match history or bookmarks left around
   // by earlier tests.
   Assert.ok(gURLBar.popup.richlistbox.children.length >= 3,
             "Should get at least heuristic result + two search suggestions");
   // The first expected search is the search term itself since the heuristic
   // result will come before the search suggestions.
   let expectedSearches = [
     "foo",
@@ -49,10 +48,11 @@ add_task(async function searchSuggestion
     if (child.getAttribute("type").split(/\s+/).indexOf("searchengine") >= 0) {
       Assert.ok(expectedSearches.length > 0);
       let suggestion = expectedSearches.shift();
       Assert.equal(child.label, suggestion + " browser_searchSuggestionEngine searchSuggestionEngine.xml Search",
                    "Result label should be: <search term> <engine name> Search");
     }
   }
   Assert.ok(expectedSearches.length == 0);
-  gURLBar.closePopup();
+  gURLBar.popup.hidePopup();
+  await promisePopupHidden(gURLBar.popup);
 });
--- a/browser/base/content/test/urlbar/browser_autocomplete_autoselect.js
+++ b/browser/base/content/test/urlbar/browser_autocomplete_autoselect.js
@@ -1,8 +1,11 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
 const ONEOFF_URLBAR_PREF = "browser.urlbar.oneOffSearches";
 
 function repeat(limit, func) {
   for (let i = 0; i < limit; i++) {
     func(i);
   }
 }
 
@@ -22,33 +25,34 @@ function is_selected_one_off(index) {
   // This is true because although both the listbox and the one-offs can have
   // selections, the test doesn't check that.
   is(gURLBar.popup.richlistbox.selectedIndex, -1,
      "A one-off is selected, so the listbox should not have a selection");
 }
 
 add_task(async function() {
   let maxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults");
-
   Services.prefs.setBoolPref(ONEOFF_URLBAR_PREF, true);
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
   registerCleanupFunction(async function() {
     await PlacesTestUtils.clearHistory();
     Services.prefs.clearUserPref(ONEOFF_URLBAR_PREF);
+    await BrowserTestUtils.removeTab(tab);
   });
 
   let visits = [];
   repeat(maxResults, i => {
     visits.push({
       uri: makeURI("http://example.com/autocomplete/?" + i),
     });
   });
   await PlacesTestUtils.addVisits(visits);
 
-  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
   await promiseAutocompleteResultPopup("example.com/autocomplete");
+  await waitForAutocompleteResultAt(maxResults - 1);
 
   let popup = gURLBar.popup;
   let results = popup.richlistbox.children;
   is(results.length, maxResults,
      "Should get maxResults=" + maxResults + " results");
   is_selected(0);
 
   info("Key Down to select the next item");
@@ -83,10 +87,9 @@ add_task(async function() {
   is_selected(0);
 
   info("Page Up again will wrap around to the end of the list");
   EventUtils.synthesizeKey("VK_PAGE_UP", {})
   is_selected(maxResults - 1);
 
   EventUtils.synthesizeKey("VK_ESCAPE", {});
   await promisePopupHidden(gURLBar.popup);
-  gBrowser.removeTab(tab);
 });
--- a/browser/base/content/test/urlbar/browser_autocomplete_cursor.js
+++ b/browser/base/content/test/urlbar/browser_autocomplete_cursor.js
@@ -1,17 +1,20 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
 add_task(async function() {
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
   await promiseAutocompleteResultPopup("www.mozilla.org");
 
   gURLBar.selectTextRange(4, 4);
 
   is(gURLBar.popup.state, "open", "Popup should be open");
   is(gURLBar.popup.richlistbox.selectedIndex, 0, "Should have selected something");
 
   EventUtils.synthesizeKey("VK_RIGHT", {});
   await promisePopupHidden(gURLBar.popup);
 
   is(gURLBar.selectionStart, 5, "Should have moved the cursor");
   is(gURLBar.selectionEnd, 5, "And not selected anything");
 
-  gBrowser.removeTab(tab);
+  await BrowserTestUtils.removeTab(tab);
 });
--- a/browser/base/content/test/urlbar/browser_autocomplete_edit_completed.js
+++ b/browser/base/content/test/urlbar/browser_autocomplete_edit_completed.js
@@ -1,24 +1,24 @@
 add_task(async function() {
-  await PlacesTestUtils.clearHistory();
+  await PlacesUtils.history.clear();
 
   await PlacesTestUtils.addVisits([
     { uri: makeURI("http://example.com/foo") },
     { uri: makeURI("http://example.com/foo/bar") },
   ]);
 
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
   registerCleanupFunction(async function() {
-    await PlacesTestUtils.clearHistory();
+    await BrowserTestUtils.removeTab(tab);
+    await PlacesUtils.history.clear();
   });
 
-  gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:blank");
-  gURLBar.focus();
-
   await promiseAutocompleteResultPopup("http://example.com");
+  await waitForAutocompleteResultAt(1);
 
   let popup = gURLBar.popup;
   let list = popup.richlistbox;
   let initialIndex = list.selectedIndex;
 
   info("Key Down to select the next item.");
   EventUtils.synthesizeKey("VK_DOWN", {});
 
@@ -39,10 +39,9 @@ add_task(async function() {
 
   info("Press return to load edited URL.");
   EventUtils.synthesizeKey("VK_RETURN", {});
   await Promise.all([
     promisePopupHidden(gURLBar.popup),
     docLoad,
   ]);
 
-  gBrowser.removeTab(gBrowser.selectedTab);
 });
--- a/browser/base/content/test/urlbar/browser_autocomplete_enter_race.js
+++ b/browser/base/content/test/urlbar/browser_autocomplete_enter_race.js
@@ -1,15 +1,17 @@
 // The order of these tests matters!
 
 add_task(async function setup() {
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
-  let bm = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
-                                                url: "http://example.com/?q=%s",
-                                                title: "test" });
+  let bm = await PlacesUtils.bookmarks.insert({
+    parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+    url: "http://example.com/?q=%s",
+    title: "test"
+  });
   registerCleanupFunction(async function() {
     await PlacesUtils.bookmarks.remove(bm);
     await BrowserTestUtils.removeTab(tab);
   });
   await PlacesUtils.keywords.insert({ keyword: "keyword",
                                       url: "http://example.com/?q=%s" });
   // Needs at least one success.
   ok(true, "Setup complete");
--- a/browser/base/content/test/urlbar/browser_autocomplete_no_title.js
+++ b/browser/base/content/test/urlbar/browser_autocomplete_no_title.js
@@ -1,15 +1,17 @@
 add_task(async function() {
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
-
-  let uri = NetUtil.newURI("http://bug1060642.example.com/beards/are/pretty/great");
-  await PlacesTestUtils.addVisits([{uri, title: ""}]);
+  await PlacesUtils.history.clear();
+  const uri = "http://bug1060642.example.com/beards/are/pretty/great";
+  await PlacesTestUtils.addVisits([{ uri, title: "" }]);
+  registerCleanupFunction(async function() {
+    await PlacesUtils.history.clear();
+    await BrowserTestUtils.removeTab(tab);
+  });
 
   await promiseAutocompleteResultPopup("bug1060642");
-  ok(gURLBar.popup.richlistbox.children.length > 1, "Should get at least 2 results");
-  let result = gURLBar.popup.richlistbox.children[1];
+  let result = await waitForAutocompleteResultAt(1);
   is(result._titleText.textContent, "bug1060642.example.com", "Result title should be as expected");
 
   gURLBar.popup.hidePopup();
   await promisePopupHidden(gURLBar.popup);
-  gBrowser.removeTab(tab);
 });
--- a/browser/base/content/test/urlbar/browser_autocomplete_tag_star_visibility.js
+++ b/browser/base/content/test/urlbar/browser_autocomplete_tag_star_visibility.js
@@ -1,21 +1,22 @@
 add_task(async function() {
-  registerCleanupFunction(() => {
-    PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
+  registerCleanupFunction(async function() {
+    await PlacesUtils.bookmarks.eraseEverything();
   });
 
   async function addTagItem(tagName) {
-    let uri = NetUtil.newURI(`http://example.com/this/is/tagged/${tagName}`);
-    PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
-                                         uri,
-                                         PlacesUtils.bookmarks.DEFAULT_INDEX,
-                                         `test ${tagName}`);
-    PlacesUtils.tagging.tagURI(uri, [tagName]);
-    await PlacesTestUtils.addVisits([{uri, title: `Test page with tag ${tagName}`}]);
+    let url = `http://example.com/this/is/tagged/${tagName}`;
+    await PlacesUtils.bookmarks.insert({
+      parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+      url,
+      title: `test ${tagName}`
+    });
+    PlacesUtils.tagging.tagURI(Services.io.newURI(url), [tagName]);
+    await PlacesTestUtils.addVisits({uri: url, title: `Test page with tag ${tagName}`});
   }
 
   // We use different tags for each part of the test, as otherwise the
   // autocomplete code tries to be smart by using the previously cached element
   // without updating it (since all parameters it knows about are the same).
 
   let testcases = [{
     description: "Test with suggest.bookmark=true",
@@ -78,17 +79,17 @@ add_task(async function() {
     info(`Test case: ${testcase.description}`);
 
     await addTagItem(testcase.tagName);
     for (let prefName of Object.keys(testcase.prefs)) {
       Services.prefs.setBoolPref(`browser.urlbar.${prefName}`, testcase.prefs[prefName]);
     }
 
     await promiseAutocompleteResultPopup(testcase.input);
-    let result = gURLBar.popup.richlistbox.children[1];
+    let result = await waitForAutocompleteResultAt(1);
     ok(result && !result.collasped, "Should have result");
 
     is(result.getAttribute("type"), testcase.expected.type, "Result should have expected type");
 
     let typeIconStyle = window.getComputedStyle(result._typeIcon);
     let imageURL = typeIconStyle.listStyleImage;
     if (testcase.expected.typeImageVisible) {
       ok(/^url\(.+\)$/.test(imageURL), "Type image should be visible");
--- a/browser/base/content/test/urlbar/browser_search_favicon.js
+++ b/browser/base/content/test/urlbar/browser_search_favicon.js
@@ -25,18 +25,17 @@ add_task(async function() {
   let uri = NetUtil.newURI("http://s.example.com/search?q=foo&client=1");
   await PlacesTestUtils.addVisits({ uri, title: "Foo - SearchEngine Search" });
 
   await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
 
   // The first autocomplete result has the action searchengine, while
   // the second result is the "search favicon" element.
   await promiseAutocompleteResultPopup("foo");
-  let result = gURLBar.popup.richlistbox.children[1];
-
+  let result = await waitForAutocompleteResultAt(1);
   isnot(result, null, "Expect a search result");
   is(result.getAttribute("type"), "searchengine", "Expect correct `type` attribute");
 
   let titleHbox = result._titleText.parentNode.parentNode;
   ok(titleHbox.classList.contains("ac-title"), "Title hbox sanity check");
   is_element_visible(titleHbox, "Title element should be visible");
 
   let urlHbox = result._urlText.parentNode.parentNode;
--- a/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions_opt-out.js
+++ b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions_opt-out.js
@@ -67,21 +67,22 @@ add_task(async function focus() {
 
 add_task(async function click_on_focused() {
   // Even if the location bar is already focused, we should still show the popup
   // and the notification on click.
   setupVisibleHint();
   gURLBar.blur();
   // Won't show the hint since it's not user initiated.
   gURLBar.focus();
-  await new Promise(resolve => setTimeout(resolve, 500));
+  await new Promise(resolve => setTimeout(resolve, 1000));
   Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
+  Assert.ok(gURLBar.focused, "The input field should be focused");
 
   let popupPromise = promisePopupShown(gURLBar.popup);
-  EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, { button: 0, type: "mousedown" });
+  EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {});
   await popupPromise;
 
   Assert.ok(gURLBar.popup.popupOpen, "popup should be open");
   assertVisible(true);
   assertFooterVisible(false);
   Assert.equal(gURLBar.popup._matchCount, 0, "popup should have no results");
   gURLBar.blur();
   Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
--- a/browser/base/content/test/urlbar/browser_urlbar_remove_match.js
+++ b/browser/base/content/test/urlbar/browser_urlbar_remove_match.js
@@ -7,21 +7,21 @@ add_task(async function test_remove_hist
   await PlacesTestUtils.addVisits(TEST_URL);
 
   registerCleanupFunction(async function() {
     await PlacesUtils.history.clear();
   });
 
   let promiseVisitRemoved = PlacesTestUtils.waitForNotification(
     "onDeleteURI", uri => uri.spec == TEST_URL, "history");
+
   await promiseAutocompleteResultPopup("remove.me/from_urlbar");
-  await BrowserTestUtils.waitForCondition(
-    () => gURLBar.popup.richlistbox.children.length > 1 &&
-          gURLBar.popup.richlistbox.children[1].getAttribute("ac-value") == TEST_URL,
-    "Waiting for the result to appear");
+  let result = await waitForAutocompleteResultAt(1);
+  Assert.equal(result.getAttribute("ac-value"), TEST_URL, "Found the expected result");
+
   EventUtils.synthesizeKey("VK_DOWN", {});
   Assert.equal(gURLBar.popup.richlistbox.selectedIndex, 1);
   let options = AppConstants.platform == "macosx" ? { shiftKey: true } : {};
   EventUtils.synthesizeKey("VK_DELETE", options);
   await promiseVisitRemoved;
   await BrowserTestUtils.waitForCondition(
     () => !gURLBar.popup.richlistbox.children.some(c => !c.collapsed && c.getAttribute("ac-value") == TEST_URL),
     "Waiting for the result to disappear");
--- a/browser/base/content/test/urlbar/browser_urlbar_search_no_speculative_connect_with_client_cert.js
+++ b/browser/base/content/test/urlbar/browser_urlbar_search_no_speculative_connect_with_client_cert.js
@@ -145,29 +145,27 @@ add_task(async function setup() {
 add_task(async function popup_mousedown_no_client_cert_dialog_until_navigate_test() {
   const test = {
     // To not trigger autofill, search keyword starts from the second character.
     search: host.substr(1, 4),
     completeValue: uri
   };
   info(`Searching for '${test.search}'`);
   await promiseAutocompleteResultPopup(test.search, window, true);
+  await waitForAutocompleteResultAt(1);
   let controller = gURLBar.popup.input.controller;
   // The first item should be 'Search with ...' thus we want the second.
   let value = controller.getFinalCompleteValueAt(1);
   info(`The value of the second item is ${value}`);
   is(value, test.completeValue, "The second item has the url we visited.");
 
-  await BrowserTestUtils.waitForCondition(() => {
-    return !!gURLBar.popup.richlistbox.childNodes[1] &&
-           is_visible(gURLBar.popup.richlistbox.childNodes[1]);
-  }, "the node is there.");
+  let listitem = await waitForAutocompleteResultAt(1);
+  Assert.ok(is_visible(listitem), "The node is there.");
 
   expectingChooseCertificate = false;
-  let listitem = gURLBar.popup.richlistbox.childNodes[1];
   EventUtils.synthesizeMouseAtCenter(listitem, {type: "mousedown"}, window);
   is(gURLBar.popup.richlistbox.selectedIndex, 1, "The second item is selected");
 
   // We shouldn't have triggered a speculative connection, because a client
   // certificate is installed.
   SimpleTest.requestFlakyTimeout("Wait for UI");
   await new Promise(resolve => setTimeout(resolve, 200));
 
--- a/browser/base/content/test/urlbar/head.js
+++ b/browser/base/content/test/urlbar/head.js
@@ -308,8 +308,17 @@ function promisePageActionViewChildrenVi
 function promiseSpeculativeConnection(httpserver) {
   return BrowserTestUtils.waitForCondition(() => {
     if (httpserver) {
       return httpserver.connectionNumber == 1;
     }
     return false;
   }, "Waiting for connection setup");
 }
+
+async function waitForAutocompleteResultAt(index) {
+  let searchString = gURLBar.controller.searchString;
+  await BrowserTestUtils.waitForCondition(
+    () => gURLBar.popup.richlistbox.children.length > index &&
+          gURLBar.popup.richlistbox.children[index].getAttribute("ac-text") == searchString,
+    `Waiting for the autocomplete result for "${searchString}" at [${index}] to appear`);
+  return gURLBar.popup.richlistbox.children[index];
+}
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -1673,18 +1673,16 @@ var CustomizableUIInternal = {
     // whether we're in an input container (text field)
     let inInput = false;
     // whether we're in a popup/context menu
     let inMenu = false;
     // whether we're in a toolbarbutton/toolbaritem
     let inItem = false;
     // whether the current menuitem has a valid closemenu attribute
     let menuitemCloseMenu = "auto";
-    // whether the toolbarbutton/item has a valid closemenu attribute.
-    let closemenu = "auto";
 
     // While keeping track of that, we go from the original target back up,
     // to the panel if we have to. We bail as soon as we find an input,
     // a toolbarbutton/item, or the panel:
     while (true && target) {
       // Skip out of iframes etc:
       if (target.nodeType == target.DOCUMENT_NODE) {
         if (!target.defaultView) {
@@ -1700,21 +1698,16 @@ var CustomizableUIInternal = {
           break;
         }
       }
       let tagName = target.localName;
       inInput = tagName == "input" || tagName == "textbox";
       inItem = tagName == "toolbaritem" || tagName == "toolbarbutton";
       let isMenuItem = tagName == "menuitem";
       inMenu = inMenu || isMenuItem;
-      if (inItem && target.hasAttribute("closemenu")) {
-        let closemenuVal = target.getAttribute("closemenu");
-        closemenu = (closemenuVal == "single" || closemenuVal == "none") ?
-                    closemenuVal : "auto";
-      }
 
       if (isMenuItem && target.hasAttribute("closemenu")) {
         let closemenuVal = target.getAttribute("closemenu");
         menuitemCloseMenu = (closemenuVal == "single" || closemenuVal == "none") ?
                             closemenuVal : "auto";
       }
       // Break out of the loop immediately for disabled items, as we need to
       // keep the menu open in that case.
@@ -1748,27 +1741,16 @@ var CustomizableUIInternal = {
       // Otherwise, we're probably fine to close the panel
       return false;
     }
     // If we're not in a menu, and we *are* in a type="menu" toolbarbutton,
     // we'll now interact with the menu
     if (inItem && target.getAttribute("type") == "menu") {
       return true;
     }
-    // If we're not in a menu, and we *are* in a type="menu-button" toolbarbutton,
-    // it depends whether we're in the dropmarker or the 'real' button:
-    if (inItem && target.getAttribute("type") == "menu-button") {
-      // 'real' button (which has a single action):
-      if (target.getAttribute("anonid") == "button") {
-        return closemenu != "none";
-      }
-      // otherwise, this is the outer button, and the user will now
-      // interact with the menu:
-      return true;
-    }
     return inInput || !inItem;
   },
 
   hidePanelForNode(aNode) {
     let panel = this._getPanelForNode(aNode);
     if (panel) {
       panel.hidePopup();
     }
@@ -4216,16 +4198,17 @@ function OverflowableToolbar(aToolbarNod
   } else {
     Services.obs.addObserver(this, "browser-delayed-startup-finished");
   }
 }
 
 OverflowableToolbar.prototype = {
   initialized: false,
   _forceOnOverflow: false,
+  _addedListener: false,
 
   observe(aSubject, aTopic, aData) {
     if (aTopic == "browser-delayed-startup-finished" &&
         aSubject == this._toolbar.ownerGlobal) {
       Services.obs.removeObserver(this, "browser-delayed-startup-finished");
       this.init();
     }
   },
@@ -4244,16 +4227,17 @@ OverflowableToolbar.prototype = {
     this._chevron.addEventListener("dragend", this);
 
     let panelId = this._toolbar.getAttribute("overflowpanel");
     this._panel = doc.getElementById(panelId);
     this._panel.addEventListener("popuphiding", this);
     CustomizableUIInternal.addPanelCloseListeners(this._panel);
 
     CustomizableUI.addListener(this);
+    this._addedListener = true;
 
     // The 'overflow' event may have been fired before init was called.
     if (this._toolbar.overflowedDuringConstruction) {
       this.onOverflow(this._toolbar.overflowedDuringConstruction);
       this._toolbar.overflowedDuringConstruction = null;
     }
 
     this.initialized = true;
@@ -4275,16 +4259,17 @@ OverflowableToolbar.prototype = {
     window.removeEventListener("resize", this);
     window.gNavToolbox.removeEventListener("customizationstarting", this);
     window.gNavToolbox.removeEventListener("aftercustomization", this);
     this._chevron.removeEventListener("command", this);
     this._chevron.removeEventListener("dragover", this);
     this._chevron.removeEventListener("dragend", this);
     this._panel.removeEventListener("popuphiding", this);
     CustomizableUI.removeListener(this);
+    this._addedListener = false;
     CustomizableUIInternal.removePanelCloseListeners(this._panel);
   },
 
   handleEvent(aEvent) {
     switch (aEvent.type) {
       case "aftercustomization":
         this._enable();
         break;
@@ -4392,20 +4377,22 @@ OverflowableToolbar.prototype = {
       if (child.getAttribute("overflows") != "false") {
         this._collapsed.set(child.id, this._target.clientWidth);
         child.setAttribute("overflowedItem", true);
         child.setAttribute("cui-anchorid", this._chevron.id);
         CustomizableUIInternal.ensureButtonContextMenu(child, this._toolbar, true);
         CustomizableUIInternal.notifyListeners("onWidgetOverflow", child, this._target);
 
         this._list.insertBefore(child, this._list.firstChild);
-        if (!this._toolbar.hasAttribute("overflowing")) {
+        if (!this._addedListener) {
           CustomizableUI.addListener(this);
         }
-        this._toolbar.setAttribute("overflowing", "true");
+        if (!CustomizableUI.isSpecialWidget(child.id)) {
+          this._toolbar.setAttribute("overflowing", "true");
+        }
       }
       child = prevChild;
     }
 
     let win = this._target.ownerGlobal;
     win.UpdateUrlbarSearchSplitterState();
   },
 
@@ -4421,17 +4408,17 @@ OverflowableToolbar.prototype = {
     let placements = gPlacements.get(this._toolbar.id);
     while (this._list.firstChild) {
       let child = this._list.firstChild;
       let minSize = this._collapsed.get(child.id);
 
       if (!shouldMoveAllItems &&
           minSize &&
           this._target.clientWidth <= minSize) {
-        return;
+        break;
       }
 
       this._collapsed.delete(child.id);
       let beforeNodeIndex = placements.indexOf(child.id) + 1;
       // If this is a skipintoolbarset item, meaning it doesn't occur in the placements list,
       // we're inserting it at the end. This will mean first-in, first-out (more or less)
       // leading to as little change in order as possible.
       if (beforeNodeIndex == 0) {
@@ -4456,19 +4443,23 @@ OverflowableToolbar.prototype = {
       child.removeAttribute("overflowedItem");
       CustomizableUIInternal.ensureButtonContextMenu(child, this._target);
       CustomizableUIInternal.notifyListeners("onWidgetUnderflow", child, this._target);
     }
 
     let win = this._target.ownerGlobal;
     win.UpdateUrlbarSearchSplitterState();
 
-    if (!this._collapsed.size) {
+    let collapsedWidgetIds = Array.from(this._collapsed.keys());
+    if (collapsedWidgetIds.every(w => CustomizableUI.isSpecialWidget(w))) {
       this._toolbar.removeAttribute("overflowing");
+    }
+    if (this._addedListener && !this._collapsed.size) {
       CustomizableUI.removeListener(this);
+      this._addedListener = false;
     }
   },
 
   _onLazyResize() {
     if (!this._enabled)
       return;
 
     if (this._target.scrollLeftMin != this._target.scrollLeftMax) {
@@ -4548,19 +4539,23 @@ OverflowableToolbar.prototype = {
       // If it used to be overflowed...
       // ... and isn't anymore, let's remove our bookkeeping:
       this._collapsed.delete(aNode.id);
       aNode.removeAttribute("cui-anchorid");
       aNode.removeAttribute("overflowedItem");
       CustomizableUIInternal.ensureButtonContextMenu(aNode, aContainer);
       CustomizableUIInternal.notifyListeners("onWidgetUnderflow", aNode, this._target);
 
-      if (!this._collapsed.size) {
+      let collapsedWidgetIds = Array.from(this._collapsed.keys());
+      if (collapsedWidgetIds.every(w => CustomizableUI.isSpecialWidget(w))) {
         this._toolbar.removeAttribute("overflowing");
+      }
+      if (this._addedListener && !this._collapsed.size) {
         CustomizableUI.removeListener(this);
+        this._addedListener = false;
       }
     } else if (aNode.previousSibling) {
       // but if it still is, it must have changed places. Bookkeep:
       let prevId = aNode.previousSibling.id;
       let minSize = this._collapsed.get(prevId);
       this._collapsed.set(aNode.id, minSize);
     } else {
       // If it's now the first item in the overflow list,
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -583,17 +583,17 @@ CustomizeMode.prototype = {
   _promiseWidgetAnimationOut(aNode) {
     if (!gCosmeticAnimationsEnabled ||
         aNode.getAttribute("cui-anchorid") == "nav-bar-overflow-button" ||
         (aNode.tagName != "toolbaritem" && aNode.tagName != "toolbarbutton") ||
         (aNode.id == "downloads-button" && aNode.hidden)) {
       return null;
     }
     let animationNode;
-    if (aNode.parentNode.id.startsWith("wrapper-")) {
+    if (aNode.parentNode && aNode.parentNode.id.startsWith("wrapper-")) {
       animationNode = aNode.parentNode;
     } else {
       animationNode = aNode;
     }
     return new Promise(resolve => {
       animationNode.classList.add("animate-out");
       animationNode.addEventListener("animationend", function cleanupWidgetAnimationEnd(e) {
         if (e.animationName == "widget-animate-out" && e.target.id == animationNode.id) {
@@ -628,17 +628,17 @@ CustomizeMode.prototype = {
     if (aNode.id == "downloads-button") {
       Services.prefs.setBoolPref(kDownloadAutoHidePref, false);
       if (this._customizing) {
         this._showDownloadsAutoHidePanel();
       }
     }
 
     if (widgetAnimationPromise) {
-      if (aNode.parentNode.id.startsWith("wrapper-")) {
+      if (aNode.parentNode && aNode.parentNode.id.startsWith("wrapper-")) {
         aNode.parentNode.classList.remove("animate-out");
       } else {
         aNode.classList.remove("animate-out")
       }
     }
   },
 
   async addToPanel(aNode) {
@@ -661,17 +661,17 @@ CustomizeMode.prototype = {
     if (aNode.id == "downloads-button") {
       Services.prefs.setBoolPref(kDownloadAutoHidePref, false);
       if (this._customizing) {
         this._showDownloadsAutoHidePanel();
       }
     }
 
     if (widgetAnimationPromise) {
-      if (aNode.parentNode.id.startsWith("wrapper-")) {
+      if (aNode.parentNode && aNode.parentNode.id.startsWith("wrapper-")) {
         aNode.parentNode.classList.remove("animate-out");
       } else {
         aNode.classList.remove("animate-out")
       }
     }
     if (gCosmeticAnimationsEnabled) {
       let overflowButton = this.document.getElementById("nav-bar-overflow-button");
       BrowserUtils.setToolbarButtonHeightProperty(overflowButton).then(() => {
@@ -707,17 +707,17 @@ CustomizeMode.prototype = {
     // If the user explicitly removes this item, turn off autohide.
     if (aNode.id == "downloads-button") {
       Services.prefs.setBoolPref(kDownloadAutoHidePref, false);
       if (this._customizing) {
         this._showDownloadsAutoHidePanel();
       }
     }
     if (widgetAnimationPromise) {
-      if (aNode.parentNode.id.startsWith("wrapper-")) {
+      if (aNode.parentNode && aNode.parentNode.id.startsWith("wrapper-")) {
         aNode.parentNode.classList.remove("animate-out");
       } else {
         aNode.classList.remove("animate-out")
       }
     }
   },
 
   populatePalette() {
--- a/browser/components/customizableui/PanelMultiView.jsm
+++ b/browser/components/customizableui/PanelMultiView.jsm
@@ -1043,18 +1043,17 @@ this.PanelMultiView = class {
             blockInBoxWorkaround();
           }
         }
         break;
       }
       case "popupshown":
         // Now that the main view is visible, we can check the height of the
         // description elements it contains.
-        if (!this.panelViews)
-          this.descriptionHeightWorkaround();
+        this.descriptionHeightWorkaround();
         break;
       case "popuphidden": {
         // WebExtensions consumers can hide the popup from viewshowing, or
         // mid-transition, which disrupts our state:
         this._viewShowing = null;
         this._transitioning = false;
         this.node.removeAttribute("panelopen");
         this.showMainView();
--- a/browser/components/customizableui/test/browser_940307_panel_click_closure_handling.js
+++ b/browser/components/customizableui/test/browser_940307_panel_click_closure_handling.js
@@ -18,61 +18,16 @@ add_task(async function plain_button() {
   await document.getElementById("nav-bar").overflowable.show();
   let hiddenAgain = promiseOverflowHidden(window);
   EventUtils.synthesizeMouseAtCenter(button, {});
   await hiddenAgain;
   CustomizableUI.removeWidgetFromArea(button.id);
   button.remove();
 });
 
-/* Clicking a menu button should close the panel, opening the popup shouldn't.  */
-add_task(async function menu_button_popup() {
-  menuButton = document.createElement("toolbarbutton");
-  menuButton.setAttribute("type", "menu-button");
-  menuButton.id = "browser_940307_menubutton";
-  menuButton.setAttribute("label", "Menu button");
-
-  let menuPopup = document.createElement("menupopup");
-  menuPopup.id = "browser_940307_menupopup";
-
-  let menuItem = document.createElement("menuitem");
-  menuItem.setAttribute("label", "Menu item");
-  menuItem.id = "browser_940307_menuitem";
-
-  menuPopup.appendChild(menuItem);
-  menuButton.appendChild(menuPopup);
-  gNavToolbox.palette.appendChild(menuButton);
-  CustomizableUI.addWidgetToArea(menuButton.id, CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
-
-  await waitForOverflowButtonShown();
-
-  await document.getElementById("nav-bar").overflowable.show();
-  let hiddenAgain = promiseOverflowHidden(window);
-  let innerButton = document.getAnonymousElementByAttribute(menuButton, "anonid", "button");
-  EventUtils.synthesizeMouseAtCenter(innerButton, {});
-  await hiddenAgain;
-
-  // Now click the dropmarker to show the menu
-  await document.getElementById("nav-bar").overflowable.show();
-  hiddenAgain = promiseOverflowHidden(window);
-  let menuShown = promisePanelElementShown(window, menuPopup);
-  let dropmarker = document.getAnonymousElementByAttribute(menuButton, "type", "menu-button");
-  EventUtils.synthesizeMouseAtCenter(dropmarker, {});
-  await menuShown;
-  // Panel should stay open:
-  ok(isOverflowOpen(), "Panel should still be open");
-  let menuHidden = promisePanelElementHidden(window, menuPopup);
-  // Then click the menu item to close all the things
-  EventUtils.synthesizeMouseAtCenter(menuItem, {});
-  await menuHidden;
-  await hiddenAgain;
-  CustomizableUI.removeWidgetFromArea(menuButton.id);
-  menuButton.remove();
-});
-
 add_task(async function searchbar_in_panel() {
   CustomizableUI.addWidgetToArea("search-container",
                                  CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
 
   await waitForOverflowButtonShown();
 
   await document.getElementById("nav-bar").overflowable.show();
 
--- a/browser/components/customizableui/test/browser_widget_animation.js
+++ b/browser/components/customizableui/test/browser_widget_animation.js
@@ -22,35 +22,33 @@ function promiseWidgetAnimationOut(aNode
   });
 }
 
 function promiseOverflowAnimationEnd() {
   return new Promise(resolve => {
     let overflowButton = document.getElementById("nav-bar-overflow-button");
     overflowButton.addEventListener("animationend", function cleanupOverflowAnimationOut(event) {
       if (event.animationName == "overflow-fade") {
-        overflowButton.removeEventListener("transitionend", cleanupOverflowAnimationOut);
+        overflowButton.removeEventListener("animationend", cleanupOverflowAnimationOut);
         ok(true, "The overflow button`s animationend event should have happened");
         resolve();
       }
     });
   });
 }
 
 // Right-click on the home widget, use the context menu to move it to the overflow menu.
 // The home widget should animate out, and the overflow menu should animate upon adding.
 add_task(async function() {
   let homeButton = document.getElementById("home-button");
   let contextMenu = document.getElementById("toolbar-context-menu");
   let shownPromise = popupShown(contextMenu);
   EventUtils.synthesizeMouseAtCenter(homeButton, {type: "contextmenu", button: 2 });
   await shownPromise;
 
-  let moveToPanel = contextMenu.querySelector(".customize-context-moveToPanel");
-  if (moveToPanel) {
-    moveToPanel.click();
-  }
+  contextMenu.querySelector(".customize-context-moveToPanel").click();
+  await contextMenu.hidePopup();
 
   await Promise.all([promiseWidgetAnimationOut(homeButton), promiseOverflowAnimationEnd()]);
   ok(true, "The widget and overflow animations should have both happened.");
 });
 
 registerCleanupFunction(CustomizableUI.reset);
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -85,17 +85,16 @@ skip-if = (os == 'win' && !debug) # bug 
 [browser_ext_geckoProfiler_symbolicate.js]
 [browser_ext_getViews.js]
 [browser_ext_identity_indication.js]
 [browser_ext_incognito_views.js]
 [browser_ext_incognito_popup.js]
 [browser_ext_lastError.js]
 [browser_ext_menus.js]
 [browser_ext_omnibox.js]
-skip-if = debug || asan # Bug 1354681
 [browser_ext_openPanel.js]
 [browser_ext_optionsPage_browser_style.js]
 [browser_ext_optionsPage_modals.js]
 [browser_ext_optionsPage_privileges.js]
 [browser_ext_pageAction_context.js]
 [browser_ext_pageAction_contextMenu.js]
 [browser_ext_pageAction_popup.js]
 [browser_ext_pageAction_popup_resize.js]
--- a/browser/components/extensions/test/browser/browser_ext_omnibox.js
+++ b/browser/components/extensions/test/browser/browser_ext_omnibox.js
@@ -89,18 +89,20 @@ add_task(async function() {
     gURLBar.value = keyword;
     EventUtils.synthesizeKey(" ", {});
     await expectEvent("on-input-started-fired");
     EventUtils.synthesizeKey("t", {});
     await expectEvent("on-input-changed-fired", {text: "t"});
     // Wait for the autocomplete search. Note that we cannot wait for the search
     // to be complete, since the add-on doesn't communicate when it's done, so
     // just check matches count.
-    await BrowserTestUtils.waitForCondition(() => gURLBar.controller.matchCount >= 2,
-                                            "waiting urlbar search to complete");
+    await BrowserTestUtils.waitForCondition(
+      () => gURLBar.controller.matchCount >= 2 &&
+            gURLBar.popup.richlistbox.children[1].getAttribute("ac-text") == gURLBar.controller.searchString,
+      "waiting urlbar search to complete");
     return "t";
   }
 
   async function testInputEvents() {
     gURLBar.focus();
 
     // Start an input session by typing in <keyword><space>.
     for (let letter of keyword) {
--- a/browser/components/extensions/test/browser/browser_ext_tabs_readerMode.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_readerMode.js
@@ -81,23 +81,26 @@ add_task(async function test_reader_mode
   let tab = await extension.awaitMessage("isArticle");
 
   ok(!tab.url.startsWith(READER_MODE_PREFIX), "Tab url does not indicate reader mode.");
   ok(tab.isArticle, "Tab is readerable.");
 
   extension.sendMessage("enterReaderMode", true);
   tab = await extension.awaitMessage("tabUpdated");
   ok(tab.url.startsWith(READER_MODE_PREFIX), "Tab url indicates reader mode.");
+  ok(tab.isInReaderMode, "tab.isInReaderMode indicates reader mode.");
 
   extension.sendMessage("leaveReaderMode");
   tab = await extension.awaitMessage("tabUpdated");
   ok(!tab.url.startsWith(READER_MODE_PREFIX), "Tab url does not indicate reader mode.");
+  ok(!tab.isInReaderMode, "tab.isInReaderMode does not indicate reader mode.");
 
   extension.sendMessage("updateUrl", false, `${TEST_PATH}readerModeNonArticle.html`);
   tab = await extension.awaitMessage("tabUpdated");
   ok(!tab.url.startsWith(READER_MODE_PREFIX), "Tab url does not indicate reader mode.");
   ok(!tab.isArticle, "Tab is not readerable.");
+  ok(!tab.isInReaderMode, "tab.isInReaderMode does not indicate reader mode.");
 
   extension.sendMessage("enterReaderMode", false);
   await extension.awaitMessage("enterFailed");
 
   await extension.unload();
 });
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -294,16 +294,18 @@ var gMainPane = {
     setEventListener("layers.acceleration.disabled", "change",
       gMainPane.updateHardwareAcceleration);
     setEventListener("connectionSettings", "command",
       gMainPane.showConnections);
     setEventListener("browserContainersCheckbox", "command",
       gMainPane.checkBrowserContainers);
     setEventListener("browserContainersSettings", "command",
       gMainPane.showContainerSettings);
+    setEventListener("browserHomePage", "input",
+      gMainPane.onBrowserHomePageChange);
 
     // Initializes the fonts dropdowns displayed in this pane.
     this._rebuildFonts();
 
     this.updateOnScreenKeyboardVisibility();
 
     // Show translation preferences if we may:
     const prefName = "browser.translation.ui.show";
@@ -738,30 +740,50 @@ var gMainPane = {
   setHomePageToCurrent() {
     let homePage = document.getElementById("browser.startup.homepage");
     let tabs = this._getTabsForHomePage();
     function getTabURI(t) {
       return t.linkedBrowser.currentURI.spec;
     }
 
     // FIXME Bug 244192: using dangerous "|" joiner!
-    if (tabs.length)
+    if (tabs.length) {
       homePage.value = tabs.map(getTabURI).join("|");
+    }
+
+    Services.telemetry.scalarAdd("preferences.use_current_page", 1);
   },
 
   /**
    * Displays a dialog in which the user can select a bookmark to use as home
    * page.  If the user selects a bookmark, that bookmark's name is displayed in
    * UI and the bookmark's address is stored to the home page preference.
    */
   setHomePageToBookmark() {
     var rv = { urls: null, names: null };
     gSubDialog.open("chrome://browser/content/preferences/selectBookmark.xul",
       "resizable=yes, modal=yes", rv,
       this._setHomePageToBookmarkClosed.bind(this, rv));
+    Services.telemetry.scalarAdd("preferences.use_bookmark", 1);
+  },
+
+  onBrowserHomePageChange() {
+    if (this.telemetryHomePageTimer) {
+      clearTimeout(this.telemetryHomePageTimer);
+    }
+    let browserHomePage = document.querySelector("#browserHomePage").value;
+    // The length of the home page URL string should be more then four,
+    // and it should contain at least one ".", for example, "https://mozilla.org".
+    if (browserHomePage.length > 4 && browserHomePage.includes(".")) {
+      this.telemetryHomePageTimer = setTimeout(() => {
+        let homePageNumber = browserHomePage.split("|").length;
+        Services.telemetry.scalarAdd("preferences.browser_home_page_change", 1);
+        Services.telemetry.keyedScalarAdd("preferences.browser_home_page_count", homePageNumber, 1);
+      }, 3000);
+    }
   },
 
   _setHomePageToBookmarkClosed(rv, aEvent) {
     if (aEvent.detail.button != "accept")
       return;
     if (rv.urls && rv.names) {
       var homePage = document.getElementById("browser.startup.homepage");
 
--- a/browser/config/mozconfigs/win32/mingw32
+++ b/browser/config/mozconfigs/win32/mingw32
@@ -35,19 +35,16 @@ MOZ_AUTOMATION_INSTALLER=0
 
 # MinGW Stuff
 ac_add_options --target=i686-w64-mingw32
 ac_add_options --with-toolchain-prefix=i686-w64-mingw32-
 
 ac_add_options --enable-debug
 ac_add_options --disable-optimize
 
-# Knock this out from above
-ac_add_options --disable-js-shell
-
 # GCC compiling for Windows exposes a lot of warnings. We are tracking them in Bug 1394433
 ac_add_options --disable-warnings-as-errors
 
 # Temporary config settings until we get these working on mingw
 ac_add_options --disable-tests
 ac_add_options --disable-accessibility # https://sourceforge.net/p/mingw-w64/bugs/648/
 
 # Long story
--- a/browser/extensions/activity-stream/data/content/activity-stream.css
+++ b/browser/extensions/activity-stream/data/content/activity-stream.css
@@ -216,24 +216,23 @@ main {
   font-size: 13px;
   font-weight: bold;
   text-transform: uppercase; }
   .section-title span {
     color: #737373;
     fill: #737373;
     vertical-align: middle; }
 
-.body-wrapper {
+.body-wrapper .sections-list .section:last-of-type,
+.body-wrapper .section-title {
   opacity: 0;
-  transition: opacity 75ms ease-in-out; }
+}
 
-@-moz-document url(about:newtab) {
-  .body-wrapper {
-    transition-delay: 125ms; } }
-  .body-wrapper.on {
+.body-wrapper.on .sections-list .section:last-of-type,
+.body-wrapper.on .section-title {
     opacity: 1; }
 
 .top-sites-list {
   list-style: none;
   margin: 0;
   margin-bottom: -18px;
   padding: 0;
   margin-inline-end: -32px; }
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -231,16 +231,70 @@ FormAutofillHandler.prototype = {
             profile[f] = FormAutofillUtils.toOneLineAddress(waitForConcat);
           }
           waitForConcat = [];
         }
       }
     }
   },
 
+  /**
+   * Replace tel with tel-national if tel violates the input element's
+   * restriction.
+   * @param {Object} profile
+   *        A profile to be converted.
+   */
+  _telTransformer(profile) {
+    if (!profile.tel || !profile["tel-national"]) {
+      return;
+    }
+
+    let detail = this.getFieldDetailByName("tel");
+    if (!detail) {
+      return;
+    }
+
+    let element = detail.elementWeakRef.get();
+    let _pattern;
+    let testPattern = str => {
+      if (!_pattern) {
+        // The pattern has to match the entire value.
+        _pattern = new RegExp("^(?:" + element.pattern + ")$", "u");
+      }
+      return _pattern.test(str);
+    };
+    if (element.pattern) {
+      if (testPattern(profile.tel)) {
+        return;
+      }
+    } else if (element.maxLength) {
+      if (profile.tel.length <= element.maxLength) {
+        return;
+      }
+    }
+
+    if (detail._reason != "autocomplete") {
+      // Since we only target people living in US and using en-US websites in
+      // MVP, it makes more sense to fill `tel-national` instead of `tel`
+      // if the field is identified by heuristics and no other clues to
+      // determine which one is better.
+      // TODO: [Bug 1407545] This should be improved once more countries are
+      // supported.
+      profile.tel = profile["tel-national"];
+    } else if (element.pattern) {
+      if (testPattern(profile["tel-national"])) {
+        profile.tel = profile["tel-national"];
+      }
+    } else if (element.maxLength) {
+      if (profile["tel-national"].length <= element.maxLength) {
+        profile.tel = profile["tel-national"];
+      }
+    }
+  },
+
   _matchSelectOptions(profile) {
     if (!this._cacheValue.matchingSelectOption) {
       this._cacheValue.matchingSelectOption = new WeakMap();
     }
 
     for (let fieldName in profile) {
       let fieldDetail = this.getFieldDetailByName(fieldName);
       if (!fieldDetail) {
@@ -272,16 +326,17 @@ FormAutofillHandler.prototype = {
         delete profile[fieldName];
       }
     }
   },
 
   getAdaptedProfiles(originalProfiles) {
     for (let profile of originalProfiles) {
       this._addressTransformer(profile);
+      this._telTransformer(profile);
       this._matchSelectOptions(profile);
     }
     return originalProfiles;
   },
 
   /**
    * Processes form fields that can be autofilled, and populates them with the
    * profile provided by backend.
--- a/browser/extensions/formautofill/content/formfill-anchor.svg
+++ b/browser/extensions/formautofill/content/formfill-anchor.svg
@@ -1,9 +1,8 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
-  <path d="M7.28 5h1.47A.25.25 0 0 0 9 4.75V.984a.984.984 0 0 0-1.97 0V4.75a.25.25 0 0 0 .25.25z"/>
-  <path d="M13.5 2H11a1 1 0 0 0 0 2h2.5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-7a.5.5 0 0 1 .5-.5H5a1 1 0 0 0 0-2H2.5A2.5 2.5 0 0 0 0 4.5v7A2.5 2.5 0 0 0 2.5 14h11a2.5 2.5 0 0 0 2.5-2.5v-7A2.5 2.5 0 0 0 13.5 2z"/>
-  <rect x="3" y="6" width="4" height="4" rx=".577" ry=".577"/>
-  <path d="M9.5 7h3a.5.5 0 0 0 0-1h-3a.5.5 0 0 0 0 1zM9.5 8a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1z"/>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M7.3 6h1.5c.1 0 .2-.1.2-.3V2c0-.5-.4-1-1-1s-1 .4-1 1v3.8c0 .1.1.2.3.2z"/>
+  <path d="M13.5 3H11c-.6 0-1 .4-1 1s.4 1 1 1h2.5c.3 0 .5.2.5.5v7c0 .3-.2.5-.5.5h-11c-.3 0-.5-.3-.5-.5v-7c0-.3.2-.5.5-.5H5c.6 0 1-.4 1-1s-.4-1-1-1H2.5C1.1 3 0 4.1 0 5.5v7C0 13.8 1.1 15 2.5 15h11c1.4 0 2.5-1.1 2.5-2.5v-7C16 4.1 14.9 3 13.5 3z"/>
+  <path d="M3.6 7h2.8c.3 0 .6.2.6.5v2.8c0 .4-.3.7-.6.7H3.6c-.3 0-.6-.3-.6-.6V7.5c0-.3.3-.5.6-.5zM9.5 8h3c.3 0 .5-.3.5-.5s-.2-.5-.5-.5h-3c-.3 0-.5.2-.5.5s.2.5.5.5zM9.5 9c-.3 0-.5.2-.5.5s.2.5.5.5h2c.3 0 .5-.2.5-.5s-.2-.5-.5-.5h-2z"/>
 </svg>
--- a/browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js
+++ b/browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js
@@ -9,16 +9,18 @@ Cu.import("resource://formautofill/FormA
 const DEFAULT_ADDRESS_RECORD = {
   "guid": "123",
   "street-address": "2 Harrison St\nline2\nline3",
   "address-line1": "2 Harrison St",
   "address-line2": "line2",
   "address-line3": "line3",
   "address-level1": "CA",
   "country": "US",
+  "tel": "+19876543210",
+  "tel-national": "9876543210",
 };
 
 const DEFAULT_CREDITCARD_RECORD = {
   "guid": "123",
   "cc-exp-month": 1,
   "cc-exp-year": 2025,
   "cc-exp": "2025-01",
 };
@@ -34,16 +36,18 @@ const TESTCASES = [
       "guid": "123",
       "street-address": "2 Harrison St line2 line3",
       "-moz-street-address-one-line": "2 Harrison St line2 line3",
       "address-line1": "2 Harrison St",
       "address-line2": "line2",
       "address-line3": "line3",
       "address-level1": "CA",
       "country": "US",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
     }],
   },
   {
     description: "Address form with street-address, address-line[1, 2, 3]",
     document: `<form>
                <input id="street-addr" autocomplete="street-address">
                <input id="line1" autocomplete="address-line1">
                <input id="line2" autocomplete="address-line2">
@@ -54,16 +58,18 @@ const TESTCASES = [
       "guid": "123",
       "street-address": "2 Harrison St line2 line3",
       "-moz-street-address-one-line": "2 Harrison St line2 line3",
       "address-line1": "2 Harrison St",
       "address-line2": "line2",
       "address-line3": "line3",
       "address-level1": "CA",
       "country": "US",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
     }],
   },
   {
     description: "Address form with street-address, address-line1",
     document: `<form>
                <input id="street-addr" autocomplete="street-address">
                <input id="line1" autocomplete="address-line1">
                </form>`,
@@ -72,16 +78,18 @@ const TESTCASES = [
       "guid": "123",
       "street-address": "2 Harrison St line2 line3",
       "-moz-street-address-one-line": "2 Harrison St line2 line3",
       "address-line1": "2 Harrison St line2 line3",
       "address-line2": "line2",
       "address-line3": "line3",
       "address-level1": "CA",
       "country": "US",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
     }],
   },
   {
     description: "Address form with street-address, address-line[1, 2]",
     document: `<form>
                <input id="street-addr" autocomplete="street-address">
                <input id="line1" autocomplete="address-line1">
                <input id="line2" autocomplete="address-line2">
@@ -91,16 +99,18 @@ const TESTCASES = [
       "guid": "123",
       "street-address": "2 Harrison St line2 line3",
       "-moz-street-address-one-line": "2 Harrison St line2 line3",
       "address-line1": "2 Harrison St",
       "address-line2": "line2 line3",
       "address-line3": "line3",
       "address-level1": "CA",
       "country": "US",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
     }],
   },
   {
     description: "Address form with street-address, address-line[1, 3]",
     document: `<form>
                <input id="street-addr" autocomplete="street-address">
                <input id="line1" autocomplete="address-line1">
                <input id="line3" autocomplete="address-line3">
@@ -110,16 +120,18 @@ const TESTCASES = [
       "guid": "123",
       "street-address": "2 Harrison St line2 line3",
       "-moz-street-address-one-line": "2 Harrison St line2 line3",
       "address-line1": "2 Harrison St",
       "address-line2": "line2 line3",
       "address-line3": "line3",
       "address-level1": "CA",
       "country": "US",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
     }],
   },
   {
     description: "Address form with exact matching options in select",
     document: `<form>
                <select autocomplete="address-level1">
                  <option id="option-address-level1-XX" value="XX">Dummy</option>
                  <option id="option-address-level1-CA" value="CA">California</option>
@@ -134,16 +146,18 @@ const TESTCASES = [
       "guid": "123",
       "street-address": "2 Harrison St\nline2\nline3",
       "-moz-street-address-one-line": "2 Harrison St line2 line3",
       "address-line1": "2 Harrison St",
       "address-line2": "line2",
       "address-line3": "line3",
       "address-level1": "CA",
       "country": "US",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
     }],
     expectedOptionElements: [{
       "address-level1": "option-address-level1-CA",
       "country": "option-country-US",
     }],
   },
   {
     description: "Address form with inexact matching options in select",
@@ -162,16 +176,18 @@ const TESTCASES = [
       "guid": "123",
       "street-address": "2 Harrison St\nline2\nline3",
       "-moz-street-address-one-line": "2 Harrison St line2 line3",
       "address-line1": "2 Harrison St",
       "address-line2": "line2",
       "address-line3": "line3",
       "address-level1": "CA",
       "country": "US",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
     }],
     expectedOptionElements: [{
       "address-level1": "option-address-level1-OO",
       "country": "option-country-OO",
     }],
   },
   {
     description: "Address form with value-omitted options in select",
@@ -190,16 +206,18 @@ const TESTCASES = [
       "guid": "123",
       "street-address": "2 Harrison St\nline2\nline3",
       "-moz-street-address-one-line": "2 Harrison St line2 line3",
       "address-line1": "2 Harrison St",
       "address-line2": "line2",
       "address-line3": "line3",
       "address-level1": "CA",
       "country": "US",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
     }],
     expectedOptionElements: [{
       "address-level1": "option-address-level1-2",
       "country": "option-country-2",
     }],
   },
   {
     description: "Address form with options with the same value in select ",
@@ -218,16 +236,18 @@ const TESTCASES = [
       "guid": "123",
       "street-address": "2 Harrison St\nline2\nline3",
       "-moz-street-address-one-line": "2 Harrison St line2 line3",
       "address-line1": "2 Harrison St",
       "address-line2": "line2",
       "address-line3": "line3",
       "address-level1": "CA",
       "country": "US",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
     }],
     expectedOptionElements: [{
       "address-level1": "option-address-level1-same2",
       "country": "option-country-same2",
     }],
   },
   {
     description: "Address form without matching options in select for address-level1 and country",
@@ -244,16 +264,186 @@ const TESTCASES = [
     profileData: [Object.assign({}, DEFAULT_ADDRESS_RECORD)],
     expectedResult: [{
       "guid": "123",
       "street-address": "2 Harrison St\nline2\nline3",
       "-moz-street-address-one-line": "2 Harrison St line2 line3",
       "address-line1": "2 Harrison St",
       "address-line2": "line2",
       "address-line3": "line3",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
+    }],
+  },
+  {
+    description: "Change the tel value of a profile to tel-national for a field without pattern and maxlength.",
+    document: `<form>
+               <input id="telephone">
+               <input id="line1" autocomplete="address-line1">
+               <input id="line2" autocomplete="address-line2">
+               </form>`,
+    profileData: [Object.assign({}, DEFAULT_ADDRESS_RECORD)],
+    expectedResult: [{
+      "guid": "123",
+      "street-address": "2 Harrison St\nline2\nline3",
+      "-moz-street-address-one-line": "2 Harrison St line2 line3",
+      "address-line1": "2 Harrison St",
+      "address-line2": "line2 line3",
+      "address-line3": "line3",
+      "address-level1": "CA",
+      "country": "US",
+      "tel": "9876543210",
+      "tel-national": "9876543210",
+    }],
+  },
+  {
+    description: "Do not change the profile for an autocomplete=\"tel\" field without patern and maxlength.",
+    document: `<form>
+               <input id="tel" autocomplete="tel">
+               <input id="line1" autocomplete="address-line1">
+               <input id="line2" autocomplete="address-line2">
+               </form>`,
+    profileData: [Object.assign({}, DEFAULT_ADDRESS_RECORD)],
+    expectedResult: [{
+      "guid": "123",
+      "street-address": "2 Harrison St\nline2\nline3",
+      "-moz-street-address-one-line": "2 Harrison St line2 line3",
+      "address-line1": "2 Harrison St",
+      "address-line2": "line2 line3",
+      "address-line3": "line3",
+      "address-level1": "CA",
+      "country": "US",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
+    }],
+  },
+  {
+    description: "`tel` field with `maxlength` can be filled with `tel` value.",
+    document: `<form>
+               <input id="telephone" maxlength="12">
+               <input id="line1" autocomplete="address-line1">
+               <input id="line2" autocomplete="address-line2">
+               </form>`,
+    profileData: [Object.assign({}, DEFAULT_ADDRESS_RECORD)],
+    expectedResult: [{
+      "guid": "123",
+      "street-address": "2 Harrison St\nline2\nline3",
+      "-moz-street-address-one-line": "2 Harrison St line2 line3",
+      "address-line1": "2 Harrison St",
+      "address-line2": "line2 line3",
+      "address-line3": "line3",
+      "address-level1": "CA",
+      "country": "US",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
+    }],
+  },
+  {
+    description: "`tel` field with `maxlength` can be filled with `tel-national` value.",
+    document: `<form>
+               <input id="telephone" maxlength="10">
+               <input id="line1" autocomplete="address-line1">
+               <input id="line2" autocomplete="address-line2">
+               </form>`,
+    profileData: [Object.assign({}, DEFAULT_ADDRESS_RECORD)],
+    expectedResult: [{
+      "guid": "123",
+      "street-address": "2 Harrison St\nline2\nline3",
+      "-moz-street-address-one-line": "2 Harrison St line2 line3",
+      "address-line1": "2 Harrison St",
+      "address-line2": "line2 line3",
+      "address-line3": "line3",
+      "address-level1": "CA",
+      "country": "US",
+      "tel": "9876543210",
+      "tel-national": "9876543210",
+    }],
+  },
+  {
+    description: "`tel` field with `pattern` attr can be filled with `tel` value.",
+    document: `<form>
+               <input id="telephone" pattern="[+][0-9]+">
+               <input id="line1" autocomplete="address-line1">
+               <input id="line2" autocomplete="address-line2">
+               </form>`,
+    profileData: [Object.assign({}, DEFAULT_ADDRESS_RECORD)],
+    expectedResult: [{
+      "guid": "123",
+      "street-address": "2 Harrison St\nline2\nline3",
+      "-moz-street-address-one-line": "2 Harrison St line2 line3",
+      "address-line1": "2 Harrison St",
+      "address-line2": "line2 line3",
+      "address-line3": "line3",
+      "address-level1": "CA",
+      "country": "US",
+      "tel": "+19876543210",
+      "tel-national": "9876543210",
+    }],
+  },
+  {
+    description: "Change the tel value of a profile to tel-national one when the pattern is matched.",
+    document: `<form>
+               <input id="telephone" pattern="\d*">
+               <input id="line1" autocomplete="address-line1">
+               <input id="line2" autocomplete="address-line2">
+               </form>`,
+    profileData: [Object.assign({}, DEFAULT_ADDRESS_RECORD)],
+    expectedResult: [{
+      "guid": "123",
+      "street-address": "2 Harrison St\nline2\nline3",
+      "-moz-street-address-one-line": "2 Harrison St line2 line3",
+      "address-line1": "2 Harrison St",
+      "address-line2": "line2 line3",
+      "address-line3": "line3",
+      "address-level1": "CA",
+      "country": "US",
+      "tel": "9876543210",
+      "tel-national": "9876543210",
+    }],
+  },
+  {
+    description: "Matching pattern when a field is with autocomplete=\"tel\".",
+    document: `<form>
+               <input id="tel" autocomplete="tel" pattern="[0-9]+">
+               <input id="line1" autocomplete="address-line1">
+               <input id="line2" autocomplete="address-line2">
+               </form>`,
+    profileData: [Object.assign({}, DEFAULT_ADDRESS_RECORD)],
+    expectedResult: [{
+      "guid": "123",
+      "street-address": "2 Harrison St\nline2\nline3",
+      "-moz-street-address-one-line": "2 Harrison St line2 line3",
+      "address-line1": "2 Harrison St",
+      "address-line2": "line2 line3",
+      "address-line3": "line3",
+      "address-level1": "CA",
+      "country": "US",
+      "tel": "9876543210",
+      "tel-national": "9876543210",
+    }],
+  },
+  {
+    description: "Checking maxlength first when a field is with maxlength.",
+    document: `<form>
+               <input id="tel" autocomplete="tel" maxlength="10">
+               <input id="line1" autocomplete="address-line1">
+               <input id="line2" autocomplete="address-line2">
+               </form>`,
+    profileData: [Object.assign({}, DEFAULT_ADDRESS_RECORD)],
+    expectedResult: [{
+      "guid": "123",
+      "street-address": "2 Harrison St\nline2\nline3",
+      "-moz-street-address-one-line": "2 Harrison St line2 line3",
+      "address-line1": "2 Harrison St",
+      "address-line2": "line2 line3",
+      "address-line3": "line3",
+      "address-level1": "CA",
+      "country": "US",
+      "tel": "9876543210",
+      "tel-national": "9876543210",
     }],
   },
   {
     description: "Credit Card form with matching options of cc-exp-year and cc-exp-month",
     document: `<form>
                <select autocomplete="cc-exp-month">
                  <option id="option-cc-exp-month-01" value="1">01</option>
                  <option id="option-cc-exp-month-02" value="2">02</option>
--- a/browser/extensions/screenshots/install.rdf
+++ b/browser/extensions/screenshots/install.rdf
@@ -7,14 +7,14 @@
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!--Firefox-->
         <em:minVersion>57.0a1</em:minVersion>
         <em:maxVersion>*</em:maxVersion>
       </Description>
     </em:targetApplication>
     <em:type>2</em:type>
-    <em:version>19.1.0</em:version>
+    <em:version>19.2.0</em:version>
     <em:bootstrap>true</em:bootstrap>
     <em:homepageURL>https://screenshots.firefox.com/</em:homepageURL>
     <em:multiprocessCompatible>true</em:multiprocessCompatible>
   </Description>
 </RDF>
--- a/browser/extensions/screenshots/webextension/manifest.json
+++ b/browser/extensions/screenshots/webextension/manifest.json
@@ -1,12 +1,12 @@
 {
   "manifest_version": 2,
   "name": "Firefox Screenshots",
-  "version": "19.1.0",
+  "version": "19.2.0",
   "description": "__MSG_addonDescription__",
   "author": "__MSG_addonAuthorsList__",
   "homepage_url": "https://github.com/mozilla-services/screenshots",
   "applications": {
     "gecko": {
       "id": "screenshots@mozilla.org",
       "strict_min_version": "57.0a1"
     }
--- a/browser/themes/shared/aboutTabCrashed.css
+++ b/browser/themes/shared/aboutTabCrashed.css
@@ -5,32 +5,23 @@
 body {
   font-size: 1.25rem;
 }
 
 .title {
   background-image: url("chrome://browser/skin/tab-crashed.svg");
 }
 
-.title > h1,
-.offers {
-  margin-left: 14px;
-}
-
 .title > h1 {
   /**
    * Add commentary?
    */
   padding-right: 14px;
 }
 
-.container {
-  width: 45%;
-}
-
 #reportSent {
   font-weight: bold;
 }
 
 #reportBox {
   background-color: var(--in-content-box-background-hover);
   margin: 24px 0;
   padding: 14px;
@@ -71,9 +62,9 @@ input[type="checkbox"] {
 
 .checkbox-with-label {
   display: flex;
 }
 
 .checkbox-with-label > label {
   margin-top: auto;
   margin-bottom: auto;
-}
\ No newline at end of file
+}
--- a/browser/themes/shared/customizableui/customizeMode.inc.css
+++ b/browser/themes/shared/customizableui/customizeMode.inc.css
@@ -198,28 +198,26 @@ toolbarpaletteitem[notransition].panel-c
 toolbarpaletteitem[notransition][place="toolbar"],
 toolbarpaletteitem[notransition][place="palette"],
 toolbarpaletteitem[notransition][place="panel"] {
   transition: none;
 }
 
 toolbarpaletteitem > toolbarbutton > .toolbarbutton-icon,
 toolbarpaletteitem > toolbarbutton > .toolbarbutton-badge-stack > .toolbarbutton-icon,
-toolbarpaletteitem > toolbaritem.panel-wide-item,
-toolbarpaletteitem > toolbarbutton[type="menu-button"] {
+toolbarpaletteitem > toolbaritem.panel-wide-item {
   transition: transform var(--drag-drop-transition-duration) cubic-bezier(.6, 2, .75, 1.5) !important;
 }
 
 toolbarpaletteitem[mousedown] > toolbarbutton > .toolbarbutton-icon,
 toolbarpaletteitem[mousedown] > toolbarbutton > .toolbarbutton-badge-stack > .toolbarbutton-icon {
   transform: scale(1.3);
 }
 
-toolbarpaletteitem[mousedown] > toolbaritem.panel-wide-item,
-toolbarpaletteitem[mousedown] > toolbarbutton[type="menu-button"] {
+toolbarpaletteitem[mousedown] > toolbaritem.panel-wide-item {
   transform: scale(1.1);
 }
 
 /* Override the toolkit styling for items being dragged over. */
 toolbarpaletteitem[place="toolbar"] {
   border-left-width: 0;
   border-right-width: 0;
   margin-right: 0;
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -77,17 +77,17 @@
 #identity-icon,
 #tracking-protection-icon,
 #connection-icon,
 .notification-anchor-icon,
 #blocked-permissions-container > .blocked-permission-icon,
 #extension-icon {
   width: 16px;
   height: 16px;
-  margin-inline-start: 2px;
+  margin-inline-start: 4px;
   -moz-context-properties: fill, fill-opacity;
 }
 
 /* MAIN IDENTITY ICON */
 #identity-icon {
   margin-inline-start: 0;
   list-style-image: url(chrome://browser/skin/identity-icon.svg);
 }
@@ -164,17 +164,17 @@
   list-style-image: url(chrome://browser/skin/tracking-protection-16.svg#disabled);
 }
 
 #tracking-protection-icon[animate] {
   transition: margin-left 200ms ease-out, margin-right 200ms ease-out;
 }
 
 #tracking-protection-icon:not([state]) {
-  margin-inline-end: -18px;
+  margin-inline-end: -20px;
   pointer-events: none;
   opacity: 0;
   /* Only animate the shield in, when it disappears hide it immediately. */
   transition: none;
 }
 
 #urlbar[pageproxystate="invalid"] > #identity-box > #extension-icon,
 #urlbar[pageproxystate="invalid"] > #identity-box > #tracking-protection-icon {
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -1197,29 +1197,27 @@ def developer_options(value):
 
 add_old_configure_assignment('DEVELOPER_OPTIONS', developer_options)
 set_config('DEVELOPER_OPTIONS', developer_options)
 
 # Linker detection
 # ==============================================================
 
 @depends(target)
-def build_not_win_mac(target):
+def is_linker_option_enabled(target):
     if target.kernel not in ('Darwin', 'WINNT', 'SunOS'):
         return True
 
 
 option('--enable-gold',
        env='MOZ_FORCE_GOLD',
        help='Enable GNU Gold Linker when it is not already the default',
-       when=build_not_win_mac)
+       when=is_linker_option_enabled)
 
-imply_option('--enable-linker',
-             depends_if('--enable-gold', when=build_not_win_mac)(lambda x: 'gold'),
-             when=build_not_win_mac)
+imply_option('--enable-linker', 'gold', when='--enable-gold')
 
 @imports('os')
 @imports('shutil')
 def enable_gnu_linker(enable_gold_option, c_compiler, developer_options, build_env,
                       toolchain_flags, linker_name):
     # Used to check the kind of linker
     version_check = ['-Wl,--version']
     cmd_base = c_compiler.wrapper + [c_compiler.compiler] + c_compiler.flags
@@ -1285,20 +1283,20 @@ def enable_gnu_linker(enable_gold_option
     # For other platforms without gold or the GNU linker
     return namespace(
         KIND='other'
     )
 
 js_option('--enable-linker', nargs=1,
           choices=('bfd', 'gold', 'lld', 'other'),
           help='Select the linker',
-          when=build_not_win_mac)
+          when=is_linker_option_enabled)
 
 @depends('--enable-linker', c_compiler, developer_options, check_build_environment,
-         extra_toolchain_flags, when=build_not_win_mac)
+         extra_toolchain_flags, when=is_linker_option_enabled)
 @checking('for linker', lambda x: x.KIND)
 def select_linker(linker, c_compiler, developer_options, build_env, toolchain_flags):
     linker = linker[0] if linker else 'other'
     if linker in ('gold', 'bfd', 'other'):
         return enable_gnu_linker(linker == 'gold', c_compiler, developer_options,
                                  build_env, toolchain_flags, linker)
     if linker == 'lld':
         version_check = ['-Wl,--version']
--- a/build/moz.configure/warnings.configure
+++ b/build/moz.configure/warnings.configure
@@ -60,16 +60,19 @@ check_and_add_gcc_warning('-Wloop-analys
 check_and_add_gcc_warning('-Wc++11-compat-pedantic', cxx_compiler)
 check_and_add_gcc_warning('-Wc++14-compat', cxx_compiler)
 check_and_add_gcc_warning('-Wc++14-compat-pedantic', cxx_compiler)
 check_and_add_gcc_warning('-Wc++1z-compat', cxx_compiler)
 
 # catches possible misuse of the comma operator
 check_and_add_gcc_warning('-Wcomma', cxx_compiler)
 
+# catches duplicated conditions in if-else-if chains
+check_and_add_gcc_warning('-Wduplicated-cond')
+
 # catches unintentional switch case fallthroughs
 check_and_add_gcc_warning('-Wimplicit-fallthrough', cxx_compiler)
 
 # catches expressions used as a null pointer constant
 # XXX: at the time of writing, the version of clang used on the OS X test
 # machines has a bug that causes it to reject some valid files if both
 # -Wnon-literal-null-conversion and -Wsometimes-uninitialized are
 # specified. We work around this by instead using
--- a/config/system-headers
+++ b/config/system-headers
@@ -489,16 +489,17 @@ gdk/gdkx.h
 gdk/gdkdirectfb.h
 gdk/gdkwayland.h
 gdk-pixbuf/gdk-pixbuf.h
 Gestalt.h
 getopt.h
 glibconfig.h
 glib.h
 glib-object.h
+glob.h
 gmodule.h
 gnome.h
 gnu/libc-version.h
 gps.h
 grp.h
 gssapi_generic.h
 gssapi/gssapi_generic.h
 gssapi/gssapi.h
--- a/devtools/client/netmonitor/src/components/request-list-column-waterfall.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-waterfall.js
@@ -34,41 +34,72 @@ const RequestListColumnWaterfall = creat
 
   shouldComponentUpdate(nextProps) {
     return !propertiesEqual(UPDATED_WATERFALL_PROPS, this.props.item, nextProps.item) ||
       this.props.firstRequestStartedMillis !== nextProps.firstRequestStartedMillis;
   },
 
   render() {
     let { firstRequestStartedMillis, item, onWaterfallMouseDown } = this.props;
-    const { boxes, tooltip } = timingBoxes(item);
+    const boxes = timingBoxes(item);
 
     return (
-      div({ className: "requests-list-column requests-list-waterfall", title: tooltip },
+      div({
+        className: "requests-list-column requests-list-waterfall",
+        onMouseOver: function ({target}) {
+          if (!target.title) {
+            target.title = timingTooltip(item);
+          }
+        }
+      },
         div({
           className: "requests-list-timings",
           style: {
             paddingInlineStart: `${item.startedMillis - firstRequestStartedMillis}px`,
           },
           onMouseDown: onWaterfallMouseDown,
         },
           boxes,
         )
       )
     );
   }
 });
 
+function timingTooltip(item) {
+  let { eventTimings, fromCache, fromServiceWorker, totalTime } = item;
+  let tooltip = [];
+
+  if (fromCache || fromServiceWorker) {
+    return tooltip;
+  }
+
+  if (eventTimings) {
+    for (let key of TIMING_KEYS) {
+      let width = eventTimings.timings[key];
+
+      if (width > 0) {
+        tooltip.push(L10N.getFormatStr("netmonitor.waterfall.tooltip." + key, width));
+      }
+    }
+  }
+
+  if (typeof totalTime === "number") {
+    tooltip.push(L10N.getFormatStr("netmonitor.waterfall.tooltip.total", totalTime));
+  }
+
+  return tooltip.join(L10N.getStr("netmonitor.waterfall.tooltip.separator"));
+}
+
 function timingBoxes(item) {
   let { eventTimings, fromCache, fromServiceWorker, totalTime } = item;
   let boxes = [];
-  let tooltip = [];
 
   if (fromCache || fromServiceWorker) {
-    return { boxes, tooltip };
+    return boxes;
   }
 
   if (eventTimings) {
     // Add a set of boxes representing timing information.
     for (let key of TIMING_KEYS) {
       let width = eventTimings.timings[key];
 
       // Don't render anything if it surely won't be visible.
@@ -76,32 +107,27 @@ function timingBoxes(item) {
       if (width > 0) {
         boxes.push(
           div({
             key,
             className: `requests-list-timings-box ${key}`,
             style: { width },
           })
         );
-        tooltip.push(L10N.getFormatStr("netmonitor.waterfall.tooltip." + key, width));
       }
     }
   }
 
   if (typeof totalTime === "number") {
     let title = L10N.getFormatStr("networkMenu.totalMS", totalTime);
     boxes.push(
       div({
         key: "total",
         className: "requests-list-timings-total",
         title,
       }, title)
     );
-    tooltip.push(L10N.getFormatStr("netmonitor.waterfall.tooltip.total", totalTime));
   }
 
-  return {
-    boxes,
-    tooltip: tooltip.join(L10N.getStr("netmonitor.waterfall.tooltip.separator"))
-  };
+  return boxes;
 }
 
 module.exports = RequestListColumnWaterfall;
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -3791,16 +3791,28 @@ nsFrameLoader::RequestFrameLoaderClose()
     // OwnerElement other than nsIBrowser is not supported yet.
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   return browser->CloseBrowser();
 }
 
 void
+nsFrameLoader::RequestUpdatePosition(ErrorResult& aRv)
+{
+  if (auto* tabParent = TabParent::GetFrom(GetRemoteBrowser())) {
+    nsresult rv = tabParent->UpdatePosition();
+
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+    }
+  }
+}
+
+void
 nsFrameLoader::Print(uint64_t aOuterWindowID,
                      nsIPrintSettings* aPrintSettings,
                      nsIWebProgressListener* aProgressListener,
                      ErrorResult& aRv)
 {
   nsresult rv = Print(aOuterWindowID, aPrintSettings, aProgressListener);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -160,16 +160,18 @@ public:
   void ActivateFrameEvent(const nsAString& aType,
                           bool aCapture,
                           mozilla::ErrorResult& aRv);
 
   void RequestNotifyAfterRemotePaint(mozilla::ErrorResult& aRv);
 
   void RequestFrameLoaderClose(mozilla::ErrorResult& aRv);
 
+  void RequestUpdatePosition(mozilla::ErrorResult& aRv);
+
   void Print(uint64_t aOuterWindowID,
              nsIPrintSettings* aPrintSettings,
              nsIWebProgressListener* aProgressListener,
              mozilla::ErrorResult& aRv);
 
   already_AddRefed<nsIGroupedSHistory> EnsureGroupedSHistory(mozilla::ErrorResult& aRv);
 
   void StartPersistence(uint64_t aOuterWindowID,
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2251,17 +2251,16 @@ GK_ATOM(RemoteType, "remoteType")
 GK_ATOM(DisplayPort, "_displayport")
 GK_ATOM(DisplayPortMargins, "_displayportmargins")
 GK_ATOM(DisplayPortBase, "_displayportbase")
 GK_ATOM(AsyncScrollLayerCreationFailed, "_asyncscrolllayercreationfailed")
 GK_ATOM(forcemessagemanager, "forcemessagemanager")
 GK_ATOM(isPreloadBrowser, "isPreloadBrowser")
 
 // Names for system metrics
-GK_ATOM(color_picker_available, "color-picker-available")
 GK_ATOM(scrollbar_start_backward, "scrollbar-start-backward")
 GK_ATOM(scrollbar_start_forward, "scrollbar-start-forward")
 GK_ATOM(scrollbar_end_backward, "scrollbar-end-backward")
 GK_ATOM(scrollbar_end_forward, "scrollbar-end-forward")
 GK_ATOM(scrollbar_thumb_proportional, "scrollbar-thumb-proportional")
 GK_ATOM(overlay_scrollbars, "overlay-scrollbars")
 GK_ATOM(windows_accent_color_in_titlebar, "windows-accent-color-in-titlebar")
 GK_ATOM(windows_default_theme, "windows-default-theme")
@@ -2281,17 +2280,16 @@ GK_ATOM(windows_theme_aero_lite, "window
 GK_ATOM(windows_theme_luna_blue, "windows-theme-luna-blue")
 GK_ATOM(windows_theme_luna_olive, "windows-theme-luna-olive")
 GK_ATOM(windows_theme_luna_silver, "windows-theme-luna-silver")
 GK_ATOM(windows_theme_royale, "windows-theme-royale")
 GK_ATOM(windows_theme_zune, "windows-theme-zune")
 GK_ATOM(windows_theme_generic, "windows-theme-generic")
 
 // And the same again, as media query keywords.
-GK_ATOM(_moz_color_picker_available, "-moz-color-picker-available")
 GK_ATOM(_moz_scrollbar_start_backward, "-moz-scrollbar-start-backward")
 GK_ATOM(_moz_scrollbar_start_forward, "-moz-scrollbar-start-forward")
 GK_ATOM(_moz_scrollbar_end_backward, "-moz-scrollbar-end-backward")
 GK_ATOM(_moz_scrollbar_end_forward, "-moz-scrollbar-end-forward")
 GK_ATOM(_moz_scrollbar_thumb_proportional, "-moz-scrollbar-thumb-proportional")
 GK_ATOM(_moz_overlay_scrollbars, "-moz-overlay-scrollbars")
 GK_ATOM(_moz_windows_accent_color_in_titlebar, "-moz-windows-accent-color-in-titlebar")
 GK_ATOM(_moz_windows_default_theme, "-moz-windows-default-theme")
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3981,17 +3981,16 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mLastCurrentTime(0.0),
     mFragmentStart(-1.0),
     mFragmentEnd(-1.0),
     mDefaultPlaybackRate(1.0),
     mPlaybackRate(1.0),
     mPreservesPitch(true),
     mPlayed(new TimeRanges(ToSupports(OwnerDoc()))),
     mCurrentPlayRangeStart(-1.0),
-    mBegun(false),
     mLoadedDataFired(false),
     mAutoplaying(true),
     mAutoplayEnabled(true),
     mPaused(true, *this),
     mStatsShowing(false),
     mAllowCasting(false),
     mIsCasting(false),
     mAudioCaptured(false),
@@ -5670,26 +5669,23 @@ HTMLMediaElement::NotifySuspendedByCache
   mDownloadSuspendedByCache = aSuspendedByCache;
 }
 
 void HTMLMediaElement::DownloadSuspended()
 {
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
     DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
   }
-  if (mBegun) {
-    ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
-  }
-}
-
-void HTMLMediaElement::DownloadResumed(bool aForceNetworkLoading)
-{
-  if (mBegun || aForceNetworkLoading) {
-    ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
-  }
+  ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
+}
+
+void
+HTMLMediaElement::DownloadResumed()
+{
+  ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_LOADING);
 }
 
 void HTMLMediaElement::CheckProgress(bool aHaveNewProgress)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING);
 
   TimeStamp now = TimeStamp::NowLoRes();
@@ -6076,29 +6072,22 @@ void HTMLMediaElement::ChangeNetworkStat
   if (mNetworkState == aState) {
     return;
   }
 
   nsMediaNetworkState oldState = mNetworkState;
   mNetworkState = aState;
   LOG(LogLevel::Debug, ("%p Network state changed to %s", this, gNetworkStateToString[aState]));
 
-  // TODO: |mBegun| reflects the download status. We should be able to remove
-  // it and check |mNetworkState| only.
-
   if (oldState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
-    // Reset |mBegun| since we're not downloading anymore.
-    mBegun = false;
     // Stop progress notification when exiting NETWORK_LOADING.
     StopProgress();
   }
 
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
-    // Download is begun.
-    mBegun = true;
     // Start progress notification when entering NETWORK_LOADING.
     StartProgress();
   } else if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE &&
              !mErrorSink->mError) {
     // Fire 'suspend' event when entering NETWORK_IDLE and no error presented.
     DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
   }
 
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -206,21 +206,17 @@ public:
   // Called by the media stream, on the main thread, when the download
   // has been suspended by the cache or because the element itself
   // asked the decoder to suspend the download.
   virtual void DownloadSuspended() final override;
 
   // Called by the media stream, on the main thread, when the download
   // has been resumed by the cache or because the element itself
   // asked the decoder to resumed the download.
-  // If aForceNetworkLoading is True, ignore the fact that the download has
-  // previously finished. We are downloading the middle of the media after
-  // having downloaded the end, we need to notify the element a download in
-  // ongoing.
-  virtual void DownloadResumed(bool aForceNetworkLoading = false) final override;
+  void DownloadResumed();
 
   // Called to indicate the download is progressing.
   virtual void DownloadProgressed() final override;
 
   // Called by the media decoder to indicate whether the media cache has
   // suspended the channel.
   virtual void NotifySuspendedByCache(bool aSuspendedByCache) final override;
 
@@ -1545,20 +1541,16 @@ protected:
   nsCOMPtr<nsITimer> mVideoDecodeSuspendTimer;
 
   // Encrypted Media Extension media keys.
   RefPtr<MediaKeys> mMediaKeys;
 
   // Stores the time at the start of the current 'played' range.
   double mCurrentPlayRangeStart;
 
-  // If true then we have begun downloading the media content.
-  // Set to false when completed, or not yet started.
-  bool mBegun;
-
   // True if loadeddata has been fired.
   bool mLoadedDataFired;
 
   // Indicates whether current playback is a result of user action
   // (ie. calling of the Play method), or automatic playback due to
   // the 'autoplay' attribute being set. A true value indicates the
   // latter case.
   // The 'autoplay' HTML attribute indicates that the video should
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -263,17 +263,16 @@
 #include "mozilla/CodeCoverageHandler.h"
 #endif
 
 // For VP9Benchmark::sBenchmarkFpsPref
 #include "Benchmark.h"
 
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 
-using base::ChildPrivileges;
 using base::KillProcess;
 
 #ifdef MOZ_CRASHREPORTER
 using namespace CrashReporter;
 #endif
 using namespace mozilla::dom::power;
 using namespace mozilla::media;
 using namespace mozilla::embedding;
@@ -2118,20 +2117,18 @@ ContentParent::ContentParent(ContentPare
   }
   // Request Windows message deferral behavior on our side of the PContent
   // channel. Generally only applies to the situation where we get caught in
   // a deadlock with the plugin process when sending CPOWs.
   GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 #endif
 
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  ChildPrivileges privs = mRemoteType.EqualsLiteral(FILE_REMOTE_TYPE)
-                          ? base::PRIVILEGES_FILEREAD
-                          : base::PRIVILEGES_DEFAULT;
-  mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, privs);
+  bool isFile = mRemoteType.EqualsLiteral(FILE_REMOTE_TYPE);
+  mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, isFile);
 }
 
 ContentParent::~ContentParent()
 {
   if (mForceKillTimer) {
     mForceKillTimer->Cancel();
   }
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -65,17 +65,16 @@ include MemoryReportTypes;
 include "mozilla/dom/PContentBridgeParent.h";
 
 using GeoPosition from "nsGeoPositionIPCSerialiser.h";
 using AlertNotificationType from "mozilla/AlertNotificationIPCSerializer.h";
 
 using struct ChromePackage from "mozilla/chrome/RegistryMessageUtils.h";
 using struct SubstitutionMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using struct OverrideMapping from "mozilla/chrome/RegistryMessageUtils.h";
-using base::ChildPrivileges from "base/process_util.h";
 using base::ProcessId from "base/process.h";
 using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h";
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using mozilla::a11y::IHandlerControlHolder from "mozilla/a11y/IPCTypes.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using mozilla::hal::ProcessPriority from "mozilla/HalTypes.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -349,16 +349,18 @@ public:
   // message-sending functions under a layer of indirection and
   // eating the return values
   void Show(const ScreenIntSize& aSize, bool aParentIsActive);
 
   void UpdateDimensions(const nsIntRect& aRect, const ScreenIntSize& aSize);
 
   DimensionInfo GetDimensionInfo();
 
+  nsresult UpdatePosition();
+
   void SizeModeChanged(const nsSizeMode& aSizeMode);
 
   void UIResolutionChanged();
 
   void ThemeChanged();
 
   void HandleAccessKey(const WidgetKeyboardEvent& aEvent,
                        nsTArray<uint32_t>& aCharCodes);
@@ -664,18 +666,16 @@ private:
   void DestroyInternal();
 
   already_AddRefed<nsFrameLoader>
   GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const;
 
   RefPtr<nsIContentParent> mManager;
   void TryCacheDPIAndScale();
 
-  nsresult UpdatePosition();
-
   bool AsyncPanZoomEnabled() const;
 
   // Cached value indicating the docshell active state of the remote browser.
   bool mDocShellIsActive;
 
   // Update state prior to routing an APZ-aware event to the child process.
   // |aOutTargetGuid| will contain the identifier
   // of the APZC instance that handled the event. aOutTargetGuid may be null.
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -8,35 +8,38 @@
 #include <string.h>
 #include "mozilla/Logging.h"
 #include "prdtoa.h"
 #include "AudioStream.h"
 #include "VideoUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/Unused.h"
 #include <algorithm>
 #include "mozilla/Telemetry.h"
 #include "CubebUtils.h"
 #include "nsPrintfCString.h"
 #include "gfxPrefs.h"
 #include "AudioConverter.h"
 #if defined(XP_WIN)
 #include "nsXULAppAPI.h"
 #endif
 
 namespace mozilla {
 
 #undef LOG
 #undef LOGW
+#undef LOGE
 
 LazyLogModule gAudioStreamLog("AudioStream");
 // For simple logs
 #define LOG(x, ...) MOZ_LOG(gAudioStreamLog, mozilla::LogLevel::Debug, ("%p " x, this, ##__VA_ARGS__))
 #define LOGW(x, ...) MOZ_LOG(gAudioStreamLog, mozilla::LogLevel::Warning, ("%p " x, this, ##__VA_ARGS__))
+#define LOGE(x, ...) NS_DebugBreak(NS_DEBUG_WARNING, nsPrintfCString("%p " x, this, ##__VA_ARGS__).get(), nullptr, __FILE__, __LINE__)
 
 /**
  * Keep a list of frames sent to the audio engine in each DataCallback along
  * with the playback rate at the moment. Since the playback rate and number of
  * underrun frames can vary in each callback. We need to keep the whole history
  * in order to calculate the playback position of the audio engine correctly.
  */
 class FrameHistory {
@@ -272,37 +275,37 @@ OpenDumpFile(uint32_t aChannels, uint32_
     0x64, 0x61, 0x74, 0x61, 0xFE, 0xFF, 0xFF, 0x7F
   };
   static const int CHANNEL_OFFSET = 22;
   static const int SAMPLE_RATE_OFFSET = 24;
   static const int BLOCK_ALIGN_OFFSET = 32;
   SetUint16LE(header + CHANNEL_OFFSET, aChannels);
   SetUint32LE(header + SAMPLE_RATE_OFFSET, aRate);
   SetUint16LE(header + BLOCK_ALIGN_OFFSET, aChannels * 2);
-  fwrite(header, sizeof(header), 1, f);
+  Unused << fwrite(header, sizeof(header), 1, f);
 
   return f;
 }
 
 template <typename T>
 typename EnableIf<IsSame<T, int16_t>::value, void>::Type
 WriteDumpFileHelper(T* aInput, size_t aSamples, FILE* aFile) {
-  fwrite(aInput, sizeof(T), aSamples, aFile);
+  Unused << fwrite(aInput, sizeof(T), aSamples, aFile);
 }
 
 template <typename T>
 typename EnableIf<IsSame<T, float>::value, void>::Type
 WriteDumpFileHelper(T* aInput, size_t aSamples, FILE* aFile) {
   AutoTArray<uint8_t, 1024*2> buf;
   buf.SetLength(aSamples*2);
   uint8_t* output = buf.Elements();
   for (uint32_t i = 0; i < aSamples; ++i) {
     SetUint16LE(output + i*2, int16_t(aInput[i]*32767.0f));
   }
-  fwrite(output, 2, aSamples, aFile);
+  Unused << fwrite(output, 2, aSamples, aFile);
   fflush(aFile);
 }
 
 static void
 WriteDumpFile(FILE* aDumpFile, AudioStream* aStream, uint32_t aFrames,
               void* aBuffer)
 {
   if (!aDumpFile)
@@ -347,17 +350,17 @@ AudioStream::Init(uint32_t aNumChannels,
   params.channels = mOutChannels;
   params.layout = CubebUtils::ConvertChannelMapToCubebLayout(aChannelMap);
   params.format = ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value;
 
   mAudioClock.Init(aRate);
 
   cubeb* cubebContext = CubebUtils::GetCubebContext();
   if (!cubebContext) {
-    NS_WARNING("Can't get cubeb context!");
+    LOGE("Can't get cubeb context!");
     CubebUtils::ReportCubebStreamInitFailure(true);
     return NS_ERROR_DOM_MEDIA_CUBEB_INITIALIZATION_ERR;
   }
 
   // cubeb's winmm backend prefills buffers on init rather than stream start.
   // See https://github.com/kinetiknz/cubeb/issues/150
   mPrefillQuirk = !strcmp(cubeb_get_backend_id(cubebContext), "winmm");
 
@@ -376,17 +379,17 @@ AudioStream::OpenCubeb(cubeb* aContext, 
     CubebUtils::GetCubebPlaybackLatencyInMilliseconds() * aParams.rate / 1000;
   if (cubeb_stream_init(aContext, &stream, "AudioStream",
                         nullptr, nullptr, nullptr, &aParams,
                         latency_frames,
                         DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
     mCubebStream.reset(stream);
     CubebUtils::ReportCubebBackendUsed();
   } else {
-    NS_WARNING(nsPrintfCString("AudioStream::OpenCubeb() %p failed to init cubeb", this).get());
+    LOGE("OpenCubeb() failed to init cubeb");
     CubebUtils::ReportCubebStreamInitFailure(aIsFirst);
     return NS_ERROR_FAILURE;
   }
 
   TimeDuration timeDelta = TimeStamp::Now() - aStartTime;
   LOG("creation time %sfirst: %u ms", aIsFirst ? "" : "not ",
       (uint32_t) timeDelta.ToMilliseconds());
   Telemetry::Accumulate(aIsFirst ? Telemetry::AUDIOSTREAM_FIRST_OPEN_MS :
@@ -396,17 +399,17 @@ AudioStream::OpenCubeb(cubeb* aContext, 
 }
 
 void
 AudioStream::SetVolume(double aVolume)
 {
   MOZ_ASSERT(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
 
   if (cubeb_stream_set_volume(mCubebStream.get(), aVolume * CubebUtils::GetVolumeScale()) != CUBEB_OK) {
-    NS_WARNING("Could not change volume on cubeb stream.");
+    LOGE("Could not change volume on cubeb stream.");
   }
 }
 
 void
 AudioStream::Start()
 {
   MonitorAutoLock mon(mMonitor);
   MOZ_ASSERT(mState == INITIALIZED);
@@ -680,17 +683,17 @@ AudioStream::StateCallback(cubeb_state a
 {
   MonitorAutoLock mon(mMonitor);
   MOZ_ASSERT(mState != SHUTDOWN, "No state callback after shutdown");
   LOG("StateCallback, mState=%d cubeb_state=%d", mState, aState);
   if (aState == CUBEB_STATE_DRAINED) {
     mState = DRAINED;
     mDataSource.Drained();
   } else if (aState == CUBEB_STATE_ERROR) {
-    LOG("StateCallback() state %d cubeb error", mState);
+    LOGE("StateCallback() state %d cubeb error", mState);
     mState = ERRORED;
   }
 }
 
 AudioClock::AudioClock()
 : mOutRate(0),
   mInRate(0),
   mPreservesPitch(true),
--- a/dom/media/ChannelMediaResource.cpp
+++ b/dom/media/ChannelMediaResource.cpp
@@ -516,17 +516,17 @@ ChannelMediaResource::OpenChannel(int64_
 
   rv = mChannel->AsyncOpen2(mListener);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Tell the media element that we are fetching data from a channel.
   MediaDecoderOwner* owner = mCallback->GetMediaOwner();
   NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
   dom::HTMLMediaElement* element = owner->GetMediaElement();
-  element->DownloadResumed(true);
+  element->DownloadResumed();
 
   return NS_OK;
 }
 
 nsresult
 ChannelMediaResource::SetupChannelHeaders(int64_t aOffset)
 {
   // Always use a byte range request even if we're reading from the start
--- a/dom/media/MediaDecoderOwner.h
+++ b/dom/media/MediaDecoderOwner.h
@@ -91,25 +91,16 @@ public:
   // when the resource has completed seeking.
   virtual void SeekCompleted() = 0;
 
   // Called by the media stream, on the main thread, when the download
   // has been suspended by the cache or because the element itself
   // asked the decoder to suspend the download.
   virtual void DownloadSuspended() = 0;
 
-  // Called by the media stream, on the main thread, when the download
-  // has been resumed by the cache or because the element itself
-  // asked the decoder to resumed the download.
-  // If aForceNetworkLoading is True, ignore the fact that the download has
-  // previously finished. We are downloading the middle of the media after
-  // having downloaded the end, we need to notify the element a download in
-  // ongoing.
-  virtual void DownloadResumed(bool aForceNetworkLoading = false) = 0;
-
   // Called by the media decoder to indicate whether the media cache has
   // suspended the channel.
   virtual void NotifySuspendedByCache(bool aSuspendedByCache) = 0;
 
   // called to notify that the principal of the decoder's media resource has changed.
   virtual void NotifyDecoderPrincipalChanged() = 0;
 
   // The status of the next frame which might be available from the decoder
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -63,29 +63,33 @@ using namespace mozilla::media;
 
 #define NS_DispatchToMainThread(...) CompileError_UseAbstractThreadDispatchInstead
 
 // avoid redefined macro in unified build
 #undef FMT
 #undef LOG
 #undef LOGV
 #undef LOGW
+#undef LOGE
 #undef SFMT
 #undef SLOG
 #undef SLOGW
+#undef SLOGE
 
 #define FMT(x, ...) "Decoder=%p " x, mDecoderID, ##__VA_ARGS__
 #define LOG(x, ...) MOZ_LOG(gMediaDecoderLog, LogLevel::Debug,   (FMT(x, ##__VA_ARGS__)))
 #define LOGV(x, ...) MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, (FMT(x, ##__VA_ARGS__)))
 #define LOGW(x, ...) NS_WARNING(nsPrintfCString(FMT(x, ##__VA_ARGS__)).get())
+#define LOGE(x, ...) NS_DebugBreak(NS_DEBUG_WARNING, nsPrintfCString(FMT(x, ##__VA_ARGS__)).get(), nullptr, __FILE__, __LINE__)
 
 // Used by StateObject and its sub-classes
 #define SFMT(x, ...) "Decoder=%p state=%s " x, mMaster->mDecoderID, ToStateStr(GetState()), ##__VA_ARGS__
 #define SLOG(x, ...) MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, (SFMT(x, ##__VA_ARGS__)))
 #define SLOGW(x, ...) NS_WARNING(nsPrintfCString(SFMT(x, ##__VA_ARGS__)).get())
+#define SLOGE(x, ...) NS_DebugBreak(NS_DEBUG_WARNING, nsPrintfCString(SFMT(x, ##__VA_ARGS__)).get(), nullptr, __FILE__, __LINE__)
 
 // Certain constants get stored as member variables and then adjusted by various
 // scale factors on a per-decoder basis. We want to make sure to avoid using these
 // constants directly, so we put them in a namespace.
 namespace detail {
 
 // Resume a suspended video decoder to the current playback position plus this
 // time premium for compensating the seeking delay.
@@ -360,17 +364,17 @@ public:
   }
 
 private:
   void OnMetadataRead(MetadataHolder&& aMetadata);
 
   void OnMetadataNotRead(const MediaResult& aError)
   {
     mMetadataRequest.Complete();
-    SLOGW("Decode metadata failed, shutting down decoder");
+    SLOGE("Decode metadata failed, shutting down decoder");
     mMaster->DecodeError(aError);
   }
 
   MozPromiseRequestHolder<MediaFormatReader::MetadataPromise> mMetadataRequest;
 };
 
 /**
  * Purpose: release decoder resources to save memory and hardware resources.
@@ -1238,17 +1242,17 @@ protected:
     CheckedInt64 framesToPrune = TimeUnitToFrames(
       mSeekJob.mTarget->GetTime() - audioTime, Info().mAudio.mRate);
     if (!framesToPrune.isValid()) {
       return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
     }
     if (framesToPrune.value() > aAudio->mFrames) {
       // We've messed up somehow. Don't try to trim frames, the |frames|
       // variable below will overflow.
-      SLOGW("Can't prune more frames that we have!");
+      SLOGE("Can't prune more frames that we have!");
       return NS_ERROR_FAILURE;
     }
     uint32_t frames = aAudio->mFrames - uint32_t(framesToPrune.value());
     uint32_t channels = aAudio->mChannels;
     AlignedAudioBuffer audioData(frames * channels);
     if (!audioData) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
@@ -3338,17 +3342,17 @@ MediaDecoderStateMachine::HasLowBuffered
   media::TimeInterval interval(start, end);
   return !mBuffered.Ref().Contains(interval);
 }
 
 void
 MediaDecoderStateMachine::DecodeError(const MediaResult& aError)
 {
   MOZ_ASSERT(OnTaskQueue());
-  LOGW("Decode error");
+  LOGE("Decode error");
   // Notify the decode error and MediaDecoder will shut down MDSM.
   mOnPlaybackErrorEvent.Notify(aError);
 }
 
 void
 MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent()
 {
   MOZ_ASSERT(OnTaskQueue());
@@ -3585,17 +3589,17 @@ MediaDecoderStateMachine::OnMediaSinkVid
   ScheduleStateMachine();
 }
 
 void
 MediaDecoderStateMachine::OnMediaSinkVideoError()
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(HasVideo());
-  LOGW("[%s]", __func__);
+  LOGE("[%s]", __func__);
 
   mMediaSinkVideoPromise.Complete();
   mVideoCompleted = true;
   if (HasAudio()) {
     return;
   }
   DecodeError(MediaResult(NS_ERROR_DOM_MEDIA_MEDIASINK_ERR, __func__));
 }
@@ -3615,17 +3619,17 @@ void MediaDecoderStateMachine::OnMediaSi
   mOnDecoderDoctorEvent.Notify(
     DecoderDoctorEvent{DecoderDoctorEvent::eAudioSinkStartup, NS_OK});
 }
 
 void MediaDecoderStateMachine::OnMediaSinkAudioError(nsresult aResult)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(HasAudio());
-  LOGW("[%s]", __func__);
+  LOGE("[%s]", __func__);
 
   mMediaSinkAudioPromise.Complete();
   mAudioCompleted = true;
 
   // Result should never be NS_OK in this *error* handler. Report to Dec-Doc.
   MOZ_ASSERT(NS_FAILED(aResult));
   mOnDecoderDoctorEvent.Notify(
     DecoderDoctorEvent{DecoderDoctorEvent::eAudioSinkStartup, aResult});
@@ -3866,10 +3870,12 @@ MediaDecoderStateMachine::CancelSuspendT
 }
 
 } // namespace mozilla
 
 // avoid redefined macro in unified build
 #undef LOG
 #undef LOGV
 #undef LOGW
+#undef LOGE
 #undef SLOGW
+#undef SLOGE
 #undef NS_DispatchToMainThread
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -44,16 +44,18 @@ ChromiumCDMParent::ChromiumCDMParent(GMP
 bool
 ChromiumCDMParent::Init(ChromiumCDMCallback* aCDMCallback,
                         bool aAllowDistinctiveIdentifier,
                         bool aAllowPersistentState,
                         nsIEventTarget* aMainThread)
 {
   GMP_LOG("ChromiumCDMParent::Init(this=%p)", this);
   if (!aCDMCallback || !aMainThread) {
+    GMP_LOG("ChromiumCDMParent::Init(this=%p) failure since aCDMCallback(%p) or"
+            " aMainThread(%p) is nullptr", this, aCDMCallback, aMainThread);
     return false;
   }
   mCDMCallback = aCDMCallback;
   mMainThread = aMainThread;
   return SendInit(aAllowDistinctiveIdentifier, aAllowPersistentState);
 }
 
 void
--- a/dom/media/gmp/ChromiumCDMProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMProxy.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ChromiumCDMProxy.h"
 #include "ChromiumCDMCallbackProxy.h"
+#include "MediaResult.h"
 #include "mozilla/dom/MediaKeySession.h"
 #include "GMPUtils.h"
 #include "nsPrintfCString.h"
 #include "GMPService.h"
 #include "content_decryption_module.h"
 
 #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
 
@@ -101,28 +102,28 @@ ChromiumCDMProxy::Init(PromiseId aPromis
           self->mCallback =
             MakeUnique<ChromiumCDMCallbackProxy>(self, self->mMainThread);
           if (!cdm->Init(self->mCallback.get(),
                          self->mDistinctiveIdentifierRequired,
                          self->mPersistentStateRequired,
                          self->mMainThread)) {
             self->RejectPromise(aPromiseId,
                                 NS_ERROR_FAILURE,
-                                NS_LITERAL_CSTRING("GetCDM failed."));
+                                NS_LITERAL_CSTRING("GetCDM failed due to CDM initialization failure."));
             return;
           }
           {
             MutexAutoLock lock(self->mCDMMutex);
             self->mCDM = cdm;
           }
           self->OnCDMCreated(aPromiseId);
         },
-        [self, aPromiseId](nsresult rv) {
+        [self, aPromiseId](MediaResult rv) {
           self->RejectPromise(
-            aPromiseId, NS_ERROR_FAILURE, NS_LITERAL_CSTRING("GetCDM failed."));
+            aPromiseId, rv.Code(), rv.Description());
         });
     }));
 
   mGMPThread->Dispatch(task.forget());
 }
 
 void
 ChromiumCDMProxy::OnCDMCreated(uint32_t aPromiseId)
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -31,16 +31,17 @@
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsHashKeys.h"
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
 #include "nsThreadUtils.h"
 #include "GMPCrashHelper.h"
 
+#include "MediaResult.h"
 #include "mozilla/dom/PluginCrashedEvent.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/SystemGroup.h"
 
 namespace mozilla {
 
 #ifdef LOG
@@ -232,44 +233,52 @@ GeckoMediaPluginService::Init()
 RefPtr<GetCDMParentPromise>
 GeckoMediaPluginService::GetCDM(const NodeId& aNodeId,
                                 nsTArray<nsCString> aTags,
                                 GMPCrashHelper* aHelper)
 {
   MOZ_ASSERT(mGMPThread->EventTarget()->IsOnCurrentThread());
 
   if (mShuttingDownOnGMPThread || aTags.IsEmpty()) {
-    return GetCDMParentPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+    nsPrintfCString reason("%s::%s failed, aTags.IsEmpty() = %d, mShuttingDownOnGMPThread = %d.",
+      __CLASS__, __FUNCTION__, aTags.IsEmpty(), mShuttingDownOnGMPThread);
+    return GetCDMParentPromise::CreateAndReject(MediaResult(NS_ERROR_FAILURE, reason.get()), __func__);
   }
 
   typedef MozPromiseHolder<GetCDMParentPromise> PromiseHolder;
   PromiseHolder* rawHolder(new PromiseHolder());
   RefPtr<GetCDMParentPromise> promise = rawHolder->Ensure(__func__);
   RefPtr<AbstractThread> thread(GetAbstractGMPThread());
   RefPtr<GMPCrashHelper> helper(aHelper);
   GetContentParent(
     aHelper, aNodeId, NS_LITERAL_CSTRING(CHROMIUM_CDM_API), aTags)
     ->Then(thread,
            __func__,
            [rawHolder, helper](RefPtr<GMPContentParent::CloseBlocker> wrapper) {
              RefPtr<GMPContentParent> parent = wrapper->mParent;
              UniquePtr<PromiseHolder> holder(rawHolder);
              RefPtr<ChromiumCDMParent> cdm = parent->GetChromiumCDM();
              if (!parent) {
-               holder->Reject(NS_ERROR_FAILURE, __func__);
+               nsPrintfCString reason(
+                 "%s::%s failed since GetChromiumCDM returns nullptr.",
+                 __CLASS__, __FUNCTION__);
+               holder->Reject(MediaResult(NS_ERROR_FAILURE, reason.get()), __func__);
                return;
              }
              if (helper) {
                cdm->SetCrashHelper(helper);
              }
              holder->Resolve(cdm, __func__);
            },
            [rawHolder] {
+             nsPrintfCString reason(
+               "%s::%s failed since GetContentParent rejects the promise.",
+               __CLASS__, __FUNCTION__);
              UniquePtr<PromiseHolder> holder(rawHolder);
-             holder->Reject(NS_ERROR_FAILURE, __func__);
+             holder->Reject(MediaResult(NS_ERROR_FAILURE, reason.get()), __func__);
            });
 
   return promise;
 }
 
 void
 GeckoMediaPluginService::ShutdownGMPThread()
 {
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -20,16 +20,17 @@
 #include "nsIWeakReference.h"
 #include "mozilla/AbstractThread.h"
 #include "nsClassHashtable.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/MozPromise.h"
 #include "GMPContentParent.h"
 #include "GMPCrashHelper.h"
 #include "ChromiumCDMParent.h"
+#include "MediaResult.h"
 
 template <class> struct already_AddRefed;
 
 namespace mozilla {
 
 class GMPCrashHelper;
 
 extern LogModule* GetGMPLog();
@@ -51,17 +52,17 @@ struct NodeId
   nsString mGMPName;
 };
 
 typedef MozPromise<RefPtr<GMPContentParent::CloseBlocker>,
                    nsresult,
                    /* IsExclusive = */ true>
   GetGMPContentParentPromise;
 typedef MozPromise<RefPtr<ChromiumCDMParent>,
-                   nsresult,
+                   MediaResult,
                    /* IsExclusive = */ true>
   GetCDMParentPromise;
 
 class GeckoMediaPluginService : public mozIGeckoMediaPluginService
                               , public nsIObserver
 {
 public:
   static already_AddRefed<GeckoMediaPluginService> GetGeckoMediaPluginService();
--- a/dom/media/gtest/TestCDMStorage.cpp
+++ b/dom/media/gtest/TestCDMStorage.cpp
@@ -6,16 +6,17 @@
 
 #include "gtest/gtest.h"
 
 #include "mozilla/RefPtr.h"
 
 #include "ChromiumCDMCallback.h"
 #include "GMPTestMonitor.h"
 #include "GMPServiceParent.h"
+#include "MediaResult.h"
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
 #include "nsNSSComponent.h" //For EnsureNSSInitializedChromeOrContent
 #include "nsThreadUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::gmp;
 
@@ -465,17 +466,17 @@ class CDMStorageTest
                     EXPECT_TRUE(!!self->mCDM);
                     self->mCallback.reset(new CallbackProxy(self));
                     self->mCDM->Init(self->mCallback.get(), false, true, GetMainThreadEventTarget());
 
                     for (auto& update : aUpdates) {
                       self->Update(update);
                     }
                   },
-                  [](nsresult rv) { EXPECT_TRUE(false); });
+                  [](MediaResult rv) { EXPECT_TRUE(false); });
   }
 
   void TestBasicStorage() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsCDMStorageIsEmpty());
 
     RefPtr<GeckoMediaPluginService> service =
       GeckoMediaPluginService::GetGeckoMediaPluginService();
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -305,21 +305,17 @@ MediaSourceDecoder::CanPlayThroughImpl()
   }
 
   if (IsNaN(mMediaSource->Duration())) {
     // Don't have any data yet.
     return false;
   }
   TimeUnit duration = TimeUnit::FromSeconds(mMediaSource->Duration());
   auto currentPosition = CurrentPosition();
-  if (duration.IsInfinite()) {
-    // We can't make an informed decision and just assume that it's a live
-    // stream
-    return true;
-  } else if (duration <= currentPosition) {
+  if (duration <= currentPosition) {
     return true;
   }
   // If we have data up to the mediasource's duration or 10s ahead, we can
   // assume that we can play without interruption.
   TimeIntervals buffered = GetBuffered();
   buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2);
   TimeUnit timeAhead =
     std::min(duration, currentPosition + TimeUnit::FromSeconds(10));
--- a/dom/media/mediasource/ResourceQueue.cpp
+++ b/dom/media/mediasource/ResourceQueue.cpp
@@ -6,16 +6,17 @@
 
 #include "ResourceQueue.h"
 #include "nsDeque.h"
 #include "MediaData.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/Unused.h"
 
 extern mozilla::LogModule* GetSourceBufferResourceLog();
 
 #define SBR_DEBUG(arg, ...) MOZ_LOG(GetSourceBufferResourceLog(), mozilla::LogLevel::Debug, ("ResourceQueue(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 #define SBR_DEBUGV(arg, ...) MOZ_LOG(GetSourceBufferResourceLog(), mozilla::LogLevel::Verbose, ("ResourceQueue(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 
 namespace mozilla {
 
@@ -166,17 +167,17 @@ ResourceQueue::Dump(const char* aPath)
     ResourceItem* item = ResourceAt(i);
 
     char buf[255];
     SprintfLiteral(buf, "%s/%08u.bin", aPath, i);
     FILE* fp = fopen(buf, "wb");
     if (!fp) {
       return;
     }
-    fwrite(item->mData->Elements(), item->mData->Length(), 1, fp);
+    Unused << fwrite(item->mData->Elements(), item->mData->Length(), 1, fp);
     fclose(fp);
   }
 }
 #endif
 
 ResourceItem*
 ResourceQueue::ResourceAt(uint32_t aIndex) const
 {
--- a/dom/webauthn/AuthenticatorAssertionResponse.cpp
+++ b/dom/webauthn/AuthenticatorAssertionResponse.cpp
@@ -87,10 +87,25 @@ nsresult
 AuthenticatorAssertionResponse::SetSignature(CryptoBuffer& aBuffer)
 {
   if (NS_WARN_IF(!mSignature.Assign(aBuffer))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   return NS_OK;
 }
 
+void
+AuthenticatorAssertionResponse::GetUserId(DOMString& aRetVal)
+{
+  // This requires mUserId to not be re-set for the life of the caller's in-var.
+  aRetVal.SetOwnedString(mUserId);
+}
+
+nsresult
+AuthenticatorAssertionResponse::SetUserId(const nsAString& aUserId)
+{
+  MOZ_ASSERT(mUserId.IsEmpty(), "We already have a UserID?");
+  mUserId.Assign(aUserId);
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/webauthn/AuthenticatorAssertionResponse.h
+++ b/dom/webauthn/AuthenticatorAssertionResponse.h
@@ -42,19 +42,26 @@ public:
   SetAuthenticatorData(CryptoBuffer& aBuffer);
 
   void
   GetSignature(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal);
 
   nsresult
   SetSignature(CryptoBuffer& aBuffer);
 
+  void
+  GetUserId(DOMString& aRetVal);
+
+  nsresult
+  SetUserId(const nsAString& aUserId);
+
 private:
   CryptoBuffer mAuthenticatorData;
   JS::Heap<JSObject*> mAuthenticatorDataCachedObj;
   CryptoBuffer mSignature;
   JS::Heap<JSObject*> mSignatureCachedObj;
+  nsString mUserId;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_AuthenticatorAssertionResponse_h
--- a/dom/webauthn/PublicKeyCredential.cpp
+++ b/dom/webauthn/PublicKeyCredential.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PublicKeyCredential.h"
 #include "mozilla/dom/WebAuthenticationBinding.h"
 #include "nsCycleCollectionParticipant.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(PublicKeyCredential)
@@ -77,10 +78,34 @@ PublicKeyCredential::SetRawId(CryptoBuff
 }
 
 void
 PublicKeyCredential::SetResponse(RefPtr<AuthenticatorResponse> aResponse)
 {
   mResponse = aResponse;
 }
 
+/* static */ already_AddRefed<Promise>
+PublicKeyCredential::IsPlatformAuthenticatorAvailable(GlobalObject& aGlobal)
+{
+  nsIGlobalObject* globalObject =
+    xpc::NativeGlobal(JS::CurrentGlobalOrNull(aGlobal.Context()));
+  if (NS_WARN_IF(!globalObject)) {
+    return nullptr;
+  }
+
+  ErrorResult rv;
+  RefPtr<Promise> promise = Promise::Create(globalObject, rv);
+  if(rv.Failed()) {
+    return nullptr;
+  }
+
+  // Complete in Bug 1406468. This shouldn't just always return true, it should
+  // follow the guidelines in
+  // https://w3c.github.io/webauthn/#isPlatformAuthenticatorAvailable
+  // such as ensuring that U2FTokenManager isn't in some way disabled.
+  promise->MaybeResolve(true);
+  return promise.forget();
+}
+
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/webauthn/PublicKeyCredential.h
+++ b/dom/webauthn/PublicKeyCredential.h
@@ -41,16 +41,19 @@ public:
   Response() const;
 
   nsresult
   SetRawId(CryptoBuffer& aBuffer);
 
   void
   SetResponse(RefPtr<AuthenticatorResponse>);
 
+  static already_AddRefed<Promise>
+  IsPlatformAuthenticatorAvailable(GlobalObject& aGlobal);
+
 private:
   CryptoBuffer mRawId;
   JS::Heap<JSObject*> mRawIdCachedObj;
   RefPtr<AuthenticatorResponse> mResponse;
   // Extensions are not supported yet.
   // <some type> mClientExtensionResults;
 };
 
--- a/dom/webauthn/WebAuthnManager.cpp
+++ b/dom/webauthn/WebAuthnManager.cpp
@@ -27,16 +27,18 @@ namespace mozilla {
 namespace dom {
 
 /***********************************************************************
  * Protocol Constants
  **********************************************************************/
 
 const uint8_t FLAG_TUP = 0x01; // Test of User Presence required
 const uint8_t FLAG_AT = 0x40; // Authenticator Data is provided
+const uint8_t FLAG_UV = 0x04; // User was Verified (biometrics, etc.); this
+                              // flag is not possible with U2F devices
 
 /***********************************************************************
  * Statics
  **********************************************************************/
 
 namespace {
 StaticRefPtr<WebAuthnManager> gWebAuthnManager;
 static mozilla::LazyLogModule gWebAuthnManagerLog("webauthnmanager");
@@ -83,17 +85,17 @@ AssembleClientData(const nsAString& aOri
   nsresult rv = aChallenge.ToJwkBase64(challengeBase64);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_FAILURE;
   }
 
   CollectedClientData clientDataObject;
   clientDataObject.mChallenge.Assign(challengeBase64);
   clientDataObject.mOrigin.Assign(aOrigin);
-  clientDataObject.mHashAlg.AssignLiteral(u"SHA-256");
+  clientDataObject.mHashAlgorithm.AssignLiteral(u"SHA-256");
 
   nsAutoString temp;
   if (NS_WARN_IF(!clientDataObject.ToJSON(temp))) {
     return NS_ERROR_FAILURE;
   }
 
   aJsonOut.Assign(NS_ConvertUTF16toUTF8(temp));
   return NS_OK;
@@ -275,17 +277,17 @@ WebAuthnManager*
 WebAuthnManager::Get()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return gWebAuthnManager;
 }
 
 already_AddRefed<Promise>
 WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent,
-                                const MakeCredentialOptions& aOptions)
+                                const MakePublicKeyCredentialOptions& aOptions)
 {
   MOZ_ASSERT(aParent);
 
   MaybeClearTransaction();
 
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aParent);
 
   ErrorResult rv;
@@ -297,16 +299,28 @@ WebAuthnManager::MakeCredential(nsPIDOMW
   nsString origin;
   nsCString rpId;
   rv = GetOrigin(aParent, origin, rpId);
   if (NS_WARN_IF(rv.Failed())) {
     promise->MaybeReject(rv);
     return promise.forget();
   }
 
+  // Enforce 4.4.3 User Account Parameters for Credential Generation
+  if (aOptions.mUser.mId.WasPassed()) {
+    // When we add UX, we'll want to do more with this value, but for now
+    // we just have to verify its correctness.
+    CryptoBuffer userId;
+    userId.Assign(aOptions.mUser.mId.Value());
+    if (userId.Length() > 64) {
+      promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR);
+      return promise.forget();
+    }
+  }
+
   // If timeoutSeconds was specified, check if its value lies within a
   // reasonable range as defined by the platform and if not, correct it to the
   // closest value lying within that range.
 
   uint32_t adjustedTimeout = 30000;
   if (aOptions.mTimeout.WasPassed()) {
     adjustedTimeout = aOptions.mTimeout.Value();
     adjustedTimeout = std::max(15000u, adjustedTimeout);
@@ -346,56 +360,56 @@ WebAuthnManager::MakeCredential(nsPIDOMW
   if (NS_WARN_IF(NS_FAILED(srv))) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
 
   // Process each element of cryptoParameters using the following steps, to
   // produce a new sequence normalizedParameters.
   nsTArray<PublicKeyCredentialParameters> normalizedParams;
-  for (size_t a = 0; a < aOptions.mParameters.Length(); ++a) {
+  for (size_t a = 0; a < aOptions.mPubKeyCredParams.Length(); ++a) {
     // Let current be the currently selected element of
     // cryptoParameters.
 
     // If current.type does not contain a PublicKeyCredentialType
     // supported by this implementation, then stop processing current and move
     // on to the next element in cryptoParameters.
-    if (aOptions.mParameters[a].mType != PublicKeyCredentialType::Public_key) {
+    if (aOptions.mPubKeyCredParams[a].mType != PublicKeyCredentialType::Public_key) {
       continue;
     }
 
     // Let normalizedAlgorithm be the result of normalizing an algorithm using
     // the procedure defined in [WebCryptoAPI], with alg set to
     // current.algorithm and op set to 'generateKey'. If an error occurs during
     // this procedure, then stop processing current and move on to the next
     // element in cryptoParameters.
 
     nsString algName;
-    if (NS_FAILED(GetAlgorithmName(aOptions.mParameters[a].mAlgorithm,
+    if (NS_FAILED(GetAlgorithmName(aOptions.mPubKeyCredParams[a].mAlg,
                                    algName))) {
       continue;
     }
 
     // Add a new object of type PublicKeyCredentialParameters to
     // normalizedParameters, with type set to current.type and algorithm set to
     // normalizedAlgorithm.
     PublicKeyCredentialParameters normalizedObj;
-    normalizedObj.mType = aOptions.mParameters[a].mType;
-    normalizedObj.mAlgorithm.SetAsString().Assign(algName);
+    normalizedObj.mType = aOptions.mPubKeyCredParams[a].mType;
+    normalizedObj.mAlg.SetAsString().Assign(algName);
 
     if (!normalizedParams.AppendElement(normalizedObj, mozilla::fallible)){
       promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
       return promise.forget();
     }
   }
 
   // If normalizedAlgorithm is empty and cryptoParameters was not empty, cancel
   // the timer started in step 2, reject promise with a DOMException whose name
   // is "NotSupportedError", and terminate this algorithm.
-  if (normalizedParams.IsEmpty() && !aOptions.mParameters.IsEmpty()) {
+  if (normalizedParams.IsEmpty() && !aOptions.mPubKeyCredParams.IsEmpty()) {
     promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return promise.forget();
   }
 
   // TODO: The following check should not be here. This is checking for
   // parameters specific to the soft key, and should be put in the soft key
   // manager in the parent process. Still need to serialize
   // PublicKeyCredentialParameters first.
@@ -404,18 +418,18 @@ WebAuthnManager::MakeCredential(nsPIDOMW
   // PublicKeyCredentialParameters and cryptographic parameters is supported. If
   // not, return an error code equivalent to NotSupportedError and terminate the
   // operation.
 
   bool isValidCombination = false;
 
   for (size_t a = 0; a < normalizedParams.Length(); ++a) {
     if (normalizedParams[a].mType == PublicKeyCredentialType::Public_key &&
-        normalizedParams[a].mAlgorithm.IsString() &&
-        normalizedParams[a].mAlgorithm.GetAsString().EqualsLiteral(
+        normalizedParams[a].mAlg.IsString() &&
+        normalizedParams[a].mAlg.GetAsString().EqualsLiteral(
           JWK_ALG_ECDSA_P_256)) {
       isValidCombination = true;
       break;
     }
   }
   if (!isValidCombination) {
     promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return promise.forget();
@@ -457,24 +471,22 @@ WebAuthnManager::MakeCredential(nsPIDOMW
 
   srv = HashCString(hashService, clientDataJSON, clientDataHash);
   if (NS_WARN_IF(NS_FAILED(srv))) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
 
   nsTArray<WebAuthnScopedCredentialDescriptor> excludeList;
-  if (aOptions.mExcludeList.WasPassed()) {
-    for (const auto& s: aOptions.mExcludeList.Value()) {
-      WebAuthnScopedCredentialDescriptor c;
-      CryptoBuffer cb;
-      cb.Assign(s.mId);
-      c.id() = cb;
-      excludeList.AppendElement(c);
-    }
+  for (const auto& s: aOptions.mExcludeCredentials) {
+    WebAuthnScopedCredentialDescriptor c;
+    CryptoBuffer cb;
+    cb.Assign(s.mId);
+    c.id() = cb;
+    excludeList.AppendElement(c);
   }
 
   // TODO: Add extension list building
   nsTArray<WebAuthnExtension> extensions;
 
   WebAuthnTransactionInfo info(rpIdHash,
                                clientDataHash,
                                adjustedTimeout,
@@ -619,23 +631,23 @@ WebAuthnManager::GetAssertion(nsPIDOMWin
   srv = HashCString(hashService, clientDataJSON, clientDataHash);
   if (NS_WARN_IF(NS_FAILED(srv))) {
     promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
     return promise.forget();
   }
 
   // Note: we only support U2F-style authentication for now, so we effectively
   // require an AllowList.
-  if (aOptions.mAllowList.Length() < 1) {
+  if (aOptions.mAllowCredentials.Length() < 1) {
     promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
     return promise.forget();
   }
 
   nsTArray<WebAuthnScopedCredentialDescriptor> allowList;
-  for (const auto& s: aOptions.mAllowList) {
+  for (const auto& s: aOptions.mAllowCredentials) {
     WebAuthnScopedCredentialDescriptor c;
     CryptoBuffer cb;
     cb.Assign(s.mId);
     c.id() = cb;
     allowList.AppendElement(c);
   }
 
   // TODO: Add extension list building
--- a/dom/webauthn/WebAuthnManager.h
+++ b/dom/webauthn/WebAuthnManager.h
@@ -52,18 +52,17 @@ public:
 
 namespace mozilla {
 namespace dom {
 
 struct Account;
 class ArrayBufferViewOrArrayBuffer;
 struct AssertionOptions;
 class OwningArrayBufferViewOrArrayBuffer;
-struct ScopedCredentialOptions;
-struct ScopedCredentialParameters;
+struct MakePublicKeyCredentialOptions;
 class Promise;
 class WebAuthnTransactionChild;
 class WebAuthnTransactionInfo;
 
 class WebAuthnManager final : public nsIIPCBackgroundChildCreateCallback,
                               public nsIDOMEventListener
 {
 public:
@@ -79,17 +78,17 @@ public:
   FinishGetAssertion(nsTArray<uint8_t>& aCredentialId,
                      nsTArray<uint8_t>& aSigBuffer);
 
   void
   Cancel(const nsresult& aError);
 
   already_AddRefed<Promise>
   MakeCredential(nsPIDOMWindowInner* aParent,
-                 const MakeCredentialOptions& aOptions);
+                 const MakePublicKeyCredentialOptions& aOptions);
 
   already_AddRefed<Promise>
   GetAssertion(nsPIDOMWindowInner* aParent,
                const PublicKeyCredentialRequestOptions& aOptions);
 
   void StartRegister();
   void StartSign();
   void StartCancel();
--- a/dom/webauthn/tests/browser/tab_webauthn_success.html
+++ b/dom/webauthn/tests/browser/tab_webauthn_success.html
@@ -30,84 +30,81 @@ function signalCompletion(aText) {
   result.id = "result";
   result.textContent = aText;
   document.body.append(result);
 }
 
 let gState = {};
 let makeCredentialOptions = {
   rp: {id: document.domain, name: "none", icon: "none"},
-  user: {id: "none", name: "none", icon: "none", displayName: "none"},
+  user: {id: new Uint8Array(), name: "none", icon: "none", displayName: "none"},
   challenge: gCredentialChallenge,
   timeout: 5000, // the minimum timeout is actually 15 seconds
-  parameters: [{type: "public-key", algorithm: "ES256"}],
+  pubKeyCredParams: [{type: "public-key", alg: "ES256"}],
 };
 
 navigator.credentials.create({publicKey: makeCredentialOptions})
 .then(function (aNewCredentialInfo) {
   gState.credential = aNewCredentialInfo;
 
   return webAuthnDecodeCBORAttestation(aNewCredentialInfo.response.attestationObject);
 })
 .then(function testAssertion(aCredInfo) {
+  gState.authDataObj = aCredInfo.authDataObj;
   gState.publicKeyHandle = aCredInfo.authDataObj.publicKeyHandle;
 
   let newCredential = {
     type: "public-key",
     id: new Uint8Array(gState.credential.rawId),
     transports: ["usb"],
   }
 
   let publicKeyCredentialRequestOptions = {
     challenge: gAssertionChallenge,
     timeout: 5000, // the minimum timeout is actually 15 seconds
     rpId: document.domain,
-    allowList: [newCredential]
+    allowCredentials: [newCredential]
   };
 
-  return navigator.credentials.get({publicKey: publicKeyCredentialRequestOptions});
+  // Make sure the RP ID hash matches what we calculate.
+  return crypto.subtle.digest("SHA-256", string2buffer(document.domain))
+  .then(function(calculatedRpIdHash) {
+    let calcHashStr = bytesToBase64UrlSafe(new Uint8Array(calculatedRpIdHash));
+    let providedHashStr = bytesToBase64UrlSafe(new Uint8Array(gState.authDataObj.rpIdHash));
+
+    if (calcHashStr != providedHashStr) {
+      return Promise.reject("Calculated RP ID hash must match what the browser derived.");
+    }
+
+    return navigator.credentials.get({publicKey: publicKeyCredentialRequestOptions});
+  });
 })
 .then(function(aAssertion) {
   let clientData = JSON.parse(buffer2string(aAssertion.response.clientDataJSON));
 
   gState.assertion = aAssertion;
 
   return webAuthnDecodeAuthDataArray(new Uint8Array(aAssertion.response.authenticatorData));
 })
-.then(function(aAttestationObj) {
-  gState.attestation = aAttestationObj;
-
-  // Make sure the RP ID hash matches what we calculate.
-  return crypto.subtle.digest("SHA-256", string2buffer(document.domain))
-  .then(function(calculatedRpIdHash) {
-    let calcHashStr = bytesToBase64UrlSafe(new Uint8Array(calculatedRpIdHash));
-    let providedHashStr = bytesToBase64UrlSafe(new Uint8Array(aAttestationObj.authDataObj.rpIdHash));
-
-    if (calcHashStr != providedHashStr) {
-      return Promise.reject("Calculated RP ID hash must match what the browser derived.");
-    }
-    return Promise.resolve(aAttestationObj);
-  });
-})
 .then(function(aAttestation) {
   if (new Uint8Array(aAttestation.flags) != flag_TUP) {
     return Promise.reject("Assertion's user presence byte not set correctly.");
   }
 
   let clientDataJSON = gState.assertion.response.clientDataJSON;
   return deriveAppAndChallengeParam(document.domain, clientDataJSON, aAttestation);
 })
 .then(function(aParams) {
   return assembleSignedData(aParams.appParam, aParams.attestation.flags,
                             aParams.attestation.counter, aParams.challengeParam);
 })
 .then(function(aSignedData) {
   let signature = gState.assertion.response.signature;
   console.log(gState.publicKeyHandle, aSignedData, signature);
-  return verifySignature(gState.publicKeyHandle, aSignedData, new Uint8Array(signature));
+  return verifySignature(gState.publicKeyHandle, aSignedData, signature);
 })
 .then(function(aSigVerifyResult) {
   signalCompletion("Signing signature verified: " + aSigVerifyResult);
   gState = {};
 })
 .catch(function(aReason) {
   signalCompletion("Failure: " + aReason);
   gState = {};
--- a/dom/webauthn/tests/mochitest.ini
+++ b/dom/webauthn/tests/mochitest.ini
@@ -1,21 +1,14 @@
 [DEFAULT]
 support-files =
   cbor/*
   pkijs/*
   u2futil.js
-
-[test_webauthn_loopback.html]
-skip-if = !e10s
-scheme = https
-[test_webauthn_no_token.html]
 skip-if = !e10s
 scheme = https
+
+[test_webauthn_loopback.html]
+[test_webauthn_no_token.html]
 [test_webauthn_make_credential.html]
-skip-if = !e10s
-scheme = https
 [test_webauthn_get_assertion.html]
-skip-if = !e10s
-scheme = https
 [test_webauthn_sameorigin.html]
-skip-if = !e10s
-scheme = https
+[test_webauthn_isplatformauthenticatoravailable.html]
--- a/dom/webauthn/tests/test_webauthn_get_assertion.html
+++ b/dom/webauthn/tests/test_webauthn_get_assertion.html
@@ -76,38 +76,38 @@
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectNotAllowedError);
         },
         function () {
           // Test with an invalid credential
           let publicKeyCredentialRequestOptions = {
             challenge: gAssertionChallenge,
-            allowList: [invalidCred]
+            allowCredentials: [invalidCred]
           };
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectTypeError);
         },
         function () {
           // Test with an unknown credential
           let publicKeyCredentialRequestOptions = {
             challenge: gAssertionChallenge,
-            allowList: [unknownCred]
+            allowCredentials: [unknownCred]
           };
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectNotAllowedError);
         },
         function () {
           // Test with an unexpected option and an invalid credential
           let publicKeyCredentialRequestOptions = {
             challenge: gAssertionChallenge,
             unknownValue: "hi",
-            allowList: [invalidCred]
+            allowCredentials: [invalidCred]
           };
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectTypeError);
         }
       ];
 
       var i = 0;
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_isplatformauthenticatoravailable.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for W3C Web Authentication isPlatformAuthenticatorAvailable</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <script type="text/javascript" src="pkijs/common.js"></script>
+  <script type="text/javascript" src="pkijs/asn1.js"></script>
+  <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+  <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<h1>Test for W3C Web Authentication isPlatformAuthenticatorAvailable</h1>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+<script class="testbody" type="text/javascript">
+"use strict";
+
+// Execute the full-scope test
+SimpleTest.waitForExplicitFinish();
+
+// Turn off all tokens. This should result in "not allowed" failures
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn", true],
+                                   ["security.webauth.webauthn_enable_softtoken", true],
+                                   ["security.webauth.webauthn_enable_usbtoken", false]]},
+function() {
+  PublicKeyCredential.isPlatformAuthenticatorAvailable()
+  .then(function(aResult) {
+    // The specification requires this method, if will return false, to wait 10
+    // minutes for anti-fingerprinting reasons. So we really can't test that
+    // in an automated way.
+    ok(aResult, "Should be available!");
+  })
+  .catch(function(aProblem) {
+    is(false, "Problem encountered: " + aProblem);
+  })
+  .then(function() {
+    SimpleTest.finish();
+  })
+});
+
+</script>
+
+</body>
+</html>
--- a/dom/webauthn/tests/test_webauthn_loopback.html
+++ b/dom/webauthn/tests/test_webauthn_loopback.html
@@ -59,34 +59,34 @@ function() {
     ok(aCredInfo.rawId === aCredInfo.rawId, "PublicKeyCredential.RawID is SameObject");
     ok(aCredInfo.response === aCredInfo.response, "PublicKeyCredential.Response is SameObject");
     ok(aCredInfo.response.clientDataJSON === aCredInfo.response.clientDataJSON, "PublicKeyCredential.Response.ClientDataJSON is SameObject");
     ok(aCredInfo.response.attestationObject === aCredInfo.response.attestationObject, "PublicKeyCredential.Response.AttestationObject is SameObject");
 
     let clientData = JSON.parse(buffer2string(aCredInfo.response.clientDataJSON));
     is(clientData.challenge, bytesToBase64UrlSafe(gCredentialChallenge), "Challenge is correct");
     is(clientData.origin, window.location.origin, "Origin is correct");
-    is(clientData.hashAlg, "SHA-256", "Hash algorithm is correct");
+    is(clientData.hashAlgorithm, "SHA-256", "Hash algorithm is correct");
 
     return webAuthnDecodeCBORAttestation(aCredInfo.response.attestationObject)
     .then(function(aAttestationObj) {
       // Make sure the RP ID hash matches what we calculate.
       return crypto.subtle.digest("SHA-256", string2buffer(document.domain))
       .then(function(calculatedRpIdHash) {
         let calcHashStr = bytesToBase64UrlSafe(new Uint8Array(calculatedRpIdHash));
         let providedHashStr = bytesToBase64UrlSafe(new Uint8Array(aAttestationObj.authDataObj.rpIdHash));
 
         is(calcHashStr, providedHashStr,
            "Calculated RP ID hash must match what the browser derived.");
         return Promise.resolve(aAttestationObj);
       });
     })
     .then(function(aAttestationObj) {
       ok(aAttestationObj.authDataObj.flags == (flag_TUP | flag_AT),
-         "User presence and Attestation Object must both be set");
+         "User presence and Attestation Object must be the only flags set");
 
       aCredInfo.clientDataObj = clientData;
       aCredInfo.publicKeyHandle = aAttestationObj.authDataObj.publicKeyHandle;
       aCredInfo.attestationObject = aAttestationObj.authDataObj.attestationAuthData;
       return aCredInfo;
     });
   }
 
@@ -103,22 +103,23 @@ function() {
 
     is(aAssertion.type, "public-key", "Credential type must be public-key")
 
     ok(aAssertion.rawId.byteLength > 0, "Key ID exists");
     is(aAssertion.id, bytesToBase64UrlSafe(new Uint8Array(aAssertion.rawId)), "Encoded Key ID and Raw Key ID match");
 
     ok(aAssertion.response.authenticatorData === aAssertion.response.authenticatorData, "AuthenticatorAssertionResponse.AuthenticatorData is SameObject");
     ok(aAssertion.response.signature === aAssertion.response.signature, "AuthenticatorAssertionResponse.Signature is SameObject");
+    isnot(aAssertion.response.userId, undefined, "AuthenticatorAssertionResponse.UserId is defined")
 
     ok(aAssertion.response.authenticatorData.byteLength > 0, "Authenticator data exists");
     let clientData = JSON.parse(buffer2string(aAssertion.response.clientDataJSON));
     is(clientData.challenge, bytesToBase64UrlSafe(gAssertionChallenge), "Challenge is correct");
     is(clientData.origin, window.location.origin, "Origin is correct");
-    is(clientData.hashAlg, "SHA-256", "Hash algorithm is correct");
+    is(clientData.hashAlgorithm, "SHA-256", "Hash algorithm is correct");
 
     return webAuthnDecodeAuthDataArray(aAssertion.response.authenticatorData)
     .then(function(aAttestation) {
       ok(new Uint8Array(aAttestation.flags) == flag_TUP, "User presence must be the only flag set");
       is(aAttestation.counter.byteLength, 4, "Counter must be 4 bytes");
       return deriveAppAndChallengeParam(window.location.host, aAssertion.response.clientDataJSON, aAttestation)
     })
     .then(function(aParams) {
@@ -131,43 +132,43 @@ function() {
     .then(function(aSignedData) {
       console.log(aPublicKey, aSignedData, aAssertion.response.signature);
       return verifySignature(aPublicKey, aSignedData, aAssertion.response.signature);
     })
   }
 
   function testMakeCredential() {
     let rp = {id: document.domain, name: "none", icon: "none"};
-    let user = {id: "none", name: "none", icon: "none", displayName: "none"};
-    let param = {type: "public-key", algorithm: "ES256"};
+    let user = {name: "none", icon: "none", displayName: "none"};
+    let param = {type: "public-key", alg: "ES256"};
     let makeCredentialOptions = {
       rp: rp,
       user: user,
       challenge: gCredentialChallenge,
-      parameters: [param]
+      pubKeyCredParams: [param]
     };
     credm.create({publicKey: makeCredentialOptions})
     .then(decodeCreatedCredential)
     .then(testMakeDuplicate)
     .catch(function(aReason) {
       ok(false, aReason);
       SimpleTest.finish();
     });
   }
 
   function testMakeDuplicate(aCredInfo) {
     let rp = {id: document.domain, name: "none", icon: "none"};
-    let user = {id: "none", name: "none", icon: "none", displayName: "none"};
-    let param = {type: "public-key", algorithm: "ES256"};
+    let user = {name: "none", icon: "none", displayName: "none"};
+    let param = {type: "public-key", alg: "ES256"};
     let makeCredentialOptions = {
       rp: rp,
       user: user,
       challenge: gCredentialChallenge,
-      parameters: [param],
-      excludeList: [{type: "public-key", id: new Uint8Array(aCredInfo.rawId),
+      pubKeyCredParams: [param],
+      excludeCredentials: [{type: "public-key", id: new Uint8Array(aCredInfo.rawId),
                      transports: ["usb"]}]
     };
     credm.create({publicKey: makeCredentialOptions})
     .then(function() {
       // We should have errored here!
       ok(false, "The excludeList didn't stop a duplicate being created!");
       SimpleTest.finish();
     })
@@ -183,17 +184,17 @@ function() {
       id: new Uint8Array(aCredInfo.rawId),
       transports: ["usb"],
     }
 
     let publicKeyCredentialRequestOptions = {
       challenge: gAssertionChallenge,
       timeout: 5000, // the minimum timeout is actually 15 seconds
       rpId: document.domain,
-      allowList: [newCredential]
+      allowCredentials: [newCredential]
     };
     credm.get({publicKey: publicKeyCredentialRequestOptions})
     .then(function(aAssertion) {
       /* Pass along the pubKey. */
       return checkAssertionAndSigValid(aCredInfo.publicKeyHandle, aAssertion);
     })
     .then(function(aSigVerifyResult) {
       ok(aSigVerifyResult, "Signing signature verified");
--- a/dom/webauthn/tests/test_webauthn_make_credential.html
+++ b/dom/webauthn/tests/test_webauthn_make_credential.html
@@ -56,46 +56,46 @@
       isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
 
       let credm = navigator.credentials;
 
       let gCredentialChallenge = new Uint8Array(16);
       window.crypto.getRandomValues(gCredentialChallenge);
 
       let rp = {id: document.domain, name: "none", icon: "none"};
-      let user = {id: "none", name: "none", icon: "none", displayName: "none"};
-      let param = {type: "public-key", algorithm: "es256"};
-      let unsupportedParam = {type: "public-key", algorithm: "3DES"};
-      let badParam = {type: "SimplePassword", algorithm: "MaxLength=2"};
+      let user = {id: new Uint8Array(64), name: "none", icon: "none", displayName: "none"};
+      let param = {type: "public-key", alg: "es256"};
+      let unsupportedParam = {type: "public-key", alg: "3DES"};
+      let badParam = {type: "SimplePassword", alg: "MaxLength=2"};
 
       var testFuncs = [
         // Test basic good call
         function() {
           let makeCredentialOptions = {
-            rp: rp, user: user, challenge: gCredentialChallenge, parameters: [param]
+            rp: rp, user: user, challenge: gCredentialChallenge, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsGood)
                       .catch(arrivingHereIsBad);
         },
 
         // Test empty account
         function() {
           let makeCredentialOptions = {
-            challenge: gCredentialChallenge, parameters: [param]
+            challenge: gCredentialChallenge, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectTypeError);
         },
 
         // Test without a parameter
         function() {
           let makeCredentialOptions = {
-            rp: rp, user: user, challenge: gCredentialChallenge, parameters: []
+            rp: rp, user: user, challenge: gCredentialChallenge, pubKeyCredParams: []
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectNotSupportedError);
         },
 
         // Test without a parameter array at all
         function() {
@@ -105,134 +105,161 @@
           return credm.create({publicKey: makeCredentialOptions})
                .then(arrivingHereIsBad)
                .catch(expectTypeError);
         },
 
         // Test with an unsupported parameter
         function() {
           let makeCredentialOptions = {
-            rp: rp, user: user, challenge: gCredentialChallenge, parameters: [unsupportedParam]
+            rp: rp, user: user, challenge: gCredentialChallenge, pubKeyCredParams: [unsupportedParam]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectNotSupportedError);
         },
 
         // Test with an unsupported parameter and a good one
         function() {
           let makeCredentialOptions = {
             rp: rp, user: user, challenge: gCredentialChallenge,
-            parameters: [param, unsupportedParam]
+            pubKeyCredParams: [param, unsupportedParam]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsGood)
                       .catch(arrivingHereIsBad);
         },
 
         // Test with a bad parameter
         function() {
           let makeCredentialOptions = {
-            rp: rp, user: user, challenge: gCredentialChallenge, parameters: [badParam]
+            rp: rp, user: user, challenge: gCredentialChallenge, pubKeyCredParams: [badParam]
           };
           return credm.create({publicKey: makeCredentialOptions})
                .then(arrivingHereIsBad)
                .catch(expectTypeError);
         },
 
         // Test with an unsupported parameter, and a bad one
         function() {
           let makeCredentialOptions = {
             rp: rp, user: user, challenge: gCredentialChallenge,
-            parameters: [unsupportedParam, badParam]
+            pubKeyCredParams: [unsupportedParam, badParam]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectTypeError);
         },
 
         // Test with an unsupported parameter, a bad one, and a good one. This
         // should still fail, as anything with a badParam should fail.
         function() {
           let makeCredentialOptions = {
             rp: rp, user: user, challenge: gCredentialChallenge,
-            parameters: [param, unsupportedParam, badParam]
+            pubKeyCredParams: [param, unsupportedParam, badParam]
           };
           return credm.create({publicKey: makeCredentialOptions})
                .then(arrivingHereIsBad)
                .catch(expectTypeError);
         },
 
         // Test without a challenge
         function() {
           let makeCredentialOptions = {
-            rp: rp, user: user, parameters: [param]
+            rp: rp, user: user, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectTypeError);
         },
 
         // Test with an invalid challenge
         function() {
           let makeCredentialOptions = {
             rp: rp, user: user, challenge: "begone, thou ill-fitting moist glove!",
-            parameters: [unsupportedParam]
+            pubKeyCredParams: [unsupportedParam]
           };
           return credm.create({publicKey: makeCredentialOptions})
                .then(arrivingHereIsBad)
                .catch(expectTypeError);
         },
 
-        // Test with duplicate parameters
+        // Test with duplicate pubKeyCredParams
         function() {
           let makeCredentialOptions = {
             rp: rp, user: user, challenge: gCredentialChallenge,
-            parameters: [param, param, param]
+            pubKeyCredParams: [param, param, param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsGood)
                       .catch(arrivingHereIsBad);
         },
 
         // Test with missing rp
         function() {
           let makeCredentialOptions = {
-            user: user, challenge: gCredentialChallenge, parameters: [param]
+            user: user, challenge: gCredentialChallenge, pubKeyCredParams: [param]
+          };
+          return credm.create({publicKey: makeCredentialOptions})
+                      .then(arrivingHereIsBad)
+                      .catch(expectTypeError);
+        },
+
+        // Test with incorrect user ID type
+        function() {
+          let invalidType = user;
+          invalidType.id = "a string, which is not a buffer";
+          let makeCredentialOptions = {
+            user: invalidType, challenge: gCredentialChallenge, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectTypeError);
         },
 
         // Test with missing user
         function() {
           let makeCredentialOptions = {
-            rp: rp, challenge: gCredentialChallenge, parameters: [param]
+            rp: rp, challenge: gCredentialChallenge, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectTypeError);
         },
 
         // Test a complete account
         function() {
           let completeRP = {id: document.domain, name: "Foxxy Name",
                             icon: "https://example.com/fox.svg"};
-          let completeUser = {id: "foxes_are_the_best@example.com",
+          let completeUser = {id: string2buffer("foxes_are_the_best@example.com"),
                               name: "Fox F. Foxington",
                               icon: "https://example.com/fox.svg",
                               displayName: "Foxxy V"};
           let makeCredentialOptions = {
             rp: completeRP, user: completeUser, challenge: gCredentialChallenge,
-            parameters: [param]
+            pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsGood)
                       .catch(arrivingHereIsBad);
+        },
+
+        // Test with too-large user ID buffer
+        function() {
+          let hugeUser = {id: new Uint8Array(65),
+                              name: "Fox F. Foxington",
+                              icon: "https://example.com/fox.svg",
+                              displayName: "Foxxy V"};
+          let makeCredentialOptions = {
+            rp: rp, user: hugeUser, challenge: gCredentialChallenge,
+            pubKeyCredParams: [param]
+          };
+          return credm.create({publicKey: makeCredentialOptions})
+                      .then(arrivingHereIsBad)
+                      .catch(expectTypeError);
         }];
 
       var i = 0;
       var runNextTest = () => {
         if (i == testFuncs.length) {
           SimpleTest.finish();
           return;
         }
--- a/dom/webauthn/tests/test_webauthn_no_token.html
+++ b/dom/webauthn/tests/test_webauthn_no_token.html
@@ -39,20 +39,20 @@ function() {
   window.crypto.getRandomValues(assertionChallenge);
   let credentialId = new Uint8Array(128);
   window.crypto.getRandomValues(credentialId);
 
   testMakeCredential();
 
   function testMakeCredential() {
     let rp = {id: document.domain, name: "none", icon: "none"};
-    let user = {id: "none", name: "none", icon: "none", displayName: "none"};
-    let param = {type: "public-key", algorithm: "es256"};
+    let user = {name: "none", icon: "none", displayName: "none"};
+    let param = {type: "public-key", alg: "es256"};
     let makeCredentialOptions = {
-      rp: rp, user: user, challenge: credentialChallenge, parameters: [param]
+      rp: rp, user: user, challenge: credentialChallenge, pubKeyCredParams: [param]
     };
     credm.create({publicKey: makeCredentialOptions})
     .then(function(aResult) {
       ok(false, "Should have failed.");
       testAssertion();
     })
     .catch(function(aReason) {
       ok(aReason.toString().startsWith("NotAllowedError"), aReason);
@@ -65,17 +65,17 @@ function() {
       type: "public-key",
       id: credentialId,
       transports: ["usb"],
     }
     let publicKeyCredentialRequestOptions = {
       challenge: assertionChallenge,
       timeout: 5000, // the minimum timeout is actually 15 seconds
       rpId: document.domain,
-      allowList: [newCredential]
+      allowCredentials: [newCredential]
     };
     credm.get({publicKey: publicKeyCredentialRequestOptions})
     .then(function(aResult) {
       ok(false, "Should have failed.");
       SimpleTest.finish();
     })
     .catch(function(aReason) {
       ok(aReason.toString().startsWith("NotAllowedError"), aReason);
--- a/dom/webauthn/tests/test_webauthn_sameorigin.html
+++ b/dom/webauthn/tests/test_webauthn_sameorigin.html
@@ -55,203 +55,203 @@
       isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
       isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
 
       let credm = navigator.credentials;
 
       let chall = new Uint8Array(16);
       window.crypto.getRandomValues(chall);
 
-      let user = {id: "none", name: "none", icon: "none", displayName: "none"};
-      let param = {type: "public-key", algorithm: "Es256"};
+      let user = {id: new Uint8Array(16), name: "none", icon: "none", displayName: "none"};
+      let param = {type: "public-key", alg: "Es256"};
 
       var testFuncs = [
         function() {
           // Test basic good call
           let rp = {id: document.domain};
           let makeCredentialOptions = {
-            rp: rp, user: user, challenge: chall, parameters: [param]
+            rp: rp, user: user, challenge: chall, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(keepThisPublicKeyCredential("basic"))
                       .then(arrivingHereIsGood)
                       .catch(arrivingHereIsBad);
         },
         function() {
           // Test rp.id being unset
           let makeCredentialOptions = {
-            rp: {}, user: user, challenge: chall, parameters: [param]
+            rp: {}, user: user, challenge: chall, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsGood)
                       .catch(arrivingHereIsBad);
         },
         function() {
           // Test this origin with optional fields
           let rp = {id: "user:pass@" + document.domain + ":8888"};
           let makeCredentialOptions = {
-            rp: rp, user: user, challenge: chall, parameters: [param]
+            rp: rp, user: user, challenge: chall, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function() {
           // Test blank rp.id
           let rp = {id: ""};
           let makeCredentialOptions = {
-            rp: rp, user: user, challenge: chall, parameters: [param]
+            rp: rp, user: user, challenge: chall, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function() {
           // Test subdomain of this origin
           let rp = {id: "subdomain." + document.domain};
           let makeCredentialOptions = {
-            rp: rp, user: user, challenge: chall, parameters: [param]
+            rp: rp, user: user, challenge: chall, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function() {
           // Test the same origin
           let rp = {id: "example.com"};
           let makeCredentialOptions = {
-            rp: rp, user: user, challenge: chall, parameters: [param]
+            rp: rp, user: user, challenge: chall, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsGood)
                       .catch(arrivingHereIsBad);
         },
         function() {
           // Test the eTLD
           let rp = {id: "com"};
           let makeCredentialOptions = {
-            rp: rp, user: user, challenge: chall, parameters: [param]
+            rp: rp, user: user, challenge: chall, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test a different domain within the same TLD
           let rp = {id: "alt.test"};
           let makeCredentialOptions = {
-            rp: rp, user: user, challenge: chall, parameters: [param]
+            rp: rp, user: user, challenge: chall, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test basic good call
           let publicKeyCredentialRequestOptions = {
             challenge: chall,
             rpId: document.domain,
-            allowList: [gTrackedCredential["basic"]]
+            allowCredentials: [gTrackedCredential["basic"]]
           };
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsGood)
                       .catch(arrivingHereIsBad);
         },
         function () {
           // Test rpId being unset
           let publicKeyCredentialRequestOptions = {
             challenge: chall,
-            allowList: [gTrackedCredential["basic"]]
+            allowCredentials: [gTrackedCredential["basic"]]
           };
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsGood)
                       .catch(arrivingHereIsBad);
         },
         function () {
           // Test this origin with optional fields
           let publicKeyCredentialRequestOptions = {
             challenge: chall,
             rpId: "user:pass@" + document.origin + ":8888",
-            allowList: [gTrackedCredential["basic"]]
+            allowCredentials: [gTrackedCredential["basic"]]
           };
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test blank rpId
           let publicKeyCredentialRequestOptions = {
             challenge: chall,
             rpId: "",
-            allowList: [gTrackedCredential["basic"]]
+            allowCredentials: [gTrackedCredential["basic"]]
           };
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test subdomain of this origin
           let publicKeyCredentialRequestOptions = {
             challenge: chall,
             rpId: "subdomain." + document.domain,
-            allowList: [gTrackedCredential["basic"]]
+            allowCredentials: [gTrackedCredential["basic"]]
           };
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test the same origin
           let publicKeyCredentialRequestOptions = {
             challenge: chall,
             rpId: "example.com",
-            allowList: [gTrackedCredential["basic"]]
+            allowCredentials: [gTrackedCredential["basic"]]
           };
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsGood)
                       .catch(arrivingHereIsBad);
         },
         function() {
           // Test the eTLD
           let publicKeyCredentialRequestOptions = {
             challenge: chall,
             rpId: "com",
-            allowList: [gTrackedCredential["basic"]]
+            allowCredentials: [gTrackedCredential["basic"]]
           };
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test a different domain within the same TLD
           let publicKeyCredentialRequestOptions = {
             challenge: chall,
             rpId: "alt.test",
-            allowList: [gTrackedCredential["basic"]]
+            allowCredentials: [gTrackedCredential["basic"]]
           };
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test basic good Create call but using an origin (Bug 1380421)
           let rp = {id: window.origin};
           let makeCredentialOptions = {
-            rp: rp, user: user, challenge: chall, parameters: [param]
+            rp: rp, user: user, challenge: chall, pubKeyCredParams: [param]
           };
           return credm.create({publicKey: makeCredentialOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         },
         function () {
           // Test basic good Get call but using an origin (Bug 1380421)
           let publicKeyCredentialRequestOptions = {
             challenge: chall,
             rpId: window.origin,
-            allowList: [gTrackedCredential["basic"]]
+            allowCredentials: [gTrackedCredential["basic"]]
           };
           return credm.get({publicKey: publicKeyCredentialRequestOptions})
                       .then(arrivingHereIsBad)
                       .catch(expectSecurityError);
         }
       ];
       var i = 0;
       var runNextTest = () => {
--- a/dom/webauthn/tests/u2futil.js
+++ b/dom/webauthn/tests/u2futil.js
@@ -1,13 +1,14 @@
 // Used by local_addTest() / local_completeTest()
 var _countCompletions = 0;
 var _expectedCompletions = 0;
 
 const flag_TUP = 0x01;
+const flag_UV = 0x04;
 const flag_AT = 0x40;
 
 function handleEventMessage(event) {
   if ("test" in event.data) {
     let summary = event.data.test + ": " + event.data.msg;
     log(event.data.status + ": " + summary);
     ok(event.data.status, summary);
   } else if ("done" in event.data) {
--- a/dom/webidl/CredentialManagement.webidl
+++ b/dom/webidl/CredentialManagement.webidl
@@ -19,10 +19,10 @@ interface CredentialsContainer {
   Promise<Credential?> create(optional CredentialCreationOptions options);
 };
 
 dictionary CredentialRequestOptions {
   PublicKeyCredentialRequestOptions publicKey;
 };
 
 dictionary CredentialCreationOptions {
-  MakeCredentialOptions publicKey;
+  MakePublicKeyCredentialOptions publicKey;
 };
--- a/dom/webidl/FrameLoader.webidl
+++ b/dom/webidl/FrameLoader.webidl
@@ -157,16 +157,22 @@ interface FrameLoader {
 
   /**
    * Close the window through the ownerElement.
    */
   [Throws]
   void requestFrameLoaderClose();
 
   /**
+   * Force a remote browser to recompute its dimension and screen position.
+   */
+  [Throws]
+  void requestUpdatePosition();
+
+  /**
    * Print the current document.
    *
    * @param aOuterWindowID the ID of the outer window to print
    * @param aPrintSettings optional print settings to use; printSilent can be
    *                       set to prevent prompting.
    * @param aProgressListener optional print progress listener.
    */
   [Throws]
--- a/dom/webidl/WebAuthentication.webidl
+++ b/dom/webidl/WebAuthentication.webidl
@@ -6,106 +6,124 @@
  * The origin of this IDL file is
  * https://www.w3.org/TR/webauthn/
  */
 
 /***** Interfaces to Data *****/
 
 [SecureContext, Pref="security.webauth.webauthn"]
 interface PublicKeyCredential : Credential {
-    [SameObject] readonly attribute ArrayBuffer           rawId;
-    [SameObject] readonly attribute AuthenticatorResponse response;
+    [SameObject] readonly attribute ArrayBuffer              rawId;
+    [SameObject] readonly attribute AuthenticatorResponse    response;
     // Extensions are not supported yet.
-    // [SameObject] readonly attribute AuthenticationExtensions clientExtensionResults;
+    // [SameObject] readonly attribute AuthenticationExtensions clientExtensionResults; // Add in Bug 1406458
+};
+
+[SecureContext]
+partial interface PublicKeyCredential {
+    static Promise<boolean> isPlatformAuthenticatorAvailable();
 };
 
 [SecureContext, Pref="security.webauth.webauthn"]
 interface AuthenticatorResponse {
     [SameObject] readonly attribute ArrayBuffer clientDataJSON;
 };
 
 [SecureContext, Pref="security.webauth.webauthn"]
 interface AuthenticatorAttestationResponse : AuthenticatorResponse {
     [SameObject] readonly attribute ArrayBuffer attestationObject;
 };
 
+[SecureContext, Pref="security.webauth.webauthn"]
+interface AuthenticatorAssertionResponse : AuthenticatorResponse {
+    [SameObject] readonly attribute ArrayBuffer      authenticatorData;
+    [SameObject] readonly attribute ArrayBuffer      signature;
+    readonly attribute DOMString                     userId;
+};
+
 dictionary PublicKeyCredentialParameters {
     required PublicKeyCredentialType  type;
-    required WebAuthnAlgorithmID algorithm; // NOTE: changed from AllgorithmIdentifier because typedef (object or DOMString) not serializable
+    required WebAuthnAlgorithmID      alg; // Switch to COSE in Bug 1381190
+};
+
+dictionary MakePublicKeyCredentialOptions {
+    required PublicKeyCredentialRpEntity   rp;
+    required PublicKeyCredentialUserEntity user;
+
+    required BufferSource                            challenge;
+    required sequence<PublicKeyCredentialParameters> pubKeyCredParams;
+
+    unsigned long                                timeout;
+    sequence<PublicKeyCredentialDescriptor>      excludeCredentials = [];
+    AuthenticatorSelectionCriteria               authenticatorSelection;
+    // Extensions are not supported yet.
+    // AuthenticationExtensions                  extensions; // Add in Bug 1406458
+};
+
+dictionary PublicKeyCredentialEntity {
+    DOMString      name;
+    USVString      icon;
+};
+
+dictionary PublicKeyCredentialRpEntity : PublicKeyCredentialEntity {
+    DOMString      id;
 };
 
 dictionary PublicKeyCredentialUserEntity : PublicKeyCredentialEntity {
-    DOMString displayName;
-};
-
-dictionary MakeCredentialOptions {
-    required PublicKeyCredentialEntity rp;
-    required PublicKeyCredentialUserEntity user;
-
-    required BufferSource                         challenge;
-    required sequence<PublicKeyCredentialParameters> parameters;
-
-    unsigned long                        timeout;
-    sequence<PublicKeyCredentialDescriptor> excludeList;
-    AuthenticatorSelectionCriteria       authenticatorSelection;
-    // Extensions are not supported yet.
-    // AuthenticationExtensions             extensions;
-};
-
-dictionary PublicKeyCredentialEntity {
-    DOMString id;
-    DOMString name;
-    USVString icon;
+    BufferSource   id;
+    DOMString      displayName;
 };
 
 dictionary AuthenticatorSelectionCriteria {
-    Attachment    attachment;
-    boolean       requireResidentKey = false;
+    AuthenticatorAttachment      authenticatorAttachment;
+    boolean                      requireResidentKey = false;
+    boolean                      requireUserVerification = false;
 };
 
-enum Attachment {
-    "platform",
-    "cross-platform"
+enum AuthenticatorAttachment {
+    "platform",       // Platform attachment
+    "cross-platform"  // Cross-platform attachment
 };
 
 dictionary PublicKeyCredentialRequestOptions {
     required BufferSource                challenge;
     unsigned long                        timeout;
     USVString                            rpId;
-    sequence<PublicKeyCredentialDescriptor> allowList = [];
+    sequence<PublicKeyCredentialDescriptor> allowCredentials = [];
     // Extensions are not supported yet.
-    // AuthenticationExtensions             extensions;
+    // AuthenticationExtensions             extensions; // Add in Bug 1406458
 };
 
+typedef record<DOMString, any>       AuthenticationExtensions;
+
 dictionary CollectedClientData {
     required DOMString           challenge;
     required DOMString           origin;
-    required DOMString           hashAlg;
-    DOMString                    tokenBinding;
+    required DOMString           hashAlgorithm;
+    DOMString                    tokenBindingId;
     // Extensions are not supported yet.
-    // AuthenticationExtensions     clientExtensions;
-    // AuthenticationExtensions     authenticatorExtensions;
+    // AuthenticationExtensions     clientExtensions; // Add in Bug 1406458
+    // AuthenticationExtensions     authenticatorExtensions; // Add in Bug 1406458
 };
 
 enum PublicKeyCredentialType {
     "public-key"
 };
 
 dictionary PublicKeyCredentialDescriptor {
     required PublicKeyCredentialType type;
     required BufferSource id;
-    sequence<WebAuthnTransport>   transports;
+    sequence<AuthenticatorTransport>   transports;
 };
 
-typedef (boolean or DOMString) WebAuthnAlgorithmID; // Fix when upstream there's a definition of how to serialize AlgorithmIdentifier
-
-[SecureContext, Pref="security.webauth.webauthn"]
-interface AuthenticatorAssertionResponse : AuthenticatorResponse {
-    [SameObject] readonly attribute ArrayBuffer      authenticatorData;
-    [SameObject] readonly attribute ArrayBuffer      signature;
-};
-
-// Renamed from "Transport" to avoid a collision with U2F
-enum WebAuthnTransport {
+enum AuthenticatorTransport {
     "usb",
     "nfc",
     "ble"
 };
+
+typedef long COSEAlgorithmIdentifier;
+
+typedef sequence<AAGUID>      AuthenticatorSelectionList;
+
+typedef BufferSource      AAGUID;
+
+typedef (boolean or DOMString) WebAuthnAlgorithmID; // Switch to COSE in Bug 1381190
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -282,20 +282,20 @@ HTMLEditor::SetInlinePropertyOnTextNode(
     }
   } else if (IsTextPropertySetByContent(&aText, &aProperty, aAttribute,
                                         &aValue)) {
     return NS_OK;
   }
 
   // Do we need to split the text node?
   ErrorResult rv;
-  RefPtr<Text> text = &aText;
+  nsCOMPtr<nsIContent> text = &aText;
   if (uint32_t(aEndOffset) != aText.Length()) {
     // We need to split off back of text node
-    text = SplitNode(aText, aEndOffset, rv)->GetAsText();
+    text = SplitNode(*text, aEndOffset, rv);
     NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
   }
 
   if (aStartOffset) {
     // We need to split off front of text node
     SplitNode(*text, aStartOffset, rv);
     NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
   }
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/crashtests/1364133.html
@@ -0,0 +1,42 @@
+<html>
+  <head>
+    <script>
+      var tr = document.createElement('tr');
+      document.documentElement.appendChild(tr);
+
+      var a1 = document.createElement('a');
+      document.documentElement.appendChild(a1);
+      var a2 = document.createElement('a');
+      tr.appendChild(a2);
+
+      var a3 = document.createElement('a');
+      document.documentElement.appendChild(a3);
+
+      var a4 = document.createElement('a');
+      document.documentElement.appendChild(a4);
+
+      var a5 = document.createElement('a');
+      a1.appendChild(a5);
+
+      var input = document.createElement('input');
+      document.documentElement.appendChild(input);
+
+      a3.contentEditable = true;
+      a5.innerText = "xx";
+      a4.outerHTML = "";
+      input.select();
+
+      document.replaceChild(document.documentElement, document.documentElement);
+      window.find("x", false, false, false, false, false, false);
+
+      var range = document.createRange();
+      range.setStart(a1, 1);
+      window.getSelection().addRange(range);
+
+      document.designMode = "on";
+
+      range.selectNode(a2);
+      document.execCommand("forecolor", false, "-moz-default-background-color");
+    </script>
+  </head>
+</html>
--- a/editor/libeditor/crashtests/crashtests.list
+++ b/editor/libeditor/crashtests/crashtests.list
@@ -71,16 +71,17 @@ load 1264921.html
 load 1272490.html
 load 1317704.html
 load 1317718.html
 load 1324505.html
 needs-focus load 1343918.html
 load 1345015.html
 load 1348851.html
 load 1350772.html
+load 1364133.html
 load 1366176.html
 load 1375131.html
 load 1381541.html
 load 1383755.html
 load 1388075.html
 load 1402469.html
 load 1402904.html
 load 1405747.html
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -550,17 +550,17 @@ private:
   DECL_OVERRIDE_PREF(Live, "layers.advanced.caret-layers",            LayersAllowCaretLayers, gfxPrefs::OverrideBase_WebRender());
   DECL_OVERRIDE_PREF(Live, "layers.advanced.columnRule-layers",       LayersAllowColumnRuleLayers, gfxPrefs::OverrideBase_WebRender());
   DECL_OVERRIDE_PREF(Live, "layers.advanced.displaybuttonborder-layers", LayersAllowDisplayButtonBorder, gfxPrefs::OverrideBase_WebRender());
   DECL_OVERRIDE_PREF(Live, "layers.advanced.filter-layers",           LayersAllowFilterLayers, gfxPrefs::OverrideBase_WebRender());
   DECL_OVERRIDE_PREF(Live, "layers.advanced.image-layers",            LayersAllowImageLayers, gfxPrefs::OverrideBase_WebRender());
   DECL_OVERRIDE_PREF(Live, "layers.advanced.outline-layers",          LayersAllowOutlineLayers, gfxPrefs::OverrideBase_WebRender());
   DECL_OVERRIDE_PREF(Live, "layers.advanced.solid-color",             LayersAllowSolidColorLayers, gfxPrefs::OverrideBase_WebRender());
   DECL_OVERRIDE_PREF(Live, "layers.advanced.table",                   LayersAllowTable, gfxPrefs::OverrideBase_WebRender());
-  DECL_OVERRIDE_PREF(Live, "layers.advanced.text-layers",             LayersAllowTextLayers, gfxPrefs::OverrideBase_WebRendest());
+  DECL_OVERRIDE_PREF(Live, "layers.advanced.text-layers",             LayersAllowTextLayers, gfxPrefs::OverrideBase_WebRender());
   DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled",     LayersAMDSwitchableGfxEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled",         AsyncPanZoomEnabledDoNotUseDirectly, bool, true);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.separate-event-thread", AsyncPanZoomSeparateEventThread, bool, false);
   DECL_GFX_PREF(Live, "layers.bench.enabled",                  LayersBenchEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.bufferrotation.enabled",         BufferRotationEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.child-process-shutdown",         ChildProcessShutdown, bool, true);
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
   // If MOZ_GFX_OPTIMIZE_MOBILE is defined, we force component alpha off
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/gfx/Swizzle.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/Unused.h"
 #include "mozilla/Vector.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIClipboardHelper.h"
 #include "nsIFile.h"
 #include "nsIGfxInfo.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsRegion.h"
@@ -1043,17 +1044,17 @@ EncodeSourceSurfaceInternal(SourceSurfac
       }
     }
   }
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(!imgData.empty(), NS_ERROR_FAILURE);
 
   if (aBinaryOrData == gfxUtils::eBinaryEncode) {
     if (aFile) {
-      fwrite(imgData.begin(), 1, imgSize, aFile);
+      Unused << fwrite(imgData.begin(), 1, imgSize, aFile);
     }
     return NS_OK;
   }
 
   // base 64, result will be null-terminated
   nsCString encodedImg;
   rv = Base64Encode(Substring(imgData.begin(), imgSize), encodedImg);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/ipc/chromium/moz.build
+++ b/ipc/chromium/moz.build
@@ -53,20 +53,16 @@ if os_win:
         'src/base/time_win.cc',
         'src/base/waitable_event_win.cc',
         'src/base/win_util.cc',
         'src/chrome/common/ipc_channel_win.cc',
         'src/chrome/common/process_watcher_win.cc',
         'src/chrome/common/transport_dib_win.cc',
     ]
 
-    EXPORTS.base += [
-        'src/base/child_privileges.h',
-    ]
-
 elif not CONFIG['MOZ_SYSTEM_LIBEVENT']:
     DIRS += ['src/third_party']
 
 if os_posix:
     UNIFIED_SOURCES += [
         'src/base/condition_variable_posix.cc',
         'src/base/file_descriptor_shuffle.cc',
         'src/base/file_util_posix.cc',
deleted file mode 100644
--- a/ipc/chromium/src/base/child_privileges.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef BASE_CHILD_PRIVILEGS_H_
-#define BASE_CHILD_PRIVILEGS_H_
-
-namespace base {
-
-enum ChildPrivileges {
-  PRIVILEGES_DEFAULT,
-  PRIVILEGES_UNPRIVILEGED,
-  PRIVILEGES_INHERIT,
-  // PRIVILEGES_DEFAULT plus file read permissions, used for file content process.
-  PRIVILEGES_FILEREAD,
-  PRIVILEGES_LAST
-};
-
-} // namespace base
-
-#endif  // BASE_CHILD_PRIVILEGS_H_
--- a/ipc/chromium/src/base/process_util.h
+++ b/ipc/chromium/src/base/process_util.h
@@ -31,24 +31,25 @@
 #include <string>
 #include <vector>
 #include <stdio.h>
 #include <stdlib.h>
 #ifndef OS_WIN
 #include <unistd.h>
 #endif
 
-#include "base/child_privileges.h"
 #include "base/command_line.h"
 #include "base/process.h"
 
 #if defined(OS_POSIX)
 #include "base/file_descriptor_shuffle.h"
 #endif
 
+#include "mozilla/UniquePtr.h"
+
 #if defined(OS_MACOSX)
 struct kinfo_proc;
 #endif
 
 namespace base {
 
 // These can be used in a 32-bit bitmask.
 enum ProcessArchitecture {
@@ -157,30 +158,32 @@ typedef std::vector<std::pair<int, int> 
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                bool wait, ProcessHandle* process_handle);
 
 typedef std::map<std::string, std::string> environment_map;
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                const environment_map& env_vars_to_set,
-               ChildPrivileges privs,
                bool wait, ProcessHandle* process_handle,
                ProcessArchitecture arch=GetCurrentProcessArchitecture());
-bool LaunchApp(const std::vector<std::string>& argv,
-               const file_handle_mapping_vector& fds_to_remap,
-               const environment_map& env_vars_to_set,
-               bool wait, ProcessHandle* process_handle,
-               ProcessArchitecture arch=GetCurrentProcessArchitecture());
+
+// Deleter for the array of strings allocated within BuildEnvironmentArray.
+struct FreeEnvVarsArray
+{
+  void operator()(char** array);
+};
+
+typedef mozilla::UniquePtr<char*[], FreeEnvVarsArray> EnvironmentArray;
+
+// Merge an environment map with the current environment.
+// Existing variables are overwritten by env_vars_to_set.
+EnvironmentArray BuildEnvironmentArray(const environment_map& env_vars_to_set);
 #endif
 
-// Adjust the privileges of this process to match |privs|.  Only
-// returns if privileges were successfully adjusted.
-void SetCurrentProcessPrivileges(ChildPrivileges privs);
-
 // Executes the application specified by cl. This function delegates to one
 // of the above two platform-specific functions.
 bool LaunchApp(const CommandLine& cl,
                bool wait, bool start_hidden, ProcessHandle* process_handle);
 
 // Used to filter processes by process ID.
 class ProcessFilter {
  public:
--- a/ipc/chromium/src/base/process_util_bsd.cc
+++ b/ipc/chromium/src/base/process_util_bsd.cc
@@ -9,103 +9,54 @@
 #include "base/process_util.h"
 
 #include <fcntl.h>
 #include <spawn.h>
 #include <sys/wait.h>
 
 #include <string>
 
-#include "nspr.h"
 #include "base/eintr_wrapper.h"
 
 namespace {
 
 static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
 
 }  // namespace
 
 namespace base {
 
-void FreeEnvVarsArray(char* array[], int length)
-{
-  for (int i = 0; i < length; i++) {
-    free(array[i]);
-  }
-  delete[] array;
-}
-
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                bool wait, ProcessHandle* process_handle) {
   return LaunchApp(argv, fds_to_remap, environment_map(),
                    wait, process_handle);
 }
 
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                const environment_map& env_vars_to_set,
                bool wait, ProcessHandle* process_handle,
                ProcessArchitecture arch) {
-  return LaunchApp(argv, fds_to_remap, env_vars_to_set,
-                   PRIVILEGES_INHERIT,
-                   wait, process_handle);
-}
-
-bool LaunchApp(const std::vector<std::string>& argv,
-               const file_handle_mapping_vector& fds_to_remap,
-               const environment_map& env_vars_to_set,
-               ChildPrivileges privs,
-               bool wait, ProcessHandle* process_handle,
-               ProcessArchitecture arch) {
   bool retval = true;
 
   char* argv_copy[argv.size() + 1];
   for (size_t i = 0; i < argv.size(); i++) {
     argv_copy[i] = const_cast<char*>(argv[i].c_str());
   }
   argv_copy[argv.size()] = NULL;
 
   // Make sure we don't leak any FDs to the child process by marking all FDs
   // as close-on-exec.
   SetAllFDsToCloseOnExec();
 
-  // Copy environment to a new char array and add the variables
-  // in env_vars_to_set.
-  // Existing variables are overwritten by env_vars_to_set.
-  int pos = 0;
-  environment_map combined_env_vars = env_vars_to_set;
-  char **environ = PR_DuplicateEnvironment();
-  while(environ[pos] != NULL) {
-    std::string varString = environ[pos];
-    std::string varName = varString.substr(0, varString.find_first_of('='));
-    std::string varValue = varString.substr(varString.find_first_of('=') + 1);
-    if (combined_env_vars.find(varName) == combined_env_vars.end()) {
-      combined_env_vars[varName] = varValue;
-    }
-    PR_Free(environ[pos++]); // PR_DuplicateEnvironment() uses PR_Malloc().
-  }
-  PR_Free(environ); // PR_DuplicateEnvironment() uses PR_Malloc().
-  int varsLen = combined_env_vars.size() + 1;
-
-  char** vars = new char*[varsLen];
-  int i = 0;
-  for (environment_map::const_iterator it = combined_env_vars.begin();
-       it != combined_env_vars.end(); ++it) {
-    std::string entry(it->first);
-    entry += "=";
-    entry += it->second;
-    vars[i] = strdup(entry.c_str());
-    i++;
-  }
-  vars[i] = NULL;
+  EnvironmentArray vars = BuildEnvironmentArray(env_vars_to_set);
 
   posix_spawn_file_actions_t file_actions;
   if (posix_spawn_file_actions_init(&file_actions) != 0) {
-    FreeEnvVarsArray(vars, varsLen);
     return false;
   }
 
   // Turn fds_to_remap array into a set of dup2 calls.
   for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
        it != fds_to_remap.end();
        ++it) {
     int src_fd = it->first;
@@ -114,31 +65,28 @@ bool LaunchApp(const std::vector<std::st
     if (src_fd == dest_fd) {
       int flags = fcntl(src_fd, F_GETFD);
       if (flags != -1) {
         fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC);
       }
     } else {
       if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0) {
         posix_spawn_file_actions_destroy(&file_actions);
-        FreeEnvVarsArray(vars, varsLen);
         return false;
       }
     }
   }
 
   pid_t pid = 0;
   int spawn_succeeded = (posix_spawnp(&pid,
                                       argv_copy[0],
                                       &file_actions,
                                       NULL,
                                       argv_copy,
-                                      vars) == 0);
-
-  FreeEnvVarsArray(vars, varsLen);
+                                      vars.get()) == 0);
 
   posix_spawn_file_actions_destroy(&file_actions);
 
   bool process_handle_valid = pid > 0;
   if (!spawn_succeeded || !process_handle_valid) {
     retval = false;
   } else {
     gProcessLog.print("==> process %d launched child process %d\n",
@@ -155,13 +103,9 @@ bool LaunchApp(const std::vector<std::st
 
 bool LaunchApp(const CommandLine& cl,
                bool wait, bool start_hidden, ProcessHandle* process_handle) {
   // TODO(playmobil): Do we need to respect the start_hidden flag?
   file_handle_mapping_vector no_files;
   return LaunchApp(cl.argv(), no_files, wait, process_handle);
 }
 
-void SetCurrentProcessPrivileges(ChildPrivileges privs) {
-
-}
-
 }  // namespace base
--- a/ipc/chromium/src/base/process_util_linux.cc
+++ b/ipc/chromium/src/base/process_util_linux.cc
@@ -1,165 +1,53 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "base/process_util.h"
 
-#include <ctype.h>
-#include <fcntl.h>
-#include <memory>
-#include <unistd.h>
 #include <string>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include "base/eintr_wrapper.h"
-#include "base/file_util.h"
 #include "base/logging.h"
-#include "base/string_util.h"
-#include "nsLiteralString.h"
+#include "mozilla/Move.h"
 #include "mozilla/UniquePtr.h"
 
-#include "prenv.h"
-
-/*
- * We fall back to an arbitrary UID. This is generally the UID for user
- * `nobody', albeit it is not always the case.
- */
-# define CHILD_UNPRIVILEGED_UID 65534
-# define CHILD_UNPRIVILEGED_GID 65534
-
 namespace {
 
 static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
 
 }  // namespace
 
 namespace base {
 
-class EnvironmentEnvp
-{
-public:
-  EnvironmentEnvp()
-    : mEnvp(PR_DuplicateEnvironment()) {}
-
-  explicit EnvironmentEnvp(const environment_map &em)
-  {
-    mEnvp = (char**) malloc(sizeof(char *) * (em.size() + 1));
-    if (!mEnvp) {
-      return;
-    }
-    char **e = mEnvp;
-    for (environment_map::const_iterator it = em.begin();
-         it != em.end(); ++it, ++e) {
-      std::string str = it->first;
-      str += "=";
-      str += it->second;
-      size_t len = str.length() + 1;
-      *e = static_cast<char*>(malloc(len));
-      memcpy(*e, str.c_str(), len);
-    }
-    *e = NULL;
-  }
-
-  ~EnvironmentEnvp()
-  {
-    if (!mEnvp) {
-      return;
-    }
-    for (char **e = mEnvp; *e; ++e) {
-      free(*e);
-    }
-    free(mEnvp);
-  }
-
-  char * const *AsEnvp() { return mEnvp; }
-
-  void ToMap(environment_map &em)
-  {
-    if (!mEnvp) {
-      return;
-    }
-    em.clear();
-    for (char **e = mEnvp; *e; ++e) {
-      const char *eq;
-      if ((eq = strchr(*e, '=')) != NULL) {
-        std::string varname(*e, eq - *e);
-        em[varname.c_str()] = &eq[1];
-      }
-    }
-  }
-
-private:
-  char **mEnvp;
-};
-
-class Environment : public environment_map
-{
-public:
-  Environment()
-  {
-    EnvironmentEnvp envp;
-    envp.ToMap(*this);
-  }
-
-  char * const *AsEnvp() {
-    mEnvp.reset(new EnvironmentEnvp(*this));
-    return mEnvp->AsEnvp();
-  }
-
-  void Merge(const environment_map &em)
-  {
-    for (const_iterator it = em.begin(); it != em.end(); ++it) {
-      (*this)[it->first] = it->second;
-    }
-  }
-private:
-  std::auto_ptr<EnvironmentEnvp> mEnvp;
-};
-
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                bool wait, ProcessHandle* process_handle) {
   return LaunchApp(argv, fds_to_remap, environment_map(),
                    wait, process_handle);
 }
 
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                const environment_map& env_vars_to_set,
                bool wait, ProcessHandle* process_handle,
                ProcessArchitecture arch) {
-  return LaunchApp(argv, fds_to_remap, env_vars_to_set,
-                   PRIVILEGES_INHERIT,
-                   wait, process_handle);
-}
-
-bool LaunchApp(const std::vector<std::string>& argv,
-               const file_handle_mapping_vector& fds_to_remap,
-               const environment_map& env_vars_to_set,
-               ChildPrivileges privs,
-               bool wait, ProcessHandle* process_handle,
-               ProcessArchitecture arch) {
   mozilla::UniquePtr<char*[]> argv_cstr(new char*[argv.size() + 1]);
   // Illegal to allocate memory after fork and before execvp
   InjectiveMultimap fd_shuffle1, fd_shuffle2;
   fd_shuffle1.reserve(fds_to_remap.size());
   fd_shuffle2.reserve(fds_to_remap.size());
 
-  Environment env;
-  env.Merge(env_vars_to_set);
-  char * const *envp = env.AsEnvp();
-  if (!envp) {
-    DLOG(ERROR) << "FAILED to duplicate environment for: " << argv_cstr[0];
-    return false;
-  }
+  EnvironmentArray envp = BuildEnvironmentArray(env_vars_to_set);
 
   pid_t pid = fork();
   if (pid < 0)
     return false;
 
   if (pid == 0) {
     for (file_handle_mapping_vector::const_iterator
         it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
@@ -171,19 +59,17 @@ bool LaunchApp(const std::vector<std::st
       _exit(127);
 
     CloseSuperfluousFds(fd_shuffle2);
 
     for (size_t i = 0; i < argv.size(); i++)
       argv_cstr[i] = const_cast<char*>(argv[i].c_str());
     argv_cstr[argv.size()] = NULL;
 
-    SetCurrentProcessPrivileges(privs);
-
-    execve(argv_cstr[0], argv_cstr.get(), envp);
+    execve(argv_cstr[0], argv_cstr.get(), envp.get());
     // if we get here, we're in serious trouble and should complain loudly
     // NOTE: This is async signal unsafe; it could deadlock instead.  (But
     // only on debug builds; otherwise it's a signal-safe no-op.)
     DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0];
     _exit(127);
   } else {
     gProcessLog.print("==> process %d launched child process %d\n",
                       GetCurrentProcId(), pid);
@@ -199,28 +85,9 @@ bool LaunchApp(const std::vector<std::st
 
 bool LaunchApp(const CommandLine& cl,
                bool wait, bool start_hidden,
                ProcessHandle* process_handle) {
   file_handle_mapping_vector no_files;
   return LaunchApp(cl.argv(), no_files, wait, process_handle);
 }
 
-void SetCurrentProcessPrivileges(ChildPrivileges privs) {
-  if (privs == PRIVILEGES_INHERIT) {
-    return;
-  }
-
-  gid_t gid = CHILD_UNPRIVILEGED_GID;
-  uid_t uid = CHILD_UNPRIVILEGED_UID;
-  if (setgid(gid) != 0) {
-    DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS";
-    _exit(127);
-  }
-  if (setuid(uid) != 0) {
-    DLOG(ERROR) << "FAILED TO setuid() CHILD PROCESS";
-    _exit(127);
-  }
-  if (chdir("/") != 0)
-    gProcessLog.print("==> could not chdir()\n");
-}
-
 }  // namespace base
--- a/ipc/chromium/src/base/process_util_mac.mm
+++ b/ipc/chromium/src/base/process_util_mac.mm
@@ -1,110 +1,60 @@
 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 
 #include "base/process_util.h"
 
-#import <Cocoa/Cocoa.h>
-#include <crt_externs.h>
+#include <fcntl.h>
 #include <spawn.h>
 #include <sys/wait.h>
 
 #include <string>
 
 #include "base/eintr_wrapper.h"
 #include "base/logging.h"
-#include "base/rand_util.h"
-#include "base/string_util.h"
-#include "base/time.h"
 
 namespace {
 
 static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
 
 }  // namespace
 
 namespace base {
 
-void FreeEnvVarsArray(char* array[], int length)
-{
-  for (int i = 0; i < length; i++) {
-    free(array[i]);
-  }
-  delete[] array;
-}
-
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                bool wait, ProcessHandle* process_handle) {
   return LaunchApp(argv, fds_to_remap, environment_map(),
                    wait, process_handle);
 }
 
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                const environment_map& env_vars_to_set,
                bool wait, ProcessHandle* process_handle,
                ProcessArchitecture arch) {
-  return LaunchApp(argv, fds_to_remap, env_vars_to_set,
-                   PRIVILEGES_INHERIT,
-                   wait, process_handle);
-}
-
-bool LaunchApp(const std::vector<std::string>& argv,
-               const file_handle_mapping_vector& fds_to_remap,
-               const environment_map& env_vars_to_set,
-               ChildPrivileges privs,
-               bool wait, ProcessHandle* process_handle,
-               ProcessArchitecture arch) {
   bool retval = true;
 
   char* argv_copy[argv.size() + 1];
   for (size_t i = 0; i < argv.size(); i++) {
     argv_copy[i] = const_cast<char*>(argv[i].c_str());
   }
   argv_copy[argv.size()] = NULL;
 
   // Make sure we don't leak any FDs to the child process by marking all FDs
   // as close-on-exec.
   SetAllFDsToCloseOnExec();
 
-  // Copy _NSGetEnviron() to a new char array and add the variables
-  // in env_vars_to_set.
-  // Existing variables are overwritten by env_vars_to_set.
-  int pos = 0;
-  environment_map combined_env_vars = env_vars_to_set;
-  while((*_NSGetEnviron())[pos] != NULL) {
-    std::string varString = (*_NSGetEnviron())[pos];
-    std::string varName = varString.substr(0, varString.find_first_of('='));
-    std::string varValue = varString.substr(varString.find_first_of('=') + 1);
-    if (combined_env_vars.find(varName) == combined_env_vars.end()) {
-      combined_env_vars[varName] = varValue;
-    }
-    pos++;
-  }
-  int varsLen = combined_env_vars.size() + 1;
-
-  char** vars = new char*[varsLen];
-  int i = 0;
-  for (environment_map::const_iterator it = combined_env_vars.begin();
-       it != combined_env_vars.end(); ++it) {
-    std::string entry(it->first);
-    entry += "=";
-    entry += it->second;
-    vars[i] = strdup(entry.c_str());
-    i++;
-  }
-  vars[i] = NULL;
+  EnvironmentArray vars = BuildEnvironmentArray(env_vars_to_set);
 
   posix_spawn_file_actions_t file_actions;
   if (posix_spawn_file_actions_init(&file_actions) != 0) {
-    FreeEnvVarsArray(vars, varsLen);
     return false;
   }
 
   // Turn fds_to_remap array into a set of dup2 calls.
   for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
        it != fds_to_remap.end();
        ++it) {
     int src_fd = it->first;
@@ -113,17 +63,16 @@ bool LaunchApp(const std::vector<std::st
     if (src_fd == dest_fd) {
       int flags = fcntl(src_fd, F_GETFD);
       if (flags != -1) {
         fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC);
       }
     } else {
       if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0) {
         posix_spawn_file_actions_destroy(&file_actions);
-        FreeEnvVarsArray(vars, varsLen);
         return false;
       }
     }
   }
 
   // Set up the CPU preference array.
   cpu_type_t cpu_types[1];
   switch (arch) {
@@ -139,39 +88,35 @@ bool LaunchApp(const std::vector<std::st
     default:
       cpu_types[0] = CPU_TYPE_ANY;
       break;
   }
 
   // Initialize spawn attributes.
   posix_spawnattr_t spawnattr;
   if (posix_spawnattr_init(&spawnattr) != 0) {
-    FreeEnvVarsArray(vars, varsLen);
     return false;
   }
 
   // Set spawn attributes.
   size_t attr_count = 1;
   size_t attr_ocount = 0;
   if (posix_spawnattr_setbinpref_np(&spawnattr, attr_count, cpu_types, &attr_ocount) != 0 ||
       attr_ocount != attr_count) {
-    FreeEnvVarsArray(vars, varsLen);
     posix_spawnattr_destroy(&spawnattr);
     return false;
   }
 
   int pid = 0;
   int spawn_succeeded = (posix_spawnp(&pid,
                                       argv_copy[0],
                                       &file_actions,
                                       &spawnattr,
                                       argv_copy,
-                                      vars) == 0);
-
-  FreeEnvVarsArray(vars, varsLen);
+                                      vars.get()) == 0);
 
   posix_spawn_file_actions_destroy(&file_actions);
 
   posix_spawnattr_destroy(&spawnattr);
 
   bool process_handle_valid = pid > 0;
   if (!spawn_succeeded || !process_handle_valid) {
     retval = false;
@@ -190,13 +135,9 @@ bool LaunchApp(const std::vector<std::st
 
 bool LaunchApp(const CommandLine& cl,
                bool wait, bool start_hidden, ProcessHandle* process_handle) {
   // TODO(playmobil): Do we need to respect the start_hidden flag?
   file_handle_mapping_vector no_files;
   return LaunchApp(cl.argv(), no_files, wait, process_handle);
 }
 
-void SetCurrentProcessPrivileges(ChildPrivileges privs) {
-
-}
-
 }  // namespace base
--- a/ipc/chromium/src/base/process_util_posix.cc
+++ b/ipc/chromium/src/base/process_util_posix.cc
@@ -24,16 +24,19 @@
 #include "base/platform_thread.h"
 #include "base/process_util.h"
 #include "base/sys_info.h"
 #include "base/time.h"
 #include "base/waitable_event.h"
 #include "base/dir_reader_posix.h"
 
 #include "mozilla/UniquePtr.h"
+// For PR_DuplicateEnvironment:
+#include "prenv.h"
+#include "prmem.h"
 
 const int kMicrosecondsPerSecond = 1000000;
 
 namespace base {
 
 ProcessId GetCurrentProcId() {
   return getpid();
 }
@@ -348,9 +351,48 @@ int ProcessMetrics::GetCPUUsage() {
                              time_delta);
 
   last_system_time_ = system_time;
   last_time_ = time;
 
   return cpu;
 }
 
+void
+FreeEnvVarsArray::operator()(char** array)
+{
+  for (char** varPtr = array; *varPtr != nullptr; ++varPtr) {
+    free(*varPtr);
+  }
+  delete[] array;
+}
+
+EnvironmentArray
+BuildEnvironmentArray(const environment_map& env_vars_to_set)
+{
+  base::environment_map combined_env_vars = env_vars_to_set;
+  char **environ = PR_DuplicateEnvironment();
+  for (char** varPtr = environ; *varPtr != nullptr; ++varPtr) {
+    std::string varString = *varPtr;
+    size_t equalPos = varString.find_first_of('=');
+    std::string varName = varString.substr(0, equalPos);
+    std::string varValue = varString.substr(equalPos + 1);
+    if (combined_env_vars.find(varName) == combined_env_vars.end()) {
+      combined_env_vars[varName] = varValue;
+    }
+    PR_Free(*varPtr); // PR_DuplicateEnvironment() uses PR_Malloc().
+  }
+  PR_Free(environ); // PR_DuplicateEnvironment() uses PR_Malloc().
+
+  EnvironmentArray array(new char*[combined_env_vars.size() + 1]);
+  size_t i = 0;
+  for (const auto& key_val : combined_env_vars) {
+    std::string entry(key_val.first);
+    entry += "=";
+    entry += key_val.second;
+    array[i] = strdup(entry.c_str());
+    i++;
+  }
+  array[i] = nullptr;
+  return array;
+}
+
 }  // namespace base
--- a/ipc/chromium/src/base/process_util_win.cc
+++ b/ipc/chromium/src/base/process_util_win.cc
@@ -396,20 +396,16 @@ bool DidProcessCrash(bool* child_exited,
       exitcode == 0xC000013A ||     // Control-C/end session.
       exitcode == 0x40010004) {     // Debugger terminated process/end session.
     return false;
   }
 
   return true;
 }
 
-void SetCurrentProcessPrivileges(ChildPrivileges privs) {
-
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // ProcesMetrics
 
 ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process),
                                                         last_time_(0),
                                                         last_system_time_(0) {
   SYSTEM_INFO system_info;
   GetSystemInfo(&system_info);
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -70,38 +70,26 @@ using mozilla::ipc::GeckoChildProcessHos
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #include "GeneratedJNIWrappers.h"
 #include "mozilla/jni/Refs.h"
 #include "mozilla/jni/Utils.h"
 #endif
 
-// We currently don't drop privileges on any platform, because we have to worry
-// about plugins and extensions breaking.
-static const bool kLowRightsSubprocesses = false;
-
 static bool
 ShouldHaveDirectoryService()
 {
   return GeckoProcessType_Default == XRE_GetProcessType();
 }
 
-/*static*/
-base::ChildPrivileges
-GeckoChildProcessHost::DefaultChildPrivileges()
-{
-  return (kLowRightsSubprocesses ?
-          base::PRIVILEGES_UNPRIVILEGED : base::PRIVILEGES_INHERIT);
-}
-
 GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
-                                             ChildPrivileges aPrivileges)
+                                             bool aIsFileContent)
   : mProcessType(aProcessType),
-    mPrivileges(aPrivileges),
+    mIsFileContent(aIsFileContent),
     mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"),
     mProcessState(CREATING_CHANNEL),
 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
     mEnableSandboxLogging(false),
     mSandboxLevel(0),
 #endif
     mChildProcessHandle(0)
 #if defined(MOZ_WIDGET_COCOA)
@@ -729,21 +717,16 @@ GeckoChildProcessHost::PerformAsyncLaunc
   // For POSIX, we have to be extremely anal about *not* using
   // std::wstring in code compiled with Mozilla's -fshort-wchar
   // configuration, because chromium is compiled with -fno-short-wchar
   // and passing wstrings from one config to the other is unsafe.  So
   // we split the logic here.
 
 # if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD) || defined(OS_SOLARIS)
   base::environment_map newEnvVars;
-  ChildPrivileges privs = mPrivileges;
-  if (privs == base::PRIVILEGES_DEFAULT ||
-      privs == base::PRIVILEGES_FILEREAD) {
-    privs = DefaultChildPrivileges();
-  }
 
 #  if defined(MOZ_WIDGET_GTK)
   if (mProcessType == GeckoProcessType_Content) {
     // disable IM module to avoid sandbox violation
     newEnvVars["GTK_IM_MODULE"] = "gtk-im-context-simple";
 
     // Disable ATK accessibility code in content processes because it conflicts
     // with the sandbox, and we proxy that information through the main process
@@ -921,17 +904,17 @@ GeckoChildProcessHost::PerformAsyncLaunc
 
   childArgv.push_back(childProcessType);
 
 # if defined(MOZ_WIDGET_ANDROID)
   LaunchAndroidService(childProcessType, childArgv, mFileMap, &process);
 # else // goes with defined(MOZ_WIDGET_ANDROID)
   base::LaunchApp(childArgv, mFileMap,
 #  if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD) || defined(OS_SOLARIS)
-                  newEnvVars, privs,
+                  newEnvVars,
 #  endif // defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD) || defined(OS_SOLARIS)
                   false, &process, arch);
 # endif // defined(MOZ_WIDGET_ANDROID)
 
   // We're in the parent and the child was launched. Close the child FD in the
   // parent as soon as possible, which will allow the parent to detect when the
   // child closes its FD (either due to normal exit or due to crash).
   GetChannel()->CloseClientFileDescriptor();
@@ -1047,17 +1030,17 @@ GeckoChildProcessHost::PerformAsyncLaunc
     case GeckoProcessType_Content:
 #  if defined(MOZ_CONTENT_SANDBOX)
       if (mSandboxLevel > 0 &&
           !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
         // For now we treat every failure as fatal in SetSecurityLevelForContentProcess
         // and just crash there right away. Should this change in the future then we
         // should also handle the error here.
         mSandboxBroker.SetSecurityLevelForContentProcess(mSandboxLevel,
-                                                         mPrivileges);
+                                                         mIsFileContent);
         shouldSandboxCurrentProcess = true;
       }
 #  endif // defined(MOZ_CONTENT_SANDBOX)
       break;
     case GeckoProcessType_Plugin:
       if (mSandboxLevel > 0 &&
           !PR_GetEnv("MOZ_DISABLE_NPAPI_SANDBOX")) {
         bool ok = mSandboxBroker.SetSecurityLevelForPluginProcess(mSandboxLevel);
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -30,23 +30,20 @@ namespace ipc {
 
 class GeckoChildProcessHost : public ChildProcessHost
 {
 protected:
   typedef mozilla::Monitor Monitor;
   typedef std::vector<std::string> StringVector;
 
 public:
-  typedef base::ChildPrivileges ChildPrivileges;
   typedef base::ProcessHandle ProcessHandle;
 
-  static ChildPrivileges DefaultChildPrivileges();
-
   explicit GeckoChildProcessHost(GeckoProcessType aProcessType,
-                                 ChildPrivileges aPrivileges=base::PRIVILEGES_DEFAULT);
+                                 bool aIsFileContent = false);
 
   ~GeckoChildProcessHost();
 
   static nsresult GetArchitecturesForBinary(const char *path, uint32_t *result);
 
   static uint32_t GetSupportedArchitecturesForProcessType(GeckoProcessType type);
 
   static uint32_t GetUniqueID();
@@ -120,17 +117,17 @@ public:
 
   // For bug 943174: Skip the EnsureProcessTerminated call in the destructor.
   void SetAlreadyDead();
 
   static void EnableSameExecutableForContentProc() { sRunSelfAsContentProc = true; }
 
 protected:
   GeckoProcessType mProcessType;
-  ChildPrivileges mPrivileges;
+  bool mIsFileContent;
   Monitor mMonitor;
   FilePath mProcessPath;
 
   // This value must be accessed while holding mMonitor.
   enum {
     // This object has been constructed, but the OS process has not
     // yet.
     CREATING_CHANNEL = 0,
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -250,23 +250,16 @@ struct ContiguousEnumSerializerInclusive
  */
 template <typename E,
           E AllBits>
 struct BitFlagsEnumSerializer
   : EnumSerializer<E,
                    BitFlagsEnumValidator<E, AllBits>>
 {};
 
-template <>
-struct ParamTraits<base::ChildPrivileges>
-  : public ContiguousEnumSerializer<base::ChildPrivileges,
-                                    base::PRIVILEGES_DEFAULT,
-                                    base::PRIVILEGES_LAST>
-{ };
-
 /**
  * A helper class for serializing plain-old data (POD) structures.
  * The memory representation of the structure is written to and read from
  * the serialized stream directly, without individual processing of the
  * structure's members.
  *
  * Derive ParamTraits<T> from PlainOldDataSerializer<T> if T is POD.
  */
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -10227,17 +10227,18 @@ nsIFrame::UpdateStyleOfOwnedChildFrame(
   //
   // 2) Content can change stylesheets that change the styles of pseudos, and
   //    extensions can add/remove stylesheets that change the styles of
   //    anonymous boxes directly.
   uint32_t equalStructs, samePointerStructs; // Not used, actually.
   nsChangeHint childHint = aChildFrame->StyleContext()->CalcStyleDifference(
     aNewStyleContext,
     &equalStructs,
-    &samePointerStructs);
+    &samePointerStructs,
+    /* aIgnoreVariables = */ true);
 
   // CalcStyleDifference will handle caching structs on the new style context,
   // but only if we're not on a style worker thread.
   MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal(),
              "if we can get in here from style worker threads, then we need "
              "a ResolveSameStructsAs call to ensure structs are cached on "
              "aNewStyleContext");
 
--- a/layout/reftests/bidi/reftest.list
+++ b/layout/reftests/bidi/reftest.list
@@ -1,11 +1,11 @@
 include dirAuto/reftest.list
 include numeral/reftest.list
-fails-if(webrender) == bdi-element.html bdi-element-ref.html
+== bdi-element.html bdi-element-ref.html
 == bidi-000.html bidi-000-ref.html
 == bidi-001.html bidi-001-ref.html
 == bidi-001-j.html bidi-001-ref.html
 == bidi-001-v.html bidi-001-ref.html
 == bidi-002.html bidi-002-ref.html
 == bidi-003.html bidi-003-ref.html
 fuzzy-if(gtkWidget,255,17) == bidi-004.html bidi-004-ref.html # inconsistency in the Hebrew font that gets used
 fuzzy-if(gtkWidget,255,17) == bidi-004-j.html bidi-004-ref.html # inconsistency in the Hebrew font that gets used
@@ -93,27 +93,27 @@ random-if(winWidget) == 305643-1.html 30
 == 489517-1.html 489517-1-ref.html
 == 489887-1.html 489887-1-ref.html
 == 492231-1.html 492231-1-ref.html
 == 496006-1.html 496006-1-ref.html
 == 503269-1.html 503269-1-ref.html
 == 503957-1.html 503957-1-ref.html
 == 525740-1.html 525740-1-ref.html
 == 536963-1.html 536963-1-ref.html
-fails-if(webrender) == 562169-1.html 562169-1-ref.html
-fails-if(webrender) == 562169-1a.html 562169-1-ref.html
+== 562169-1.html 562169-1-ref.html
+== 562169-1a.html 562169-1-ref.html
 == 562169-2.html 562169-2-ref.html
 == 562169-2a.html 562169-2-ref.html
 == 562169-3.html 562169-3-ref.html
 == 562169-3a.html 562169-3-ref.html
 == 562169-4.html 562169-4-ref.html
 == 588739-1.html 588739-ref.html
 == 588739-2.html 588739-ref.html
 == 588739-3.html 588739-ref.html
-== 612843-1.html 612843-1-ref.html
+fails-if(webrender) == 612843-1.html 612843-1-ref.html
 == 613149-1a.html 613149-1-ref.html
 == 613149-1b.html 613149-1-ref.html
 fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)&&!layersGPUAccelerated&&!azureSkia,36,2) == 613149-2a.html 613149-2-ref.html
 fuzzy-if(Android,24,1) fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)&&!layersGPUAccelerated&&!azureSkia,36,2) == 613149-2b.html 613149-2-ref.html
 == 613157-1.html 613157-1-ref.html
 fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)&&!layersGPUAccelerated&&!azureSkia,255,6) == 613157-2.html 613157-2-ref.html
 == 662288-1.html 662288-1-ref.html
 == 670226-1.html 670226-1-ref.html
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1177,17 +1177,17 @@ fuzzy(127,2) fails-if(webrender) == 4481
 != 449149-1a.html about:blank
 != 449149-1b.html about:blank
 # Retry the above with XBL scopes
 test-pref(dom.use_xbl_scopes_for_remote_xul,true) != 449149-1a.html about:blank
 test-pref(dom.use_xbl_scopes_for_remote_xul,true) != 449149-1b.html about:blank
 == 449149-2.html 449149-2-ref.html
 == 449171-1.html 449171-ref.html
 == 449362-1.html 449362-1-ref.html
-== 449519-1.html 449519-1-ref.html
+fuzzy-if(webrender,3,349) == 449519-1.html 449519-1-ref.html
 == 450670-1.html 450670-1-ref.html
 == 451168-1.html 451168-1-ref.html
 == 451876-1.html 451876-1-ref.html
 == 451876-2.html 451876-2-ref.html
 == 452915-1.html 452915-1-ref.html
 == 452964-1.html 452964-1-ref.html
 == 454361.html about:blank
 == 455105-1.html 455105-ref.html
@@ -1629,17 +1629,17 @@ needs-focus == 613433-1.html 613433-3-re
 needs-focus == 613433-2.html 613433-1-ref.html
 needs-focus == 613433-2.html 613433-2-ref.html
 needs-focus == 613433-2.html 613433-3-ref.html
 needs-focus == 613433-3.html 613433-1-ref.html
 needs-focus == 613433-3.html 613433-2-ref.html
 needs-focus == 613433-3.html 613433-3-ref.html
 == 614272-1.svg  614272-1-ref.svg
 HTTP(..) == 615121-1.html 615121-1-ref.html
-HTTP(..) != 615121-2.html 615121-2-notref.html
+fails-if(webrender) HTTP(..) != 615121-2.html 615121-2-notref.html
 == 617242-1.html 617242-1-ref.html
 != 618071.html 618071-notref.html
 == 619117-1.html 619117-1-ref.html
 HTTP(..) == 619511-1.html 619511-1-ref.html
 skip-if(Android) HTTP(..) == 621253-1-externalFilter.html 621253-1-ref.html
 skip-if(Android) == 621253-1-internalFilter.html 621253-1-ref.html
 HTTP(..) == 621253-2-externalFilter.html 621253-2-ref.html
 == 621253-2-internalFilter.html 621253-2-ref.html
--- a/layout/reftests/counter-style/reftest.list
+++ b/layout/reftests/counter-style/reftest.list
@@ -1,35 +1,35 @@
 == system-cyclic.html     system-cyclic-ref.html
-== system-fixed.html      system-fixed-ref.html
-== system-symbolic.html   system-symbolic-ref.html
+fails-if(webrender) == system-fixed.html      system-fixed-ref.html
+fails-if(webrender) == system-symbolic.html   system-symbolic-ref.html
 == system-alphabetic.html system-alphabetic-ref.html
 == system-numeric.html    system-numeric-ref.html
 fails-if(webrender) == system-additive.html   system-additive-ref.html
 == system-extends.html    system-extends-ref.html
 == system-cyclic-invalid.html     system-common-invalid-ref.html
 == system-fixed-invalid.html      system-common-invalid2-ref.html
 == system-symbolic-invalid.html   system-common-invalid-ref.html
 == system-alphabetic-invalid.html system-common-invalid2-ref.html
 == system-numeric-invalid.html    system-common-invalid2-ref.html
 == system-additive-invalid.html   system-common-invalid-ref.html
 == system-extends-invalid.html    system-extends-invalid-ref.html
 == descriptor-negative.html descriptor-negative-ref.html
 == descriptor-prefix.html   descriptor-prefix-ref.html
-== descriptor-suffix.html   descriptor-suffix-ref.html
+fails-if(webrender) == descriptor-suffix.html   descriptor-suffix-ref.html
 == descriptor-range.html    descriptor-range-ref.html
 == descriptor-pad.html      descriptor-pad-ref.html
 == descriptor-fallback.html descriptor-fallback-ref.html
 == descriptor-symbols.html  descriptor-symbols-ref.html
 == descriptor-negative-invalid.html descriptor-negative-invalid-ref.html
 == descriptor-prefix-invalid.html   descriptor-prefix-invalid-ref.html
 == descriptor-suffix-invalid.html   descriptor-suffix-invalid-ref.html
 == descriptor-range-invalid.html    descriptor-range-invalid-ref.html
 == descriptor-pad-invalid.html      descriptor-pad-invalid-ref.html
 == descriptor-fallback.html         descriptor-fallback-ref.html
 == descriptor-symbols-invalid.html  descriptor-symbols-invalid-ref.html
 == name-case-sensitivity.html       name-case-sensitivity-ref.html
-== dependent-builtin.html           dependent-builtin-ref.html
+fails-if(webrender) == dependent-builtin.html           dependent-builtin-ref.html
 == redefine-builtin.html            redefine-builtin-ref.html
 == redefine-attr-mapping.html       redefine-attr-mapping-ref.html
 == disclosure-styles.html           disclosure-styles-ref.html
 == symbols-function.html            symbols-function-ref.html
 == symbols-function-invalid.html    symbols-function-invalid-ref.html
--- a/layout/reftests/counters/reftest.list
+++ b/layout/reftests/counters/reftest.list
@@ -63,17 +63,17 @@
 fails-if(xulRuntime.XPCOMABI.match(/arm/)) == counter-reset-integer-range.html counter-reset-integer-range-ref.html # bug 989718
 == counter-ua-limits-00.html counter-ua-limits-00-ref.html
 == counter-ua-limits-01.html counter-ua-limits-01-ref.html
 fails-if(xulRuntime.XPCOMABI.match(/arm/)) == counter-ua-limits-02.html counter-ua-limits-02-ref.html # bug 989718
 == counter-ua-limits-03.html counter-ua-limits-03-ref.html
 == counter-ua-limits-list-00.html counter-ua-limits-list-00-ref.html
 == counter-ua-limits-list-01.html counter-ua-limits-list-01-ref.html
 == multiple-thai-counters.html multiple-thai-counters-ref.html
-== counter-suffix.html counter-suffix-ref.html
+fails-if(webrender) == counter-suffix.html counter-suffix-ref.html
 == counter-cjk-decimal.html counter-cjk-decimal-ref.html
 == counter-japanese-informal.html counter-japanese-informal-ref.html
 == counter-japanese-formal.html counter-japanese-formal-ref.html
 == counter-korean-hangul-formal.html counter-korean-hangul-formal-ref.html
 == counter-korean-hanja-informal.html counter-korean-hanja-informal-ref.html
 == counter-korean-hanja-formal.html counter-korean-hanja-formal-ref.html
 == counter-simp-chinese-informal.html counter-simp-chinese-informal-ref.html
 == counter-simp-chinese-formal.html counter-simp-chinese-formal-ref.html
--- a/layout/reftests/css-blending/reftest.list
+++ b/layout/reftests/css-blending/reftest.list
@@ -1,44 +1,44 @@
 pref(layout.css.mix-blend-mode.enabled,true) == blend-canvas.html blend-canvas-ref.html
 pref(layout.css.mix-blend-mode.enabled,true) == blend-constant-background-color.html blend-constant-background-color-ref.html
 pref(layout.css.mix-blend-mode.enabled,true) fuzzy-if(webrender,1-1,7875-7875) == blend-gradient-background-color.html blend-gradient-background-color-ref.html
 pref(layout.css.mix-blend-mode.enabled,true) == blend-image.html blend-image-ref.html
 pref(layout.css.mix-blend-mode.enabled,true) == blend-difference-stacking.html blend-difference-stacking-ref.html
 
 fuzzy-if(skiaContent,1,30000) pref(layout.css.background-blend-mode.enabled,true) == background-blending-alpha.html background-blending-alpha-ref.html
 pref(layout.css.background-blend-mode.enabled,true) fuzzy-if(webrender,1-1,7875-7875) == background-blending-gradient-color.html background-blending-gradient-color-ref.html
-fuzzy-if(azureSkiaGL,3,7597) fuzzy-if(cocoaWidget,3,7597) fuzzy-if(d2d,1,3800) fuzzy-if(d3d11,1,4200) fuzzy-if(skiaContent,2,9450) pref(layout.css.background-blend-mode.enabled,true) fails-if(webrender) == background-blending-gradient-gradient.html background-blending-gradient-gradient-ref.html
-fuzzy-if(azureSkiaGL,2,7174) pref(layout.css.background-blend-mode.enabled,true) fails-if(webrender) == background-blending-gradient-image.html background-blending-gradient-color-ref.html
+fuzzy-if(azureSkiaGL,3,7597) fuzzy-if(cocoaWidget,3,7597) fuzzy-if(d2d,1,3800) fuzzy-if(d3d11,1,4200) fuzzy-if(skiaContent,2,9450) fuzzy-if(webrender,1-1,23625-23625) pref(layout.css.background-blend-mode.enabled,true) == background-blending-gradient-gradient.html background-blending-gradient-gradient-ref.html
+fuzzy-if(azureSkiaGL,2,7174) fuzzy-if(webrender,1-1,7875-7875) pref(layout.css.background-blend-mode.enabled,true) == background-blending-gradient-image.html background-blending-gradient-color-ref.html
 fuzzy-if(azureSkia||d2d||gtkWidget,1,10000) pref(layout.css.background-blend-mode.enabled,true) == background-blending-image-color-jpg.html background-blending-image-color-ref.html
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-image-color-png.html background-blending-image-color-ref.html
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-image-color-svg.html background-blending-image-color-ref.html
-fuzzy-if(azureSkiaGL,2,7174) pref(layout.css.background-blend-mode.enabled,true) fails-if(webrender) == background-blending-image-gradient.html background-blending-gradient-color-ref.html
+fuzzy-if(azureSkiaGL,2,7174) fuzzy-if(webrender,1-1,7875-7875) pref(layout.css.background-blend-mode.enabled,true) == background-blending-image-gradient.html background-blending-gradient-color-ref.html
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-image-image.html background-blending-image-color-ref.html
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-isolation.html background-blending-isolation-ref.html
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-list-repeat.html background-blending-list-repeat-ref.html
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-multiple-images.html background-blending-multiple-images-ref.html
 
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-color-burn.html background-blending-color-burn-ref.svg
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-color-dodge.html background-blending-color-dodge-ref.svg
 # need to investigate why these tests are fuzzy - first suspect is a possible color space conversion on some platforms; same for mix-blend-mode tests
-fuzzy-if(azureSkia||gtkWidget,2,9600) fuzzy-if(d2d,1,8000) pref(layout.css.background-blend-mode.enabled,true) fails-if(webrender) == background-blending-color.html background-blending-color-ref.svg
+fuzzy-if(azureSkia||gtkWidget,2,9600) fuzzy-if(d2d,1,8000) pref(layout.css.background-blend-mode.enabled,true) == background-blending-color.html background-blending-color-ref.svg
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-darken.html background-blending-darken-ref.svg
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-difference.html background-blending-difference-ref.svg
 fuzzy-if(skiaContent,1,1600) pref(layout.css.background-blend-mode.enabled,true) == background-blending-exclusion.html background-blending-exclusion-ref.svg
 fuzzy-if(cocoaWidget||d2d,1,1600) pref(layout.css.background-blend-mode.enabled,true) == background-blending-hard-light.html background-blending-hard-light-ref.svg
-fuzzy-if(d2d,1,9600) fuzzy-if(azureSkia||gtkWidget,2,9600) pref(layout.css.background-blend-mode.enabled,true) fails-if(webrender) == background-blending-hue.html background-blending-hue-ref.svg
+fuzzy-if(d2d,1,9600) fuzzy-if(azureSkia||gtkWidget,2,9600) fuzzy-if(webrender,1-1,11200-11200) pref(layout.css.background-blend-mode.enabled,true) == background-blending-hue.html background-blending-hue-ref.svg
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-lighten.html background-blending-lighten-ref.svg
-fuzzy-if(d2d,1,8000) fuzzy-if(azureSkia||gtkWidget,2,9600) pref(layout.css.background-blend-mode.enabled,true) fails-if(webrender) == background-blending-luminosity.html background-blending-luminosity-ref.svg
+fuzzy-if(d2d,1,8000) fuzzy-if(azureSkia||gtkWidget,2,9600) pref(layout.css.background-blend-mode.enabled,true) == background-blending-luminosity.html background-blending-luminosity-ref.svg
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-multiply.html background-blending-multiply-ref.svg
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-normal.html background-blending-normal-ref.svg
-fuzzy-if(azureSkia||gtkWidget,1,1600) pref(layout.css.background-blend-mode.enabled,true) fails-if(webrender) == background-blending-overlay.html background-blending-overlay-ref.svg
-fuzzy-if(d2d,1,1600) fuzzy-if(azureSkia||gtkWidget,2,12800) pref(layout.css.background-blend-mode.enabled,true) fails-if(webrender) == background-blending-saturation.html background-blending-saturation-ref.svg
-fuzzy-if(d2d||azureSkia||gtkWidget,1,1600) pref(layout.css.background-blend-mode.enabled,true) fails-if(webrender) == background-blending-screen.html background-blending-screen-ref.svg
-fuzzy-if(d2d||azureSkia||gtkWidget,10,4800) pref(layout.css.background-blend-mode.enabled,true) fails-if(webrender) == background-blending-soft-light.html background-blending-soft-light-ref.svg
+fuzzy-if(azureSkia||gtkWidget,1,1600) pref(layout.css.background-blend-mode.enabled,true) == background-blending-overlay.html background-blending-overlay-ref.svg
+fuzzy-if(d2d,1,1600) fuzzy-if(azureSkia||gtkWidget,2,12800) pref(layout.css.background-blend-mode.enabled,true) == background-blending-saturation.html background-blending-saturation-ref.svg
+fuzzy-if(d2d||azureSkia||gtkWidget,1,1600) pref(layout.css.background-blend-mode.enabled,true) == background-blending-screen.html background-blending-screen-ref.svg
+fuzzy-if(d2d||azureSkia||gtkWidget,10,4800) pref(layout.css.background-blend-mode.enabled,true) == background-blending-soft-light.html background-blending-soft-light-ref.svg
 
 fuzzy-if(azureSkia||d2d||gtkWidget,1,40000) pref(layout.css.background-blend-mode.enabled,true) == background-blending-image-color-959674.html background-blending-image-color-959674-ref.html
 
 #fuzzy due to inconsistencies in rounded rect cliping between parent and child; may be related to antialiasing. Between platforms, the max difference is the same, and the number of different pixels is either 36 or 37. (Win, Mac and Lin)
 fuzzy(64,53) pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-952051.html mix-blend-mode-952051-ref.html
 
 fuzzy-if(d3d11,49,200) pref(layout.css.mix-blend-mode.enabled,true) pref(layout.css.filters.enabled,true) == mix-blend-mode-and-filter.html mix-blend-mode-and-filter-ref.html
 fuzzy-if(d3d11,1,3) pref(layout.css.mix-blend-mode.enabled,true) pref(layout.css.filters.enabled,true) == mix-blend-mode-and-filter.svg mix-blend-mode-and-filter-ref.svg
@@ -87,14 +87,14 @@ pref(layout.css.background-blend-mode.en
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-background-attachement-fixed.html background-blending-background-attachement-fixed-ref.html
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-background-attachement-fixed-scroll.html background-blending-background-attachement-fixed-scroll-ref.html
 
 pref(layout.css.background-blend-mode.enabled,true) == background-blend-mode-body-image.html background-blend-mode-body-image-ref.html
 fuzzy-if(Android,4,768) fuzzy-if(gtkWidget,1,132) fuzzy-if(skiaContent,1,800) pref(layout.css.background-blend-mode.enabled,true) == background-blend-mode-body-transparent-image.html background-blend-mode-body-transparent-image-ref.html
 
 pref(layout.css.background-blend-mode.enabled,true) == background-blending-moz-element.html background-blending-moz-element-ref.html
 
-fuzzy(1,40000) pref(layout.css.background-blend-mode.enabled,true) fails-if(webrender) == mix-blend-mode-soft-light.html mix-blend-mode-soft-light-ref.html
+fuzzy(1,40000) pref(layout.css.background-blend-mode.enabled,true) == mix-blend-mode-soft-light.html mix-blend-mode-soft-light-ref.html
 
 # Test plan 4.4.2 element with isolation:isolate creates an isolated group for blended children
 pref(layout.css.isolation.enabled,true) == blend-isolation.html blend-isolation-ref.html
 
 pref(layout.css.background-blend-mode.enabled,true) == bug1281593.html bug1281593-ref.html
--- a/layout/reftests/font-features/reftest.list
+++ b/layout/reftests/font-features/reftest.list
@@ -115,9 +115,9 @@ HTTP(..) != subsuper-fallback.html subsu
 HTTP(..) != subsuper-fallback.html subsuper-fallback-notref3.html
 HTTP(..) != subsuper-fallback-omega.html subsuper-fallback-omega-notref.html
 HTTP(..) == subsuper-nofallback.html subsuper-nofallback-ref1.html
 random-if(cocoaWidget) HTTP(..) == subsuper-nofallback.html subsuper-nofallback-ref2.html # bug 1139269
 HTTP(..) != subsuper-nofallback.html subsuper-nofallback-notref.html
 HTTP(..) == subsuper-fallback-size.html subsuper-fallback-size-ref.html
 
 # GPOS spacing adjustments in vertical mode -- subsetted opentype/cff test font fails to load on Win7
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) HTTP(..) == 1376231-vertical-gpos-adjustments.html 1376231-vertical-gpos-adjustments-ref.html
+fails-if(webrender) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) HTTP(..) == 1376231-vertical-gpos-adjustments.html 1376231-vertical-gpos-adjustments-ref.html
rename from layout/reftests/forms/input/checkbox/checkbox-clamp-ref.html
rename to layout/reftests/forms/input/checkbox/checkbox-clamp-01-ref.html
--- a/layout/reftests/forms/input/checkbox/checkbox-clamp-ref.html
+++ b/layout/reftests/forms/input/checkbox/checkbox-clamp-01-ref.html
@@ -1,1 +1,13 @@
-<input type="checkbox" style="top:58px; position:absolute">
+<style>
+  div {
+    top: 50px;
+    line-height: 0px;
+    position: relative;
+  }
+</style>
+
+<div>
+  <input type="checkbox">
+  <input type="checkbox" checked>
+</div>
+
rename from layout/reftests/forms/input/checkbox/checkbox-clamp.html
rename to layout/reftests/forms/input/checkbox/checkbox-clamp-01.html
--- a/layout/reftests/forms/input/checkbox/checkbox-clamp.html
+++ b/layout/reftests/forms/input/checkbox/checkbox-clamp-01.html
@@ -5,12 +5,29 @@
 -->
 <html>
   <head>
   <meta charset="utf-8">
   <title>Test clamping width/height of checkbox.</title>
   <link rel="author" title="Louis Chang" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1400050">
   <link rel="match" href="checkbox-clamp-ref.html">
   </head>
+    <style>
+div {
+  position: relative;
+  line-height: 0px;
+}
+input {
+  height: 113px;
+}
+    </style>
   <body>
-    <input type="checkbox" style="height:113px">
+  <!--
+    Default Size of checkbox is 13px * 13px on all platforms except Linux
+    (18px * 18px). So given a checkbox with height 113px, the checkbox will be
+    clamped to 13px * 13px and move to center which y is 50px ((113 - 13) / 2).
+  -->
+    <div>
+      <input type="checkbox">
+      <input type="checkbox" checked>
+    <div>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/checkbox/checkbox-clamp-02-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Testcase for bug 1405986</title>
+  <style type="text/css">
+html,body {
+  color: black;
+  background-color: white;
+  font: 16px/1 monospace;
+  padding: 0;
+  margin: 0;
+}
+
+.grid {
+  display: inline-grid;
+  grid: 100px / 40px 40px;
+  border: 1px solid;
+  vertical-align: top;
+  justify-items: start;
+}
+
+input {
+  margin: 0;
+}
+  </style>
+</head>
+<body>
+  <div class="grid">
+    <input type=checkbox style="align-self: center">
+    <input type=checkbox checked style="align-self: center">
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/checkbox/checkbox-clamp-02.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Testcase for bug 1405986</title>
+  <style type="text/css">
+html,body {
+  color: black;
+  background-color: white;
+  font: 16px/1 monospace;
+  padding: 0;
+  margin: 0;
+}
+
+.grid {
+  display: inline-grid;
+  grid: 100px / 40px 40px;
+  border: 1px solid;
+  vertical-align: top;
+  justify-items: start;
+}
+
+input {
+  margin: 0;
+}
+  </style>
+</head>
+<body>
+  <div class="grid">
+    <input type=checkbox style="height: 100px">
+    <input type=checkbox checked style="height: 100px">
+  </div>
+</body>
+</html>
+
--- a/layout/reftests/forms/input/checkbox/reftest.list
+++ b/layout/reftests/forms/input/checkbox/reftest.list
@@ -9,10 +9,11 @@
 == indeterminate-checked-notref.html about:blank
 == indeterminate-unchecked.html about:blank
 != indeterminate-native-checked.html indeterminate-native-checked-notref.html
 != indeterminate-native-unchecked.html indeterminate-native-unchecked-notref.html
 == indeterminate-selector.html indeterminate-selector-ref.html
 skip-if(!gtkWidget) == gtk-theme-width-height.html gtk-theme-width-height-ref.html
 == checkbox-baseline.html checkbox-baseline-ref.html
 == checkbox-radio-color.html checkbox-radio-color-ref.html
-fails-if(gtkWidget) == checkbox-clamp.html checkbox-clamp-ref.html
+skip-if(gtkWidget) == checkbox-clamp-01.html checkbox-clamp-01-ref.html
+skip-if(OSX||winWidget) == checkbox-clamp-02.html checkbox-clamp-02-ref.html
 fails-if(OSX) == checkbox-minimum-size.html checkbox-minimum-size-ref.html
--- a/layout/reftests/forms/input/color/reftest.list
+++ b/layout/reftests/forms/input/color/reftest.list
@@ -1,14 +1,10 @@
-# Simple test. Should fail on platforms where input type color isn't activated
-# yet. The missing platform is Android (bug 875750).
-fails-if(Android) == input-color-1.html input-color-1-ref.html
+== input-color-1.html input-color-1-ref.html
 
 default-preferences pref(dom.forms.color,true)
 
-# Despite the "default-preferences" line above, Android is still
-# excluded from some style in forms.css, which makes the following tests fail.
-fails-if(Android) == margin-padding-1.html margin-padding-1-ref.html
+== margin-padding-1.html margin-padding-1-ref.html
 == block-invalidate-1.html block-invalidate-1-ref.html
 == block-invalidate-2.html block-invalidate-2-ref.html
-fuzzy-if(gtkWidget,8,33) fuzzy-if(skiaContent,8,80) fails-if(Android) == transformations-1.html transformations-1-ref.html
-fails-if(Android) == custom-style-1.html custom-style-1-ref.html
-fails-if(Android) == custom-style-2.html custom-style-2-ref.html
+fuzzy-if(gtkWidget,8,33) fuzzy-if(skiaContent,8,80) == transformations-1.html transformations-1-ref.html
+== custom-style-1.html custom-style-1-ref.html
+== custom-style-2.html custom-style-2-ref.html
rename from layout/reftests/forms/input/radio/radio-clamp-ref.html
rename to layout/reftests/forms/input/radio/radio-clamp-01-ref.html
--- a/layout/reftests/forms/input/radio/radio-clamp-ref.html
+++ b/layout/reftests/forms/input/radio/radio-clamp-01-ref.html
@@ -1,1 +1,12 @@
-<input type="radio" style="top:58px; position:absolute">
+<style>
+  div {
+    top: 50px;
+    line-height: 0px;
+    position: relative;
+  }
+</style>
+
+<div>
+  <input type="radio">
+  <input type="radio" checked>
+</div>
rename from layout/reftests/forms/input/radio/radio-clamp.html
rename to layout/reftests/forms/input/radio/radio-clamp-01.html
--- a/layout/reftests/forms/input/radio/radio-clamp.html
+++ b/layout/reftests/forms/input/radio/radio-clamp-01.html
@@ -5,12 +5,29 @@
 -->
 <html>
   <head>
   <meta charset="utf-8">
   <title>Test clamping width/height of radio.</title>
   <link rel="author" title="Louis Chang" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1400050">
   <link rel="match" href="radio-clamp-ref.html">
   </head>
+    <style>
+div {
+  position: relative;
+  line-height: 0px;
+}
+input {
+  height: 113px;
+}
+    </style>
   <body>
-    <input type="radio" style="height:113px">
+  <!--
+    Default Size of radio is 13px * 13px on all platforms except Linux
+    (18px * 18px). So given a radio with height 113px, the radio will be
+    clamped to 13px * 13px and move to center which y is 50px ((113 - 13) / 2).
+  -->
+    <div>
+      <input type="radio">
+      <input type="radio" checked>
+    <div>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/radio/radio-clamp-02-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Testcase for bug 1405986</title>
+  <style type="text/css">
+html,body {
+  color: black;
+  background-color: white;
+  font: 16px/1 monospace;
+  padding: 0;
+  margin: 0;
+}
+
+.grid {
+  display: inline-grid;
+  grid: 100px / 40px 40px;
+  border: 1px solid;
+  vertical-align: top;
+  justify-items: start;
+}
+
+input {
+  margin: 0;
+}
+  </style>
+</head>
+<body>
+  <div class="grid">
+    <input type=radio style="align-self: center">
+    <input type=radio checked style="align-self: center">
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/radio/radio-clamp-02.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Testcase for bug 1405986</title>
+  <style type="text/css">
+html,body {
+  color: black;
+  background-color: white;
+  font: 16px/1 monospace;
+  padding: 0;
+  margin: 0;
+}
+
+.grid {
+  display: inline-grid;
+  grid: 100px / 40px 40px;
+  border: 1px solid;
+  vertical-align: top;
+  justify-items: start;
+}
+
+input {
+  margin: 0;
+}
+  </style>
+</head>
+<body>
+  <div class="grid">
+    <input type=radio style="height: 100px">
+    <input type=radio checked style="height: 100px">
+  </div>
+</body>
+</html>
+
--- a/layout/reftests/forms/input/radio/reftest.list
+++ b/layout/reftests/forms/input/radio/reftest.list
@@ -1,9 +1,10 @@
 == label-dynamic.html label-dynamic-ref.html
 != checked-native.html checked-native-notref.html
 == checked-appearance-none.html about:blank
 == unchecked-appearance-none.html about:blank
 != checked-native.html about:blank
 != checked-native-notref.html about:blank
 skip-if(!gtkWidget) == gtk-theme-width-height.html gtk-theme-width-height-ref.html
-fails-if(gtkWidget) == radio-clamp.html radio-clamp-ref.html
+skip-if(gtkWidget) == radio-clamp-01.html radio-clamp-01-ref.html
+skip-if(OSX||winWidget) == radio-clamp-02.html radio-clamp-02-ref.html
 fails-if(OSX) == radio-minimum-size.html radio-minimum-size-ref.html
--- a/layout/reftests/svg/text/reftest.list
+++ b/layout/reftests/svg/text/reftest.list
@@ -189,15 +189,15 @@ fuzzy-if(skiaContent&&winWidget,126,336)
 # vertical text
 fuzzy-if(skiaContent,1,80) == textpath-vertical-dx.svg textpath-vertical-dx-ref.svg
 
 # selection
 needs-focus == deselectAll.svg deselectAll-ref.svg
 fuzzy-if(skiaContent,1,250) needs-focus == selectSubString.svg selectSubString-ref.svg
 fuzzy-if(skiaContent,1,600) needs-focus == selectSubString-2.svg selectSubString-2-ref.svg
 fuzzy-if(skiaContent,1,250) needs-focus == selectSubString-3.svg selectSubString-3-ref.svg
-needs-focus == simple-selection.svg simple-selection-ref.html
-fuzzy-if(skiaContent,1,100) needs-focus == simple-bidi-selection.svg simple-bidi-selection-ref.html
-fuzzy-if(skiaContent,1,50) needs-focus == simple-fill-color-selection.svg simple-fill-color-selection-ref.html
-fuzzy-if(skiaContent,1,150) needs-focus == simple-underline-selection.svg simple-underline-selection-ref.html
-fuzzy-if(skiaContent,1,300) needs-focus == multiple-text-selection.svg multiple-text-selection-ref.html
+fuzzy-if(webrender,1-1,237-237) needs-focus == simple-selection.svg simple-selection-ref.html
+fuzzy-if(skiaContent,1,100) fuzzy-if(webrender,1-1,575-575) needs-focus == simple-bidi-selection.svg simple-bidi-selection-ref.html
+fuzzy-if(skiaContent,1,50) fuzzy-if(webrender,1-1,237-237) needs-focus == simple-fill-color-selection.svg simple-fill-color-selection-ref.html
+fuzzy-if(skiaContent,1,150) fuzzy-if(webrender,1-1,222-222) needs-focus == simple-underline-selection.svg simple-underline-selection-ref.html
+fuzzy-if(skiaContent,1,300) fuzzy-if(webrender,1-1,934-934) needs-focus == multiple-text-selection.svg multiple-text-selection-ref.html
 needs-focus == multiple-chunks-selection.svg multiple-chunks-selection-ref.svg
 fuzzy-if(skiaContent,1,200) needs-focus == textpath-selection.svg textpath-selection-ref.svg
--- a/layout/reftests/text-decoration/reftest.list
+++ b/layout/reftests/text-decoration/reftest.list
@@ -1,10 +1,10 @@
-== complex-decoration-style-quirks.html complex-decoration-style-quirks-ref.html
-== complex-decoration-style-standards.html complex-decoration-style-standards-ref.html
+fails-if(webrender) == complex-decoration-style-quirks.html complex-decoration-style-quirks-ref.html
+fails-if(webrender) == complex-decoration-style-standards.html complex-decoration-style-standards-ref.html
 == decoration-color-quirks.html decoration-color-quirks-ref.html
 == decoration-color-standards.html decoration-color-standards-ref.html
 == decoration-style-quirks.html decoration-style-quirks-ref.html
 == decoration-style-standards.html decoration-style-standards-ref.html
 == dynamic-underline-vertical-align-quirks-1.html dynamic-underline-vertical-align-quirks-1-ref.html
 == dynamic-underline-vertical-align-standards-1.html dynamic-underline-vertical-align-standards-1-ref.html
 == dynamic-underline-vertical-align-quirks-2.html dynamic-underline-vertical-align-quirks-2-ref.html
 == dynamic-underline-vertical-align-standards-2.html dynamic-underline-vertical-align-standards-2-ref.html
--- a/layout/reftests/text-overflow/reftest.list
+++ b/layout/reftests/text-overflow/reftest.list
@@ -4,23 +4,23 @@ fuzzy-if(Android,16,244) HTTP(..) == mar
 HTTP(..) == marker-string.html marker-string-ref.html
 skip-if(Android) HTTP(..) == bidi-simple.html bidi-simple-ref.html # Fails on Android due to anti-aliasing
 skip-if(!gtkWidget) fuzzy-if(gtkWidget,2,289) HTTP(..) == bidi-simple-scrolled.html bidi-simple-scrolled-ref.html # Fails on Windows and OSX due to anti-aliasing
 fuzzy-if(Android,24,4000) fuzzy-if(cocoaWidget,1,40) fuzzy-if(asyncPan&&!layersGPUAccelerated,149,1836) HTTP(..) == scroll-rounding.html scroll-rounding-ref.html # bug 760264
 fuzzy(2,453) fuzzy-if(skiaContent,9,2100) fails-if(gtkWidget) HTTP(..) == anonymous-block.html anonymous-block-ref.html # gtkWidget:bug 1309103
 HTTP(..) == false-marker-overlap.html false-marker-overlap-ref.html
 HTTP(..) == visibility-hidden.html visibility-hidden-ref.html
 fuzzy-if(asyncPan&&!layersGPUAccelerated,102,1724) fuzzy-if(gtkWidget,10,8) HTTP(..) == block-padding.html block-padding-ref.html
-HTTP(..) == quirks-decorations.html quirks-decorations-ref.html
+fuzzy-if(webrender,3,825) HTTP(..) == quirks-decorations.html quirks-decorations-ref.html
 HTTP(..) == quirks-line-height.html quirks-line-height-ref.html
 HTTP(..) == standards-decorations.html standards-decorations-ref.html
 HTTP(..) == standards-line-height.html standards-line-height-ref.html
 fuzzy-if(skiaContent,1,4200) HTTP(..) == selection.html selection-ref.html
-HTTP(..) == marker-shadow.html marker-shadow-ref.html
-== aligned-baseline.html aligned-baseline-ref.html
+fuzzy-if(webrender,5,509) HTTP(..) == marker-shadow.html marker-shadow-ref.html
+fuzzy-if(webrender,3,25) == aligned-baseline.html aligned-baseline-ref.html
 skip-if(Android) fuzzy-if(skiaContent,1,5) == clipped-elements.html clipped-elements-ref.html
 HTTP(..) == theme-overflow.html theme-overflow-ref.html
 HTTP(..) == table-cell.html table-cell-ref.html
 fuzzy-if(gtkWidget,10,32) HTTP(..) == two-value-syntax.html two-value-syntax-ref.html
 HTTP(..) == single-value.html single-value-ref.html
 fuzzy-if(gtkWidget,10,2) HTTP(..) == atomic-under-marker.html atomic-under-marker-ref.html
 fuzzy(1,2616) skip-if(Android) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,12352) fails-if(gtkWidget) HTTP(..) == xulscroll.html xulscroll-ref.html # gtkWidget:bug 1309107, bug 1328771
 HTTP(..) == combobox-zoom.html combobox-zoom-ref.html
--- a/layout/reftests/text-shadow/reftest.list
+++ b/layout/reftests/text-shadow/reftest.list
@@ -8,33 +8,33 @@ random-if(Android) == basic-negcoord.xul
 HTTP(..) == blur-opacity.html blur-opacity-ref.html
 
 == basic.html basic-ref.html
 == basic-negcoord.html basic-negcoord-ref.html
 == basic-opacity.html basic-opacity-ref.html
 != blur.html blur-notref.html
 == color-inherit.html color-inherit-ref.html
 == color-parserorder.html color-parserorder-ref.html
-== decorations-multiple-zorder.html decorations-multiple-zorder-ref.html
+fails-if(webrender) == decorations-multiple-zorder.html decorations-multiple-zorder-ref.html
 == multiple-noblur.html multiple-noblur-ref.html
-== quirks-decor-noblur.html quirks-decor-noblur-ref.html
-== standards-decor-noblur.html standards-decor-noblur-ref.html
+fails-if(webrender) == quirks-decor-noblur.html quirks-decor-noblur-ref.html
+fails-if(webrender) == standards-decor-noblur.html standards-decor-noblur-ref.html
 == padding-decoration.html padding-decoration-ref.html
 == textindent.html textindent-ref.html
 == lineoverflow.html lineoverflow-ref.html
 
 == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref.html
 == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref2.html
 == overflow-not-scrollable-2.html overflow-not-scrollable-2-ref.html
 
 needs-focus != text-shadow-selected-1.html text-shadow-selected-1-notref.html
-fails-if(Android) fuzzy-if(skiaContent&&!Android,1,1000) needs-focus fails-if(webrender) == text-shadow-selected-1.html text-shadow-selected-1-ref.html # different foreground selection color on Android
+fails-if(Android) fuzzy-if(skiaContent&&!Android,1,1000) needs-focus == text-shadow-selected-1.html text-shadow-selected-1-ref.html # different foreground selection color on Android
 needs-focus != text-shadow-selected-2.html text-shadow-selected-2-notref.html
-fails-if(Android) fuzzy-if(skiaContent&&!Android,1,1400) needs-focus fails-if(webrender) == text-shadow-selected-2.html text-shadow-selected-2-ref.html # different foreground selection color on Android
+fails-if(Android) fuzzy-if(skiaContent&&!Android,1,1400) needs-focus == text-shadow-selected-2.html text-shadow-selected-2-ref.html # different foreground selection color on Android
 
 # bug 692744
-== text-shadow-on-space-1.html text-shadow-on-space-1-ref.html
+fuzzy-if(webrender,19,2872) == text-shadow-on-space-1.html text-shadow-on-space-1-ref.html
 
 # bug 721750
 needs-focus == text-shadow-on-selection-1.html text-shadow-on-selection-1-ref.html
 needs-focus == text-shadow-on-selection-2.html text-shadow-on-selection-2-ref.html
 
 == overflow-decoration.html overflow-decoration-ref.html
--- a/layout/reftests/text/reftest.list
+++ b/layout/reftests/text/reftest.list
@@ -106,17 +106,17 @@ HTTP(..) == synthetic-bold-papyrus-01.ht
 != text-align-justify-last-end.html text-align-justify-last-center.html
 != text-align-justify-last-end.html text-align-justify-last-justify.html
 != text-align-justify-last-center.html text-align-justify-last-justify.html
 == text-align-left-in-rtl-block.html text-align-left-in-rtl-block-ref.html
 HTTP(..) == variation-selector-unsupported-1.html variation-selector-unsupported-1-ref.html
 == white-space-1a.html white-space-1-ref.html
 == white-space-1b.html white-space-1-ref.html
 == white-space-2.html white-space-2-ref.html
-fails-if(webrender) == wordbreak-1.html wordbreak-1-ref.html
+== wordbreak-1.html wordbreak-1-ref.html
 == wordbreak-2.html wordbreak-2-ref.html
 == wordbreak-3.html wordbreak-3-ref.html
 skip-if(Android) == wordbreak-4a.html wordbreak-4a-ref.html
 == wordbreak-4b.html wordbreak-4b-ref.html
 == wordbreak-5.html wordbreak-5-ref.html
 fails-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)) == wordbreak-6.html wordbreak-6-ref.html # Bug 1258239
 HTTP(..) == wordbreak-7a.html wordbreak-7a-ref.html
 fails HTTP(..) == wordbreak-7b.html wordbreak-7b-ref.html # bug 479829
@@ -171,17 +171,17 @@ random-if(!winWidget) == arial-bold-lam-
 # in the 'cmap' will prevent the test font (without GSUB) being used.
 fails-if(cocoaWidget||Android) HTTP(..) == arabic-fallback-1.html arabic-fallback-1-ref.html
 fails-if(cocoaWidget||Android) HTTP(..) == arabic-fallback-2.html arabic-fallback-2-ref.html
 fails-if(cocoaWidget||Android) HTTP(..) == arabic-fallback-3.html arabic-fallback-3-ref.html
 fails-if(!cocoaWidget&&!Android) HTTP(..) != arabic-fallback-4.html arabic-fallback-4-notref.html
 == arabic-marks-1.html arabic-marks-1-ref.html
 == arabic-final-ligature-spacing.html arabic-final-ligature-spacing-ref.html
 # harfbuzz fallback mark stacking in the absence of GPOS:
-HTTP(..) != fallback-mark-stacking-1.html fallback-mark-stacking-1-notref.html
+fails-if(webrender) HTTP(..) != fallback-mark-stacking-1.html fallback-mark-stacking-1-notref.html
 
 == 726392-1.html 726392-1-ref.html
 == 726392-2.html 726392-2-ref.html
 == 726392-3.html 726392-3-ref.html
 == 745555-1.html 745555-1-ref.html
 == 745555-2.html 745555-2-ref.html
 == 820255.html 820255-ref.html
 HTTP(..) != 1170688.html 1170688-ref.html
--- a/layout/reftests/transform-3d/reftest.list
+++ b/layout/reftests/transform-3d/reftest.list
@@ -6,17 +6,17 @@ fuzzy-if(webrender,0-1,0-6) == rotatey-1
 # Check that the perspectve() transform function results in some visual changes
 != rotatex-perspective-1a.html rotatex-1-ref.html
 # Check that -moz-perspective results in visual changes to child transformed elements
 != rotatex-perspective-1b.html rotatex-1-ref.html
 # -moz-perspective should only apply to child elements
 == rotatex-perspective-1c.html rotatex-1-ref.html
 == rotatex-perspective-3a.html rotatex-perspective-3-ref.html
 == scalez-1a.html scalez-1-ref.html
-fuzzy-if(gtkWidget||winWidget,8,376) fuzzy-if(Android,8,441) fuzzy-if(cocoaWidget,17,4) fuzzy-if(skiaContent,16,286) fails-if(webrender) == preserve3d-1a.html preserve3d-1-ref.html
+fuzzy-if(gtkWidget||winWidget,8,376) fuzzy-if(Android,8,441) fuzzy-if(cocoaWidget,17,4) fuzzy-if(skiaContent,16,286) == preserve3d-1a.html preserve3d-1-ref.html
 == preserve3d-1b.html about:blank
 == preserve3d-clipped.html about:blank
 == preserve3d-2a.html preserve3d-2-ref.html
 == preserve3d-2b.html preserve3d-2-ref.html
 == preserve3d-2c.html preserve3d-2-ref.html
 == preserve3d-2d.html preserve3d-2-ref.html
 == preserve3d-3a.html preserve3d-3-ref.html
 == preserve3d-4a.html about:blank
--- a/layout/reftests/w3c-css/failures.list
+++ b/layout/reftests/w3c-css/failures.list
@@ -106,23 +106,22 @@ fuzzy-if(OSX||winWidget,135,1080) css-wr
 fuzzy-if(OSX||winWidget,111,960)  css-writing-modes-3/inline-block-alignment-srl-008.xht
 fuzzy-if(OSX||winWidget,213,1540) css-writing-modes-3/line-box-direction-???-0??.xht
 fuzzy-if(OSX||winWidget,110,1200) css-writing-modes-3/row-progression-???-0??.xht
 fuzzy-if(OSX||winWidget,110,1200) css-writing-modes-3/table-column-order-00?.xht
 fuzzy-if(winWidget,110,1200) css-writing-modes-3/table-column-order-slr-007.xht
 fuzzy-if(OSX||winWidget,110,1200) css-writing-modes-3/table-column-order-srl-006.xht
 fuzzy-if(OSX||winWidget,75,404)   css-writing-modes-3/text-align-v??-0??.xht
 fuzzy-if(OSX||winWidget,215,780)  css-writing-modes-3/text-baseline-???-00?.xht
-fuzzy-if(OSX,15,16)    css-writing-modes-3/text-combine-upright-decorations-001.html
+fuzzy-if(OSX,15,16) fails-if(webrender) css-writing-modes-3/text-combine-upright-decorations-001.html
 fuzzy-if(OSX||winWidget,255,480)  css-writing-modes-3/text-indent-v??-0??.xht
 fuzzy-if(OSX||winWidget,226,960)  css-writing-modes-3/text-orientation-016.xht
 fuzzy-if(OSX||winWidget,223,720)  css-writing-modes-3/vertical-alignment-*.xht
 fuzzy-if(OSX||winWidget,153,612)  css-writing-modes-3/writing-mode-vertical-??-00?.*
-fuzzy(255,960) css-writing-modes-3/text-combine-upright-value-all-00?.html
-fuzzy(255,960) css-writing-modes-3/text-combine-upright-value-all-001.html
+fuzzy(255,960) random-if(webrender) css-writing-modes-3/text-combine-upright-value-all-00?.html
 
 # Bug 1167911
 skip css-writing-modes-3/abs-pos-non-replaced-icb-vlr-021.xht
 skip css-writing-modes-3/abs-pos-non-replaced-icb-vrl-020.xht
 
 # Bug 1244601
 fails css-writing-modes-3/block-flow-direction-slr-058.xht
 fails css-writing-modes-3/block-flow-direction-srl-057.xht
--- a/layout/reftests/w3c-css/received/reftest.list
+++ b/layout/reftests/w3c-css/received/reftest.list
@@ -1068,26 +1068,26 @@ fuzzy-if(OSX||winWidget,75,404) == css-w
 fuzzy-if(OSX||winWidget,215,780) == css-writing-modes-3/text-baseline-slr-009.xht css-writing-modes-3/text-baseline-slr-009-ref.xht
 fuzzy-if(OSX||winWidget,215,780) == css-writing-modes-3/text-baseline-srl-008.xht css-writing-modes-3/text-baseline-vrl-006-ref.xht
 fuzzy-if(OSX||winWidget,215,780) == css-writing-modes-3/text-baseline-vlr-003.xht css-writing-modes-3/text-baseline-vrl-002-ref.xht
 fuzzy-if(OSX||winWidget,215,780) == css-writing-modes-3/text-baseline-vlr-005.xht css-writing-modes-3/text-baseline-vrl-002-ref.xht
 fuzzy-if(OSX||winWidget,215,780) == css-writing-modes-3/text-baseline-vlr-007.xht css-writing-modes-3/text-baseline-vrl-006-ref.xht
 fuzzy-if(OSX||winWidget,215,780) == css-writing-modes-3/text-baseline-vrl-002.xht css-writing-modes-3/text-baseline-vrl-002-ref.xht
 fuzzy-if(OSX||winWidget,215,780) == css-writing-modes-3/text-baseline-vrl-004.xht css-writing-modes-3/text-baseline-vrl-002-ref.xht
 fuzzy-if(OSX||winWidget,215,780) == css-writing-modes-3/text-baseline-vrl-006.xht css-writing-modes-3/text-baseline-vrl-006-ref.xht
-fuzzy-if(OSX,15,16) == css-writing-modes-3/text-combine-upright-decorations-001.html css-writing-modes-3/reference/text-combine-upright-decorations-001.html
+fuzzy-if(OSX,15,16) fails-if(webrender) == css-writing-modes-3/text-combine-upright-decorations-001.html css-writing-modes-3/reference/text-combine-upright-decorations-001.html
 == css-writing-modes-3/text-combine-upright-inherit-all-001.html css-writing-modes-3/reference/text-combine-upright-inherit-all-001.html
 == css-writing-modes-3/text-combine-upright-inherit-all-002.html css-writing-modes-3/reference/text-combine-upright-inherit-all-002.html
 == css-writing-modes-3/text-combine-upright-layout-rules-001.html css-writing-modes-3/reference/text-combine-upright-layout-rules-001-ref.html
 == css-writing-modes-3/text-combine-upright-line-breaking-rules-001.html css-writing-modes-3/text-combine-upright-line-breaking-rules-001-ref.html
-fuzzy(255,960) == css-writing-modes-3/text-combine-upright-value-all-001.html css-writing-modes-3/reference/text-combine-upright-value-single-character.html
-fuzzy(255,960) == css-writing-modes-3/text-combine-upright-value-all-002.html css-writing-modes-3/reference/vertical-ahem-1x1-ref.html
-fuzzy(255,960) != css-writing-modes-3/text-combine-upright-value-all-002.html css-writing-modes-3/reference/horizontal-ahem-1x1-notref.html
-fuzzy(255,960) == css-writing-modes-3/text-combine-upright-value-all-003.html css-writing-modes-3/reference/vertical-ahem-1x1-ref.html
-fuzzy(255,960) != css-writing-modes-3/text-combine-upright-value-all-003.html css-writing-modes-3/reference/horizontal-ahem-1x1-notref.html
+fuzzy(255,960) random-if(webrender) == css-writing-modes-3/text-combine-upright-value-all-001.html css-writing-modes-3/reference/text-combine-upright-value-single-character.html
+fuzzy(255,960) random-if(webrender) == css-writing-modes-3/text-combine-upright-value-all-002.html css-writing-modes-3/reference/vertical-ahem-1x1-ref.html
+fuzzy(255,960) random-if(webrender) != css-writing-modes-3/text-combine-upright-value-all-002.html css-writing-modes-3/reference/horizontal-ahem-1x1-notref.html
+fuzzy(255,960) random-if(webrender) == css-writing-modes-3/text-combine-upright-value-all-003.html css-writing-modes-3/reference/vertical-ahem-1x1-ref.html
+fuzzy(255,960) random-if(webrender) != css-writing-modes-3/text-combine-upright-value-all-003.html css-writing-modes-3/reference/horizontal-ahem-1x1-notref.html
 skip == css-writing-modes-3/text-combine-upright-value-digits2-001.html css-writing-modes-3/reference/text-combine-upright-value-single-character.html
 skip == css-writing-modes-3/text-combine-upright-value-digits2-002.html css-writing-modes-3/reference/vertical-ahem-1x1-ref.html
 skip != css-writing-modes-3/text-combine-upright-value-digits2-002.html css-writing-modes-3/reference/horizontal-ahem-1x1-notref.html
 == css-writing-modes-3/text-combine-upright-value-digits2-003.html css-writing-modes-3/reference/vertical-ahem-1x3-ref.html
 != css-writing-modes-3/text-combine-upright-value-digits2-003.html css-writing-modes-3/reference/horizontal-ahem-1x3-notref.html
 skip == css-writing-modes-3/text-combine-upright-value-digits3-001.html css-writing-modes-3/reference/vertical-ahem-1x1-ref.html
 skip != css-writing-modes-3/text-combine-upright-value-digits3-001.html css-writing-modes-3/reference/horizontal-ahem-1x1-notref.html
 skip == css-writing-modes-3/text-combine-upright-value-digits3-002.html css-writing-modes-3/reference/vertical-ahem-1x1-ref.html
--- a/layout/reftests/w3c-css/submitted/selectors4/reftest.list
+++ b/layout/reftests/w3c-css/submitted/selectors4/reftest.list
@@ -1,12 +1,12 @@
 needs-focus == focus-within-1.html focus-within-1-ref.html
 needs-focus == focus-within-2.html focus-within-2-ref.html
 needs-focus == focus-within-3.html focus-within-3-ref.html
-fails-if(webrender) == dir-style-01a.html dir-style-01-ref.html
-fails-if(webrender) == dir-style-01b.html dir-style-01-ref.html
+== dir-style-01a.html dir-style-01-ref.html
+== dir-style-01b.html dir-style-01-ref.html
 == dir-style-02a.html dir-style-02-ref.html
 == dir-style-02b.html dir-style-02-ref.html
 == dir-style-03a.html dir-style-03-ref.html
 == dir-style-03b.html dir-style-03-ref.html
 == dir-style-04.html dir-style-04-ref.html
 == child-index-no-parent-01.html child-index-no-parent-01-ref.html
 == class-id-attr-selector-invalidation-01.html class-id-attr-selector-invalidation-01-ref.html
--- a/layout/reftests/writing-mode/reftest.list
+++ b/layout/reftests/writing-mode/reftest.list
@@ -26,17 +26,17 @@ fuzzy-if(azureSkia,255,2700) == 1090168-
 == 1096224-1b.html 1096224-1-ref.html
 fails == 1102175-1a.html 1102175-1-ref.html
 == 1102175-1b.html 1102175-1-ref.html
 == 1103613-1.html 1103613-1-ref.html
 == 1105268-1-min-max-dimensions.html 1105268-1-min-max-dimensions-ref.html
 == 1105268-2-min-max-dimensions.html 1105268-2-min-max-dimensions-ref.html
 == 1106669-1-intrinsic-for-container.html 1106669-1-intrinsic-for-container-ref.html
 == 1108923-1-percentage-margins.html 1108923-1-percentage-margins-ref.html
-== 1111944-1-list-marker.html 1111944-1-list-marker-ref.html
+fails-if(webrender) == 1111944-1-list-marker.html 1111944-1-list-marker-ref.html
 fuzzy(116,94) fuzzy-if(winWidget,135,124) HTTP(..) == 1115916-1-vertical-metrics.html 1115916-1-vertical-metrics-ref.html
 == 1117210-1-vertical-baseline-snap.html 1117210-1-vertical-baseline-snap-ref.html
 random-if(webrender) == 1117227-1-text-overflow.html 1117227-1-text-overflow-ref.html
 == 1122366-1-margin-collapse.html 1122366-1-margin-collapse-ref.html
 == 1124636-1-fieldset-max-height.html 1124636-1-fieldset-max-height-ref.html
 == 1124636-2-fieldset-min-height.html 1124636-2-fieldset-min-height-ref.html
 
 == ua-style-sheet-margin-1.html ua-style-sheet-margin-1-ref.html
@@ -61,19 +61,21 @@ random-if(webrender) == 1117227-1-text-o
 == ua-style-sheet-size-1.html ua-style-sheet-size-1-ref.html
 == ua-style-sheet-size-2.html ua-style-sheet-size-2-ref.html
 
 == ua-style-sheet-fieldset-1.html ua-style-sheet-fieldset-1-ref.html
 skip-if(Android||winWidget) == ua-style-sheet-textarea-1.html ua-style-sheet-textarea-1a-ref.html
 skip-if(!(Android)) == ua-style-sheet-textarea-1.html ua-style-sheet-textarea-1b-ref.html
 skip-if(!winWidget) == ua-style-sheet-textarea-1.html ua-style-sheet-textarea-1c-ref.html
 fuzzy-if(Android,1,18) == ua-style-sheet-checkbox-radio-1.html ua-style-sheet-checkbox-radio-1-ref.html
+# Android uses different margins for buttons compared to the ref.
 skip-if(Android) fuzzy-if(skiaContent&&!Android,2,6) == ua-style-sheet-button-1.html ua-style-sheet-button-1a-ref.html
 skip-if(!(Android)) == ua-style-sheet-button-1.html ua-style-sheet-button-1b-ref.html
-== ua-style-sheet-input-color-1.html ua-style-sheet-input-color-1-ref.html
+# Android uses different margins for buttons compared to the ref.
+skip-if(Android) == ua-style-sheet-input-color-1.html ua-style-sheet-input-color-1-ref.html
 fuzzy-if(gtkWidget,1,15) == ua-style-sheet-input-number-1.html ua-style-sheet-input-number-1-ref.html
 
 HTTP(..) == 1127488-align-default-horizontal-tb-ltr.html 1127488-align-top-left-ref.html
 HTTP(..) == 1127488-align-start-horizontal-tb-ltr.html 1127488-align-top-left-ref.html
 HTTP(..) == 1127488-align-end-horizontal-tb-ltr.html 1127488-align-top-right-ref.html
 HTTP(..) == 1127488-align-left-horizontal-tb-ltr.html 1127488-align-top-left-ref.html
 HTTP(..) == 1127488-align-right-horizontal-tb-ltr.html 1127488-align-top-right-ref.html
 HTTP(..) == 1127488-align-default-horizontal-tb-rtl.html 1127488-align-top-right-ref.html
--- a/layout/reftests/writing-mode/ua-style-sheet-input-color-1-ref.html
+++ b/layout/reftests/writing-mode/ua-style-sheet-input-color-1-ref.html
@@ -1,24 +1,24 @@
 <!DOCTYPE html>
 <meta charset=utf-8>
 <title>Test for logical properties of input type=color in the UA stylesheet</title>
 <style>
 .v-rl { writing-mode: vertical-rl; }
 .ltr, .rtl, .v-rl { border: 1px solid blue; }
 .a { -moz-appearance: none; }
 
-.ltr input[type="color"]:-moz-system-metric(color-picker-available),
-.rtl input[type="color"]:-moz-system-metric(color-picker-available) {
+.ltr input[type="color"],
+.rtl input[type="color"] {
   width: 64px;
   height: 23px;
   padding: 0px 8px;
 }
 
-.v-rl input[type="color"]:-moz-system-metric(color-picker-available) {
+.v-rl input[type="color"] {
   height: 64px;
   width: 23px;
   padding: 8px 0px;
 }
 </style>
 <div class=ltr>
   <input type=color><br>
   <input type=color class=a><br>
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -396,17 +396,17 @@ Gecko_CalcStyleDifference(ServoStyleCont
 
   uint32_t equalStructs;
   uint32_t samePointerStructs;  // unused
   nsChangeHint result = const_cast<ServoStyleContext*>(aOldStyle)->
     CalcStyleDifference(
       const_cast<ServoStyleContext*>(aNewStyle),
       &equalStructs,
       &samePointerStructs,
-      NS_STYLE_INHERIT_MASK);
+      /* aIgnoreVariables = */ true);
 
   *aAnyStyleChanged = equalStructs != NS_STYLE_INHERIT_MASK;
 
   const uint32_t kInheritStructsMask =
     NS_STYLE_INHERIT_MASK & ~NS_STYLE_RESET_STRUCT_MASK;
 
   *aOnlyResetStructsChanged =
     (equalStructs & kInheritStructsMask) == kInheritStructsMask;
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -423,16 +423,55 @@ public:
                                          aKeyframes);
   }
   void SetKeyframes(KeyframeEffectReadOnly& aEffect,
                     nsTArray<Keyframe>&& aKeyframes)
   {
     aEffect.SetKeyframes(Move(aKeyframes), mStyleContext);
   }
 
+  // Currently all the animation building code in this file is based on
+  // assumption that creating and removing animations should *not* trigger
+  // additional restyles since those changes will be handled within the same
+  // restyle.
+  //
+  // While that is true for the Gecko style backend, it is not true for the
+  // Servo style backend where we want restyles to be triggered so that we
+  // perform a second animation restyle where we will incorporate the changes
+  // arising from creating and removing animations.
+  //
+  // Fortunately, our attempts to avoid posting extra restyles as part of the
+  // processing here are imperfect and most of the time we happen to post
+  // them anyway. Occasionally, however, we don't. For example, we don't post
+  // a restyle when we create a new animation whose an animation index matches
+  // the default value it was given already (which is typically only true when
+  // the CSSAnimation we create is the first Animation created in a particular
+  // content process).
+  //
+  // As a result, when we are using the Servo backend, whenever we have an added
+  // or removed animation we need to explicitly trigger a restyle.
+  //
+  // This code should eventually disappear along with the Gecko style backend
+  // and we should simply call Play() / Pause() / Cancel() etc. which will
+  // post the required restyles.
+  void NotifyNewOrRemovedAnimation(const Animation& aAnimation)
+  {
+    AnimationEffectReadOnly* effect = aAnimation.GetEffect();
+    if (!effect) {
+      return;
+    }
+
+    KeyframeEffectReadOnly* keyframeEffect = effect->AsKeyframeEffect();
+    if (!keyframeEffect) {
+      return;
+    }
+
+    keyframeEffect->RequestRestyle(EffectCompositor::RestyleType::Standard);
+  }
+
 private:
   const ServoStyleContext* mStyleContext;
 };
 
 class MOZ_STACK_CLASS GeckoCSSAnimationBuilder final {
 public:
   GeckoCSSAnimationBuilder(GeckoStyleContext* aStyleContext,
                            const NonOwningAnimationTarget& aTarget)
@@ -447,16 +486,18 @@ public:
                       const StyleAnimation& aSrc,
                       nsTArray<Keyframe>& aKeyframs);
   void SetKeyframes(KeyframeEffectReadOnly& aEffect,
                     nsTArray<Keyframe>&& aKeyframes)
   {
     aEffect.SetKeyframes(Move(aKeyframes), mStyleContext);
   }
 
+  void NotifyNewOrRemovedAnimation(const Animation&) {}
+
 private:
   nsTArray<Keyframe> BuildAnimationFrames(nsPresContext* aPresContext,
                                           const StyleAnimation& aSrc,
                                           const nsCSSKeyframesRule* aRule);
   Maybe<ComputedTimingFunction> GetKeyframeTimingFunction(
     nsPresContext* aPresContext,
     nsCSSKeyframeRule* aKeyframeRule,
     const Maybe<ComputedTimingFunction>& aInheritedTimingFunction);
@@ -600,16 +641,18 @@ BuildAnimation(nsPresContext* aPresConte
   animation->SetEffectNoUpdate(effect);
 
   if (isStylePaused) {
     animation->PauseFromStyle();
   } else {
     animation->PlayFromStyle();
   }
 
+  aBuilder.NotifyNewOrRemovedAnimation(*animation);
+
   return animation.forget();
 }
 
 bool
 GeckoCSSAnimationBuilder::BuildKeyframes(nsPresContext* aPresContext,
                                          const StyleAnimation& aSrc,
                                          nsTArray<Keyframe>& aKeyframes)
 {
@@ -1094,11 +1137,12 @@ nsAnimationManager::DoUpdateAnimations(
     if (createdCollection) {
       AddElementCollection(collection);
     }
   }
   collection->mAnimations.SwapElements(newAnimations);
 
   // Cancel removed animations
   for (size_t newAnimIdx = newAnimations.Length(); newAnimIdx-- != 0; ) {
+    aBuilder.NotifyNewOrRemovedAnimation(*newAnimations[newAnimIdx]);
     newAnimations[newAnimIdx]->CancelFromStyle();
   }
 }
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -1139,21 +1139,16 @@ nsCSSRuleProcessor::InitSystemMetrics()
     sSystemMetrics->AppendElement(nsGkAtoms::windows_compositor);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsGlass, &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
     sSystemMetrics->AppendElement(nsGkAtoms::windows_glass);
   }
 
-  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_ColorPickerAvailable, &metricResult);
-  if (NS_SUCCEEDED(rv) && metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::color_picker_available);
-  }
-
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsClassic, &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
     sSystemMetrics->AppendElement(nsGkAtoms::windows_classic);
   }
 
   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_TouchEnabled, &metricResult);
   if (NS_SUCCEEDED(rv) && metricResult) {
     sSystemMetrics->AppendElement(nsGkAtoms::touch_enabled);
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -629,24 +629,16 @@ nsMediaFeatures::features[] = {
     &nsGkAtoms::_moz_is_resource_document,
     nsMediaFeature::eMinMaxNotAllowed,
     nsMediaFeature::eBoolInteger,
     nsMediaFeature::eNoRequirements,
     { nullptr },
     GetIsResourceDocument
   },
   {
-    &nsGkAtoms::_moz_color_picker_available,
-    nsMediaFeature::eMinMaxNotAllowed,
-    nsMediaFeature::eBoolInteger,
-    nsMediaFeature::eNoRequirements,
-    { &nsGkAtoms::color_picker_available },
-    GetSystemMetric
-  },
-  {
     &nsGkAtoms::_moz_scrollbar_start_backward,
     nsMediaFeature::eMinMaxNotAllowed,
     nsMediaFeature::eBoolInteger,
     nsMediaFeature::eNoRequirements,
     { &nsGkAtoms::scrollbar_start_backward },
     GetSystemMetric
   },
   {
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -101,29 +101,25 @@ nsStyleContext::nsStyleContext(nsAtom* a
                 "Number of items in dependency table doesn't match IDs");
 #endif
 }
 
 nsChangeHint
 nsStyleContext::CalcStyleDifference(nsStyleContext* aNewContext,
                                     uint32_t* aEqualStructs,
                                     uint32_t* aSamePointerStructs,
-                                    uint32_t aRelevantStructs)
+                                    bool aIgnoreVariables)
 {
   AUTO_PROFILER_LABEL("nsStyleContext::CalcStyleDifference", CSS);
 
   static_assert(nsStyleStructID_Length <= 32,
                 "aEqualStructs is not big enough");
 
-  MOZ_ASSERT(aRelevantStructs == kAllResolvedStructs || IsServo(),
-             "aRelevantStructs must be kAllResolvedStructs for Gecko contexts");
-
-  if (aRelevantStructs == kAllResolvedStructs) {
-    aRelevantStructs = mBits & NS_STYLE_INHERIT_MASK;
-  }
+  MOZ_ASSERT(!aIgnoreVariables || IsServo(),
+             "aIgnoreVariables must be false for Gecko contexts");
 
   *aEqualStructs = 0;
   *aSamePointerStructs = 0;
 
   nsChangeHint hint = nsChangeHint(0);
   NS_ENSURE_TRUE(aNewContext, hint);
   // We must always ensure that we populate the structs on the new style
   // context that are filled in on the old context, so that if we get
@@ -153,17 +149,18 @@ nsStyleContext::CalcStyleDifference(nsSt
         *aEqualStructs |= NS_STYLE_INHERIT_BIT(Variables);
       } else if (thisVariables->mVariables == otherVariables->mVariables) {
         *aEqualStructs |= NS_STYLE_INHERIT_BIT(Variables);
       }
     } else {
       *aEqualStructs |= NS_STYLE_INHERIT_BIT(Variables);
     }
   } else {
-    if (Servo_ComputedValues_EqualCustomProperties(
+    if (aIgnoreVariables ||
+        Servo_ComputedValues_EqualCustomProperties(
           AsServo()->ComputedData(),
           aNewContext->ComputedData())) {
       *aEqualStructs |= NS_STYLE_INHERIT_BIT(Variables);
     }
   }
 
   DebugOnly<int> styleStructCount = 1;  // count Variables already
 
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -236,49 +236,38 @@ public:
    *
    * Perhaps this shouldn't be a public nsStyleContext API.
    */
   #define STYLE_STRUCT(name_, checkdata_cb_)  \
     inline const nsStyle##name_ * PeekStyle##name_();
   #include "nsStyleStructList.h"
   #undef STYLE_STRUCT
 
-  // Value that can be passed as CalcStyleDifference's aRelevantStructs
-  // argument to indicate that all structs that are currently resolved on the
-  // old style context should be compared.  This is only relevant for
-  // ServoStyleContexts.
-  enum { kAllResolvedStructs = 0xffffffff };
-  static_assert(kAllResolvedStructs != NS_STYLE_INHERIT_MASK,
-                "uint32_t not big enough for special kAllResolvedStructs value");
-
   /**
    * Compute the style changes needed during restyling when this style
    * context is being replaced by aNewContext.  (This is nonsymmetric since
    * we optimize by skipping comparison for styles that have never been
    * requested.)
    *
    * This method returns a change hint (see nsChangeHint.h).  All change
    * hints apply to the frame and its later continuations or ib-split
    * siblings.  Most (all of those except the "NotHandledForDescendants"
    * hints) also apply to all descendants.
    *
    * aEqualStructs must not be null.  Into it will be stored a bitfield
    * representing which structs were compared to be non-equal.
    *
-   * aRelevantStructs must be kAllResolvedStructs for GeckoStyleContexts.
-   * For ServoStyleContexts, it controls which structs will be compared.
-   * This is needed because in some cases, we can't rely on mBits in the
-   * old style context to accurately reflect which are the relevant
-   * structs to be compared.
+   * aIgnoreVariables indicates whether to skip comparing the Variables
+   * struct.  This must only be true for Servo style contexts.  When
+   * true, the Variables bit in aEqualStructs will be set.
    */
   nsChangeHint CalcStyleDifference(nsStyleContext* aNewContext,
                                    uint32_t* aEqualStructs,
                                    uint32_t* aSamePointerStructs,
-                                   uint32_t aRelevantStructs =
-                                     kAllResolvedStructs);
+				   bool aIgnoreVariables = false);
 
 public:
   /**
    * Get a color that depends on link-visitedness using this and
    * this->GetStyleIfVisited().
    *
    * @param aField A pointer to a member variable in a style struct.
    *               The member variable and its style struct must have
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -514,17 +514,17 @@ input[type="file"] > xul|label {
 input[type="file"] > button[type="button"] {
   block-size: inherit;
   font-size: inherit;
   letter-spacing: inherit;
   cursor: inherit;
 }
 
 /* colored part of the color selector button */
-input[type="color"]:-moz-system-metric(color-picker-available)::-moz-color-swatch {
+input[type="color"]::-moz-color-swatch {
   width: 100%;
   height: 100%;
   min-width: 3px;
   min-height: 3px;
   margin-inline-start: auto;
   margin-inline-end: auto;
   box-sizing: border-box;
   border: 1px solid grey;
@@ -601,17 +601,17 @@ input[type="search"] {
 /* buttons */
 
 /* Note: Values in nsNativeTheme IsWidgetStyled function
    need to match button background/border values here */
 
 /* Non text-related properties for buttons: these ones are shared with
    input[type="color"] */
 button,
-input[type="color"]:-moz-system-metric(color-picker-available),
+input[type="color"],
 input[type="reset"],
 input[type="button"],
 input[type="submit"] {
   -moz-appearance: button;
   /* The sum of border and padding on block-start and block-end
      must be the same here, for text inputs, and for <select>.
      Note -moz-focus-inner padding does not affect button size. */
   padding-block-start: 0px;
@@ -636,17 +636,17 @@ input[type="submit"] {
   font: -moz-button;
   line-height: normal;
   white-space: pre;
   text-align: center;
   text-shadow: none;
   overflow-clip-box: padding-box;
 }
 
-input[type="color"]:-moz-system-metric(color-picker-available) {
+input[type="color"] {
   inline-size: 64px;
   block-size: 23px;
 }
 
 button {
   /* Buttons should lay out like "normal" html, mostly */
   white-space: inherit;
   text-indent: 0;
@@ -684,32 +684,32 @@ button {
   /* CSS Align */
   align-content: inherit;
   align-items: inherit;
   justify-content: inherit;
   justify-items: inherit;
 }
 
 button:hover,
-input[type="color"]:-moz-system-metric(color-picker-available):hover,
+input[type="color"]:hover,
 input[type="reset"]:hover,
 input[type="button"]:hover,
 input[type="submit"]:hover {
   background-color: -moz-buttonhoverface;
 }
 
 button:hover,
 input[type="reset"]:hover,
 input[type="button"]:hover,
 input[type="submit"]:hover {
   color: -moz-buttonhovertext;
 }
 
 button:active:hover,
-input[type="color"]:-moz-system-metric(color-picker-available):active:hover,
+input[type="color"]:active:hover,
 input[type="reset"]:active:hover,
 input[type="button"]:active:hover,
 input[type="submit"]:active:hover {
 %ifndef XP_MACOSX
   padding-block-start: 0px;
   padding-inline-end: 7px;
   padding-block-end: 0px;
   padding-inline-start: 9px;
@@ -721,41 +721,41 @@ input[type="submit"]:active:hover {
 button:active:hover,
 input[type="reset"]:active:hover,
 input[type="button"]:active:hover,
 input[type="submit"]:active:hover {
   color: ButtonText;
 }
 
 button::-moz-focus-inner,
-input[type="color"]:-moz-system-metric(color-picker-available)::-moz-focus-inner,
+input[type="color"]::-moz-focus-inner,
 input[type="reset"]::-moz-focus-inner,
 input[type="button"]::-moz-focus-inner,
 input[type="submit"]::-moz-focus-inner,
 input[type="file"] > button[type="button"]::-moz-focus-inner {
   /* Note this padding only affects the -moz-focus-inner ring, not the button itself */
   padding-block-start: 0px;
   padding-inline-end: 2px;
   padding-block-end: 0px;
   padding-inline-start: 2px;
   border: 1px dotted transparent;
 }
 
 button:-moz-focusring::-moz-focus-inner,
-input[type="color"]:-moz-system-metric(color-picker-available):-moz-focusring::-moz-focus-inner,
+input[type="color"]:-moz-focusring::-moz-focus-inner,
 input[type="reset"]:-moz-focusring::-moz-focus-inner,
 input[type="button"]:-moz-focusring::-moz-focus-inner,
 input[type="submit"]:-moz-focusring::-moz-focus-inner,
 input[type="file"] > button[type="button"]:-moz-focusring::-moz-focus-inner {
   border-color: ButtonText;
 }
 
 button:disabled:active, button:disabled,
-input[type="color"]:-moz-system-metric(color-picker-available):disabled:active,
-input[type="color"]:-moz-system-metric(color-picker-available):disabled,
+input[type="color"]:disabled:active,
+input[type="color"]:disabled,
 input[type="reset"]:disabled:active,
 input[type="reset"]:disabled,
 input[type="button"]:disabled:active,
 input[type="button"]:disabled,
 select:disabled > button,
 select:disabled > button,
 input[type="submit"]:disabled:active,
 input[type="submit"]:disabled {
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -8082,17 +8082,18 @@ nsTableFrame::UpdateStyleOfOwnedAnonBoxe
   //
   // NOTE(emilio): We can't use the ChangesHandledFor optimization (and we
   // assert against that), because the table wrapper is up in the frame tree
   // compared to the owner frame.
   uint32_t equalStructs, samePointerStructs; // Not used, actually.
   nsChangeHint wrapperHint = aWrapperFrame->StyleContext()->CalcStyleDifference(
     newContext,
     &equalStructs,
-    &samePointerStructs);
+    &samePointerStructs,
+    /* aIgnoreVariables = */ true);
 
   // CalcStyleDifference will handle caching structs on the new style context,
   // but only if we're not on a style worker thread.
   MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal(),
              "if we can get in here from style worker threads, then we need "
              "a ResolveSameStructsAs call to ensure structs are cached on "
              "aNewStyleContext");
 
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -303,17 +303,17 @@ class RefTest(object):
             prefs['browser.tabs.remote.autostart'] = True
             prefs['extensions.e10sBlocksEnabling'] = False
 
         # Bug 1262954: For winXP + e10s disable acceleration
         if platform.system() in ("Windows", "Microsoft") and \
            '5.1' in platform.version() and options.e10s:
             prefs['layers.acceleration.disabled'] = True
 
-        sandbox_whitelist_paths = [SCRIPT_DIRECTORY] + options.sandboxReadWhitelist
+        sandbox_whitelist_paths = options.sandboxReadWhitelist
         if (platform.system() == "Linux" or
             platform.system() in ("Windows", "Microsoft")):
             # Trailing slashes are needed to indicate directories on Linux and Windows
             sandbox_whitelist_paths = map(lambda p: os.path.join(p, ""),
                                           sandbox_whitelist_paths)
 
         # Bug 1300355: Disable canvas cache for win7 as it uses
         # too much memory and causes OOMs.
--- a/media/webrtc/signaling/gtest/mediaconduit_unittests.cpp
+++ b/media/webrtc/signaling/gtest/mediaconduit_unittests.cpp
@@ -8,16 +8,17 @@
 #include <vector>
 #include <math.h>
 
 using namespace std;
 
 #include <MediaConduitInterface.h>
 #include <VideoConduit.h>
 #include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
 #include "nss.h"
 #include "runnable_utils.h"
 #include "signaling/src/common/EncodingConstraints.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 
 const uint32_t SSRC = 1;
@@ -219,17 +220,17 @@ void AudioSendAndReceive::GenerateAndRea
      free(inbuf);
      fclose(inFile);
      return;
    }
 
    //Create input file with the music
    WriteWaveHeader(PLAYOUT_SAMPLE_FREQUENCY, 1, inFile);
    GenerateMusic(inbuf, SAMPLES);
-   fwrite(inbuf,1,SAMPLES*sizeof(inbuf[0])*CHANNELS,inFile);
+   mozilla::Unused << fwrite(inbuf,1,SAMPLES*sizeof(inbuf[0])*CHANNELS,inFile);
    FinishWaveHeader(inFile);
    fclose(inFile);
 
    WriteWaveHeader(PLAYOUT_SAMPLE_FREQUENCY, 1, outFile);
    unsigned int numSamplesReadFromInput = 0;
    do
    {
     if(!memcpy(audioInput.get(), inbuf, sampleLengthInBytes))
old mode 100644
new mode 100755
--- a/memory/mozalloc/mozalloc.h
+++ b/memory/mozalloc/mozalloc.h
@@ -225,28 +225,44 @@ void* operator new[](size_t size, const 
 }
 
 MOZALLOC_EXPORT_NEW MOZALLOC_INLINE
 void operator delete(void* ptr) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
 {
     return free_impl(ptr);
 }
 
+#if __cplusplus >= 201402L
+MOZALLOC_EXPORT_NEW MOZALLOC_INLINE
+void operator delete(void* ptr, size_t size) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
+{
+    return free_impl(ptr);
+}
+#endif
+
 MOZALLOC_EXPORT_NEW MOZALLOC_INLINE
 void operator delete(void* ptr, const std::nothrow_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
 {
     return free_impl(ptr);
 }
 
 MOZALLOC_EXPORT_NEW MOZALLOC_INLINE
 void operator delete[](void* ptr) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
 {
     return free_impl(ptr);
 }
 
+#if __cplusplus >= 201402L
+MOZALLOC_EXPORT_NEW MOZALLOC_INLINE
+void operator delete[](void* ptr, size_t size) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
+{
+    return free_impl(ptr);
+}
+#endif
+
 MOZALLOC_EXPORT_NEW MOZALLOC_INLINE
 void operator delete[](void* ptr, const std::nothrow_t&) MOZALLOC_THROW_IF_HAS_EXCEPTIONS
 {
     return free_impl(ptr);
 }
 
 
 /*
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java
@@ -3,26 +3,26 @@
  * 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/. */
 
 package org.mozilla.gecko;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
-import java.util.concurrent.SynchronousQueue;
 
 import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
+import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.GamepadUtils;
 import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
 
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
+import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Matrix;
 import android.graphics.RectF;
 import android.media.AudioManager;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
@@ -212,17 +212,17 @@ class GeckoInputConnection
         View view = getView();
         if (view == null) {
             return null;
         }
         Context context = view.getContext();
         return InputMethods.getInputMethodManager(context);
     }
 
-    private void showSoftInput() {
+    private void showSoftInputWithToolbar(final boolean showToolbar) {
         if (mSoftInputReentrancyGuard) {
             return;
         }
         final View v = getView();
         final InputMethodManager imm = getInputMethodManager();
         if (v == null || imm == null) {
             return;
         }
@@ -232,17 +232,17 @@ class GeckoInputConnection
             public void run() {
                 if (v.hasFocus() && !imm.isActive(v)) {
                     // Marshmallow workaround: The view has focus but it is not the active
                     // view for the input method. (Bug 1211848)
                     v.clearFocus();
                     v.requestFocus();
                 }
                 final GeckoView view = getView();
-                if (view != null) {
+                if (view != null && showToolbar) {
                     view.getDynamicToolbarAnimator().showToolbar(/*immediately*/ true);
                 }
                 mSoftInputReentrancyGuard = true;
                 imm.showSoftInput(v, 0);
                 mSoftInputReentrancyGuard = false;
             }
         });
     }
@@ -647,19 +647,22 @@ class GeckoInputConnection
             outAttrs.initialSelEnd = 0;
             return mKeyInputConnection;
         }
         Editable editable = getEditable();
         outAttrs.initialSelStart = Selection.getSelectionStart(editable);
         outAttrs.initialSelEnd = Selection.getSelectionEnd(editable);
 
         if (mIsUserAction) {
-            showSoftInput();
+            if ((context instanceof Activity) && ActivityUtils.isFullScreen((Activity) context)) {
+                showSoftInputWithToolbar(false);
+            } else {
+                showSoftInputWithToolbar(true);
+            }
         }
-
         return this;
     }
 
     private boolean replaceComposingSpanWithSelection() {
         final Editable content = getEditable();
         if (content == null) {
             return false;
         }
@@ -935,17 +938,17 @@ class GeckoInputConnection
                 break;
 
             case NOTIFY_IME_OF_BLUR:
                 // Showing/hiding vkb is done in notifyIMEContext
                 mFocused = false;
                 break;
 
             case NOTIFY_IME_OPEN_VKB:
-                showSoftInput();
+                showSoftInputWithToolbar(false);
                 break;
 
             case GeckoEditableListener.NOTIFY_IME_TO_COMMIT_COMPOSITION: {
                 // Gecko already committed its composition. However, Android keyboards
                 // have trouble dealing with us removing the composition manually on the
                 // Java side. Therefore, we keep the composition intact on the Java side.
                 // The text content should still be in-sync on both sides.
                 //
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityUtils.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityUtils.java
@@ -32,16 +32,18 @@ public class ActivityUtils {
                         View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                         View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                         View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                         View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
             } else {
                 newVis |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
             }
         } else {
+            // no need to prevent status bar to appear when exiting full screen
+            preventDisplayStatusbar(activity, false);
             newVis = View.SYSTEM_UI_FLAG_VISIBLE;
         }
 
         if (AppConstants.Versions.feature23Plus) {
             // We also have to set SYSTEM_UI_FLAG_LIGHT_STATUS_BAR with to current system ui status
             // to support both light and dark status bar.
             final int oldVis = window.getDecorView().getSystemUiVisibility();
             newVis |= (oldVis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
@@ -72,9 +74,26 @@ public class ActivityUtils {
         while (context instanceof ContextWrapper) {
             if (context instanceof Activity) {
                 return (Activity) context;
             }
             context = ((ContextWrapper) context).getBaseContext();
         }
         return null;
     }
+
+    public static void preventDisplayStatusbar(final Activity activity, boolean registering) {
+        final View decorView = activity.getWindow().getDecorView();
+        if (registering) {
+            decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
+                @Override
+                public void onSystemUiVisibilityChange(int visibility) {
+                    if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
+                        setFullScreen(activity, true);
+                    }
+                }
+            });
+        } else {
+            decorView.setOnSystemUiVisibilityChangeListener(null);
+        }
+
+    }
 }
--- a/mobile/android/themes/geckoview/content.css
+++ b/mobile/android/themes/geckoview/content.css
@@ -162,16 +162,17 @@ textarea {
   padding-inline-end: 1px;
   padding-block-start: 2px;
   padding-block-end: 2px;
 }
 
 input[type="button"],
 input[type="submit"],
 input[type="reset"],
+input[type="color"],
 button {
   border-width: 1px;
   padding-inline-start: 7px;
   padding-inline-end: 7px;
   padding-block-start: 0;
   padding-block-end: 0;
 }
 
@@ -201,16 +202,17 @@ input[type="file"]:focus > input[type="t
   border-style: solid;
   border-color: var(--form_border);
   background-color: var(--form_background);
 }
 
 select:not([size]):not([multiple]):focus,
 select[size="0"]:focus,
 select[size="1"]:focus,
+input[type="color"]:focus,
 input[type="button"]:focus,
 input[type="submit"]:focus,
 input[type="reset"]:focus,
 button:focus {
   outline: 0px !important;
   border-style: solid;
   border-color: var(--form_border);
   background-color: var(--form_background);
@@ -242,16 +244,18 @@ select[size="1"]:disabled {
 }
 
 input[type="button"]:disabled,
 input[type="button"]:disabled:active,
 input[type="submit"]:disabled,
 input[type="submit"]:disabled:active,
 input[type="reset"]:disabled,
 input[type="reset"]:disabled:active,
+input[type="color"]:disabled,
+input[type="color"]:disabled:active,
 button:disabled,
 button:disabled:active {
   padding-inline-start: 7px;
   padding-inline-end: 7px;
   padding-block-start: 0;
   padding-block-end: 0;
   background-color: var(--form_background_disabled);
 }
@@ -283,17 +287,17 @@ select:not(:disabled):active,
 textarea:not(:focus):not(:disabled):active,
 option:active,
 label:active,
 xul|menulist:active {
   background-color: var(--color_background_highlight_overlay);
 }
 
 button:active:hover,
-input[type="color"]:-moz-system-metric(color-picker-available):active:hover,
+input[type="color"]:active:hover,
 input[type="reset"]:active:hover,
 input[type="button"]:active:hover,
 input[type="submit"]:active:hover {
   padding-inline-end: 7px;
   padding-inline-start: 7px;
 }
 
 input[type=number] > div > div, /* work around bug 946184 */
--- a/moz.build
+++ b/moz.build
@@ -93,17 +93,16 @@ if not CONFIG['JS_STANDALONE']:
     ]
 
     GENERATED_FILES['buildid.h'].script = 'build/variables.py:buildid_header'
     GENERATED_FILES['source-repo.h'].script = 'build/variables.py:source_repo_header'
 
     DIRS += [
         'build',
         'tools',
-        'probes',
     ]
 
 if CONFIG['COMPILE_ENVIRONMENT']:
     DIRS += ['js/src']
 
 DIRS += [
     'config/external/fdlibm',
     'config/external/nspr',
--- a/netwerk/base/MozURL.cpp
+++ b/netwerk/base/MozURL.cpp
@@ -6,19 +6,20 @@
 
 namespace mozilla {
 namespace net {
 
 NS_IMPL_ADDREF(MozURL)
 NS_IMPL_RELEASE(MozURL)
 
 /* static */ nsresult
-MozURL::Init(const nsACString& aSpec, MozURL** aURL)
+MozURL::Init(MozURL** aURL, const nsACString& aSpec, const MozURL* aBaseURL)
 {
-  rusturl* ptr = rusturl_new(&aSpec);
+  rusturl* base = aBaseURL ? aBaseURL->mURL.get() : nullptr;
+  rusturl* ptr = rusturl_new(&aSpec, base);
   if (!ptr) {
     return NS_ERROR_FAILURE;
   }
   RefPtr<MozURL> url = new MozURL(ptr);
   url.forget(aURL);
   return NS_OK;
 }
 
--- a/netwerk/base/MozURL.h
+++ b/netwerk/base/MozURL.h
@@ -13,32 +13,33 @@ namespace net {
 
 // This class provides a thread-safe, immutable URL parser.
 // As long as there is RefPtr to the object, you may use it on any thread.
 // The constructor is private. One can instantiate the object by
 // calling the Init() method as such:
 //
 // RefPtr<MozURL> url;
 // nsAutoCString href("http://example.com/path?query#ref");
-// nsresult rv = MozURL::Init(href, getter_AddRefs(url));
+// nsresult rv = MozURL::Init(getter_AddRefs(url), href);
 // if (NS_SUCCEEDED(rv)) { /* use url */ }
 //
 // When changing the URL is needed, you need to call the Mutate() method.
 // This gives you a Mutator object, on which you can perform setter operations.
 // Calling Finalize() on the Mutator will result in a new MozURL and a status
 // code. If any of the setter operations failed, it will be reflected in the
 // status code, and a null MozURL.
 //
 // Note: In the case of a domain name containing non-ascii characters,
 // GetSpec and GetHostname will return the IDNA(punycode) version of the host.
 // Also note that for now, MozURL only supports the UTF-8 charset.
 class MozURL final
 {
 public:
-  static nsresult Init(const nsACString& aSpec, MozURL** aURL);
+  static nsresult Init(MozURL** aURL, const nsACString& aSpec,
+                       const MozURL* aBaseURL = nullptr);
 
   nsresult GetScheme(nsACString& aScheme);
   nsresult GetSpec(nsACString& aSpec);
   nsresult GetUsername(nsACString& aUser);
   nsresult GetPassword(nsACString& aPassword);
   // Will return the hostname of URL. If the hostname is an IPv6 address,
   // it will be enclosed in square brackets, such as `[::1]`
   nsresult GetHostname(nsACString& aHost);
--- a/netwerk/base/RustURL.cpp
+++ b/netwerk/base/RustURL.cpp
@@ -53,17 +53,17 @@ RustURL::GetSpec(nsACString & aSpec)
   return rusturl_get_spec(mURL.get(), &aSpec);
 }
 
 NS_IMETHODIMP
 RustURL::SetSpec(const nsACString & aSpec)
 {
   ENSURE_MUTABLE();
 
-  rusturl* ptr = rusturl_new(&aSpec);
+  rusturl* ptr = rusturl_new(&aSpec, nullptr);
   if (!ptr) {
     return NS_ERROR_FAILURE;
   }
   mURL.reset(ptr);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/rust-url-capi/src/lib.rs
+++ b/netwerk/base/rust-url-capi/src/lib.rs
@@ -35,22 +35,29 @@ fn default_port(scheme: &str) -> Option<
         "rtsp" => Some(443),
         "moz-anno" => Some(443),
         "android" => Some(443),
         _ => None,
     }
 }
 
 #[no_mangle]
-pub extern "C" fn rusturl_new(spec: &nsACString) -> *mut Url {
+pub extern "C" fn rusturl_new(spec: &nsACString, baseptr: Option<&Url>) -> *mut Url {
   let url_spec = match str::from_utf8(spec) {
     Ok(spec) => spec,
     Err(_) => return ptr::null_mut(),
   };
 
+  if let Some(base) = baseptr {
+    match base.join(url_spec) {
+         Ok(url) => return Box::into_raw(Box::new(url)),
+         Err(_) => return ptr::null_mut()
+    };
+  }
+
   match parser().parse(url_spec) {
     Ok(url) => Box::into_raw(Box::new(url)),
     Err(_) => return ptr::null_mut(),
   }
 }
 
 #[no_mangle]
 pub extern "C" fn rusturl_clone(urlptr: Option<&Url>) -> *mut Url {
--- a/netwerk/base/rust-url-capi/src/rust-url-capi.h
+++ b/netwerk/base/rust-url-capi/src/rust-url-capi.h
@@ -14,17 +14,17 @@ extern "C" {
 // * The int32_t* and bool* outparameter pointer is unchecked, and must
 //   be non-null.
 // * All rusturl* pointers must refer to pointers which are returned
 //   by rusturl_new, and must be freed with rusturl_free.
 
 // The `rusturl` opaque type is equivalent to the rust type `::url::Url`
 struct rusturl;
 
-rusturl* rusturl_new(const nsACString* spec);
+rusturl* rusturl_new(const nsACString* spec, const rusturl* base);
 rusturl* rusturl_clone(const rusturl* url);
 /* unsafe */ void rusturl_free(rusturl* url);
 
 nsresult rusturl_get_spec(const rusturl* url, nsACString* cont);
 nsresult rusturl_get_scheme(const rusturl* url, nsACString* cont);
 nsresult rusturl_get_username(const rusturl* url, nsACString* cont);
 nsresult rusturl_get_password(const rusturl* url, nsACString* cont);
 nsresult rusturl_get_host(const rusturl* url, nsACString* cont);
--- a/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp
+++ b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp
@@ -186,17 +186,19 @@ WyciwygChannelChild::OnStartRequest(cons
                                     const int32_t& source,
                                     const nsCString& charset,
                                     const nsCString& securityInfo)
 {
   LOG(("WyciwygChannelChild::RecvOnStartRequest [this=%p]\n", this));
 
   mState = WCC_ONSTART;
 
-  mStatus = statusCode;
+  if (!mCanceled && NS_SUCCEEDED(mStatus)) {
+    mStatus = statusCode;
+  }
   mContentLength = contentLength;
   mCharsetSource = source;
   mCharset = charset;
 
   if (!securityInfo.IsEmpty()) {
     NS_DeserializeObject(securityInfo, getter_AddRefs(mSecurityInfo));
   }
 
--- a/netwerk/test/gtest/TestMozURL.cpp
+++ b/netwerk/test/gtest/TestMozURL.cpp
@@ -5,17 +5,17 @@
 #include "../../base/MozURL.h"
 
 using namespace mozilla::net;
 
 TEST(TestMozURL, Getters)
 {
   nsAutoCString href("http://user:pass@example.com/path?query#ref");
   RefPtr<MozURL> url;
-  ASSERT_EQ(MozURL::Init(href, getter_AddRefs(url)), NS_OK);
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
 
   nsAutoCString out;
 
   ASSERT_EQ(url->GetScheme(out), NS_OK);
   ASSERT_TRUE(out.EqualsLiteral("http"));
 
   ASSERT_EQ(url->GetSpec(out), NS_OK);
   ASSERT_TRUE(out == href);
@@ -34,26 +34,26 @@ TEST(TestMozURL, Getters)
 
   ASSERT_EQ(url->GetQuery(out), NS_OK);
   ASSERT_TRUE(out.EqualsLiteral("query"));
 
   ASSERT_EQ(url->GetRef(out), NS_OK);
   ASSERT_TRUE(out.EqualsLiteral("ref"));
 
   url = nullptr;
-  ASSERT_EQ(MozURL::Init(NS_LITERAL_CSTRING(""), getter_AddRefs(url)),
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url), NS_LITERAL_CSTRING("")),
             NS_ERROR_FAILURE);
   ASSERT_EQ(url, nullptr);
 }
 
 TEST(TestMozURL, MutatorChain)
 {
   nsAutoCString href("http://user:pass@example.com/path?query#ref");
   RefPtr<MozURL> url;
-  ASSERT_EQ(MozURL::Init(href, getter_AddRefs(url)), NS_OK);
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
   nsAutoCString out;
 
   RefPtr<MozURL> url2;
   ASSERT_EQ(url->Mutate().SetScheme(NS_LITERAL_CSTRING("https"))
                          .SetUsername(NS_LITERAL_CSTRING("newuser"))
                          .SetPassword(NS_LITERAL_CSTRING("newpass"))
                          .SetHostname(NS_LITERAL_CSTRING("test"))
                          .SetFilePath(NS_LITERAL_CSTRING("new/file/path"))
@@ -64,17 +64,17 @@ TEST(TestMozURL, MutatorChain)
   ASSERT_EQ(url2->GetSpec(out), NS_OK);
   ASSERT_TRUE(out.EqualsLiteral("https://newuser:newpass@test/new/file/path?bla#huh"));
 }
 
 TEST(TestMozURL, MutatorFinalizeTwice)
 {
   nsAutoCString href("http://user:pass@example.com/path?query#ref");
   RefPtr<MozURL> url;
-  ASSERT_EQ(MozURL::Init(href, getter_AddRefs(url)), NS_OK);
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
   nsAutoCString out;
 
   RefPtr<MozURL> url2;
   MozURL::Mutator mut = url->Mutate();
   mut.SetScheme(NS_LITERAL_CSTRING("https")); // Change the scheme to https
   ASSERT_EQ(mut.Finalize(getter_AddRefs(url2)), NS_OK);
   ASSERT_EQ(url2->GetSpec(out), NS_OK);
   ASSERT_TRUE(out.EqualsLiteral("https://user:pass@example.com/path?query#ref"));
@@ -84,20 +84,38 @@ TEST(TestMozURL, MutatorFinalizeTwice)
   ASSERT_EQ(mut.Finalize(getter_AddRefs(url2)), NS_ERROR_NOT_AVAILABLE);
   ASSERT_EQ(url2, nullptr);
 }
 
 TEST(TestMozURL, MutatorErrorStatus)
 {
   nsAutoCString href("http://user:pass@example.com/path?query#ref");
   RefPtr<MozURL> url;
-  ASSERT_EQ(MozURL::Init(href, getter_AddRefs(url)), NS_OK);
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
   nsAutoCString out;
 
   // Test that trying to set the scheme to a bad value will get you an error
   MozURL::Mutator mut = url->Mutate();
   mut.SetScheme(NS_LITERAL_CSTRING("!@#$%^&*("));
   ASSERT_EQ(mut.GetStatus(), NS_ERROR_MALFORMED_URI);
 
   // Test that the mutator will not work after one faulty operation
   mut.SetScheme(NS_LITERAL_CSTRING("test"));
   ASSERT_EQ(mut.GetStatus(), NS_ERROR_MALFORMED_URI);
 }
+
+TEST(TestMozURL, InitWithBase)
+{
+  nsAutoCString href("https://example.net/a/b.html");
+  RefPtr<MozURL> url;
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url), href), NS_OK);
+  nsAutoCString out;
+
+  ASSERT_EQ(url->GetSpec(out), NS_OK);
+  ASSERT_TRUE(out.EqualsLiteral("https://example.net/a/b.html"));
+
+  RefPtr<MozURL> url2;
+  ASSERT_EQ(MozURL::Init(getter_AddRefs(url2), NS_LITERAL_CSTRING("c.png"),
+                         url), NS_OK);
+
+  ASSERT_EQ(url2->GetSpec(out), NS_OK);
+  ASSERT_TRUE(out.EqualsLiteral("https://example.net/a/c.png"));
+}
deleted file mode 100644
--- a/probes/moz.build
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- 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/.
-
-with Files('**'):
-    BUG_COMPONENT = ('Core', 'JavaScript Engine')
-
-if CONFIG['HAVE_DTRACE']:
-    EXPORTS += [
-        '!mozilla-trace.h',
-    ]
-
-    GENERATED_FILES += [
-        'mozilla-trace.h',
-    ]
-
-    trace = GENERATED_FILES['mozilla-trace.h']
-    trace.script = 'trace-gen.py'
-    trace.inputs += ['mozilla-trace.d']
deleted file mode 100644
--- a/probes/mozilla-trace.d
+++ /dev/null
@@ -1,23 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-/*
- * mozilla provider probes:
- * Data types defined in the generated file mozilla-trace.h
- *
- * TBD
- */
-
-provider mozilla {
-  /* Probe definitions go here */
-};
-
-/*
-#pragma D attributes Unstable/Unstable/Common provider mozilla provider
-#pragma D attributes Private/Private/Unknown provider mozilla module
-#pragma D attributes Private/Private/Unknown provider mozilla function
-#pragma D attributes Unstable/Unstable/Common provider mozilla name
-#pragma D attributes Unstable/Unstable/Common provider mozilla args
-*/
-
deleted file mode 100644
--- a/probes/trace-gen.py
+++ /dev/null
@@ -1,15 +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/.
-
-import os
-import subprocess
-
-def main(fp, input):
-    temporary_file = 'mozilla-trace.h.tmp'
-    subprocess.check_call(['dtrace', '-x', 'nolibs', '-h', '-s', input, '-o', temporary_file])
-    
-    with open(temporary_file, 'r') as temporary_fp:
-        output = temporary_fp.read()
-    fp.write(output.replace('if _DTRACE_VERSION', 'ifdef INCLUDE_MOZILLA_DTRACE'))
-    os.remove(temporary_file)
--- a/python/mozbuild/mozbuild/artifacts.py
+++ b/python/mozbuild/mozbuild/artifacts.py
@@ -785,17 +785,19 @@ class ArtifactCache(object):
             binascii.unhexlify(fname)
         except TypeError:
             # We download to a temporary name like HASH[:16]-basename to
             # differentiate among URLs with the same basenames.  We used to then
             # extract the build ID from the downloaded artifact and use it to make a
             # human readable unique name, but extracting build IDs is time consuming
             # (especially on Mac OS X, where we must mount a large DMG file).
             hash = hashlib.sha256(url).hexdigest()[:16]
-            fname = hash + '-' + os.path.basename(url)
+            # Strip query string and fragments.
+            basename = os.path.basename(urlparse.urlparse(url).path)
+            fname = hash + '-' + basename
 
         path = os.path.abspath(mozpath.join(self._cache_dir, fname))
         if self._skip_cache and os.path.exists(path):
             self.log(logging.DEBUG, 'artifact',
                 {'path': path},
                 'Skipping cache: removing cached downloaded artifact {path}')
             os.remove(path)
 
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -547,19 +547,19 @@ class AbsolutePath(Path):
 
 
 @memoize
 def ContextDerivedTypedList(klass, base_class=List):
     """Specialized TypedList for use with ContextDerivedValue types.
     """
     assert issubclass(klass, ContextDerivedValue)
     class _TypedList(ContextDerivedValue, TypedList(klass, base_class)):
-        def __init__(self, context, iterable=[]):
+        def __init__(self, context, iterable=[], **kwargs):
             self.context = context
-            super(_TypedList, self).__init__(iterable)
+            super(_TypedList, self).__init__(iterable, **kwargs)
 
         def normalize(self, e):
             if not isinstance(e, klass):
                 e = klass(self.context, e)
             return e
 
     return _TypedList
 
@@ -673,30 +673,30 @@ def ContextDerivedTypedHierarchicalStrin
             child = self._children.get(name)
             if not child:
                 child = self._children[name] = _TypedListWithItems(
                     self._context)
             return child
 
     return _TypedListWithItems
 
-def OrderedListWithAction(action):
+def OrderedPathListWithAction(action):
     """Returns a class which behaves as a StrictOrderingOnAppendList, but
     invokes the given callable with each input and a context as it is
     read, storing a tuple including the result and the original item.
 
     This used to extend moz.build reading to make more data available in
     filesystem-reading mode.
     """
-    class _OrderedListWithAction(ContextDerivedValue,
-                                 StrictOrderingOnAppendListWithAction):
+    class _OrderedListWithAction(ContextDerivedTypedList(SourcePath,
+                                 StrictOrderingOnAppendListWithAction)):
         def __init__(self, context, *args):
             def _action(item):
                 return item, action(context, item)
-            super(_OrderedListWithAction, self).__init__(action=_action, *args)
+            super(_OrderedListWithAction, self).__init__(context, action=_action, *args)
 
     return _OrderedListWithAction
 
 def TypedListWithAction(typ, action):
     """Returns a class which behaves as a TypedList with the provided type, but
     invokes the given given callable with each input and a context as it is
     read, storing a tuple including the result and the original item.
 
@@ -708,18 +708,18 @@ def TypedListWithAction(typ, action):
             def _action(item):
                 return item, action(context, item)
             super(_TypedListWithAction, self).__init__(action=_action, *args)
     return _TypedListWithAction
 
 WebPlatformTestManifest = TypedNamedTuple("WebPlatformTestManifest",
                                           [("manifest_path", unicode),
                                            ("test_root", unicode)])
-ManifestparserManifestList = OrderedListWithAction(read_manifestparser_manifest)
-ReftestManifestList = OrderedListWithAction(read_reftest_manifest)
+ManifestparserManifestList = OrderedPathListWithAction(read_manifestparser_manifest)
+ReftestManifestList = OrderedPathListWithAction(read_reftest_manifest)
 WptManifestList = TypedListWithAction(WebPlatformTestManifest, read_wpt_manifest)
 
 OrderedSourceList = ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendList)
 OrderedTestFlavorList = TypedList(Enum(*all_test_flavors()),
                                   StrictOrderingOnAppendList)
 OrderedStringList = TypedList(unicode, StrictOrderingOnAppendList)
 DependentTestsEntry = ContextDerivedTypedRecord(('files', OrderedSourceList),
                                                 ('tags', OrderedStringList),
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -1270,17 +1270,17 @@ class TreeMetadataEmitter(LoggingMixin):
         for flavor in WEB_PLATFORM_TESTS_FLAVORS:
             for path, manifest in context.get("%s_MANIFESTS" % flavor.upper().replace('-', '_'), []):
                 for obj in self._process_web_platform_tests_manifest(context, path, manifest):
                     yield obj
 
     def _process_test_manifest(self, context, info, manifest_path, mpmanifest):
         flavor, install_root, install_subdir, package_tests = info
 
-        path = mozpath.normpath(mozpath.join(context.srcdir, manifest_path))
+        path = manifest_path.full_path
         manifest_dir = mozpath.dirname(path)
         manifest_reldir = mozpath.dirname(mozpath.relpath(path,
             context.config.topsrcdir))
         manifest_sources = [mozpath.relpath(pth, context.config.topsrcdir)
                             for pth in mpmanifest.source_files]
         install_prefix = mozpath.join(install_root, install_subdir)
 
         try:
@@ -1370,18 +1370,17 @@ class TreeMetadataEmitter(LoggingMixin):
             yield obj
         except (AssertionError, Exception):
             raise SandboxValidationError('Error processing test '
                 'manifest file %s: %s' % (path,
                     '\n'.join(traceback.format_exception(*sys.exc_info()))),
                 context)
 
     def _process_reftest_manifest(self, context, flavor, manifest_path, manifest):
-        manifest_full_path = mozpath.normpath(mozpath.join(
-            context.srcdir, manifest_path))
+        manifest_full_path = manifest_path.full_path
         manifest_reldir = mozpath.dirname(mozpath.relpath(manifest_full_path,
             context.config.topsrcdir))
 
         # reftest manifests don't come from manifest parser. But they are
         # similar enough that we can use the same emitted objects. Note
         # that we don't perform any installs for reftests.
         obj = TestManifest(context, manifest_full_path, manifest,
                 flavor=flavor, install_prefix='%s/' % flavor,
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -1858,18 +1858,20 @@ class PackageFrontend(MachCommandBase):
                                 break
                             except Exception:
                                 pass
                 for algorithm, digest in (data.get('artifacts', {})
                                               .get(artifact_name, {}).items()):
                     pass
 
                 name = os.path.basename(artifact_name)
+                artifact_url = get_artifact_url(task_id, artifact_name,
+                    use_proxy=not artifact_name.startswith('public/'))
                 super(ArtifactRecord, self).__init__(
-                    get_artifact_url(task_id, artifact_name), name,
+                    artifact_url, name,
                     None, digest, algorithm, unpack=True)
 
         records = OrderedDict()
         downloaded = []
 
         if tooltool_manifest:
             manifest = open_manifest(tooltool_manifest)
             for record in manifest.file_records:
--- a/python/mozbuild/mozbuild/testing.py
+++ b/python/mozbuild/mozbuild/testing.py
@@ -504,25 +504,25 @@ def install_test_files(topsrcdir, topobj
     copier = FileCopier()
     manifest.populate_registry(copier)
     copier.copy(objdir_dest,
                 remove_unaccounted=False)
 
 
 # Convenience methods for test manifest reading.
 def read_manifestparser_manifest(context, manifest_path):
-    path = mozpath.normpath(mozpath.join(context.srcdir, manifest_path))
+    path = manifest_path.full_path
     return manifestparser.TestManifest(manifests=[path], strict=True,
                                        rootdir=context.config.topsrcdir,
                                        finder=context._finder,
                                        handle_defaults=False)
 
 def read_reftest_manifest(context, manifest_path):
     import reftest
-    path = mozpath.normpath(mozpath.join(context.srcdir, manifest_path))
+    path = manifest_path.full_path
     manifest = reftest.ReftestManifest(finder=context._finder)
     manifest.load(path)
     return manifest
 
 def read_wpt_manifest(context, paths):
     manifest_path, tests_root = paths
     full_path = mozpath.normpath(mozpath.join(context.srcdir, manifest_path))
     old_path = sys.path[:]
--- a/security/pkix/test/lib/pkixtestutil.cpp
+++ b/security/pkix/test/lib/pkixtestutil.cpp
@@ -29,16 +29,18 @@
 #include <limits>
 #include <new>
 #include <sstream>
 #include <cstdlib>
 
 #include "pkixder.h"
 #include "pkixutil.h"
 
+#include "mozilla/Unused.h"
+
 using namespace std;
 
 namespace mozilla { namespace pkix { namespace test {
 
 namespace {
 
 inline void
 fclose_void(FILE* file) {
@@ -504,17 +506,17 @@ MaybeLogOutput(const ByteString& result,
       assert(false);
       return;
     }
     string filename = counterStream.str() + '-' + suffix + ".der";
 
     ++counter;
     ScopedFILE file(OpenFile(logPath, filename, "wb"));
     if (file) {
-      (void) fwrite(result.data(), result.length(), 1, file.get());
+      Unused << fwrite(result.data(), result.length(), 1, file.get());
     }
   }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Certificates
 
 static ByteString TBSCertificate(long version, const ByteString& serialNumber,
--- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
@@ -17,29 +17,36 @@
 #include "mozilla/dom/ContentChild.h"
 #include "nsPrintfCString.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "SpecialSystemDirectory.h"
+#include "nsReadableUtils.h"
+#include "nsIFileStreams.h"
+#include "nsILineInputStream.h"
+#include "nsNetCID.h"
 
 #ifdef ANDROID
 #include "cutils/properties.h"
 #endif
 
 #ifdef MOZ_WIDGET_GTK
 #include <glib.h>
 #endif
 
 #include <dirent.h>
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
+#ifndef ANDROID
+#include <glob.h>
+#endif
 
 namespace mozilla {
 
 #if defined(MOZ_CONTENT_SANDBOX)
 namespace {
 static const int rdonly = SandboxBroker::MAY_READ;
 static const int wronly = SandboxBroker::MAY_WRITE;
 static const int rdwr = rdonly | wronly;
@@ -80,16 +87,101 @@ AddMesaSysfsPaths(SandboxBroker::Policy*
           }
         }
       }
     }
     closedir(dir);
   }
 }
 
+static void
+AddPathsFromFile(SandboxBroker::Policy* aPolicy, nsACString& aPath)
+{
+  nsresult rv;
+  nsCOMPtr<nsIFile> ldconfig(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+  rv = ldconfig->InitWithNativePath(aPath);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+  nsCOMPtr<nsIFileInputStream> fileStream(
+    do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+  rv = fileStream->Init(ldconfig, -1, -1, 0);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+  nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+  nsAutoCString line;
+  bool more = true;
+  do {
+    rv = lineStream->ReadLine(line, &more);
+    // Cut off any comments at the end of the line, also catches lines
+    // that are entirely a comment
+    int32_t hash = line.FindChar('#');
+    if (hash >= 0) {
+      line = Substring(line, 0, hash);
+    }
+    // Simplify our following parsing by trimming whitespace
+    line.CompressWhitespace(true, true);
+    if (line.IsEmpty()) {
+      // Skip comment lines
+      continue;
+    }
+    // Check for any included files and recursively process
+    nsACString::const_iterator start, end, token_end;
+
+    line.BeginReading(start);
+    line.EndReading(end);
+    token_end = end;
+
+    if (FindInReadable(NS_LITERAL_CSTRING("include "), start, token_end)) {
+      nsAutoCString includes(Substring(token_end, end));
+      for (const nsACString& includeGlob : includes.Split(' ')) {
+        glob_t globbuf;
+        if (!glob(PromiseFlatCString(includeGlob).get(), GLOB_NOSORT, nullptr, &globbuf)) {
+          for (size_t fileIdx = 0; fileIdx < globbuf.gl_pathc; fileIdx++) {
+            nsAutoCString filePath(globbuf.gl_pathv[fileIdx]);
+            AddPathsFromFile(aPolicy, filePath);
+          }
+          globfree(&globbuf);
+        }
+      }
+    }
+    // Skip anything left over that isn't an absolute path
+    if (line.First() != '/') {
+      continue;
+    }
+    // Cut off anything behind an = sign, used by dirname=TYPE directives
+    int32_t equals = line.FindChar('=');
+    if (equals >= 0) {
+      line = Substring(line, 0, equals);
+    }
+    char* resolvedPath = realpath(line.get(), nullptr);
+    if (resolvedPath) {
+      aPolicy->AddDir(rdonly, resolvedPath);
+      free(resolvedPath);
+    }
+  } while (more);
+}
+
+static void
+AddLdconfigPaths(SandboxBroker::Policy* aPolicy)
+{
+  nsAutoCString ldconfigPath(NS_LITERAL_CSTRING("/etc/ld.so.conf"));
+  AddPathsFromFile(aPolicy, ldconfigPath);
+}
+
 SandboxBrokerPolicyFactory::SandboxBrokerPolicyFactory()
 {
   // Policy entries that are the same in every process go here, and
   // are cached over the lifetime of the factory.
 #if defined(MOZ_CONTENT_SANDBOX)
   SandboxBroker::Policy* policy = new SandboxBroker::Policy;
   policy->AddDir(rdwrcr, "/dev/shm");
   // Write permssions
@@ -162,16 +254,17 @@ SandboxBrokerPolicyFactory::SandboxBroke
   policy->AddDir(rdonly, "/var/tmp");
   // Various places where fonts reside
   policy->AddDir(rdonly, "/usr/X11R6/lib/X11/fonts");
   policy->AddDir(rdonly, "/nix/store");
   policy->AddDir(rdonly, "/run/host/fonts");
   policy->AddDir(rdonly, "/run/host/user-fonts");
 
   AddMesaSysfsPaths(policy);
+  AddLdconfigPaths(policy);
 
   // Bug 1385715: NVIDIA PRIME support
   policy->AddPath(rdonly, "/proc/modules");
 
 #ifdef MOZ_PULSEAUDIO
   // See bug 1384986 comment #1.
   if (const auto xauth = PR_GetEnv("XAUTHORITY")) {
     policy->AddPath(rdonly, xauth);
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
@@ -376,17 +376,17 @@ SetJobLevel(sandbox::TargetPolicy* aPoli
 
   return aPolicy->SetJobLevel(sandbox::JOB_NONE, 0);
 }
 
 #if defined(MOZ_CONTENT_SANDBOX)
 
 void
 SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
-                                                 base::ChildPrivileges aPrivs)
+                                                 bool aIsFileProcess)
 {
   MOZ_RELEASE_ASSERT(mPolicy, "mPolicy must be set before this call.");
 
   sandbox::JobLevel jobLevel;
   sandbox::TokenLevel accessTokenLevel;
   sandbox::IntegrityLevel initialIntegrityLevel;
   sandbox::IntegrityLevel delayedIntegrityLevel;
 
@@ -416,18 +416,19 @@ SandboxBroker::SetSecurityLevelForConten
     delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
   } else if (aSandboxLevel == 1) {
     jobLevel = sandbox::JOB_NONE;
     accessTokenLevel = sandbox::USER_NON_ADMIN;
     initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
     delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
   }
 
-  // If PRIVILEGES_FILEREAD required, don't allow settings that block reads.
-  if (aPrivs == base::ChildPrivileges::PRIVILEGES_FILEREAD) {
+  // If the process will handle file: URLs, don't allow settings that
+  // block reads.
+  if (aIsFileProcess) {
     if (accessTokenLevel < sandbox::USER_NON_ADMIN) {
       accessTokenLevel = sandbox::USER_NON_ADMIN;
     }
     if (delayedIntegrityLevel > sandbox::INTEGRITY_LEVEL_LOW) {
       delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
     }
   }
 
@@ -498,18 +499,17 @@ SandboxBroker::SetSecurityLevelForConten
   // reason the addition of the content temp failed, this will give write access
   // to the normal TEMP dir. However such failures should be pretty rare and
   // without this printing will not currently work.
   AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_ANY,
                    sContentTempDir, NS_LITERAL_STRING("\\*"));
 
   // We still have edge cases where the child at low integrity can't read some
   // files, so add a rule to allow read access to everything when required.
-  if (aSandboxLevel == 1 ||
-      aPrivs == base::ChildPrivileges::PRIVILEGES_FILEREAD) {
+  if (aSandboxLevel == 1 || aIsFileProcess) {
     result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
                               sandbox::TargetPolicy::FILES_ALLOW_READONLY,
                               L"*");
     MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
                        "With these static arguments AddRule should never fail, what happened?");
   } else {
     // Add rule to allow read access to installation directory.
     AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.h
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __SECURITY_SANDBOX_SANDBOXBROKER_H__
 #define __SECURITY_SANDBOX_SANDBOXBROKER_H__
 
 #include <stdint.h>
 #include <windows.h>
 
-#include "base/child_privileges.h"
 #include "nsXULAppAPI.h"
 
 namespace sandbox {
   class BrokerServices;
   class TargetPolicy;
 }
 
 namespace mozilla {
@@ -38,17 +37,17 @@ public:
                  GeckoProcessType aProcessType,
                  const bool aEnableLogging,
                  void **aProcessHandle);
   virtual ~SandboxBroker();
 
   // Security levels for different types of processes
 #if defined(MOZ_CONTENT_SANDBOX)
   void SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
-                                         base::ChildPrivileges aPrivs);
+                                         bool aIsFileProcess);
 #endif
 
   void SetSecurityLevelForGPUProcess(int32_t aSandboxLevel);
 
   bool SetSecurityLevelForPluginProcess(int32_t aSandboxLevel);
   enum SandboxLevel {
     LockDown,
     Restricted
--- a/services/sync/modules/engines/clients.js
+++ b/services/sync/modules/engines/clients.js
@@ -45,16 +45,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "getRepairResponder",
   "resource://services-sync/collection_repair.js");
 
 const CLIENTS_TTL = 1814400; // 21 days
 const CLIENTS_TTL_REFRESH = 604800; // 7 days
 const STALE_CLIENT_REMOTE_AGE = 604800; // 7 days
 
 const SUPPORTED_PROTOCOL_VERSIONS = [SYNC_API_VERSION];
+const LAST_MODIFIED_ON_PROCESS_COMMAND_PREF = "services.sync.clients.lastModifiedOnProcessCommands";
 
 function hasDupeCommand(commands, action) {
   if (!commands) {
     return false;
   }
   return commands.some(other => other.command == action.command &&
     Utils.deepEquals(other.args, action.args));
 }
@@ -87,16 +88,27 @@ this.ClientEngine = function ClientEngin
 ClientEngine.prototype = {
   __proto__: SyncEngine.prototype,
   _storeObj: ClientStore,
   _recordObj: ClientsRec,
   _trackerObj: ClientsTracker,
   allowSkippedRecord: false,
   _knownStaleFxADeviceIds: null,
 
+  // These two properties allow us to avoid replaying the same commands
+  // continuously if we cannot manage to upload our own record.
+  _localClientLastModified: 0,
+  get _lastModifiedOnProcessCommands() {
+    return Services.prefs.getIntPref(LAST_MODIFIED_ON_PROCESS_COMMAND_PREF, -1);
+  },
+
+  set _lastModifiedOnProcessCommands(value) {
+    Services.prefs.setIntPref(LAST_MODIFIED_ON_PROCESS_COMMAND_PREF, value);
+  },
+
   // Always sync client data as it controls other sync behavior
   get enabled() {
     return true;
   },
 
   get lastRecordUpload() {
     return Svc.Prefs.get(this.name + ".lastRecordUpload", 0);
   },
@@ -387,16 +399,17 @@ ClientEngine.prototype = {
         }
       }
       let localFxADeviceId = await fxAccounts.getDeviceId();
       // Bug 1264498: Mobile clients don't remove themselves from the clients
       // collection when the user disconnects Sync, so we mark as stale clients
       // with the same name that haven't synced in over a week.
       // (Note we can't simply delete them, or we re-apply them next sync - see
       // bug 1287687)
+      this._localClientLastModified = Math.round(this._incomingClients[this.localID]);
       delete this._incomingClients[this.localID];
       let names = new Set([this.localName]);
       let seenDeviceIds = new Set([localFxADeviceId]);
       let idToLastModifiedList = Object.entries(this._incomingClients)
                                  .sort((a, b) => b[1] - a[1]);
       for (let [id, serverLastModified] of idToLastModifiedList) {
         let record = this._store._remoteClients[id];
         // stash the server last-modified time on the record.
@@ -665,19 +678,22 @@ ClientEngine.prototype = {
 
   /**
    * Check if the local client has any remote commands and perform them.
    *
    * @return false to abort sync
    */
   async processIncomingCommands() {
     return this._notify("clients:process-commands", "", async function() {
-      if (!this.localCommands) {
+      if (!this.localCommands ||
+          (this._lastModifiedOnProcessCommands == this._localClientLastModified
+           && !this.ignoreLastModifiedOnProcessCommands)) {
         return true;
       }
+      this._lastModifiedOnProcessCommands = this._localClientLastModified;
 
       const clearedCommands = await this._readCommands()[this.localID];
       const commands = this.localCommands.filter(command => !hasDupeCommand(clearedCommands, command));
       let didRemoveCommand = false;
       let URIsToDisplay = [];
       // Process each command in order.
       for (let rawCommand of commands) {
         let shouldRemoveCommand = true; // most commands are auto-removed.
--- a/services/sync/tests/unit/test_bookmark_repair.js
+++ b/services/sync/tests/unit/test_bookmark_repair.js
@@ -33,16 +33,17 @@ const BOOKMARK_REPAIR_STATE_PREFS = [
 ];
 
 let clientsEngine;
 let bookmarksEngine;
 var recordedEvents = [];
 
 add_task(async function setup() {
   clientsEngine = Service.clientsEngine;
+  clientsEngine.ignoreLastModifiedOnProcessCommands = true;
   bookmarksEngine = Service.engineManager.get("bookmarks");
 
   await generateNewKeys(Service.collectionKeys);
 
   Service.recordTelemetryEvent = (object, method, value, extra = undefined) => {
     recordedEvents.push({ object, method, value, extra });
   };
 });
@@ -195,16 +196,17 @@ add_task(async function test_bookmark_re
       "Should not record repair telemetry after sending repair request");
 
     _("Back up repair state to restore later");
     let restoreInitialRepairState = backupPrefs(BOOKMARK_REPAIR_STATE_PREFS);
 
     // so now let's take over the role of that other client!
     _("Create new clients engine pretending to be remote client");
     let remoteClientsEngine = Service.clientsEngine = new ClientEngine(Service);
+    remoteClientsEngine.ignoreLastModifiedOnProcessCommands = true;
     await remoteClientsEngine.initialize();
     remoteClientsEngine.localID = remoteID;
 
     _("Restore missing bookmark");
     // Pretend Sync wrote the bookmark, so that we upload it as part of the
     // repair instead of the sync.
     bookmarkInfo.source = PlacesUtils.bookmarks.SOURCE_SYNC;
     await PlacesUtils.bookmarks.insert(bookmarkInfo);
@@ -302,16 +304,17 @@ add_task(async function test_bookmark_re
       },
     }]);
     await revalidationPromise;
     ok(!Services.prefs.prefHasUserValue("services.sync.repairs.bookmarks.state"),
       "Should clear repair pref after successfully completing repair");
   } finally {
     await cleanup(server);
     clientsEngine = Service.clientsEngine = new ClientEngine(Service);
+    clientsEngine.ignoreLastModifiedOnProcessCommands = true;
     clientsEngine.initialize();
   }
 });
 
 add_task(async function test_repair_client_missing() {
   enableValidationPrefs();
 
   _("Ensure that a record missing from the client only will get re-downloaded from the server");
--- a/services/sync/tests/unit/test_clients_engine.js
+++ b/services/sync/tests/unit/test_clients_engine.js
@@ -1347,17 +1347,17 @@ add_task(async function test_keep_cleare
       },
       {
         command: "displayURI",
         args: ["https://deviceclink2.com", deviceCID, "Device C link 2"],
         flowID: Utils.makeGUID(),
       }],
       version: "48",
       protocols: ["1.5"],
-    }), now - 10));
+    }), now - 5));
 
     // Simulate reboot
     SyncEngine.prototype._uploadOutgoing = oldUploadOutgoing;
     engine = Service.clientsEngine = new ClientEngine(Service);
     await engine.initialize();
 
     commandsProcessed = 0;
     engine._handleDisplayURIs = (uris) => { commandsProcessed = uris.length };
@@ -1789,13 +1789,31 @@ add_task(async function process_incoming
 
   await engine._processIncoming();
   ok(stubRefresh.notCalled, "Should not refresh the known stale clients since it's already populated");
 
   stubProcessIncoming.restore();
   stubRefresh.restore();
 });
 
+add_task(async function process_incoming_refreshes_known_stale_clients() {
+  Services.prefs.clearUserPref("services.sync.clients.lastModifiedOnProcessCommands");
+  engine._localClientLastModified = Math.round(Date.now() / 1000);
+
+  const stubRemoveLocalCommand = sinon.stub(engine, "removeLocalCommand");
+  const tabProcessedSpy = sinon.spy(engine, "_handleDisplayURIs");
+  engine.localCommands = [{ command: "displayURI", args: ["https://foo.bar", "fxaid1", "foo"] }];
+
+  await engine.processIncomingCommands();
+  ok(tabProcessedSpy.calledOnce);
+  // Let's say we failed to upload and we end up calling processIncomingCommands again
+  await engine.processIncomingCommands();
+  ok(tabProcessedSpy.calledOnce);
+
+  tabProcessedSpy.restore();
+  stubRemoveLocalCommand.restore();
+});
+
 function run_test() {
   initTestLogging("Trace");
   Log.repository.getLogger("Sync.Engine.Clients").level = Log.Level.Trace;
   run_next_test();
 }
--- a/servo/components/hashglobe/src/diagnostic.rs
+++ b/servo/components/hashglobe/src/diagnostic.rs
@@ -204,16 +204,18 @@ impl<K, V, S> Default for DiagnosticHash
     }
 }
 
 impl<K: Hash + Eq, V, S: BuildHasher> Drop for DiagnosticHashMap<K, V, S>
     where K: Eq + Hash,
           S: BuildHasher
 {
     fn drop(&mut self) {
+        self.map.verify();
         debug_assert!(self.readonly, "Dropped while mutating");
+        self.verify();
     }
 }
 
 extern "C" {
     pub fn Gecko_AnnotateCrashReport(key_str: *const ::std::os::raw::c_char,
                                      value_str: *const ::std::os::raw::c_char);
 }
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -1297,31 +1297,40 @@ impl Element {
             self.set_string_attribute(local_name, DOMString::new());
         } else {
             self.remove_attribute(&ns!(), local_name);
         }
     }
 
     pub fn get_url_attribute(&self, local_name: &LocalName) -> DOMString {
         assert!(*local_name == local_name.to_ascii_lowercase());
-        if !self.has_attribute(local_name) {
-            return DOMString::new();
-        }
-        let url = self.get_string_attribute(local_name);
-        let doc = document_from_node(self);
-        let base = doc.base_url();
-        // https://html.spec.whatwg.org/multipage/#reflect
-        // XXXManishearth this doesn't handle `javascript:` urls properly
-        match base.join(&url) {
-            Ok(parsed) => DOMString::from(parsed.into_string()),
-            Err(_) => DOMString::from(""),
+        let attr = match self.get_attribute(&ns!(), local_name) {
+            Some(attr) => attr,
+            None => return DOMString::new(),
+        };
+        let value = attr.value();
+        match *value {
+            AttrValue::Url(ref value, _) => {
+                // XXXManishearth this doesn't handle `javascript:` urls properly
+                let base = document_from_node(self).base_url();
+                let value = base.join(value)
+                    .map(|parsed| parsed.into_string())
+                    .unwrap_or_else(|_| value.clone());
+                DOMString::from(value)
+            },
+            _ => panic!("attribute value should be AttrValue::Url(..)"),
         }
     }
+
     pub fn set_url_attribute(&self, local_name: &LocalName, value: DOMString) {
-        self.set_string_attribute(local_name, value);
+        let value = AttrValue::from_url(
+            document_from_node(self).base_url(),
+            value.into(),
+        );
+        self.set_attribute(local_name, value);
     }
 
     pub fn get_string_attribute(&self, local_name: &LocalName) -> DOMString {
         match self.get_attribute(&ns!(), local_name) {
             Some(x) => x.Value(),
             None => DOMString::new(),
         }
     }
--- a/servo/components/script/dom/htmlbaseelement.rs
+++ b/servo/components/script/dom/htmlbaseelement.rs
@@ -11,17 +11,16 @@ use dom::bindings::str::DOMString;
 use dom::document::Document;
 use dom::element::{AttributeMutation, Element};
 use dom::htmlelement::HTMLElement;
 use dom::node::{Node, UnbindContext, document_from_node};
 use dom::virtualmethods::VirtualMethods;
 use dom_struct::dom_struct;
 use html5ever::{LocalName, Prefix};
 use servo_url::ServoUrl;
-use style::attr::AttrValue;
 
 #[dom_struct]
 pub struct HTMLBaseElement {
     htmlelement: HTMLElement
 }
 
 impl HTMLBaseElement {
     fn new_inherited(local_name: LocalName, prefix: Option<Prefix>, document: &Document) -> HTMLBaseElement {
@@ -62,38 +61,41 @@ impl HTMLBaseElement {
             document.refresh_base_element();
         }
     }
 }
 
 impl HTMLBaseElementMethods for HTMLBaseElement {
     // https://html.spec.whatwg.org/multipage/#dom-base-href
     fn Href(&self) -> DOMString {
+        // Step 1.
         let document = document_from_node(self);
 
-        // Step 1.
-        if !self.upcast::<Element>().has_attribute(&local_name!("href")) {
-            return DOMString::from(document.base_url().as_str());
-        }
-
         // Step 2.
-        let fallback_base_url = document.fallback_base_url();
+        let attr = self.upcast::<Element>().get_attribute(&ns!(), &local_name!("href"));
+        let value = attr.as_ref().map(|attr| attr.value());
+        let url = value.as_ref().map_or("", |value| &**value);
 
         // Step 3.
-        let url = self.upcast::<Element>().get_url_attribute(&local_name!("href"));
+        let url_record = document.fallback_base_url().join(url);
 
-        // Step 4.
-        let url_record = fallback_base_url.join(&*url);
-
-        // Step 5, 6.
-        DOMString::from(url_record.as_ref().map(|url| url.as_str()).unwrap_or(""))
+        match url_record {
+            Err(_) => {
+                // Step 4.
+                url.into()
+            }
+            Ok(url_record) => {
+                // Step 5.
+                url_record.into_string().into()
+            },
+        }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-base-href
-    make_url_setter!(SetHref, "href");
+    make_setter!(SetHref, "href");
 }
 
 impl VirtualMethods for HTMLBaseElement {
     fn super_type(&self) -> Option<&VirtualMethods> {
         Some(self.upcast::<HTMLElement>() as &VirtualMethods)
     }
 
     fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
--- a/servo/components/script/dom/htmlbodyelement.rs
+++ b/servo/components/script/dom/htmlbodyelement.rs
@@ -1,14 +1,15 @@
 /* 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 cssparser::RGBA;
 use dom::attr::Attr;
+use dom::bindings::codegen::Bindings::AttrBinding::AttrBinding::AttrMethods;
 use dom::bindings::codegen::Bindings::HTMLBodyElementBinding::{self, HTMLBodyElementMethods};
 use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::root::{LayoutDom, DomRoot};
 use dom::bindings::str::DOMString;
 use dom::document::Document;
 use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
 use dom::eventtarget::EventTarget;
@@ -68,17 +69,22 @@ impl HTMLBodyElementMethods for HTMLBody
 
     // https://html.spec.whatwg.org/multipage/#dom-body-text
     make_getter!(Text, "text");
 
     // https://html.spec.whatwg.org/multipage/#dom-body-text
     make_legacy_color_setter!(SetText, "text");
 
     // https://html.spec.whatwg.org/multipage/#dom-body-background
-    make_getter!(Background, "background");
+    fn Background(&self) -> DOMString {
+        self.upcast::<Element>()
+            .get_attribute(&ns!(), &local_name!("background"))
+            .map(|attr| attr.Value())
+            .unwrap_or_default()
+    }
 
     // https://html.spec.whatwg.org/multipage/#dom-body-background
     make_url_setter!(SetBackground, "background");
 
     // https://html.spec.whatwg.org/multipage/#windoweventhandlers
     window_event_handlers!(ForwardToWindow);
 }
 
@@ -149,17 +155,17 @@ impl VirtualMethods for HTMLBodyElement 
         window.upcast::<GlobalScope>().script_to_constellation_chan().send(event).unwrap();
     }
 
     fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
         match *name {
             local_name!("bgcolor") |
             local_name!("text") => AttrValue::from_legacy_color(value.into()),
             local_name!("background") => {
-                AttrValue::from_url(document_from_node(self).url(), value.into())
+                AttrValue::from_url(document_from_node(self).base_url(), value.into())
             },
             _ => self.super_type().unwrap().parse_plain_attribute(name, value),
         }
     }
 
     fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
         let do_super_mutate = match (attr.local_name(), mutation) {
             (name, AttributeMutation::Set(_)) if name.starts_with("on") => {
--- a/servo/components/script/dom/htmlbuttonelement.rs
+++ b/servo/components/script/dom/htmlbuttonelement.rs
@@ -88,17 +88,17 @@ impl HTMLButtonElementMethods for HTMLBu
 
     // https://html.spec.whatwg.org/multipage/#dom-button-type
     make_enumerated_getter!(Type, "type", "submit", "reset" | "button" | "menu");
 
     // https://html.spec.whatwg.org/multipage/#dom-button-type
     make_setter!(SetType, "type");
 
     // https://html.spec.whatwg.org/multipage/#dom-fs-formaction
-    make_url_or_base_getter!(FormAction, "formaction");
+    make_form_action_getter!(FormAction, "formaction");
 
     // https://html.spec.whatwg.org/multipage/#dom-fs-formaction
     make_setter!(SetFormAction, "formaction");
 
     // https://html.spec.whatwg.org/multipage/#dom-fs-formenctype
     make_enumerated_getter!(FormEnctype,
                             "formenctype",
                             "application/x-www-form-urlencoded",
--- a/servo/components/script/dom/htmlformelement.rs
+++ b/servo/components/script/dom/htmlformelement.rs
@@ -95,17 +95,17 @@ impl HTMLFormElement {
 impl HTMLFormElementMethods for HTMLFormElement {
     // https://html.spec.whatwg.org/multipage/#dom-form-acceptcharset
     make_getter!(AcceptCharset, "accept-charset");
 
     // https://html.spec.whatwg.org/multipage/#dom-form-acceptcharset
     make_setter!(SetAcceptCharset, "accept-charset");
 
     // https://html.spec.whatwg.org/multipage/#dom-fs-action
-    make_string_or_document_url_getter!(Action, "action");
+    make_form_action_getter!(Action, "action");
 
     // https://html.spec.whatwg.org/multipage/#dom-fs-action
     make_setter!(SetAction, "action");
 
     // https://html.spec.whatwg.org/multipage/#dom-form-autocomplete
     make_enumerated_getter!(Autocomplete, "autocomplete", "on", "off");
 
     // https://html.spec.whatwg.org/multipage/#dom-form-autocomplete
--- a/servo/components/script/dom/htmliframeelement.rs
+++ b/servo/components/script/dom/htmliframeelement.rs
@@ -563,24 +563,20 @@ pub fn Navigate(iframe: &HTMLIFrameEleme
     }
     debug!(concat!("this frame is not mozbrowser: mozbrowser attribute missing, or not a top",
                    "level window, or mozbrowser preference not set (use --pref dom.mozbrowser.enabled)"));
     Err(Error::NotSupported)
 }
 
 impl HTMLIFrameElementMethods for HTMLIFrameElement {
     // https://html.spec.whatwg.org/multipage/#dom-iframe-src
-    fn Src(&self) -> DOMString {
-        self.upcast::<Element>().get_string_attribute(&local_name!("src"))
-    }
+    make_url_getter!(Src, "src");
 
     // https://html.spec.whatwg.org/multipage/#dom-iframe-src
-    fn SetSrc(&self, src: DOMString) {
-        self.upcast::<Element>().set_url_attribute(&local_name!("src"), src)
-    }
+    make_url_setter!(SetSrc, "src");
 
     // https://html.spec.whatwg.org/multipage/#dom-iframe-sandbox
     fn Sandbox(&self) -> DomRoot<DOMTokenList> {
         self.sandbox.or_init(|| DOMTokenList::new(self.upcast::<Element>(), &local_name!("sandbox")))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-iframe-contentwindow
     fn GetContentWindow(&self) -> Option<DomRoot<WindowProxy>> {
@@ -760,16 +756,17 @@ impl VirtualMethods for HTMLIFrameElemen
                 }
             },
             _ => {},
         }
     }
 
     fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
         match name {
+            &local_name!("src") => AttrValue::from_url(document_from_node(self).base_url(), value.into()),
             &local_name!("sandbox") => AttrValue::from_serialized_tokenlist(value.into()),
             &local_name!("width") => AttrValue::from_dimension(value.into()),
             &local_name!("height") => AttrValue::from_dimension(value.into()),
             _ => self.super_type().unwrap().parse_plain_attribute(name, value),
         }
     }
 
     fn bind_to_tree(&self, tree_in_doc: bool) {
--- a/servo/components/script/dom/htmlimageelement.rs
+++ b/servo/components/script/dom/htmlimageelement.rs
@@ -819,18 +819,19 @@ pub fn parse_a_sizes_attribute(input: DO
 impl HTMLImageElementMethods for HTMLImageElement {
     // https://html.spec.whatwg.org/multipage/#dom-img-alt
     make_getter!(Alt, "alt");
     // https://html.spec.whatwg.org/multipage/#dom-img-alt
     make_setter!(SetAlt, "alt");
 
     // https://html.spec.whatwg.org/multipage/#dom-img-src
     make_url_getter!(Src, "src");
+
     // https://html.spec.whatwg.org/multipage/#dom-img-src
-    make_setter!(SetSrc, "src");
+    make_url_setter!(SetSrc, "src");
 
     // https://html.spec.whatwg.org/multipage/#dom-img-crossOrigin
     fn GetCrossOrigin(&self) -> Option<DOMString> {
         reflect_cross_origin_attribute(self.upcast::<Element>())
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-img-crossOrigin
     fn SetCrossOrigin(&self, value: Option<DOMString>) {
@@ -975,16 +976,17 @@ impl VirtualMethods for HTMLImageElement
         match attr.local_name() {
             &local_name!("src") => self.update_the_image_data(),
             _ => {},
         }
     }
 
     fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
         match name {
+            &local_name!("src") => AttrValue::from_url(document_from_node(self).base_url(), value.into()),
             &local_name!("name") => AttrValue::from_atomic(value.into()),
             &local_name!("width") | &local_name!("height") => AttrValue::from_dimension(value.into()),
             &local_name!("hspace") | &local_name!("vspace") => AttrValue::from_u32(value.into(), 0),
             _ => self.super_type().unwrap().parse_plain_attribute(name, value),
         }
     }
 
     fn handle_event(&self, event: &Event) {
--- a/servo/components/script/dom/htmlinputelement.rs
+++ b/servo/components/script/dom/htmlinputelement.rs
@@ -447,17 +447,17 @@ impl HTMLInputElementMethods for HTMLInp
 
     // https://html.spec.whatwg.org/multipage/#dom-input-placeholder
     make_getter!(Placeholder, "placeholder");
 
     // https://html.spec.whatwg.org/multipage/#dom-input-placeholder
     make_setter!(SetPlaceholder, "placeholder");
 
     // https://html.spec.whatwg.org/multipage/#dom-input-formaction
-    make_url_or_base_getter!(FormAction, "formaction");
+    make_form_action_getter!(FormAction, "formaction");
 
     // https://html.spec.whatwg.org/multipage/#dom-input-formaction
     make_setter!(SetFormAction, "formaction");
 
     // https://html.spec.whatwg.org/multipage/#dom-input-formenctype
     make_enumerated_getter!(FormEnctype,
                             "formenctype",
                             "application/x-www-form-urlencoded",
@@ -1051,16 +1051,17 @@ impl VirtualMethods for HTMLInputElement
                 self.form_attribute_mutated(mutation);
             },
             _ => {},
         }
     }
 
     fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
         match name {
+            &local_name!("src") => AttrValue::from_url(document_from_node(self).base_url(), value.into()),
             &local_name!("accept") => AttrValue::from_comma_separated_tokenlist(value.into()),
             &local_name!("name") => AttrValue::from_atomic(value.into()),
             &local_name!("size") => AttrValue::from_limited_u32(value.into(), DEFAULT_INPUT_SIZE),
             &local_name!("type") => AttrValue::from_atomic(value.into()),
             &local_name!("maxlength") => AttrValue::from_limited_i32(value.into(), DEFAULT_MAX_LENGTH),
             &local_name!("minlength") => AttrValue::from_limited_i32(value.into(), DEFAULT_MIN_LENGTH),
             _ => self.super_type().unwrap().parse_plain_attribute(name, value),
         }
--- a/servo/components/script/dom/htmllinkelement.rs
+++ b/servo/components/script/dom/htmllinkelement.rs
@@ -203,16 +203,17 @@ impl VirtualMethods for HTMLLinkElement 
                 }
             },
             _ => {},
         }
     }
 
     fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
         match name {
+            &local_name!("href") => AttrValue::from_url(document_from_node(self).base_url(), value.into()),
             &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
             _ => self.super_type().unwrap().parse_plain_attribute(name, value),
         }
     }
 
     fn bind_to_tree(&self, tree_in_doc: bool) {
         if let Some(ref s) = self.super_type() {
             s.bind_to_tree(tree_in_doc);
@@ -368,17 +369,17 @@ impl StylesheetOwner for HTMLLinkElement
     }
 }
 
 impl HTMLLinkElementMethods for HTMLLinkElement {
     // https://html.spec.whatwg.org/multipage/#dom-link-href
     make_url_getter!(Href, "href");
 
     // https://html.spec.whatwg.org/multipage/#dom-link-href
-    make_setter!(SetHref, "href");
+    make_url_setter!(SetHref, "href");
 
     // https://html.spec.whatwg.org/multipage/#dom-link-rel
     make_getter!(Rel, "rel");
 
     // https://html.spec.whatwg.org/multipage/#dom-link-rel
     fn SetRel(&self, rel: DOMString) {
         self.upcast::<Element>().set_tokenlist_attribute(&local_name!("rel"), rel);
     }
--- a/servo/components/script/dom/htmlmediaelement.rs
+++ b/servo/components/script/dom/htmlmediaelement.rs
@@ -41,16 +41,17 @@ use net_traits::request::{CredentialsMod
 use network_listener::{NetworkListener, PreInvoke};
 use script_thread::ScriptThread;
 use servo_url::ServoUrl;
 use std::cell::Cell;
 use std::collections::VecDeque;
 use std::mem;
 use std::rc::Rc;
 use std::sync::{Arc, Mutex};
+use style::attr::AttrValue;
 use task_source::TaskSource;
 use time::{self, Timespec, Duration};
 
 #[dom_struct]
 // FIXME(nox): A lot of tasks queued for this element should probably be in the
 // media element event task source.
 pub struct HTMLMediaElement {
     htmlelement: HTMLElement,
@@ -832,18 +833,19 @@ impl HTMLMediaElementMethods for HTMLMed
 
     // https://html.spec.whatwg.org/multipage/#dom-media-autoplay
     make_bool_getter!(Autoplay, "autoplay");
     // https://html.spec.whatwg.org/multipage/#dom-media-autoplay
     make_bool_setter!(SetAutoplay, "autoplay");
 
     // https://html.spec.whatwg.org/multipage/#dom-media-src
     make_url_getter!(Src, "src");
+
     // https://html.spec.whatwg.org/multipage/#dom-media-src
-    make_setter!(SetSrc, "src");
+    make_url_setter!(SetSrc, "src");
 
     // https://html.spec.whatwg.org/multipage/#dom-media-srcobject
     fn GetSrcObject(&self) -> Option<DomRoot<Blob>> {
         self.src_object.get()
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-media-srcobject
     fn SetSrcObject(&self, value: Option<&Blob>) {
@@ -908,16 +910,23 @@ impl HTMLMediaElementMethods for HTMLMed
     }
 }
 
 impl VirtualMethods for HTMLMediaElement {
     fn super_type(&self) -> Option<&VirtualMethods> {
         Some(self.upcast::<HTMLElement>() as &VirtualMethods)
     }
 
+    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
+        match name {
+            &local_name!("src") => AttrValue::from_url(document_from_node(self).base_url(), value.into()),
+            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
+        }
+    }
+
     fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
         self.super_type().unwrap().attribute_mutated(attr, mutation);
 
         match attr.local_name() {
             &local_name!("src") => {
                 if mutation.new_value(attr).is_some() {
                     self.media_element_load_algorithm();
                 }
--- a/servo/components/script/dom/htmlscriptelement.rs
+++ b/servo/components/script/dom/htmlscriptelement.rs
@@ -37,16 +37,17 @@ use servo_config::opts;
 use servo_url::ServoUrl;
 use std::ascii::AsciiExt;
 use std::cell::Cell;
 use std::fs::File;
 use std::io::{Read, Write};
 use std::path::PathBuf;
 use std::process::{Command, Stdio};
 use std::sync::{Arc, Mutex};
+use style::attr::AttrValue;
 use style::str::{HTML_SPACE_CHARACTERS, StaticStringVec};
 use uuid::Uuid;
 
 #[dom_struct]
 pub struct HTMLScriptElement {
     htmlelement: HTMLElement,
 
     /// https://html.spec.whatwg.org/multipage/#already-started
@@ -648,16 +649,23 @@ impl HTMLScriptElement {
     }
 }
 
 impl VirtualMethods for HTMLScriptElement {
     fn super_type(&self) -> Option<&VirtualMethods> {
         Some(self.upcast::<HTMLElement>() as &VirtualMethods)
     }
 
+    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
+        match name {
+            &local_name!("src") => AttrValue::from_url(document_from_node(self).base_url(), value.into()),
+            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
+        }
+    }
+
     fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
         self.super_type().unwrap().attribute_mutated(attr, mutation);
         match *attr.local_name() {
             local_name!("src") => {
                 if let AttributeMutation::Set(_) = mutation {
                     if !self.parser_inserted.get() && self.upcast::<Node>().is_in_doc() {
                         self.prepare();
                     }
@@ -697,18 +705,19 @@ impl VirtualMethods for HTMLScriptElemen
             copy.downcast::<HTMLScriptElement>().unwrap().set_already_started(true);
         }
     }
 }
 
 impl HTMLScriptElementMethods for HTMLScriptElement {
     // https://html.spec.whatwg.org/multipage/#dom-script-src
     make_url_getter!(Src, "src");
+
     // https://html.spec.whatwg.org/multipage/#dom-script-src
-    make_setter!(SetSrc, "src");
+    make_url_setter!(SetSrc, "src");
 
     // https://html.spec.whatwg.org/multipage/#dom-script-type
     make_getter!(Type, "type");
     // https://html.spec.whatwg.org/multipage/#dom-script-type
     make_setter!(SetType, "type");
 
     // https://html.spec.whatwg.org/multipage/#dom-script-charset
     make_getter!(Charset, "charset");
--- a/servo/components/script/dom/macros.rs
+++ b/servo/components/script/dom/macros.rs
@@ -101,48 +101,32 @@ macro_rules! make_url_getter(
             use dom::element::Element;
             let element = self.upcast::<Element>();
             element.get_url_attribute(&local_name!($htmlname))
         }
     );
 );
 
 #[macro_export]
-macro_rules! make_url_or_base_getter(
+macro_rules! make_form_action_getter(
     ( $attr:ident, $htmlname:tt ) => (
         fn $attr(&self) -> DOMString {
             use dom::bindings::inheritance::Castable;
             use dom::element::Element;
             let element = self.upcast::<Element>();
-            let url = element.get_url_attribute(&local_name!($htmlname));
-            if url.is_empty() {
-                let window = window_from_node(self);
-                DOMString::from(window.get_url().into_string())
-            } else {
-                url
-            }
-        }
-    );
-);
-
-#[macro_export]
-macro_rules! make_string_or_document_url_getter(
-    ( $attr:ident, $htmlname:tt ) => (
-        fn $attr(&self) -> DOMString {
-            use dom::bindings::inheritance::Castable;
-            use dom::element::Element;
-            use dom::node::document_from_node;
-            let element = self.upcast::<Element>();
-            let val = element.get_string_attribute(&local_name!($htmlname));
-
-            if val.is_empty() {
-                let doc = document_from_node(self);
-                DOMString::from(doc.url().into_string())
-            } else {
-                val
+            let doc = ::dom::node::document_from_node(self);
+            let attr = element.get_attribute(&ns!(), &local_name!($htmlname));
+            let value = attr.as_ref().map(|attr| attr.value());
+            let value = match value {
+                Some(ref value) if !value.is_empty() => &***value,
+                _ => return doc.url().into_string().into(),
+            };
+            match doc.base_url().join(value) {
+                Ok(parsed) => parsed.into_string().into(),
+                Err(_) => value.to_owned().into(),
             }
         }
     );
 );
 
 #[macro_export]
 macro_rules! make_enumerated_getter(
     ( $attr:ident, $htmlname:tt, $default:expr, $($choices: pat)|+) => (
@@ -189,21 +173,18 @@ macro_rules! make_bool_setter(
 );
 
 #[macro_export]
 macro_rules! make_url_setter(
     ( $attr:ident, $htmlname:tt ) => (
         fn $attr(&self, value: DOMString) {
             use dom::bindings::inheritance::Castable;
             use dom::element::Element;
-            use dom::node::document_from_node;
-            let value = AttrValue::from_url(document_from_node(self).url(),
-                                            value.into());
             let element = self.upcast::<Element>();
-            element.set_attribute(&local_name!($htmlname), value);
+            element.set_url_attribute(&local_name!($htmlname), value);
         }
     );
 );
 
 #[macro_export]
 macro_rules! make_uint_setter(
     ($attr:ident, $htmlname:tt, $default:expr) => (
         fn $attr(&self, value: u32) {
--- a/servo/components/style/gecko/restyle_damage.rs
+++ b/servo/components/style/gecko/restyle_damage.rs
@@ -52,16 +52,26 @@ impl GeckoRestyleDamage {
         let hint = unsafe {
             bindings::Gecko_CalcStyleDifference(
                 old_style,
                 new_style,
                 &mut any_style_changed,
                 &mut reset_only,
             )
         };
+        if reset_only &&
+           old_style.custom_properties() != new_style.custom_properties() {
+            // The Gecko_CalcStyleDifference call only checks the non-custom
+            // property structs, so we check the custom properties here. Since
+            // they generate no damage themselves, we can skip this check if we
+            // already know we had some inherited (regular) property
+            // differences.
+            any_style_changed = true;
+            reset_only = false;
+        }
         let change = if any_style_changed {
             StyleChange::Changed { reset_only }
         } else {
             StyleChange::Unchanged
         };
         StyleDifference::new(GeckoRestyleDamage(nsChangeHint(hint)), change)
     }
 
--- a/servo/components/style/invalidation/element/invalidator.rs
+++ b/servo/components/style/invalidation/element/invalidator.rs
@@ -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/. */
 
 //! The struct that takes care of encapsulating all the logic on where and how
 //! element styles need to be invalidated.
 
 use Atom;
-use context::{SharedStyleContext, StackLimitChecker};
+use context::{QuirksMode, SharedStyleContext, StackLimitChecker};
 use data::ElementData;
 use dom::{TElement, TNode};
 use element_state::{ElementState, IN_VISITED_OR_UNVISITED_STATE};
 use invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
 use invalidation::element::invalidation_map::*;
 use invalidation::element::restyle_hints::*;
 use selector_map::SelectorMap;
 use selector_parser::{SelectorImpl, Snapshot};
@@ -257,17 +257,17 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator
         let invalidated_self = {
             let mut collector = InvalidationCollector {
                 wrapper,
                 lookup_element,
                 nth_index_cache: self.nth_index_cache.as_mut().map(|c| &mut **c),
                 state_changes,
                 element: self.element,
                 snapshot: &snapshot,
-                shared_context: self.shared_context,
+                quirks_mode: self.shared_context.quirks_mode(),
                 removed_id: id_removed.as_ref(),
                 added_id: id_added.as_ref(),
                 classes_removed: &classes_removed,
                 classes_added: &classes_added,
                 descendant_invalidations: &mut descendant_invalidations,
                 sibling_invalidations: &mut sibling_invalidations,
                 invalidates_self: false,
             };
@@ -277,16 +277,20 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator
             });
 
             // TODO(emilio): Consider storing dependencies from the UA sheet in
             // a different map. If we do that, we can skip the stuff on the
             // shared stylist iff cut_off_inheritance is true, and we can look
             // just at that map.
             let _cut_off_inheritance =
                 self.element.each_xbl_stylist(|stylist| {
+                    // FIXME(emilio): Replace with assert / remove when we
+                    // figure out what to do with the quirks mode mismatches
+                    // (that is, when bug 1406875 is properly fixed).
+                    collector.quirks_mode = stylist.quirks_mode();
                     stylist.each_invalidation_map(|invalidation_map| {
                         collector.collect_dependencies_in_invalidation_map(invalidation_map);
                     });
                 });
 
             collector.invalidates_self
         };
 
@@ -818,17 +822,17 @@ impl<'a, 'b: 'a, E> TreeStyleInvalidator
 
 struct InvalidationCollector<'a, 'b: 'a, E>
     where E: TElement,
 {
     element: E,
     wrapper: ElementWrapper<'b, E>,
     nth_index_cache: Option<&'a mut NthIndexCache>,
     snapshot: &'a Snapshot,
-    shared_context: &'a SharedStyleContext<'b>,
+    quirks_mode: QuirksMode,
     lookup_element: E,
     removed_id: Option<&'a Atom>,
     added_id: Option<&'a Atom>,
     classes_removed: &'a SmallVec<[Atom; 8]>,
     classes_added: &'a SmallVec<[Atom; 8]>,
     state_changes: ElementState,
     descendant_invalidations: &'a mut InvalidationVector,
     sibling_invalidations: &'a mut InvalidationVector,
@@ -837,17 +841,17 @@ struct InvalidationCollector<'a, 'b: 'a,
 
 impl<'a, 'b: 'a, E> InvalidationCollector<'a, 'b, E>
     where E: TElement,
 {
     fn collect_dependencies_in_invalidation_map(
         &mut self,
         map: &InvalidationMap,
     ) {
-        let quirks_mode = self.shared_context.quirks_mode();
+        let quirks_mode = self.quirks_mode;
         let removed_id = self.removed_id;
         if let Some(ref id) = removed_id {
             if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
                 for dep in deps {
                     self.scan_dependency(dep, VisitedDependent::No);
                 }
             }
         }
@@ -890,34 +894,34 @@ impl<'a, 'b: 'a, E> InvalidationCollecto
     }
 
     fn collect_dependencies_in_map(
         &mut self,
         map: &SelectorMap<Dependency>,
     ) {
         map.lookup_with_additional(
             self.lookup_element,
-            self.shared_context.quirks_mode(),
+            self.quirks_mode,
             self.removed_id,
             self.classes_removed,
             &mut |dependency| {
                 self.scan_dependency(dependency, VisitedDependent::No);
                 true
             },
         );
     }
 
     fn collect_state_dependencies(
         &mut self,
         map: &SelectorMap<StateDependency>,
         state_changes: ElementState,
     ) {
         map.lookup_with_additional(
             self.lookup_element,
-            self.shared_context.quirks_mode(),
+            self.quirks_mode,
             self.removed_id,
             self.classes_removed,
             &mut |dependency| {
                 if !dependency.state.intersects(state_changes) {
                     return true;
                 }
                 let visited_dependent =
                     if dependency.state.intersects(IN_VISITED_OR_UNVISITED_STATE) {
@@ -940,17 +944,17 @@ impl<'a, 'b: 'a, E> InvalidationCollecto
         relevant_link_found: &mut bool,
     ) -> bool {
         let (matches_now, relevant_link_found_now) = {
             let mut context = MatchingContext::new_for_visited(
                 MatchingMode::Normal,
                 None,
                 self.nth_index_cache.as_mut().map(|c| &mut **c),
                 visited_handling_mode,
-                self.shared_context.quirks_mode(),
+                self.quirks_mode,
             );
 
             let matches_now = matches_selector(
                 &dependency.selector,
                 dependency.selector_offset,
                 None,
                 &self.element,
                 &mut context,
@@ -961,17 +965,17 @@ impl<'a, 'b: 'a, E> InvalidationCollecto
         };
 
         let (matched_then, relevant_link_found_then) = {
             let mut context = MatchingContext::new_for_visited(
                 MatchingMode::Normal,
                 None,
                 self.nth_index_cache.as_mut().map(|c| &mut **c),
                 visited_handling_mode,
-                self.shared_context.quirks_mode(),
+                self.quirks_mode,
             );
 
             let matched_then = matches_selector(
                 &dependency.selector,
                 dependency.selector_offset,
                 None,
                 &self.wrapper,
                 &mut context,
--- a/servo/components/style/invalidation/stylesheets.rs
+++ b/servo/components/style/invalidation/stylesheets.rs
@@ -3,84 +3,108 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! A collection of invalidations due to changes in which stylesheets affect a
 //! document.
 
 #![deny(unsafe_code)]
 
 use Atom;
+use LocalName as SelectorLocalName;
 use dom::{TElement, TNode};
 use fnv::FnvHashSet;
-use invalidation::element::restyle_hints::RestyleHint;
+use invalidation::element::restyle_hints::{RESTYLE_SELF, RestyleHint};
 use media_queries::Device;
 use selector_parser::SelectorImpl;
 use selectors::attr::CaseSensitivity;
-use selectors::parser::{Component, Selector};
+use selectors::parser::{Component, LocalName, Selector};
 use shared_lock::SharedRwLockReadGuard;
 use stylesheets::{CssRule, StylesheetInDocument};
 
-/// An invalidation scope represents a kind of subtree that may need to be
-/// restyled.
+/// A style sheet invalidation represents a kind of element or subtree that may
+/// need to be restyled. Whether it represents a whole subtree or just a single
+/// element is determined by whether the invalidation is stored in the
+/// StylesheetInvalidationSet's invalid_scopes or invalid_elements table.
 #[derive(Debug, Eq, Hash, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-enum InvalidationScope {
-    /// All the descendants of an element with a given id.
+enum Invalidation {
+    /// An element with a given id.
     ID(Atom),
-    /// All the descendants of an element with a given class name.
+    /// An element with a given class name.
     Class(Atom),
+    /// An element with a given local name.
+    LocalName { name: SelectorLocalName, lower_name: SelectorLocalName },
 }
 
-impl InvalidationScope {
+impl Invalidation {
     fn is_id(&self) -> bool {
-        matches!(*self, InvalidationScope::ID(..))
+        matches!(*self, Invalidation::ID(..))
+    }
+
+    fn is_id_or_class(&self) -> bool {
+        matches!(*self, Invalidation::ID(..) | Invalidation::Class(..))
     }
 
     fn matches<E>(&self, element: E) -> bool
         where E: TElement,
     {
         match *self {
-            InvalidationScope::Class(ref class) => {
+            Invalidation::Class(ref class) => {
+                // FIXME This should look at the quirks mode of the document to
+                // determine case sensitivity.
                 element.has_class(class, CaseSensitivity::CaseSensitive)
             }
-            InvalidationScope::ID(ref id) => {
+            Invalidation::ID(ref id) => {
                 match element.get_id() {
+                    // FIXME This should look at the quirks mode of the document
+                    // to determine case sensitivity.
                     Some(element_id) => element_id == *id,
                     None => false,
                 }
             }
+            Invalidation::LocalName { ref name, ref lower_name } => {
+                // This could look at the quirks mode of the document, instead
+                // of testing against both names, but it's probably not worth
+                // it.
+                let local_name = element.get_local_name();
+                *local_name == **name || *local_name == **lower_name
+            }
         }
     }
 }
 
 /// A set of invalidations due to stylesheet additions.
 ///
 /// TODO(emilio): We might be able to do the same analysis for media query
 /// changes too (or even selector changes?).
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct StylesheetInvalidationSet {
-    /// The style scopes we know we have to restyle so far.
-    invalid_scopes: FnvHashSet<InvalidationScope>,
-    /// Whether the whole document should be invalid.
+    /// The subtrees we know we have to restyle so far.
+    invalid_scopes: FnvHashSet<Invalidation>,
+    /// The elements we know we have to restyle so far.
+    invalid_elements: FnvHashSet<Invalidation>,
+    /// Whether the whole document should be restyled.
     fully_invalid: bool,
 }
 
 impl StylesheetInvalidationSet {
     /// Create an empty `StylesheetInvalidationSet`.
     pub fn new() -> Self {
         Self {
             invalid_scopes: FnvHashSet::default(),
+            invalid_elements: FnvHashSet::default(),
             fully_invalid: false,
         }
     }
 
     /// Mark the DOM tree styles' as fully invalid.
     pub fn invalidate_fully(&mut self) {
         debug!("StylesheetInvalidationSet::invalidate_fully");
         self.invalid_scopes.clear();
+        self.invalid_elements.clear();
         self.fully_invalid = true;
     }
 
     /// Analyze the given stylesheet, and collect invalidations from their
     /// rules, in order to avoid doing a full restyle when we style the document
     /// next time.
     pub fn collect_invalidations_for<S>(
         &mut self,
@@ -102,21 +126,23 @@ impl StylesheetInvalidationSet {
             debug!(" > Stylesheet was not effective");
             return; // Nothing to do here.
         }
 
         for rule in stylesheet.effective_rules(device, guard) {
             self.collect_invalidations_for_rule(rule, guard);
             if self.fully_invalid {
                 self.invalid_scopes.clear();
+                self.invalid_elements.clear();
                 break;
             }
         }
 
-        debug!(" > resulting invalidations: {:?}", self.invalid_scopes);
+        debug!(" > resulting subtree invalidations: {:?}", self.invalid_scopes);
+        debug!(" > resulting self invalidations: {:?}", self.invalid_elements);
         debug!(" > fully_invalid: {}", self.fully_invalid);
     }
 
     /// Clears the invalidation set, invalidating elements as needed if
     /// `document_element` is provided.
     ///
     /// Returns true if any invalidations ocurred.
     pub fn flush<E>(&mut self, document_element: Option<E>) -> bool
@@ -128,16 +154,17 @@ impl StylesheetInvalidationSet {
         };
         self.clear();
         have_invalidations
     }
 
     /// Clears the invalidation set without processing.
     pub fn clear(&mut self) {
         self.invalid_scopes.clear();
+        self.invalid_elements.clear();
         self.fully_invalid = false;
     }
 
     fn process_invalidations<E>(&self, element: E) -> bool
         where E: TElement,
     {
         {
             let mut data = match element.mutate_data() {
@@ -148,27 +175,27 @@ impl StylesheetInvalidationSet {
             if self.fully_invalid {
                 debug!("process_invalidations: fully_invalid({:?})",
                        element);
                 data.hint.insert(RestyleHint::restyle_subtree());
                 return true;
             }
         }
 
-        if self.invalid_scopes.is_empty() {
+        if self.invalid_scopes.is_empty() && self.invalid_elements.is_empty() {
             debug!("process_invalidations: empty invalidation set");
             return false;
         }
 
         self.process_invalidations_in_subtree(element)
     }
 
-    /// Process style invalidations in a given subtree, that is, look for all
-    /// the relevant scopes in the subtree, and mark as dirty only the relevant
-    /// ones.
+    /// Process style invalidations in a given subtree. This traverses the
+    /// subtree looking for elements that match the invalidations in
+    /// invalid_scopes and invalid_elements.
     ///
     /// Returns whether it invalidated at least one element's style.
     #[allow(unsafe_code)]
     fn process_invalidations_in_subtree<E>(&self, element: E) -> bool
         where E: TElement,
     {
         let mut data = match element.mutate_data() {
             Some(data) => data,
@@ -180,25 +207,38 @@ impl StylesheetInvalidationSet {
         }
 
         if data.hint.contains_subtree() {
             debug!("process_invalidations_in_subtree: {:?} was already invalid",
                    element);
             return false;
         }
 
-        for scope in &self.invalid_scopes {
-            if scope.matches(element) {
-                debug!("process_invalidations_in_subtree: {:?} matched {:?}",
-                       element, scope);
+        for invalidation in &self.invalid_scopes {
+            if invalidation.matches(element) {
+                debug!("process_invalidations_in_subtree: {:?} matched subtree {:?}",
+                       element, invalidation);
                 data.hint.insert(RestyleHint::restyle_subtree());
                 return true;
             }
         }
 
+        let mut self_invalid = false;
+
+        if !data.hint.contains(RESTYLE_SELF) {
+            for invalidation in &self.invalid_elements {
+                if invalidation.matches(element) {
+                    debug!("process_invalidations_in_subtree: {:?} matched self {:?}",
+                           element, invalidation);
+                    data.hint.insert(RESTYLE_SELF);
+                    self_invalid = true;
+                    break;
+                }
+            }
+        }
 
         let mut any_children_invalid = false;
 
         for child in element.as_node().traversal_children() {
             let child = match child.as_element() {
                 Some(e) => e,
                 None => continue,
             };
@@ -207,81 +247,101 @@ impl StylesheetInvalidationSet {
         }
 
         if any_children_invalid {
             debug!("Children of {:?} changed, setting dirty descendants",
                    element);
             unsafe { element.set_dirty_descendants() }
         }
 
-        return any_children_invalid
+        return self_invalid || any_children_invalid
     }
 
     fn scan_component(
         component: &Component<SelectorImpl>,
-        scope: &mut Option<InvalidationScope>)
+        invalidation: &mut Option<Invalidation>)
     {
         match *component {
+            Component::LocalName(LocalName { ref name, ref lower_name }) => {
+                if invalidation.as_ref().map_or(true, |s| !s.is_id_or_class()) {
+                    *invalidation = Some(Invalidation::LocalName {
+                        name: name.clone(),
+                        lower_name: lower_name.clone(),
+                    });
+                }
+            }
             Component::Class(ref class) => {
-                if scope.as_ref().map_or(true, |s| !s.is_id()) {
-                    *scope = Some(InvalidationScope::Class(class.clone()));
+                if invalidation.as_ref().map_or(true, |s| !s.is_id()) {
+                    *invalidation = Some(Invalidation::Class(class.clone()));
                 }
             }
             Component::ID(ref id) => {
-                if scope.is_none() {
-                    *scope = Some(InvalidationScope::ID(id.clone()));
+                if invalidation.is_none() {
+                    *invalidation = Some(Invalidation::ID(id.clone()));
                 }
             }
             _ => {
                 // Ignore everything else, at least for now.
             }
         }
     }
 
-    /// Collect a style scopes for a given selector.
+    /// Collect invalidations for a given selector.
     ///
-    /// We look at the outermost class or id selector to the left of an ancestor
-    /// combinator, in order to restyle only a given subtree.
+    /// We look at the outermost local name, class, or ID selector to the left
+    /// of an ancestor combinator, in order to restyle only a given subtree.
+    ///
+    /// If the selector has no ancestor combinator, then we do the same for
+    /// the only sequence it has, but record it as an element invalidation
+    /// instead of a subtree invalidation.
     ///
-    /// We prefer id scopes to class scopes, and outermost scopes to innermost
-    /// scopes (to reduce the amount of traversal we need to do).
-    fn collect_scopes(&mut self, selector: &Selector<SelectorImpl>) {
-        debug!("StylesheetInvalidationSet::collect_scopes({:?})", selector);
+    /// We prefer IDs to classs, and classes to local names, on the basis
+    /// that the former should be more specific than the latter. We also
+    /// prefer to generate subtree invalidations for the outermost part
+    /// of the selector, to reduce the amount of traversal we need to do
+    /// when flushing invalidations.
+    fn collect_invalidations(&mut self, selector: &Selector<SelectorImpl>) {
+        debug!("StylesheetInvalidationSet::collect_invalidations({:?})", selector);
 
-        let mut scope: Option<InvalidationScope> = None;
+        let mut element_invalidation: Option<Invalidation> = None;
+        let mut subtree_invalidation: Option<Invalidation> = None;
 
-        let mut scan = true;
+        let mut scan_for_element_invalidation = true;
+        let mut scan_for_subtree_invalidation = false;
+
         let mut iter = selector.iter();
 
         loop {
             for component in &mut iter {
-                if scan {
-                    Self::scan_component(component, &mut scope);
+                if scan_for_element_invalidation {
+                    Self::scan_component(component, &mut element_invalidation);
+                } else if scan_for_subtree_invalidation {
+                    Self::scan_component(component, &mut subtree_invalidation);
                 }
             }
             match iter.next_sequence() {
                 None => break,
                 Some(combinator) => {
-                    scan = combinator.is_ancestor();
+                    scan_for_subtree_invalidation = combinator.is_ancestor();
                 }
             }
+            scan_for_element_invalidation = false;
         }
 
-        match scope {
-            Some(s) => {
-                debug!(" > Found scope: {:?}", s);
-                self.invalid_scopes.insert(s);
-            }
-            None => {
-                debug!(" > Scope not found");
-
-                // If we didn't find a scope, any element could match this, so
-                // let's just bail out.
-                self.fully_invalid = true;
-            }
+        if let Some(s) = subtree_invalidation {
+            debug!(" > Found subtree invalidation: {:?}", s);
+            self.invalid_scopes.insert(s);
+        } else if let Some(s) = element_invalidation {
+            debug!(" > Found element invalidation: {:?}", s);
+            self.invalid_elements.insert(s);
+        } else {
+            // The selector was of a form that we can't handle. Any element
+            // could match it, so let's just bail out.
+            debug!(" > Can't handle selector, marking fully invalid");
+            self.fully_invalid = true;
         }
     }
 
     /// Collects invalidations for a given CSS rule.
     fn collect_invalidations_for_rule(
         &mut self,
         rule: &CssRule,
         guard: &SharedRwLockReadGuard)
@@ -289,17 +349,17 @@ impl StylesheetInvalidationSet {
         use stylesheets::CssRule::*;
         debug!("StylesheetInvalidationSet::collect_invalidations_for_rule");
         debug_assert!(!self.fully_invalid, "Not worth to be here!");
 
         match *rule {
             Style(ref lock) => {
                 let style_rule = lock.read_with(guard);
                 for selector in &style_rule.selectors.0 {
-                    self.collect_scopes(selector);
+                    self.collect_invalidations(selector);
                     if self.fully_invalid {
                         return;
                     }
                 }
             }
             Document(..) |
             Namespace(..) |
             Import(..) |
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -246,17 +246,17 @@ impl ops::Deref for ComputedValues {
 impl ops::DerefMut for ComputedValues {
     fn deref_mut(&mut self) -> &mut ComputedValuesInner {
         &mut self.0.mSource
     }
 }
 
 impl ComputedValuesInner {
     /// Clone the visited style.  Used for inheriting parent styles in
-    /// StyleBuilder::for_inheritance.
+    /// StyleBuilder::for_derived_style.
     pub fn clone_visited_style(&self) -> Option<Arc<ComputedValues>> {
         self.visited_style.as_ref().map(|x| x.clone_arc())
     }
 
     #[inline]
     pub fn is_display_contents(&self) -> bool {
         self.get_box().clone_display() == longhands::display::computed_value::T::contents
     }
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -2206,17 +2206,17 @@ impl ComputedValuesInner {
         self.rules.as_ref().unwrap()
     }
 
     /// Whether this style has a -moz-binding value. This is always false for
     /// Servo for obvious reasons.
     pub fn has_moz_binding(&self) -> bool { false }
 
     /// Clone the visited style.  Used for inheriting parent styles in
-    /// StyleBuilder::for_inheritance.
+    /// StyleBuilder::for_derived_style.
     pub fn clone_visited_style(&self) -> Option<Arc<ComputedValues>> {
         self.visited_style.clone()
     }
 
     /// Returns whether this style's display value is equal to contents.
     ///
     /// Since this isn't supported in Servo, this is always false for Servo.
     pub fn is_display_contents(&self) -> bool { false }
@@ -2837,30 +2837,42 @@ impl<'a> StyleBuilder<'a> {
 
     /// Inherits style from the parent element, accounting for the default
     /// computed values that need to be provided as well.
     pub fn for_inheritance(
         device: &'a Device,
         parent: &'a ComputedValues,
         pseudo: Option<<&'a PseudoElement>,
     ) -> Self {
+        // Rebuild the visited style from the parent, ensuring that it will also
+        // not have rules.  This matches the unvisited style that will be
+        // produced by this builder.  This assumes that the caller doesn't need
+        // to adjust or process visited style, so we can just build visited
+        // style here for simplicity.
+        let visited_style = parent.visited_style().map(|style| {
+            Self::for_inheritance(
+                device,
+                style,
+                pseudo,
+            ).build()
+        });
         // FIXME(emilio): This Some(parent) here is inconsistent with what we
         // usually do if `parent` is the default computed values, but that's
         // fine, and we want to eventually get rid of it.
         Self::new(
             device,
             Some(parent),
             Some(parent),
             pseudo,
             CascadeFlags::empty(),
             /* rules = */ None,
             parent.custom_properties().cloned(),
             parent.writing_mode,
             parent.flags,
-            parent.clone_visited_style()
+            visited_style,
         )
     }
 
     /// Returns whether we have a visited style.
     pub fn has_visited_style(&self) -> bool {
         self.visited_style.is_some()
     }
 
--- a/taskcluster/ci/source-test/kind.yml
+++ b/taskcluster/ci/source-test/kind.yml
@@ -1,15 +1,16 @@
 # 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/.
 ---
 loader: taskgraph.loader.transform:loader
 
 transforms:
+   - taskgraph.transforms.try_job:transforms
    - taskgraph.transforms.source_test:transforms
    - taskgraph.transforms.job:transforms
    - taskgraph.transforms.task:transforms
 
 jobs-from:
    - cram.yml
    - doc.yml
    - mocha.yml
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -63,16 +63,17 @@ talos:
     - talos-g5
     - talos-other
     - talos-svgr
     - talos-tp5o
     - talos-perf-reftest
     - talos-perf-reftest-singletons
     - talos-tp6
     - talos-tp6-stylo-threads
+    - talos-speedometer
 
 awsy:
     - awsy
 
 awsy-stylo-disabled:
     - awsy-stylo-disabled
 
 awsy-stylo-sequential:
@@ -108,16 +109,17 @@ qr-talos:
     - talos-g3
     - talos-g4
     - talos-g5
     # - talos-other # fails with layers-free
     # - talos-svgr # fails with layers-free
     - talos-tp5o
     - talos-perf-reftest
     - talos-perf-reftest-singletons
+    - talos-speedometer
 
 qr-tests:
     - cppunit
     - crashtest
     - gtest
     - jittest
     - jsreftest
     - mochitest-a11y
@@ -142,16 +144,17 @@ linux-talos-stylo-disabled:
     - talos-g4-stylo-disabled
     - talos-g5-stylo-disabled
     - talos-other-stylo-disabled
     - talos-svgr-stylo-disabled
     - talos-tp5o-stylo-disabled
     - talos-perf-reftest-stylo-disabled
     - talos-perf-reftest-singletons-stylo-disabled
     - talos-tp6-stylo-disabled
+    - talos-speedometer-stylo-disabled
 
 windows-reftest-gpu:
     - reftest-gpu
 
 windows-tests:
     - cppunit
     - crashtest
     - firefox-ui-functional-local
@@ -187,31 +190,33 @@ windows-talos:
     - talos-other
     - talos-perf-reftest
     - talos-perf-reftest-singletons
     - talos-svgr
     - talos-tp5o
     - talos-xperf
     - talos-tp6
     - talos-tp6-stylo-threads
+    - talos-speedometer
 
 windows-talos-stylo-disabled:
     - talos-chrome-stylo-disabled
     - talos-dromaeojs-stylo-disabled
     - talos-g1-stylo-disabled
     - talos-g2-stylo-disabled
     - talos-g4-stylo-disabled
     - talos-g5-stylo-disabled
     - talos-other-stylo-disabled
     - talos-perf-reftest-stylo-disabled
     - talos-perf-reftest-singletons-stylo-disabled
     - talos-svgr-stylo-disabled
     - talos-tp5o-stylo-disabled
     - talos-xperf-stylo-disabled
     - talos-tp6-stylo-disabled
+    - talos-speedometer-stylo-disabled
 
 macosx64-tests:
     - cppunit
     - crashtest
     - firefox-ui-functional-local
     - firefox-ui-functional-remote
     - gtest
     - jittest
@@ -242,30 +247,32 @@ macosx64-talos:
     - talos-g5
     - talos-other
     - talos-svgr
     - talos-tp5o
     - talos-perf-reftest
     - talos-perf-reftest-singletons
     - talos-tp6
     - talos-tp6-stylo-threads
+    - talos-speedometer
 
 macosx64-talos-stylo-disabled:
     - talos-chrome-stylo-disabled
     - talos-dromaeojs-stylo-disabled
     - talos-g1-stylo-disabled
     - talos-g2-stylo-disabled
     - talos-g4-stylo-disabled
     - talos-g5-stylo-disabled
     - talos-other-stylo-disabled
     - talos-svgr-stylo-disabled
     - talos-tp5o-stylo-disabled
     - talos-perf-reftest-stylo-disabled
     - talos-perf-reftest-singletons-stylo-disabled
     - talos-tp6-stylo-disabled
+    - talos-speedometer-stylo-disabled
 
 linux32-tests:
     - cppunit
     - crashtest
     - firefox-ui-functional-local
     - firefox-ui-functional-remote
     - gtest
     - jittest
--- a/taskcluster/ci/test/tests.yml
+++ b/taskcluster/ci/test/tests.yml
@@ -1631,16 +1631,67 @@ talos-perf-reftest-stylo-disabled:
             by-test-platform:
                 windows.*:
                     - talos/windows_config.py
                 default:
                     - talos/linux_config.py
         extra-options:
             - --suite=perf-reftest-stylo-disabled
 
+talos-speedometer:
+    description: "Talos speedometer"
+    suite: talos
+    try-name: speedometer
+    treeherder-symbol: tc-T(sp)
+    virtualization: hardware
+    run-on-projects:
+        by-test-platform:
+            linux64-qr/.*: ['mozilla-central', 'try']
+            default: ['mozilla-beta', 'mozilla-central', 'mozilla-inbound', 'autoland', 'try']
+    max-run-time: 3600
+    mozharness:
+        script: talos_script.py
+        no-read-buildbot-config: true
+        config:
+            by-test-platform:
+                macosx.*:
+                    - talos/mac_config.py
+                windows.*:
+                    - talos/windows_config.py
+                default:
+                    - talos/linux_config.py
+        extra-options:
+            - --suite=speedometer
+            - --add-option
+            - --webServer,localhost
+
+talos-speedometer-stylo-disabled:
+    description: "Talos Stylo disabled speedometer"
+    suite: talos
+    try-name: speedometer-stylo-disabled
+    treeherder-symbol: tc-Tsd(sp)
+    virtualization: hardware
+    run-on-projects: ['mozilla-beta', 'mozilla-central', 'try']
+    max-run-time: 3600
+    mozharness:
+        script: talos_script.py
+        no-read-buildbot-config: true
+        config:
+            by-test-platform:
+                macosx.*:
+                    - talos/mac_config.py
+                windows.*:
+                    - talos/windows_config.py
+                default:
+                    - talos/linux_config.py
+        extra-options:
+            - --suite=speedometer-stylo-disabled
+            - --add-option
+            - --webServer,localhost
+
 talos-svgr:
     description: "Talos svgr"
     suite: talos
     try-name: svgr
     treeherder-symbol: tc-T(s)
     virtualization: hardware
     run-on-projects:
         by-test-platform:
--- a/taskcluster/docs/try.rst
+++ b/taskcluster/docs/try.rst
@@ -8,17 +8,18 @@ various forms, and can sometimes show it
 Access to "push to try" is typically avilable to a much larger group of
 developers than those who can land changes in integration and release branches.
 Specifically, try pushes are allowed for anyone with `SCM Level`_ 1, while
 integration branches are at SCM level 3.
 
 Scheduling a Task on Try
 ------------------------
 
-There are two methods for scheduling a task on try.
+There are three methods for scheduling a task on try: legacy try option syntax,
+try task config, and an empty try.
 
 Try Option Syntax
 :::::::::::::::::
 
 The first, older method is a command line string called ``try syntax`` which is passed
 into the decision task via the commit message. The resulting commit is then
 pushed to the https://hg.mozilla.org/try repository.  An example try syntax
 might look like:
@@ -127,14 +128,22 @@ It looks like this:
         "enabled": 1,
         ...
       },
       "taskId": "<task id>"
     }
 
 See the `existing templates`_ for examples.
 
+Empty Try
+:::::::::
+
+If there is no try syntax or ``try_task_config.json``, the ``try_mode``
+parameter is None and no tasks are selected to run.  The resulting push will
+only have a decision task, but one with an "add jobs" action that can be used
+to add the desired jobs to the try push.
+
 .. _tryselect: https://dxr.mozilla.org/mozilla-central/source/tools/tryselect
 .. _JSON-e: https://taskcluster.github.io/json-e/
 .. _taskgraph module: https://dxr.mozilla.org/mozilla-central/source/taskcluster/taskgraph/templates
 .. _condition statements: https://taskcluster.github.io/json-e/#%60$if%60%20-%20%60then%60%20-%20%60else%60
 .. _existing templates: https://dxr.mozilla.org/mozilla-central/source/taskcluster/taskgraph/templates
 .. _SCM Level: https://www.mozilla.org/en-US/about/governance/policies/commit/access-policy/
--- a/taskcluster/taskgraph/decision.py
+++ b/taskcluster/taskgraph/decision.py
@@ -194,47 +194,46 @@ def get_decision_parameters(options):
     parameters.setdefault('release_history', dict())
     if 'nightly' in parameters.get('target_tasks_method', ''):
         parameters['release_history'] = populate_release_history('Firefox', project)
 
     # if try_task_config.json is present, load it
     task_config_file = os.path.join(os.getcwd(), 'try_task_config.json')
 
     # load try settings
-    parameters['try_mode'] = None
-    if os.path.isfile(task_config_file):
-        parameters['try_mode'] = 'try_task_config'
-        with open(task_config_file, 'r') as fh:
-            parameters['try_task_config'] = json.load(fh)
-    else:
-        parameters['try_task_config'] = None
-
-    if 'try:' in parameters['message']:
-        parameters['try_mode'] = 'try_option_syntax'
-        args = parse_message(parameters['message'])
-        parameters['try_options'] = args
-    else:
-        parameters['try_options'] = None
+    if project == 'try':
+        parameters['try_mode'] = None
+        if os.path.isfile(task_config_file):
+            parameters['try_mode'] = 'try_task_config'
+            with open(task_config_file, 'r') as fh:
+                parameters['try_task_config'] = json.load(fh)
+        else:
+            parameters['try_task_config'] = None
 
-    parameters['optimize_target_tasks'] = {
-        # The user has explicitly requested a set of jobs, so run them all
-        # regardless of optimization.  Their dependencies can be optimized,
-        # though.
-        'try_task_config': False,
+        if 'try:' in parameters['message']:
+            parameters['try_mode'] = 'try_option_syntax'
+            args = parse_message(parameters['message'])
+            parameters['try_options'] = args
+        else:
+            parameters['try_options'] = None
 
-        # Always perform optimization.  This makes it difficult to use try
-        # pushes to run a task that would otherwise be optimized, but is a
-        # compromise to avoid essentially disabling optimization in try.
-        # to run tasks that would otherwise be optimized, ues try_task_config.
-        'try_option_syntax': True,
+        if parameters['try_mode']:
+            # The user has explicitly requested a set of jobs, so run them all
+            # regardless of optimization.  Their dependencies can be optimized,
+            # though.
+            parameters['optimize_target_tasks'] = False
+        else:
+            # For a try push with no task selection, apply the default optimization
+            # process to all of the tasks.
+            parameters['optimize_target_tasks'] = True
 
-        # since no try jobs have been specified, the standard target task will
-        # be applied, and tasks should be optimized out of that.
-        None: True,
-    }[parameters['try_mode']]
+    else:
+        parameters['try_mode'] = None
+        parameters['try_task_config'] = None
+        parameters['try_options'] = None
 
     return Parameters(**parameters)
 
 
 def write_artifact(filename, data):
     logger.info('writing artifact file `{}`'.format(filename))
     if not os.path.isdir(ARTIFACTS_DIR):
         os.mkdir(ARTIFACTS_DIR)
--- a/taskcluster/taskgraph/optimize.py
+++ b/taskcluster/taskgraph/optimize.py
@@ -289,19 +289,26 @@ class OnlyIfDependenciesRun(Optimization
     # should_replace_task is called, then a task has no un-optimized
     # dependencies and can be removed (indicated by returning True)
 
     def should_replace_task(self, task, params, arg):
         return True
 
 
 class IndexSearch(OptimizationStrategy):
-    def should_remove_task(self, task, params, index_paths):
-        "If this task has no dependencies, don't run it.."
-        return True
+
+    # A task with no dependencies remaining after optimization will be replaced
+    # if artifacts exist for the corresponding index_paths.
+    # Otherwise, we're in one of the following cases:
+    # - the task has un-optimized dependencies
+    # - the artifacts have expired
+    # - some changes altered the index_paths and new artifacts need to be
+    # created.
+    # In every of those cases, we need to run the task to create or refresh
+    # artifacts.
 
     def should_replace_task(self, task, params, index_paths):
         "Look for a task with one of the given index paths"
         for index_path in index_paths:
             try:
                 task_id = find_task_id(
                     index_path,
                     use_proxy=bool(os.environ.get('TASK_ID')))
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -105,21 +105,19 @@ def _try_option_syntax(full_task_graph, 
 @_target_task('try_tasks')
 def target_tasks_try(full_task_graph, parameters):
     try_mode = parameters['try_mode']
     if try_mode == 'try_task_config':
         return _try_task_config(full_task_graph, parameters)
     elif try_mode == 'try_option_syntax':
         return _try_option_syntax(full_task_graph, parameters)
     else:
-        # With no try mode, we would like to schedule everything (following
-        # run_on_projects) and let optimization trim it down.  But optimization
-        # isn't yet up to the task, so instead we use try_option_syntax with
-        # an empty message (which basically just schedules `-j`objs)
-        return _try_option_syntax(full_task_graph, parameters)
+        # With no try mode, we schedule nothing, allowing the user to add tasks
+        # later via treeherder.
+        return []
 
 
 @_target_task('default')
 def target_tasks_default(full_task_graph, parameters):
     """Target the tasks which have indicated they should be run on this project
     via the `run_on_projects` attributes."""
     return [l for l, t in full_task_graph.tasks.iteritems()
             if standard_filter(t, parameters)]
--- a/taskcluster/taskgraph/test/test_decision.py
+++ b/taskcluster/taskgraph/test/test_decision.py
@@ -71,25 +71,27 @@ class TestGetDecisionParameters(unittest
 
     def test_no_email_owner(self):
         self.options['owner'] = 'ffxbld'
         params = decision.get_decision_parameters(self.options)
         self.assertEqual(params['owner'], 'ffxbld@noreply.mozilla.org')
 
     def test_try_options(self):
         self.options['message'] = 'try: -b do -t all'
+        self.options['project'] = 'try'
         params = decision.get_decision_parameters(self.options)
         self.assertEqual(params['try_mode'], 'try_option_syntax')
         self.assertEqual(params['try_options']['build_types'], 'do')
         self.assertEqual(params['try_options']['unittests'], 'all')
         self.assertEqual(params['try_task_config'], None)
 
     def test_try_task_config(self):
         ttc = {'tasks': ['a', 'b'], 'templates': {}}
         ttc_file = os.path.join(os.getcwd(), 'try_task_config.json')
+        self.options['project'] = 'try'
         with MockedOpen({ttc_file: json.dumps(ttc)}):
             params = decision.get_decision_parameters(self.options)
             self.assertEqual(params['try_mode'], 'try_task_config')
             self.assertEqual(params['try_options'], None)
             self.assertEqual(params['try_task_config'], ttc)
 
 
 if __name__ == '__main__':
--- a/taskcluster/taskgraph/test/test_target_tasks.py
+++ b/taskcluster/taskgraph/test/test_target_tasks.py
@@ -79,26 +79,27 @@ class TestTargetTasks(unittest.TestCase)
     def fake_TryOptionSyntax(self):
         orig_TryOptionSyntax = try_option_syntax.TryOptionSyntax
         try:
             try_option_syntax.TryOptionSyntax = FakeTryOptionSyntax
             yield
         finally:
             try_option_syntax.TryOptionSyntax = orig_TryOptionSyntax
 
-    def test_just_try_it(self):
-        "try_mode = None runs try optoin syntax with no options"
+    def test_empty_try(self):
+        "try_mode = None runs nothing"
         tg = self.make_task_graph()
         method = target_tasks.get_method('try_tasks')
-        with self.fake_TryOptionSyntax():
-            params = {
-                'try_mode': None,
-                'message': '',
-            }
-            self.assertEqual(method(tg, params), ['b'])
+        params = {
+            'try_mode': None,
+            'project': 'try',
+            'message': '',
+        }
+        # only runs the task with run_on_projects: try
+        self.assertEqual(method(tg, params), [])
 
     def test_try_option_syntax(self):
         "try_mode = try_option_syntax uses TryOptionSyntax"
         tg = self.make_task_graph()
         method = target_tasks.get_method('try_tasks')
         with self.fake_TryOptionSyntax():
             params = {
                 'try_mode': 'try_option_syntax',
--- a/taskcluster/taskgraph/test/test_try_option_syntax.py
+++ b/taskcluster/taskgraph/test/test_try_option_syntax.py
@@ -64,17 +64,17 @@ graph_with_jobs = TaskGraph(tasks, Graph
 class TestTryOptionSyntax(unittest.TestCase):
 
     def test_unknown_args(self):
         "unknown arguments are ignored"
         parameters = {'try_options': parse_message('try: --doubledash -z extra')}
         tos = TryOptionSyntax(parameters, graph_with_jobs)
         # equilvant to "try:"..
         self.assertEqual(tos.build_types, [])
-        self.assertEqual(tos.jobs, None)
+        self.assertEqual(tos.jobs, [])
 
     def test_apostrophe_in_message(self):
         "apostrophe does not break parsing"
         parameters = {'try_options': parse_message('Increase spammy log\'s log level. try: -b do')}
         tos = TryOptionSyntax(parameters, graph_with_jobs)
         self.assertEqual(sorted(tos.build_types), ['debug', 'opt'])
 
     def test_b_do(self):
--- a/taskcluster/taskgraph/transforms/job/toolchain.py
+++ b/taskcluster/taskgraph/transforms/job/toolchain.py
@@ -107,20 +107,24 @@ def add_optimization(config, run, taskde
 
 
 @run_job_using("docker-worker", "toolchain-script", schema=toolchain_run_schema)
 def docker_worker_toolchain(config, job, taskdesc):
     run = job['run']
     taskdesc['run-on-projects'] = ['trunk', 'try']
 
     worker = taskdesc['worker']
-    worker['artifacts'] = []
     worker['chain-of-trust'] = True
 
-    docker_worker_add_public_artifacts(config, job, taskdesc)
+    # Allow the job to specify where artifacts come from, but add
+    # public/build if it's not there already.
+    artifacts = worker.setdefault('artifacts', [])
+    if not any(artifact.get('name') == 'public/build' for artifact in artifacts):
+        docker_worker_add_public_artifacts(config, job, taskdesc)
+
     docker_worker_add_tc_vcs_cache(config, job, taskdesc)
     docker_worker_add_gecko_vcs_env_vars(config, job, taskdesc)
     support_vcs_checkout(config, job, taskdesc, sparse=True)
 
     env = worker['env']
     env.update({
         'MOZ_BUILD_DATE': config.params['moz_build_date'],
         'MOZ_SCM_LEVEL': config.params['level'],
--- a/taskcluster/taskgraph/transforms/source_test.py
+++ b/taskcluster/taskgraph/transforms/source_test.py
@@ -66,18 +66,16 @@ def validate(config, jobs):
     for job in jobs:
         yield validate_schema(source_test_description_schema, job,
                               "In job {!r}:".format(job['name']))
 
 
 @transforms.add
 def set_job_name(config, jobs):
     for job in jobs:
-        job.setdefault('attributes', {}).setdefault('job_try_name', job['name'])
-
         if 'job-from' in job and job['job-from'] != 'kind.yml':
             from_name = os.path.splitext(job['job-from'])[0]
             job['name'] = '{}-{}'.format(from_name, job['name'])
         yield job
 
 
 @transforms.add
 def expand_platforms(config, jobs):
--- a/taskcluster/taskgraph/transforms/use_toolchains.py
+++ b/taskcluster/taskgraph/transforms/use_toolchains.py
@@ -63,41 +63,51 @@ def use_toolchains(config, jobs):
                     % (job['name'], key, aliases[key])
                 )
             return key
     else:
         def get_alias(key):
             return aliases.get(key, key)
 
     for job in jobs:
+        scopes = job.setdefault('scopes', [])
         env = job.setdefault('worker', {}).setdefault('env', {})
 
         toolchains = [get_alias(t)
                       for t in job.pop('toolchains', [])]
 
         if config.kind == 'toolchain' and job['name'] in toolchains:
             raise Exception("Toolchain job %s can't use itself as toolchain"
                             % job['name'])
 
         filenames = {}
         for t in toolchains:
             if t not in artifacts:
                 raise Exception('Missing toolchain job for %s-%s: %s'
                                 % (config.kind, job['name'], t))
 
-            f = os.path.basename(artifacts[t])
+            dirname, f = os.path.split(artifacts[t])
             if f in filenames:
                 # Build jobs don't support toolchain artifacts with the same
                 # name: they would overwrite one with the other.
                 raise Exception('%s-%s cannot use both %s and %s toolchains: '
                                 'they both have the same artifact name %s'
                                 % (config.kind, job['name'], filenames[f],
                                    t, f))
             filenames[f] = t
 
+            if not artifacts[t].startswith('public/'):
+                # Use taskcluster-proxy and request appropriate scope.
+                # For example, add 'scopes: [queue:get-artifact:path/to/*]'
+                # for 'path/to/artifact.tar.xz'.
+                job['worker']['taskcluster-proxy'] = True
+                scope = 'queue:get-artifact:{}/*'.format(dirname)
+                if scope not in scopes:
+                    scopes.append(scope)
+
             if t.endswith('-sccache'):
                 job['needs-sccache'] = True
 
         if toolchains:
             job.setdefault('dependencies', {}).update(
                 ('toolchain-%s' % t, 'toolchain-%s' % t)
                 for t in toolchains
             )
--- a/taskcluster/taskgraph/try_option_syntax.py
+++ b/taskcluster/taskgraph/try_option_syntax.py
@@ -318,17 +318,19 @@ class TryOptionSyntax(object):
         self.talos_trigger_tests = options['talos_trigger_tests']
         self.env = options['env']
         self.profile = options['profile']
         self.tag = options['tag']
         self.no_retry = options['no_retry']
         self.include_nightly = options['include_nightly']
 
     def parse_jobs(self, jobs_arg):
-        if not jobs_arg or jobs_arg == ['all']:
+        if not jobs_arg or jobs_arg == ['none']:
+            return []  # default is `-j none`
+        if jobs_arg == ['all']:
             return None
         expanded = []
         for job in jobs_arg:
             expanded.extend(j.strip() for j in job.split(','))
         return expanded
 
     def parse_build_types(self, build_types_arg, full_task_graph):
         if build_types_arg is None:
@@ -592,33 +594,27 @@ class TryOptionSyntax(object):
                 platform = attr('test_platform', '').split('/')[0]
                 # Platforms can be forced by syntax like "-u xpcshell[Windows 8]"
                 return platform in test['platforms']
             elif run_by_default:
                 return check_run_on_projects()
             else:
                 return False
 
-        job_try_name = attr('job_try_name')
-        if job_try_name:
+        if attr('job_try_name'):
             # Beware the subtle distinction between [] and None for self.jobs and self.platforms.
             # They will be [] if there was no try syntax, and None if try syntax was detected but
             # they remained unspecified.
-            if self.jobs:
-                return job_try_name in self.jobs
-            elif not self.jobs and 'build' in task.dependencies:
-                # We exclude tasks with build dependencies from the default set of jobs because
-                # they will schedule their builds even if they end up optimized away. This means
-                # to run these tasks on try, they'll need to be explicitly specified by -j until
-                # we find a better solution (see bug 1372510).
-                return False
-            elif not self.jobs and attr('