Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 29 Sep 2016 11:48:33 +0200
changeset 315811 6a996a75330fb7d4376b4ed6d439126d8b5a50a2
parent 315810 d9b67ef4fb0a2f2de2c398034ffe027c07aae8e9 (current diff)
parent 315727 f7d5008ee2ab9200052e45ad6ecc3f3a348f7f86 (diff)
child 315812 826cc48624a31a2755c23bfe83535311df1567bc
push id30757
push usercbook@mozilla.com
push dateFri, 30 Sep 2016 10:02:43 +0000
treeherdermozilla-central@5ffed033557e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
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;