Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 29 Sep 2016 11:48:33 +0200
changeset 315819 6a996a75330fb7d4376b4ed6d439126d8b5a50a2
parent 315818 d9b67ef4fb0a2f2de2c398034ffe027c07aae8e9 (current diff)
parent 315728 f7d5008ee2ab9200052e45ad6ecc3f3a348f7f86 (diff)
child 315820 826cc48624a31a2755c23bfe83535311df1567bc
push id20634
push usercbook@mozilla.com
push dateFri, 30 Sep 2016 10:10:13 +0000
treeherderfx-team@afe79b010d13 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone52.0a1
Merge mozilla-central to mozilla-inbound
toolkit/components/places/tests/unit/test_420331_wyciwyg.js
--- a/.taskcluster.yml
+++ b/.taskcluster.yml
@@ -77,16 +77,17 @@ tasks:
           GECKO_HEAD_REV: '{{revision}}'
 
         cache:
           level-{{level}}-hg-shared: /home/worker/hg-shared
           level-{{level}}-checkouts: /home/worker/checkouts
 
         features:
           taskclusterProxy: true
+          chainOfTrust: true
 
         # Note: This task is built server side without the context or tooling that
         # exist in tree so we must hard code the version
         image: 'taskcluster/decision:0.1.6'
 
         maxRunTime: 1800
 
         # TODO use mozilla-unified for the base repository once the tc-vcs
--- a/Makefile.in
+++ b/Makefile.in
@@ -167,17 +167,17 @@ install-manifests: $(addprefix install-,
 ifneq (,$(filter FasterMake+RecursiveMake,$(BUILD_BACKENDS)))
 install-manifests: faster
 .PHONY: faster
 faster: install-dist/idl
 	$(MAKE) -C faster FASTER_RECURSIVE_MAKE=1
 endif
 
 .PHONY: tup
-tup: install-manifests buildid.h
+tup: install-manifests buildid.h source-repo.h
 	@$(TUP) $(if $(findstring s,$(filter-out --%,$(MAKEFLAGS))),,--verbose)
 
 # process_install_manifest needs to be invoked with --no-remove when building
 # js as standalone because automated builds are building nspr separately and
 # that would remove the resulting files.
 # Eventually, a standalone js build would just be able to build nspr itself,
 # removing the need for the former.
 ifdef JS_STANDALONE
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -894,19 +894,17 @@ bin/libfreebl_32int64_3.so
 @BINPATH@/b2g-bin@BIN_SUFFIX@
 #endif
 
 #ifdef PACKAGE_MOZTT
 @RESPATH@/fonts/*
 #endif
 
 ; media
-#ifdef MOZ_EME
 @RESPATH@/gmp-clearkey/0.1/@DLL_PREFIX@clearkey@DLL_SUFFIX@
 @RESPATH@/gmp-clearkey/0.1/clearkey.info
-#endif
 
 #ifdef PKG_LOCALE_MANIFEST
 #include @PKG_LOCALE_MANIFEST@
 #endif
 
 @RESPATH@/components/simpleServices.js
 @RESPATH@/components/utils.manifest
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -967,26 +967,34 @@ pref("security.sandbox.content.level", 1
 // process because the only other sandbox (for GMP) has too strict a policy to
 // allow stack tracing.  This does not require a restart to take effect.
 pref("security.sandbox.windows.log.stackTraceDepth", 0);
 #endif
 #endif
 #endif
 
 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
-// This pref is discussed in bug 1083344, the naming is inspired from its Windows
-// counterpart, but on Mac it's an integer which means:
+// This pref is discussed in bug 1083344, the naming is inspired from its
+// Windows counterpart, but on Mac it's an integer which means:
 // 0 -> "no sandbox"
-// 1 -> "an imperfect sandbox designed to allow firefox to run reasonably well"
-// 2 -> "an ideal sandbox which may break many things"
+// 1 -> "preliminary content sandboxing enabled: write access to
+//       home directory is prevented"
+// 2 -> "preliminary content sandboxing enabled with profile protection:
+//       write access to home directory is prevented, read and write access
+//       to ~/Library and profile directories are prevented (excluding
+//       $PROFILE/{extensions,weave})"
 // This setting is read when the content process is started. On Mac the content
 // process is killed when all windows are closed, so a change will take effect
 // when the 1st window is opened.
+#if defined(NIGHTLY_BUILD)
+pref("security.sandbox.content.level", 2);
+#else
 pref("security.sandbox.content.level", 1);
 #endif
+#endif
 
 #if defined(XP_LINUX) && defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
 // This pref is introduced as part of bug 742434, the naming is inspired from
 // its Windows/Mac counterpart, but on Linux it's an integer which means:
 // 0 -> "no sandbox"
 // 1 -> "content sandbox using seccomp-bpf when available"
 // 2 -> "seccomp-bpf + file broker"
 // Content sandboxing on Linux is currently in the stage of
--- a/browser/components/places/tests/chrome/test_bug549192.xul
+++ b/browser/components/places/tests/chrome/test_bug549192.xul
@@ -43,25 +43,32 @@
 
     function runTest() {
       SimpleTest.waitForExplicitFinish();
 
       Task.spawn(function* () {
         yield PlacesTestUtils.clearHistory();
 
         // Add some visits.
+        let timeInMicroseconds = PlacesUtils.toPRTime(Date.now() - 10000);
+
+        function newTimeInMicroseconds() {
+          timeInMicroseconds = timeInMicroseconds + 1000;
+          return timeInMicroseconds;
+        }
+
         let vtime = Date.now() * 1000;
         const ttype = PlacesUtils.history.TRANSITION_TYPED;
         let places =
           [{ uri: Services.io.newURI("http://example.tld/", null, null),
-             visitDate: ++vtime, transition: ttype },
+             visitDate: newTimeInMicroseconds(), transition: ttype },
            { uri: Services.io.newURI("http://example2.tld/", null, null),
-             visitDate: ++vtime, transition: ttype },
+             visitDate: newTimeInMicroseconds(), transition: ttype },
            { uri: Services.io.newURI("http://example3.tld/", null, null),
-             visitDate: ++vtime, transition: ttype }];
+             visitDate: newTimeInMicroseconds(), transition: ttype }];
 
         yield PlacesTestUtils.addVisits(places);
 
         // Make a history query.
         let query = PlacesUtils.history.getNewQuery();
         let opts = PlacesUtils.history.getNewQueryOptions();
         opts.sortingMode = opts.SORT_BY_DATE_DESCENDING;
         let queryURI = PlacesUtils.history.queriesToQueryString([query], 1, opts);
@@ -80,17 +87,17 @@
           let node = tree.selectedNode;
           is(node.uri, places[rc - i - 1].uri.spec,
              "Found expected node at position " + i + ".");
         }
 
         is(rc, 3, "Found expected number of rows.");
 
         // First check live-update of the view when adding visits.
-        places.forEach(place => place.visitDate = ++vtime);
+        places.forEach(place => place.visitDate = newTimeInMicroseconds());
         yield PlacesTestUtils.addVisits(places);
 
         for (let i = 0; i < rc; i++) {
           selection.select(i);
           let node = tree.selectedNode;
           is(node.uri, places[rc - i - 1].uri.spec,
              "Found expected node at position " + i + ".");
         }
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -1,17 +1,15 @@
 <?xml version="1.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/. -->
 
 <?xml-stylesheet href="chrome://global/skin/global.css"?>
 
-<?xml-stylesheet href="chrome://mozapps/content/preferences/preferences.css"?>
-
 <?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
 <?xml-stylesheet href="chrome://global/skin/in-content/common.css"?>
 <?xml-stylesheet
   href="chrome://browser/skin/preferences/in-content/preferences.css"?>
 <?xml-stylesheet
   href="chrome://browser/content/preferences/handlers.css"?>
 <?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
 <?xml-stylesheet href="chrome://browser/skin/preferences/in-content/search.css"?>
--- a/browser/components/preferences/in-content/subdialogs.js
+++ b/browser/components/preferences/in-content/subdialogs.js
@@ -6,21 +6,22 @@
 
 var gSubDialog = {
   _closingCallback: null,
   _closingEvent: null,
   _isClosing: false,
   _frame: null,
   _overlay: null,
   _box: null,
-  _injectedStyleSheets: ["chrome://mozapps/content/preferences/preferences.css",
-                         "chrome://browser/skin/preferences/preferences.css",
-                         "chrome://global/skin/in-content/common.css",
-                         "chrome://browser/skin/preferences/in-content/preferences.css",
-                         "chrome://browser/skin/preferences/in-content/dialog.css"],
+  _injectedStyleSheets: [
+    "chrome://browser/skin/preferences/preferences.css",
+    "chrome://global/skin/in-content/common.css",
+    "chrome://browser/skin/preferences/in-content/preferences.css",
+    "chrome://browser/skin/preferences/in-content/dialog.css",
+  ],
   _resizeObserver: null,
 
   init: function() {
     this._frame = document.getElementById("dialogFrame");
     this._overlay = document.getElementById("dialogOverlay");
     this._box = document.getElementById("dialogBox");
     this._closeButton = document.getElementById("dialogClose");
   },
--- a/browser/components/sessionstore/SessionWorker.js
+++ b/browser/components/sessionstore/SessionWorker.js
@@ -145,34 +145,33 @@ var Agent = {
           tab.entries = tab.entries.slice(lower, upper);
           tab.index -= lower;
         }
       }
     }
 
     let stateString = JSON.stringify(state);
     let data = Encoder.encode(stateString);
-    let startWriteMs, stopWriteMs;
 
     try {
 
       if (this.state == STATE_CLEAN || this.state == STATE_EMPTY) {
         // The backups directory may not exist yet. In all other cases,
         // we have either already read from or already written to this
         // directory, so we are satisfied that it exists.
         File.makeDir(this.Paths.backups);
       }
 
       if (this.state == STATE_CLEAN) {
         // Move $Path.clean out of the way, to avoid any ambiguity as
         // to which file is more recent.
         File.move(this.Paths.clean, this.Paths.cleanBackup);
       }
 
-      startWriteMs = Date.now();
+      let startWriteMs = Date.now();
 
       if (options.isFinalWrite) {
         // We are shutting down. At this stage, we know that
         // $Paths.clean is either absent or corrupted. If it was
         // originally present and valid, it has been moved to
         // $Paths.cleanBackup a long time ago. We can therefore write
         // with the guarantees that we erase no important data.
         File.writeAtomic(this.Paths.clean, data, {
@@ -193,17 +192,18 @@ var Agent = {
         // In other cases, either $Path.recovery is not necessary, or
         // it doesn't exist or it has been corrupted. Regardless,
         // don't backup $Path.recovery.
         File.writeAtomic(this.Paths.recovery, data, {
           tmpPath: this.Paths.recovery + ".tmp"
         });
       }
 
-      stopWriteMs = Date.now();
+      telemetry.FX_SESSION_RESTORE_WRITE_FILE_MS = Date.now() - startWriteMs;
+      telemetry.FX_SESSION_RESTORE_FILE_SIZE_BYTES = data.byteLength;
 
     } catch (ex) {
       // Don't throw immediately
       exn = exn || ex;
     }
 
     // If necessary, perform an upgrade backup
     let upgradeBackupComplete = false;
@@ -271,20 +271,17 @@ var Agent = {
     if (exn) {
       throw exn;
     }
 
     return {
       result: {
         upgradeBackup: upgradeBackupComplete
       },
-      telemetry: {
-        FX_SESSION_RESTORE_WRITE_FILE_MS: stopWriteMs - startWriteMs,
-        FX_SESSION_RESTORE_FILE_SIZE_BYTES: data.byteLength,
-      }
+      telemetry: telemetry,
     };
   },
 
   /**
    * Wipes all files holding session data from disk.
    */
   wipe: function () {
 
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -825,20 +825,18 @@ bin/libfreebl_32int64_3.so
 #endif
 
 #if defined(MOZ_ASAN) && defined(CLANG_CL)
 @BINPATH@/clang_rt.asan_dynamic-i386.dll
 #endif
 
 
 ; media
-#ifdef MOZ_EME
 @RESPATH@/gmp-clearkey/0.1/@DLL_PREFIX@clearkey@DLL_SUFFIX@
 @RESPATH@/gmp-clearkey/0.1/clearkey.info
-#endif
 
 ; gfx
 #ifdef XP_WIN
 @RESPATH@/components/GfxSanityTest.manifest
 @RESPATH@/components/SanityTest.js
 #endif
 
 #ifdef MOZ_MULET
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -341,248 +341,16 @@ menuitem.bookmark-item {
   opacity: 0.5;
 }
 
 .bookmark-item[cutting] > .toolbarbutton-text,
 .bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
   opacity: 0.7;
 }
 
-/* Stock icons for the menu bar items */
-menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
-  -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
-}
-
-#placesContext_open\:newwindow,
-#menu_newNavigator,
-#context-openlink,
-#context-openframe {
-  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
-  -moz-image-region: rect(0px 80px 16px 64px);
-}
-
-#placesContext_open\:newtab,
-#placesContext_openContainer\:tabs,
-#menu_newNavigatorTab,
-#context-openlinkintab,
-#context-openlinkincontainertab,
-#context-openframeintab {
-  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
-  -moz-image-region: rect(0px 64px 16px 48px);
-}
-
-#menu_openFile {
-  list-style-image: url("moz-icon://stock/gtk-open?size=menu");
-}
-
-#menu_close {
-  list-style-image: url("moz-icon://stock/gtk-close?size=menu");
-}
-
-#context-media-play {
-  list-style-image: url("moz-icon://stock/gtk-media-play?size=menu");
-}
-
-#context-media-pause {
-  list-style-image: url("moz-icon://stock/gtk-media-pause?size=menu");
-}
-
-#menu_savePage,
-#context-savelink,
-#context-saveimage,
-#context-savevideo,
-#context-saveaudio,
-#context-savepage,
-#context-saveframe {
-  list-style-image: url("moz-icon://stock/gtk-save-as?size=menu");
-}
-
-#menu_printPreview {
-  list-style-image: url("moz-icon://stock/gtk-print-preview?size=menu");
-}
-
-#menu_print,
-#context-printframe {
-  list-style-image: url("moz-icon://stock/gtk-print?size=menu");
-}
-
-#menu_FileQuitItem {
-  list-style-image: url("moz-icon://stock/gtk-quit?size=menu");
-}
-
-#menu_undo,
-#context-undo {
-  list-style-image: url("moz-icon://stock/gtk-undo?size=menu");
-}
-
-#menu_undo[disabled],
-#context-undo[disabled] {
-  list-style-image: url("moz-icon://stock/gtk-undo?size=menu&state=disabled");
-}
-
-#menu_redo {
-  list-style-image: url("moz-icon://stock/gtk-redo?size=menu");
-}
-
-#menu_redo[disabled] {
-  list-style-image: url("moz-icon://stock/gtk-redo?size=menu&state=disabled");
-}
-
-#menu_cut,
-#placesContext_cut,
-#context-cut {
-  list-style-image: url("moz-icon://stock/gtk-cut?size=menu");
-}
-
-#menu_cut[disabled],
-#placesContext_cut[disabled],
-#context-cut[disabled] {
-  list-style-image: url("moz-icon://stock/gtk-cut?size=menu&state=disabled");
-}
-
-#menu_copy,
-#placesContext_copy,
-#context-copy,
-#context-copyimage,
-#context-copyvideourl,
-#context-copyaudiourl,
-#context-copylink,
-#context-copyemail {
-  list-style-image: url("moz-icon://stock/gtk-copy?size=menu");
-}
-
-#menu_copy[disabled],
-#placesContext_copy[disabled],
-#context-copy[disabled] {
-  list-style-image: url("moz-icon://stock/gtk-copy?size=menu&state=disabled");
-}
-
-#menu_paste,
-#placesContext_paste,
-#context-paste {
-  list-style-image: url("moz-icon://stock/gtk-paste?size=menu");
-}
-
-#menu_paste[disabled],
-#placesContext_paste[disabled],
-#context-paste[disabled] {
-  list-style-image: url("moz-icon://stock/gtk-paste?size=menu&state=disabled");
-}
-
-#menu_delete,
-#placesContext_delete,
-#placesContext_delete_history,
-#context-delete {
-  list-style-image: url("moz-icon://stock/gtk-delete?size=menu");
-}
-
-#menu_delete[disabled],
-#placesContext_delete[disabled],
-#placesContext_delete_history[disabled],
-#context-delete[disabled] {
-  list-style-image: url("moz-icon://stock/gtk-delete?size=menu&state=disabled");
-}
-
-#menu_selectAll,
-#context-selectall {
-  list-style-image: url("moz-icon://stock/gtk-select-all?size=menu");
-}
-
-#menu_find {
-  list-style-image: url("moz-icon://stock/gtk-find?size=menu");
-}
-
-#menu_find[disabled] {
-  list-style-image: url("moz-icon://stock/gtk-find?size=menu&state=disabled");
-}
-
-#menu_preferences {
-  list-style-image: url("moz-icon://stock/gtk-preferences?size=menu");
-}
-
-#placesContext_reload,
-#context-reloadframe {
-  list-style-image: url("moz-icon://stock/gtk-refresh?size=menu");
-}
-
-#menu_zoomEnlarge {
-  list-style-image: url("moz-icon://stock/gtk-zoom-in?size=menu");
-}
-
-#menu_zoomReduce {
-  list-style-image: url("moz-icon://stock/gtk-zoom-out?size=menu");
-}
-
-#menu_zoomReset {
-  list-style-image: url("moz-icon://stock/gtk-zoom-100?size=menu");
-}
-
-#menu_showAllHistory {
-  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
-  -moz-image-region: rect(0px 32px 16px 16px);
-}
-
-#bookmarksShowAll {
-  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
-  -moz-image-region: rect(0px 48px 16px 32px);
-}
-
-#subscribeToPageMenuitem:not([disabled]),
-#subscribeToPageMenupopup {
-  list-style-image: url("chrome://browser/skin/page-livemarks.png");
-}
-
-#bookmarksToolbarFolderMenu,
-#BMB_bookmarksToolbar,
-#panelMenu_bookmarksToolbar {
-  list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png");
-}
-
-#menu_unsortedBookmarks,
-#BMB_unsortedBookmarks,
-#panelMenu_unsortedBookmarks {
-  list-style-image: url("chrome://browser/skin/places/unsortedBookmarks.png");
-}
-
-#menu_openDownloads {
-  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
-  -moz-image-region: rect(0px 16px 16px 0px);
-}
-
-#menu_openAddons {
-  list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
-}
-
-#menu_pageInfo,
-#context-viewinfo,
-#context-viewframeinfo {
-  list-style-image: url("moz-icon://stock/gtk-info?size=menu");
-}
-
-#placesContext_open\:newprivatewindow,
-#privateBrowsingItem {
-  list-style-image: url("chrome://browser/skin/Privacy-16.png");
-}
-
-#placesContext_show\:info {
-  list-style-image: url("moz-icon://stock/gtk-properties?size=menu");
-}
-
-#sanitizeItem {
-  list-style-image: url("moz-icon://stock/gtk-clear?size=menu");
-}
-
-#menu_openHelp {
-  list-style-image: url("moz-icon://stock/gtk-help?size=menu");
-}
-
-#aboutName {
-  list-style-image: url("moz-icon://stock/gtk-about?size=menu");
-}
-
 /* Primary toolbar buttons */
 
 :-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1 > .toolbarbutton-icon,
 :-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1 > :-moz-any(.toolbarbutton-menubutton-button, .toolbarbutton-badge-stack) > .toolbarbutton-icon {
   max-width: 16px;
 }
 
 :-moz-any(toolbar, .widget-overflow-list) .toolbarbutton-1:-moz-any(@primaryToolbarButtons@, .toolbarbutton-legacy-addon) > .toolbarbutton-icon,
@@ -1861,26 +1629,21 @@ notification.pluginVulnerable > .notific
 #UITourTooltipButtons {
   margin-left: -10px;
   margin-bottom: -10px;
 }
 
 %include ../shared/contextmenu.inc.css
 
 #context-navigation > .menuitem-iconic > .menu-iconic-left {
-  visibility: visible;
   /* override toolkit/themes/linux/global/menu.css */
   padding-inline-end: 0 !important;
   margin-inline-end: 0 !important;
 }
 
 .browser-extension-panel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
   overflow: hidden;
 }
 
 .webextension-popup-browser {
   border-radius: inherit;
 }
-
-.menuitem-iconic[usercontextid] > .menu-iconic-left > .menu-iconic-icon {
-  visibility: visible;
-}
--- a/browser/themes/linux/pageInfo.css
+++ b/browser/themes/linux/pageInfo.css
@@ -260,21 +260,8 @@ treechildren::-moz-tree-cell-text(broken
 #securityPanel .fieldValue {
   font-weight: bold;
   margin: 2px 10px 3px;
 }
 
 #securityPanel row {
   -moz-box-align: center;
 }
-
-/* Icons for context menus */
-menuitem:not([type]) {
-  -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
-}
-
-#menu_selectall {
-  list-style-image: url("moz-icon://stock/gtk-select-all?size=menu");
-}
-
-#menu_copy {
-  list-style-image: url("moz-icon://stock/gtk-copy?size=menu");
-}
--- a/browser/themes/linux/places/organizer.css
+++ b/browser/themes/linux/places/organizer.css
@@ -85,28 +85,15 @@
 #detailsDeck {
   padding: 5px;
 }
 
 #infoBoxExpanderLabel {
   padding-inline-start: 2px;
 }
 
-/**** menuitem stock icons ****/
-#orgClose {
-  list-style-image: url("moz-icon://stock/gtk-close?size=menu");
-}
-
-#fileImport {
-  list-style-image: url("moz-icon://stock/gtk-revert-to-saved?size=menu");
-}
-
-#fileExport {
-  list-style-image: url("moz-icon://stock/gtk-save-as?size=menu");
-}
-
 /**
  * Downloads pane
  */
 
 #clearDownloadsButton > .toolbarbutton-icon {
   display: none;
 }
--- a/browser/themes/linux/places/places.css
+++ b/browser/themes/linux/places/places.css
@@ -115,107 +115,8 @@ treechildren::-moz-tree-image(title, que
 
 treechildren::-moz-tree-image(cutting) {
   opacity: 0.5;
 }
 
 treechildren::-moz-tree-cell-text(cutting) {
   opacity: 0.7;
 }
-
-/**** menuitem stock icons ****/
-menuitem:not([type]) {
-  -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
-}
-
-menuitem[command="cmd_cut"],
-menuitem[cmd="cmd_cut"] {
-  list-style-image: url("moz-icon://stock/gtk-cut?size=menu");
-}
-
-menuitem[command="cmd_cut"][disabled],
-menuitem[cmd="cmd_cut"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-cut?size=menu&state=disabled");
-}
-
-menuitem[command="cmd_copy"],
-menuitem[cmd="cmd_copy"] {
-  list-style-image: url("moz-icon://stock/gtk-copy?size=menu");
-}
-
-menuitem[command="cmd_copy"][disabled],
-menuitem[cmd="cmd_copy"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-copy?size=menu&state=disabled");
-}
-
-menuitem[command="cmd_paste"],
-menuitem[cmd="cmd_paste"] {
-  list-style-image: url("moz-icon://stock/gtk-paste?size=menu");
-}
-
-menuitem[command="cmd_paste"][disabled],
-menuitem[cmd="cmd_paste"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-paste?size=menu&state=disabled");
-}
-
-menuitem[command="cmd_delete"],
-menuitem[cmd="cmd_delete"] {
-  list-style-image: url("moz-icon://stock/gtk-delete?size=menu");
-}
-
-menuitem[command="cmd_delete"][disabled],
-menuitem[cmd="cmd_delete"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-delete?size=menu&state=disabled");
-}
-
-menuitem[command="cmd_undo"],
-menuitem[cmd="cmd_undo"] {
-  list-style-image: url("moz-icon://stock/gtk-undo?size=menu");
-}
-
-menuitem[command="cmd_undo"][disabled],
-menuitem[cmd="cmd_undo"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-undo?size=menu&state=disabled");
-}
-
-menuitem[command="cmd_redo"] {
-  list-style-image: url("moz-icon://stock/gtk-redo?size=menu");
-}
-
-menuitem[command="cmd_redo"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-redo?size=menu&state=disabled");
-}
-
-menuitem[command="cmd_selectAll"],
-menuitem[cmd="cmd_selectAll"] {
-  list-style-image: url("moz-icon://stock/gtk-select-all?size=menu");
-}
-
-menuitem[command="cmd_selectAll"][disabled],
-menuitem[cmd="cmd_selectAll"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-select-all?size=menu&state=disabled");
-}
-
-#placesContext_open\:newwindow,
-menuitem[command="placesCmd_open:window"] {
-  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
-  -moz-image-region: rect(0px 80px 16px 64px);
-}
-
-#placesContext_open\:newprivatewindow,
-menuitem[command="placesCmd_open:privatewindow"] {
-  list-style-image: url("chrome://browser/skin/Privacy-16.png");
-}
-
-#placesContext_open\:newtab,
-menuitem[command="placesCmd_open:tab"] {
-  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
-  -moz-image-region: rect(0px 64px 16px 48px);
-}
-
-#placesContext_show\:info,
-menuitem[command="placesCmd_show:info"] {
-  list-style-image: url("moz-icon://stock/gtk-properties?size=menu");
-}
-
-#placesContext_reload {
-  list-style-image: url("moz-icon://stock/gtk-refresh?size=menu");
-}
--- a/build/moz.configure/ffi.configure
+++ b/build/moz.configure/ffi.configure
@@ -2,17 +2,17 @@
 # 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/.
 
 @depends(target)
 def force_system_ffi(target):
     # Pre-emptively move to system ffi for non-tier one platforms.
-    if target.cpu not in ('x86', 'x86_64', 'arm'):
+    if target.cpu not in ('x86', 'x86_64', 'arm', 'aarch64'):
         return True
 
 imply_option('--with-system-ffi', force_system_ffi, "target")
 
 js_option('--with-system-ffi',
           help='Use system libffi (located with pkgconfig)')
 
 use_system_ffi = depends_if('--with-system-ffi')(lambda _: True)
@@ -23,32 +23,35 @@ system_ffi = pkg_check_modules('MOZ_FFI'
 building_ffi = depends(system_ffi)(lambda v: not bool(v))
 
 set_config('MOZ_SYSTEM_FFI', system_ffi)
 add_old_configure_assignment('MOZ_SYSTEM_FFI', system_ffi)
 
 # Target selection, based on ffi/configure.ac.
 @depends_when(target, when=building_ffi)
 def ffi_target(target):
-    if target.cpu not in ('x86', 'x86_64', 'arm'):
+    if target.cpu not in ('x86', 'x86_64', 'arm', 'aarch64'):
         die('Building libffi from the tree is not supported on this platform. '
             'Use --with-system-ffi instead.')
 
     if target.os == 'WINNT':
         target_dir = 'x86'
         if target.cpu == 'x86_64':
             target_name = 'X86_WIN64'
         else:
             target_name = 'X86_WIN32'
     elif target.os == 'OSX':
         target_dir = 'x86'
         target_name = 'X86_DARWIN'
     elif target.cpu == 'arm':
         target_dir = 'arm'
         target_name = 'ARM'
+    elif target.cpu == 'aarch64':
+        target_dir = 'aarch64'
+        target_name = 'AARCH64'
     else:
         target_dir = 'x86'
         target_name = target.cpu.upper()
 
     return namespace(
         name=target_name,
         dir=target_dir
     )
--- a/config/external/ffi/moz.build
+++ b/config/external/ffi/moz.build
@@ -1,18 +1,19 @@
 # -*- 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/.
 
+FINAL_LIBRARY = 'js'
+
 if CONFIG['MOZ_SYSTEM_FFI']:
     OS_LIBS += CONFIG['MOZ_FFI_LIBS']
 else:
-    FINAL_LIBRARY = 'js'
     ALLOW_COMPILER_WARNINGS = True
     NO_VISIBILITY_FLAGS = True
 
     CONFIGURE_DEFINE_FILES += [
         '../../../js/src/ctypes/libffi/fficonfig.h',
     ]
     GENERATED_FILES += [
         '../../../js/src/ctypes/libffi/include/ffi.h',
@@ -72,16 +73,18 @@ else:
     ]
 
     # Per-platform sources and flags.
     ffi_srcs = ()
     if CONFIG['FFI_TARGET'] == 'ARM':
         ffi_srcs = ('sysv.S', 'ffi.c')
         if CONFIG['CLANG_CXX']:
             ASFLAGS += ['-no-integrated-as']
+    elif CONFIG['FFI_TARGET'] == 'AARCH64':
+        ffi_srcs = ('sysv.S', 'ffi.c')
     elif CONFIG['FFI_TARGET'] == 'X86':
         ffi_srcs = ('ffi.c', 'sysv.S', 'win32.S')
     elif CONFIG['FFI_TARGET'] == 'X86_64':
         ffi_srcs = ('ffi64.c', 'unix64.S', 'ffi.c', 'sysv.S')
     elif CONFIG['FFI_TARGET'] == 'X86_WIN32':
         ffi_srcs = ('ffi.c', 'win32.S')
     elif CONFIG['FFI_TARGET'] == 'X86_WIN64':
         ffi_srcs = ('ffi.c', 'win64.S')
--- a/devtools/client/commandline/test/browser_cmd_commands.js
+++ b/devtools/client/commandline/test/browser_cmd_commands.js
@@ -1,16 +1,22 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test various GCLI commands
 
 const TEST_URI = "data:text/html;charset=utf-8,gcli-commands";
 const HUDService = require("devtools/client/webconsole/hudservice");
 
+// Use the old webconsole since pprint isn't working on new one (Bug 1304794)
+Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
+});
+
 function test() {
   return Task.spawn(spawnTest).then(finish, helpers.handleError);
 }
 
 function* spawnTest() {
   let options = yield helpers.openTab(TEST_URI);
   yield helpers.openToolbar(options);
 
--- a/devtools/client/debugger/test/mochitest/browser_dbg_instruments-pane-collapse.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_instruments-pane-collapse.js
@@ -8,32 +8,35 @@
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_recursion-stack.html";
 
 var gTab, gPanel, gDebugger;
 var gPrefs, gOptions;
 
 function test() {
-  let options = {
-    source: TAB_URL,
-    line: 1
-  };
-  initDebugger(TAB_URL, options).then(([aTab,, aPanel]) => {
+  Task.spawn(function* () {
+    let options = {
+      source: TAB_URL,
+      line: 1
+    };
+
+    let [aTab,, aPanel] = yield initDebugger(TAB_URL, options);
+
     gTab = aTab;
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
     gPrefs = gDebugger.Prefs;
     gOptions = gDebugger.DebuggerView.Options;
 
     testPanesState();
 
     gDebugger.DebuggerView.toggleInstrumentsPane({ visible: true, animated: false });
 
-    testInstrumentsPaneCollapse();
+    yield testInstrumentsPaneCollapse();
     testPanesStartupPref();
 
     closeDebuggerAndFinish(gPanel);
   });
 }
 
 function testPanesState() {
   let instrumentsPane =
@@ -45,17 +48,17 @@ function testPanesState() {
      instrumentsPaneToggleButton.classList.contains("pane-collapsed"),
     "The debugger view instruments pane should initially be hidden.");
   is(gPrefs.panesVisibleOnStartup, false,
     "The debugger view instruments pane should initially be preffed as hidden.");
   isnot(gOptions._showPanesOnStartupItem.getAttribute("checked"), "true",
     "The options menu item should not be checked.");
 }
 
-function testInstrumentsPaneCollapse() {
+function* testInstrumentsPaneCollapse () {
   let instrumentsPane =
     gDebugger.document.getElementById("instruments-pane");
   let instrumentsPaneToggleButton =
     gDebugger.document.getElementById("instruments-pane-toggle");
 
   let width = parseInt(instrumentsPane.getAttribute("width"));
   is(width, gPrefs.instrumentsWidth,
     "The instruments pane has an incorrect width.");
@@ -64,31 +67,37 @@ function testInstrumentsPaneCollapse() {
   is(instrumentsPane.style.marginRight, "0px",
     "The instruments pane has an incorrect right margin.");
   ok(!instrumentsPane.hasAttribute("animated"),
     "The instruments pane has an incorrect animated attribute.");
   ok(!instrumentsPane.classList.contains("pane-collapsed") &&
      !instrumentsPaneToggleButton.classList.contains("pane-collapsed"),
     "The instruments pane should at this point be visible.");
 
+  // Trigger reflow to make sure the UI is in required state.
+  gDebugger.document.documentElement.getBoundingClientRect();
+
   gDebugger.DebuggerView.toggleInstrumentsPane({ visible: false, animated: true });
 
+  yield once(instrumentsPane, "transitionend");
+
   is(gPrefs.panesVisibleOnStartup, false,
     "The debugger view panes should still initially be preffed as hidden.");
   isnot(gOptions._showPanesOnStartupItem.getAttribute("checked"), "true",
     "The options menu item should still not be checked.");
 
   let margin = -(width + 1) + "px";
   is(width, gPrefs.instrumentsWidth,
     "The instruments pane has an incorrect width after collapsing.");
   is(instrumentsPane.style.marginLeft, margin,
     "The instruments pane has an incorrect left margin after collapsing.");
   is(instrumentsPane.style.marginRight, margin,
     "The instruments pane has an incorrect right margin after collapsing.");
-  ok(instrumentsPane.hasAttribute("animated"),
+
+  ok(!instrumentsPane.hasAttribute("animated"),
     "The instruments pane has an incorrect attribute after an animated collapsing.");
   ok(instrumentsPane.classList.contains("pane-collapsed") &&
      instrumentsPaneToggleButton.classList.contains("pane-collapsed"),
     "The instruments pane should not be visible after collapsing.");
 
   gDebugger.DebuggerView.toggleInstrumentsPane({ visible: true, animated: false });
 
   is(gPrefs.panesVisibleOnStartup, false,
--- a/devtools/client/framework/test/browser_toolbox_select_event.js
+++ b/devtools/client/framework/test/browser_toolbox_select_event.js
@@ -2,16 +2,18 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const PAGE_URL = "data:text/html;charset=utf-8,test select events";
 
+requestLongerTimeout(2);
+
 add_task(function* () {
   let tab = yield addTab(PAGE_URL);
 
   let toolbox = yield openToolboxForTab(tab, "webconsole", "bottom");
   yield testSelectEvent("inspector");
   yield testSelectEvent("webconsole");
   yield testSelectEvent("styleeditor");
   yield testSelectEvent("inspector");
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -330,16 +330,17 @@ Toolbox.prototype = {
   },
 
   /**
    * Get the toggled state of the split console
    */
   get splitConsole() {
     return this._splitConsole;
   },
+
   /**
    * Get the focused state of the split console
    */
   isSplitConsoleFocused: function () {
     if (!this._splitConsole) {
       return false;
     }
     let focusedWin = Services.focus.focusedWindow;
@@ -1262,16 +1263,26 @@ Toolbox.prototype = {
       // Prevent flicker while loading by waiting to make visible until now.
       iframe.style.visibility = "visible";
 
       // The build method should return a panel instance, so events can
       // be fired with the panel as an argument. However, in order to keep
       // backward compatibility with existing extensions do a check
       // for a promise return value.
       let built = definition.build(iframe.contentWindow, this);
+
+      // Set the dir attribute on the documents of panels using HTML.
+      let docEl = iframe.contentWindow && iframe.contentWindow.document.documentElement;
+      if (docEl && docEl.namespaceURI === HTML_NS) {
+        let top = this.win.top;
+        let topDocEl = top.document.documentElement;
+        let isRtl = top.getComputedStyle(topDocEl).direction === "rtl";
+        docEl.setAttribute("dir", isRtl ? "rtl" : "ltr");
+      }
+
       if (!(typeof built.then == "function")) {
         let panel = built;
         iframe.panel = panel;
 
         // The panel instance is expected to fire (and listen to) various
         // framework events, so make sure it's properly decorated with
         // appropriate API (on, off, once, emit).
         // In this case we decorate panel instances directly returned by
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -133,17 +133,17 @@ function CssComputedView(inspector, docu
   this.inspector = inspector;
   this.styleDocument = document;
   this.styleWindow = this.styleDocument.defaultView;
   this.pageStyle = pageStyle;
 
   this.propertyViews = [];
 
   let cssProperties = getCssProperties(inspector.toolbox);
-  this._outputParser = new OutputParser(document, cssProperties.supportsType);
+  this._outputParser = new OutputParser(document, cssProperties);
 
   // Create bound methods.
   this.focusWindow = this.focusWindow.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
   this._onClick = this._onClick.bind(this);
   this._onCopy = this._onCopy.bind(this);
   this._onFilterStyles = this._onFilterStyles.bind(this);
   this._onClearSearch = this._onClearSearch.bind(this);
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -425,18 +425,16 @@ MarkupView.prototype = {
   _showBoxModel: function (nodeFront) {
     return this._inspector.toolbox.highlighterUtils
       .highlightNodeFront(nodeFront);
   },
 
   /**
    * Hide the box model highlighter on a given node front
    *
-   * @param  {NodeFront} nodeFront
-   *         The node to hide the highlighter for
    * @param  {Boolean} forceHide
    *         See toolbox-highlighter-utils/unhighlight
    * @return {Promise} Resolves when the highlighter for this nodeFront is
    *         hidden, taking into account that there could already be highlighter
    *         requests queued up
    */
   _hideBoxModel: function (forceHide) {
     return this._inspector.toolbox.highlighterUtils.unhighlight(forceHide);
--- a/devtools/client/inspector/rules/models/text-property.js
+++ b/devtools/client/inspector/rules/models/text-property.js
@@ -47,16 +47,17 @@ XPCOMUtils.defineLazyGetter(this, "domUt
 function TextProperty(rule, name, value, priority, enabled = true,
                       invisible = false) {
   this.rule = rule;
   this.name = name;
   this.value = value;
   this.priority = priority;
   this.enabled = !!enabled;
   this.invisible = invisible;
+  this.panelDoc = this.rule.elementStyle.ruleView.inspector.panelDoc;
 
   const toolbox = this.rule.elementStyle.ruleView.inspector.toolbox;
   this.cssProperties = getCssProperties(toolbox);
 
   this.updateComputed();
 }
 
 TextProperty.prototype = {
@@ -199,17 +200,17 @@ TextProperty.prototype = {
    * @return {Boolean} true if the property value is valid, false otherwise.
    */
   isValid: function () {
     // Starting with FF49, StyleRuleActors provide a list of parsed
     // declarations, with data about their validity, but if we don't have this,
     // compute validity locally (which might not be correct, but better than
     // nothing).
     if (!this.rule.domRule.declarations) {
-      return domUtils.cssPropertyIsValid(this.name, this.value);
+      return this.cssProperties.isValidOnClient(this.name, this.value, this.panelDoc);
     }
 
     let selfIndex = this.rule.textProps.indexOf(this);
 
     // When adding a new property in the rule-view, the TextProperty object is
     // created right away before the rule gets updated on the server, so we're
     // not going to find the corresponding declaration object yet. Default to
     // true.
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -100,17 +100,17 @@ function CssRuleView(inspector, document
   this.store = store || {};
   this.pageStyle = pageStyle;
 
   // Allow tests to override throttling behavior, as this can cause intermittents.
   this.throttle = throttle;
 
   this.cssProperties = getCssProperties(inspector.toolbox);
 
-  this._outputParser = new OutputParser(document, this.cssProperties.supportsType);
+  this._outputParser = new OutputParser(document, this.cssProperties);
 
   this._onAddRule = this._onAddRule.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
   this._onCopy = this._onCopy.bind(this);
   this._onFilterStyles = this._onFilterStyles.bind(this);
   this._onClearSearch = this._onClearSearch.bind(this);
   this._onFilterTextboxContextMenu =
     this._onFilterTextboxContextMenu.bind(this);
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -194,16 +194,17 @@ skip-if = (os == "win" && debug) # bug 9
 [browser_rules_search-filter_context-menu.js]
 subsuite = clipboard
 [browser_rules_search-filter_escape-keypress.js]
 [browser_rules_select-and-copy-styles.js]
 subsuite = clipboard
 [browser_rules_selector-highlighter_01.js]
 [browser_rules_selector-highlighter_02.js]
 [browser_rules_selector-highlighter_03.js]
+[browser_rules_selector-highlighter_04.js]
 [browser_rules_selector_highlight.js]
 [browser_rules_strict-search-filter-computed-list_01.js]
 [browser_rules_strict-search-filter_01.js]
 [browser_rules_strict-search-filter_02.js]
 [browser_rules_strict-search-filter_03.js]
 [browser_rules_style-editor-link.js]
 [browser_rules_urls-clickable.js]
 [browser_rules_user-agent-styles.js]
--- a/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_02.js
@@ -71,14 +71,8 @@ add_task(function* () {
   yield selectNode("body", inspector);
   icon = getRuleViewSelectorHighlighterIcon(view, "body");
   yield clickSelectorIcon(icon, view);
   is(HighlighterFront.nodeFront.tagName, "BODY",
     "The right NodeFront is passed to the highlighter (2)");
   is(HighlighterFront.options.selector, "body",
     "The right selector option is passed to the highlighter (2)");
 });
-
-function* clickSelectorIcon(icon, view) {
-  let onToggled = view.once("ruleview-selectorhighlighter-toggled");
-  EventUtils.synthesizeMouseAtCenter(icon, {}, view.styleWindow);
-  yield onToggled;
-}
--- a/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_03.js
+++ b/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_03.js
@@ -71,14 +71,8 @@ add_task(function* () {
 
   info("Switching back to .node-1 and clicking on the div selector");
   yield selectNode(".node-1", inspector);
   icon = getRuleViewSelectorHighlighterIcon(view, "div");
   yield clickSelectorIcon(icon, view);
   ok(!HighlighterFront.isShown,
     "The highlighter is hidden now that the same selector was clicked");
 });
-
-function* clickSelectorIcon(icon, view) {
-  let onToggled = view.once("ruleview-selectorhighlighter-toggled");
-  EventUtils.synthesizeMouseAtCenter(icon, {}, view.styleWindow);
-  yield onToggled;
-}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_04.js
@@ -0,0 +1,53 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the selector highlighter is shown when clicking on a selector icon
+// for the 'element {}' rule
+
+// Note that in this test, we mock the highlighter front, merely testing the
+// behavior of the style-inspector UI for now
+
+const TEST_URI = `
+<p>Testing the selector highlighter for the 'element {}' rule</p>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+
+  // Mock the highlighter front to get the reference of the NodeFront
+  let HighlighterFront = {
+    isShown: false,
+    nodeFront: null,
+    options: null,
+    show: function (nodeFront, options) {
+      this.nodeFront = nodeFront;
+      this.options = options;
+      this.isShown = true;
+    },
+    hide: function () {
+      this.nodeFront = null;
+      this.options = null;
+      this.isShown = false;
+    }
+  };
+  // Inject the mock highlighter in the rule-view
+  view.selectorHighlighter = HighlighterFront;
+
+  info("Checking that the right NodeFront reference and options are passed");
+  yield selectNode("p", inspector);
+  let icon = getRuleViewSelectorHighlighterIcon(view, "element");
+
+  yield clickSelectorIcon(icon, view);
+  is(HighlighterFront.nodeFront.tagName, "P",
+     "The right NodeFront is passed to the highlighter (1)");
+  is(HighlighterFront.options.selector, "body > p:nth-child(1)",
+     "The right selector option is passed to the highlighter (1)");
+  ok(HighlighterFront.isShown, "The toggle event says the highlighter is visible");
+
+  yield clickSelectorIcon(icon, view);
+  ok(!HighlighterFront.isShown, "The toggle event says the highlighter is not visible");
+});
--- a/devtools/client/inspector/rules/test/head.js
+++ b/devtools/client/inspector/rules/test/head.js
@@ -813,16 +813,27 @@ function waitForStyleModification(inspec
         }
       }
     }
     inspector.on("markupmutation", checkForStyleModification);
   });
 }
 
 /**
+ * Click on the selector icon
+ * @param {DOMNode} icon
+ * @param {CSSRuleView} view
+ */
+function* clickSelectorIcon(icon, view) {
+  let onToggled = view.once("ruleview-selectorhighlighter-toggled");
+  EventUtils.synthesizeMouseAtCenter(icon, {}, view.styleWindow);
+  yield onToggled;
+}
+
+/**
  * Make sure window is properly focused before sending a key event.
  * @param {Window} win
  * @param {Event} key
  */
 function focusAndSendKey(win, key) {
   win.document.documentElement.focus();
   EventUtils.sendKey(key, win);
 }
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -144,19 +144,20 @@ RuleEditor.prototype = {
 
       editableField({
         element: this.selectorText,
         done: this._onSelectorDone,
         cssProperties: this.rule.cssProperties
       });
     }
 
-    if (this.rule.domRule.type !== CSSRule.KEYFRAME_RULE &&
-        this.rule.domRule.selectors) {
-      let selector = this.rule.domRule.selectors.join(", ");
+    if (this.rule.domRule.type !== CSSRule.KEYFRAME_RULE) {
+      let selector = this.rule.domRule.selectors
+               ? this.rule.domRule.selectors.join(", ")
+               : this.ruleView.inspector.selectionCssSelector;
 
       let selectorHighlighter = createChild(header, "span", {
         class: "ruleview-selectorhighlighter" +
                (this.ruleView.highlightedSelector === selector ?
                 " highlighted" : ""),
         title: l10n("rule.selectorHighlighter.tooltip")
       });
       selectorHighlighter.addEventListener("click", () => {
--- a/devtools/client/inspector/shared/style-inspector-overlays.js
+++ b/devtools/client/inspector/shared/style-inspector-overlays.js
@@ -26,16 +26,17 @@ const {
   SwatchColorPickerTooltip,
   SwatchCubicBezierTooltip,
   SwatchFilterTooltip
 } = require("devtools/client/shared/widgets/Tooltip");
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
 const {Task} = require("devtools/shared/task");
 const Services = require("Services");
+const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 
 const PREF_IMAGE_TOOLTIP_SIZE = "devtools.inspector.imagePreviewTooltipSize";
 
 // Types of existing tooltips
 const TOOLTIP_IMAGE_TYPE = "image";
 const TOOLTIP_FONTFAMILY_TYPE = "font-family";
 
 // Types of nodes in the rule/computed-view
@@ -257,16 +258,17 @@ HighlightersOverlay.prototype = {
  * @param {CssRuleView|CssComputedView} view
  *        Either the rule-view or computed-view panel
  */
 function TooltipsOverlay(view) {
   this.view = view;
 
   let {CssRuleView} = require("devtools/client/inspector/rules/rules");
   this.isRuleView = view instanceof CssRuleView;
+  this._cssProperties = getCssProperties(this.view.inspector.toolbox);
 
   this._onNewSelection = this._onNewSelection.bind(this);
   this.view.inspector.selection.on("new-node-front", this._onNewSelection);
 }
 
 exports.TooltipsOverlay = TooltipsOverlay;
 
 TooltipsOverlay.prototype = {
@@ -300,17 +302,18 @@ TooltipsOverlay.prototype = {
     this.cssDocs = new CssDocsTooltip(toolbox);
 
     if (this.isRuleView) {
       // Color picker tooltip
       this.colorPicker = new SwatchColorPickerTooltip(toolbox, this.view.inspector);
       // Cubic bezier tooltip
       this.cubicBezier = new SwatchCubicBezierTooltip(toolbox);
       // Filter editor tooltip
-      this.filterEditor = new SwatchFilterTooltip(toolbox);
+      this.filterEditor = new SwatchFilterTooltip(toolbox,
+        this._cssProperties.getValidityChecker(this.view.inspector.panelDoc));
     }
 
     this._isStarted = true;
   },
 
   /**
    * Remove the tooltips overlay from the view. This will stop tracking mouse
    * movements and displaying tooltips
--- a/devtools/client/inspector/shared/test/browser_styleinspector_output-parser.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_output-parser.js
@@ -297,17 +297,17 @@ const TEST_DATA = [
 ];
 
 add_task(function* () {
   // Mock the toolbox that initCssProperties expect so we get the fallback css properties.
   let toolbox = {target: {client: {}, hasActor: () => false}};
   yield initCssProperties(toolbox);
   let cssProperties = getCssProperties(toolbox);
 
-  let parser = new OutputParser(document, cssProperties.supportsType);
+  let parser = new OutputParser(document, cssProperties);
   for (let i = 0; i < TEST_DATA.length; i++) {
     let data = TEST_DATA[i];
     info("Output-parser test data " + i + ". {" + data.name + " : " +
       data.value + ";}");
     data.test(parser.parseCssProperty(data.name, data.value, {
       colorClass: COLOR_CLASS,
       urlClass: URL_CLASS,
       bezierClass: CUBIC_BEZIER_CLASS,
--- a/devtools/client/inspector/test/browser_inspector_menu-04-use-in-console.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-04-use-in-console.js
@@ -6,16 +6,23 @@ http://creativecommons.org/publicdomain/
 // Tests "Use in Console" menu item
 
 const TEST_URL = URL_ROOT + "doc_inspector_menu.html";
 
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
 });
 
+// Use the old webconsole since the node isn't being rendered as an HTML tag
+// in the new one (Bug 1304794)
+Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
+});
+
 add_task(function* () {
   let { inspector, toolbox } = yield openInspectorForURL(TEST_URL);
 
   yield testUseInConsole();
 
   function* testUseInConsole() {
     info("Testing 'Use in Console' menu item.");
 
--- a/devtools/client/memory/memory.xhtml
+++ b/devtools/client/memory/memory.xhtml
@@ -1,30 +1,27 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE html [
   <!ENTITY % htmlDTD
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "DTD/xhtml1-strict.dtd">
   %htmlDTD;
-  <!ENTITY % globalDTD
-    SYSTEM "chrome://global/locale/global.dtd">
-  %globalDTD;
 ]>
 
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <link rel="stylesheet" href="chrome://devtools/skin/widgets.css" type="text/css"/>
     <link rel="stylesheet" href="chrome://devtools/skin/memory.css" type="text/css"/>
     <link rel="stylesheet" href="chrome://devtools/skin/components-frame.css" type="text/css"/>
     <link rel="stylesheet" href="chrome://devtools/skin/components-h-split-box.css" type="text/css"/>
   </head>
-  <body class="theme-body" dir="&locale.dir;">
+  <body class="theme-body">
     <div id="app"></div>
 
     <script type="application/javascript;version=1.8"
             src="chrome://devtools/content/shared/theme-switching.js"
             defer="true">
     </script>
 
     <script type="application/javascript;version=1.8"
--- a/devtools/client/netmonitor/test/browser_net_pane-collapse.js
+++ b/devtools/client/netmonitor/test/browser_net_pane-collapse.js
@@ -29,26 +29,31 @@ add_task(function* () {
   is(detailsPane.style.marginRight, "0px",
     "The details pane has an incorrect right margin.");
   ok(!detailsPane.hasAttribute("animated"),
     "The details pane has an incorrect animated attribute.");
   ok(!detailsPane.classList.contains("pane-collapsed") &&
      !detailsPaneToggleButton.classList.contains("pane-collapsed"),
     "The details pane should at this point be visible.");
 
+  // Trigger reflow to make sure the UI is in required state.
+  document.documentElement.getBoundingClientRect();
+
   NetMonitorView.toggleDetailsPane({ visible: false, animated: true });
 
+  yield once(detailsPane, "transitionend");
+
   let margin = -(width + 1) + "px";
   is(width, Prefs.networkDetailsWidth,
     "The details pane has an incorrect width after collapsing.");
   is(detailsPane.style.marginLeft, margin,
     "The details pane has an incorrect left margin after collapsing.");
   is(detailsPane.style.marginRight, margin,
     "The details pane has an incorrect right margin after collapsing.");
-  ok(detailsPane.hasAttribute("animated"),
+  ok(!detailsPane.hasAttribute("animated"),
     "The details pane has an incorrect attribute after an animated collapsing.");
   ok(detailsPane.classList.contains("pane-collapsed") &&
      detailsPaneToggleButton.classList.contains("pane-collapsed"),
     "The details pane should not be visible after collapsing.");
 
   NetMonitorView.toggleDetailsPane({ visible: true, animated: false });
 
   is(width, Prefs.networkDetailsWidth,
--- a/devtools/client/scratchpad/test/browser_scratchpad_wrong_window_focus.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_wrong_window_focus.js
@@ -1,13 +1,18 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 /* Bug 661762 */
 
+// Use the old webconsole since scratchpad focus isn't working on new one (Bug 1304794)
+Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
+});
 
 function test()
 {
   waitForExplicitFinish();
 
   // To test for this bug we open a Scratchpad window, save its
   // reference and then open another one. This way the first window
   // loses its focus.
--- a/devtools/client/shared/components/sidebar-toggle.css
+++ b/devtools/client/shared/components/sidebar-toggle.css
@@ -2,31 +2,31 @@
 /* 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/. */
 
 .sidebar-toggle {
   display: block;
 }
 
-.sidebar-toggle:-moz-locale-dir(ltr)::before,
-.sidebar-toggle.pane-collapsed:-moz-locale-dir(rtl)::before {
+.sidebar-toggle::before,
+.sidebar-toggle.pane-collapsed:dir(rtl)::before {
   background-image: var(--theme-pane-collapse-image);
 }
 
-.sidebar-toggle.pane-collapsed:-moz-locale-dir(ltr)::before,
-.sidebar-toggle:-moz-locale-dir(rtl)::before {
+.sidebar-toggle.pane-collapsed::before,
+.sidebar-toggle:dir(rtl)::before {
   background-image: var(--theme-pane-expand-image);
 }
 
 /* Rotate button icon 90deg if the toolbox container is
   in vertical mode (sidebar displayed under the main panel) */
 @media (max-width: 700px) {
-  .sidebar-toggle:-moz-locale-dir(ltr)::before {
+  .sidebar-toggle::before {
     transform: rotate(90deg);
   }
 
   /* Since RTL swaps the used images, we need to flip them
      the other way round */
-  .sidebar-toggle:-moz-locale-dir(rtl)::before {
+  .sidebar-toggle:dir(rtl)::before {
     transform: rotate(-90deg);
   }
 }
--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.js
@@ -24,17 +24,17 @@ loader.lazyGetter(this, "prefBranch", fu
                     .QueryInterface(Ci.nsIPrefBranch2);
 });
 
 loader.lazyRequireGetter(this, "gcliInit", "devtools/shared/gcli/commands/index");
 loader.lazyRequireGetter(this, "util", "gcli/util/util");
 loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/utils/webconsole-utils", true);
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
-loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants", true);
+loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 
 /**
  * A collection of utilities to help working with commands
  */
 var CommandUtils = {
   /**
    * Utility to ensure that things are loaded in the correct order
--- a/devtools/client/shared/output-parser.js
+++ b/devtools/client/shared/output-parser.js
@@ -1,56 +1,54 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {Cc, Ci} = require("chrome");
 const {angleUtils} = require("devtools/client/shared/css-angle");
 const {colorUtils} = require("devtools/shared/css/color");
 const {getCSSLexer} = require("devtools/shared/css/lexer");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {
   ANGLE_TAKING_FUNCTIONS,
   BEZIER_KEYWORDS,
   COLOR_TAKING_FUNCTIONS,
   CSS_TYPES
 } = require("devtools/shared/css/properties-db");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
-loader.lazyGetter(this, "DOMUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
-
 /**
  * This module is used to process text for output by developer tools. This means
  * linking JS files with the debugger, CSS files with the style editor, JS
  * functions with the debugger, placing color swatches next to colors and
  * adding doorhanger previews where possible (images, angles, lengths,
  * border radius, cubic-bezier etc.).
  *
  * Usage:
  *   const {OutputParser} = require("devtools/client/shared/output-parser");
  *
  *   let parser = new OutputParser(document, supportsType);
  *
  *   parser.parseCssProperty("color", "red"); // Returns document fragment.
  *
  * @param {Document} document Used to create DOM nodes.
- * @param {Function} supportsTypes A function that returns a boolean when asked if a css
- * property name supports a given css type.
- * The function is executed like supportsType("color", CSS_TYPES.COLOR) where CSS_TYPES is
- * defined in devtools/shared/css/properties-db.js
+ * @param {Function} supportsTypes - A function that returns a boolean when asked if a css
+ *                   property name supports a given css type.
+ *                   The function is executed like supportsType("color", CSS_TYPES.COLOR)
+ *                   where CSS_TYPES is defined in devtools/shared/css/properties-db.js
+ * @param {Function} isValidOnClient - A function that checks if a css property
+ *                   name/value combo is valid.
  */
-function OutputParser(document, supportsType) {
+function OutputParser(document, {supportsType, isValidOnClient}) {
   this.parsed = [];
   this.doc = document;
   this.supportsType = supportsType;
+  this.isValidOnClient = isValidOnClient;
   this.colorSwatches = new WeakMap();
   this.angleSwatches = new WeakMap();
   this._onColorSwatchMouseDown = this._onColorSwatchMouseDown.bind(this);
   this._onAngleSwatchMouseDown = this._onAngleSwatchMouseDown.bind(this);
 }
 
 exports.OutputParser = OutputParser;
 
@@ -336,17 +334,17 @@ OutputParser.prototype = {
    * Check if a CSS property supports a specific value.
    *
    * @param  {String} name
    *         CSS Property name to check
    * @param  {String} value
    *         CSS Property value to check
    */
   _cssPropertySupportsValue: function (name, value) {
-    return DOMUtils.cssPropertyIsValid(name, value);
+    return this.isValidOnClient(name, value, this.doc);
   },
 
   /**
    * Tests if a given colorObject output by CssColor is valid for parsing.
    * Valid means it's really a color, not any of the CssColor SPECIAL_VALUES
    * except transparent
    */
   _isValidColor: function (colorObj) {
--- a/devtools/client/shared/test/browser_filter-editor-01.js
+++ b/devtools/client/shared/test/browser_filter-editor-01.js
@@ -5,35 +5,37 @@
 
 // Tests that the Filter Editor Widget parses filter values correctly (setCssValue)
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
 const DOMUtils =
       Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 // Verify that the given string consists of a valid CSS URL token.
 // Return true on success, false on error.
 function verifyURL(string) {
   let lexer = DOMUtils.getCSSLexer(string);
 
   let token = lexer.nextToken();
   if (!token || token.tokenType !== "url") {
     return false;
   }
 
   return lexer.nextToken() === null;
 }
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const container = doc.querySelector("#filter-container");
-  let widget = new CSSFilterEditorWidget(container, "none");
+  let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
 
   info("Test parsing of a valid CSS Filter value");
   widget.setCssValue("blur(2px) contrast(200%)");
   is(widget.getCssValue(),
      "blur(2px) contrast(200%)",
      "setCssValue should work for computed values");
 
   info("Test parsing of space-filled value");
--- a/devtools/client/shared/test/browser_filter-editor-02.js
+++ b/devtools/client/shared/test/browser_filter-editor-02.js
@@ -1,25 +1,27 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that the Filter Editor Widget renders filters correctly
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const STRINGS_URI = "devtools/locale/filterwidget.properties";
 const L10N = new LocalizationHelper(STRINGS_URI);
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const TEST_DATA = [
     {
       cssValue: "blur(2px) contrast(200%) hue-rotate(20.2deg) drop-shadow(5px 5px black)",
       expected: [
         {
           label: "blur",
           value: "2",
@@ -64,17 +66,17 @@ add_task(function* () {
     },
     {
       cssValue: "none",
       expected: []
     }
   ];
 
   const container = doc.querySelector("#filter-container");
-  let widget = new CSSFilterEditorWidget(container, "none");
+  let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
 
   info("Test rendering of different types");
 
 
   for (let {cssValue, expected} of TEST_DATA) {
     widget.setCssValue(cssValue);
 
     if (cssValue === "none") {
--- a/devtools/client/shared/test/browser_filter-editor-03.js
+++ b/devtools/client/shared/test/browser_filter-editor-03.js
@@ -1,26 +1,28 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests the Filter Editor Widget add, removeAt, updateAt, getValueAt methods
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 const GRAYSCALE_MAX = 100;
 const INVERT_MIN = 0;
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const container = doc.querySelector("#filter-container");
-  let widget = new CSSFilterEditorWidget(container, "none");
+  let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
 
   info("Test add method");
   const blur = widget.add("blur", "10.2px");
   is(widget.getCssValue(), "blur(10.2px)",
      "Should add filters");
 
   const url = widget.add("url", "test.svg");
   is(widget.getCssValue(), "blur(10.2px) url(test.svg)",
--- a/devtools/client/shared/test/browser_filter-editor-04.js
+++ b/devtools/client/shared/test/browser_filter-editor-04.js
@@ -1,26 +1,28 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests the Filter Editor Widget's drag-drop re-ordering
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 const LIST_ITEM_HEIGHT = 32;
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const container = doc.querySelector("#filter-container");
   const initialValue = "blur(2px) contrast(200%) brightness(200%)";
-  let widget = new CSSFilterEditorWidget(container, initialValue);
+  let widget = new CSSFilterEditorWidget(container, initialValue, cssIsValid);
 
   const filters = widget.el.querySelector("#filters");
   function first() {
     return filters.children[0];
   }
   function mid() {
     return filters.children[1];
   }
--- a/devtools/client/shared/test/browser_filter-editor-05.js
+++ b/devtools/client/shared/test/browser_filter-editor-05.js
@@ -3,31 +3,33 @@
 
 "use strict";
 
 requestLongerTimeout(2);
 
 // Tests the Filter Editor Widget's label-dragging
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 const FAST_VALUE_MULTIPLIER = 10;
 const SLOW_VALUE_MULTIPLIER = 0.1;
 const DEFAULT_VALUE_MULTIPLIER = 1;
 
 const GRAYSCALE_MAX = 100,
   GRAYSCALE_MIN = 0;
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const container = doc.querySelector("#filter-container");
-  let widget = new CSSFilterEditorWidget(container, "grayscale(0%) url(test.svg)");
+  let widget = new CSSFilterEditorWidget(container, "grayscale(0%) url(test.svg)", cssIsValid);
 
   const filters = widget.el.querySelector("#filters");
   const grayscale = filters.children[0];
   const url = filters.children[1];
 
   info("Test label-dragging on number-type filters without modifiers");
   widget._mouseDown({
     target: grayscale.querySelector("label"),
--- a/devtools/client/shared/test/browser_filter-editor-06.js
+++ b/devtools/client/shared/test/browser_filter-editor-06.js
@@ -1,28 +1,30 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests the Filter Editor Widget's add button
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const STRINGS_URI = "devtools/locale/filterwidget.properties";
 const L10N = new LocalizationHelper(STRINGS_URI);
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const container = doc.querySelector("#filter-container");
-  let widget = new CSSFilterEditorWidget(container, "none");
+  let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
 
   const select = widget.el.querySelector("select"),
     add = widget.el.querySelector("#add-filter");
 
   const TEST_DATA = [
     {
       name: "blur",
       unit: "px",
--- a/devtools/client/shared/test/browser_filter-editor-07.js
+++ b/devtools/client/shared/test/browser_filter-editor-07.js
@@ -1,27 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests the Filter Editor Widget's remove button
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const STRINGS_URI = "devtools/locale/filterwidget.properties";
 const L10N = new LocalizationHelper(STRINGS_URI);
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const container = doc.querySelector("#filter-container");
-  let widget = new CSSFilterEditorWidget(container, "blur(2px) contrast(200%)");
+  let widget = new CSSFilterEditorWidget(container, "blur(2px) contrast(200%)", cssIsValid);
 
   info("Test removing filters with remove button");
   widget.el.querySelector(".filter button").click();
 
   is(widget.getCssValue(), "contrast(200%)",
      "Should remove the clicked filter");
 });
--- a/devtools/client/shared/test/browser_filter-editor-08.js
+++ b/devtools/client/shared/test/browser_filter-editor-08.js
@@ -2,29 +2,31 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests the Filter Editor Widget inputs increase/decrease value using
 // arrow keys, applying multiplier using alt/shift on number-type filters
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 const FAST_VALUE_MULTIPLIER = 10;
 const SLOW_VALUE_MULTIPLIER = 0.1;
 const DEFAULT_VALUE_MULTIPLIER = 1;
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const container = doc.querySelector("#filter-container");
   const initialValue = "blur(2px)";
-  let widget = new CSSFilterEditorWidget(container, initialValue);
+  let widget = new CSSFilterEditorWidget(container, initialValue, cssIsValid);
 
   let value = 2;
 
   triggerKey = triggerKey.bind(widget);
 
   info("Test simple arrow keys");
   triggerKey(40);
 
--- a/devtools/client/shared/test/browser_filter-editor-09.js
+++ b/devtools/client/shared/test/browser_filter-editor-09.js
@@ -2,29 +2,31 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests the Filter Editor Widget inputs increase/decrease value when cursor is
 // on a number using arrow keys, applying multiplier using alt/shift on strings
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 const FAST_VALUE_MULTIPLIER = 10;
 const SLOW_VALUE_MULTIPLIER = 0.1;
 const DEFAULT_VALUE_MULTIPLIER = 1;
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const container = doc.querySelector("#filter-container");
   const initialValue = "drop-shadow(rgb(0, 0, 0) 1px 1px 0px)";
-  let widget = new CSSFilterEditorWidget(container, initialValue);
+  let widget = new CSSFilterEditorWidget(container, initialValue, cssIsValid);
   widget.el.querySelector("#filters input").setSelectionRange(13, 13);
 
   let value = 1;
 
   triggerKey = triggerKey.bind(widget);
 
   info("Test simple arrow keys");
   triggerKey(40);
--- a/devtools/client/shared/test/browser_filter-editor-10.js
+++ b/devtools/client/shared/test/browser_filter-editor-10.js
@@ -2,29 +2,31 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests the Filter Editor Widget inputs increase/decrease value when cursor is
 // on a number using arrow keys if cursor is behind/mid/after the number strings
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 const FAST_VALUE_MULTIPLIER = 10;
 const SLOW_VALUE_MULTIPLIER = 0.1;
 const DEFAULT_VALUE_MULTIPLIER = 1;
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const container = doc.querySelector("#filter-container");
   const initialValue = "drop-shadow(rgb(0, 0, 0) 10px 1px 0px)";
-  let widget = new CSSFilterEditorWidget(container, initialValue);
+  let widget = new CSSFilterEditorWidget(container, initialValue, cssIsValid);
   const input = widget.el.querySelector("#filters input");
 
   let value = 10;
 
   triggerKey = triggerKey.bind(widget);
 
   info("Test increment/decrement of string-type numbers without selection");
 
--- a/devtools/client/shared/test/browser_filter-presets-01.js
+++ b/devtools/client/shared/test/browser_filter-presets-01.js
@@ -1,24 +1,26 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests saving presets
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const container = doc.querySelector("#filter-container");
-  let widget = new CSSFilterEditorWidget(container, "none");
+  let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
   // First render
   yield widget.once("render");
 
   const VALUE = "blur(2px) contrast(150%)";
   const NAME = "Test";
 
   yield showFilterPopupPresetsAndCreatePreset(widget, NAME, VALUE);
 
--- a/devtools/client/shared/test/browser_filter-presets-02.js
+++ b/devtools/client/shared/test/browser_filter-presets-02.js
@@ -1,24 +1,26 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests loading presets
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const container = doc.querySelector("#filter-container");
-  let widget = new CSSFilterEditorWidget(container, "none");
+  let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
   // First render
   yield widget.once("render");
 
   const VALUE = "blur(2px) contrast(150%)";
   const NAME = "Test";
 
   yield showFilterPopupPresetsAndCreatePreset(widget, NAME, VALUE);
 
--- a/devtools/client/shared/test/browser_filter-presets-03.js
+++ b/devtools/client/shared/test/browser_filter-presets-03.js
@@ -1,24 +1,26 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests deleting presets
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
+const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 const TEST_URI = `data:text/html,<div id="filter-container" />`;
 
 add_task(function* () {
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
+  const cssIsValid = getClientCssProperties().getValidityChecker(doc);
 
   const container = doc.querySelector("#filter-container");
-  let widget = new CSSFilterEditorWidget(container, "none");
+  let widget = new CSSFilterEditorWidget(container, "none", cssIsValid);
   // First render
   yield widget.once("render");
 
   const NAME = "Test";
   const VALUE = "blur(2px) contrast(150%)";
 
   yield showFilterPopupPresetsAndCreatePreset(widget, NAME, VALUE);
 
--- a/devtools/client/shared/test/browser_outputparser.js
+++ b/devtools/client/shared/test/browser_outputparser.js
@@ -16,17 +16,17 @@ function* performTest() {
   let [host, , doc] = yield createHost("bottom", "data:text/html," +
     "<h1>browser_outputParser.js</h1><div></div>");
 
   // Mock the toolbox that initCssProperties expect so we get the fallback css properties.
   let toolbox = {target: {client: {}, hasActor: () => false}};
   yield initCssProperties(toolbox);
   let cssProperties = getCssProperties(toolbox);
 
-  let parser = new OutputParser(doc, cssProperties.supportsType);
+  let parser = new OutputParser(doc, cssProperties);
   testParseCssProperty(doc, parser);
   testParseCssVar(doc, parser);
   testParseURL(doc, parser);
   testParseFilter(doc, parser);
   testParseAngle(doc, parser);
 
   host.destroy();
 }
@@ -283,9 +283,8 @@ function testParseAngle(doc, parser) {
   frag = parser.parseCssProperty("background-image",
     "linear-gradient(90deg, red, blue", {
       angleSwatchClass: "test-angleswatch"
     });
 
   swatchCount = frag.querySelectorAll(".test-angleswatch").length;
   is(swatchCount, 1, "angle swatch was created");
 }
-
--- a/devtools/client/shared/test/browser_toolbar_webconsole_errors_count.js
+++ b/devtools/client/shared/test/browser_toolbar_webconsole_errors_count.js
@@ -1,13 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the developer toolbar errors count works properly.
 
+// Use the old webconsole since this is directly accessing old DOM, and
+// the error count isn't reset when pressing the clear button in new one
+// See Bug 1304794.
+Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
+});
+
 function test() {
   const TEST_URI = TEST_URI_ROOT + "browser_toolbar_webconsole_errors_count.html";
 
 
   let tab1, tab2, webconsole;
 
   Services.prefs.setBoolPref("javascript.options.strict", true);
 
--- a/devtools/client/shared/widgets/FilterWidget.js
+++ b/devtools/client/shared/widgets/FilterWidget.js
@@ -110,21 +110,24 @@ const SPECIAL_VALUES = new Set(["none", 
  *
  * You can, however, use add/remove/update methods manually.
  * See each method's comments for more details
  *
  * @param {nsIDOMNode} el
  *        The widget container.
  * @param {String} value
  *        CSS filter value
+ * @param {Function} cssIsValid
+ *        Test whether css name / value is valid.
  */
-function CSSFilterEditorWidget(el, value = "") {
+function CSSFilterEditorWidget(el, value = "", cssIsValid) {
   this.doc = el.ownerDocument;
   this.win = this.doc.defaultView;
   this.el = el;
+  this._cssIsValid = cssIsValid;
 
   this._addButtonClick = this._addButtonClick.bind(this);
   this._removeButtonClick = this._removeButtonClick.bind(this);
   this._mouseMove = this._mouseMove.bind(this);
   this._mouseUp = this._mouseUp.bind(this);
   this._mouseDown = this._mouseDown.bind(this);
   this._keyDown = this._keyDown.bind(this);
   this._input = this._input.bind(this);
@@ -763,17 +766,17 @@ CSSFilterEditorWidget.prototype = {
       this.render();
       return;
     }
 
     for (let {name, value, quote} of tokenizeFilterValue(cssValue)) {
       // If the specified value is invalid, replace it with the
       // default.
       if (name !== "url") {
-        if (!DOMUtils.cssPropertyIsValid("filter", name + "(" + value + ")")) {
+        if (!this._cssIsValid("filter", name + "(" + value + ")")) {
           value = null;
         }
       }
 
       this.add(name, value, quote, true);
     }
 
     this.emit("updated", this.getCssValue());
--- a/devtools/client/shared/widgets/Tooltip.js
+++ b/devtools/client/shared/widgets/Tooltip.js
@@ -913,20 +913,24 @@ Heritage.extend(SwatchBasedEditorTooltip
  * The swatch-based css filter tooltip class is a specific class meant to be
  * used along with rule-view's generated css filter swatches.
  * It extends the parent SwatchBasedEditorTooltip class.
  * It just wraps a standard Tooltip and sets its content with an instance of a
  * CSSFilterEditorWidget.
  *
  * @param {Toolbox} toolbox
  *        The devtools toolbox, needed to get the devtools main window.
+ * @param {function} cssIsValid
+ *        A function to check that css declaration's name and values are valid together.
+ *        This can be obtained from "shared/fronts/css-properties.js".
  */
-function SwatchFilterTooltip(toolbox) {
+function SwatchFilterTooltip(toolbox, cssIsValid) {
   let stylesheet = "chrome://devtools/content/shared/widgets/filter-widget.css";
   SwatchBasedEditorTooltip.call(this, toolbox, stylesheet);
+  this._cssIsValid = cssIsValid;
 
   // Creating a filter editor instance.
   this.widget = this.setFilterContent("none");
   this._onUpdate = this._onUpdate.bind(this);
 }
 
 exports.SwatchFilterTooltip = SwatchFilterTooltip;
 
@@ -940,17 +944,17 @@ Heritage.extend(SwatchBasedEditorTooltip
   setFilterContent: function (filter) {
     let { doc } = this.tooltip;
 
     let container = doc.createElementNS(XHTML_NS, "div");
     container.id = "filter-container";
 
     this.tooltip.setContent(container, { width: 510, height: 200 });
 
-    return new CSSFilterEditorWidget(container, filter);
+    return new CSSFilterEditorWidget(container, filter, this._cssIsValid);
   },
 
   show: Task.async(function* () {
     // Call the parent class' show function
     yield SwatchBasedEditorTooltip.prototype.show.call(this);
     // Then set the filter value and listen to changes to preview them
     if (this.activeSwatch) {
       this.currentFilterValue = this.activeSwatch.nextSibling;
--- a/devtools/client/shared/widgets/view-helpers.js
+++ b/devtools/client/shared/widgets/view-helpers.js
@@ -247,16 +247,21 @@ const ViewHelpers = exports.ViewHelpers 
     }
 
     // Hiding is always handled via margins, not the hidden attribute.
     pane.removeAttribute("hidden");
 
     // Add a class to the pane to handle min-widths, margins and animations.
     pane.classList.add("generic-toggled-pane");
 
+    // Avoid toggles in the middle of animation.
+    if (pane.hasAttribute("animated")) {
+      return;
+    }
+
     // Avoid useless toggles.
     if (flags.visible == !pane.classList.contains("pane-collapsed")) {
       if (flags.callback) {
         flags.callback();
       }
       return;
     }
 
@@ -278,33 +283,46 @@ const ViewHelpers = exports.ViewHelpers 
         pane.style.marginBottom = "0";
         pane.classList.remove("pane-collapsed");
       } else {
         let width = Math.floor(pane.getAttribute("width")) + 1;
         let height = Math.floor(pane.getAttribute("height")) + 1;
         pane.style.marginLeft = -width + "px";
         pane.style.marginRight = -width + "px";
         pane.style.marginBottom = -height + "px";
-        pane.classList.add("pane-collapsed");
       }
 
       // Wait for the animation to end before calling afterToggle()
       if (flags.animated) {
-        pane.addEventListener("transitionend", function onEvent() {
-          pane.removeEventListener("transitionend", onEvent, false);
+        let options = {
+          useCapture: false,
+          once: true
+        };
+
+        pane.addEventListener("transitionend", () => {
           // Prevent unwanted transitions: if the panel is hidden and the layout
           // changes margins will be updated and the panel will pop out.
           pane.removeAttribute("animated");
+
+          if (!flags.visible) {
+            pane.classList.add("pane-collapsed");
+          }
           if (flags.callback) {
             flags.callback();
           }
-        }, false);
-      } else if (flags.callback) {
+        }, options);
+      } else {
+        if (!flags.visible) {
+          pane.classList.add("pane-collapsed");
+        }
+
         // Invoke the callback immediately since there's no transition.
-        flags.callback();
+        if (flags.callback) {
+          flags.callback();
+        }
       }
     };
 
     // Sometimes it's useful delaying the toggle a few ticks to ensure
     // a smoother slide in-out animation.
     if (flags.delayed) {
       pane.ownerDocument.defaultView.setTimeout(doToggle,
                                                 PANE_APPEARANCE_DELAY);
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -320,17 +320,17 @@
   visibility: hidden;
 }
 
 .ruleview-expander {
   vertical-align: middle;
   display: inline-block;
 }
 
-.ruleview-expander.theme-twisty:-moz-locale-dir(rtl) {
+.ruleview-expander.theme-twisty:dir(rtl) {
   /* for preventing .theme-twisty's wrong direction in rtl; Bug 1296648 */
   transform: none;
 }
 
 .ruleview-newproperty {
   /* (enable checkbox width: 12px) + (expander width: 15px) */
   margin-inline-start: 27px;
 }
--- a/devtools/client/themes/widgets.css
+++ b/devtools/client/themes/widgets.css
@@ -277,33 +277,33 @@
 
 .breadcrumbs-widget-item:first-child {
   background-image: none;
 }
 
 /* RTL support: move the images that were on the left to the right,
  * and move images that were on the right to the left.
  */
-.breadcrumbs-widget-item:-moz-locale-dir(rtl) {
+.breadcrumbs-widget-item:dir(rtl) {
   padding: 0 20px 0 8px;
 }
 
-.breadcrumbs-widget-item:-moz-locale-dir(rtl),
-.breadcrumbs-widget-item[checked] + .breadcrumbs-widget-item:-moz-locale-dir(rtl) {
+.breadcrumbs-widget-item:dir(rtl),
+.breadcrumbs-widget-item[checked] + .breadcrumbs-widget-item:dir(rtl) {
   background-position: center right;
 }
 
-#breadcrumb-separator-before:-moz-locale-dir(rtl),
-#breadcrumb-separator-after:-moz-locale-dir(rtl),
-#breadcrumb-separator-normal:-moz-locale-dir(rtl) {
+#breadcrumb-separator-before:dir(rtl),
+#breadcrumb-separator-after:dir(rtl),
+#breadcrumb-separator-normal:dir(rtl) {
   transform: scaleX(-1);
 }
 
-#breadcrumb-separator-before:-moz-locale-dir(rtl):after,
-#breadcrumb-separator-after:-moz-locale-dir(rtl):after {
+#breadcrumb-separator-before:dir(rtl):after,
+#breadcrumb-separator-after:dir(rtl):after {
   transform: translateX(-5px) rotate(45deg);
 }
 
 .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-id,
 .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-tag,
 .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-pseudo-classes,
 .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-classes {
   color: var(--theme-selection-color);
--- a/devtools/client/webconsole/jsterm.js
+++ b/devtools/client/webconsole/jsterm.js
@@ -372,18 +372,17 @@ JSTerm.prototype = {
         result.type == "undefined" &&
         helperResult && !helperHasRawOutput) {
       callback && callback();
       return;
     }
 
     if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
       this.hud.newConsoleOutput.dispatchMessageAdd(response);
-      // @TODO figure out what to do about the callback.
-      callback && callback();
+      callback && callback(this.hud.newConsoleOutput.getLastMessage());
       return;
     }
     let msg = new Messages.JavaScriptEvalOutput(response,
                                                 errorMessage, errorDocLink);
     this.hud.output.addMessage(msg);
 
     if (callback) {
       let oldFlushCallback = this.hud._flushCallback;
@@ -421,17 +420,17 @@ JSTerm.prototype = {
    *        This is deprecated - please use the promise return value instead.
    * @returns Promise
    *          Resolves with the message once the result is displayed.
    */
   execute: function (executeString, callback) {
     let deferred = promise.defer();
     let resultCallback;
     if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
-      resultCallback = () => deferred.resolve();
+      resultCallback = (msg) => deferred.resolve(msg);
     } else {
       resultCallback = (msg) => {
         deferred.resolve(msg);
         if (callback) {
           callback(msg);
         }
       };
     }
@@ -448,17 +447,17 @@ JSTerm.prototype = {
       selectedNodeActor = inspectorSelection.nodeFront.actorID;
     }
 
     if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
       const { ConsoleCommand } = require("devtools/client/webconsole/new-console-output/types");
       let message = new ConsoleCommand({
         messageText: executeString,
       });
-      this.hud.newConsoleOutput.dispatchMessageAdd(message);
+      this.hud.proxy.dispatchMessageAdd(message);
     } else {
       let message = new Messages.Simple(executeString, {
         category: "input",
         severity: "log",
       });
       this.hud.output.addMessage(message);
     }
     let onResult = this._executeResultCallback.bind(this, resultCallback);
--- a/devtools/client/webconsole/net/test/mochitest/head.js
+++ b/devtools/client/webconsole/net/test/mochitest/head.js
@@ -20,16 +20,23 @@ const NET_XHR_PREF = "devtools.webconsol
 Services.prefs.setBoolPref(NET_INFO_PREF, true);
 Services.prefs.setBoolPref(NET_XHR_PREF, true);
 
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref(NET_INFO_PREF, true);
   Services.prefs.clearUserPref(NET_XHR_PREF, true);
 });
 
+// Use the old webconsole since the new one doesn't yet support
+// XHR spy. See Bug 1304794.
+Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
+});
+
 /**
  * Add a new test tab in the browser and load the given url.
  * @param {String} url The url to be loaded in the new tab
  * @return a promise that resolves to the tab object when the url is loaded
  */
 function addTestTab(url) {
   info("Adding a new JSON tab with URL: '" + url + "'");
 
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
+++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
@@ -13,16 +13,17 @@ const { configureStore } = require("devt
 
 const ConsoleOutput = React.createFactory(require("devtools/client/webconsole/new-console-output/components/console-output"));
 const FilterBar = React.createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar"));
 
 const store = configureStore();
 
 function NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner) {
   this.parentNode = parentNode;
+  this.parentNode = parentNode;
   this.jsterm = jsterm;
   this.toolbox = toolbox;
   this.owner = owner;
 
   this.init = this.init.bind(this);
 }
 
 NewConsoleOutputWrapper.prototype = {
@@ -63,12 +64,18 @@ NewConsoleOutputWrapper.prototype = {
   },
   dispatchMessagesAdd: (messages) => {
     const batchedActions = messages.map(message => actions.messageAdd(message));
     store.dispatch(actions.batchActions(batchedActions));
   },
   dispatchMessagesClear: () => {
     store.dispatch(actions.messagesClear());
   },
+  getLastMessage: function() {
+    // Return the last message in the DOM as the message that was just dispatched. This may not
+    // always be correct in the case of filtered messages, but it's close enough for our tests.
+    let messageNodes = this.parentNode.querySelectorAll(".message");
+    return messageNodes[messageNodes.length - 1]
+  },
 };
 
 // Exports from this module
 module.exports = NewConsoleOutputWrapper;
--- a/devtools/client/webconsole/new-console-output/test/mochitest/head.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/head.js
@@ -47,26 +47,29 @@ var openNewTabAndConsole = Task.async(fu
  * @param object options
  *        - hud: the webconsole
  *        - messages: Array[Object]. An array of messages to match. Current supported options:
  *            - text: Exact text match in .message-body
  */
 function waitForMessages({ hud, messages }) {
   return new Promise(resolve => {
     let numMatched = 0;
-    let receivedLog = hud.ui.on("new-messages", function messagesReceieved(e, newMessage) {
+    let receivedLog = hud.ui.on("new-messages", function messagesReceieved(e, newMessages) {
       for (let message of messages) {
         if (message.matched) {
           continue;
         }
 
-        if (newMessage.node.querySelector(".message-body").textContent == message.text) {
-          numMatched++;
-          message.matched = true;
-          info("Matched a message with text: " + message.text + ", still waiting for " + (messages.length - numMatched) + " messages");
+        for (let newMessage of newMessages) {
+          if (newMessage.node.querySelector(".message-body").textContent == message.text) {
+            numMatched++;
+            message.matched = true;
+            info("Matched a message with text: " + message.text + ", still waiting for " + (messages.length - numMatched) + " messages");
+            break;
+          }
         }
 
         if (numMatched === messages.length) {
           hud.ui.off("new-messages", messagesReceieved);
           resolve(receivedLog);
           return;
         }
       }
--- a/devtools/client/webconsole/test/head.js
+++ b/devtools/client/webconsole/test/head.js
@@ -316,16 +316,22 @@ var finishTest = Task.async(function* ()
   }
 
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   yield gDevTools.closeToolbox(target);
 
   finish();
 });
 
+// Always use the 'old' frontend for tests that rely on it
+Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
+});
+
 registerCleanupFunction(function* () {
   flags.testing = false;
 
   // Remove stored console commands in between tests
   yield asyncStorage.removeItem("webConsoleHistory");
 
   dumpConsoles();
 
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -3261,24 +3261,20 @@ WebConsoleConnectionProxy.prototype = {
     this.webConsoleFrame._onUpdateListeners();
   },
 
   /**
    * Dispatch a message add on the new frontend and emit an event for tests.
    */
   dispatchMessageAdd: function(packet) {
     this.webConsoleFrame.newConsoleOutput.dispatchMessageAdd(packet);
-
-    // Return the last message in the DOM as the message that was just dispatched. This may not
-    // always be true in the case of filtered messages, but it's close enough for our tests.
-    let messageNodes = this.webConsoleFrame.experimentalOutputNode.querySelectorAll(".message");
-    this.webConsoleFrame.emit("new-messages", {
+    this.webConsoleFrame.emit("new-messages", new Set([{
       response: packet,
-      node: messageNodes[messageNodes.length - 1],
-    });
+      node: this.webConsoleFrame.newConsoleOutput.getLastMessage(),
+    }]));
   },
 
   /**
    * Batched dispatch of messages.
    */
   dispatchMessagesAdd: function(packets) {
     this.webConsoleFrame.newConsoleOutput.dispatchMessagesAdd(packets);
   },
--- a/devtools/server/actors/source.js
+++ b/devtools/server/actors/source.js
@@ -621,35 +621,42 @@ let SourceActor = ActorClassWithSpec(sou
    */
   unblackbox: function () {
     this.threadActor.sources.unblackBox(this.url);
   },
 
   /**
    * Handle a request to set a breakpoint.
    *
-   * @param JSON request
-   *        A JSON object representing the request.
+   * @param Number line
+   *        Line to break on.
+   * @param Number column
+   *        Column to break on.
+   * @param String condition
+   *        A condition which must be true for breakpoint to be hit.
+   * @param Boolean noSliding
+   *        If true, disables breakpoint sliding.
    *
    * @returns Promise
    *          A promise that resolves to a JSON object representing the
    *          response.
    */
-  setBreakpoint: function (line, column, condition) {
+  setBreakpoint: function (line, column, condition, noSliding) {
     if (this.threadActor.state !== "paused") {
       throw {
         error: "wrongState",
         message: "Cannot set breakpoint while debuggee is running."
       };
     }
 
     let location = new OriginalLocation(this, line, column);
     return this._getOrCreateBreakpointActor(
       location,
-      condition
+      condition,
+      noSliding
     ).then((actor) => {
       let response = {
         actor: actor.actorID,
         isPending: actor.isPending
       };
 
       let actualLocation = actor.originalLocation;
       if (!actualLocation.equals(location)) {
@@ -666,31 +673,33 @@ let SourceActor = ActorClassWithSpec(sou
    * match the given location.
    *
    * @param OriginalLocation originalLocation
    *        An OriginalLocation representing the location of the breakpoint in
    *        the original source.
    * @param String condition
    *        A string that is evaluated whenever the breakpoint is hit. If the
    *        string evaluates to false, the breakpoint is ignored.
+   * @param Boolean noSliding
+   *        If true, disables breakpoint sliding.
    *
    * @returns BreakpointActor
    *          A BreakpointActor representing the breakpoint.
    */
-  _getOrCreateBreakpointActor: function (originalLocation, condition) {
+  _getOrCreateBreakpointActor: function (originalLocation, condition, noSliding) {
     let actor = this.breakpointActorMap.getActor(originalLocation);
     if (!actor) {
       actor = new BreakpointActor(this.threadActor, originalLocation);
       this.threadActor.threadLifetimePool.addActor(actor);
       this.breakpointActorMap.setActor(originalLocation, actor);
     }
 
     actor.condition = condition;
 
-    return this._setBreakpoint(actor);
+    return this._setBreakpoint(actor, noSliding);
   },
 
   /*
    * Ensure the given BreakpointActor is set as a breakpoint handler on all
    * scripts that match its location in the original source.
    *
    * If there are no scripts that match the location of the BreakpointActor,
    * we slide its location to the next closest line (for line breakpoints) or
@@ -700,28 +709,29 @@ let SourceActor = ActorClassWithSpec(sou
    * any code for the given location, or they were all garbage collected before
    * the debugger started running. We cannot distinguish between these two
    * cases, so we insert the BreakpointActor in the BreakpointActorMap as
    * a pending breakpoint. Whenever a new script is introduced, this method is
    * called again for each pending breakpoint.
    *
    * @param BreakpointActor actor
    *        The BreakpointActor to be set as a breakpoint handler.
+   * @param Boolean noSliding
+   *        If true, disables breakpoint sliding.
    *
    * @returns A Promise that resolves to the given BreakpointActor.
    */
-  _setBreakpoint: function (actor) {
+  _setBreakpoint: function (actor, noSliding) {
     const { originalLocation } = actor;
     const { originalLine, originalSourceActor } = originalLocation;
 
     if (!this.isSourceMapped) {
-      if (!this._setBreakpointAtGeneratedLocation(
-        actor,
-        GeneratedLocation.fromOriginalLocation(originalLocation)
-      )) {
+      const generatedLocation = GeneratedLocation.fromOriginalLocation(originalLocation);
+      if (!this._setBreakpointAtGeneratedLocation(actor, generatedLocation) &&
+          !noSliding) {
         const query = { line: originalLine };
         // For most cases, we have a real source to query for. The
         // only time we don't is for HTML pages. In that case we want
         // to query for scripts in an HTML page based on its URL, as
         // there could be several sources within an HTML page.
         if (this.source) {
           query.source = this.source;
         } else {
--- a/devtools/server/tests/mochitest/test_css-properties_01.html
+++ b/devtools/server/tests/mochitest/test_css-properties_01.html
@@ -89,16 +89,21 @@ window.onload = function() {
 
     const bgColorValues = cssProperties.getValues('background-color');
     ok(bgColorValues.includes("blanchedalmond"),
       "A property with color values includes blanchedalmond.");
     ok(bgColorValues.includes("papayawhip"),
       "A property with color values includes papayawhip.");
     ok(bgColorValues.includes("rgb"),
       "A property with color values includes non-colors.");
+
+    ok(cssProperties.isValidOnClient("margin", "0px", window.document),
+      "Margin and 0px are valid CSS values");
+    ok(!cssProperties.isValidOnClient("margin", "foo", window.document),
+      "Margin and foo are not valid CSS values");
   });
 
   addAsyncTest(function* setup() {
     let url = document.getElementById("cssProperties").href;
     yield runCssPropertiesTests(url, true);
     yield runCssPropertiesTests(url, false);
 
     runNextTest();
--- a/devtools/server/tests/unit/test_breakpoint-03.js
+++ b/devtools/server/tests/unit/test_breakpoint-03.js
@@ -31,50 +31,62 @@ function run_test_with_server(aServer, a
                            "test-stack",
                            function (aResponse, aTabClient, aThreadClient) {
                              gThreadClient = aThreadClient;
                              test_skip_breakpoint();
                            });
   });
 }
 
-function test_skip_breakpoint()
-{
-  gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+var test_no_skip_breakpoint = Task.async(function*(source, location) {
+  let [aResponse, bpClient] = yield source.setBreakpoint(
+    Object.assign({}, location, { noSliding: true })
+  );
+
+  do_check_true(!aResponse.actualLocation);
+  do_check_eq(bpClient.location.line, gDebuggee.line0 + 3);
+  yield bpClient.remove();
+});
+
+var test_skip_breakpoint = function() {
+  gThreadClient.addOneTimeListener("paused", Task.async(function *(aEvent, aPacket) {
     let location = { line: gDebuggee.line0 + 3 };
     let source = gThreadClient.source(aPacket.frame.where.source);
 
-    source.setBreakpoint(location, function (aResponse, bpClient) {
-      // Check that the breakpoint has properly skipped forward one line.
-      do_check_true(!!aResponse.actualLocation);
-      do_check_eq(aResponse.actualLocation.source.actor, source.actor);
-      do_check_eq(aResponse.actualLocation.line, location.line + 1);
+    // First, make sure that we can disable sliding with the
+    // `noSliding` option.
+    yield test_no_skip_breakpoint(source, location);
+
+    // Now make sure that the breakpoint properly slides forward one line.
+    const [aResponse, bpClient] = yield source.setBreakpoint(location);
+    do_check_true(!!aResponse.actualLocation);
+    do_check_eq(aResponse.actualLocation.source.actor, source.actor);
+    do_check_eq(aResponse.actualLocation.line, location.line + 1);
 
-      gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
-        // Check the return value.
-        do_check_eq(aPacket.type, "paused");
-        do_check_eq(aPacket.frame.where.source.actor, source.actor);
-        do_check_eq(aPacket.frame.where.line, location.line + 1);
-        do_check_eq(aPacket.why.type, "breakpoint");
-        do_check_eq(aPacket.why.actors[0], bpClient.actor);
-        // Check that the breakpoint worked.
-        do_check_eq(gDebuggee.a, 1);
-        do_check_eq(gDebuggee.b, undefined);
+    gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
+      // Check the return value.
+      do_check_eq(aPacket.type, "paused");
+      do_check_eq(aPacket.frame.where.source.actor, source.actor);
+      do_check_eq(aPacket.frame.where.line, location.line + 1);
+      do_check_eq(aPacket.why.type, "breakpoint");
+      do_check_eq(aPacket.why.actors[0], bpClient.actor);
+      // Check that the breakpoint worked.
+      do_check_eq(gDebuggee.a, 1);
+      do_check_eq(gDebuggee.b, undefined);
 
-        // Remove the breakpoint.
-        bpClient.remove(function (aResponse) {
-          gThreadClient.resume(function () {
-            gClient.close().then(gCallback);
-          });
+      // Remove the breakpoint.
+      bpClient.remove(function (aResponse) {
+        gThreadClient.resume(function () {
+          gClient.close().then(gCallback);
         });
       });
+    });
 
-      gThreadClient.resume();
-    });
-  });
+    gThreadClient.resume();
+  }));
 
   // Use `evalInSandbox` to make the debugger treat it as normal
   // globally-scoped code, where breakpoint sliding rules apply.
   Cu.evalInSandbox(
     "var line0 = Error().lineNumber;\n" +
     "debugger;\n" +      // line0 + 1
     "var a = 1;\n" +     // line0 + 2
     "// A comment.\n" +  // line0 + 3
--- a/devtools/shared/client/main.js
+++ b/devtools/shared/client/main.js
@@ -2888,30 +2888,31 @@ SourceClient.prototype = {
    * Request to set a breakpoint in the specified location.
    *
    * @param object aLocation
    *        The location and condition of the breakpoint in
    *        the form of { line[, column, condition] }.
    * @param function aOnResponse
    *        Called with the thread's response.
    */
-  setBreakpoint: function ({ line, column, condition }, aOnResponse = noop) {
+  setBreakpoint: function ({ line, column, condition, noSliding }, aOnResponse = noop) {
     // A helper function that sets the breakpoint.
     let doSetBreakpoint = aCallback => {
       let root = this._client.mainRoot;
       let location = {
         line: line,
         column: column
       };
 
       let packet = {
         to: this.actor,
         type: "setBreakpoint",
         location: location,
-        condition: condition
+        condition: condition,
+        noSliding: noSliding
       };
 
       // Backwards compatibility: send the breakpoint request to the
       // thread if the server doesn't support Debugger.Source actors.
       if (!root.traits.debuggerSourceActors) {
         packet.to = this._activeThread.actor;
         packet.location.url = this.url;
       }
--- a/devtools/shared/fronts/css-properties.js
+++ b/devtools/shared/fronts/css-properties.js
@@ -60,31 +60,74 @@ const CssPropertiesFront = FrontClassWit
  */
 function CssProperties(db) {
   this.properties = db.properties;
   this.pseudoElements = db.pseudoElements;
 
   this.isKnown = this.isKnown.bind(this);
   this.isInherited = this.isInherited.bind(this);
   this.supportsType = this.supportsType.bind(this);
+  this.isValidOnClient = this.isValidOnClient.bind(this);
+
+  // A weakly held dummy HTMLDivElement to test CSS properties on the client.
+  this._dummyElements = new WeakMap();
 }
 
 CssProperties.prototype = {
   /**
    * Checks to see if the property is known by the browser. This function has
    * `this` already bound so that it can be passed around by reference.
    *
    * @param {String} property The property name to be checked.
    * @return {Boolean}
    */
   isKnown(property) {
     return !!this.properties[property] || isCssVariable(property);
   },
 
   /**
+   * Quickly check if a CSS name/value combo is valid on the client.
+   *
+   * @param {String} Property name.
+   * @param {String} Property value.
+   * @param {Document} The client's document object.
+   * @return {Boolean}
+   */
+  isValidOnClient(name, value, doc) {
+    let dummyElement = this._dummyElements.get(doc);
+    if (!dummyElement) {
+      dummyElement = doc.createElement("div");
+      this._dummyElements.set(doc, dummyElement);
+    }
+
+    // `!important` is not a valid value when setting a style declaration in the
+    // CSS Object Model.
+    const sanitizedValue = ("" + value).replace(/!\s*important\s*$/, "");
+
+    // Test the style on the element.
+    dummyElement.style[name] = sanitizedValue;
+    const isValid = !!dummyElement.style[name];
+
+    // Reset the state of the dummy element;
+    dummyElement.style[name] = "";
+    return isValid;
+  },
+
+  /**
+   * Get a function that will check the validity of css name/values for a given document.
+   * Useful for injecting isValidOnClient into components when needed.
+   *
+   * @param {Document} The client's document object.
+   * @return {Function} this.isValidOnClient with the document pre-set.
+   */
+  getValidityChecker(doc) {
+    return (name, value) => this.isValidOnClient(name, value, doc);
+  },
+
+  /**
    * Checks to see if the property is an inherited one.
    *
    * @param {String} property The property name to be checked.
    * @return {Boolean}
    */
   isInherited(property) {
     return this.properties[property] && this.properties[property].isInherited;
   },
--- a/devtools/shared/specs/source.js
+++ b/devtools/shared/specs/source.js
@@ -24,16 +24,17 @@ const sourceSpec = generateActorSpec({
     blackbox: { response: { pausedInSource: RetVal("boolean") } },
     unblackbox: {},
     setBreakpoint: {
       request: {
         location: {
           line: Arg(0, "number"),
           column: Arg(1, "nullable:number")
         },
-        condition: Arg(2, "nullable:string")
+        condition: Arg(2, "nullable:string"),
+        noSliding: Arg(3, "nullable:boolean")
       },
       response: RetVal("json")
     },
   },
 });
 
 exports.sourceSpec = sourceSpec;
--- a/dom/animation/DocumentTimeline.cpp
+++ b/dom/animation/DocumentTimeline.cpp
@@ -41,30 +41,29 @@ NS_IMPL_RELEASE_INHERITED(DocumentTimeli
 JSObject*
 DocumentTimeline::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return DocumentTimelineBinding::Wrap(aCx, this, aGivenProto);
 }
 
 /* static */ already_AddRefed<DocumentTimeline>
 DocumentTimeline::Constructor(const GlobalObject& aGlobal,
-                              const DOMHighResTimeStamp& aOriginTime,
+                              const DocumentTimelineOptions& aOptions,
                               ErrorResult& aRv)
 {
   nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aGlobal.Context());
   if (!doc) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
+  TimeDuration originTime =
+    TimeDuration::FromMilliseconds(aOptions.mOriginTime);
 
-  TimeDuration originTime = TimeDuration::FromMilliseconds(aOriginTime);
   if (originTime == TimeDuration::Forever() ||
       originTime == -TimeDuration::Forever()) {
-    nsAutoString inputOriginTime;
-    inputOriginTime.AppendFloat(aOriginTime);
     aRv.ThrowTypeError<dom::MSG_TIME_VALUE_OUT_OF_RANGE>(
       NS_LITERAL_STRING("Origin time"));
     return nullptr;
   }
   RefPtr<DocumentTimeline> timeline = new DocumentTimeline(doc, originTime);
 
   return timeline.forget();
 }
--- a/dom/animation/DocumentTimeline.h
+++ b/dom/animation/DocumentTimeline.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_DocumentTimeline_h
 #define mozilla_dom_DocumentTimeline_h
 
+#include "mozilla/dom/DocumentTimelineBinding.h"
 #include "mozilla/TimeStamp.h"
 #include "AnimationTimeline.h"
 #include "nsIDocument.h"
 #include "nsDOMNavigationTiming.h" // for DOMHighResTimeStamp
 #include "nsRefreshDriver.h"
 
 struct JSContext;
 
@@ -49,17 +50,17 @@ public:
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(DocumentTimeline,
                                                          AnimationTimeline)
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<DocumentTimeline>
   Constructor(const GlobalObject& aGlobal,
-              const DOMHighResTimeStamp& aOriginTime,
+              const DocumentTimelineOptions& aOptions,
               ErrorResult& aRv);
 
   // AnimationTimeline methods
   virtual Nullable<TimeDuration> GetCurrentTime() const override;
 
   bool TracksWallclockTime() const override
   {
     nsRefreshDriver* refreshDriver = GetRefreshDriver();
--- a/dom/animation/test/mozilla/file_document-timeline-origin-time-range.html
+++ b/dom/animation/test/mozilla/file_document-timeline-origin-time-range.html
@@ -7,20 +7,24 @@
 
 // If the originTime parameter passed to the DocumentTimeline exceeds
 // the range of the internal storage type (a signed 64-bit integer number
 // of ticks--a platform-dependent unit) then we should throw.
 // Infinity isn't allowed as an origin time value and clamping to just
 // inside the allowed range will just mean we overflow elsewhere.
 
 test(function(t) {
-  assert_throws({name: 'TypeError'},
-    function() { new DocumentTimeline(Number.MAX_SAFE_INTEGER); });
+  assert_throws({ name: 'TypeError'},
+    function() {
+      new DocumentTimeline({ originTime: Number.MAX_SAFE_INTEGER });
+    });
 }, 'Calculated current time is positive infinity');
 
 test(function(t) {
-  assert_throws({name: 'TypeError'},
-    function() { new DocumentTimeline(-1 * Number.MAX_SAFE_INTEGER); });
+  assert_throws({ name: 'TypeError'},
+    function() {
+      new DocumentTimeline({ originTime: -1 * Number.MAX_SAFE_INTEGER });
+    });
 }, 'Calculated current time is negative infinity');
 
 done();
 </script>
 </body>
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -111,20 +111,18 @@
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 
 #if defined(XP_LINUX)
 #include "mozilla/Hal.h"
 #endif
 #include "mozilla/dom/ContentChild.h"
 
-#ifdef MOZ_EME
 #include "mozilla/EMEUtils.h"
 #include "mozilla/DetailedPromise.h"
-#endif
 
 namespace mozilla {
 namespace dom {
 
 static bool sVibratorEnabled   = false;
 static uint32_t sMaxVibrateMS  = 0;
 static uint32_t sMaxVibrateListLen = 0;
 static const char* kVibrationPermissionType = "vibration";
@@ -244,19 +242,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelManager)
 #endif
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCameraManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimeManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
-#ifdef MOZ_EME
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
-#endif
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorageAreaListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresentation)
 #ifdef MOZ_GAMEPAD
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepadServiceTest)
 #endif
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRGetDisplaysPromises)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -381,22 +377,20 @@ Navigator::Invalidate()
   }
 
   if (mPresentation) {
     mPresentation = nullptr;
   }
 
   mServiceWorkerContainer = nullptr;
 
-#ifdef MOZ_EME
   if (mMediaKeySystemAccessManager) {
     mMediaKeySystemAccessManager->Shutdown();
     mMediaKeySystemAccessManager = nullptr;
   }
-#endif
 
   if (mDeviceStorageAreaListener) {
     mDeviceStorageAreaListener = nullptr;
   }
 
 #ifdef MOZ_GAMEPAD
   if (mGamepadServiceTest) {
     mGamepadServiceTest->Shutdown();
@@ -2319,17 +2313,16 @@ Navigator::GetUserAgent(nsPIDOMWindowInn
     do_GetService("@mozilla.org/dom/site-specific-user-agent;1");
   if (!siteSpecificUA) {
     return NS_OK;
   }
 
   return siteSpecificUA->GetUserAgentForURIAndWindow(aURI, aWindow, aUserAgent);
 }
 
-#ifdef MOZ_EME
 static nsCString
 ToCString(const nsString& aString)
 {
   nsCString str("'");
   str.Append(NS_ConvertUTF16toUTF8(aString));
   str.AppendLiteral("'");
   return str;
 }
@@ -2445,17 +2438,16 @@ Navigator::RequestMediaKeySystemAccess(c
 
   if (!mMediaKeySystemAccessManager) {
     mMediaKeySystemAccessManager = new MediaKeySystemAccessManager(mWindow);
   }
 
   mMediaKeySystemAccessManager->Request(promise, aKeySystem, aConfigs);
   return promise.forget();
 }
-#endif
 
 Presentation*
 Navigator::GetPresentation(ErrorResult& aRv)
 {
   if (!mPresentation) {
     if (!mWindow) {
       aRv.Throw(NS_ERROR_UNEXPECTED);
       return nullptr;
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -13,19 +13,17 @@
 #include "nsIDOMNavigator.h"
 #include "nsIMozNavigatorNetwork.h"
 #include "nsWrapperCache.h"
 #include "nsHashKeys.h"
 #include "nsInterfaceHashtable.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWeakPtr.h"
-#ifdef MOZ_EME
 #include "mozilla/dom/MediaKeySystemAccessManager.h"
-#endif
 
 class nsPluginArray;
 class nsMimeTypeArray;
 class nsPIDOMWindowInner;
 class nsIDOMNavigatorSystemMessages;
 class nsDOMCameraManager;
 class nsDOMDeviceStorage;
 class nsIPrincipal;
@@ -312,24 +310,22 @@ public:
   }
 
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
   // GetWindowFromGlobal returns the inner window for this global, if
   // any, else null.
   static already_AddRefed<nsPIDOMWindowInner> GetWindowFromGlobal(JSObject* aGlobal);
 
-#ifdef MOZ_EME
   already_AddRefed<Promise>
   RequestMediaKeySystemAccess(const nsAString& aKeySystem,
                               const Sequence<MediaKeySystemConfiguration>& aConfig,
                               ErrorResult& aRv);
 private:
   RefPtr<MediaKeySystemAccessManager> mMediaKeySystemAccessManager;
-#endif
 
 public:
   void NotifyVRDisplaysUpdated();
   void NotifyActiveVRDisplaysChanged();
 
 private:
   virtual ~Navigator();
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -4351,17 +4351,16 @@ void
 nsDocument::SetScopeObject(nsIGlobalObject* aGlobal)
 {
   mScopeObject = do_GetWeakReference(aGlobal);
   if (aGlobal) {
     mHasHadScriptHandlingObject = true;
   }
 }
 
-#ifdef MOZ_EME
 static void
 CheckIfContainsEMEContent(nsISupports* aSupports, void* aContainsEME)
 {
   nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aSupports));
   if (domMediaElem) {
     nsCOMPtr<nsIContent> content(do_QueryInterface(domMediaElem));
     MOZ_ASSERT(content, "aSupports is not a content");
     HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(content.get());
@@ -4375,17 +4374,16 @@ CheckIfContainsEMEContent(nsISupports* a
 bool
 nsDocument::ContainsEMEContent()
 {
   bool containsEME = false;
   EnumerateActivityObservers(CheckIfContainsEMEContent,
                              static_cast<void*>(&containsEME));
   return containsEME;
 }
-#endif // MOZ_EME
 
 static void
 CheckIfContainsMSEContent(nsISupports* aSupports, void* aContainsMSE)
 {
   nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aSupports));
   if (domMediaElem) {
     nsCOMPtr<nsIContent> content(do_QueryInterface(domMediaElem));
     MOZ_ASSERT(content, "aSupports is not a content");
@@ -8356,23 +8354,21 @@ nsDocument::CanSavePresentation(nsIReque
     bool active;
     pcManager->HasActivePeerConnection(win->WindowID(), &active);
     if (active) {
       return false;
     }
   }
 #endif // MOZ_WEBRTC
 
-#ifdef MOZ_EME
   // Don't save presentations for documents containing EME content, so that
   // CDMs reliably shutdown upon user navigation.
   if (ContainsEMEContent()) {
     return false;
   }
-#endif
 
   // Don't save presentations for documents containing MSE content, to
   // reduce memory usage.
   if (ContainsMSEContent()) {
     return false;
   }
 
   // Don't save presentation if there are active FlyWeb connections or FlyWeb
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1259,19 +1259,17 @@ public:
   bool IsTopLevelContentDocument();
   void SetIsTopLevelContentDocument(bool aIsTopLevelContentDocument);
 
   bool IsContentDocument() const;
   void SetIsContentDocument(bool aIsContentDocument);
 
   js::ExpandoAndGeneration mExpandoAndGeneration;
 
-#ifdef MOZ_EME
   bool ContainsEMEContent();
-#endif
 
   bool ContainsMSEContent();
 
 protected:
   already_AddRefed<nsIPresShell> doCreateShell(nsPresContext* aContext,
                                                nsViewManager* aViewManager,
                                                mozilla::StyleSetHandle aStyleSet);
 
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2223,18 +2223,16 @@ GK_ATOM(forcemessagemanager, "forcemessa
 
 // 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(images_in_menus, "images-in-menus")
-GK_ATOM(images_in_buttons, "images-in-buttons")
 GK_ATOM(overlay_scrollbars, "overlay-scrollbars")
 GK_ATOM(windows_default_theme, "windows-default-theme")
 GK_ATOM(mac_graphite_theme, "mac-graphite-theme")
 GK_ATOM(mac_yosemite_theme, "mac-yosemite-theme")
 GK_ATOM(windows_compositor, "windows-compositor")
 GK_ATOM(windows_glass, "windows-glass")
 GK_ATOM(touch_enabled, "touch-enabled")
 GK_ATOM(menubar_drag, "menubar-drag")
@@ -2254,18 +2252,16 @@ GK_ATOM(windows_theme_generic, "windows-
 
 // 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_images_in_menus, "-moz-images-in-menus")
-GK_ATOM(_moz_images_in_buttons, "-moz-images-in-buttons")
 GK_ATOM(_moz_overlay_scrollbars, "-moz-overlay-scrollbars")
 GK_ATOM(_moz_windows_default_theme, "-moz-windows-default-theme")
 GK_ATOM(_moz_mac_graphite_theme, "-moz-mac-graphite-theme")
 GK_ATOM(_moz_mac_yosemite_theme, "-moz-mac-yosemite-theme")
 GK_ATOM(_moz_windows_compositor, "-moz-windows-compositor")
 GK_ATOM(_moz_windows_classic, "-moz-windows-classic")
 GK_ATOM(_moz_windows_glass, "-moz-windows-glass")
 GK_ATOM(_moz_windows_theme, "-moz-windows-theme")
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4780,22 +4780,20 @@ CanvasRenderingContext2D::DrawImage(cons
     mozilla::gl::GLContext* gl = gfxPlatform::GetPlatform()->GetSkiaGLGlue()->GetGLContext();
     MOZ_ASSERT(gl);
 
     HTMLVideoElement* video = &aImage.GetAsHTMLVideoElement();
     if (!video) {
       return;
     }
 
-#ifdef MOZ_EME
     if (video->ContainsRestrictedContent()) {
       aError.Throw(NS_ERROR_NOT_AVAILABLE);
       return;
     }
-#endif
 
     uint16_t readyState;
     if (NS_SUCCEEDED(video->GetReadyState(&readyState)) &&
         readyState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
       // still loading, just return
       return;
     }
 
--- a/dom/canvas/test/webgl-mochitest/mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest/mochitest.ini
@@ -48,16 +48,17 @@ fail-if = (os == 'android') || (os == 'w
 
 [regress/test_bug_1268096.html]
 
 
 [test_backends.html]
 [test_backbuffer_channels.html]
 fail-if = (os == 'b2g')
 [test_depth_readpixels.html]
+[test_canvas_size.html]
 [test_capture.html]
 support-files = ../captureStream_common.js
 # Even though we use ../ here, in the test HTML, we need to omit this. Sub-CWD relative
 # paths are fine, but they locate the file and dump it in the current directory.
 [test_cubemap_must_be_square.html]
 [test_depth_tex_lazy_clear.html]
 [test_draw.html]
 [test_fb_param.html]
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/test_canvas_size.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset='UTF-8'>
+  <script src='/tests/SimpleTest/SimpleTest.js'></script>
+  <link rel='stylesheet' href='/tests/SimpleTest/test.css'>
+</head>
+<title>WebGL test: Framebuffer maximum size test. (Bug 1290333)</title>
+<body>
+<script>
+function TestSize(contextName, testSize) {
+  var attributes = {
+    antialias: false,
+  };
+
+  var canvas = document.createElement('canvas');
+  var gl = canvas.getContext(contextName, attributes);
+
+  if (!gl) {
+    todo(false, contextName + 'is unavailable.');
+    return;
+  }
+  gl.canvas.width = testSize;
+  gl.canvas.height = testSize;
+
+  ok(true, contextName + 'test complete.');
+}
+
+function run() {
+  TestSize('webgl', 16384);
+  TestSize('webgl2', 16384);
+
+  ok(true, 'Test complete.');
+  SimpleTest.finish();
+}
+
+////////////////////////////////////////
+
+SimpleTest.waitForExplicitFinish();
+
+try {
+  var prefPairList = [
+    ['webgl.force-enabled', true],
+  ];
+  var prefEnv = {'set': prefPairList};
+  SpecialPowers.pushPrefEnv(prefEnv, run);
+} catch (e) {
+  warning('No SpecialPowers, but trying WebGL2 anyway...');
+  run();
+}
+
+</script>
+</body>
+</html>
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -6,19 +6,17 @@
 
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 #include "mozilla/dom/HTMLSourceElement.h"
 #include "mozilla/dom/ElementInlines.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/AsyncEventDispatcher.h"
-#ifdef MOZ_EME
 #include "mozilla/dom/MediaEncryptedEvent.h"
-#endif
 
 #include "base/basictypes.h"
 #include "nsIDOMHTMLMediaElement.h"
 #include "nsIDOMHTMLSourceElement.h"
 #include "TimeRanges.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValueInlines.h"
 #include "nsPresContext.h"
@@ -706,19 +704,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
   for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputStreams[i].mStream);
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlayed);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextTrackManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioTrackList)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVideoTrackList)
-#ifdef MOZ_EME
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeys)
-#endif
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedVideoStreamTrack)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
   if (tmp->mSrcStream) {
     // Need to EndMediaStreamPlayback to clear mSrcStream and make sure everything
     // gets unhooked correctly.
     tmp->EndSrcMediaStreamPlayback();
@@ -733,19 +729,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
   for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputStreams[i].mStream)
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlayed)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextTrackManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioTrackList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoTrackList)
-#ifdef MOZ_EME
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
-#endif
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectedVideoStreamTrack)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement)
   NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLMediaElement)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
 
@@ -913,24 +907,22 @@ void HTMLMediaElement::ShutdownDecoder()
   NS_ASSERTION(mDecoder, "Must have decoder to shut down");
   mWaitingForKeyListener.DisconnectIfExists();
   mDecoder->Shutdown();
   mDecoder = nullptr;
 }
 
 void HTMLMediaElement::AbortExistingLoads()
 {
-#ifdef MOZ_EME
   // If there is no existing decoder then we don't have anything to
   // report. This prevents reporting the initial load from an
   // empty video element as a failed EME load.
   if (mDecoder) {
     ReportEMETelemetry();
   }
-#endif
   // Abort any already-running instance of the resource selection algorithm.
   mLoadWaitStatus = NOT_WAITING;
 
   // Set a new load ID. This will cause events which were enqueued
   // with a different load ID to silently be cancelled.
   mCurrentLoadID++;
 
   if (mChannelLoader) {
@@ -979,19 +971,17 @@ void HTMLMediaElement::AbortExistingLoad
   mIsLoadingFromSourceChildren = false;
   mSuspendedAfterFirstFrame = false;
   mAllowSuspendAfterFirstFrame = true;
   mHaveQueuedSelectResource = false;
   mSuspendedForPreloadNone = false;
   mDownloadSuspendedByCache = false;
   mMediaInfo = MediaInfo();
   mIsEncrypted = false;
-#ifdef MOZ_EME
   mPendingEncryptedInitData.mInitDatas.Clear();
-#endif // MOZ_EME
   mWaitingForKey = false;
   mSourcePointer = nullptr;
 
   mTags = nullptr;
 
   if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
     NS_ASSERTION(!mDecoder && !mSrcStream, "How did someone setup a new stream/decoder already?");
     // ChangeNetworkState() will call UpdateAudioChannelPlayingState()
@@ -1622,16 +1612,28 @@ nsresult HTMLMediaElement::LoadResource(
   nsCOMPtr<nsIDocShell> docShell = OwnerDoc()->GetDocShell();
   if (docShell && !docShell->GetAllowMedia()) {
     return NS_ERROR_FAILURE;
   }
 
   // Set the media element's CORS mode only when loading a resource
   mCORSMode = AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin));
 
+  bool isBlob = false;
+  if (mMediaKeys &&
+      Preferences::GetBool("media.eme.mse-only", true) &&
+      // We only want mediaSource URLs, but they are BlobURL, so we have to
+      // check the schema and if they are not MediaStream or real Blob.
+      (NS_FAILED(mLoadingSrc->SchemeIs(BLOBURI_SCHEME, &isBlob)) ||
+       !isBlob ||
+       IsMediaStreamURI(mLoadingSrc) ||
+       IsBlobURI(mLoadingSrc))) {
+    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+  }
+
   HTMLMediaElement* other = LookupMediaElementURITable(mLoadingSrc);
   if (other && other->mDecoder) {
     // Clone it.
     nsresult rv = InitializeDecoderAsClone(other->mDecoder);
     if (NS_SUCCEEDED(rv))
       return rv;
   }
 
@@ -2520,21 +2522,19 @@ HTMLMediaElement::CaptureStreamInternal(
   MOZ_RELEASE_ASSERT(aGraph);
 
     MarkAsContentSource(CallerAPI::CAPTURE_STREAM);
 
   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
   if (!window) {
     return nullptr;
   }
-#ifdef MOZ_EME
   if (ContainsRestrictedContent()) {
     return nullptr;
   }
-#endif
 
   if (!mOutputStreams.IsEmpty() &&
       aGraph != mOutputStreams[0].mStream->GetInputStream()->Graph()) {
     return nullptr;
   }
 
   OutputMediaStream* out = mOutputStreams.AppendElement();
   MediaStreamTrackSourceGetter* getter = new CaptureStreamTrackSourceGetter(this);
@@ -3458,29 +3458,27 @@ void HTMLMediaElement::HiddenVideoStop()
   mVideoDecodeSuspendTime.Pause();
   if (!mVideoDecodeSuspendTimer) {
     return;
   }
   mVideoDecodeSuspendTimer->Cancel();
   mVideoDecodeSuspendTimer = nullptr;
 }
 
-#ifdef MOZ_EME
 void
 HTMLMediaElement::ReportEMETelemetry()
 {
   // Report telemetry for EME videos when a page is unloaded.
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   if (mIsEncrypted && Preferences::GetBool("media.eme.enabled")) {
     Telemetry::Accumulate(Telemetry::VIDEO_EME_PLAY_SUCCESS, mLoadedDataFired);
     LOG(LogLevel::Debug, ("%p VIDEO_EME_PLAY_SUCCESS = %s",
                        this, mLoadedDataFired ? "true" : "false"));
   }
 }
-#endif
 
 void
 HTMLMediaElement::ReportTelemetry()
 {
   // Report telemetry for videos when a page is unloaded. We
   // want to know data on what state the video is at when
   // the user has exited.
   enum UnloadedState {
@@ -3827,27 +3825,25 @@ nsresult HTMLMediaElement::FinishDecoder
       continue;
     }
 
     ms.mCapturingDecoder = true;
     aDecoder->AddOutputStream(ms.mStream->GetInputStream()->AsProcessedStream(),
                               ms.mFinishWhenEnded);
   }
 
-#ifdef MOZ_EME
   if (mMediaKeys) {
     if (mMediaKeys->GetCDMProxy()) {
       mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
     } else {
       // CDM must have crashed.
       ShutdownDecoder();
       return NS_ERROR_FAILURE;
     }
   }
-#endif
 
   MediaEventSource<void>* waitingForKeyProducer = mDecoder->WaitingForKeyEvent();
   // Not every decoder will produce waitingForKey events, only add ones that can
   if (waitingForKeyProducer) {
     mWaitingForKeyListener = waitingForKeyProducer->Connect(
       AbstractThread::MainThread(), this, &HTMLMediaElement::CannotDecryptWaitingForKey);
   }
 
@@ -4300,21 +4296,17 @@ void HTMLMediaElement::MetadataLoaded(co
 
   // If the element is gaining or losing an audio track, we need to notify
   // the audio channel agent so that the correct audio-playback events will
   // get dispatched.
   AutoNotifyAudioChannelAgent autoNotify(this);
 
   SetMediaInfo(*aInfo);
 
-  mIsEncrypted = aInfo->IsEncrypted()
-#ifdef MOZ_EME
-                 || mPendingEncryptedInitData.IsEncrypted()
-#endif // MOZ_EME
-                 ;
+  mIsEncrypted = aInfo->IsEncrypted() || mPendingEncryptedInitData.IsEncrypted();
   mTags = aTags.forget();
   mLoadedDataFired = false;
   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
 
   DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
   if (IsVideo() && HasVideo()) {
     DispatchAsyncEvent(NS_LITERAL_STRING("resize"));
   }
@@ -4330,23 +4322,21 @@ void HTMLMediaElement::MetadataLoaded(co
   if (mIsEncrypted) {
     // We only support playback of encrypted content via MSE by default.
     if (!mMediaSource && Preferences::GetBool("media.eme.mse-only", true)) {
       DecodeError(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                               "Encrypted content not supported outside of MSE"));
       return;
     }
 
-#ifdef MOZ_EME
     // Dispatch a distinct 'encrypted' event for each initData we have.
     for (const auto& initData : mPendingEncryptedInitData.mInitDatas) {
       DispatchEncrypted(initData.mInitData, initData.mType);
     }
     mPendingEncryptedInitData.mInitDatas.Clear();
-#endif // MOZ_EME
   }
 
   mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal);
 
   if (IsVideo() && aInfo->HasVideo()) {
     // We are a video element playing video so update the screen wakelock
     NotifyOwnerDocumentActivityChanged();
   }
@@ -4866,27 +4856,24 @@ void HTMLMediaElement::ChangeReadyState(
       !mLoadedDataFired) {
     DispatchAsyncEvent(NS_LITERAL_STRING("loadeddata"));
     mLoadedDataFired = true;
   }
 
   if (oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
       mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
     DispatchAsyncEvent(NS_LITERAL_STRING("canplay"));
+    if (!mPaused) {
+      mWaitingForKey = false;
+      DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
+    }
   }
 
   CheckAutoplayDataReady();
 
-  if (oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
-      mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
-      IsPotentiallyPlaying()) {
-    mWaitingForKey = false;
-    DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
-  }
-
   if (oldState < nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA &&
       mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
     DispatchAsyncEvent(NS_LITERAL_STRING("canplaythrough"));
   }
 }
 
 static const char* const gNetworkStateToString[] = {
   "EMPTY",
@@ -4985,16 +4972,18 @@ void HTMLMediaElement::CheckAutoplayData
       mDecoder->Play();
     }
   } else if (mSrcStream) {
     SetPlayedOrSeeked(true);
   }
 
   // For blocked media, the event would be pending until it is resumed.
   DispatchAsyncEvent(NS_LITERAL_STRING("play"));
+
+  DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
 }
 
 bool HTMLMediaElement::IsActive() const
 {
   nsIDocument* ownerDoc = OwnerDoc();
   return ownerDoc && ownerDoc->IsActive() && ownerDoc->IsVisible();
 }
 
@@ -5255,43 +5244,37 @@ void HTMLMediaElement::SuspendOrResumeEl
       this, aPauseElement, aSuspendEvents, OwnerDoc()->Hidden()));
 
   if (aPauseElement != mPausedForInactiveDocumentOrChannel) {
     mPausedForInactiveDocumentOrChannel = aPauseElement;
     UpdateSrcMediaStreamPlaying();
     UpdateAudioChannelPlayingState();
     if (aPauseElement) {
       ReportTelemetry();
-#ifdef MOZ_EME
       ReportEMETelemetry();
-#endif
-
-#ifdef MOZ_EME
+
       // For EME content, force destruction of the CDM client (and CDM
       // instance if this is the last client for that CDM instance) and
       // the CDM's decoder. This ensures the CDM gets reliable and prompt
       // shutdown notifications, as it may have book-keeping it needs
       // to do on shutdown.
       if (mMediaKeys) {
         mMediaKeys->Shutdown();
         mMediaKeys = nullptr;
         if (mDecoder) {
           ShutdownDecoder();
         }
       }
-#endif
       if (mDecoder) {
         mDecoder->Pause();
         mDecoder->Suspend();
       }
       mEventDeliveryPaused = aSuspendEvents;
     } else {
-#ifdef MOZ_EME
       MOZ_ASSERT(!mMediaKeys);
-#endif
       if (mDecoder) {
         mDecoder->Resume();
         if (!mPaused && !mDecoder->IsEnded()) {
           mDecoder->Play();
         }
       }
       if (mEventDeliveryPaused) {
         mEventDeliveryPaused = false;
@@ -6064,17 +6047,17 @@ HTMLMediaElement::OnVisibilityChange(Vis
       HiddenVideoStop();
 
       mDecoder->NotifyOwnerActivityChanged(true);
       break;
     }
   }
 
 }
-#ifdef MOZ_EME
+
 MediaKeys*
 HTMLMediaElement::GetMediaKeys() const
 {
   return mMediaKeys;
 }
 
 bool
 HTMLMediaElement::ContainsRestrictedContent()
@@ -6270,17 +6253,16 @@ HTMLMediaElement::GetTopLevelPrincipal()
   }
   nsIDocument* doc = top->GetExtantDoc();
   if (!doc) {
     return nullptr;
   }
   principal = doc->NodePrincipal();
   return principal.forget();
 }
-#endif // MOZ_EME
 
 void
 HTMLMediaElement::CannotDecryptWaitingForKey()
 {
   // See: http://w3c.github.io/encrypted-media/#dom-evt-waitingforkey
   // Spec: 7.5.4 Queue a "waitingforkey" Event
   // Spec: 1. Let the media element be the specified HTMLMediaElement object.
 
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -15,19 +15,17 @@
 #include "mozilla/CORSMode.h"
 #include "DecoderTraits.h"
 #include "nsIAudioChannelAgent.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/TextTrackManager.h"
 #include "mozilla/WeakPtr.h"
 #include "MediaDecoder.h"
-#ifdef MOZ_EME
 #include "mozilla/dom/MediaKeys.h"
-#endif
 #include "mozilla/StateWatching.h"
 #include "nsGkAtoms.h"
 #include "PrincipalChangeObserver.h"
 
 // X.h on Linux #defines CurrentTime as 0L, so we have to #undef it here.
 #ifdef CurrentTime
 #undef CurrentTime
 #endif
@@ -621,17 +619,16 @@ public:
 
   bool MozPreservesPitch() const
   {
     return mPreservesPitch;
   }
 
   // XPCOM MozPreservesPitch() is OK
 
-#ifdef MOZ_EME
   MediaKeys* GetMediaKeys() const;
 
   already_AddRefed<Promise> SetMediaKeys(MediaKeys* mediaKeys,
                                          ErrorResult& aRv);
 
   mozilla::dom::EventHandlerNonNull* GetOnencrypted();
   void SetOnencrypted(mozilla::dom::EventHandlerNonNull* aCallback);
 
@@ -643,17 +640,16 @@ public:
 
   bool IsEventAttributeName(nsIAtom* aName) override;
 
   // Returns the principal of the "top level" document; the origin displayed
   // in the URL bar of the browser window.
   already_AddRefed<nsIPrincipal> GetTopLevelPrincipal();
 
   bool ContainsRestrictedContent();
-#endif // MOZ_EME
 
   void CannotDecryptWaitingForKey();
 
   bool MozAutoplayEnabled() const
   {
     return mAutoplayEnabled;
   }
 
@@ -1186,19 +1182,18 @@ protected:
    */
   void HiddenVideoStart();
   /**
    * Video is not playing anymore and/or has become visible.
    * Used to track hidden-video telemetry.
    */
   void HiddenVideoStop();
 
-#ifdef MOZ_EME
   void ReportEMETelemetry();
-#endif
+
   void ReportTelemetry();
 
   // Check the permissions for audiochannel.
   bool CheckAudioChannelPermissions(const nsAString& aType);
 
   // Seeks to aTime seconds. aSeekType can be Exact to seek to exactly the
   // seek target, or PrevSyncPoint if a quicker but less precise seek is
   // desired, and we'll seek to the sync point (keyframe and/or start of the
@@ -1447,20 +1442,18 @@ protected:
   RefPtr<TimeRanges> mPlayed;
 
   // Timer used for updating progress events.
   nsCOMPtr<nsITimer> mProgressTimer;
 
   // Timer used to simulate video-suspend.
   nsCOMPtr<nsITimer> mVideoDecodeSuspendTimer;
 
-#ifdef MOZ_EME
   // Encrypted Media Extension media keys.
   RefPtr<MediaKeys> mMediaKeys;
-#endif
 
   // 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;
 
@@ -1599,20 +1592,18 @@ protected:
   // True when the CDM cannot decrypt the current block, and the
   // waitingforkey event has been fired. Back to false when keys have become
   // available and we can advance the current playback position.
   bool mWaitingForKey;
 
   // Listens for waitingForKey events from the owned decoder.
   MediaEventListener mWaitingForKeyListener;
 
-#ifdef MOZ_EME
   // Init Data that needs to be sent in 'encrypted' events in MetadataLoaded().
   EncryptionInfo mPendingEncryptedInitData;
-#endif // MOZ_EME
 
   // True if the media's channel's download has been suspended.
   Watchable<bool> mDownloadSuspendedByCache;
 
   // Audio Channel.
   AudioChannel mAudioChannel;
 
   // The audio channel volume
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -41,19 +41,17 @@
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/GeolocationBinding.h"
-#ifdef MOZ_EME
 #include "mozilla/dom/MediaKeySystemAccess.h"
-#endif
 #include "mozilla/dom/Notification.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
 #include "mozilla/dom/PFMRadioParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
@@ -970,24 +968,19 @@ ContentParent::RecvGetGMPPluginVersionFo
 }
 
 bool
 ContentParent::RecvIsGMPPresentOnDisk(const nsString& aKeySystem,
                                       const nsCString& aVersion,
                                       bool* aIsPresent,
                                       nsCString* aMessage)
 {
-#ifdef MOZ_EME
   *aIsPresent = MediaKeySystemAccess::IsGMPPresentOnDisk(aKeySystem,
                                                          aVersion,
                                                          *aMessage);
-#else
-  *aIsPresent = false;
-#endif
-
   return true;
 }
 
 bool
 ContentParent::RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID)
 {
   *aRv = NS_OK;
   return mozilla::plugins::SetupBridge(aPluginId, this, false, aRv, aRunID);
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -25,19 +25,17 @@ namespace mozilla
 namespace layers
 {
   class ImageContainer;
 } // namespace layers
 class MediaResource;
 class ReentrantMonitor;
 class VideoFrameContainer;
 class MediaDecoderOwner;
-#ifdef MOZ_EME
 class CDMProxy;
-#endif
 
 typedef nsDataHashtable<nsCStringHashKey, nsCString> MetadataTags;
 
 static inline bool IsCurrentThread(nsIThread* aThread) {
   return NS_GetCurrentThread() == aThread;
 }
 
 /**
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -306,23 +306,20 @@ MediaDecoder::NotifyOwnerActivityChanged
   StartDormantTimer();
 }
 
 bool
 MediaDecoder::IsHeuristicDormantSupported() const
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  return
-#if defined(MOZ_EME)
-    // We disallow dormant for encrypted media until bug 1181864 is fixed.
-    mInfo &&
-    !mInfo->IsEncrypted() &&
-#endif
-    mIsHeuristicDormantSupported;
+  // We disallow dormant for encrypted media until bug 1181864 is fixed.
+  return mInfo &&
+         !mInfo->IsEncrypted() &&
+         mIsHeuristicDormantSupported;
 }
 
 void
 MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!IsShutdown());
 
@@ -489,19 +486,17 @@ MediaDecoder::IsInfinite() const
   name(AbstractThread::MainThread(), val, "MediaDecoder::" #name " (Canonical)")
 
 MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
   : mWatchManager(this, AbstractThread::MainThread())
   , mDormantSupported(false)
   , mLogicalPosition(0.0)
   , mDuration(std::numeric_limits<double>::quiet_NaN())
   , mResourceCallback(new ResourceCallback())
-#ifdef MOZ_EME
   , mCDMProxyPromise(mCDMProxyPromiseHolder.Ensure(__func__))
-#endif
   , mIgnoreProgressData(false)
   , mInfiniteStream(false)
   , mOwner(aOwner)
   , mFrameStats(new FrameStatistics())
   , mVideoFrameContainer(aOwner->GetVideoFrameContainer())
   , mPlaybackStatistics(new MediaChannelStatistics())
   , mPinnedForSeek(false)
   , mPausedForPlaybackRateNull(false)
@@ -587,19 +582,17 @@ MediaDecoder::Shutdown()
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!IsShutdown());
 
   // Unwatch all watch targets to prevent further notifications.
   mWatchManager.Shutdown();
 
   mResourceCallback->Disconnect();
 
-#ifdef MOZ_EME
   mCDMProxyPromiseHolder.RejectIfExists(true, __func__);
-#endif
 
   DiscardOngoingSeekIfExists();
 
   // This changes the decoder state to SHUTDOWN and does other things
   // necessary to unblock the state machine thread if it's blocked, so
   // the asynchronous shutdown in nsDestroyStateMachine won't deadlock.
   if (mDecoderStateMachine) {
     mTimedMetadataListener.Disconnect();
@@ -1702,32 +1695,30 @@ MediaDecoder::UnpinForSeek()
 bool
 MediaDecoder::CanPlayThrough()
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE(mDecoderStateMachine, false);
   return GetStatistics().CanPlayThrough();
 }
 
-#ifdef MOZ_EME
 RefPtr<MediaDecoder::CDMProxyPromise>
 MediaDecoder::RequestCDMProxy() const
 {
   return mCDMProxyPromise;
 }
 
 void
 MediaDecoder::SetCDMProxy(CDMProxy* aProxy)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aProxy);
 
   mCDMProxyPromiseHolder.ResolveIfExists(aProxy, __func__);
 }
-#endif
 
 bool
 MediaDecoder::IsOpusEnabled()
 {
   return Preferences::GetBool("media.opus.enabled");
 }
 
 bool
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -2,21 +2,18 @@
 /* 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/. */
 
 #if !defined(MediaDecoder_h_)
 #define MediaDecoder_h_
 
-#ifdef MOZ_EME
+#include "mozilla/Atomics.h"
 #include "mozilla/CDMProxy.h"
-#endif
-
-#include "mozilla/Atomics.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/StateMirroring.h"
 #include "mozilla/StateWatching.h"
 
 #include "mozilla/dom/AudioChannelBinding.h"
 
 #include "necko-config.h"
@@ -439,25 +436,23 @@ private:
   // Notifies the element that decoding has failed.
   void DecodeError(const MediaResult& aError);
 
   // Indicate whether the media is same-origin with the element.
   void UpdateSameOriginStatus(bool aSameOrigin);
 
   MediaDecoderOwner* GetOwner() const override;
 
-#ifdef MOZ_EME
   typedef MozPromise<RefPtr<CDMProxy>, bool /* aIgnored */, /* IsExclusive = */ true> CDMProxyPromise;
 
   // Resolved when a CDMProxy is available and the capabilities are known or
   // rejected when this decoder is about to shut down.
   RefPtr<CDMProxyPromise> RequestCDMProxy() const;
 
   void SetCDMProxy(CDMProxy* aProxy);
-#endif
 
   void EnsureTelemetryReported();
 
   static bool IsOggEnabled();
   static bool IsOpusEnabled();
   static bool IsWaveEnabled();
   static bool IsWebMEnabled();
 
@@ -615,20 +610,18 @@ private:
   // after mPlayState is LOADING and before mPlayState is SHUTDOWN. It
   // is safe to access it during this period.
   //
   // Explicitly prievate to force access via accessors.
   RefPtr<MediaDecoderStateMachine> mDecoderStateMachine;
 
   RefPtr<ResourceCallback> mResourceCallback;
 
-#ifdef MOZ_EME
   MozPromiseHolder<CDMProxyPromise> mCDMProxyPromiseHolder;
   RefPtr<CDMProxyPromise> mCDMProxyPromise;
-#endif
 
 protected:
   // The promise resolving/rejection is queued as a "micro-task" which will be
   // handled immediately after the current JS task and before any pending JS
   // tasks.
   // At the time we are going to resolve/reject a promise, the "seeking" event
   // task should already be queued but might yet be processed, so we queue one
   // more task to file the promise resolving/rejection micro-tasks
--- a/dom/media/MediaDecoderOwner.h
+++ b/dom/media/MediaDecoderOwner.h
@@ -138,21 +138,19 @@ public:
   // Called by media decoder when the audible state changed
   virtual void SetAudibleState(bool aAudible) = 0;
 
   // Notified by the shutdown manager that XPCOM shutdown has begun.
   // The decoder owner should call Shutdown() on the decoder and drop the
   // reference to the decoder to prevent further calls into the decoder.
   virtual void NotifyXPCOMShutdown() = 0;
 
-#ifdef MOZ_EME
   // Dispatches a "encrypted" event to the HTMLMediaElement, with the
   // provided init data. Actual dispatch may be delayed until HAVE_METADATA.
   // Main thread only.
   virtual void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
                                  const nsAString& aInitDataType) = 0;
-#endif // MOZ_EME
 };
 
 } // namespace mozilla
 
 #endif
 
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -181,19 +181,17 @@ public:
   // needed for a while. The reader can use these notifications to enter
   // a low power state when the decoder isn't needed, if desired.
   // This is most useful on mobile.
   // Note: DecodeVideoFrame, DecodeAudioData, ReadMetadata and Seek should
   // activate the decoder if necessary. The state machine only needs to know
   // when to call SetIdle().
   virtual void SetIdle() {}
 
-#ifdef MOZ_EME
   virtual void SetCDMProxy(CDMProxy* aProxy) {}
-#endif
 
   // Tell the reader that the data decoded are not for direct playback, so it
   // can accept more files, in particular those which have more channels than
   // available in the audio output.
   void SetIgnoreAudioOutputFormat()
   {
     mIgnoreAudioOutputFormat = true;
   }
--- a/dom/media/MediaDecoderReaderWrapper.h
+++ b/dom/media/MediaDecoderReaderWrapper.h
@@ -111,19 +111,17 @@ public:
   }
   AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() {
     return mReader->CanonicalBuffered();
   }
   AbstractCanonical<bool>* CanonicalIsSuspended() {
     return mReader->CanonicalIsSuspended();
   }
 
-#ifdef MOZ_EME
   void SetCDMProxy(CDMProxy* aProxy) { mReader->SetCDMProxy(aProxy); }
-#endif
 
   void SetVideoBlankDecode(bool aIsBlankDecode);
 
 private:
   ~MediaDecoderReaderWrapper();
 
   void OnMetadataRead(MetadataHolder* aMetadata);
   void OnMetadataNotRead() {}
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -312,22 +312,17 @@ private:
     }
 
     // In general, we wait until we know the duration before notifying the decoder.
     // However, we notify  unconditionally in this case without waiting for the start
     // time, since the caller might be waiting on metadataloaded to be fired before
     // feeding in the CDM, which we need to decode the first frame (and
     // thus get the metadata). We could fix this if we could compute the start
     // time by demuxing without necessaring decoding.
-    bool waitingForCDM =
-#ifdef MOZ_EME
-    mMaster->mInfo.IsEncrypted() && !mMaster->mCDMProxy;
-#else
-    false;
-#endif
+    bool waitingForCDM = mMaster->mInfo.IsEncrypted() && !mMaster->mCDMProxy;
 
     mMaster->mNotifyMetadataBeforeFirstFrame =
       mMaster->mDuration.Ref().isSome() || waitingForCDM;
 
     if (mMaster->mNotifyMetadataBeforeFirstFrame) {
       mMaster->EnqueueLoadedMetadataEvent();
     }
 
@@ -1341,22 +1336,20 @@ nsresult MediaDecoderStateMachine::Init(
     mTaskQueue, this, &MediaDecoderStateMachine::OnAudioPopped);
   mVideoQueueListener = VideoQueue().PopEvent().Connect(
     mTaskQueue, this, &MediaDecoderStateMachine::OnVideoPopped);
 
   mMetadataManager.Connect(mReader->TimedMetadataEvent(), OwnerThread());
 
   mMediaSink = CreateMediaSink(mAudioCaptured);
 
-#ifdef MOZ_EME
   mCDMProxyPromise.Begin(aDecoder->RequestCDMProxy()->Then(
     OwnerThread(), __func__, this,
     &MediaDecoderStateMachine::OnCDMProxyReady,
     &MediaDecoderStateMachine::OnCDMProxyNotReady));
-#endif
 
   nsresult rv = mReader->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<MediaDecoderStateMachine> self = this;
   OwnerThread()->Dispatch(NS_NewRunnableFunction([self] () {
     self->mStateObj->Enter();
   }));
@@ -1673,19 +1666,17 @@ MediaDecoderStateMachine::Shutdown()
   mQueuedSeek.RejectIfExists(__func__);
 
   DiscardSeekTaskIfExist();
 
   // Shutdown happens will decode timer is active, we need to disconnect and
   // dispose of the timer.
   mVideoDecodeSuspendTimer.Reset();
 
-#ifdef MOZ_EME
   mCDMProxyPromise.DisconnectIfExists();
-#endif
 
   if (IsPlaying()) {
     StopPlayback();
   }
 
   // To break the cycle-reference between MediaDecoderReaderWrapper and MDSM.
   CancelMediaDecoderReaderWrapperCallback();
 
@@ -2387,16 +2378,20 @@ bool MediaDecoderStateMachine::HasLowBuf
 
   if (endOfDecodedData == INT64_MAX) {
     // Have decoded all samples. No point buffering.
     return false;
   }
 
   int64_t start = endOfDecodedData;
   int64_t end = std::min(GetMediaTime() + aUsecs, Duration().ToMicroseconds());
+  if (start >= end) {
+    // Duration of decoded samples is greater than our threshold.
+    return false;
+  }
   media::TimeInterval interval(media::TimeUnit::FromMicroseconds(start),
                                media::TimeUnit::FromMicroseconds(end));
   return !mBuffered.Ref().Contains(interval);
 }
 
 void
 MediaDecoderStateMachine::DecodeError(const MediaResult& aError)
 {
@@ -2902,34 +2897,32 @@ void MediaDecoderStateMachine::OnMediaSi
     return;
   }
 
   // Otherwise notify media decoder/element about this error for it makes
   // no sense to play an audio-only file without sound output.
   DecodeError(MediaResult(NS_ERROR_DOM_MEDIA_MEDIASINK_ERR, __func__));
 }
 
-#ifdef MOZ_EME
 void
 MediaDecoderStateMachine::OnCDMProxyReady(RefPtr<CDMProxy> aProxy)
 {
   MOZ_ASSERT(OnTaskQueue());
   mCDMProxyPromise.Complete();
   mCDMProxy = aProxy;
   mReader->SetCDMProxy(aProxy);
   mStateObj->HandleCDMProxyReady();
 }
 
 void
 MediaDecoderStateMachine::OnCDMProxyNotReady()
 {
   MOZ_ASSERT(OnTaskQueue());
   mCDMProxyPromise.Complete();
 }
-#endif
 
 void
 MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (aCaptured == mAudioCaptured) {
     return;
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -868,22 +868,20 @@ private:
   MediaEventProducer<MediaResult> mOnPlaybackErrorEvent;
 
   MediaEventProducer<DecoderDoctorEvent> mOnDecoderDoctorEvent;
 
   // True if audio is offloading.
   // Playback will not start when audio is offloading.
   bool mAudioOffloading;
 
-#ifdef MOZ_EME
   void OnCDMProxyReady(RefPtr<CDMProxy> aProxy);
   void OnCDMProxyNotReady();
   RefPtr<CDMProxy> mCDMProxy;
   MozPromiseRequestHolder<MediaDecoder::CDMProxyPromise> mCDMProxyPromise;
-#endif
 
 private:
   // The buffered range. Mirrored from the decoder thread.
   Mirror<media::TimeIntervals> mBuffered;
 
   Mirror<bool> mIsReaderSuspended;
 
   // The duration according to the demuxer's current estimate, mirrored from the main thread.
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.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/CDMProxy.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "nsContentUtils.h"
 #include "nsPrintfCString.h"
 #include "nsSize.h"
 #include "Layers.h"
 #include "MediaData.h"
@@ -16,20 +17,16 @@
 #include "MediaFormatReader.h"
 #include "MediaResource.h"
 #include "mozilla/SharedThreadPool.h"
 #include "VideoUtils.h"
 #include "VideoFrameContainer.h"
 
 #include <algorithm>
 
-#ifdef MOZ_EME
-#include "mozilla/CDMProxy.h"
-#endif
-
 using namespace mozilla::media;
 
 using mozilla::layers::Image;
 using mozilla::layers::LayerManager;
 using mozilla::layers::LayersBackend;
 
 static mozilla::LazyLogModule sFormatDecoderLog("MediaFormatReader");
 mozilla::LazyLogModule gMediaDemuxerLog("MediaDemuxer");
@@ -180,17 +177,16 @@ MediaFormatReader::Init()
 
   // Note: GMPCrashHelper must be created on main thread, as it may use
   // weak references, which aren't threadsafe.
   mCrashHelper = mDecoder->GetCrashHelper();
 
   return NS_OK;
 }
 
-#ifdef MOZ_EME
 class DispatchKeyNeededEvent : public Runnable {
 public:
   DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder,
                          nsTArray<uint8_t>& aInitData,
                          const nsString& aInitDataType)
     : mDecoder(aDecoder)
     , mInitData(aInitData)
     , mInitDataType(aInitDataType)
@@ -218,26 +214,21 @@ MediaFormatReader::SetCDMProxy(CDMProxy*
   RefPtr<CDMProxy> proxy = aProxy;
   RefPtr<MediaFormatReader> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
     MOZ_ASSERT(self->OnTaskQueue());
     self->mCDMProxy = proxy;
   });
   OwnerThread()->Dispatch(r.forget());
 }
-#endif // MOZ_EME
 
 bool
 MediaFormatReader::IsWaitingOnCDMResource() {
   MOZ_ASSERT(OnTaskQueue());
-#ifdef MOZ_EME
   return IsEncrypted() && !mCDMProxy;
-#else
-  return false;
-#endif
 }
 
 RefPtr<MediaDecoderReader::MetadataPromise>
 MediaFormatReader::AsyncReadMetadata()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   MOZ_DIAGNOSTIC_ASSERT(mMetadataPromise.IsEmpty());
@@ -332,23 +323,21 @@ MediaFormatReader::OnDemuxerInitDone(nsr
     } else {
       mAudio.mTrackDemuxer->BreakCycles();
       mAudio.mTrackDemuxer = nullptr;
     }
   }
 
   UniquePtr<EncryptionInfo> crypto = mDemuxer->GetCrypto();
   if (mDecoder && crypto && crypto->IsEncrypted()) {
-#ifdef MOZ_EME
     // Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING.
     for (uint32_t i = 0; i < crypto->mInitDatas.Length(); i++) {
       NS_DispatchToMainThread(
         new DispatchKeyNeededEvent(mDecoder, crypto->mInitDatas[i].mInitData, crypto->mInitDatas[i].mType));
     }
-#endif // MOZ_EME
     mInfo.mCrypto = *crypto;
   }
 
   int64_t videoDuration = HasVideo() ? mInfo.mVideo.mDuration : 0;
   int64_t audioDuration = HasAudio() ? mInfo.mAudio.mDuration : 0;
 
   int64_t duration = std::max(videoDuration, audioDuration);
   if (duration != -1) {
@@ -395,22 +384,18 @@ MediaFormatReader::EnsureDecoderCreated(
 
   if (decoder.mDecoder) {
     return NS_OK;
   }
 
   if (!mPlatform) {
     mPlatform = new PDMFactory();
     if (IsEncrypted()) {
-#ifdef MOZ_EME
       MOZ_ASSERT(mCDMProxy);
       mPlatform->SetCDMProxy(mCDMProxy);
-#else
-      return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, "EME not supported");
-#endif
     }
   }
 
   decoder.mDecoderInitialized = false;
 
   MonitorAutoLock mon(decoder.mMonitor);
 
   switch (aTrack) {
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -88,19 +88,17 @@ public:
     OwnerThread()->Dispatch(r.forget());
   }
 
   bool UseBufferingHeuristics() const override
   {
     return mTrackDemuxersMayBlock;
   }
 
-#ifdef MOZ_EME
   void SetCDMProxy(CDMProxy* aProxy) override;
-#endif
 
   // Returns a string describing the state of the decoder data.
   // Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
 
   void SetVideoBlankDecode(bool aIsBlankDecode) override;
 
 private:
@@ -560,19 +558,18 @@ private:
   // Temporary seek information while we wait for the data
   Maybe<media::TimeUnit> mFallbackSeekTime;
   Maybe<media::TimeUnit> mPendingSeekTime;
   MozPromiseHolder<SeekPromise> mSeekPromise;
 
   RefPtr<VideoFrameContainer> mVideoFrameContainer;
   layers::ImageContainer* GetImageContainer();
 
-#ifdef MOZ_EME
   RefPtr<CDMProxy> mCDMProxy;
-#endif
+
   RefPtr<GMPCrashHelper> mCrashHelper;
 
   void SetBlankDecode(TrackType aTrack, bool aIsBlankDecode);
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -217,17 +217,19 @@ ChannelMediaResource::OnStartRequest(nsI
     hc->GetResponseHeader(NS_LITERAL_CSTRING("Accept-Ranges"),
                           ranges);
     bool acceptsRanges = ranges.EqualsLiteral("bytes");
     // True if this channel will not return an unbounded amount of data
     bool dataIsBounded = false;
 
     int64_t contentLength = -1;
     hc->GetContentLength(&contentLength);
-    if (contentLength >= 0 && responseStatus == HTTP_OK_CODE) {
+    if (contentLength >= 0 &&
+        (responseStatus == HTTP_OK_CODE ||
+         responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
       // "OK" status means Content-Length is for the whole resource.
       // Since that's bounded, we know we have a finite-length resource.
       dataIsBounded = true;
     }
 
     // Assume Range requests have a bounded upper limit unless the
     // Content-Range header tells us otherwise.
     bool boundedSeekLimit = true;
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -4,19 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MP4Decoder.h"
 #include "MediaDecoderStateMachine.h"
 #include "MP4Demuxer.h"
 #include "mozilla/Preferences.h"
 #include "nsCharSeparatedTokenizer.h"
-#ifdef MOZ_EME
 #include "mozilla/CDMProxy.h"
-#endif
 #include "mozilla/Logging.h"
 #include "mozilla/SharedThreadPool.h"
 #include "nsMimeTypes.h"
 #include "nsContentTypeParser.h"
 #include "VideoUtils.h"
 
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -36,20 +36,18 @@ using CrashReporter::GetIDFromMinidump;
 #endif
 
 #include "mozilla/Telemetry.h"
 
 #ifdef XP_WIN
 #include "WMFDecoderModule.h"
 #endif
 
-#ifdef MOZ_EME
 #include "mozilla/dom/WidevineCDMManifestBinding.h"
 #include "widevine-adapter/WidevineAdapter.h"
-#endif
 
 namespace mozilla {
 
 #undef LOG
 #undef LOGD
 
 extern LogModule* GetGMPLog();
 #define LOG(level, x, ...) MOZ_LOG(GetGMPLog(), (level), (x, ##__VA_ARGS__))
@@ -805,28 +803,24 @@ GMPParent::ReadGMPMetaData()
     return GenericPromise::CreateAndReject(rv, __func__);
   }
   infoFile->AppendRelativePath(mName + NS_LITERAL_STRING(".info"));
 
   if (FileExists(infoFile)) {
     return ReadGMPInfoFile(infoFile);
   }
 
-#ifdef MOZ_EME
   // Maybe this is the Widevine adapted plugin?
   nsCOMPtr<nsIFile> manifestFile;
   rv = mDirectory->Clone(getter_AddRefs(manifestFile));
   if (NS_FAILED(rv)) {
     return GenericPromise::CreateAndReject(rv, __func__);
   }
   manifestFile->AppendRelativePath(NS_LITERAL_STRING("manifest.json"));
   return ReadChromiumManifestFile(manifestFile);
-#else
-  return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
-#endif
 }
 
 RefPtr<GenericPromise>
 GMPParent::ReadGMPInfoFile(nsIFile* aFile)
 {
   GMPInfoFileParser parser;
   if (!parser.Init(aFile)) {
     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
@@ -930,17 +924,16 @@ GMPParent::ReadChromiumManifestFile(nsIF
 }
 
 RefPtr<GenericPromise>
 GMPParent::ParseChromiumManifest(nsString aJSON)
 {
   LOGD("%s: for '%s'", __FUNCTION__, NS_LossyConvertUTF16toASCII(aJSON).get());
 
   MOZ_ASSERT(NS_IsMainThread());
-#ifdef MOZ_EME
   mozilla::dom::WidevineCDMManifest m;
   if (!m.Init(aJSON)) {
     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   nsresult ignored; // Note: ToInteger returns 0 on failure.
   if (!WidevineAdapter::Supports(m.mX_cdm_module_versions.ToInteger(&ignored),
                                  m.mX_cdm_interface_versions.ToInteger(&ignored),
@@ -965,20 +958,16 @@ GMPParent::ParseChromiumManifest(nsStrin
 
   MOZ_ASSERT(mName.EqualsLiteral("widevinecdm"));
   mAdapter = NS_LITERAL_STRING("widevine");
 #ifdef XP_WIN
   mLibs = NS_LITERAL_CSTRING("dxva2.dll");
 #endif
 
   return GenericPromise::CreateAndResolve(true, __func__);
-#else
-  MOZ_ASSERT_UNREACHABLE("don't call me if EME isn't enabled");
-  return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
-#endif
 }
 
 bool
 GMPParent::CanBeSharedCrossNodeIds() const
 {
   return !mAsyncShutdownInProgress &&
          mNodeId.IsEmpty() &&
          // XXX bug 1159300 hack -- maybe remove after openh264 1.4
--- a/dom/media/gmp/moz.build
+++ b/dom/media/gmp/moz.build
@@ -30,16 +30,18 @@ EXPORTS += [
     'gmp-api/gmp-video-frame.h',
     'gmp-api/gmp-video-host.h',
     'gmp-api/gmp-video-plane.h',
     'GMPAudioDecoderChild.h',
     'GMPAudioDecoderParent.h',
     'GMPAudioDecoderProxy.h',
     'GMPAudioHost.h',
     'GMPCallbackBase.h',
+    'GMPCDMCallbackProxy.h',
+    'GMPCDMProxy.h',
     'GMPChild.h',
     'GMPContentChild.h',
     'GMPContentParent.h',
     'GMPCrashHelperHolder.h',
     'GMPDecryptorChild.h',
     'GMPDecryptorParent.h',
     'GMPDecryptorProxy.h',
     'GMPEncryptedBufferDataImpl.h',
@@ -66,36 +68,32 @@ EXPORTS += [
     'GMPVideoEncoderChild.h',
     'GMPVideoEncoderParent.h',
     'GMPVideoEncoderProxy.h',
     'GMPVideoHost.h',
     'GMPVideoi420FrameImpl.h',
     'GMPVideoPlaneImpl.h',
 ]
 
-if CONFIG['MOZ_EME']:
-    EXPORTS += [
-        'GMPCDMCallbackProxy.h',
-        'GMPCDMProxy.h',
-    ]
-
 # We link GMPLoader into xul on B2G/Fennec as its code does not need to be
 # covered by a DRM vendor's voucher.
 if CONFIG['OS_TARGET'] == 'Android':
     SOURCES += [
       'GMPLoader.cpp',
     ]
     USE_LIBS += [
         'rlz',
     ]
 
 UNIFIED_SOURCES += [
     'GMPAudioDecoderChild.cpp',
     'GMPAudioDecoderParent.cpp',
     'GMPAudioHost.cpp',
+    'GMPCDMCallbackProxy.cpp',
+    'GMPCDMProxy.cpp',
     'GMPChild.cpp',
     'GMPContentChild.cpp',
     'GMPContentParent.cpp',
     'GMPDecryptorChild.cpp',
     'GMPDecryptorParent.cpp',
     'GMPDiskStorage.cpp',
     'GMPEncryptedBufferDataImpl.cpp',
     'GMPMemoryStorage.cpp',
@@ -117,22 +115,16 @@ UNIFIED_SOURCES += [
     'GMPVideoEncodedFrameImpl.cpp',
     'GMPVideoEncoderChild.cpp',
     'GMPVideoEncoderParent.cpp',
     'GMPVideoHost.cpp',
     'GMPVideoi420FrameImpl.cpp',
     'GMPVideoPlaneImpl.cpp',
 ]
 
-if CONFIG['MOZ_EME']:
-    UNIFIED_SOURCES += [
-        'GMPCDMCallbackProxy.cpp',
-        'GMPCDMProxy.cpp',
-    ]
-
 DIRS += [
     'rlz',
     'widevine-adapter',
 ]
 
 IPDL_SOURCES += [
   'GMPTypes.ipdlh',
   'PGMP.ipdl',
--- a/dom/media/gtest/MockMediaDecoderOwner.h
+++ b/dom/media/gtest/MockMediaDecoderOwner.h
@@ -29,20 +29,18 @@ public:
   bool HasError() const override { return false; }
   void LoadAborted() override {}
   void PlaybackEnded() override {}
   void SeekStarted() override {}
   void SeekCompleted() override {}
   void DownloadProgressed() override {}
   void UpdateReadyState() override {}
   void FirstFrameLoaded() override {}
-#ifdef MOZ_EME
   void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
                          const nsAString& aInitDataType) override {}
-#endif // MOZ_EME
   bool IsActive() const override { return true; }
   bool IsHidden() const override { return false; }
   void DownloadSuspended() override {}
   void DownloadResumed(bool aForceNetworkLoading) override {}
   void NotifySuspendedByCache(bool aIsSuspended) override {}
   void NotifyDecoderPrincipalChanged() override {}
   VideoFrameContainer* GetVideoFrameContainer() override
   {
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -2,16 +2,17 @@
 # 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/.
 
 UNIFIED_SOURCES += [
     'MockMediaResource.cpp',
     'TestAudioCompactor.cpp',
+    'TestEME.cpp',
     'TestGMPCrossOrigin.cpp',
     'TestGMPRemoveAndDelete.cpp',
     'TestGMPUtils.cpp',
     'TestIntervalSet.cpp',
     'TestMediaDataDecoder.cpp',
     'TestMediaEventSource.cpp',
     'TestMediaFormatReader.cpp',
     'TestMozPromise.cpp',
@@ -20,21 +21,16 @@ UNIFIED_SOURCES += [
     # 'TestMP4Reader.cpp', disabled so we can turn check tests back on (bug 1175752)
     'TestTrackEncoder.cpp',
     'TestVideoSegment.cpp',
     'TestVideoUtils.cpp',
     'TestVPXDecoding.cpp',
     'TestWebMBuffered.cpp',
 ]
 
-if CONFIG['MOZ_EME']:
-    UNIFIED_SOURCES += [
-        'TestEME.cpp',
-    ]
-
 if CONFIG['MOZ_WEBM_ENCODER']:
     UNIFIED_SOURCES += [
         'TestVideoTrackEncoder.cpp',
         'TestWebMWriter.cpp',
     ]
 
 if CONFIG['MOZ_RUST']:
     UNIFIED_SOURCES += ['TestRust.cpp',]
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -53,17 +53,16 @@ AppendStateToStr(SourceBufferAttributes:
       return "PARSING_MEDIA_SEGMENT";
     default:
       return "IMPOSSIBLE";
   }
 }
 
 static Atomic<uint32_t> sStreamSourceID(0u);
 
-#ifdef MOZ_EME
 class DispatchKeyNeededEvent : public Runnable {
 public:
   DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder,
                          nsTArray<uint8_t>& aInitData,
                          const nsString& aInitDataType)
     : mDecoder(aDecoder)
     , mInitData(aInitData)
     , mInitDataType(aInitDataType)
@@ -79,17 +78,16 @@ public:
     mDecoder = nullptr;
     return NS_OK;
   }
 private:
   RefPtr<AbstractMediaDecoder> mDecoder;
   nsTArray<uint8_t> mInitData;
   nsString mInitDataType;
 };
-#endif // MOZ_EME
 
 TrackBuffersManager::TrackBuffersManager(MediaSourceDecoder* aParentDecoder,
                                          const nsACString& aType)
   : mInputBuffer(new MediaByteBuffer)
   , mBufferFull(false)
   , mFirstInitializationSegmentReceived(false)
   , mNewMediaSegmentStarted(false)
   , mActiveTrack(false)
@@ -1058,24 +1056,22 @@ TrackBuffersManager::OnDemuxerInitDone(n
     mFirstInitializationSegmentReceived = true;
   } else {
     mAudioTracks.mLastInfo = new SharedTrackInfo(info.mAudio, streamID);
     mVideoTracks.mLastInfo = new SharedTrackInfo(info.mVideo, streamID);
   }
 
   UniquePtr<EncryptionInfo> crypto = mInputDemuxer->GetCrypto();
   if (crypto && crypto->IsEncrypted()) {
-#ifdef MOZ_EME
     // Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING.
     for (uint32_t i = 0; i < crypto->mInitDatas.Length(); i++) {
       NS_DispatchToMainThread(
         new DispatchKeyNeededEvent(mParentDecoder, crypto->mInitDatas[i].mInitData,
                                    crypto->mInitDatas[i].mType));
     }
-#endif // MOZ_EME
     info.mCrypto = *crypto;
     // We clear our crypto init data array, so the MediaFormatReader will
     // not emit an encrypted event for the same init data again.
     info.mCrypto.mInitDatas.Clear();
   }
 
   {
     MonitorAutoLock mon(mMonitor);
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -15,16 +15,17 @@ with Files('PeerConnection*'):
 with Files('RTC*'):
     BUG_COMPONENT = component_signaling
 
 component_av = ('Core', 'WebRTC: Audio/Video')
 with Files('GetUserMedia*'):
     BUG_COMPONENT = component_av
 
 DIRS += [
+    'eme',
     'encoder',
     'flac',
     'gmp',
     'gmp-plugin',
     'gmp-plugin-openh264',
     'imagecapture',
     'ipc',
     'mediasink',
@@ -51,19 +52,16 @@ if CONFIG['MOZ_FMP4']:
     DIRS += ['fmp4']
 
 if CONFIG['MOZ_WEBRTC']:
     DIRS += ['bridge']
 
 if CONFIG['MOZ_OMX_DECODER']:
     DIRS += ['omx']
 
-if CONFIG['MOZ_EME']:
-    DIRS += ['eme']
-
 TEST_DIRS += [
     'compiledtest',
     'gtest',
 ]
 
 MOCHITEST_MANIFESTS += [
     'test/mochitest.ini',
     'tests/mochitest/identity/mochitest.ini',
--- a/dom/media/platforms/PDMFactory.cpp
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -21,33 +21,30 @@
 #ifdef MOZ_GONK_MEDIACODEC
 #include "GonkDecoderModule.h"
 #endif
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidDecoderModule.h"
 #endif
 #include "GMPDecoderModule.h"
 
+#include "mozilla/CDMProxy.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/SharedThreadPool.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/TaskQueue.h"
 
 #include "MediaInfo.h"
 #include "MediaPrefs.h"
 #include "FuzzingWrapper.h"
 #include "H264Converter.h"
 
 #include "AgnosticDecoderModule.h"
-
-#ifdef MOZ_EME
 #include "EMEDecoderModule.h"
-#include "mozilla/CDMProxy.h"
-#endif
 
 #include "DecoderDoctorDiagnostics.h"
 
 #include "MP4Decoder.h"
 #include "mozilla/dom/RemoteVideoDecoder.h"
 
 #include "mp4_demuxer/H264.h"
 
@@ -414,18 +411,16 @@ PDMFactory::GetDecoder(const nsACString&
     if (current->SupportsMimeType(aMimeType, aDiagnostics)) {
       pdm = current;
       break;
     }
   }
   return pdm.forget();
 }
 
-#ifdef MOZ_EME
 void
 PDMFactory::SetCDMProxy(CDMProxy* aProxy)
 {
   RefPtr<PDMFactory> m = new PDMFactory();
   mEMEPDM = new EMEDecoderModule(aProxy, m);
 }
-#endif
 
 }  // namespace mozilla
--- a/dom/media/platforms/PDMFactory.h
+++ b/dom/media/platforms/PDMFactory.h
@@ -31,24 +31,22 @@ public:
   // PlatformDecoderModules alive at the same time.
   // This is called on the decode task queue.
   already_AddRefed<MediaDataDecoder>
   CreateDecoder(const CreateDecoderParams& aParams);
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const;
 
-#ifdef MOZ_EME
   // Creates a PlatformDecoderModule that uses a CDMProxy to decrypt or
   // decrypt-and-decode EME encrypted content. If the CDM only decrypts and
   // does not decode, we create a PDM and use that to create MediaDataDecoders
   // that we use on on aTaskQueue to decode the decrypted stream.
   // This is called on the decode task queue.
   void SetCDMProxy(CDMProxy* aProxy);
-#endif
 
   static constexpr int kYUV400 = 0;
   static constexpr int kYUV420 = 1;
   static constexpr int kYUV422 = 2;
   static constexpr int kYUV444 = 3;
 
 private:
   virtual ~PDMFactory();
--- a/dom/media/platforms/moz.build
+++ b/dom/media/platforms/moz.build
@@ -26,26 +26,24 @@ UNIFIED_SOURCES += [
     'agnostic/VPXDecoder.cpp',
     'agnostic/WAVDecoder.cpp',
     'PDMFactory.cpp',
     'wrappers/FuzzingWrapper.cpp',
     'wrappers/H264Converter.cpp'
 ]
 
 DIRS += [
+    'agnostic/eme',
     'agnostic/gmp',
     'omx'
 ]
 
 if CONFIG['MOZ_WMF']:
     DIRS += [ 'wmf' ];
 
-if CONFIG['MOZ_EME']:
-    DIRS += ['agnostic/eme']
-
 if CONFIG['MOZ_FFVPX'] or CONFIG['MOZ_FFMPEG']:
     # common code to either FFmpeg or FFVPX
     UNIFIED_SOURCES += [
         'ffmpeg/FFmpegLibWrapper.cpp',
     ]
 
 if CONFIG['MOZ_FFVPX']:
     DIRS += [
--- a/dom/media/tests/mochitest/test_getUserMedia_loadedmetadata.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_loadedmetadata.html
@@ -13,16 +13,17 @@
   /**
    * Tests that assigning a stream to a media element results in the
    * "loadedmetadata" event without having to play() the media element.
    *
    * Also makes sure that the video size has been set on "loadedmetadata".
    */
   runTest(function () {
     var v = document.createElement("video");
+    document.body.appendChild(v);
     v.preload = "metadata";
 
     var constraints = {video: true, audio: true};
     return getUserMedia(constraints).then(stream => new Promise(resolve => {
       v.srcObject = stream;
       v.onloadedmetadata = resolve;
     })).then(() => {
       isnot(v.videoWidth, 0, "videoWidth shall be set on 'loadedmetadata'");
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -349,22 +349,21 @@ AudioContext::CreateStereoPanner(ErrorRe
 already_AddRefed<MediaElementAudioSourceNode>
 AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
                                        ErrorResult& aRv)
 {
   if (mIsOffline) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
-#ifdef MOZ_EME
+
   if (aMediaElement.ContainsRestrictedContent()) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
-#endif
 
   if (CheckClosed(aRv)) {
     return nullptr;
   }
 
   RefPtr<DOMMediaStream> stream =
     aMediaElement.CaptureAudio(aRv, mDestination->Stream()->Graph());
   if (aRv.Failed()) {
--- a/dom/media/webspeech/synth/windows/SapiService.cpp
+++ b/dom/media/webspeech/synth/windows/SapiService.cpp
@@ -369,23 +369,32 @@ SapiService::Speak(const nsAString& aTex
   // Set the pitch using xml
   nsAutoString xml;
   xml.AssignLiteral("<pitch absmiddle=\"");
   // absmiddle doesn't allow float type
   xml.AppendInt(static_cast<int32_t>(aPitch * 10.0f - 10.0f));
   xml.AppendLiteral("\">");
   uint32_t textOffset = xml.Length();
 
-  const char16_t* escapedText =
-    nsEscapeHTML2(aText.BeginReading(), aText.Length());
-  if (!escapedText) {
-    return NS_ERROR_OUT_OF_MEMORY;
+  for (size_t i = 0; i < aText.Length(); i++) {
+    switch (aText[i]) {
+      case '&':
+        xml.AppendLiteral("&amp;");
+        break;
+      case '<':
+        xml.AppendLiteral("&lt;");
+        break;
+      case '>':
+        xml.AppendLiteral("&gt;");
+        break;
+      default:
+        xml.Append(aText[i]);
+        break;
+    }
   }
-  xml.Append(escapedText);
-  free((void*)escapedText);
 
   xml.AppendLiteral("</pitch>");
 
   RefPtr<SapiCallback> callback =
     new SapiCallback(aTask, spVoice, textOffset, aText.Length());
 
   // The last three parameters doesn't matter for an indirect service
   nsresult rv = aTask->Setup(callback, 0, 0, 0);
--- a/dom/webidl/DocumentTimeline.webidl
+++ b/dom/webidl/DocumentTimeline.webidl
@@ -5,12 +5,16 @@
  *
  * The origin of this IDL file is
  * https://w3c.github.io/web-animations/#documenttimeline
  *
  * Copyright © 2015 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
+dictionary DocumentTimelineOptions {
+  DOMHighResTimeStamp originTime = 0;
+};
+
 [Func="nsDocument::IsWebAnimationsEnabled",
- Constructor (DOMHighResTimeStamp originTime)]
+ Constructor (optional DocumentTimelineOptions options)]
 interface DocumentTimeline : AnimationTimeline {
 };
--- a/dom/webidl/HTMLMediaElement.webidl
+++ b/dom/webidl/HTMLMediaElement.webidl
@@ -148,33 +148,31 @@ partial interface HTMLMediaElement {
   // * onmozinterruptend - called when the interruption is concluded
   [Pref="media.useAudioChannelAPI"]
   attribute EventHandler onmozinterruptbegin;
 
   [Pref="media.useAudioChannelAPI"]
   attribute EventHandler onmozinterruptend;
 };
 
-#ifdef MOZ_EME
 // Encrypted Media Extensions
 partial interface HTMLMediaElement {
   [Pref="media.eme.apiVisible"]
   readonly attribute MediaKeys? mediaKeys;
 
   // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457
   [Pref="media.eme.apiVisible", NewObject]
   Promise<void> setMediaKeys(MediaKeys? mediaKeys);
 
   [Pref="media.eme.apiVisible"]
   attribute EventHandler onencrypted;
 
   [Pref="media.eme.apiVisible"]
   attribute EventHandler onwaitingforkey;
 };
-#endif
 
 // This is just for testing
 partial interface HTMLMediaElement {
   [Pref="media.useAudioChannelService.testing"]
   readonly attribute double computedVolume;
   [Pref="media.useAudioChannelService.testing"]
   readonly attribute boolean computedMuted;
   [Pref="media.useAudioChannelService.testing"]
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -417,24 +417,22 @@ partial interface Navigator {
   readonly attribute Presentation? presentation;
 };
 
 partial interface Navigator {
   [NewObject, Func="mozilla::dom::TCPSocket::ShouldTCPSocketExist"]
   readonly attribute LegacyMozTCPSocket mozTCPSocket;
 };
 
-#ifdef MOZ_EME
 partial interface Navigator {
   [Pref="media.eme.apiVisible", NewObject]
   Promise<MediaKeySystemAccess>
   requestMediaKeySystemAccess(DOMString keySystem,
                               sequence<MediaKeySystemConfiguration> supportedConfigurations);
 };
-#endif
 
 #ifdef NIGHTLY_BUILD
 partial interface Navigator {
   [Func="Navigator::IsE10sEnabled"]
   readonly attribute boolean mozE10sEnabled;
 };
 #endif
 
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -4,17 +4,16 @@
 # 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/.
 
 GENERATED_WEBIDL_FILES = [
     'CSS2Properties.webidl',
 ]
 
 PREPROCESSED_WEBIDL_FILES = [
-    'HTMLMediaElement.webidl',
     'Navigator.webidl',
     'Node.webidl',
     'Promise.webidl',
     'PromiseDebugging.webidl',
     'Window.webidl',
 ]
 
 WEBIDL_FILES = [
@@ -220,16 +219,17 @@ WEBIDL_FILES = [
     'HTMLIFrameElement.webidl',
     'HTMLImageElement.webidl',
     'HTMLInputElement.webidl',
     'HTMLLabelElement.webidl',
     'HTMLLegendElement.webidl',
     'HTMLLIElement.webidl',
     'HTMLLinkElement.webidl',
     'HTMLMapElement.webidl',
+    'HTMLMediaElement.webidl',
     'HTMLMenuElement.webidl',
     'HTMLMenuItemElement.webidl',
     'HTMLMetaElement.webidl',
     'HTMLMeterElement.webidl',
     'HTMLModElement.webidl',
     'HTMLObjectElement.webidl',
     'HTMLOListElement.webidl',
     'HTMLOptGroupElement.webidl',
@@ -296,17 +296,25 @@ WEBIDL_FILES = [
     'LegacyQueryInterface.webidl',
     'LinkStyle.webidl',
     'ListBoxObject.webidl',
     'LocalMediaStream.webidl',
     'Location.webidl',
     'MediaDeviceInfo.webidl',
     'MediaDevices.webidl',
     'MediaElementAudioSourceNode.webidl',
+    'MediaEncryptedEvent.webidl',
     'MediaError.webidl',
+    'MediaKeyError.webidl',
+    'MediaKeyMessageEvent.webidl',
+    'MediaKeys.webidl',
+    'MediaKeySession.webidl',
+    'MediaKeysRequestStatus.webidl',
+    'MediaKeyStatusMap.webidl',
+    'MediaKeySystemAccess.webidl',
     'MediaList.webidl',
     'MediaQueryList.webidl',
     'MediaRecorder.webidl',
     'MediaSource.webidl',
     'MediaStream.webidl',
     'MediaStreamAudioDestinationNode.webidl',
     'MediaStreamAudioSourceNode.webidl',
     'MediaStreamError.webidl',
@@ -597,16 +605,17 @@ WEBIDL_FILES = [
     'VTTRegion.webidl',
     'WaveShaperNode.webidl',
     'WebComponents.webidl',
     'WebGL2RenderingContext.webidl',
     'WebGLRenderingContext.webidl',
     'WebKitCSSMatrix.webidl',
     'WebSocket.webidl',
     'WheelEvent.webidl',
+    'WidevineCDMManifest.webidl',
     'WifiOptions.webidl',
     'WindowRoot.webidl',
     'Worker.webidl',
     'WorkerDebuggerGlobalScope.webidl',
     'WorkerGlobalScope.webidl',
     'WorkerLocation.webidl',
     'WorkerNavigator.webidl',
     'XMLDocument.webidl',
@@ -893,29 +902,16 @@ if CONFIG['MOZ_BUILD_APP'] in ['browser'
         'BrowserFeedWriter.webidl',
     ]
 
 if CONFIG['MOZ_BUILD_APP'] in ['browser', 'mobile/android', 'xulrunner']:
     WEBIDL_FILES += [
         'External.webidl',
     ]
 
-if CONFIG['MOZ_EME']:
-    WEBIDL_FILES += [
-        'MediaEncryptedEvent.webidl',
-        'MediaKeyError.webidl',
-        'MediaKeyMessageEvent.webidl',
-        'MediaKeys.webidl',
-        'MediaKeySession.webidl',
-        'MediaKeysRequestStatus.webidl',
-        'MediaKeyStatusMap.webidl',
-        'MediaKeySystemAccess.webidl',
-        'WidevineCDMManifest.webidl',
-    ]
-
 if CONFIG['MOZ_B2G']:
     WEBIDL_FILES += [
         'Apps.webidl',
         'Identity.webidl',
         'MozApplicationEvent.webidl'
     ]
     GENERATED_EVENTS_WEBIDL_FILES += [
         'MozApplicationEvent.webidl'
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -1431,32 +1431,32 @@ DrawTargetD2D1::GetDeviceSpaceClipRect(D
     }
   }
   return true;
 }
 
 already_AddRefed<ID2D1Image>
 DrawTargetD2D1::GetImageForLayerContent(bool aShouldPreserveContent)
 {
+  PopAllClips();
+
   if (!CurrentLayer().mCurrentList) {
     RefPtr<ID2D1Bitmap> tmpBitmap;
     HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap));
     if (FAILED(hr)) {
       gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat;
       // For now, crash in this scenario; this should happen because tmpBitmap is
       // null and CopyFromBitmap call below dereferences it.
       // return;
     }
     mDC->Flush();
 
     tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
     return tmpBitmap.forget();
   } else {
-    PopAllClips();
-
     RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
     mDC->CreateCommandList(getter_AddRefs(CurrentLayer().mCurrentList));
     mDC->SetTarget(CurrentTarget());
     list->Close();
 
     RefPtr<ID2D1Bitmap1> tmpBitmap;
     if (mDidComplexBlendWithListInList) {
       D2D1_BITMAP_PROPERTIES1 props =
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -899,21 +899,26 @@ public:
   static Matrix4x4Typed Translation(Float aX, Float aY, Float aZ)
   {
     return Matrix4x4Typed(1.0f, 0.0f, 0.0f, 0.0f,
                           0.0f, 1.0f, 0.0f, 0.0f,
                           0.0f, 0.0f, 1.0f, 0.0f,
                           aX,   aY,   aZ, 1.0f);
   }
 
-  static Matrix4x4Typed Translation(const Point3D& aP)
+  static Matrix4x4Typed Translation(const TargetPoint3D& aP)
   {
     return Translation(aP.x, aP.y, aP.z);
   }
 
+  static Matrix4x4Typed Translation(const TargetPoint& aP)
+  {
+    return Translation(aP.x, aP.y, 0);
+  }
+
   /**
    * Apply a translation to this matrix.
    *
    * The "Pre" in this method's name means that the translation is applied
    * -before- this matrix's existing transformation. That is, any vector that
    * is multiplied by the resulting matrix will first be translated, then be
    * transformed by the original transform.
    *
@@ -967,20 +972,24 @@ public:
     _13 += _14 * aZ;
     _23 += _24 * aZ;
     _33 += _34 * aZ;
     _43 += _44 * aZ;
 
     return *this;
   }
 
-  Matrix4x4Typed &PostTranslate(const Point3D& aPoint) {
+  Matrix4x4Typed &PostTranslate(const TargetPoint3D& aPoint) {
     return PostTranslate(aPoint.x, aPoint.y, aPoint.z);
   }
 
+  Matrix4x4Typed &PostTranslate(const TargetPoint& aPoint) {
+    return PostTranslate(aPoint.x, aPoint.y, 0);
+  }
+
   static Matrix4x4Typed Scaling(Float aScaleX, Float aScaleY, float aScaleZ)
   {
     return Matrix4x4Typed(aScaleX, 0.0f, 0.0f, 0.0f,
                           0.0f, aScaleY, 0.0f, 0.0f,
                           0.0f, 0.0f, aScaleZ, 0.0f,
                           0.0f, 0.0f, 0.0f, 1.0f);
   }
 
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -194,25 +194,27 @@ AsyncCompositionManager::ComputeRotation
 {
   if (!mTargetConfig.naturalBounds().IsEmpty()) {
     mWorldTransform =
       ComputeTransformForRotation(mTargetConfig.naturalBounds(),
                                   mTargetConfig.rotation());
   }
 }
 
+#ifdef DEBUG
 static void
 GetBaseTransform(Layer* aLayer, Matrix4x4* aTransform)
 {
   // Start with the animated transform if there is one
   *aTransform =
     (aLayer->AsLayerComposite()->GetShadowTransformSetByAnimation()
         ? aLayer->GetLocalTransform()
         : aLayer->GetTransform());
 }
+#endif
 
 static void
 TransformClipRect(Layer* aLayer,
                   const ParentLayerToParentLayerMatrix4x4& aTransform)
 {
   MOZ_ASSERT(aTransform.Is2D());
   const Maybe<ParentLayerIntRect>& clipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
   if (clipRect) {
@@ -254,36 +256,36 @@ SetShadowTransform(Layer* aLayer, LayerT
   aTransform.PostScale(1.0f / aLayer->GetPostXScale(),
                        1.0f / aLayer->GetPostYScale(),
                        1);
   aLayer->AsLayerComposite()->SetShadowBaseTransform(aTransform.ToUnknownMatrix());
 }
 
 static void
 TranslateShadowLayer(Layer* aLayer,
-                     const gfxPoint& aTranslation,
+                     const ParentLayerPoint& aTranslation,
                      bool aAdjustClipRect,
                      AsyncCompositionManager::ClipPartsCache* aClipPartsCache)
 {
   // This layer might also be a scrollable layer and have an async transform.
   // To make sure we don't clobber that, we start with the shadow transform.
   // (i.e. GetLocalTransform() instead of GetTransform()).
   // Note that the shadow transform is reset on every frame of composition so
   // we don't have to worry about the adjustments compounding over successive
   // frames.
   LayerToParentLayerMatrix4x4 layerTransform = aLayer->GetLocalTransformTyped();
 
   // Apply the translation to the layer transform.
-  layerTransform.PostTranslate(aTranslation.x, aTranslation.y, 0);
+  layerTransform.PostTranslate(aTranslation);
 
   SetShadowTransform(aLayer, layerTransform);
   aLayer->AsLayerComposite()->SetShadowTransformSetByAnimation(false);
 
   if (aAdjustClipRect) {
-    auto transform = ParentLayerToParentLayerMatrix4x4::Translation(aTranslation.x, aTranslation.y, 0);
+    auto transform = ParentLayerToParentLayerMatrix4x4::Translation(aTranslation);
     // If we're passed a clip parts cache, only transform the fixed part of
     // the clip.
     if (aClipPartsCache) {
       auto iter = aClipPartsCache->find(aLayer);
       MOZ_ASSERT(iter != aClipPartsCache->end());
       TransformFixedClip(aLayer, transform, iter->second);
     } else {
       TransformClipRect(aLayer, transform);
@@ -292,28 +294,30 @@ TranslateShadowLayer(Layer* aLayer,
     // If a fixed- or sticky-position layer has a mask layer, that mask should
     // move along with the layer, so apply the translation to the mask layer too.
     if (Layer* maskLayer = aLayer->GetMaskLayer()) {
       TranslateShadowLayer(maskLayer, aTranslation, false, aClipPartsCache);
     }
   }
 }
 
+#ifdef DEBUG
 static void
 AccumulateLayerTransforms(Layer* aLayer,
                           Layer* aAncestor,
                           Matrix4x4& aMatrix)
 {
   // Accumulate the transforms between this layer and the subtree root layer.
   for (Layer* l = aLayer; l && l != aAncestor; l = l->GetParent()) {
     Matrix4x4 transform;
     GetBaseTransform(l, &transform);
     aMatrix *= transform;
   }
 }
+#endif
 
 static LayerPoint
 GetLayerFixedMarginsOffset(Layer* aLayer,
                            const ScreenMargin& aFixedLayerMargins)
 {
   // Work out the necessary translation, in root scrollable layer space.
   // Because fixed layer margins are stored relative to the root scrollable
   // layer, we can just take the difference between these values.
@@ -424,138 +428,155 @@ IsFixedOrSticky(Layer* aLayer)
   if (aLayer->GetIsStickyPosition()) {
     return Some(aLayer->GetStickyScrollContainerId());
   }
   return Nothing();
 }
 
 void
 AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aTransformedSubtreeRoot,
+                                                   Layer* aStartTraversalAt,
                                                    FrameMetrics::ViewID aTransformScrollId,
                                                    const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
                                                    const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
                                                    const ScreenMargin& aFixedLayerMargins,
                                                    ClipPartsCache* aClipPartsCache)
 {
-  ForEachNode<ForwardIterator>(
-      aTransformedSubtreeRoot,
-      [&] (Layer* layer)
-      {
-        bool needsAsyncTransformUnapplied = false;
-        if (Maybe<FrameMetrics::ViewID> fixedTo = IsFixedOrSticky(layer)) {
-          needsAsyncTransformUnapplied = AsyncTransformShouldBeUnapplied(layer,
-              *fixedTo, aTransformedSubtreeRoot, aTransformScrollId);
-        }
+  // We're going to be inverting |aCurrentTransformForRoot|.
+  // If it's singular, there's nothing we can do.
+  if (aCurrentTransformForRoot.IsSingular()) {
+    return;
+  }
+
+  Layer* layer = aStartTraversalAt;
+  bool needsAsyncTransformUnapplied = false;
+  if (Maybe<FrameMetrics::ViewID> fixedTo = IsFixedOrSticky(layer)) {
+    needsAsyncTransformUnapplied = AsyncTransformShouldBeUnapplied(layer,
+        *fixedTo, aTransformedSubtreeRoot, aTransformScrollId);
+  }
 
-        // We want to process all the fixed and sticky descendants of
-        // aTransformedSubtreeRoot. Once we do encounter such a descendant, we don't
-        // need to recurse any deeper because the adjustment to the fixed or sticky
-        // layer will apply to its subtree.
-        if (!needsAsyncTransformUnapplied) {
-          return TraversalFlag::Continue;
-        }
+  // We want to process all the fixed and sticky descendants of
+  // aTransformedSubtreeRoot. Once we do encounter such a descendant, we don't
+  // need to recurse any deeper because the adjustment to the fixed or sticky
+  // layer will apply to its subtree.
+  if (!needsAsyncTransformUnapplied) {
+    for (Layer* child = layer->GetFirstChild(); child; child = child->GetNextSibling()) {
+      AlignFixedAndStickyLayers(aTransformedSubtreeRoot, child,
+          aTransformScrollId, aPreviousTransformForRoot,
+          aCurrentTransformForRoot, aFixedLayerMargins, aClipPartsCache);
+    }
+    return;
+  }
 
-        // Insert a translation so that the position of the anchor point is the same
-        // before and after the change to the transform of aTransformedSubtreeRoot.
-
-        // Accumulate the transforms between this layer and the subtree root layer.
-        Matrix4x4 ancestorTransform;
-        AccumulateLayerTransforms(layer->GetParent(), aTransformedSubtreeRoot,
-                                  ancestorTransform);
+  // Insert a translation so that the position of the anchor point is the same
+  // before and after the change to the transform of aTransformedSubtreeRoot.
 
-        // Calculate the cumulative transforms between the subtree root with the
-        // old transform and the current transform.
-        Matrix4x4 oldCumulativeTransform = ancestorTransform * aPreviousTransformForRoot.ToUnknownMatrix();
-        Matrix4x4 newCumulativeTransform = ancestorTransform * aCurrentTransformForRoot.ToUnknownMatrix();
-        if (newCumulativeTransform.IsSingular()) {
-          return TraversalFlag::Skip;
-        }
+  // A transform creates a containing block for fixed-position descendants,
+  // so there shouldn't be a transform in between the fixed layer and
+  // the subtree root layer.
+#ifdef DEBUG
+  Matrix4x4 ancestorTransform;
+  if (layer != aTransformedSubtreeRoot) {
+    AccumulateLayerTransforms(layer->GetParent(), aTransformedSubtreeRoot,
+                              ancestorTransform);
+  }
+  MOZ_ASSERT(ancestorTransform.IsIdentity());
+#endif
 
-        // Add in the layer's local transform, if it isn't already included in
-        // |aPreviousTransformForRoot| and |aCurrentTransformForRoot| (this happens
-        // when the fixed/sticky layer is itself the transformed subtree root).
-        Matrix4x4 localTransform;
-        GetBaseTransform(layer, &localTransform);
-        if (layer != aTransformedSubtreeRoot) {
-          oldCumulativeTransform = localTransform * oldCumulativeTransform;
-          newCumulativeTransform = localTransform * newCumulativeTransform;
-        }
+  // Since we create container layers for fixed layers, there shouldn't
+  // a local CSS or OMTA transform on the fixed layer, either (any local
+  // transform would go onto a descendant layer inside the container
+  // layer).
+#ifdef DEBUG
+  Matrix4x4 localTransform;
+  GetBaseTransform(layer, &localTransform);
+  MOZ_ASSERT(localTransform.IsIdentity());
+#endif
 
-        // Now work out the translation necessary to make sure the layer doesn't
-        // move given the new sub-tree root transform.
+  // Now work out the translation necessary to make sure the layer doesn't
+  // move given the new sub-tree root transform.
 
-        // Get the layer's fixed anchor point, in the layer's local coordinate space
-        // (before any cumulative transform is applied).
-        LayerPoint anchor = layer->GetFixedPositionAnchor();
+  // Get the layer's fixed anchor point, in the layer's local coordinate space
+  // (before any transform is applied).
+  LayerPoint anchor = layer->GetFixedPositionAnchor();
 
-        // Offset the layer's anchor point to make sure fixed position content
-        // respects content document fixed position margins.
-        LayerPoint offsetAnchor = anchor + GetLayerFixedMarginsOffset(layer, aFixedLayerMargins);
+  // Offset the layer's anchor point to make sure fixed position content
+  // respects content document fixed position margins.
+  LayerPoint offsetAnchor = anchor + GetLayerFixedMarginsOffset(layer, aFixedLayerMargins);
 
-        // Additionally transform the anchor to compensate for the change
-        // from the old cumulative transform to the new cumulative transform. We do
-        // this by using the old transform to take the offset anchor back into
-        // subtree root space, and then the inverse of the new cumulative transform
-        // to bring it back to layer space.
-        LayerPoint transformedAnchor = ViewAs<LayerPixel>(
-            newCumulativeTransform.Inverse().TransformPoint(
-              (oldCumulativeTransform.TransformPoint(offsetAnchor.ToUnknownPoint()))));
+  // Additionally transform the anchor to compensate for the change
+  // from the old transform to the new transform. We do
+  // this by using the old transform to take the offset anchor back into
+  // subtree root space, and then the inverse of the new transform
+  // to bring it back to layer space.
+  ParentLayerPoint offsetAnchorInSubtreeRootSpace =
+      aPreviousTransformForRoot.TransformPoint(offsetAnchor);
+  LayerPoint transformedAnchor = aCurrentTransformForRoot.Inverse()
+      .TransformPoint(offsetAnchorInSubtreeRootSpace);
+
+  // We want to translate the layer by the difference between
+  // |transformedAnchor| and |anchor|.
+  LayerPoint translation = transformedAnchor - anchor;
 
-        // We want to translate the layer by the difference between |transformedAnchor|
-        // and |anchor|. To achieve this, we will add a translation to the layer's
-        // transform. This translation will apply on top of the layer's local
-        // transform, but |anchor| and |transformedAnchor| are in a coordinate space
-        // where the local transform isn't applied yet, so apply it and then subtract
-        // to get the desired translation.
-        auto localTransformTyped = ViewAs<LayerToParentLayerMatrix4x4>(localTransform);
-        ParentLayerPoint translation = TransformBy(localTransformTyped, transformedAnchor)
-                                     - TransformBy(localTransformTyped, anchor);
+  // A fixed layer will "consume" (be unadjusted by) the entire translation
+  // calculated above. A sticky layer may consume all, part, or none of it,
+  // depending on where we are relative to its sticky scroll range.
+  // The remainder of the translation (the unconsumed portion) needs to
+  // be propagated to descendant fixed/sticky layers.
+  LayerPoint unconsumedTranslation;
 
-        // A fixed layer will "consume" (be unadjusted by) the entire translation
-        // calculated above. A sticky layer may consume all, part, or none of it,
-        // depending on where we are relative to its sticky scroll range.
-        bool translationConsumed = true;
+  if (layer->GetIsStickyPosition()) {
+    // For sticky positioned layers, the difference between the two rectangles
+    // defines a pair of translation intervals in each dimension through which
+    // the layer should not move relative to the scroll container. To
+    // accomplish this, we limit each dimension of the |translation| to that
+    // part of it which overlaps those intervals.
+    const LayerRect& stickyOuter = layer->GetStickyScrollRangeOuter();
+    const LayerRect& stickyInner = layer->GetStickyScrollRangeInner();
 
-        if (layer->GetIsStickyPosition()) {
-          // For sticky positioned layers, the difference between the two rectangles
-          // defines a pair of translation intervals in each dimension through which
-          // the layer should not move relative to the scroll container. To
-          // accomplish this, we limit each dimension of the |translation| to that
-          // part of it which overlaps those intervals.
-          const LayerRect& stickyOuter = layer->GetStickyScrollRangeOuter();
-          const LayerRect& stickyInner = layer->GetStickyScrollRangeInner();
+    LayerPoint originalTranslation = translation;
+    translation.y = IntervalOverlap(translation.y, stickyOuter.y, stickyOuter.YMost()) -
+                    IntervalOverlap(translation.y, stickyInner.y, stickyInner.YMost());
+    translation.x = IntervalOverlap(translation.x, stickyOuter.x, stickyOuter.XMost()) -
+                    IntervalOverlap(translation.x, stickyInner.x, stickyInner.XMost());
+    unconsumedTranslation = translation - originalTranslation;
+  }
+
+  // Finally, apply the translation to the layer transform. Note that in cases
+  // where the async transform on |aTransformedSubtreeRoot| affects this layer's
+  // clip rect, we need to apply the same translation to said clip rect, so
+  // that the effective transform on the clip rect takes it back to where it was
+  // originally, had there been no async scroll.
+  TranslateShadowLayer(layer, ViewAs<ParentLayerPixel>(translation,
+      PixelCastJustification::NoTransformOnLayer), true, aClipPartsCache);
 
-          // TODO: There's a unit mismatch here, as |translation| is in ParentLayer
-          //       space while |stickyOuter| and |stickyInner| are in Layer space.
-          ParentLayerPoint originalTranslation = translation;
-          translation.y = IntervalOverlap(translation.y, stickyOuter.y, stickyOuter.YMost()) -
-                          IntervalOverlap(translation.y, stickyInner.y, stickyInner.YMost());
-          translation.x = IntervalOverlap(translation.x, stickyOuter.x, stickyOuter.XMost()) -
-                          IntervalOverlap(translation.x, stickyInner.x, stickyInner.XMost());
-          if (translation != originalTranslation) {
-            translationConsumed = false;
-          }
-        }
+  // Propragate the unconsumed portion of the translation to descendant
+  // fixed/sticky layers.
+  if (unconsumedTranslation != LayerPoint()) {
+    // Take the computations we performed to derive |translation| from
+    // |aCurrentTransformForRoot|, and perform them in reverse, keeping other
+    // quantities fixed, to come up with a new transform |newTransform| that
+    // would produce |unconsumedTranslation|.
+    LayerPoint newTransformedAnchor = unconsumedTranslation + anchor;
+    ParentLayerPoint newTransformedAnchorInSubtreeRootSpace =
+        aPreviousTransformForRoot.TransformPoint(newTransformedAnchor);
+    LayerToParentLayerMatrix4x4 newTransform = aPreviousTransformForRoot;
+    newTransform.PostTranslate(newTransformedAnchorInSubtreeRootSpace -
+                               offsetAnchorInSubtreeRootSpace);
 
-        // Finally, apply the translation to the layer transform. Note that in cases
-        // where the async transform on |aTransformedSubtreeRoot| affects this layer's
-        // clip rect, we need to apply the same translation to said clip rect, so
-        // that the effective transform on the clip rect takes it back to where it was
-        // originally, had there been no async scroll.
-        TranslateShadowLayer(layer, ThebesPoint(translation.ToUnknownPoint()),
-            true, aClipPartsCache);
+    // Propagate this new transform to our descendants as the new value of
+    // |aCurrentTransformForRoot|. This allows them to consume the unconsumed
+    // translation.
+    for (Layer* child = layer->GetFirstChild(); child; child = child->GetNextSibling()) {
+      AlignFixedAndStickyLayers(aTransformedSubtreeRoot, child, aTransformScrollId,
+          aPreviousTransformForRoot, newTransform, aFixedLayerMargins, aClipPartsCache);
+    }
+  }
 
-        // If we didn't consume the entire translation, continue the traversal
-        // to allow a descendant fixed or sticky layer to consume the rest.
-        // TODO: We curently don't handle the case where we consume part but not
-        //       all of the translation correctly. In such a case,
-        //       |a[Previous|Current]TransformForRoot| would need to be adjusted
-        //       to reflect only the unconsumed part of the translation.
-        return translationConsumed ? TraversalFlag::Skip : TraversalFlag::Continue;
-      });
+  return;
 }
 
 static void
 SampleValue(float aPortion, Animation& aAnimation,
             const StyleAnimationValue& aStart, const StyleAnimationValue& aEnd,
             const StyleAnimationValue& aLastValue, uint64_t aCurrentIteration,
             Animatable* aValue, Layer* aLayer)
 {
@@ -847,17 +868,17 @@ MoveScrollbarForLayerMargin(Layer* aRoot
               aNode->GetScrollbarTargetContainerId() == aRootScrollId);
     });
   if (scrollbar) {
     // Shift the horizontal scrollbar down into the new space exposed by the
     // dynamic toolbar hiding. Technically we should also scale the vertical
     // scrollbar a bit to expand into the new space but it's not as noticeable
     // and it would add a lot more complexity, so we're going with the "it's not
     // worth it" justification.
-    TranslateShadowLayer(scrollbar, gfxPoint(0, -aFixedLayerMargins.bottom), true, nullptr);
+    TranslateShadowLayer(scrollbar, ParentLayerPoint(0, -aFixedLayerMargins.bottom), true, nullptr);
     if (scrollbar->GetParent()) {
       // The layer that has the HORIZONTAL direction sits inside another
       // ContainerLayer. This ContainerLayer also has a clip rect that causes
       // the scrollbar to get clipped. We need to expand that clip rect to
       // prevent that from happening. This is kind of ugly in that we're
       // assuming a particular layer tree structure but short of adding more
       // flags to the layer there doesn't appear to be a good way to do this.
       ExpandRootClipRect(scrollbar->GetParent(), aFixedLayerMargins);
@@ -1039,17 +1060,17 @@ AsyncCompositionManager::ApplyAsyncConte
           // effects apply to fixed and sticky layers. We do this by using
           // GetTransform() as the base transform rather than GetLocalTransform(),
           // which would include those factors.
           LayerToParentLayerMatrix4x4 transformWithoutOverscrollOrOmta =
               layer->GetTransformTyped()
             * CompleteAsyncTransform(
                 AdjustForClip(asyncTransformWithoutOverscroll, layer));
 
-          AlignFixedAndStickyLayers(layer, metrics.GetScrollId(), oldTransform,
+          AlignFixedAndStickyLayers(layer, layer, metrics.GetScrollId(), oldTransform,
                                     transformWithoutOverscrollOrOmta, fixedLayerMargins,
                                     &clipPartsCache);
 
           // Combine the local clip with the ancestor scrollframe clip. This is not
           // included in the async transform above, since the ancestor clip should not
           // move with this APZC.
           if (scrollMetadata.HasScrollClip()) {
             ParentLayerIntRect clip = scrollMetadata.ScrollClip().GetClipRect();
@@ -1483,17 +1504,17 @@ AsyncCompositionManager::TransformScroll
   if (mContentRect.height * userZoom.scale < metrics.GetCompositionBounds().height) {
     underZoomScale.height = (mContentRect.height * userZoom.scale) /
       metrics.GetCompositionBounds().height;
   }
   oldTransform.PreScale(underZoomScale.width, underZoomScale.height, 1);
 
   // Make sure fixed position layers don't move away from their anchor points
   // when we're asynchronously panning or zooming
-  AlignFixedAndStickyLayers(aLayer, metrics.GetScrollId(), oldTransform,
+  AlignFixedAndStickyLayers(aLayer, aLayer, metrics.GetScrollId(), oldTransform,
                             aLayer->GetLocalTransformTyped(),
                             fixedLayerMargins, nullptr);
 
   ExpandRootClipRect(aLayer, fixedLayerMargins);
 }
 
 void
 AsyncCompositionManager::GetFrameUniformity(FrameUniformityData* aOutData)
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -167,35 +167,37 @@ private:
                         const CSSToLayerScale& aPaintedResolution,
                         bool aLayersUpdated,
                         int32_t aPaintSyncId,
                         ScreenMargin& aFixedLayerMargins);
 
   /**
    * Adds a translation to the transform of any fixed position (whose parent
    * layer is not fixed) or sticky position layer descendant of
-   * aTransformedSubtreeRoot. The translation is chosen so that the layer's
-   * anchor point relative to aTransformedSubtreeRoot's parent layer is the same
-   * as it was when aTransformedSubtreeRoot's GetLocalTransform() was
-   * aPreviousTransformForRoot. aCurrentTransformForRoot is
-   * aTransformedSubtreeRoot's current GetLocalTransform() modulo any
+   * |aTransformedSubtreeRoot|. The translation is chosen so that the layer's
+   * anchor point relative to |aTransformedSubtreeRoot|'s parent layer is the same
+   * as it was when |aTransformedSubtreeRoot|'s GetLocalTransform() was
+   * |aPreviousTransformForRoot|. |aCurrentTransformForRoot| is
+   * |aTransformedSubtreeRoot|'s current GetLocalTransform() modulo any
    * overscroll-related transform, which we don't want to adjust for.
    * For sticky position layers, the translation is further intersected with
    * the layer's sticky scroll ranges.
    * This function will also adjust layers so that the given content document
    * fixed position margins will be respected during asynchronous panning and
    * zooming.
-   * aTransformAffectsLayerClip indicates whether the transform on
-   * aTransformedSubtreeRoot affects aLayer's clip rects, so we know
-   * whether we need to perform a corresponding unadjustment to keep
-   * the clip rect fixed.
-   * aClipPartsCache optionally maps layers to separate fixed and scrolled
+   * |aTransformScrollId| is the scroll id of the scroll frame that scrolls
+   * |aTransformedSubtreeRoot|.
+   * |aClipPartsCache| optionally maps layers to separate fixed and scrolled
    * clips, so we can only adjust the fixed portion.
+   * This function has a recursive implementation; aStartTraversalAt specifies
+   * where to start the current recursion of the traversal. For the initial
+   * call, it should be the same as aTrasnformedSubtreeRoot.
    */
   void AlignFixedAndStickyLayers(Layer* aTransformedSubtreeRoot,
+                                 Layer* aStartTraversalAt,
                                  FrameMetrics::ViewID aTransformScrollId,
                                  const LayerToParentLayerMatrix4x4& aPreviousTransformForRoot,
                                  const LayerToParentLayerMatrix4x4& aCurrentTransformForRoot,
                                  const ScreenMargin& aFixedLayerMargins,
                                  ClipPartsCache* aClipPartsCache);
 
   /**
    * DRAWING PHASE ONLY
--- a/ipc/chromium/src/base/process_util.h
+++ b/ipc/chromium/src/base/process_util.h
@@ -69,32 +69,35 @@ struct kinfo_proc;
 namespace base {
 
 // These can be used in a 32-bit bitmask.
 enum ProcessArchitecture {
   PROCESS_ARCH_I386 = 0x1,
   PROCESS_ARCH_X86_64 = 0x2,
   PROCESS_ARCH_PPC = 0x4,
   PROCESS_ARCH_ARM = 0x8,
-  PROCESS_ARCH_MIPS = 0x10
+  PROCESS_ARCH_MIPS = 0x10,
+  PROCESS_ARCH_ARM64 = 0x20
 };
 
 inline ProcessArchitecture GetCurrentProcessArchitecture()
 {
   base::ProcessArchitecture currentArchitecture;
 #if defined(ARCH_CPU_X86)
   currentArchitecture = base::PROCESS_ARCH_I386;
 #elif defined(ARCH_CPU_X86_64)
   currentArchitecture = base::PROCESS_ARCH_X86_64;
 #elif defined(ARCH_CPU_PPC)
   currentArchitecture = base::PROCESS_ARCH_PPC;
 #elif defined(ARCH_CPU_ARMEL)
   currentArchitecture = base::PROCESS_ARCH_ARM;
 #elif defined(ARCH_CPU_MIPS)
   currentArchitecture = base::PROCESS_ARCH_MIPS;
+#elif defined(ARCH_CPU_ARM64)
+  currentArchitecture = base::PROCESS_ARCH_ARM64;
 #endif
   return currentArchitecture;
 }
 
 // A minimalistic but hopefully cross-platform set of exit codes.
 // Do not change the enumeration values or you will break third-party
 // installers.
 enum {
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -2,46 +2,56 @@
 # 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/.
 
 EXPORTS.mozilla.mscom += [
     'COMApartmentRegion.h',
     'COMPtrHolder.h',
-    'DispatchForwarder.h',
     'EnsureMTA.h',
-    'Interceptor.h',
-    'InterceptorLog.h',
-    'MainThreadHandoff.h',
-    'MainThreadInvoker.h',
     'MainThreadRuntime.h',
     'ProxyStream.h',
     'Ptr.h',
-    'Registration.h',
     'Utils.h',
-    'WeakRef.h',
 ]
 
 SOURCES += [
-    'Interceptor.cpp',
-    'Registration.cpp',
     'Utils.cpp',
-    'WeakRef.cpp',
 ]
 
 UNIFIED_SOURCES += [
-    'DispatchForwarder.cpp',
     'EnsureMTA.cpp',
-    'InterceptorLog.cpp',
-    'MainThreadHandoff.cpp',
-    'MainThreadInvoker.cpp',
     'MainThreadRuntime.cpp',
     'ProxyStream.cpp',
 ]
 
+if CONFIG['ACCESSIBILITY']:
+    EXPORTS.mozilla.mscom += [
+        'DispatchForwarder.h',
+        'Interceptor.h',
+        'InterceptorLog.h',
+        'MainThreadHandoff.h',
+        'MainThreadInvoker.h',
+        'Registration.h',
+        'WeakRef.h',
+    ]
+
+    SOURCES += [
+        'Interceptor.cpp',
+        'Registration.cpp',
+        'WeakRef.cpp',
+    ]
+
+    UNIFIED_SOURCES += [
+        'DispatchForwarder.cpp',
+        'InterceptorLog.cpp',
+        'MainThreadHandoff.cpp',
+        'MainThreadInvoker.cpp',
+    ]
+
 LOCAL_INCLUDES += [
     '/xpcom/build',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/layout/base/UnitTransforms.h
+++ b/layout/base/UnitTransforms.h
@@ -47,17 +47,20 @@ enum class PixelCastJustification : uint
   // some widget/tab dimension information as the OS does -- in screen units.
   LayoutDeviceIsScreenForTabDims,
   // A combination of LayoutDeviceIsScreenForBounds and
   // ScreenIsParentLayerForRoot, which is how we're using it.
   LayoutDeviceIsParentLayerForRCDRSF,
   // Used to treat the product of AsyncTransformComponentMatrix objects
   // as an AsyncTransformMatrix. See the definitions of these matrices in
   // LayersTypes.h for details.
-  MultipleAsyncTransforms
+  MultipleAsyncTransforms,
+  // We have reason to believe a layer doesn't have a local transform.
+  // Should only be used if we've already checked or asserted this.
+  NoTransformOnLayer
 };
 
 template <class TargetUnits, class SourceUnits>
 gfx::CoordTyped<TargetUnits> ViewAs(const gfx::CoordTyped<SourceUnits>& aCoord, PixelCastJustification) {
   return gfx::CoordTyped<TargetUnits>(aCoord.value);
 }
 template <class TargetUnits, class SourceUnits>
 gfx::SizeTyped<TargetUnits> ViewAs(const gfx::SizeTyped<SourceUnits>& aSize, PixelCastJustification) {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -7498,21 +7498,19 @@ nsLayoutUtils::SurfaceFromElement(HTMLVi
                                   RefPtr<DrawTarget>& aTarget)
 {
   SurfaceFromElementResult result;
 
   NS_WARNING_ASSERTION(
     (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) == 0,
     "We can't support non-premultiplied alpha for video!");
 
-#ifdef MOZ_EME
   if (aElement->ContainsRestrictedContent()) {
     return result;
   }
-#endif
 
   uint16_t readyState;
   if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) &&
       (readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING ||
        readyState == nsIDOMHTMLMediaElement::HAVE_METADATA)) {
     result.mIsStillLoading = true;
     return result;
   }
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -4222,17 +4222,17 @@ ScrollFrameHelper::ScrollToRestoredPosit
           (mScrollPort.XMost() - scrollToPos.x - mScrolledFrame->GetRect().width);
       }
       nsWeakFrame weakFrame(mOuter);
       ScrollToWithOrigin(scrollToPos, nsIScrollableFrame::INSTANT,
                          nsGkAtoms::restore, nullptr);
       if (!weakFrame.IsAlive()) {
         return;
       }
-      if (PageIsStillLoading()) {
+      if (PageIsStillLoading() || NS_SUBTREE_DIRTY(mOuter)) {
         // If we're trying to do a history scroll restore, then we want to
         // keep trying this until we succeed, because the page can be loading
         // incrementally. So re-get the scroll position for the next iteration,
         // it might not be exactly equal to mRestorePos due to rounding and
         // clamping.
         mLastPos = GetLogicalScrollPosition();
         return;
       }
--- a/layout/generic/test/mochitest.ini
+++ b/layout/generic/test/mochitest.ini
@@ -142,8 +142,9 @@ support-files = selection_expanding_xbl.
 skip-if = buildapp == 'mulet'
 [test_selection_splitText-normalize.html]
 [test_selection_touchevents.html]
 [test_taintedfilters.html]
 support-files = file_taintedfilters_feDisplacementMap-tainted-1.svg file_taintedfilters_feDisplacementMap-tainted-2.svg file_taintedfilters_feDisplacementMap-tainted-3.svg file_taintedfilters_feDisplacementMap-tainted-ref.svg file_taintedfilters_feDisplacementMap-untainted-ref.svg file_taintedfilters_feDisplacementMap-untainted-1.svg file_taintedfilters_feDisplacementMap-untainted-2.svg file_taintedfilters_red-flood-for-feImage-cors.svg file_taintedfilters_red-flood-for-feImage-cors.svg^headers^ file_taintedfilters_red-flood-for-feImage.svg
 [test_scroll_position_restore.html]
 support-files = file_scroll_position_restore.html
 [test_scroll_animation_restore.html]
+[test_scroll_position_iframe.html]
new file mode 100644
--- /dev/null
+++ b/layout/generic/test/test_scroll_position_iframe.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1305579
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1305579</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1305579">Mozilla Bug 1305579</a>
+<p id="display"></p>
+<style>
+.off {
+  display:none;
+}
+</style>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+function runtest() {
+  var iframe = document.getElementById("iframe");
+  iframe.contentDocument.scrollingElement.scrollTop = 50;
+  iframe.classList.toggle("off");
+  waitForAllPaintsFlushed(function() {
+    iframe.classList.toggle("off");
+    is(iframe.contentDocument.scrollingElement.scrollTop, 50, "scroll position restored");
+    SimpleTest.finish();
+  });
+}
+</script>
+<iframe onload="runtest()" id="iframe" class="" src="data:text/html,<p>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br>bla<br></p>"></iframe><br>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/position-fixed-inside-sticky-2-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<style>
+body {
+  height: 4000px;
+  margin: 0;
+  overflow: hidden;
+}
+#fixed {
+  position: fixed;
+  top: 50px;
+  left: 50px;
+  width: 100px;
+  height: 100px;
+  box-sizing: border-box;
+  border: 1px solid blue;
+}
+</style>
+<div id="fixed"></div>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/position-fixed-inside-sticky-2.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html reftest-async-scroll
+      reftest-displayport-x="0" reftest-displayport-y="0"
+      reftest-displayport-w="800" reftest-displayport-h="2000"
+      reftest-async-scroll-x="0" reftest-async-scroll-y="50">
+<style>
+body {
+  height: 4000px;
+  margin: 0;
+  overflow: hidden;
+}
+#sticky {
+  position: sticky;
+  top: 25px; /* scrolls regularly until the top edge hits 25px, then stays fixed */
+  margin-top: 50px;
+  height: 1200px;
+  border-bottom: 1px solid black;
+}
+#fixed {
+  position: fixed;
+  top: 50px;
+  left: 50px;
+  width: 100px;
+  height: 100px;
+  box-sizing: border-box;
+  border: 1px solid blue;
+}
+</style>
+<div id="sticky">
+  <div id="fixed"></div>
+</div>
+</html>
--- a/layout/reftests/async-scrolling/reftest.list
+++ b/layout/reftests/async-scrolling/reftest.list
@@ -26,16 +26,17 @@ skip-if(!asyncPan) == split-layers-multi
 fuzzy-if(skiaContent,2,240000) fuzzy-if(browserIsRemote&&!skiaContent&&(cocoaWidget||winWidget),1,240000) skip-if(!asyncPan) == split-opacity-layers-1.html split-opacity-layers-1-ref.html
 skip-if(!asyncPan) == sticky-pos-scrollable-1.html sticky-pos-scrollable-1-ref.html
 skip-if(!asyncPan) == fixed-pos-scrollable-1.html fixed-pos-scrollable-1-ref.html
 skip-if(!asyncPan) == culling-1.html culling-1-ref.html
 skip-if(!asyncPan) == position-fixed-iframe-1.html position-fixed-iframe-1-ref.html
 skip-if(!asyncPan) == position-fixed-iframe-2.html position-fixed-iframe-2-ref.html
 fuzzy-if(skiaContent,1,11300) skip-if(!asyncPan) == position-fixed-in-scroll-container.html position-fixed-in-scroll-container-ref.html
 skip-if(!asyncPan) == position-fixed-inside-sticky-1.html position-fixed-inside-sticky-1-ref.html
+skip-if(!asyncPan) == position-fixed-inside-sticky-2.html position-fixed-inside-sticky-2-ref.html
 fuzzy(1,60000) skip-if(!asyncPan) == group-opacity-surface-size-1.html group-opacity-surface-size-1-ref.html
 skip-if(!asyncPan) == position-sticky-transformed.html position-sticky-transformed-ref.html
 skip-if(!asyncPan) == offscreen-prerendered-active-opacity.html offscreen-prerendered-active-opacity-ref.html
 fuzzy-if(Android,6,4) fuzzy-if(skiaContent&&!Android,1,34) skip-if(!asyncPan) == offscreen-clipped-blendmode-1.html offscreen-clipped-blendmode-ref.html
 fuzzy-if(Android,6,4) skip-if(!asyncPan) == offscreen-clipped-blendmode-2.html offscreen-clipped-blendmode-ref.html
 fuzzy-if(Android,6,4) skip == offscreen-clipped-blendmode-3.html offscreen-clipped-blendmode-ref.html # bug 1251588 - wrong AGR on mix-blend-mode item
 fuzzy-if(Android,6,4) skip-if(!asyncPan) == offscreen-clipped-blendmode-4.html offscreen-clipped-blendmode-ref.html
 fuzzy-if(Android,7,4) skip-if(!asyncPan) == perspective-scrolling-1.html perspective-scrolling-1-ref.html
--- a/layout/reftests/w3c-css/import-tests.py
+++ b/layout/reftests/w3c-css/import-tests.py
@@ -246,18 +246,19 @@ def copy_and_prefix(test, aSourceFileNam
         searchRegex = "\s*<style\s*"
 
         if not isSupportFile and not ahemFontAdded and 'ahem' in gTestFlags[test] and re.search(searchRegex, line):
             # First put our ahem font declation before the first <style>
             # element
             newFile.write(AHEM_DECL_HTML if is_html(aDestFileName) else AHEM_DECL_XML)
             ahemFontAdded = True
 
-        for rule in aProps:
-            replacementLine = replacementLine.replace(rule, "-moz-" + rule)
+        for prop in aProps:
+            replacementLine = re.sub(r"([^-#]|^)" + prop + r"\b", r"\1-moz-" + prop, replacementLine)
+
         newFile.write(replacementLine)
 
     newFile.close()
     unPrefixedFile.close()
 
 def read_options():
     global gArgs, gOptions
     op = OptionParser()
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -1097,28 +1097,16 @@ InitSystemMetrics()
 
   metricResult =
     LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollSliderStyle);
   if (metricResult != LookAndFeel::eScrollThumbStyle_Normal) {
     sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_thumb_proportional);
   }
 
   metricResult =
-    LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInMenus);
-  if (metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::images_in_menus);
-  }
-
-  metricResult =
-    LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInButtons);
-  if (metricResult) {
-    sSystemMetrics->AppendElement(nsGkAtoms::images_in_buttons);
-  }
-
-  metricResult =
     LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars);
   if (metricResult) {
     sSystemMetrics->AppendElement(nsGkAtoms::overlay_scrollbars);
   }
 
   metricResult =
     LookAndFeel::GetInt(LookAndFeel::eIntID_MenuBarDrag);
   if (metricResult) {
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -672,32 +672,16 @@ nsMediaFeatures::features[] = {
     &nsGkAtoms::_moz_scrollbar_thumb_proportional,
     nsMediaFeature::eMinMaxNotAllowed,
     nsMediaFeature::eBoolInteger,
     nsMediaFeature::eNoRequirements,
     { &nsGkAtoms::scrollbar_thumb_proportional },
     GetSystemMetric
   },
   {
-    &nsGkAtoms::_moz_images_in_menus,
-    nsMediaFeature::eMinMaxNotAllowed,
-    nsMediaFeature::eBoolInteger,
-    nsMediaFeature::eNoRequirements,
-    { &nsGkAtoms::images_in_menus },
-    GetSystemMetric
-  },
-  {
-    &nsGkAtoms::_moz_images_in_buttons,
-    nsMediaFeature::eMinMaxNotAllowed,
-    nsMediaFeature::eBoolInteger,
-    nsMediaFeature::eNoRequirements,
-    { &nsGkAtoms::images_in_buttons },
-    GetSystemMetric
-  },
-  {
     &nsGkAtoms::_moz_overlay_scrollbars,
     nsMediaFeature::eMinMaxNotAllowed,
     nsMediaFeature::eBoolInteger,
     nsMediaFeature::eNoRequirements,
     { &nsGkAtoms::overlay_scrollbars },
     GetSystemMetric
   },
   {
--- a/layout/style/test/chrome/bug418986-2.js
+++ b/layout/style/test/chrome/bug418986-2.js
@@ -32,17 +32,16 @@ var expected_values = [
                                 "landscape" : "portrait",
                               window.innerWidth > window.innerHeight ?
                                 "landscape" : "portrait"]
 ];
 
 // These media queries return value 0 or 1 when the pref is off.
 // When the pref is on, they should not match.
 var suppressed_toggles = [
-  "-moz-images-in-menus",
   "-moz-mac-graphite-theme",
   // Not available on most OSs.
 //  "-moz-maemo-classic",
   "-moz-scrollbar-end-backward",
   "-moz-scrollbar-end-forward",
   "-moz-scrollbar-start-backward",
   "-moz-scrollbar-start-forward",
   "-moz-scrollbar-thumb-proportional",
--- a/layout/style/test/test_media_queries.html
+++ b/layout/style/test/test_media_queries.html
@@ -619,86 +619,76 @@ function run() {
   should_not_apply("(grid: -1)");
 
   // System metrics
   expression_should_be_parseable("-moz-scrollbar-start-backward");
   expression_should_be_parseable("-moz-scrollbar-start-forward");
   expression_should_be_parseable("-moz-scrollbar-end-backward");
   expression_should_be_parseable("-moz-scrollbar-end-forward");
   expression_should_be_parseable("-moz-scrollbar-thumb-proportional");
-  expression_should_be_parseable("-moz-images-in-menus");
-  expression_should_be_parseable("-moz-images-in-buttons");
   expression_should_be_parseable("-moz-overlay-scrollbars");
   expression_should_be_parseable("-moz-windows-default-theme");
   expression_should_be_parseable("-moz-mac-graphite-theme");
   expression_should_be_parseable("-moz-mac-yosemite-theme");
   expression_should_be_parseable("-moz-windows-compositor");
   expression_should_be_parseable("-moz-windows-classic");
   expression_should_be_parseable("-moz-windows-glass");
   expression_should_be_parseable("-moz-touch-enabled");
   expression_should_be_parseable("-moz-swipe-animation-enabled");
 
   expression_should_be_parseable("-moz-scrollbar-start-backward: 0");
   expression_should_be_parseable("-moz-scrollbar-start-forward: 0");
   expression_should_be_parseable("-moz-scrollbar-end-backward: 0");
   expression_should_be_parseable("-moz-scrollbar-end-forward: 0");
   expression_should_be_parseable("-moz-scrollbar-thumb-proportional: 0");
-  expression_should_be_parseable("-moz-images-in-menus: 0");
-  expression_should_be_parseable("-moz-images-in-buttons: 0");
   expression_should_be_parseable("-moz-overlay-scrollbars: 0");
   expression_should_be_parseable("-moz-windows-default-theme: 0");
   expression_should_be_parseable("-moz-mac-graphite-theme: 0");
   expression_should_be_parseable("-moz-mac-yosemite-theme: 0");
   expression_should_be_parseable("-moz-windows-compositor: 0");
   expression_should_be_parseable("-moz-windows-classic: 0");
   expression_should_be_parseable("-moz-windows-glass: 0");
   expression_should_be_parseable("-moz-touch-enabled: 0");
   expression_should_be_parseable("-moz-swipe-animation-enabled: 0");
 
   expression_should_be_parseable("-moz-scrollbar-start-backward: 1");
   expression_should_be_parseable("-moz-scrollbar-start-forward: 1");
   expression_should_be_parseable("-moz-scrollbar-end-backward: 1");
   expression_should_be_parseable("-moz-scrollbar-end-forward: 1");
   expression_should_be_parseable("-moz-scrollbar-thumb-proportional: 1");
-  expression_should_be_parseable("-moz-images-in-menus: 1");
-  expression_should_be_parseable("-moz-images-in-buttons: 1");
   expression_should_be_parseable("-moz-overlay-scrollbars: 1");
   expression_should_be_parseable("-moz-windows-default-theme: 1");
   expression_should_be_parseable("-moz-mac-graphite-theme: 1");
   expression_should_be_parseable("-moz-mac-yosemite-theme: 1");
   expression_should_be_parseable("-moz-windows-compositor: 1");
   expression_should_be_parseable("-moz-windows-classic: 1");
   expression_should_be_parseable("-moz-windows-glass: 1");
   expression_should_be_parseable("-moz-touch-enabled: 1");
   expression_should_be_parseable("-moz-swipe-animation-enabled: 1");
 
   expression_should_not_be_parseable("-moz-scrollbar-start-backward: -1");
   expression_should_not_be_parseable("-moz-scrollbar-start-forward: -1");
   expression_should_not_be_parseable("-moz-scrollbar-end-backward: -1");
   expression_should_not_be_parseable("-moz-scrollbar-end-forward: -1");
   expression_should_not_be_parseable("-moz-scrollbar-thumb-proportional: -1");
-  expression_should_not_be_parseable("-moz-images-in-menus: -1");
-  expression_should_not_be_parseable("-moz-images-in-buttons: -1");
   expression_should_not_be_parseable("-moz-overlay-scrollbars: -1");
   expression_should_not_be_parseable("-moz-windows-default-theme: -1");
   expression_should_not_be_parseable("-moz-mac-graphite-theme: -1");
   expression_should_not_be_parseable("-moz-mac-yosemite-theme: -1");
   expression_should_not_be_parseable("-moz-windows-compositor: -1");
   expression_should_not_be_parseable("-moz-windows-classic: -1");
   expression_should_not_be_parseable("-moz-windows-glass: -1");
   expression_should_not_be_parseable("-moz-touch-enabled: -1");
   expression_should_not_be_parseable("-moz-swipe-animation-enabled: -1");
 
   expression_should_not_be_parseable("-moz-scrollbar-start-backward: true");
   expression_should_not_be_parseable("-moz-scrollbar-start-forward: true");
   expression_should_not_be_parseable("-moz-scrollbar-end-backward: true");
   expression_should_not_be_parseable("-moz-scrollbar-end-forward: true");
   expression_should_not_be_parseable("-moz-scrollbar-thumb-proportional: true");
-  expression_should_not_be_parseable("-moz-images-in-menus: true");
-  expression_should_not_be_parseable("-moz-images-in-buttons: true");
   expression_should_not_be_parseable("-moz-overlay-scrollbars: true");
   expression_should_not_be_parseable("-moz-windows-default-theme: true");
   expression_should_not_be_parseable("-moz-mac-graphite-theme: true");
   expression_should_not_be_parseable("-moz-mac-yosemite-theme: true");
   expression_should_not_be_parseable("-moz-windows-compositor: true");
   expression_should_not_be_parseable("-moz-windows-classic: true");
   expression_should_not_be_parseable("-moz-windows-glass: true");
   expression_should_not_be_parseable("-moz-touch-enabled: true");
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -397,31 +397,40 @@ all_resources = \
 # All of generated/org/mozilla/gecko/R.java, gecko.ap_, and R.txt are
 # produced by aapt; this saves aapt invocations.  The trailing
 # semi-colon defines an empty recipe; defining no recipe at all causes
 # Make to treat the target differently, in a way that defeats our
 # dependencies.
 
 generated/org/mozilla/gecko/R.java: .aapt.deps ;
 
+# Only add libraries that contain resources here. We (unecessarily) generate an identical R.java which
+# is copied into each of these locations, and each of these files contains thousands of fields.
+# Each unnecessary copy therefore wastes unnecessary fields in the output dex file.
+# Note: usually proguard will help clean this up after the fact, but having too many fields will cause
+# dexing to fail, regardless of any later optimisations proguard could later make to bring us back
+# under the limit.
+# Ideally we would fix our aapt invocations to correctly generate minimal copies of R.java for each
+# package, but that seems redundant since gradle builds are able to correctly generate these files.
+
 # If native devices are enabled, add Google Play Services, build their resources
-generated/android/support/v4/R.java: .aapt.deps ;
+# (no resources) generated/android/support/v4/R.java: .aapt.deps ;
 generated/android/support/v7/appcompat/R.java: .aapt.deps ;
 generated/android/support/v7/cardview/R.java: .aapt.deps ;
 generated/android/support/design/R.java: .aapt.deps ;
 generated/android/support/v7/mediarouter/R.java: .aapt.deps ;
 generated/android/support/v7/recyclerview/R.java: .aapt.deps ;
-generated/android/support/customtabs/R.java: .aapt.deps ;
-generated/android/support/v7/palette/R.java: .aapt.deps ;
+# (no resources) generated/android/support/customtabs/R.java: .aapt.deps ;
+# (no resources) generated/android/support/v7/palette/R.java: .aapt.deps ;
 generated/com/google/android/gms/R.java: .aapt.deps ;
 generated/com/google/android/gms/ads/R.java: .aapt.deps ;
 generated/com/google/android/gms/base/R.java: .aapt.deps ;
 generated/com/google/android/gms/cast/R.java: .aapt.deps ;
-generated/com/google/android/gms/gcm/R.java: .aapt.deps ;
-generated/com/google/android/gms/measurement/R.java: .aapt.deps ;
+# (no resources) generated/com/google/android/gms/gcm/R.java: .aapt.deps ;
+# (no resources) generated/com/google/android/gms/measurement/R.java: .aapt.deps ;
 
 gecko.ap_: .aapt.deps ;
 R.txt: .aapt.deps ;
 
 # [Comment 2/3] This tom-foolery provides a target that forces a
 # rebuild of gecko.ap_.  This is used during packaging to ensure that
 # resources are fresh.  The alternative would be complicated; see
 # [Comment 1/3].
--- a/mobile/android/base/java/org/mozilla/gecko/delegates/OfflineTabStatusDelegate.java
+++ b/mobile/android/base/java/org/mozilla/gecko/delegates/OfflineTabStatusDelegate.java
@@ -58,19 +58,29 @@ public class OfflineTabStatusDelegate ex
             return;
         }
 
         // Ignore tabs displaying about pages
         if (AboutPages.isAboutPage(tab.getURL())) {
             return;
         }
 
+        // We only want to show these notifications for tabs that were loaded successfully.
+        if (tab.getState() != Tab.STATE_SUCCESS) {
+            return;
+        }
+
         switch (event) {
-            // Show offline notification if tab is visible, or queue it for display later.
-            case PAGE_SHOW:
+            // We listen specifically for the STOP event (as opposed to PAGE_SHOW), because we need
+            // to know if page load actually succeeded. When STOP is triggered, tab.getState()
+            // will return definitive STATE_SUCCESS or STATE_ERROR. When PAGE_SHOW is triggered,
+            // tab.getState() will return STATE_LOADING, which is ambiguous for our purposes.
+            // We don't want to show these notifications for 404 pages, for example. See Bug 1304914.
+            case STOP:
+                // Show offline notification if tab is visible, or queue it for display later.
                 if (!isTabsTrayVisible() && Tabs.getInstance().isSelectedTab(tab)) {
                     showLoadedOfflineSnackbar(activityReference.get());
                 } else {
                     tabsQueuedForOfflineSnackbar.put(tab, null);
                 }
                 break;
             // Fallthrough; see Bug 1278980 for details on why this event is here.
             case OPENED_FROM_TABS_TRAY:
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
@@ -265,17 +265,17 @@ public class CombinedHistoryPanel extend
             mSavedRestoreBundle = data;
         }
     }
 
     private void setPanelStateFromBundle(Bundle data) {
         if (data != null && data.getBoolean("goToRecentTabs", false) && mPanelLevel != PanelLevel.CHILD_RECENT_TABS) {
             mPanelLevel = PanelLevel.CHILD_RECENT_TABS;
             mRecyclerView.swapAdapter(mRecentTabsAdapter, true);
-            updateEmptyView();
+            updateEmptyView(PanelLevel.CHILD_RECENT_TABS);
             updateButtonFromLevel();
         }
     }
 
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
         mCursorLoaderCallbacks = new CursorLoaderCallbacks();
@@ -339,46 +339,47 @@ public class CombinedHistoryPanel extend
         }
 
         @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
             final int loaderId = loader.getId();
             switch (loaderId) {
                 case LOADER_ID_HISTORY:
                     mHistoryAdapter.setHistory(c);
+                    updateEmptyView(PanelLevel.PARENT);
                     break;
 
                 case LOADER_ID_REMOTE:
                     final List<RemoteClient> clients = mDB.getTabsAccessor().getClientsFromCursor(c);
                     mHistoryAdapter.getDeviceUpdateHandler().onDeviceCountUpdated(clients.size());
                     mClientsAdapter.setClients(clients);
+                    updateEmptyView(PanelLevel.CHILD_SYNC);
                     break;
             }
 
-            updateEmptyView();
             updateButtonFromLevel();
         }
 
         @Override
         public void onLoaderReset(Loader<Cursor> loader) {
             mClientsAdapter.setClients(Collections.<RemoteClient>emptyList());
             mHistoryAdapter.setHistory(null);
         }
     }
 
     public interface PanelStateUpdateHandler {
-        void onPanelStateUpdated();
+        void onPanelStateUpdated(PanelLevel level);
     }
 
     public PanelStateUpdateHandler getPanelStateUpdateHandler() {
         if (mPanelStateUpdateHandler == null) {
             mPanelStateUpdateHandler = new PanelStateUpdateHandler() {
                 @Override
-                public void onPanelStateUpdated() {
-                    updateEmptyView();
+                public void onPanelStateUpdated(PanelLevel level) {
+                    updateEmptyView(level);
                     updateButtonFromLevel();
                 }
             };
         }
         return mPanelStateUpdateHandler;
     }
 
     protected class OnLevelChangeListener implements OnPanelLevelChangeListener {
@@ -398,17 +399,17 @@ public class CombinedHistoryPanel extend
                     mRecyclerView.swapAdapter(mClientsAdapter, true);
                     mRefreshLayout.setEnabled(mClientsAdapter.getClientsCount() > 0);
                     break;
                 case CHILD_RECENT_TABS:
                     mRecyclerView.swapAdapter(mRecentTabsAdapter, true);
                     break;
             }
 
-            updateEmptyView();
+            updateEmptyView(level);
             updateButtonFromLevel();
             return true;
         }
     }
 
     private void updateButtonFromLevel() {
         switch (mPanelLevel) {
             case PARENT:
@@ -473,40 +474,41 @@ public class CombinedHistoryPanel extend
                     if (telemetryExtra != null) {
                         Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.BUTTON, telemetryExtra);
                     }
                     break;
             }
         }
     }
 
-    private void updateEmptyView() {
+    private void updateEmptyView(PanelLevel level) {
         boolean showEmptyHistoryView = false;
         boolean showEmptyClientsView = false;
         boolean showEmptyRecentTabsView = false;
-        switch (mPanelLevel) {
-            case PARENT:
-                showEmptyHistoryView = mHistoryAdapter.getItemCount() == mHistoryAdapter.getNumVisibleSmartFolders();
-                break;
+        if (mPanelLevel == level) {
+            switch (mPanelLevel) {
+                case PARENT:
+                    showEmptyHistoryView = mHistoryAdapter.getItemCount() == mHistoryAdapter.getNumVisibleSmartFolders();
+                    mHistoryEmptyView.setVisibility(showEmptyHistoryView ? View.VISIBLE : View.GONE);
+                    break;
 
-            case CHILD_SYNC:
-                showEmptyClientsView = mClientsAdapter.getItemCount() == 1;
-                break;
+                case CHILD_SYNC:
+                    showEmptyClientsView = mClientsAdapter.getItemCount() == 1;
+                    mClientsEmptyView.setVisibility(showEmptyClientsView ? View.VISIBLE : View.GONE);
+                    break;
 
-            case CHILD_RECENT_TABS:
-                showEmptyRecentTabsView = mRecentTabsAdapter.getClosedTabsCount() == 0;
-                break;
+                case CHILD_RECENT_TABS:
+                    showEmptyRecentTabsView = mRecentTabsAdapter.getClosedTabsCount() == 0;
+                    mRecentTabsEmptyView.setVisibility(showEmptyRecentTabsView ? View.VISIBLE : View.GONE);
+                    break;
+            }
         }
 
         final boolean showEmptyView = showEmptyClientsView || showEmptyHistoryView || showEmptyRecentTabsView;
         mRecyclerView.setOverScrollMode(showEmptyView ? View.OVER_SCROLL_NEVER : View.OVER_SCROLL_IF_CONTENT_SCROLLS);
-
-        mClientsEmptyView.setVisibility(showEmptyClientsView ? View.VISIBLE : View.GONE);
-        mHistoryEmptyView.setVisibility(showEmptyHistoryView ? View.VISIBLE : View.GONE);
-        mRecentTabsEmptyView.setVisibility(showEmptyRecentTabsView ? View.VISIBLE : View.GONE);
     }
 
     /**
      * Make Span that is clickable, and underlined
      * between the string markers <code>FORMAT_S1</code> and
      * <code>FORMAT_S2</code>.
      *
      * @param text String to format
--- a/mobile/android/base/java/org/mozilla/gecko/home/RecentTabsAdapter.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/RecentTabsAdapter.java
@@ -29,16 +29,17 @@ import org.mozilla.gecko.util.EventCallb
 import org.mozilla.gecko.util.NativeEventListener;
 import org.mozilla.gecko.util.NativeJSObject;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import java.util.ArrayList;
 import java.util.List;
 
 import static org.mozilla.gecko.home.CombinedHistoryItem.ItemType;
+import static org.mozilla.gecko.home.CombinedHistoryPanel.OnPanelLevelChangeListener.PanelLevel.CHILD_RECENT_TABS;
 
 public class RecentTabsAdapter extends RecyclerView.Adapter<CombinedHistoryItem>
                                implements CombinedHistoryRecyclerView.AdapterContextMenuBuilder, NativeEventListener {
     private static final String LOGTAG = "GeckoRecentTabsAdapter";
 
     private static final int NAVIGATION_BACK_BUTTON_INDEX = 0;
 
     private static final String TELEMETRY_EXTRA_LAST_TIME = "recent_tabs_last_time";
@@ -130,17 +131,17 @@ public class RecentTabsAdapter extends R
                 int prevClosedTabsCount = recentlyClosedTabs.length;
                 boolean prevSectionHeaderVisibility = isSectionHeaderVisible();
                 int prevSectionHeaderIndex = getSectionHeaderIndex();
 
                 recentlyClosedTabs = closedTabs;
                 recentlyClosedTabsReceived = true;
                 recentTabsUpdateHandler.onRecentTabsCountUpdated(
                         getClosedTabsCount(), recentlyClosedTabsReceived);
-                panelStateUpdateHandler.onPanelStateUpdated();
+                panelStateUpdateHandler.onPanelStateUpdated(CHILD_RECENT_TABS);
 
                 // Handle the section header hiding/unhiding.
                 updateHeaderVisibility(prevSectionHeaderVisibility, prevSectionHeaderIndex);
 
                 // Update the "Recently closed" part of the tab list.
                 updateTabsList(prevClosedTabsCount, recentlyClosedTabs.length, getFirstRecentTabIndex(), getLastRecentTabIndex());
             }
         });
@@ -187,17 +188,17 @@ public class RecentTabsAdapter extends R
                         // smarter about notifying the recycler view which bits changed.
                         int prevClosedTabsCount = lastSessionTabs.length;
                         boolean prevSectionHeaderVisibility = isSectionHeaderVisible();
                         int prevSectionHeaderIndex = getSectionHeaderIndex();
 
                         lastSessionTabs = closedTabs;
                         recentTabsUpdateHandler.onRecentTabsCountUpdated(
                                 getClosedTabsCount(), recentlyClosedTabsReceived);
-                        panelStateUpdateHandler.onPanelStateUpdated();
+                        panelStateUpdateHandler.onPanelStateUpdated(CHILD_RECENT_TABS);
 
                         // Handle the section header hiding/unhiding.
                         updateHeaderVisibility(prevSectionHeaderVisibility, prevSectionHeaderIndex);
 
                         // Update the "Tabs from last time" part of the tab list.
                         updateTabsList(prevClosedTabsCount, lastSessionTabs.length, getFirstLastSessionTabIndex(), getLastLastSessionTabIndex());
                     }
                 });
@@ -218,17 +219,17 @@ public class RecentTabsAdapter extends R
                 // smarter about notifying the recycler view which bits changed.
                 int prevClosedTabsCount = lastSessionTabs.length;
                 boolean prevSectionHeaderVisibility = isSectionHeaderVisible();
                 int prevSectionHeaderIndex = getSectionHeaderIndex();
 
                 lastSessionTabs = emptyLastSessionTabs;
                 recentTabsUpdateHandler.onRecentTabsCountUpdated(
                         getClosedTabsCount(), recentlyClosedTabsReceived);
-                panelStateUpdateHandler.onPanelStateUpdated();
+                panelStateUpdateHandler.onPanelStateUpdated(CHILD_RECENT_TABS);
 
                 // Handle the section header hiding.
                 updateHeaderVisibility(prevSectionHeaderVisibility, prevSectionHeaderIndex);
 
                 // Handle the "tabs from last time" being cleared.
                 if (prevClosedTabsCount > 0) {
                     notifyItemRangeRemoved(getFirstLastSessionTabIndex(), prevClosedTabsCount);
                 }
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
@@ -504,17 +504,17 @@ OnSharedPreferenceChangeListener
         Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, Method.BACK, "settings");
     }
 
     @Override
     protected void onDestroy() {
         super.onDestroy();
 
         GeckoApp.getEventDispatcher().unregisterGeckoThreadListener(this,
-                                                                    "Sanitize:Finished", 
+                                                                    "Sanitize:Finished",
                                                                     "Snackbar:Show");
 
         if (mPrefsRequest != null) {
             PrefsHelper.removeObserver(mPrefsRequest);
             mPrefsRequest = null;
         }
     }
 
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -57,17 +57,17 @@ resjar = add_java_jar('gecko-R')
 resjar.sources = []
 resjar.generated_sources += [
     'org/mozilla/gecko/R.java',
 ]
 
 if CONFIG['ANDROID_SUPPORT_V4_AAR']:
     ANDROID_EXTRA_PACKAGES += ['android.support.v4']
     ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_SUPPORT_V4_AAR_RES']]
-    resjar.generated_sources += ['android/support/v4/R.java']
+# (no resources) resjar.generated_sources += ['android/support/v4/R.java']
 if CONFIG['ANDROID_APPCOMPAT_V7_AAR']:
     ANDROID_EXTRA_PACKAGES += ['android.support.v7.appcompat']
     ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_APPCOMPAT_V7_AAR_RES']]
     resjar.generated_sources += ['android/support/v7/appcompat/R.java']
 if CONFIG['ANDROID_CARDVIEW_V7_AAR']:
     ANDROID_EXTRA_PACKAGES += ['android.support.v7.cardview']
     ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_CARDVIEW_V7_AAR_RES']]
     resjar.generated_sources += ['android/support/v7/cardview/R.java']
@@ -77,22 +77,21 @@ if CONFIG['ANDROID_DESIGN_AAR']:
     resjar.generated_sources += ['android/support/design/R.java']
 if CONFIG['ANDROID_RECYCLERVIEW_V7_AAR']:
     ANDROID_EXTRA_PACKAGES += ['android.support.v7.recyclerview']
     ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_RES']]
     resjar.generated_sources += ['android/support/v7/recyclerview/R.java']
 if CONFIG['ANDROID_CUSTOMTABS_AAR']:
     ANDROID_EXTRA_PACKAGES += ['android.support.customtabs']
     ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_CUSTOMTABS_AAR_RES']]
-    resjar.generated_sources += ['android/support/customtabs/R.java']
+# (no resources) resjar.generated_sources += ['android/support/customtabs/R.java']
 if CONFIG['ANDROID_PALETTE_V7_AAR']:
     ANDROID_EXTRA_PACKAGES += ['android.support.v7.palette']
     ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_PALETTE_V7_AAR_RES']]
-    resjar.generated_sources += ['android/support/v7/palette/R.java']
-
+# (no resources) resjar.generated_sources += ['android/support/v7/palette/R.java']
 
 resjar.javac_flags += ['-Xlint:all']
 
 mgjar = add_java_jar('gecko-mozglue')
 mgjar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x for x in [
     'mozglue/ByteBufferInputStream.java',
     'mozglue/DirectBufferAllocator.java',
     'mozglue/GeckoLoader.java',
@@ -875,22 +874,22 @@ if CONFIG['MOZ_ANDROID_GCM']:
     if CONFIG['ANDROID_PLAY_SERVICES_BASEMENT_AAR']:
         ANDROID_EXTRA_PACKAGES += ['com.google.android.gms']
         ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_PLAY_SERVICES_BASEMENT_AAR_RES']]
         resjar.generated_sources += ['com/google/android/gms/R.java']
 
     if CONFIG['ANDROID_PLAY_SERVICES_GCM_AAR']:
         ANDROID_EXTRA_PACKAGES += ['com.google.android.gms.gcm']
         ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_PLAY_SERVICES_GCM_AAR_RES']]
-        resjar.generated_sources += ['com/google/android/gms/gcm/R.java']
+# (no resources) resjar.generated_sources += ['com/google/android/gms/gcm/R.java']
 
     if CONFIG['ANDROID_PLAY_SERVICES_MEASUREMENT_AAR']:
         ANDROID_EXTRA_PACKAGES += ['com.google.android.gms.measurement']
         ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_PLAY_SERVICES_MEASUREMENT_AAR_RES']]
-        resjar.generated_sources += ['com/google/android/gms/measurement/R.java']
+# (no resources) resjar.generated_sources += ['android/support/v7/palette/R.java']
 
 if CONFIG['MOZ_INSTALL_TRACKING']:
     gbjar.extra_jars += [
         CONFIG['ANDROID_PLAY_SERVICES_ADS_AAR_LIB'],
         CONFIG['ANDROID_PLAY_SERVICES_BASEMENT_AAR_LIB'],
     ]
 
     if CONFIG['ANDROID_PLAY_SERVICES_ADS_AAR']:
--- a/python/mozbuild/mozbuild/backend/tup.py
+++ b/python/mozbuild/mozbuild/backend/tup.py
@@ -11,18 +11,20 @@ from mozbuild.base import MozbuildObject
 from mozbuild.backend.base import PartialBackend, HybridBackend
 from mozbuild.backend.recursivemake import RecursiveMakeBackend
 from mozbuild.shellutil import quote as shell_quote
 
 from .common import CommonBackend
 from ..frontend.data import (
     ContextDerived,
     Defines,
+    FinalTargetPreprocessedFiles,
     GeneratedFile,
     HostDefines,
+    ObjdirPreprocessedFiles,
 )
 from ..util import (
     FileAvoidWrite,
 )
 
 
 class BackendTupfile(object):
     """Represents a generated Tupfile.
@@ -34,16 +36,17 @@ class BackendTupfile(object):
         self.objdir = objdir
         self.relobjdir = mozpath.relpath(objdir, topobjdir)
         self.environment = environment
         self.name = mozpath.join(objdir, 'Tupfile')
         self.rules_included = False
         self.shell_exported = False
         self.defines = []
         self.host_defines = []
+        self.delayed_generated_files = []
 
         self.fh = FileAvoidWrite(self.name, capture_diff=True)
         self.fh.write('# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.\n')
         self.fh.write('\n')
 
     def write(self, buf):
         self.fh.write(buf)
 
@@ -134,65 +137,62 @@ class TupOnly(CommonBackend, PartialBack
         # Even if CommonBackend acknowledged the object, we still need to let
         # the RecursiveMake backend also handle these objects.
         if consumed:
             return False
 
         backend_file = self._get_backend_file_for(obj)
 
         if isinstance(obj, GeneratedFile):
-            # TODO: These are directories that don't work in the tup backend
-            # yet, because things they depend on aren't built yet.
-            skip_directories = (
-                'build', # FinalTargetPreprocessedFiles
-                'layout/style/test', # HostSimplePrograms
-                'toolkit/library', # libxul.so
+            # These files are already generated by make before tup runs.
+            skip_files = (
+                'buildid.h',
+                'source-repo.h',
             )
-            if obj.script and obj.method and obj.relobjdir not in skip_directories:
-                backend_file.export_shell()
-                cmd = self._py_action('file_generate')
-                cmd.extend([
-                    obj.script,
-                    obj.method,
-                    obj.outputs[0],
-                    '%s.pp' % obj.outputs[0], # deps file required
-                ])
-                full_inputs = [f.full_path for f in obj.inputs]
-                cmd.extend(full_inputs)
+            if any(f in skip_files for f in obj.outputs):
+                # Let the RecursiveMake backend handle these.
+                return False
 
-                outputs = []
-                outputs.extend(obj.outputs)
-                outputs.append('%s.pp' % obj.outputs[0])
-
-                backend_file.rule(
-                    display='python {script}:{method} -> [%o]'.format(script=obj.script, method=obj.method),
-                    cmd=cmd,
-                    inputs=full_inputs,
-                    outputs=outputs,
-                )
+            if 'application.ini.h' in obj.outputs:
+                # application.ini.h is a special case since we need to process
+                # the FINAL_TARGET_PP_FILES for application.ini before running
+                # the GENERATED_FILES script, and tup doesn't handle the rules
+                # out of order.
+                backend_file.delayed_generated_files.append(obj)
+            else:
+                self._process_generated_file(backend_file, obj)
         elif isinstance(obj, Defines):
             self._process_defines(backend_file, obj)
         elif isinstance(obj, HostDefines):
             self._process_defines(backend_file, obj, host=True)
+        elif isinstance(obj, FinalTargetPreprocessedFiles):
+            self._process_final_target_pp_files(obj, backend_file)
+        elif isinstance(obj, ObjdirPreprocessedFiles):
+            self._process_final_target_pp_files(obj, backend_file)
 
         return True
 
     def consume_finished(self):
         CommonBackend.consume_finished(self)
 
         for objdir, backend_file in sorted(self._backend_files.items()):
+            for obj in backend_file.delayed_generated_files:
+                self._process_generated_file(backend_file, obj)
             with self._write_file(fh=backend_file):
                 pass
 
         with self._write_file(mozpath.join(self.environment.topobjdir, 'Tuprules.tup')) as fh:
             acdefines = [name for name in self.environment.defines
                 if not name in self.environment.non_global_defines]
             acdefines_flags = ' '.join(['-D%s=%s' % (name,
                 shell_quote(self.environment.defines[name]))
                 for name in sorted(acdefines)])
+            # TODO: AB_CD only exists in Makefiles at the moment.
+            acdefines_flags += ' -DAB_CD=en-US'
+
             fh.write('MOZ_OBJ_ROOT = $(TUP_CWD)\n')
             fh.write('DIST = $(MOZ_OBJ_ROOT)/dist\n')
             fh.write('ACDEFINES = %s\n' % acdefines_flags)
             fh.write('topsrcdir = $(MOZ_OBJ_ROOT)/%s\n' % (
                 os.path.relpath(self.environment.topsrcdir, self.environment.topobjdir)
             ))
             fh.write('PYTHON = $(MOZ_OBJ_ROOT)/_virtualenv/bin/python -B\n')
             fh.write('PYTHON_PATH = $(PYTHON) $(topsrcdir)/config/pythonpath.py\n')
@@ -200,24 +200,60 @@ class TupOnly(CommonBackend, PartialBack
             fh.write('IDL_PARSER_DIR = $(topsrcdir)/xpcom/idl-parser\n')
             fh.write('IDL_PARSER_CACHE_DIR = $(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidl\n')
 
         # Run 'tup init' if necessary.
         if not os.path.exists(mozpath.join(self.environment.topsrcdir, ".tup")):
             tup = self.environment.substs.get('TUP', 'tup')
             self._cmd.run_process(cwd=self.environment.topsrcdir, log_name='tup', args=[tup, 'init'])
 
+    def _process_generated_file(self, backend_file, obj):
+        # TODO: These are directories that don't work in the tup backend
+        # yet, because things they depend on aren't built yet.
+        skip_directories = (
+            'layout/style/test', # HostSimplePrograms
+            'toolkit/library', # libxul.so
+        )
+        if obj.script and obj.method and obj.relobjdir not in skip_directories:
+            backend_file.export_shell()
+            cmd = self._py_action('file_generate')
+            cmd.extend([
+                obj.script,
+                obj.method,
+                obj.outputs[0],
+                '%s.pp' % obj.outputs[0], # deps file required
+            ])
+            full_inputs = [f.full_path for f in obj.inputs]
+            cmd.extend(full_inputs)
+
+            outputs = []
+            outputs.extend(obj.outputs)
+            outputs.append('%s.pp' % obj.outputs[0])
+
+            backend_file.rule(
+                display='python {script}:{method} -> [%o]'.format(script=obj.script, method=obj.method),
+                cmd=cmd,
+                inputs=full_inputs,
+                outputs=outputs,
+            )
+
     def _process_defines(self, backend_file, obj, host=False):
         defines = list(obj.get_defines())
         if defines:
             if host:
                 backend_file.host_defines = defines
             else:
                 backend_file.defines = defines
 
+    def _process_final_target_pp_files(self, obj, backend_file):
+        for i, (path, files) in enumerate(obj.files.walk()):
+            for f in files:
+                self._preprocess(backend_file, f.full_path,
+                                 destdir=mozpath.join(self.environment.topobjdir, obj.install_target, path))
+
     def _handle_idl_manager(self, manager):
         backend_file = self._get_backend_file('xpcom/xpidl')
         backend_file.export_shell()
 
         for module, data in sorted(manager.modules.iteritems()):
             dest, idls = data
             cmd = [
                 '$(PYTHON_PATH)',
@@ -240,26 +276,31 @@ class TupOnly(CommonBackend, PartialBack
                     '$(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidl/xpidllex.py',
                     '$(MOZ_OBJ_ROOT)/xpcom/idl-parser/xpidl/xpidlyacc.py',
                 ],
                 display='XPIDL %s' % module,
                 cmd=cmd,
                 outputs=outputs,
             )
 
-    def _preprocess(self, backend_file, input_file):
+    def _preprocess(self, backend_file, input_file, destdir=None):
         cmd = self._py_action('preprocessor')
         cmd.extend(backend_file.defines)
         cmd.extend(['$(ACDEFINES)', '%f', '-o', '%o'])
 
+        base_input = mozpath.basename(input_file)
+        if base_input.endswith('.in'):
+            base_input = mozpath.splitext(base_input)[0]
+        output = mozpath.join(destdir, base_input) if destdir else base_input
+
         backend_file.rule(
             inputs=[input_file],
             display='Preprocess %o',
             cmd=cmd,
-            outputs=[mozpath.basename(input_file)],
+            outputs=[output],
         )
 
     def _handle_ipdl_sources(self, ipdl_dir, sorted_ipdl_sources,
                              unified_ipdl_cppsrcs_mapping):
         # TODO: This isn't implemented yet in the tup backend, but it is called
         # by the CommonBackend.
         pass
 
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -1185,21 +1185,26 @@ void HandshakeCallback(PRFileDesc* fd, v
           infoObject->IsFullHandshake()
             ? Telemetry::SSL_SYMMETRIC_CIPHER_FULL
             : Telemetry::SSL_SYMMETRIC_CIPHER_RESUMED,
           cipherInfo.symCipher);
     }
   }
 
   PRBool siteSupportsSafeRenego;
-  rv = SSL_HandshakeNegotiatedExtension(fd, ssl_renegotiation_info_xtn,
-                                        &siteSupportsSafeRenego);
-  MOZ_ASSERT(rv == SECSuccess);
-  if (rv != SECSuccess) {
-    siteSupportsSafeRenego = false;
+  if (channelInfo.protocolVersion != SSL_LIBRARY_VERSION_TLS_1_3) {
+    rv = SSL_HandshakeNegotiatedExtension(fd, ssl_renegotiation_info_xtn,
+                                          &siteSupportsSafeRenego);
+    MOZ_ASSERT(rv == SECSuccess);
+    if (rv != SECSuccess) {
+      siteSupportsSafeRenego = false;
+    }
+  } else {
+    // TLS 1.3 dropped support for renegotiation.
+    siteSupportsSafeRenego = true;
   }
   bool renegotiationUnsafe = !siteSupportsSafeRenego &&
                              ioLayerHelpers.treatUnsafeNegotiationAsBroken();
 
 
   /* Set the SSL Status information */
   RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
   if (!status) {
--- a/security/sandbox/mac/Sandbox.mm
+++ b/security/sandbox/mac/Sandbox.mm
@@ -368,36 +368,50 @@ static const char contentSandboxRules[] 
   "\n"
   "  (allow file-write* (var-folders2-regex \"/org\\.chromium\\.[a-zA-Z0-9]*$\"))\n"
   "\n"
   "; Per-user and system-wide Extensions dir\n"
   "  (allow file-read*\n"
   "      (home-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
   "      (resolving-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\"))\n"
   "\n"
-  "; Profile subdirectories\n"
-  "  (if (not (zero? hasProfileDir)) (allow file-read*\n"
-  "      (profile-subpath \"/extensions\")\n"
-  "      (profile-subpath \"/weave\")))\n"
+  "; The following rules impose file access restrictions which get\n"
+  "; more restrictive in higher levels. When file-origin-specific\n"
+  "; content processes are used for file:// origin browsing, the\n"
+  "; global file-read* permission should be removed from each level.\n"
   "\n"
-  "; the following rules should be removed when printing and\n"
-  "; opening a file from disk are brokered through the main process\n"
-  "  (if (< sandbox-level 2)\n"
+  "; level 1: global read access permitted, no home write access\n"
+  "  (if (= sandbox-level 1)\n"
+  "    (begin\n"
+  "      (allow file-read*)\n"
+  "      (allow file-write* (require-not (subpath home-path)))))\n"
+  "\n"
+  "; level 2: global read access permitted, no home write access,\n"
+  ";          no read/write access to ~/Library,\n"
+  ";          no read/write access to $PROFILE,\n"
+  ";          read access permitted to $PROFILE/{extensions,weave}\n"
+  "  (if (= sandbox-level 2)\n"
   "    (if (not (zero? hasProfileDir))\n"
-  "      (allow file*\n"
-  "          (require-all\n"
+  "      ; we have a profile dir\n"
+  "      (begin\n"
+  "        (allow file-read* (require-all\n"
   "              (require-not (home-subpath \"/Library\"))\n"
   "              (require-not (subpath profileDir))))\n"
-  "      (allow file*\n"
-  "          (require-not (home-subpath \"/Library\"))))\n"
-  "    (allow file*\n"
-  "        (require-all\n"
-  "            (subpath home-path)\n"
-  "            (require-not\n"
-  "                (home-subpath \"/Library\")))))\n"
+  "        (allow file-write* (require-all\n"
+  "              (require-not (subpath home-path))\n"
+  "              (require-not (subpath profileDir))))\n"
+  "        (allow file-read*\n"
+  "              (profile-subpath \"/extensions\")\n"
+  "              (profile-subpath \"/weave\")))\n"
+  "      ; we don't have a profile dir\n"
+  "      (begin\n"
+  "        (allow file-read*\n"
+  "              (require-not (home-subpath \"/Library\")))\n"
+  "        (allow file-write* (require-all\n"
+  "              (require-not (subpath home-path)))))))\n"
   "\n"
   "; accelerated graphics\n"
   "  (allow-shared-preferences-read \"com.apple.opengl\")\n"
   "  (allow-shared-preferences-read \"com.nvidia.OpenGL\")\n"
   "  (allow mach-lookup\n"
   "      (global-name \"com.apple.cvmsServ\"))\n"
   "  (allow iokit-open\n"
   "      (iokit-connection \"IOAccelerator\")\n"
--- a/taskcluster/ci/build/linux.yml
+++ b/taskcluster/ci/build/linux.yml
@@ -123,16 +123,43 @@ linux/debug:
             - builds/releng_base_linux_32_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
         secrets: true
         custom-build-variant-cfg: debug
         tooltool-downloads: public
         need-xvfb: true
 
+linux/pgo:
+    description: "Linux32 PGO"
+    index:
+        product: firefox
+        job-name:
+            gecko-v2: linux-pgo
+    treeherder:
+        platform: linux32/pgo
+        symbol: tc(B)
+        tier: 2
+    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
+    worker:
+        implementation: docker-worker
+        max-run-time: 36000
+    coalesce-name: linux32-pgo
+    run:
+        using: mozharness
+        actions: [get-secrets build check-test generate-build-stats update]
+        options: [enable-pgo]
+        config:
+            - builds/releng_base_linux_32_builds.py
+            - balrog/production.py
+        script: "mozharness/scripts/fx_desktop_build.py"
+        secrets: true
+        tooltool-downloads: public
+        need-xvfb: true
+
 linux64-asan/opt:
     description: "Linux64 Opt ASAN"
     index:
         product: firefox
         job-name: linux64-asan-opt
     treeherder:
         platform: linux64/asan
         symbol: tc(Bo)
@@ -220,9 +247,9 @@ linux64-ccov/opt:
         actions: [get-secrets build check-test generate-build-stats update]
         config:
             - builds/releng_base_linux_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
         secrets: true
         custom-build-variant-cfg: code-coverage
         tooltool-downloads: public
-        need-xvfb: true
\ No newline at end of file
+        need-xvfb: true
--- a/taskcluster/ci/source-check/mozlint.yml
+++ b/taskcluster/ci/source-check/mozlint.yml
@@ -42,17 +42,17 @@ mozlint-eslint/opt:
             - 'tools/lint/**'
             - 'testing/docker/lint/**'
 
 mozlint-flake8/opt:
     description: flake8 run over the gecko codebase
     treeherder:
         symbol: f8
         kind: test
-        tier: 2
+        tier: 1
         platform: lint/opt
     worker-type: aws-provisioner-v1/b2gtest
     worker:
         implementation: docker-worker
         docker-image: {in-tree: "lint"}
         max-run-time: 1800
     run:
         using: mach
@@ -68,17 +68,17 @@ mozlint-flake8/opt:
             - 'tools/lint/**'
             - 'testing/docker/lint/**'
 
 wptlint-gecko/opt:
     description: web-platform-tests linter
     treeherder:
         symbol: W
         kind: test
-        tier: 2
+        tier: 1
         platform: lint/opt
     worker-type: aws-provisioner-v1/b2gtest
     worker:
         implementation: docker-worker
         docker-image: {in-tree: "lint"}
         max-run-time: 1800
     run:
         using: mach
--- a/taskcluster/docs/how-tos.rst
+++ b/taskcluster/docs/how-tos.rst
@@ -37,17 +37,17 @@ 1. Find a recent decision task on the pr
    contents are simple enough if you would like to modify it, and it is
    documented in :doc:`parameters`.
 
 2. Run one of the ``mach taskgraph`` subcommands (see :doc:`taskgraph`) to
    generate a baseline against which to measure your changes.  For example:
 
    .. code-block:: none
 
-       ./mach taskgraph --json -p parameters.yml tasks > old-tasks.json
+       ./mach taskgraph tasks --json -p parameters.yml > old-tasks.json
 
 3. Make your modifications under ``taskcluster/``.
 
 4. Run the same ``mach taskgraph`` command, sending the output to a new file,
    and use ``diff`` to compare the old and new files.  Make sure your changes
    have the desired effect and no undesirable side-effects.
 
 5. When you are satisfied with the changes, push them to try to ensure that the
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -52,16 +52,27 @@ def target_tasks_try_option_syntax(full_
 
     # If the developer wants test jobs to be rebuilt N times we add that value here
     if int(options.trigger_tests) > 1:
         for l in target_tasks_labels:
             task = full_task_graph[l]
             if 'unittest_suite' in task.attributes:
                 task.attributes['task_duplicates'] = options.trigger_tests
 
+    # Add notifications here as well
+    if options.notifications:
+        for task in full_task_graph:
+            owner = parameters.get('owner')
+            routes = task.task.setdefault('routes', [])
+            if options.notifications == 'all':
+                routes.append("notify.email.{}.on-any".format(owner))
+            elif options.notifications == 'failure':
+                routes.append("notify.email.{}.on-failed".format(owner))
+                routes.append("notify.email.{}.on-exception".format(owner))
+
     return target_tasks_labels
 
 
 @_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."""
     def filter(task):
--- a/taskcluster/taskgraph/test/test_target_tasks.py
+++ b/taskcluster/taskgraph/test/test_target_tasks.py
@@ -13,16 +13,17 @@ from ..taskgraph import TaskGraph
 from .util import TestTask
 from mozunit import main
 
 
 class FakeTryOptionSyntax(object):
 
     def __init__(self, message, task_graph):
         self.trigger_tests = 0
+        self.notifications = None
 
     def task_matches(self, attributes):
         return 'at-at' in attributes
 
 
 class TestTargetTasks(unittest.TestCase):
 
     def test_from_parameters(self):
--- a/taskcluster/taskgraph/test/test_try_option_syntax.py
+++ b/taskcluster/taskgraph/test/test_try_option_syntax.py
@@ -250,10 +250,25 @@ class TestTryOptionSyntax(unittest.TestC
         tos = TryOptionSyntax('try: --rebuild 10', empty_graph)
         self.assertEqual(tos.trigger_tests, 10)
 
     def test_interactive(self):
         "--interactive sets interactive"
         tos = TryOptionSyntax('try: --interactive', empty_graph)
         self.assertEqual(tos.interactive, True)
 
+    def test_all_email(self):
+        "--all-emails sets notifications"
+        tos = TryOptionSyntax('try: --all-emails', empty_graph)
+        self.assertEqual(tos.notifications, 'all')
+
+    def test_fail_email(self):
+        "--failure-emails sets notifications"
+        tos = TryOptionSyntax('try: --failure-emails', empty_graph)
+        self.assertEqual(tos.notifications, 'failure')
+
+    def test_no_email(self):
+        "no email settings don't set notifications"
+        tos = TryOptionSyntax('try:', empty_graph)
+        self.assertEqual(tos.notifications, None)
+
 if __name__ == '__main__':
     main()
--- a/taskcluster/taskgraph/transforms/gecko_v2_whitelist.py
+++ b/taskcluster/taskgraph/transforms/gecko_v2_whitelist.py
@@ -36,16 +36,17 @@ JOB_NAME_WHITELIST = set([
     'linux64-jsdcov-opt',
     'linux64-l10n-opt',
     'linux64-opt',
     'linux64-pgo',
     'linux64-st-an-opt',
     'linux64-valgrind-opt',
     'linux-debug',
     'linux-opt',
+    'linux-pgo',
     'macosx64-debug',
     'macosx64-opt',
     'macosx64-st-an-opt',
     'mulet-dbg',
     'mulet-haz-debug',
     'mulet-opt',
     'nexus-5-l-eng-debug',
     'nexus-5-l-eng-opt',
--- a/taskcluster/taskgraph/try_option_syntax.py
+++ b/taskcluster/taskgraph/try_option_syntax.py
@@ -181,17 +181,18 @@ class TryOptionSyntax(object):
 
         The resulting object has attributes:
 
         - build_types: a list containing zero or more of 'opt' and 'debug'
         - platforms: a list of selected platform names, or None for all
         - unittests: a list of tests, of the form given below, or None for all
         - jobs: a list of requested job names, or None for all
         - trigger_tests: the number of times tests should be triggered (--rebuild)
-        - interactive; true if --interactive
+        - interactive: true if --interactive
+        - notifications: either None if no notifications or one of 'all' or 'failure'
 
         Note that -t is currently completely ignored.
 
         The unittests and talos lists contain dictionaries of the form:
 
         {
             'test': '<suite name>',
             'platforms': [..platform names..], # to limit to only certain platforms
@@ -200,16 +201,17 @@ class TryOptionSyntax(object):
         """
         self.jobs = []
         self.build_types = []
         self.platforms = []
         self.unittests = []
         self.talos = []
         self.trigger_tests = 0
         self.interactive = False
+        self.notifications = None
 
         # shlex used to ensure we split correctly when giving values to argparse.
         parts = shlex.split(self.escape_whitespace_in_brackets(message))
         try_idx = None
         for idx, part in enumerate(parts):
             if part == TRY_DELIMITER:
                 try_idx = idx
                 break
@@ -222,29 +224,34 @@ class TryOptionSyntax(object):
         parser.add_argument('-b', '--build', dest='build_types')
         parser.add_argument('-p', '--platform', nargs='?',
                             dest='platforms', const='all', default='all')
         parser.add_argument('-u', '--unittests', nargs='?',
                             dest='unittests', const='all', default='all')
         parser.add_argument('-t', '--talos', nargs='?', dest='talos', const='all', default='all')
         parser.add_argument('-i', '--interactive',
                             dest='interactive', action='store_true', default=False)
+        parser.add_argument('-e', '--all-emails',
+                            dest='notifications', action='store_const', const='all')
+        parser.add_argument('-f', '--failure-emails',
+                            dest='notifications', action='store_const', const='failure')
         parser.add_argument('-j', '--job', dest='jobs', action='append')
         # In order to run test jobs multiple times
         parser.add_argument('--rebuild', dest='trigger_tests', type=int, default=1)
         args, _ = parser.parse_known_args(parts[try_idx:])
 
         self.jobs = self.parse_jobs(args.jobs)
         self.build_types = self.parse_build_types(args.build_types)
         self.platforms = self.parse_platforms(args.platforms)
         self.unittests = self.parse_test_option(
             "unittest_try_name", args.unittests, full_task_graph)
         self.talos = self.parse_test_option("talos_try_name", args.talos, full_task_graph)
         self.trigger_tests = args.trigger_tests
         self.interactive = args.interactive
+        self.notifications = args.notifications
 
     def parse_jobs(self, jobs_arg):
         if not jobs_arg or jobs_arg == ['all']:
             return None
         expanded = []
         for job in jobs_arg:
             expanded.extend(j.strip() for j in job.split(','))
         return expanded
@@ -537,9 +544,10 @@ class TryOptionSyntax(object):
 
         return "\n".join([
             "build_types: " + ", ".join(self.build_types),
             "platforms: " + none_for_all(self.platforms),
             "unittests: " + none_for_all(self.unittests),
             "jobs: " + none_for_all(self.jobs),
             "trigger_tests: " + str(self.trigger_tests),
             "interactive: " + str(self.interactive),
+            "notifications: " + self.notifications,
         ])
--- a/testing/docker/decision/Dockerfile
+++ b/testing/docker/decision/Dockerfile
@@ -3,16 +3,19 @@ MAINTAINER    Greg Arndt <garndt@mozilla
 
 # Add worker user
 RUN useradd -d /home/worker -s /bin/bash -m worker
 RUN mkdir /home/worker/artifacts && chown worker:worker /home/worker/artifacts
 
 # %include testing/docker/recipes/tooltool.py
 ADD topsrcdir/testing/docker/recipes/tooltool.py /tmp/tooltool.py
 
+# %include testing/mozharness/external_tools/robustcheckout.py
+ADD topsrcdir/testing/mozharness/external_tools/robustcheckout.py /usr/local/mercurial/robustcheckout.py
+
 # %include testing/docker/recipes/install-mercurial.sh
 ADD topsrcdir/testing/docker/recipes/install-mercurial.sh /tmp/install-mercurial.sh
 
 ADD system-setup.sh /tmp/system-setup.sh
 RUN bash /tmp/system-setup.sh
 
 # %include testing/docker/recipes/run-task
 ADD topsrcdir/testing/docker/recipes/run-task /home/worker/bin/run-task
--- a/testing/docker/desktop-test/Dockerfile
+++ b/testing/docker/desktop-test/Dockerfile
@@ -2,16 +2,19 @@ FROM          ubuntu:12.04
 MAINTAINER    Jonas Finnemann Jensen <jopsen@gmail.com>
 
 RUN useradd -d /home/worker -s /bin/bash -m worker
 WORKDIR /home/worker
 
 # %include testing/docker/recipes/tooltool.py
 ADD topsrcdir/testing/docker/recipes/tooltool.py /setup/tooltool.py
 
+# %include testing/mozharness/external_tools/robustcheckout.py
+ADD topsrcdir/testing/mozharness/external_tools/robustcheckout.py /usr/local/mercurial/robustcheckout.py
+
 # %include testing/docker/recipes/install-mercurial.sh
 ADD topsrcdir/testing/docker/recipes/install-mercurial.sh /tmp/install-mercurial.sh
 
 # Add wrapper scripts for xvfb allowing tasks to easily retry starting up xvfb
 # %include testing/docker/recipes/xvfb.sh
 ADD topsrcdir/testing/docker/recipes/xvfb.sh /home/worker/scripts/xvfb.sh
 
 # %include testing/docker/recipes/ubuntu1204-test-system-setup.sh
--- a/testing/docker/desktop1604-test/Dockerfile
+++ b/testing/docker/desktop1604-test/Dockerfile
@@ -2,16 +2,19 @@ FROM          ubuntu:16.04
 MAINTAINER    Joel Maher <joel.maher@gmail.com>
 
 RUN useradd -d /home/worker -s /bin/bash -m worker
 WORKDIR /home/worker
 
 # %include testing/docker/recipes/tooltool.py
 ADD topsrcdir/testing/docker/recipes/tooltool.py /setup/tooltool.py
 
+# %include testing/mozharness/external_tools/robustcheckout.py
+ADD topsrcdir/testing/mozharness/external_tools/robustcheckout.py /usr/local/mercurial/robustcheckout.py
+
 # %include testing/docker/recipes/install-mercurial.sh
 ADD topsrcdir/testing/docker/recipes/install-mercurial.sh /setup/install-mercurial.sh
 
 # %include testing/docker/recipes/ubuntu1604-test-system-setup.sh
 ADD topsrcdir/testing/docker/recipes/ubuntu1604-test-system-setup.sh /setup/system-setup.sh
 RUN           bash /setup/system-setup.sh
 
 # Add wrapper scripts for xvfb allowing tasks to easily retry starting up xvfb
--- a/testing/docker/lint/Dockerfile
+++ b/testing/docker/lint/Dockerfile
@@ -3,16 +3,19 @@ MAINTAINER    Andrew Halberstadt <ahalbe
 
 RUN useradd -d /home/worker -s /bin/bash -m worker
 WORKDIR /home/worker
 
 RUN mkdir /build
 # %include testing/docker/recipes/tooltool.py
 ADD topsrcdir/testing/docker/recipes/tooltool.py /build/tooltool.py
 
+# %include testing/mozharness/external_tools/robustcheckout.py
+ADD topsrcdir/testing/mozharness/external_tools/robustcheckout.py /usr/local/mercurial/robustcheckout.py
+
 # %include testing/docker/recipes/install-mercurial.sh
 ADD topsrcdir/testing/docker/recipes/install-mercurial.sh /build/install-mercurial.sh
 ADD system-setup.sh /tmp/system-setup.sh
 RUN bash /tmp/system-setup.sh
 
 # %include testing/docker/recipes/run-task
 ADD topsrcdir/testing/docker/recipes/run-task /home/worker/bin/run-task
 RUN chown -R worker:worker /home/worker/bin && chmod 755 /home/worker/bin/*
--- a/testing/docker/recipes/install-mercurial.sh
+++ b/testing/docker/recipes/install-mercurial.sh
@@ -51,28 +51,16 @@ tooltool_fetch <<EOF
 EOF
 
     dpkg -i ${HG_COMMON_FILENAME} ${HG_FILENAME}
 else
     echo "Do not know how to install Mercurial on this OS"
     exit 1
 fi
 
-mkdir -p /usr/local/mercurial
-tooltool_fetch <<'EOF'
-[
-{
-    "size": 11849,
-    "digest": "c88d9b8afd6649bd28bbacfa654ebefec8087a01d1662004aae088d485edeb03a92df1193d1310c0369d7721f475b974fcd4a911428ec65936f7e40cf1609c49",
-    "algorithm": "sha512",
-    "filename": "robustcheckout.py"
-}
-]
-EOF
-mv robustcheckout.py /usr/local/mercurial/robustcheckout.py
 chmod 644 /usr/local/mercurial/robustcheckout.py
 
 mkdir -p /etc/mercurial
 cat >/etc/mercurial/hgrc <<EOF
 # By default the progress bar starts after 3s and updates every 0.1s. We
 # change this so it shows and updates every 1.0s.
 # We also tell progress to assume a TTY is present so updates are printed
 # even if there is no known TTY.
--- a/testing/firefox-ui/tests/functional/security/test_safe_browsing_initial_download.py
+++ b/testing/firefox-ui/tests/functional/security/test_safe_browsing_initial_download.py
@@ -1,85 +1,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/.
 
 import os
-import re
 
 from firefox_ui_harness.testcases import FirefoxTestCase
 from marionette_driver import Wait
-from marionette_driver.errors import TimeoutException
 
 
 class TestSafeBrowsingInitialDownload(FirefoxTestCase):
 
-    test_data = [{
-        'platforms': ['linux', 'windows_nt', 'darwin'],
-        'files': [
-            # Phishing
-            r'^goog-badbinurl-shavar.pset$',
-            r'^goog-badbinurl-shavar.sbstore$',
-            r'^goog-malware-shavar.pset$',
-            r'^goog-malware-shavar.sbstore$',
-            r'^goog(pub)?-phish-shavar.pset$',
-            r'^goog(pub)?-phish-shavar.sbstore$',
-            r'^goog-unwanted-shavar.pset$',
-            r'^goog-unwanted-shavar.sbstore$',
+    file_extensions = [
+        'pset',
+        'sbstore',
+    ]
 
-            # Tracking Protections
-            r'^base-track-digest256.pset$',
-            r'^base-track-digest256.sbstore$',
-            r'^mozstd-trackwhite-digest256.pset$',
-            r'^mozstd-trackwhite-digest256.sbstore$',
-        ],
-    }, {
-        'platforms': ['windows_nt'],
-        'files': [
-            r'^goog-downloadwhite-digest256.pset$',
-            r'^goog-downloadwhite-digest256.sbstore$',
-        ]
-    },
+    prefs_download_lists = [
+        'urlclassifier.blockedTable',
+        'urlclassifier.downloadAllowTable',
+        'urlclassifier.downloadBlockTable',
+        'urlclassifier.malwareTable',
+        'urlclassifier.phishTable',
+        'urlclassifier.trackingTable',
+        'urlclassifier.trackingWhitelistTable',
     ]
 
-    browser_prefs = {
-        'browser.safebrowsing.downloads.enabled': 'true',
-        'browser.safebrowsing.phishing.enabled': 'true',
-        'browser.safebrowsing.malware.enabled': 'true',
+    prefs_provider_update_time = {
+        # Force an immediate download of the safebrowsing files
         'browser.safebrowsing.provider.google.nextupdatetime': 1,
         'browser.safebrowsing.provider.mozilla.nextupdatetime': 1,
-        'privacy.trackingprotection.enabled': 'true',
-        'privacy.trackingprotection.pbmode.enabled': 'true',
+    }
+
+    prefs_safebrowsing = {
+        'browser.safebrowsing.blockedURIs.enabled': True,
+        'browser.safebrowsing.downloads.enabled': True,
+        'browser.safebrowsing.phishing.enabled': True,
+        'browser.safebrowsing.malware.enabled': True,
+        'privacy.trackingprotection.enabled': True,
+        'privacy.trackingprotection.pbmode.enabled': True,
     }
 
+    def get_safebrowsing_files(self):
+        files = []
+        for pref_name in self.prefs_download_lists:
+            base_names = self.marionette.get_pref(pref_name).split(',')
+            for ext in self.file_extensions:
+                files.extend(['{file}.{ext}'.format(file=f, ext=ext) for f in base_names if f])
+
+        return set(sorted(files))
+
     def setUp(self):
         FirefoxTestCase.setUp(self)
 
-        # Set Browser Preferences
-        self.marionette.enforce_gecko_prefs(self.browser_prefs)
+        # Force the preferences for the new profile
+        enforce_prefs = self.prefs_safebrowsing
+        enforce_prefs.update(self.prefs_provider_update_time)
+        self.marionette.enforce_gecko_prefs(enforce_prefs)
 
-        # Get safebrowsing path where downloaded data gets stored
-        self.sb_files_path = os.path.join(self.marionette.instance.profile.profile, 'safebrowsing')
+        self.safebrowsing_path = os.path.join(self.marionette.instance.profile.profile,
+                                              'safebrowsing')
+        self.safebrowsing_files = self.get_safebrowsing_files()
 
     def tearDown(self):
         try:
+            # Restart with a fresh profile
             self.restart(clean=True)
         finally:
             FirefoxTestCase.tearDown(self)
 
     def test_safe_browsing_initial_download(self):
-        wait = Wait(self.marionette, timeout=self.browser.timeout_page_load,
-                    ignored_exceptions=[OSError])
-
-        for data in self.test_data:
-            if self.platform not in data['platforms']:
-                continue
+        def check_downloaded(_):
+            return reduce(lambda state, pref: state and int(self.marionette.get_pref(pref)) != 1,
+                          self.prefs_provider_update_time.keys(), True)
 
-            for item in data['files']:
-                try:
-                    wait.until(
-                        lambda _: [f for f in os.listdir(self.sb_files_path)
-                                   if re.search(item, f)],
-                        message='Safe Browsing File: {} not found!'.format(item))
-                except TimeoutException:
-                    self.logger.info('Downloaded safebrowsing files: {}'.format(
-                        os.listdir(self.sb_files_path)))
-                    raise
+        try:
+            Wait(self.marionette, timeout=60).until(
+                check_downloaded, message='Not all safebrowsing files have been downloaded')
+        finally:
+            self.assertSetEqual(self.safebrowsing_files, set(os.listdir(self.safebrowsing_path)))
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -994,59 +994,76 @@ class Marionette(object):
         instance with the requested preferences.
 
         : param prefs: A dictionary whose keys are preference names.
         """
         if not self.instance:
             raise errors.MarionetteException("enforce_gecko_prefs() can only be called "
                                              "on Gecko instances launched by Marionette")
         pref_exists = True
-        self.set_context(self.CONTEXT_CHROME)
-        for pref, value in prefs.iteritems():
-            if type(value) is not str:
-                value = json.dumps(value)
-            pref_exists = self.execute_script("""
-            let prefInterface = Components.classes["@mozilla.org/preferences-service;1"]
-                                          .getService(Components.interfaces.nsIPrefBranch);
-            let pref = '%s';
-            let value = '%s';
-            let type = prefInterface.getPrefType(pref);
-            switch(type) {
-                case prefInterface.PREF_STRING:
-                    return value == prefInterface.getCharPref(pref).toString();
-                case prefInterface.PREF_BOOL:
-                    return value == prefInterface.getBoolPref(pref).toString();
-                case prefInterface.PREF_INT:
-                    return value == prefInterface.getIntPref(pref).toString();
-                case prefInterface.PREF_INVALID:
-                    return false;
-            }
-            """ % (pref, value))
-            if not pref_exists:
-                break
-        self.set_context(self.CONTEXT_CONTENT)
+        with self.using_context(self.CONTEXT_CHROME):
+            for pref, value in prefs.iteritems():
+                if type(value) is not str:
+                    value = json.dumps(value)
+                pref_exists = self.execute_script("""
+                let prefInterface = Components.classes["@mozilla.org/preferences-service;1"]
+                                              .getService(Components.interfaces.nsIPrefBranch);
+                let pref = '%s';
+                let value = '%s';
+                let type = prefInterface.getPrefType(pref);
+                switch(type) {
+                    case prefInterface.PREF_STRING:
+                        return value == prefInterface.getCharPref(pref).toString();
+                    case prefInterface.PREF_BOOL:
+                        return value == prefInterface.getBoolPref(pref).toString();
+                    case prefInterface.PREF_INT:
+                        return value == prefInterface.getIntPref(pref).toString();
+                    case prefInterface.PREF_INVALID:
+                        return false;
+                }
+                """ % (pref, value))
+                if not pref_exists:
+                    break
+
         if not pref_exists:
+            context = self._send_message("getContext", key="value")
             self.delete_session()
             self.instance.restart(prefs)
             self.raise_for_port(self.wait_for_port())
             self.start_session()
             self.reset_timeouts()
 
+            # Restore the context as used before the restart
+            self.set_context(context)
+
     def _request_in_app_shutdown(self, shutdown_flags=None):
         """Terminate the currently running instance from inside the application.
 
         :param shutdown_flags: If specified use additional flags for the shutdown
                                of the application. Possible values here correspond
                                to constants in nsIAppStartup: http://mzl.la/1X0JZsC.
         """
-        flags = set(["eForceQuit"])
+        flags = set([])
         if shutdown_flags:
             flags.add(shutdown_flags)
+
+        # Trigger a 'quit-application-requested' observer notification so that
+        # components can safely shutdown before quitting the application.
+        with self.using_context("chrome"):
+            canceled = self.execute_script("""
+                Components.utils.import("resource://gre/modules/Services.jsm");
+                let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
+                                 createInstance(Components.interfaces.nsISupportsPRBool);
+                Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
+                return cancelQuit.data;
+                """)
+            if canceled:
+                raise errors.MarionetteException("Something canceled the quit application request")
+
         self._send_message("quitApplication", {"flags": list(flags)})
-
         self.delete_session(in_app=True)
 
     @do_process_check
     def quit(self, in_app=False, callback=None):
         """Terminate the currently running instance.
 
         This command will delete the active marionette session. It also allows
         manipulation of eg. the profile data while the application is not running.
@@ -1089,16 +1106,18 @@ class Marionette(object):
                        browser. Otherwise the browser will be restarted immediately
                        by killing the process.
         :param callback: If provided and `in_app` is True, the callback will be
                          used to trigger the restart.
         """
         if not self.instance:
             raise errors.MarionetteException("restart() can only be called "
                                              "on Gecko instances launched by Marionette")
+
+        context = self._send_message("getContext", key="value")
         session_id = self.session_id
 
         if in_app:
             if clean:
                 raise ValueError("An in_app restart cannot be triggered with the clean flag set")
 
             if callable(callback):
                 callback()
@@ -1116,16 +1135,19 @@ class Marionette(object):
         else:
             self.delete_session()
             self.instance.restart(clean=clean)
             self.raise_for_port(self.wait_for_port())
 
         self.start_session(session_id=session_id)
         self.reset_timeouts()
 
+        # Restore the context as used before the restart
+        self.set_context(context)
+
         if in_app and self.session.get("processId"):
             # In some cases Firefox restarts itself by spawning into a new process group.
             # As long as mozprocess cannot track that behavior (bug 1284864) we assist by
             # informing about the new process id.
             self.instance.runner.process_handler.check_for_detached(self.session["processId"])
 
     def absolute_url(self, relative_url):
         '''
--- a/testing/marionette/harness/marionette/tests/unit/test_profile_management.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_profile_management.py
@@ -20,17 +20,15 @@ class TestProfileManagement(MarionetteTe
         self.assertTrue(self.marionette.get_pref("marionette.test.bool"))
         self.assertEqual(self.marionette.get_pref("marionette.test.string"), "testing")
         self.assertEqual(self.marionette.get_pref("marionette.test.int"), 3)
 
     def test_change_preference(self):
         self.assertTrue(self.marionette.get_pref("marionette.test.bool"))
 
         self.marionette.enforce_gecko_prefs({"marionette.test.bool": False})
-        self.marionette.set_context('chrome')
 
         self.assertFalse(self.marionette.get_pref("marionette.test.bool"))
 
     def test_clean_profile(self):
         self.marionette.restart(clean=True)
-        self.marionette.set_context('chrome')
 
         self.assertEqual(self.marionette.get_pref("marionette.test.bool"), None)
--- a/testing/marionette/harness/marionette/tests/unit/test_quit_restart.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_quit_restart.py
@@ -96,8 +96,56 @@ class TestQuitRestart(MarionetteTestCase
                              callback=self.marionette._request_in_app_shutdown)
         self.assertEqual(self.marionette.session, None)
         with self.assertRaisesRegexp(MarionetteException, "Please start a session"):
             self.marionette.get_url()
 
         self.marionette.start_session()
         self.assertNotEqual(self.marionette.session_id, self.session_id)
         self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3)
+
+    def test_reset_context_after_quit_by_set_context(self):
+        # Check that we are in content context which is used by default in Marionette
+        self.assertNotIn('chrome://', self.marionette.get_url(),
+                         "Context doesn't default to content")
+
+        self.marionette.set_context('chrome')
+        self.marionette.quit()
+        self.assertEqual(self.marionette.session, None)
+        self.marionette.start_session()
+        self.assertNotIn('chrome://', self.marionette.get_url(),
+                         "Not in content context after quit with using_context")
+
+    def test_reset_context_after_quit_by_using_context(self):
+        # Check that we are in content context which is used by default in Marionette
+        self.assertNotIn('chrome://', self.marionette.get_url(),
+                         "Context doesn't default to content")
+
+        with self.marionette.using_context('chrome'):
+            self.marionette.quit()
+            self.assertEqual(self.marionette.session, None)
+            self.marionette.start_session()
+            self.assertNotIn('chrome://', self.marionette.get_url(),
+                             "Not in content context after quit with using_context")
+
+    def test_keep_context_after_restart_by_set_context(self):
+        # Check that we are in content context which is used by default in Marionette
+        self.assertNotIn('chrome://', self.marionette.get_url(),
+                         "Context doesn't default to content")
+
+        # restart while we are in chrome context
+        self.marionette.set_context('chrome')
+        self.marionette.restart()
+        self.assertNotEqual(self.marionette.session["processId"], self.pid)
+        self.assertIn('chrome://', self.marionette.get_url(),
+                      "Not in chrome context after a restart with set_context")
+
+    def test_keep_context_after_restart_by_using_context(self):
+        # Check that we are in content context which is used by default in Marionette
+        self.assertNotIn('chrome://', self.marionette.get_url(),
+                         "Context doesn't default to content")
+
+        # restart while we are in chrome context
+        with self.marionette.using_context('chrome'):
+            self.marionette.restart()
+            self.assertNotEqual(self.marionette.session["processId"], self.pid)
+            self.assertIn('chrome://', self.marionette.get_url(),
+                          "Not in chrome context after a restart with using_context")
--- a/testing/mozharness/external_tools/robustcheckout.py
+++ b/testing/mozharness/external_tools/robustcheckout.py
@@ -138,16 +138,26 @@ def robustcheckout(ui, url, dest, upstre
     if not sharebase:
         raise error.Abort('share base directory not defined; refusing to operate',
                           hint='define share.pool config option or pass --sharebase')
 
     # worker.backgroundclose only makes things faster if running anti-virus,
     # which our automation doesn't. Disable it.
     ui.setconfig('worker', 'backgroundclose', False)
 
+    # By default the progress bar starts after 3s and updates every 0.1s. We
+    # change this so it shows and updates every 1.0s.
+    # We also tell progress to assume a TTY is present so updates are printed
+    # even if there is no known TTY.
+    # We make the config change here instead of in a config file because
+    # otherwise we're at the whim of whatever configs are used in automation.
+    ui.setconfig('progress', 'delay', 1.0)
+    ui.setconfig('progress', 'refresh', 1.0)
+    ui.setconfig('progress', 'assume-tty', True)
+
     sharebase = os.path.realpath(sharebase)
 
     return _docheckout(ui, url, dest, upstream, revision, branch, purge,
                        sharebase)
 
 def _docheckout(ui, url, dest, upstream, revision, branch, purge, sharebase):
     def callself():
         return _docheckout(ui, url, dest, upstream, revision, branch, purge,
--- a/testing/mozharness/mozharness/base/python.py
+++ b/testing/mozharness/mozharness/base/python.py
@@ -8,16 +8,17 @@
 '''
 
 import distutils.version
 import os
 import subprocess
 import sys
 import time
 import json
+import socket
 import traceback
 import urlparse
 
 import mozharness
 from mozharness.base.script import (
     PostScriptAction,
     PostScriptRun,
     PreScriptAction,
@@ -261,18 +262,26 @@ class VirtualenvMixin(object):
             self.fatal("install_module() doesn't understand an install_method of %s!" % install_method)
 
         # Add --find-links pages to look at. Add --trusted-host automatically if
         # the host isn't secure. This allows modern versions of pip to connect
         # without requiring an override.
         proxxy = Proxxy(self.config, self.log_obj)
         trusted_hosts = set()
         for link in proxxy.get_proxies_and_urls(c.get('find_links', [])):
+            parsed = urlparse.urlparse(link)
+
+            try:
+                socket.gethostbyname(parsed.hostname)
+            except socket.gaierror as e:
+                self.info('error resolving %s (ignoring): %s' %
+                          (parsed.hostname, e.message))
+                continue
+
             command.extend(["--find-links", link])
-            parsed = urlparse.urlparse(link)
             if parsed.scheme != 'https':
                 trusted_hosts.add(parsed.hostname)
 
         if self.pip_version >= distutils.version.LooseVersion('6.0'):
             for host in sorted(trusted_hosts):
                 command.extend(['--trusted-host', host])
 
         # module_url can be None if only specifying requirements files
@@ -395,31 +404,30 @@ class VirtualenvMixin(object):
             if c.get('virtualenv_python_dll'):
                 # We may someday want to copy a differently-named dll, but
                 # let's not think about that right now =\
                 dll_name = os.path.basename(c['virtualenv_python_dll'])
                 target = self.query_python_path(dll_name)
                 scripts_dir = os.path.dirname(target)
                 self.mkdir_p(scripts_dir)
                 self.copyfile(c['virtualenv_python_dll'], target, error_level=WARNING)
-            else:
-                self.mkdir_p(dirs['abs_work_dir'])
 
             # make this list configurable?
             for module in ('distribute', 'pip'):
                 if c.get('%s_url' % module):
                     self.download_file(c['%s_url' % module],
                                        parent_dir=dirs['abs_work_dir'])
 
             virtualenv_options = c.get('virtualenv_options',
                                        ['--no-site-packages', '--distribute'])
 
         if os.path.exists(self.query_python_path()):
             self.info("Virtualenv %s appears to already exist; skipping virtualenv creation." % self.query_python_path())
         else:
+            self.mkdir_p(dirs['abs_work_dir'])
             self.run_command(virtualenv + virtualenv_options + [venv_path],
                              cwd=dirs['abs_work_dir'],
                              error_list=VirtualenvErrorList,
                              halt_on_failure=True)
 
         # Resolve the pip version so we can conditionally do things if we have
         # a modern pip.
         pip = self.query_python_path('pip')
--- a/testing/mozharness/mozharness/base/script.py
+++ b/testing/mozharness/mozharness/base/script.py
@@ -387,24 +387,25 @@ class ScriptMixin(PlatformMixin):
         # Bug 1301855 - URLError: <urlopen error [Errno 60] Operation timed out>
         # Bug 1302237 - URLError: <urlopen error [Errno 104] Connection reset by peer>
         # Bug 1301807 - BadStatusLine: ''
         response = urllib2.urlopen(request)
 
         if parsed_url.scheme in ('http', 'https'):
             expected_file_size = int(response.headers.get('Content-Length'))
 
-        self.info('Expected file size: {}'.format(expected_file_size))
-        self.debug('Url: {}'.format(url))
-        self.info('Content-Encoding {}'.format(response.headers.get('Content-Encoding')))
-        self.info('Content-Type {}'.format(response.headers.get('Content-Type')))
-        self.info('Http code {}'.format(response.getcode()))
+        self.info('Http code: {}'.format(response.getcode()))
+        for k in ('Content-Encoding', 'Content-Type', 'via', 'x-amz-cf-id',
+                  'x-amz-version-id', 'x-cache'):
+            self.info('{}: {}'.format(k, response.headers.get(k)))
 
         file_contents = response.read()
         obtained_file_size = len(file_contents)
+        self.info('Expected file size: {}'.format(expected_file_size))
+        self.info('Obtained file size: {}'.format(obtained_file_size))
 
         if obtained_file_size != expected_file_size:
             raise FetchedIncorrectFilesize(
                 'The expected file size is {} while we got instead {}'.format(
                     expected_file_size, obtained_file_size)
             )
 
         # Use BytesIO instead of StringIO
@@ -548,17 +549,17 @@ class ScriptMixin(PlatformMixin):
             compressed_file (object): File-like object with the contents of a compressed zip file.
             extract_to (str): where to extract the compressed file.
             extract_dirs (list, optional): directories inside the archive file to extract.
                                            Defaults to '*'.
             verbose (bool, optional): whether or not extracted content should be displayed.
                                       Defaults to False.
 
         Raises:
-            zipfile.BadZipFile: on contents of zipfile being invalid
+            zipfile.BadZipfile: on contents of zipfile being invalid
         """
         with zipfile.ZipFile(compressed_file) as bundle:
             entries = self._filter_entries(bundle.namelist(), extract_dirs)
 
             for entry in entries:
                 if verbose:
                     self.info(' {}'.format(entry))
 
@@ -678,17 +679,28 @@ class ScriptMixin(PlatformMixin):
             self.fetch_url_into_memory,
             kwargs={'url': url},
             **retry_args
         )
 
         # 2) We're guaranteed to have download the file with error_level=FATAL
         #    Let's unpack the file
         function, kwargs = _determine_extraction_method_and_kwargs(url)
-        function(**kwargs)
+        try:
+            function(**kwargs)
+        except zipfile.BadZipfile:
+            # Bug 1305752 - Sometimes a good download turns out to be a
+            # corrupted zipfile. Let's upload the file for inspection
+            filepath = os.path.join(self.query_abs_dirs()['abs_upload_dir'], url.split('/')[-1])
+            self.info('Storing corrupted file to {}'.format(filepath))
+            with open(filepath, 'w') as f:
+                f.write(compressed_file.read())
+
+            # Dump the exception and exit
+            self.exception(level=FATAL)
 
 
     def load_json_url(self, url, error_level=None, *args, **kwargs):
         """ Returns a json object from a url (it retries). """
         contents = self._retry_download(
             url=url, error_level=error_level, *args, **kwargs
         )
         return json.loads(contents.read())
--- a/testing/puppeteer/firefox/firefox_puppeteer/testcases/base.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/testcases/base.py
@@ -66,34 +66,21 @@ class BaseFirefoxTestCase(unittest.TestC
             self.browser.tabbar.close_all_tabs([self.browser.tabbar.tabs[0]])
             self.browser.tabbar.tabs[0].switch_to()
 
     def restart(self, **kwargs):
         """Restart Firefox and re-initialize data.
 
         :param flags: Specific restart flags for Firefox
         """
-
-        # TODO: Bug 1148220 Marionette's in_app restart has to send 'quit-application-requested'
-        # observer notification before an in_app restart
-        with self.marionette.using_context('chrome'):
-            self.marionette.execute_script("""
-                Components.utils.import("resource://gre/modules/Services.jsm");
-                let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
-                                         .createInstance(Components.interfaces.nsISupportsPRBool);
-                Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
-                """)
         if kwargs.get('clean'):
             self.marionette.restart(clean=True)
         else:
             self.marionette.restart(in_app=True)
 
-        # Marionette doesn't keep the former context, so restore to chrome
-        self.marionette.set_context('chrome')
-
         # Ensure that we always have a valid browser instance available
         self.browser = self.windows.switch_to(lambda win: type(win) is BrowserWindow)
 
     def setUp(self, *args, **kwargs):
         super(BaseFirefoxTestCase, self).setUp(*args, **kwargs)
 
         self._start_handle_count = len(self.marionette.window_handles)
         self._init_tab_handles = self.marionette.window_handles
--- a/testing/web-platform/tests/web-animations/interfaces/AnimationTimeline/idlharness.html
+++ b/testing/web-platform/tests/web-animations/interfaces/AnimationTimeline/idlharness.html
@@ -7,17 +7,20 @@
 <script src="/resources/idlharness.js"></script>
 <div id="log"></div>
 <script type="text/plain" id="AnimationTimeline-IDL">
 interface AnimationTimeline {
   readonly attribute double? currentTime;
 };
 </script>
 <script type="text/plain" id="DocumentTimeline-IDL">
-[Constructor (DOMHighResTimeStamp originTime)]
+dictionary DocumentTimelineOptions {
+  DOMHighResTimeStamp originTime = 0;
+};
+[Constructor (optional DocumentTimelineOptions options)]
 interface DocumentTimeline : AnimationTimeline {
 };
 </script>
 <script>
 'use strict';
 
 var idlArray;
 test(function() {
--- a/testing/web-platform/tests/web-animations/interfaces/DocumentTimeline/constructor.html
+++ b/testing/web-platform/tests/web-animations/interfaces/DocumentTimeline/constructor.html
@@ -6,29 +6,37 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="../../testcommon.js"></script>
 <body>
 <div id="log"></div>
 <script>
 "use strict";
 
 test(function(t) {
-  var timeline = new DocumentTimeline(0);
+  var timeline = new DocumentTimeline();
 
   assert_times_equal(timeline.currentTime, document.timeline.currentTime);
-}, 'zero origin time');
+}, 'An origin time of zero is used when none is supplied');
 
 test(function(t) {
-  var timeline = new DocumentTimeline(10 * MS_PER_SEC);
+  var timeline = new DocumentTimeline({ originTime: 0 });
+  assert_times_equal(timeline.currentTime, document.timeline.currentTime);
+}, 'A zero origin time produces a document timeline with a current time ' +
+   'identical to the default document timeline');
+
+test(function(t) {
+  var timeline = new DocumentTimeline({ originTime: 10 * MS_PER_SEC });
 
   assert_times_equal(timeline.currentTime,
                      (document.timeline.currentTime - 10 * MS_PER_SEC));
-}, 'positive origin time');
+}, 'A positive origin time makes the document timeline\'s current time lag ' +
+   'behind the default document timeline');
 
 test(function(t) {
-  var timeline = new DocumentTimeline(-10 * MS_PER_SEC);
+  var timeline = new DocumentTimeline({ originTime: -10 * MS_PER_SEC });
 
   assert_times_equal(timeline.currentTime,
                 (document.timeline.currentTime + 10 * MS_PER_SEC));
-}, 'negative origin time');
+}, 'A negative origin time makes the document timeline\'s current time run ' +
+   'ahead of the default document timeline');
 
 </script>
 </body>
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -196,24 +196,23 @@ ExtensionContext = class extends BaseCon
     // This is the MessageSender property passed to extension.
     // It can be augmented by the "page-open" hook.
     let sender = {id: extension.uuid};
     if (uri) {
       sender.url = uri.spec;
     }
     Management.emit("page-load", this, params, sender);
 
-    // Properties in |filter| must match those in the |recipient|
-    // parameter of sendMessage.
     let filter = {extensionId: extension.id};
+    let optionalFilter = {};
     // Addon-generated messages (not necessarily from the same process as the
     // addon itself) are sent to the main process, which forwards them via the
     // parent process message manager. Specific replies can be sent to the frame
     // message manager.
-    this.messenger = new Messenger(this, [Services.cpmm, this.messageManager], sender, filter);
+    this.messenger = new Messenger(this, [Services.cpmm, this.messageManager], sender, filter, optionalFilter);
 
     if (this.externallyVisible) {
       this.extension.views.add(this);
     }
   }
 
   get cloneScope() {
     return this.contentWindow;
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -320,20 +320,19 @@ class ExtensionContext extends BaseConte
         window.XMLHttpRequest = XMLHttpRequest;
         window.fetch = fetch;
       `, this.sandbox);
     }
 
     let url = contentWindow.location.href;
     // The |sender| parameter is passed directly to the extension.
     let sender = {id: this.extension.uuid, frameId, url};
-    // Properties in |filter| must match those in the |recipient|
-    // parameter of sendMessage.
-    let filter = {extensionId: this.extension.id, frameId};
-    this.messenger = new Messenger(this, [this.messageManager], sender, filter);
+    let filter = {extensionId: this.extension.id};
+    let optionalFilter = {frameId};
+    this.messenger = new Messenger(this, [this.messageManager], sender, filter, optionalFilter);
 
     this.chromeObj = Cu.createObjectIn(this.sandbox, {defineAs: "browser"});
 
     // Sandboxes don't get Xrays for some weird compatibility
     // reason. However, we waive here anyway in case that changes.
     Cu.waiveXrays(this.sandbox).chrome = this.chromeObj;
 
     let localApis = {};
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -311,16 +311,18 @@ class BaseContext {
    * @param {object} [options.recipient]
    *
    * @returns {Promise}
    */
   sendMessage(target, messageName, data, options = {}) {
     options.recipient = options.recipient || {};
     options.sender = options.sender || {};
 
+    // TODO(robwu): This should not unconditionally be overwritten once we
+    // support onMessageExternal / onConnectExternal (bugzil.la/1258360).
     options.recipient.extensionId = this.extension.id;
     options.sender.extensionId = this.extension.id;
     options.sender.contextId = this.contextId;
 
     return MessageChannel.sendMessage(target, messageName, data, options);
   }
 
   get lastError() {
@@ -1152,17 +1154,18 @@ function promiseObserved(topic, test = (
 
 let gNextPortId = 1;
 
 /**
  * Abstraction for a Port object in the extension API.
  *
  * @param {BaseContext} context The context that owns this port.
  * @param {nsIMessageSender} senderMM The message manager to send messages to.
- * @param {Array<nsIMessageSender>} receiverMMs Message managers to listen on.
+ * @param {Array<nsIMessageListenerManager>} receiverMMs Message managers to
+ *     listen on.
  * @param {string} name Arbitrary port name as defined by the addon.
  * @param {string} id An ID that uniquely identifies this port's channel.
  * @param {object} sender The `port.sender` property.
  * @param {object} recipient The recipient of messages sent from this port.
  */
 function Port(context, senderMM, receiverMMs, name, id, sender, recipient) {
   this.context = context;
   this.senderMM = senderMM;
@@ -1289,28 +1292,45 @@ Port.prototype = {
 
 function getMessageManager(target) {
   if (target instanceof Ci.nsIFrameLoaderOwner) {
     return target.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
   }
   return target.QueryInterface(Ci.nsIMessageSender);
 }
 
-// Each extension scope gets its own Messenger object. It handles the
-// basics of sendMessage, onMessage, connect, and onConnect.
-//
-// |context| is the extension scope.
-// |messageManagers| is an array of MessageManagers used to receive messages.
-// |sender| is an object describing the sender (usually giving its extension id, tabId, etc.)
-// |filter| is a recipient filter to apply to incoming messages from the broker.
-function Messenger(context, messageManagers, sender, filter) {
+/**
+ * Each extension context gets its own Messenger object. It handles the
+ * basics of sendMessage, onMessage, connect and onConnect.
+ *
+ * @param {BaseContext} context The context to which this Messenger is tied.
+ * @param {Array<nsIMessageListenerManager>} messageManagers
+ *     The message managers used to receive messages (e.g. onMessage/onConnect
+ *     requests).
+ * @param {object} sender Describes this sender to the recipient. This object
+ *     is extended further by BaseContext's sendMessage method and appears as
+ *     the `sender` object to `onConnect` and `onMessage`.
+ *     Do not set the `extensionId`, `contextId` or `tab` properties. The former
+ *     two are added by BaseContext's sendMessage, while `sender.tab` is set by
+ *     the ProxyMessenger in the main process.
+ * @param {object} filter A recipient filter to apply to incoming messages from
+ *     the broker. Messages are only handled by this Messenger if all key-value
+ *     pairs match the `recipient` as specified by the sender of the message.
+ *     In other words, this filter defines the required fields of `recipient`.
+ * @param {object} [optionalFilter] An additional filter to apply to incoming
+ *     messages. Unlike `filter`, the keys from `optionalFilter` are allowed to
+ *     be omitted from `recipient`. Only keys that are present in both
+ *     `optionalFilter` and `recipient` are applied to filter incoming messages.
+ */
+function Messenger(context, messageManagers, sender, filter, optionalFilter) {
   this.context = context;
   this.messageManagers = messageManagers;
   this.sender = sender;
   this.filter = filter;
+  this.optionalFilter = optionalFilter;
 
   MessageChannel.setupMessageManagers(messageManagers);
 }
 
 Messenger.prototype = {
   _sendMessage(messageManager, message, data, recipient) {
     let options = {
       recipient,
@@ -1332,17 +1352,18 @@ Messenger.prototype = {
       });
 
     return this.context.wrapPromise(promise, responseCallback);
   },
 
   onMessage(name) {
     return new SingletonEventManager(this.context, name, callback => {
       let listener = {
-        messageFilterPermissive: this.filter,
+        messageFilterPermissive: this.optionalFilter,
+        messageFilterStrict: this.filter,
 
         filterMessage: (sender, recipient) => {
           // Ignore the message if it was sent by this Messenger.
           return sender.contextId !== this.context.contextId;
         },
 
         receiveMessage: ({target, data: message, sender, recipient}) => {
           if (!this.context.active) {
@@ -1388,17 +1409,18 @@ Messenger.prototype = {
     this._sendMessage(messageManager, "Extension:Connect", msg, recipient)
       .catch(e => port.disconnectByOtherEnd());
     return port.api();
   },
 
   onConnect(name) {
     return new SingletonEventManager(this.context, name, callback => {
       let listener = {
-        messageFilterPermissive: this.filter,
+        messageFilterPermissive: this.optionalFilter,
+        messageFilterStrict: this.filter,
 
         filterMessage: (sender, recipient) => {
           // Ignore the port if it was created by this Messenger.
           return sender.contextId !== this.context.contextId;
         },
 
         receiveMessage: ({target, data: message, sender}) => {
           let {name, portId} = message;
--- a/toolkit/components/extensions/MessageChannel.jsm
+++ b/toolkit/components/extensions/MessageChannel.jsm
@@ -133,17 +133,17 @@ class FilteringMessageManager {
    *         An object containing either a `handler` or an `error` property.
    *         If no error occurs, `handler` will be a matching handler that
    *         was registered by `addHandler`. Otherwise, the `error` property
    *         will contain an object describing the error.
    *
    *        data:
    *          An object describing the message, as defined in
    *          `MessageChannel.addListener`.
-   * @param {nsIMessageManager} messageManager
+   * @param {nsIMessageListenerManager} messageManager
    */
   constructor(messageName, callback, messageManager) {
     this.messageName = messageName;
     this.callback = callback;
     this.messageManager = messageManager;
 
     this.messageManager.addMessageListener(this.messageName, this, true);
 
@@ -241,17 +241,17 @@ class FilteringMessageManagerMap extends
     this.messageName = messageName;
     this.callback = callback;
   }
 
   /**
    * Returns, and possibly creates, a message broker for the given
    * message manager.
    *
-   * @param {nsIMessageSender} target
+   * @param {nsIMessageListenerManager} target
    *     The message manager for which to return a broker.
    *
    * @returns {FilteringMessageManager}
    */
   get(target) {
     if (this.has(target)) {
       return super.get(target);
     }
@@ -343,17 +343,17 @@ this.MessageChannel = {
   /**
    * Fire-and-forget: The sender of this message does not expect a reply.
    */
   RESPONSE_NONE: 3,
 
   /**
    * Initializes message handlers for the given message managers if needed.
    *
-   * @param {[nsIMessageSender]} messageManagers
+   * @param {Array<nsIMessageListenerManager>} messageManagers
    */
   setupMessageManagers(messageManagers) {
     for (let mm of messageManagers) {
       // This call initializes a FilteringMessageManager for |mm| if needed.
       // The FilteringMessageManager must be created to make sure that senders
       // of messages that expect a reply, such as MessageChannel:Message, do
       // actually receive a default reply even if there are no explicit message
       // handlers.
@@ -387,17 +387,17 @@ this.MessageChannel = {
     return Object.keys(filter).every(key => {
       return !(key in data) || data[key] === filter[key];
     });
   },
 
   /**
    * Adds a message listener to the given message manager.
    *
-   * @param {nsIMessageSender|[nsIMessageSender]} targets
+   * @param {nsIMessageListenerManager|Array<nsIMessageListenerManager>} targets
    *    The message managers on which to listen.
    * @param {string|number} messageName
    *    The name of the message to listen for.
    * @param {MessageReceiver} handler
    *    The handler to dispatch to. Must be an object with the following
    *    properties:
    *
    *      receiveMessage:
@@ -453,17 +453,17 @@ this.MessageChannel = {
     for (let target of [].concat(targets)) {
       this.messageManagers.get(target).addHandler(messageName, handler);
     }
   },
 
   /**
    * Removes a message listener from the given message manager.
    *
-   * @param {nsIMessageSender|Array<nsIMessageSender>} targets
+   * @param {nsIMessageListenerManager|Array<nsIMessageListenerManager>} targets
    *    The message managers on which to stop listening.
    * @param {string|number} messageName
    *    The name of the message to stop listening for.
    * @param {MessageReceiver} handler
    *    The handler to stop dispatching to.
    */
   removeListener(targets, messageName, handler) {
     for (let target of [].concat(targets)) {
@@ -602,17 +602,17 @@ this.MessageChannel = {
    * appropriate `MessageReceivers`, and routing the responses back to the
    * original senders.
    *
    * Each handler object is a `MessageReceiver` object as passed to
    * `addListener`.
    *
    * @param {Array<MessageHandler>} handlers
    * @param {object} data
-   * @param {nsIMessageSender|nsIMessageManagerOwner} data.target
+   * @param {nsIMessageSender|{messageManager:nsIMessageSender}} data.target
    */
   _handleMessage(handlers, data) {
     // The target passed to `receiveMessage` is sometimes a message manager
     // owner instead of a message manager, so make sure to convert it to a
     // message manager first if necessary.
     let {target} = data;
     if (!(target instanceof Ci.nsIMessageSender)) {
       target = target.messageManager;
@@ -681,17 +681,17 @@ this.MessageChannel = {
   /**
    * Handles message callbacks from the response brokers.
    *
    * Each handler object is a deferred object created by `sendMessage`, and
    * should be resolved or rejected based on the contents of the response.
    *
    * @param {Array<MessageHandler>} handlers
    * @param {object} data
-   * @param {nsIMessageSender|nsIMessageManagerOwner} data.target
+   * @param {nsIMessageSender|{messageManager:nsIMessageSender}} data.target
    */
   _handleResponse(handlers, data) {
     // If we have an error at this point, we have handler to report it to,
     // so just log it.
     if (handlers.length == 0) {
       Cu.reportError(`No matching message response handler for ${data.messageName}`);
     } else if (handlers.length > 1) {
       Cu.reportError(`Multiple matching response handlers for ${data.messageName}`);
@@ -757,17 +757,17 @@ this.MessageChannel = {
       }
     }
   },
 
   /**
    * Aborts any pending message responses to the broker for the given
    * message manager.
    *
-   * @param {nsIMessageSender} target
+   * @param {nsIMessageListenerManager} target
    *    The message manager for which to abort brokers.
    * @param {object} reason
    *    An object describing the reason the responses were aborted.
    *    Will be passed to the promise rejection handler of all aborted
    *    responses.
    */
   abortMessageManager(target, reason) {
     for (let response of this.pendingResponses) {
--- a/toolkit/components/extensions/ext-downloads.js
+++ b/toolkit/components/extensions/ext-downloads.js
@@ -414,19 +414,16 @@ extensions.registerSchemaAPI("downloads"
         }
 
         if (options.conflictAction == "prompt") {
           // TODO
           return Promise.reject({message: "conflictAction prompt not yet implemented"});
         }
 
         function createTarget(downloadsDir) {
-          // TODO
-          // if (options.saveAs) { }
-
           let target;
           if (filename) {
             target = OS.Path.join(downloadsDir, filename);
           } else {
             let uri = NetUtil.newURI(options.url);
 
             let remote = "download";
             if (uri instanceof Ci.nsIURL) {
@@ -450,17 +447,39 @@ extensions.registerSchemaAPI("downloads"
                 default:
                   target = DownloadPaths.createNiceUniqueFile(new FileUtils.File(target)).path;
                   break;
 
                 case "overwrite":
                   break;
               }
             }
-            return target;
+          }).then(() => {
+            if (!options.saveAs) {
+              return Promise.resolve(target);
+            }
+
+            // Setup the file picker Save As dialog.
+            const picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+            const window = Services.wm.getMostRecentWindow("navigator:browser");
+            picker.init(window, null, Ci.nsIFilePicker.modeSave);
+            picker.displayDirectory = new FileUtils.File(dir);
+            picker.appendFilters(Ci.nsIFilePicker.filterAll);
+            picker.defaultString = OS.Path.basename(target);
+
+            // Open the dialog and resolve/reject with the result.
+            return new Promise((resolve, reject) => {
+              picker.open(result => {
+                if (result === Ci.nsIFilePicker.returnCancel) {
+                  reject({message: "Download canceled by the user"});
+                } else {
+                  resolve(picker.file.path);
+                }
+              });
+            });
           });
         }
 
         let download;
         return Downloads.getPreferredDownloadsDirectory()
           .then(downloadsDir => createTarget(downloadsDir))
           .then(target => Downloads.createDownload({
             source: options.url,
--- a/toolkit/components/extensions/ext-webRequest.js
+++ b/toolkit/components/extensions/ext-webRequest.js
@@ -38,16 +38,21 @@ function WebRequestEventManager(context,
         originUrl: data.originUrl,
         method: data.method,
         type: data.type,
         timeStamp: Date.now(),
         frameId: ExtensionManagement.getFrameId(data.windowId),
         parentFrameId: ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId),
       };
 
+      const maybeCached = ["onResponseStarted", "onBeforeRedirect", "onCompleted", "onErrorOccurred"];
+      if (maybeCached.includes(eventName)) {
+        data2.fromCache = !!data.fromCache;
+      }
+
       if ("ip" in data) {
         data2.ip = data.ip;
       }
 
       // Fills in tabId typically.
       let result = {};
       extensions.emit("fill-browser-data", data.browser, data2, result);
       if (result.cancel) {
--- a/toolkit/components/extensions/schemas/downloads.json
+++ b/toolkit/components/extensions/schemas/downloads.json
@@ -376,17 +376,16 @@
                 "optional": true,
                 "type": "string"
               },
               "conflictAction": {
                 "$ref": "FilenameConflictAction",
                 "optional": true
               },
               "saveAs": {
-                "unsupported": true,
                 "description": "Use a file-chooser to allow the user to select a filename.",
                 "optional": true,
                 "type": "boolean"
               },
               "method": {
                 "unsupported": true,
                 "description": "The HTTP method to use if the URL uses the HTTP[S] protocol.",
                 "enum": [
--- a/toolkit/components/extensions/test/mochitest/chrome.ini
+++ b/toolkit/components/extensions/test/mochitest/chrome.ini
@@ -22,8 +22,9 @@ skip-if = os != "mac" && os != "linux"
 skip-if = buildapp == 'b2g'
 [test_ext_cookies_permissions.html]
 skip-if = buildapp == 'b2g'
 [test_ext_jsversion.html]
 skip-if = buildapp == 'b2g'
 [test_ext_schema.html]
 [test_chrome_ext_storage_cleanup.html]
 [test_chrome_ext_idle.html]
+[test_chrome_ext_downloads_saveAs.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_downloads_saveAs.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<html>
+<head>
+  <title>Test downloads.download() saveAs option</title>
+  <script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
+  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <link rel="stylesheet" href="chrome://mochikit/contents/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="text/javascript">
+"use strict";
+
+add_task(function* test_downloads_saveAs() {
+
+  function background() {
+    const url = URL.createObjectURL(new Blob(['file content']));
+    browser.test.onMessage.addListener(() =>
+      browser.downloads.download({url, saveAs: true})
+        .then(id => browser.downloads.onChanged.addListener(delta => {
+          if (delta.state.current === "complete") {
+            browser.test.sendMessage("done", {ok: true, id});
+          }
+        })).catch(({message}) => {
+          browser.test.sendMessage("done", {ok: false, message});
+        }));
+    browser.test.sendMessage("ready");
+  }
+
+  const {MockFilePicker} = SpecialPowers;
+  const manifest = {background, manifest: {permissions: ["downloads"]}};
+  const extension = ExtensionTestUtils.loadExtension(manifest);
+
+  MockFilePicker.init(window);
+  MockFilePicker.useAnyFile();
+  const [file] = MockFilePicker.returnFiles;
+
+  yield extension.startup();
+  yield extension.awaitMessage("ready");
+
+  extension.sendMessage("download");
+  let result = yield extension.awaitMessage("done");
+
+  ok(result.ok, "downloads.download() works with saveAs");
+  is(file.fileSize, 12, "downloaded file is the correct size");
+  file.remove(false);
+
+  // Test the user canceling the save dialog.
+  MockFilePicker.returnValue = MockFilePicker.returnCancel;
+
+  extension.sendMessage("download");
+  result = yield extension.awaitMessage("done");
+
+  ok(!result.ok, "download rejected if the user cancels the dialog");
+  is(result.message, "Download canceled by the user", "with the correct message");
+  ok(!file.exists(), "file was not downloaded");
+
+  yield extension.unload();
+  MockFilePicker.cleanup();
+});
+
+</script>
+
+</body>
+</html>
--- a/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html
@@ -425,48 +425,59 @@ function backgroundScript() {
       if (/^https?:/.test(details.url)) {
         browser.test.assertEq(details.ip, "127.0.0.1", "correct ip");
       }
       completedUrls[kind].add(details.url);
     }
     checkStatus(details);
   }
 
+  function checkFromCache(kind, details) {
+    if (checkCompleted) {
+      // If we have already completed a GET request for this url,
+      // and it was found, we expect for the response to come fromCache.
+      const completed = kind in completedUrls && completedUrls[kind].has(details.url);
+      const expected = completed && details.method === "GET" && details.statusCode != 404;
+      browser.test.assertEq(expected, details.fromCache, "fromCache is correct");
+    }
+    checkIpAndRecord(kind, details);
+  }
+
   function onHeadersReceived(details) {
     checkIpAndRecord("headersReceived", details);
     processHeaders("response", details);
     browser.test.log(`After processing response headers: ${details.responseHeaders.toSource()}`);
     return {responseHeaders: details.responseHeaders};
   }
 
   function onErrorOccurred(details) {
     onRecord("error", details);
     browser.test.assertTrue(/^NS_ERROR_/.test(details.error), `onErrorOccurred reported for ${details.url} (${details.error})`);
   }
 
   function onCompleted(details) {
-    checkIpAndRecord("completed", details);
+    checkFromCache("completed", details);
     checkHeaders("response", details);
   }
 
   browser.webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: ["<all_urls>"]}, ["blocking"]);
   try {
     browser.webRequest.onBeforeRequest.addListener(onUpload, {urls: ["http://*/*"]}, ["blocking", "requestBody"]);
   } catch (e) {
     // requestBody is disabled in release builds
     if (!/\brequestBody\b/.test(e.message)) {
       throw e;
     }
   }
   browser.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: ["<all_urls>"]}, ["blocking", "requestHeaders"]);
   browser.webRequest.onSendHeaders.addListener(onSendHeaders, {urls: ["<all_urls>"]}, ["requestHeaders"]);
   browser.webRequest.onBeforeRedirect.addListener(onBeforeRedirect, {urls: ["<all_urls>"]});
   browser.webRequest.onHeadersReceived.addListener(onHeadersReceived, {urls: ["<all_urls>"]}, ["blocking", "responseHeaders"]);
-  browser.webRequest.onResponseStarted.addListener(checkIpAndRecord.bind(null, "responseStarted"), {urls: ["<all_urls>"]});
-  browser.webRequest.onResponseStarted.addListener(checkIpAndRecord.bind(null, "responseStarted2"), {urls: ["<all_urls>"]});
+  browser.webRequest.onResponseStarted.addListener(checkFromCache.bind(null, "responseStarted"), {urls: ["<all_urls>"]});
+  browser.webRequest.onResponseStarted.addListener(checkFromCache.bind(null, "responseStarted2"), {urls: ["<all_urls>"]});
   browser.webRequest.onErrorOccurred.addListener(onErrorOccurred, {urls: ["<all_urls>"]});
   browser.webRequest.onCompleted.addListener(onCompleted, {urls: ["<all_urls>"]}, ["responseHeaders"]);
 
   function onTestMessage(msg) {
     if (msg == "skipCompleted") {
       checkCompleted = false;
       browser.test.sendMessage("ackSkipCompleted");
     } else {
--- a/toolkit/components/places/History.jsm
+++ b/toolkit/components/places/History.jsm
@@ -474,17 +474,17 @@ function validatePageInfo(pageInfo) {
   };
 
   if (!pageInfo.url) {
     throw new TypeError("PageInfo object must have a url property");
   }
 
   info.url = normalizeToURLOrGUID(pageInfo.url);
 
-  if (typeof pageInfo.title === "string" && pageInfo.title.length) {
+  if (typeof pageInfo.title === "string") {
     info.title = pageInfo.title;
   } else if (pageInfo.title != null && pageInfo.title != undefined) {
     throw new TypeError(`title property of PageInfo object: ${pageInfo.title} must be a string if provided`);
   }
 
   if (!pageInfo.visits || !Array.isArray(pageInfo.visits) || !pageInfo.visits.length) {
     throw new TypeError("PageInfo object must have an array of visits");
   }
--- a/toolkit/components/places/tests/PlacesTestUtils.jsm
+++ b/toolkit/components/places/tests/PlacesTestUtils.jsm
@@ -23,79 +23,66 @@ this.PlacesTestUtils = Object.freeze({
    *
    * @param aPlaceInfo
    *        Can be an nsIURI, in such a case a single LINK visit will be added.
    *        Otherwise can be an object describing the visit to add, or an array
    *        of these objects:
    *          { uri: nsIURI of the page,
    *            [optional] transition: one of the TRANSITION_* from nsINavHistoryService,
    *            [optional] title: title of the page,
-   *            [optional] visitDate: visit date in microseconds from the epoch
+   *            [optional] visitDate: visit date, either in microseconds from the epoch or as a date object
    *            [optional] referrer: nsIURI of the referrer for this visit
    *          }
    *
    * @return {Promise}
    * @resolves When all visits have been added successfully.
    * @rejects JavaScript exception.
    */
   addVisits: Task.async(function* (placeInfo) {
     let places = [];
+    let infos = [];
+
     if (placeInfo instanceof Ci.nsIURI ||
         placeInfo instanceof URL ||
         typeof placeInfo == "string") {
       places.push({ uri: placeInfo });
     }
     else if (Array.isArray(placeInfo)) {
       places = places.concat(placeInfo);
     } else if (typeof placeInfo == "object" && placeInfo.uri) {
       places.push(placeInfo)
     } else {
       throw new Error("Unsupported type passed to addVisits");
     }
 
-    // Create mozIVisitInfo for each entry.
-    let now = Date.now();
+    // Create a PageInfo for each entry.
     for (let place of places) {
-      if (typeof place.uri == "string") {
-        place.uri = NetUtil.newURI(place.uri);
-      } else if (place.uri instanceof URL) {
-        place.uri = NetUtil.newURI(place.uri.href);
-      }
-      if (typeof place.title != "string") {
-        place.title = "test visit for " + place.uri.spec;
-      }
+      let info = {url: place.uri};
+      info.title = (typeof place.title === "string") ? place.title : "test visit for " + info.url.spec ;
       if (typeof place.referrer == "string") {
         place.referrer = NetUtil.newURI(place.referrer);
       } else if (place.referrer && place.referrer instanceof URL) {
         place.referrer = NetUtil.newURI(place.referrer.href);
       }
-      place.visits = [{
-        transitionType: place.transition === undefined ? Ci.nsINavHistoryService.TRANSITION_LINK
-                                                       : place.transition,
-        visitDate: place.visitDate || (now++) * 1000,
-        referrerURI: place.referrer
+      let visitDate = place.visitDate;
+      if (visitDate) {
+        if (!(visitDate instanceof Date)) {
+          visitDate = PlacesUtils.toDate(visitDate);
+        }
+      } else {
+        visitDate = new Date();
+      }
+      info.visits = [{
+        transition: place.transition,
+        date: visitDate,
+        referrer: place.referrer
       }];
+      infos.push(info);
     }
-
-    yield new Promise((resolve, reject) => {
-      PlacesUtils.asyncHistory.updatePlaces(
-        places,
-        {
-          handleError(resultCode, placeInfo) {
-            let ex = new Components.Exception("Unexpected error in adding visits.",
-                                              resultCode);
-            reject(ex);
-          },
-          handleResult: function () {},
-          handleCompletion() {
-            resolve();
-          }
-        }
-      );
-    });
+    return PlacesUtils.history.insertMany(infos);
   }),
 
   /**
    * Clear all history.
    *
    * @return {Promise}
    * @resolves When history was cleared successfully.
    * @rejects JavaScript exception.
--- a/toolkit/components/places/tests/expiration/test_notifications_onDeleteVisits.js
+++ b/toolkit/components/places/tests/expiration/test_notifications_onDeleteVisits.js
@@ -70,21 +70,27 @@ add_task(function* test_notifications_on
   setMaxPages(0);
 
   for (let testIndex = 1; testIndex <= tests.length; testIndex++) {
     let currentTest = tests[testIndex -1];
     print("\nTEST " + testIndex + ": " + currentTest.desc);
     currentTest.receivedNotifications = 0;
 
     // Setup visits.
-    let now = getExpirablePRTime();
+    let timeInMicroseconds = getExpirablePRTime(8);
+
+    function newTimeInMicroseconds() {
+      timeInMicroseconds = timeInMicroseconds + 1000;
+      return timeInMicroseconds;
+    }
+
     for (let j = 0; j < currentTest.visitsPerPage; j++) {
       for (let i = 0; i < currentTest.addPages; i++) {
         let page = "http://" + testIndex + "." + i + ".mozilla.org/";
-        yield PlacesTestUtils.addVisits({ uri: uri(page), visitDate: now++ });
+        yield PlacesTestUtils.addVisits({ uri: uri(page), visitDate: newTimeInMicroseconds() });
       }
     }
 
     // Setup bookmarks.
     currentTest.bookmarks = [];
     for (let i = 0; i < currentTest.addBookmarks; i++) {
       let page = "http://" + testIndex + "." + i + ".mozilla.org/";
       yield PlacesUtils.bookmarks.insert({
--- a/toolkit/components/places/tests/favicons/test_page-icon_protocol.js
+++ b/toolkit/components/places/tests/favicons/test_page-icon_protocol.js
@@ -25,17 +25,17 @@ function fetchIconForSpec(spec) {
     });
   });
 }
 
 var gDefaultFavicon;
 var gFavicon;
 
 add_task(function* setup() {
-  PlacesTestUtils.addVisits({ uri: TEST_URI });
+  yield PlacesTestUtils.addVisits({ uri: TEST_URI });
 
   PlacesUtils.favicons.replaceFaviconDataFromDataURL(
     ICON_URI, ICON_DATA, (Date.now() + 8640000) * 1000,
     Services.scriptSecurityManager.getSystemPrincipal());
 
   yield new Promise(resolve => {
     PlacesUtils.favicons.setAndFetchFaviconForPage(
       TEST_URI, ICON_URI, false,
--- a/toolkit/components/places/tests/queries/head_queries.js
+++ b/toolkit/components/places/tests/queries/head_queries.js
@@ -18,21 +18,20 @@ Cu.import("resource://gre/modules/Servic
   Services.scriptloader.loadSubScript(uri.spec, this);
 }
 
 // Put any other stuff relative to this test folder below.
 
 
 // Some Useful Date constants - PRTime uses microseconds, so convert
 const DAY_MICROSEC = 86400000000;
-const today = Date.now() * 1000;
+const today = PlacesUtils.toPRTime(Date.now());
 const yesterday = today - DAY_MICROSEC;
 const lastweek = today - (DAY_MICROSEC * 7);
 const daybefore = today - (DAY_MICROSEC * 2);
-const tomorrow = today + DAY_MICROSEC;
 const old = today - (DAY_MICROSEC * 3);
 const futureday = today + (DAY_MICROSEC * 3);
 const olderthansixmonths = today - (DAY_MICROSEC * 31 * 7);
 
 
 /**
  * Generalized function to pull in an array of objects of data and push it into
  * the database.  It does NOT do any checking to see that the input is
--- a/toolkit/components/places/tests/queries/test_history_queries_tags_liveUpdate.js
+++ b/toolkit/components/places/tests/queries/test_history_queries_tags_liveUpdate.js
@@ -1,40 +1,46 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // This test ensures that tags changes are correctly live-updated in a history
 // query.
 
-var gNow = Date.now();
+let timeInMicroseconds = PlacesUtils.toPRTime(Date.now() - 10000);
+
+function newTimeInMicroseconds() {
+  timeInMicroseconds = timeInMicroseconds + 1000;
+  return timeInMicroseconds;
+}
+
 var gTestData = [
   {
     isVisit: true,
     uri: "http://example.com/1/",
-    lastVisit: gNow,
+    lastVisit: newTimeInMicroseconds(),
     isInQuery: true,
     isBookmark: true,
     parentGuid: PlacesUtils.bookmarks.unfiledGuid,
     index: PlacesUtils.bookmarks.DEFAULT_INDEX,
     title: "example1",
   },
   {
     isVisit: true,
     uri: "http://example.com/2/",
-    lastVisit: gNow++,
+    lastVisit: newTimeInMicroseconds(),
     isInQuery: true,
     isBookmark: true,
     parentGuid: PlacesUtils.bookmarks.unfiledGuid,
     index: PlacesUtils.bookmarks.DEFAULT_INDEX,
     title: "example2",
   },
   {
     isVisit: true,
     uri: "http://example.com/3/",
-    lastVisit: gNow++,
+    lastVisit: newTimeInMicroseconds(),
     isInQuery: true,
     isBookmark: true,
     parentGuid: PlacesUtils.bookmarks.unfiledGuid,
     index: PlacesUtils.bookmarks.DEFAULT_INDEX,
     title: "example3",
   },
 ];
 
--- a/toolkit/components/places/tests/queries/test_history_queries_titles_liveUpdate.js
+++ b/toolkit/components/places/tests/queries/test_history_queries_titles_liveUpdate.js
@@ -1,34 +1,40 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // This test ensures that tags changes are correctly live-updated in a history
 // query.
 
-var gNow = Date.now();
+let timeInMicroseconds = PlacesUtils.toPRTime(Date.now() - 10000);
+
+function newTimeInMicroseconds() {
+  timeInMicroseconds = timeInMicroseconds + 1000;
+  return timeInMicroseconds;
+}
+
 var gTestData = [
   {
     isVisit: true,
     uri: "http://example.com/1/",
-    lastVisit: gNow,
+    lastVisit: newTimeInMicroseconds(),
     isInQuery: true,
     title: "title1",
   },
   {
     isVisit: true,
     uri: "http://example.com/2/",
-    lastVisit: gNow++,
+    lastVisit: newTimeInMicroseconds(),
     isInQuery: true,
     title: "title2",
   },
   {
     isVisit: true,
     uri: "http://example.com/3/",
-    lastVisit: gNow++,
+    lastVisit: newTimeInMicroseconds(),
     isInQuery: true,
     title: "title3",
   },
 ];
 
 function searchNodeHavingUrl(aRoot, aUrl) {
   for (let i = 0; i < aRoot.childCount; i++) {
     if (aRoot.getChild(i).uri == aUrl) {
--- a/toolkit/components/places/tests/queries/test_redirects.js
+++ b/toolkit/components/places/tests/queries/test_redirects.js
@@ -206,47 +206,52 @@ add_task(function* test_add_visits_to_da
     Ci.nsINavHistoryService.TRANSITION_FRAMED_LINK,
     // Would make hard sorting by visit date because last_visit_date is actually
     // calculated excluding download transitions, but the query includes
     // downloads.
     // TODO: Bug 488966 could fix this behavior.
     //Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
   ];
 
+  function newTimeInMicroseconds() {
+    timeInMicroseconds = timeInMicroseconds - 1000;
+    return timeInMicroseconds;
+  }
+
   // we add a visit for each of the above transition types.
   t.forEach(transition => visits.push(
     { isVisit: true,
       transType: transition,
       uri: "http://" + transition + ".example.com/",
       title: transition + "-example",
       isRedirect: true,
-      lastVisit: timeInMicroseconds--,
+      lastVisit: newTimeInMicroseconds(),
       visitCount: (transition == Ci.nsINavHistoryService.TRANSITION_EMBED ||
                    transition == Ci.nsINavHistoryService.TRANSITION_FRAMED_LINK) ? 0 : visitCount++,
       isInQuery: true }));
 
   // Add a REDIRECT_TEMPORARY layer of visits for each of the above visits.
   t.forEach(transition => visits.push(
     { isVisit: true,
       transType: Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY,
       uri: "http://" + transition + ".redirect.temp.example.com/",
       title: transition + "-redirect-temp-example",
-      lastVisit: timeInMicroseconds--,
+      lastVisit: newTimeInMicroseconds(),
       isRedirect: true,
       referrer: "http://" + transition + ".example.com/",
       visitCount: visitCount++,
       isInQuery: true }));
 
   // Add a REDIRECT_PERMANENT layer of visits for each of the above redirects.
   t.forEach(transition => visits.push(
     { isVisit: true,
       transType: Ci.nsINavHistoryService.TRANSITION_REDIRECT_PERMANENT,
       uri: "http://" + transition + ".redirect.perm.example.com/",
       title: transition + "-redirect-perm-example",
-      lastVisit: timeInMicroseconds--,
+      lastVisit: newTimeInMicroseconds(),
       isRedirect: true,
       referrer: "http://" + transition + ".redirect.temp.example.com/",
       visitCount: visitCount++,
       isInQuery: true }));
 
   // Add a REDIRECT_PERMANENT layer of visits that loop to the first visit.
   // These entries should not change visitCount or lastVisit, otherwise
   // guessing an order would be a nightmare.
--- a/toolkit/components/places/tests/queries/test_results-as-visit.js
+++ b/toolkit/components/places/tests/queries/test_results-as-visit.js
@@ -1,23 +1,29 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 var testData = [];
-var now = Date.now() * 1000;
+var timeInMicroseconds = PlacesUtils.toPRTime(Date.now() - 10000);
+
+function newTimeInMicroseconds() {
+  timeInMicroseconds = timeInMicroseconds + 1000;
+  return timeInMicroseconds;
+}
+
 function createTestData() {
   function generateVisits(aPage) {
     for (var i = 0; i < aPage.visitCount; i++) {
       testData.push({ isInQuery: aPage.inQuery,
                       isVisit: true,
                       title: aPage.title,
                       uri: aPage.uri,
-                      lastVisit: now++,
+                      lastVisit: newTimeInMicroseconds(),
                       isTag: aPage.tags && aPage.tags.length > 0,
                       tagArray: aPage.tags });
     }
   }
 
   var pages = [
     { uri: "http://foo.com/", title: "amo", tags: ["moz"], visitCount: 3, inQuery: true },
     { uri: "http://moilla.com/", title: "bMoz", tags: ["bugzilla"], visitCount: 5, inQuery: true },
@@ -82,32 +88,32 @@ add_task(function* test_results_as_visit
                     uri: "http://foo.mail.com/changeme2.html" }];
    yield task_populateDB(change2);
    do_check_true(isInResult(change2, root));
 
    // Update some visits - add one and take one out of query set, and simply
    // change one so that it still applies to the query.
    do_print("Updating More Items");
    var change3 = [{ isVisit: true,
-                    lastVisit: now++,
+                    lastVisit: newTimeInMicroseconds(),
                     uri: "http://foo.mail.com/changeme1.html",
                     title: "foo"},
                   { isVisit: true,
-                    lastVisit: now++,
+                    lastVisit: newTimeInMicroseconds(),
                     uri: "http://foo.mail.com/changeme3.html",
                     title: "moz",
                     isTag: true,
                     tagArray: ["foo", "moz"] }];
    yield task_populateDB(change3);
    do_check_false(isInResult({uri: "http://foo.mail.com/changeme1.html"}, root));
    do_check_true(isInResult({uri: "http://foo.mail.com/changeme3.html"}, root));
 
    // And now, delete one
    do_print("Delete item outside of batch");
    var change4 = [{ isVisit: true,
-                    lastVisit: now++,
+                    lastVisit: newTimeInMicroseconds(),
                     uri: "http://moilla.com/",
                     title: "mo,z" }];
    yield task_populateDB(change4);
    do_check_false(isInResult(change4, root));
 
    root.containerOpen = false;
 });
--- a/toolkit/components/places/tests/queries/test_searchterms-domain.js
+++ b/toolkit/components/places/tests/queries/test_searchterms-domain.js
@@ -37,25 +37,25 @@
     uri: "https://foo.com/", lastVisit: today},
 
    // Begin the invalid queries: wrong search term
    {isInQuery: false, isVisit:true, isDetails: true, title: "m o z",
     uri: "http://foo.com/tooearly.php", lastVisit: today},
 
    // Test bad URI
    {isInQuery: false, isVisit:true, isDetails: true, title: "moz",
-    uri: "http://sffoo.com/justwrong.htm", lastVisit: tomorrow},
+    uri: "http://sffoo.com/justwrong.htm", lastVisit: yesterday},
 
    // Test what we do with escaping in titles
    {isInQuery: false, isVisit:true, isDetails: true, title: "m%0o%0z",
     uri: "http://foo.com/changeme1.htm", lastVisit: yesterday},
 
    // Test another invalid title - for updating later
    {isInQuery: false, isVisit:true, isDetails: true, title: "m,oz",
-    uri: "http://foo.com/changeme2.htm", lastVisit: tomorrow}];
+    uri: "http://foo.com/changeme2.htm", lastVisit: yesterday}];
 
 /**
  * This test will test Queries that use relative search terms and domain options
  */
 function run_test()
 {
   run_next_test();
 }
--- a/toolkit/components/places/tests/queries/test_searchterms-uri.js
+++ b/toolkit/components/places/tests/queries/test_searchterms-uri.js
@@ -31,17 +31,17 @@
       title: "hugelongconfmozlagurationofwordswithasearchtermsinit whoo-hoo"},
 
     // Test what we do with escaping in titles
     {isInQuery: false, isVisit:true, isDetails: true, title: "m%0o%0z",
      uri: "http://foo.com/changeme1.htm", lastVisit: yesterday},
 
     // Test another invalid title - for updating later
     {isInQuery: false, isVisit:true, isDetails: true, title: "m,oz",
-     uri: "http://foo.com/changeme2.htm", lastVisit: tomorrow}];
+     uri: "http://foo.com/changeme2.htm", lastVisit: yesterday}];
 
 /**
  * This test will test Queries that use relative search terms and URI options
  */
 function run_test()
 {
   run_next_test();
 }
--- a/toolkit/components/places/tests/queries/test_sort-date-site-grouping.js
+++ b/toolkit/components/places/tests/queries/test_sort-date-site-grouping.js
@@ -55,17 +55,17 @@ var testData = [
     uri: "http://example.com/4",
     lastVisit: olderthansixmonths,
     title: "test visit",
     isInQuery: true
   },
   {
     isVisit: true,
     uri: "http://example.net/1",
-    lastVisit: olderthansixmonths + 1,
+    lastVisit: olderthansixmonths + 1000,
     title: "test visit",
     isInQuery: true
   }
 ];
 var domainsInRange = [2, 3];
 var leveledTestData = [// Today
                        [[0],    // Today, local files
                         [1, 2]], // Today, example.com
--- a/toolkit/components/places/tests/queries/test_sorting.js
+++ b/toolkit/components/places/tests/queries/test_sorting.js
@@ -155,59 +155,59 @@ tests.push({
     var timeInMicroseconds = Date.now() * 1000;
     this._unsortedData = [
       { isVisit: true,
         isDetails: true,
         isBookmark: true,
         parentGuid: PlacesUtils.bookmarks.toolbarGuid,
         index: 0,
         uri: "http://example.com/c1",
-        lastVisit: timeInMicroseconds - 2,
+        lastVisit: timeInMicroseconds - 2000,
         title: "x1",
         isInQuery: true },
 
       { isVisit: true,
         isDetails: true,
         isBookmark: true,
         parentGuid: PlacesUtils.bookmarks.toolbarGuid,
         index: 1,
         uri: "http://example.com/a",
-        lastVisit: timeInMicroseconds - 1,
+        lastVisit: timeInMicroseconds - 1000,
         title: "z",
         isInQuery: true },
 
       { isVisit: true,
         isDetails: true,
         isBookmark: true,
         parentGuid: PlacesUtils.bookmarks.toolbarGuid,
         index: 2,
         uri: "http://example.com/b",
-        lastVisit: timeInMicroseconds - 3,
+        lastVisit: timeInMicroseconds - 3000,
         title: "y",
         isInQuery: true },
 
       // if dates are equal, should fall back to title
       { isVisit: true,
         isDetails: true,
         isBookmark: true,
         parentGuid: PlacesUtils.bookmarks.toolbarGuid,
         index: 3,
         uri: "http://example.com/c2",
-        lastVisit: timeInMicroseconds - 2,
+        lastVisit: timeInMicroseconds - 2000,
         title: "x2",
         isInQuery: true },
 
       // if dates and title are equal, should fall back to bookmark index
       { isVisit: true,
         isDetails: true,
         isBookmark: true,
         parentGuid: PlacesUtils.bookmarks.toolbarGuid,
         index: 4,
         uri: "http://example.com/c2",
-        lastVisit: timeInMicroseconds - 2,
+        lastVisit: timeInMicroseconds - 2000,
         title: "x2",
         isInQuery: true },
     ];
 
     this._sortedData = [
       this._unsortedData[2],
       this._unsortedData[0],
       this._unsortedData[3],
@@ -1184,57 +1184,63 @@ tests.push({
 // SORT_BY_FRECENCY_*
 
 tests.push({
   _sortingMode: Ci.nsINavHistoryQueryOptions.SORT_BY_FRECENCY_ASCENDING,
 
   *setup() {
     do_print("Sorting test 13: SORT BY FRECENCY ");
 
-    var timeInMicroseconds = Date.now() * 1000;
+    let timeInMicroseconds = PlacesUtils.toPRTime(Date.now() - 10000);
+
+    function newTimeInMicroseconds() {
+      timeInMicroseconds = timeInMicroseconds + 1000;
+      return timeInMicroseconds;
+    }
+
     this._unsortedData = [
       { isVisit: true,
         isDetails: true,
         uri: "http://moz.com/",
-        lastVisit: timeInMicroseconds++,
-        title: "I",
-        isInQuery: true },
-
-      { isVisit: true,
-        isDetails: true,
-        uri: "http://moz.com/",
-        lastVisit: timeInMicroseconds++,
+        lastVisit: newTimeInMicroseconds(),
         title: "I",
         isInQuery: true },
 
       { isVisit: true,
         isDetails: true,
         uri: "http://moz.com/",
-        lastVisit: timeInMicroseconds++,
+        lastVisit: newTimeInMicroseconds(),
+        title: "I",
+        isInQuery: true },
+
+      { isVisit: true,
+        isDetails: true,
+        uri: "http://moz.com/",
+        lastVisit: newTimeInMicroseconds(),
         title: "I",
         isInQuery: true },
 
       { isVisit: true,
         isDetails: true,
         uri: "http://is.com/",
-        lastVisit: timeInMicroseconds++,
+        lastVisit: newTimeInMicroseconds(),
         title: "love",
         isInQuery: true },
 
       { isVisit: true,
         isDetails: true,
         uri: "http://best.com/",
-        lastVisit: timeInMicroseconds++,
+        lastVisit: newTimeInMicroseconds(),
         title: "moz",
         isInQuery: true },
 
       { isVisit: true,
         isDetails: true,
         uri: "http://best.com/",
-        lastVisit: timeInMicroseconds++,
+        lastVisit: newTimeInMicroseconds(),
         title: "moz",
         isInQuery: true },
     ];
 
     this._sortedData = [
       this._unsortedData[3],
       this._unsortedData[5],
       this._unsortedData[2],
--- a/toolkit/components/places/tests/unit/test_385397.js
+++ b/toolkit/components/places/tests/unit/test_385397.js
@@ -8,28 +8,28 @@ const TOTAL_SITES = 20;
 
 function run_test()
 {
   run_next_test();
 }
 
 add_task(function* test_execute()
 {
-  let now = Date.now() * 1000;
+  let now = (Date.now() - 10000) * 1000;
 
   for (let i = 0; i < TOTAL_SITES; i++) {
     let site = "http://www.test-" + i + ".com/";
     let testURI = uri(site);
     let testImageURI = uri(site + "blank.gif");
-    let when = now + (i * TOTAL_SITES);
+    let when = now + (i * TOTAL_SITES * 1000);
     yield PlacesTestUtils.addVisits([
       { uri: testURI, visitDate: when, transition: TRANSITION_TYPED },
-      { uri: testImageURI, visitDate: ++when, transition: TRANSITION_EMBED },
-      { uri: testImageURI, visitDate: ++when, transition: TRANSITION_FRAMED_LINK },
-      { uri: testURI, visitDate: ++when, transition: TRANSITION_LINK },
+      { uri: testImageURI, visitDate: when + 1000, transition: TRANSITION_EMBED },
+      { uri: testImageURI, visitDate: when + 2000, transition: TRANSITION_FRAMED_LINK },
+      { uri: testURI, visitDate: when + 3000, transition: TRANSITION_LINK },
     ]);
   }
 
   // verify our visits AS_VISIT, ordered by date descending
   // including hidden
   // we should get 80 visits:
   // http://www.test-19.com/
   // http://www.test-19.com/blank.gif
deleted file mode 100644
--- a/toolkit/components/places/tests/unit/test_420331_wyciwyg.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-function run_test()
-{
-  run_next_test();
-}
-
-add_task(function* test_execute()
-{
-  var testURI = uri("wyciwyg://nodontjudgeabookbyitscover");
-
-  try
-  {
-    yield PlacesTestUtils.addVisits(testURI);
-    do_throw("Should have generated an exception.");
-  } catch (ex) {
-    if (ex.result != Cr.NS_ERROR_ILLEGAL_VALUE) {
-      throw ex;
-    }
-    // Adding wyciwyg URIs should raise NS_ERROR_ILLEGAL_VALUE.
-  }
-});
--- a/toolkit/components/places/tests/unit/test_browserhistory.js
+++ b/toolkit/components/places/tests/unit/test_browserhistory.js
@@ -56,37 +56,37 @@ add_task(function* test_removePages() {
 
   // Cleanup.
   PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
   yield PlacesTestUtils.clearHistory();
 });
 
 add_task(function* test_removePagesByTimeframe() {
   let visits = [];
-  let startDate = Date.now() * 1000;
+  let startDate = (Date.now() - 10000) * 1000;
   for (let i = 0; i < 10; i++) {
     visits.push({
       uri: NetUtil.newURI(TEST_URI.spec + i),
-      visitDate: startDate + i
+      visitDate: startDate + i * 1000
     });
   }
 
   yield PlacesTestUtils.addVisits(visits);
 
   // Delete all pages except the first and the last.
-  PlacesUtils.bhistory.removePagesByTimeframe(startDate + 1, startDate + 8);
+  PlacesUtils.bhistory.removePagesByTimeframe(startDate + 1000, startDate + 8000);
 
   // Check that we have removed the correct pages.
   for (let i = 0; i < 10; i++) {
     do_check_eq(page_in_database(NetUtil.newURI(TEST_URI.spec + i)) == 0,
                 i > 0 && i < 9);
   }
 
   // Clear remaining items and check that all pages have been removed.
-  PlacesUtils.bhistory.removePagesByTimeframe(startDate, startDate + 9);
+  PlacesUtils.bhistory.removePagesByTimeframe(startDate, startDate + 9000);
   do_check_eq(0, PlacesUtils.history.hasHistoryEntries);
 });
 
 add_task(function* test_removePagesFromHost() {
   yield PlacesTestUtils.addVisits(TEST_URI);
   PlacesUtils.bhistory.removePagesFromHost("mozilla.com", true);
   do_check_eq(0, PlacesUtils.history.hasHistoryEntries);
 });
--- a/toolkit/components/places/tests/unit/test_isvisited.js
+++ b/toolkit/components/places/tests/unit/test_isvisited.js
@@ -58,21 +58,18 @@ add_task(function* test_execute()
     }
     catch (e) {
       // nsIIOService.newURI() can throw if e.g. our app knows about imap://
       // but the account is not set up and so the URL is invalid for us.
       // Note this in the log but ignore as it's not the subject of this test.
       do_print("Could not construct URI for '" + currentURL + "'; ignoring");
     }
     if (cantAddUri) {
-      try {
-        yield PlacesTestUtils.addVisits({uri: cantAddUri, referrer: referrer});
-        do_throw("Should have generated an exception.");
-      } catch (ex) {
-        if (ex.result != Cr.NS_ERROR_ILLEGAL_VALUE) {
-          throw ex;
-        }
-      }
+      PlacesTestUtils.addVisits({uri: cantAddUri, referrer: referrer}).then(() => {
+        do_throw("Should not have added history for invalid URI.");
+      }, error => {
+        do_check_true(error.message.includes("No items were added to history"));
+      });
       do_check_false(yield promiseIsURIVisited(cantAddUri));
     }
   }
 });
 
--- a/toolkit/components/places/tests/unit/test_telemetry.js
+++ b/toolkit/components/places/tests/unit/test_telemetry.js
@@ -89,21 +89,27 @@ add_task(function* test_execute()
   // Request to gather telemetry data.
   Cc["@mozilla.org/places/categoriesStarter;1"]
     .getService(Ci.nsIObserver)
     .observe(null, "gather-telemetry", null);
 
   yield PlacesTestUtils.promiseAsyncUpdates();
 
   // Test expiration probes.
-  let now =  getExpirablePRTime();
+  let timeInMicroseconds = getExpirablePRTime(8);
+
+  function newTimeInMicroseconds() {
+    timeInMicroseconds = timeInMicroseconds + 1000;
+    return timeInMicroseconds;
+  }
+
   for (let i = 0; i < 3; i++) {
     yield PlacesTestUtils.addVisits({
       uri: NetUtil.newURI("http://" +  i + ".moz.org/"),
-      visitDate: now++
+      visitDate: newTimeInMicroseconds()
     });
   }
   Services.prefs.setIntPref("places.history.expiration.max_pages", 0);
   yield promiseForceExpirationStep(2);
   yield promiseForceExpirationStep(2);
 
   // Test autocomplete probes.
   /*
--- a/toolkit/components/places/tests/unit/xpcshell.ini
+++ b/toolkit/components/places/tests/unit/xpcshell.ini
@@ -38,17 +38,16 @@ skip-if = os == "linux"
 # Bug 676989: test hangs consistently on Android
 skip-if = os == "android"
 [test_413784.js]
 [test_415460.js]
 [test_415757.js]
 [test_418643_removeFolderChildren.js]
 [test_419731.js]
 [test_419792_node_tags_property.js]
-[test_420331_wyciwyg.js]
 [test_425563.js]
 [test_429505_remove_shortcuts.js]
 [test_433317_query_title_update.js]
 [test_433525_hasChildren_crash.js]
 [test_452777.js]
 [test_454977.js]
 [test_463863.js]
 [test_485442_crash_bug_nsNavHistoryQuery_GetUri.js]
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -2836,22 +2836,16 @@ AccumulateChild(const nsTArray<Accumulat
 }
 
 void
 AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations)
 {
   TelemetryHistogram::AccumulateChildKeyed(aAccumulations);
 }
 
-void
-ClearHistogram(ID aId)
-{
-  TelemetryHistogram::ClearHistogram(aId);
-}
-
 const char*
 GetHistogramName(ID id)
 {
   return TelemetryHistogram::GetHistogramName(id);
 }
 
 bool
 CanRecordBase()
--- a/toolkit/components/telemetry/Telemetry.h
+++ b/toolkit/components/telemetry/Telemetry.h
@@ -138,23 +138,16 @@ void AccumulateChild(const nsTArray<Accu
 /**
  * Accumulate child data into child keyed histograms
  *
  * @param aAccumulations - accumulation actions to perform
  */
 void AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations);
 
 /**
- * This clears the data for a histogram in TelemetryHistogramEnums.h.
- *
- * @param id - histogram id
- */
-void ClearHistogram(ID id);
-
-/**
  * Enable/disable recording for this histogram at runtime.
  * Recording is enabled by default, unless listed at kRecordingInitiallyDisabledIDs[].
  * id must be a valid telemetry enum, otherwise an assertion is triggered.
  *
  * @param id - histogram id
  * @param enabled - whether or not to enable recording from now on.
  */
 void SetHistogramRecordingEnabled(ID id, bool enabled);
--- a/toolkit/components/telemetry/TelemetryHistogram.cpp
+++ b/toolkit/components/telemetry/TelemetryHistogram.cpp
@@ -2210,31 +2210,16 @@ TelemetryHistogram::AccumulateChildKeyed
       continue;
     }
     internal_AccumulateChildKeyed(aAccumulations[i].mId,
                                   aAccumulations[i].mKey,
                                   aAccumulations[i].mSample);
   }
 }
 
-void
-TelemetryHistogram::ClearHistogram(mozilla::Telemetry::ID aId)
-{
-  StaticMutexAutoLock locker(gTelemetryHistogramMutex);
-  if (!internal_CanRecordBase()) {
-    return;
-  }
-
-  Histogram *h;
-  nsresult rv = internal_GetHistogramByEnumId(aId, &h);
-  if (NS_SUCCEEDED(rv) && h) {
-    internal_HistogramClear(*h, false);
-  }
-}
-
 nsresult
 TelemetryHistogram::GetHistogramById(const nsACString &name, JSContext *cx,
                                      JS::MutableHandle<JS::Value> ret)
 {
   Histogram *h = nullptr;
   {
     StaticMutexAutoLock locker(gTelemetryHistogramMutex);
     nsresult rv = internal_GetHistogramByName(name, &h);
--- a/toolkit/components/telemetry/TelemetryHistogram.h
+++ b/toolkit/components/telemetry/TelemetryHistogram.h
@@ -42,19 +42,16 @@ void Accumulate(mozilla::Telemetry::ID a
 void Accumulate(const char* name, uint32_t sample);
 void Accumulate(const char* name, const nsCString& key, uint32_t sample);
 
 void AccumulateCategorical(mozilla::Telemetry::ID aId, const nsCString& aLabel);
 
 void AccumulateChild(const nsTArray<mozilla::Telemetry::Accumulation>& aAccumulations);
 void AccumulateChildKeyed(const nsTArray<mozilla::Telemetry::KeyedAccumulation>& aAccumulations);
 
-void
-ClearHistogram(mozilla::Telemetry::ID aId);
-
 nsresult
 GetHistogramById(const nsACString &name, JSContext *cx,
                  JS::MutableHandle<JS::Value> ret);
 
 nsresult
 GetKeyedHistogramById(const nsACString &name, JSContext *cx,
                       JS::MutableHandle<JS::Value> ret);
 
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -110,24 +110,16 @@ button[type="repeat"] {
 button[type="menu"], button[type="panel"] {
   -moz-binding: url("chrome://global/content/bindings/button.xml#menu");
 }
 
 button[type="menu-button"] {
   -moz-binding: url("chrome://global/content/bindings/button.xml#menu-button");
 }
 
-%ifdef MOZ_WIDGET_GTK
-/********* detection of system setting to use icons in buttons ***********/
-button[label]:not([label=""]) > .button-box > .button-icon:not(:-moz-system-metric(images-in-buttons)),
-button[label]:not([label=""]) > .button-box > .box-inherit > .button-icon:not(:-moz-system-metric(images-in-buttons)) {
-  display: none;
-}
-%endif
-
 /********** toolbarbutton **********/
 
 toolbarbutton {
   -moz-binding: url("chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton");
 }
 
 toolbarbutton.badged-button > toolbarbutton,
 toolbarbutton.badged-button {
@@ -391,28 +383,16 @@ menuitem.menuitem-non-iconic {
 menucaption {
   -moz-binding: url("chrome://global/content/bindings/menu.xml#menucaption");
 }
 
 .menu-text {
   -moz-box-flex: 1;
 }
 
-%ifdef MOZ_WIDGET_GTK
-/********* detection of system setting to use icons in menus ***********/
-@media not all and (-moz-images-in-menus) {
-  .menu-iconic-left {
-    visibility: hidden;
-  }
-  :-moz-any(menuitem[type], .menuitem-with-favicon) > .menu-iconic-left {
-    visibility: visible;
-  }
-}
-%endif
-
 /********* menuseparator ***********/
 
 menuseparator {
   -moz-binding: url("chrome://global/content/bindings/menu.xml#menuseparator");
 }
 
 /********* popup & menupopup ***********/
 
--- a/toolkit/modules/FinderHighlighter.jsm
+++ b/toolkit/modules/FinderHighlighter.jsm
@@ -564,17 +564,17 @@ FinderHighlighter.prototype = {
    *                                        Optional, defaults to `true`
    * @return {Rect}
    */
   _getRootBounds(window, includeScroll = true) {
     let dwu = this._getDWU(window.top);
     let cssPageRect = Rect.fromRect(dwu.getRootBounds());
     let scrollX = {};
     let scrollY = {};
-    if (includeScroll) {
+    if (includeScroll && window == window.top) {
       dwu.getScrollXY(false, scrollX, scrollY);
       cssPageRect.translate(scrollX.value, scrollY.value);
     }
 
     // If we're in a frame, update the position of the rect (top/ left).
     let currWin = window;
     while (currWin != window.top) {
       // Since the frame is an element inside a parent window, we'd like to
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -423,19 +423,21 @@ HttpObserverManager = {
   },
 
   observe(subject, topic, data) {
     let channel = subject.QueryInterface(Ci.nsIHttpChannel);
     switch (topic) {
       case "http-on-modify-request":
         this.modify(channel, topic, data);
         break;
-      case "http-on-examine-response":
       case "http-on-examine-cached-response":
       case "http-on-examine-merged-response":
+        getData(channel).fromCache = true;
+        // falls through
+      case "http-on-examine-response":
         this.examine(channel, topic, data);
         break;
     }
   },
 
   // We map activity values with tentative error names, e.g. "STATUS_RESOLVING" => "NS_ERROR_NET_ON_RESOLVING".
   get activityErrorsMap() {
     let prefix = /^(?:ACTIVITY_SUBTYPE_|STATUS_)/;
@@ -533,16 +535,17 @@ HttpObserverManager = {
 
       if (!commonData) {
         commonData = {
           requestId: RequestId.get(channel),
           url: uri.spec,
           method: channel.requestMethod,
           browser: browser,
           type: WebRequestCommon.typeForPolicyType(policyType),
+          fromCache: getData(channel).fromCache,
         };
 
         if (loadInfo) {
           let originPrincipal = loadInfo.triggeringPrincipal || loadInfo.loadingPrincipal;
           if (originPrincipal && originPrincipal.URI) {
             commonData.originUrl = originPrincipal.URI.spec;
           }
           Object.assign(commonData, {
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -378,18 +378,16 @@ def eme(value, fmp4):
             'Fragmented MP4 support')
     if enabled:
         return True
 
 @depends(enable_eme)
 def eme_modules(value):
     return value
 
-set_config('MOZ_EME', eme)
-set_define('MOZ_EME', eme)
 set_config('MOZ_EME_MODULES', eme_modules)
 
 option(name='--enable-chrome-format',
        help='Select FORMAT of chrome files during packaging.',
        nargs=1,
        choices=('omni', 'jar', 'flat'),
        default='omni')
 
--- a/toolkit/mozapps/extensions/content/update.xul
+++ b/toolkit/mozapps/extensions/content/update.xul
@@ -82,17 +82,16 @@
   </wizardpage>
     
   <wizardpage id="noupdates" pageid="noupdates"
               label="&noupdates.wizard.title;"
               onpageshow="gNoUpdatesPage.onPageShow();">
     <description>&noupdates.intro.desc;</description>
     <separator class="thin"/>
     <hbox id="updateCheckErrorNotFound" class="alertBox" hidden="true" align="top">
-      <image id="alert"/>
       <description flex="1">&noupdates.error.desc;</description>
     </hbox>
     <separator class="thin"/>
     <description id="noupdatesCheckEnabled" hidden="true">
       &noupdates.checkEnabled.desc;
     </description>
     <vbox id="noupdatesCheckDisabled" hidden="true">
       <description>&finished.checkDisabled.desc;</description>
@@ -152,17 +151,16 @@
     <separator class="thin"/>
   </wizardpage>
   
   <wizardpage id="adminDisabled" pageid="adminDisabled"
               label="&adminDisabled.wizard.title;"
               onpageshow="gAdminDisabledPage.onPageShow();">
     <separator/>
     <hbox class="alertBox" align="top">
-      <image id="alert"/>
       <description flex="1">&adminDisabled.warning.label;</description>
     </hbox>
     <separator flex="1"/>
 #ifndef XP_MACOSX
     <label>&clickFinish.label;</label>
 #else
     <label>&clickFinish.labelMac;</label>
 #endif
--- a/toolkit/themes/linux/global/button.css
+++ b/toolkit/themes/linux/global/button.css
@@ -145,204 +145,8 @@ button[type="disclosure"] {
   list-style-image: url("chrome://global/skin/tree/twisty-clsd.png");
   min-width: 0px !important;
   background-color: transparent;
 }
 
 button[type="disclosure"][open="true"] {
   list-style-image: url("chrome://global/skin/tree/twisty-open.png"); 
 }
-
-/*
- * GNOME Stock Icon Styles
- */
-
-button[icon="accept"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-ok?size=button");
-}
-
-button[icon="accept"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-ok?size=button&state=disabled");
-}
-
-button[icon="cancel"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-cancel?size=button");
-}
-
-button[icon="cancel"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-cancel?size=button&state=disabled");
-}
-
-button[icon="help"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-help?size=button");
-}
-
-button[icon="help"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-help?size=button&state=disabled");
-}
-
-button[icon="open"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-open?size=button");
-}
-
-button[icon="open"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-open?size=button&state=disabled");
-}
-
-button[icon="save"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-save?size=button");
-}
-
-button[icon="save"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-save?size=button&state=disabled");
-}
-
-button[icon="find"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-find?size=button");
-}
-
-button[icon="find"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-find?size=button&state=disabled");
-}
-
-button[icon="clear"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-clear?size=button");
-}
-
-button[icon="clear"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-clear?size=button&state=disabled");
-}
-
-button[icon="yes"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-yes?size=button");
-}
-
-button[icon="yes"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-yes?size=button&state=disabled");
-}
-
-button[icon="no"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-no?size=button");
-}
-
-button[icon="no"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-no?size=button&state=disabled");
-}
-
-button[icon="apply"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-apply?size=button");
-}
-
-button[icon="apply"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-apply?size=button&state=disabled");
-}
-
-button[icon="close"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-close?size=button");
-}
-
-button[icon="close"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-close?size=button&state=disabled");
-}
-
-button[icon="print"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-print?size=button");
-}
-
-button[icon="print"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-print?size=button&state=disabled");
-}
-
-button[icon="add"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-add?size=button");
-}
-
-button[icon="add"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-add?size=button&state=disabled");
-}
-
-button[icon="remove"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-remove?size=button");
-}
-
-button[icon="remove"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-remove?size=button&state=disabled");
-}
-
-button[icon="refresh"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-refresh?size=button");
-}
-
-button[icon="refresh"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-refresh?size=button&state=disabled");
-}
-
-button[icon="revert"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-revert-to-saved?size=button");
-}
-
-button[icon="revert"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-revert-to-saved?size=button&state=disabled");
-}
-
-button[icon="go-forward"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=button");
-}
-
-button[icon="go-forward"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=button&state=disabled");
-}
-
-button[icon="go-forward"]:-moz-locale-dir(rtl) .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=button");
-}
-
-button[icon="go-forward"]:-moz-locale-dir(rtl)[disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=button&state=disabled");
-}
-
-button[icon="go-back"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=button");
-}
-
-button[icon="go-back"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=button&state=disabled");
-}
-
-button[icon="go-back"]:-moz-locale-dir(rtl) .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=button");
-}
-
-button[icon="go-back"]:-moz-locale-dir(rtl)[disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=button&state=disabled");
-}
-
-button[icon="properties"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-properties?size=button");
-}
-
-button[icon="properties"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-properties?size=button&state=disabled");
-}
-
-button[icon="select-font"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-select-font?size=button");
-}
-
-button[icon="select-font"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-select-font?size=button&state=disabled");
-}
-
-button[icon="select-color"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-color-picker?size=button");
-}
-
-button[icon="select-color"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-color-picker?size=button&state=disabled");
-}
-
-button[icon="network"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-network?size=button");
-}
-
-button[icon="network"][disabled="true"] .button-icon {
-  list-style-image: url("moz-icon://stock/gtk-network?size=button&state=disabled");
-}
--- a/toolkit/themes/linux/global/textbox.css
+++ b/toolkit/themes/linux/global/textbox.css
@@ -85,63 +85,8 @@ textbox.plain html|*.textbox-textarea {
   cursor: pointer;
 }
 
 /* ::::: textboxes inside toolbarpaletteitems ::::: */
 
 toolbarpaletteitem > toolbaritem > textbox > .textbox-input-box > html|*.textbox-input {
   visibility: hidden;
 }
-
-/* ::::: context menu ::::: */
-
-menuitem:not([type]) {
-  -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
-}
-
-menuitem[cmd="cmd_undo"] {
-  list-style-image: url("moz-icon://stock/gtk-undo?size=menu");
-}
-
-menuitem[cmd="cmd_undo"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-undo?size=menu&state=disabled");
-}
-
-menuitem[cmd="cmd_cut"] {
-  list-style-image: url("moz-icon://stock/gtk-cut?size=menu");
-}
-
-menuitem[cmd="cmd_cut"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-cut?size=menu&state=disabled");
-}
-
-menuitem[cmd="cmd_copy"] {
-  list-style-image: url("moz-icon://stock/gtk-copy?size=menu");
-}
-
-menuitem[cmd="cmd_copy"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-copy?size=menu&state=disabled");
-}
-
-menuitem[cmd="cmd_paste"] {
-  list-style-image: url("moz-icon://stock/gtk-paste?size=menu");
-}
-
-menuitem[cmd="cmd_paste"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-paste?size=menu&state=disabled");
-}
-
-menuitem[cmd="cmd_delete"] {
-  list-style-image: url("moz-icon://stock/gtk-delete?size=menu");
-}
-
-menuitem[cmd="cmd_delete"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-delete?size=menu&state=disabled");
-}
-
-menuitem[cmd="cmd_selectAll"] {
-  list-style-image: url("moz-icon://stock/gtk-select-all?size=menu");
-}
-
-menuitem[cmd="cmd_selectAll"][disabled] {
-  list-style-image: url("moz-icon://stock/gtk-select-all?size=menu&state=disabled");
-}
-
--- a/toolkit/themes/linux/global/tree.css
+++ b/toolkit/themes/linux/global/tree.css
@@ -188,20 +188,16 @@ treechildren::-moz-tree-progressmeter {
   border-top-color: ThreeDShadow;
   border-right-color: ThreeDHighlight;
   border-bottom-color: ThreeDHighlight;
   border-left-color: ThreeDShadow;
   background-color: -moz-Dialog;
   color: ThreeDShadow;
 }
 
-treechildren::-moz-tree-progressmeter(progressUndetermined) {
-  list-style-image: url("chrome://global/skin/progressmeter/progressmeter-busy.gif");
-}
-
 treechildren::-moz-tree-cell-text(progressmeter) {
   margin: 2px 4px;
 }
 
 /* ::::: tree columns ::::: */
 
 treecol,
 treecolpicker {
--- a/toolkit/themes/osx/global/tree.css
+++ b/toolkit/themes/osx/global/tree.css
@@ -150,21 +150,16 @@ treechildren::-moz-tree-progressmeter {
   margin: 2px 4px;
   border: 2px solid;
   -moz-border-top-colors: #AAAAAA #000000;
   -moz-border-right-colors: #FFFFFF #000000;
   -moz-border-bottom-colors: #FFFFFF #000000;
   -moz-border-left-colors: #AAAAAA #000000;
 }
 
-/*
-treechildren::-moz-tree-progressmeter(progressUndetermined) {
-}
-*/
-
 treechildren::-moz-tree-cell-text(progressmeter) {
   margin: 2px 4px;
   -moz-appearance: progressbar;
 }
 
 /* ::::: tree columns ::::: */
 
 treecol,
--- a/toolkit/themes/osx/mozapps/extensions/update.css
+++ b/toolkit/themes/osx/mozapps/extensions/update.css
@@ -1,16 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#alert {
-  list-style-image: url("chrome://mozapps/skin/update/warning.gif"); 
-}
-
 .throbber {
   list-style-image: url("chrome://global/skin/icons/loading.png");
   width: 16px;
   height: 16px;
   margin-top: 5px;
   margin-bottom: 5px;
   margin-inline-start: 5px;
   margin-inline-end: 2px;
--- a/toolkit/themes/shared/non-mac.jar.inc.mn
+++ b/toolkit/themes/shared/non-mac.jar.inc.mn
@@ -85,16 +85,18 @@
   skin/classic/global/icons/tabprompts-bgtexture.png       (../../windows/global/icons/tabprompts-bgtexture.png)
   skin/classic/global/icons/Warning.png                    (../../windows/global/icons/Warning.png)
   skin/classic/global/icons/warning-large.png              (../../windows/global/icons/warning-large.png)
   skin/classic/global/icons/warning-16.png                 (../../windows/global/icons/warning-16.png)
   skin/classic/global/icons/warning-64.png                 (../../windows/global/icons/warning-64.png)
   skin/classic/global/icons/windowControls.png             (../../windows/global/icons/windowControls.png)
   skin/classic/global/radio/radio-check.gif                (../../windows/global/radio/radio-check.gif)
   skin/classic/global/radio/radio-check-dis.gif            (../../windows/global/radio/radio-check-dis.gif)
+  skin/classic/global/scale/scale-tray-horiz.gif           (../../windows/global/scale/scale-tray-horiz.gif)
+  skin/classic/global/scale/scale-tray-vert.gif            (../../windows/global/scale/scale-tray-vert.gif)
   skin/classic/global/scrollbar/slider.gif                 (../../windows/global/scrollbar/slider.gif)
   skin/classic/global/splitter/grip-bottom.gif             (../../windows/global/splitter/grip-bottom.gif)
   skin/classic/global/splitter/grip-top.gif                (../../windows/global/splitter/grip-top.gif)
   skin/classic/global/splitter/grip-left.gif               (../../windows/global/splitter/grip-left.gif)
   skin/classic/global/splitter/grip-right.gif              (../../windows/global/splitter/grip-right.gif)
   skin/classic/global/toolbar/chevron.gif                  (../../windows/global/toolbar/chevron.gif)
   skin/classic/global/toolbar/chevron-inverted.png         (../../windows/global/toolbar/chevron-inverted.png)
   skin/classic/global/tree/columnpicker.gif                (../../windows/global/tree/columnpicker.gif)
--- a/toolkit/themes/windows/global/tree.css
+++ b/toolkit/themes/windows/global/tree.css
@@ -187,20 +187,16 @@ treechildren::-moz-tree-progressmeter {
   border-top-color: ThreeDShadow;
   border-right-color: ThreeDHighlight;
   border-bottom-color: ThreeDHighlight;
   border-left-color: ThreeDShadow;
   background-color: -moz-Dialog;
   color: ThreeDShadow;
 }
 
-treechildren::-moz-tree-progressmeter(progressUndetermined) {
-  list-style-image: url("chrome://global/skin/progressmeter/progressmeter-busy.gif");
-}
-
 treechildren::-moz-tree-cell-text(progressmeter) {
   margin: 2px 4px;
 }
 
 /* ::::: tree columns ::::: */
 
 treecol,
 treecolpicker {
--- a/toolkit/themes/windows/mozapps/extensions/update.css
+++ b/toolkit/themes/windows/mozapps/extensions/update.css
@@ -1,16 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#alert {
-  list-style-image: url("chrome://mozapps/skin/update/update.png"); 
-}
-
 .throbber {
   list-style-image: url("chrome://global/skin/icons/loading.png");
   width: 16px;
   height: 16px;
   margin-top: 5px;
   margin-bottom: 5px;
   margin-inline-start: 5px;
   margin-inline-end: 2px;
--- a/widget/LookAndFeel.h
+++ b/widget/LookAndFeel.h
@@ -332,24 +332,16 @@ public:
      */
     eIntID_IMERawInputUnderlineStyle,
     eIntID_IMESelectedRawTextUnderlineStyle,
     eIntID_IMEConvertedTextUnderlineStyle,
     eIntID_IMESelectedConvertedTextUnderline,
     eIntID_SpellCheckerUnderlineStyle,
 
     /**
-     * If this metric != 0, show icons in menus.
-     */
-    eIntID_ImagesInMenus,
-    /**
-     * If this metric != 0, show icons in buttons.
-     */
-    eIntID_ImagesInButtons,
-    /**
      * If this metric != 0, support window dragging on the menubar.
      */
     eIntID_MenuBarDrag,
     /**
      * Return the appropriate WindowsThemeIdentifier for the current theme.
      */
     eIntID_WindowsThemeIdentifier,
     /**
--- a/widget/gtk/gtk2drawing.c
+++ b/widget/gtk/gtk2drawing.c
@@ -3195,42 +3195,16 @@ moz_gtk_get_scrollbar_metrics(MozGtkScro
                           NULL);
 
     metrics->min_slider_size =
         GTK_RANGE(gHorizScrollbarWidget)->min_slider_size;
 
     return MOZ_GTK_SUCCESS;
 }
 
-gboolean
-moz_gtk_images_in_menus()
-{
-    gboolean result;
-    GtkSettings* settings;
-
-    ensure_image_menu_item_widget();
-    settings = gtk_widget_get_settings(gImageMenuItemWidget);
-
-    g_object_get(settings, "gtk-menu-images", &result, NULL);
-    return result;
-}
-
-gboolean
-moz_gtk_images_in_buttons()
-{
-    gboolean result;
-    GtkSettings* settings;
-
-    ensure_button_widget();
-    settings = gtk_widget_get_settings(gButtonWidget);
-
-    g_object_get(settings, "gtk-button-images", &result, NULL);
-    return result;
-}
-
 gint
 moz_gtk_widget_paint(WidgetNodeType widget, GdkDrawable* drawable,
                      GdkRectangle* rect, GdkRectangle* cliprect,
                      GtkWidgetState* state, gint flags,
                      GtkTextDirection direction)
 {
     switch (widget) {
     case MOZ_GTK_BUTTON:
--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -2616,40 +2616,16 @@ moz_gtk_get_scrollbar_metrics(MozGtkScro
         gtk_style_context_get(style, gtk_style_context_get_state(style),
                               "min-height", &metrics->min_slider_size, nullptr);
         ReleaseStyleContext(style);
     }
 
     return MOZ_GTK_SUCCESS;
 }
 
-gboolean
-moz_gtk_images_in_menus()
-{
-    gboolean result;
-    GtkSettings* settings;
-
-    settings = gtk_widget_get_settings(GetWidget(MOZ_GTK_IMAGEMENUITEM));
-
-    g_object_get(settings, "gtk-menu-images", &result, NULL);
-    return result;
-}
-
-gboolean
-moz_gtk_images_in_buttons()
-{
-    gboolean result;
-    GtkSettings* settings;
-
-    settings = gtk_widget_get_settings(GetWidget(MOZ_GTK_BUTTON));
-
-    g_object_get(settings, "gtk-button-images", &result, NULL);
-    return result;
-}
-
 /* cairo_t *cr argument has to be a system-cairo. */
 gint
 moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr,
                      GdkRectangle* rect,
                      GtkWidgetState* state, gint flags,
                      GtkTextDirection direction)
 {
     /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=694086
--- a/widget/gtk/gtkdrawing.h
+++ b/widget/gtk/gtkdrawing.h
@@ -500,28 +500,16 @@ GtkWidget* moz_gtk_get_scrollbar_widget(
 
 /**
  * Get the YTHICKNESS of a tab (notebook extension).
  */
 gint
 moz_gtk_get_tab_thickness(WidgetNodeType aNodeType);
 
 /**
- * Get a boolean which indicates whether or not to use images in menus.
- * If TRUE, use images in menus.
- */
-gboolean moz_gtk_images_in_menus(void);
-
-/**
- * Get a boolean which indicates whether or not to use images in buttons.
- * If TRUE, use images in buttons.
- */
-gboolean moz_gtk_images_in_buttons(void);
-
-/**
  * Get a boolean which indicates whether the theme draws scrollbar buttons.
  * If TRUE, draw scrollbar buttons.
  */
 gboolean moz_gtk_has_scrollbar_buttons(void);
 
 #if (MOZ_WIDGET_GTK == 2)
 #ifdef __cplusplus
 }
--- a/widget/gtk/nsLookAndFeel.cpp
+++ b/widget/gtk/nsLookAndFeel.cpp
@@ -779,22 +779,16 @@ nsLookAndFeel::GetIntImpl(IntID aID, int
         break;
     case eIntID_IMESelectedRawTextUnderlineStyle:
     case eIntID_IMESelectedConvertedTextUnderline:
         aResult = NS_STYLE_TEXT_DECORATION_STYLE_NONE;
         break;
     case eIntID_SpellCheckerUnderlineStyle:
         aResult = NS_STYLE_TEXT_DECORATION_STYLE_WAVY;
         break;
-    case eIntID_ImagesInMenus:
-        aResult = moz_gtk_images_in_menus();
-        break;
-    case eIntID_ImagesInButtons:
-        aResult = moz_gtk_images_in_buttons();
-        break;
     case eIntID_MenuBarDrag:
         aResult = sMenuSupportsDrag;
         break;
     case eIntID_ScrollbarButtonAutoRepeatBehavior:
         aResult = 1;
         break;
     case eIntID_SwipeAnimationEnabled:
         aResult = 0;