Merge autoland to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 20 Jun 2017 18:24:29 -0700
changeset 416208 c55e582aee5f4dd7c28cd9820156ecd0335e4e79
parent 416075 f31652d75fb5f377db8de3da30b0252600d3c8ca (current diff)
parent 416207 798a5d133c0b52979aa60cb70eef50f7529b7a8c (diff)
child 416209 e1e4a481b7e88dce163b9cccc2fb72032023befa
child 416222 99d4f967cf0ac31bca6ce3496043f8e99f3a7164
child 416373 f7b9dc31956c2aa3e4a1cd7da3720551f8565992
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone56.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 autoland to m-c a=merge MozReview-Commit-ID: LFtpTAueYrF
browser/base/content/tabbrowser.xml
browser/components/nsBrowserGlue.js
browser/themes/osx/browser.css
browser/themes/windows/customizableui/panelUI.css
devtools/client/framework/toolbox-options.js
devtools/client/inspector/boxmodel/test/head.js
devtools/client/inspector/grids/test/head.js
devtools/client/inspector/inspector.js
devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js
devtools/client/locales/en-US/layout.properties
devtools/client/preferences/devtools.js
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsJSEnvironment.cpp
dom/media/MediaFormatReader.cpp
dom/media/ipc/VideoDecoderManagerParent.cpp
dom/media/platforms/wmf/WMFVideoMFTManager.cpp
gfx/layers/ImageContainer.h
gfx/layers/d3d11/TextureD3D11.cpp
gfx/layers/d3d11/TextureD3D11.h
gfx/layers/moz.build
modules/libpref/init/all.js
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpHandler.cpp
parser/html/nsHtml5TreeOpExecutor.cpp
storage/mozStorageAsyncStatement.cpp
storage/mozStorageConnection.cpp
storage/mozStorageService.cpp
taskcluster/ci/test/tests.yml
third_party/rust/aho-corasick/.gitignore
third_party/rust/ansi_term/.gitignore
third_party/rust/app_units-0.4.1/.cargo-checksum.json
third_party/rust/app_units-0.4.1/.cargo-ok
third_party/rust/app_units-0.4.1/.gitignore
third_party/rust/app_units-0.4.1/.travis.yml
third_party/rust/app_units-0.4.1/Cargo.toml
third_party/rust/app_units-0.4.1/README.md
third_party/rust/app_units-0.4.1/src/app_unit.rs
third_party/rust/app_units-0.4.1/src/lib.rs
third_party/rust/app_units/.gitignore
third_party/rust/app_units/Cargo.toml.orig
third_party/rust/arraydeque/.gitignore
third_party/rust/arraydeque/Cargo.toml.orig
third_party/rust/arrayvec/.gitignore
third_party/rust/atomic_refcell/.gitignore
third_party/rust/atty/.gitignore
third_party/rust/backtrace/.gitignore
third_party/rust/base64/.gitignore
third_party/rust/binary-space-partition/.gitignore
third_party/rust/bincode/.gitignore
third_party/rust/bincode/src/refbox.rs
third_party/rust/bincode/src/serde/mod.rs
third_party/rust/bincode/src/serde/reader.rs
third_party/rust/bincode/src/serde/writer.rs
third_party/rust/bindgen/.gitignore
third_party/rust/bindgen/Cargo.toml.orig
third_party/rust/bindgen/book/.gitignore
third_party/rust/bit-set/.gitignore
third_party/rust/bit-vec/.gitignore
third_party/rust/bitflags-0.7.0/.gitignore
third_party/rust/bitflags/.gitignore
third_party/rust/bitreader/.gitignore
third_party/rust/byteorder/.gitignore
third_party/rust/bzip2/.gitignore
third_party/rust/cexpr/.gitignore
third_party/rust/cfg-if/.gitignore
third_party/rust/chrono/.gitignore
third_party/rust/clang-sys/.gitignore
third_party/rust/clap/.gitignore
third_party/rust/coco/.gitignore
third_party/rust/coco/Cargo.toml.orig
third_party/rust/cookie/.gitignore
third_party/rust/core-graphics/.gitignore
third_party/rust/core-text/.gitignore
third_party/rust/crossbeam/.gitignore
third_party/rust/cssparser/.gitignore
third_party/rust/dwrote/.gitignore
third_party/rust/either/.gitignore
third_party/rust/encoding_c/.gitignore
third_party/rust/encoding_rs/.gitignore
third_party/rust/euclid-0.14.4/.cargo-checksum.json
third_party/rust/euclid-0.14.4/.cargo-ok
third_party/rust/euclid-0.14.4/.gitignore
third_party/rust/euclid-0.14.4/.travis.yml
third_party/rust/euclid-0.14.4/COPYRIGHT
third_party/rust/euclid-0.14.4/Cargo.toml
third_party/rust/euclid-0.14.4/LICENSE-APACHE
third_party/rust/euclid-0.14.4/LICENSE-MIT
third_party/rust/euclid-0.14.4/README.md
third_party/rust/euclid-0.14.4/src/approxeq.rs
third_party/rust/euclid-0.14.4/src/length.rs
third_party/rust/euclid-0.14.4/src/lib.rs
third_party/rust/euclid-0.14.4/src/macros.rs
third_party/rust/euclid-0.14.4/src/num.rs
third_party/rust/euclid-0.14.4/src/point.rs
third_party/rust/euclid-0.14.4/src/rect.rs
third_party/rust/euclid-0.14.4/src/scale_factor.rs
third_party/rust/euclid-0.14.4/src/side_offsets.rs
third_party/rust/euclid-0.14.4/src/size.rs
third_party/rust/euclid-0.14.4/src/transform2d.rs
third_party/rust/euclid-0.14.4/src/transform3d.rs
third_party/rust/euclid-0.14.4/src/trig.rs
third_party/rust/euclid-0.14.4/src/vector.rs
third_party/rust/euclid/.gitignore
third_party/rust/euclid/Cargo.toml.orig
third_party/rust/flate2/.gitignore
third_party/rust/fnv/.gitignore
third_party/rust/freetype/.gitignore
third_party/rust/futures/.gitignore
third_party/rust/gamma-lut/.gitignore
third_party/rust/gcc/.gitignore
third_party/rust/gl_generator/Cargo.toml.orig
third_party/rust/gleam/.gitignore
third_party/rust/glob/.gitignore
third_party/rust/heapsize-0.3.8/.gitignore
third_party/rust/heapsize/.gitignore
third_party/rust/httparse/.gitignore
third_party/rust/isatty/.gitignore
third_party/rust/itoa/.gitignore
third_party/rust/khronos_api/Cargo.toml.orig
third_party/rust/lazy_static-0.1.16/.gitignore
third_party/rust/lazy_static/.gitignore
third_party/rust/libc/.gitignore
third_party/rust/libloading/.gitignore
third_party/rust/libz-sys/.gitignore
third_party/rust/log/.gitignore
third_party/rust/memchr/.gitignore
third_party/rust/mime/.gitignore
third_party/rust/mozprofile/.gitignore
third_party/rust/mozrunner/.gitignore
third_party/rust/mozversion/.gitignore
third_party/rust/msdos_time/.gitignore
third_party/rust/nom/.gitignore
third_party/rust/num/.gitignore
third_party/rust/num/ci/.gitignore
third_party/rust/num_cpus/.gitignore
third_party/rust/ordered-float/.gitignore
third_party/rust/owning_ref/.gitignore
third_party/rust/parking_lot/.gitignore
third_party/rust/pdqsort/.gitignore
third_party/rust/peeking_take_while/.gitignore
third_party/rust/pkg-config/.gitignore
third_party/rust/plane-split/.gitignore
third_party/rust/podio/.gitignore
third_party/rust/precomputed-hash/.gitignore
third_party/rust/rand/.gitignore
third_party/rust/rayon-core/Cargo.toml.orig
third_party/rust/rayon/.gitignore
third_party/rust/rayon/Cargo.toml.orig
third_party/rust/redox_syscall/.gitignore
third_party/rust/regex/.gitignore
third_party/rust/rust-ini/.gitignore
third_party/rust/rustc-demangle/.gitignore
third_party/rust/rustc-serialize/.gitignore
third_party/rust/rustc_version/.gitignore
third_party/rust/same-file/.gitignore
third_party/rust/scopeguard/.gitignore
third_party/rust/semver-0.1.20/.gitignore
third_party/rust/semver-parser/.gitignore
third_party/rust/semver/.gitignore
third_party/rust/serde-0.9.9/.cargo-checksum.json
third_party/rust/serde-0.9.9/.cargo-ok
third_party/rust/serde-0.9.9/Cargo.toml
third_party/rust/serde-0.9.9/LICENSE-APACHE
third_party/rust/serde-0.9.9/LICENSE-MIT
third_party/rust/serde-0.9.9/README.md
third_party/rust/serde-0.9.9/src/bytes.rs
third_party/rust/serde-0.9.9/src/de/content.rs
third_party/rust/serde-0.9.9/src/de/from_primitive.rs
third_party/rust/serde-0.9.9/src/de/impls.rs
third_party/rust/serde-0.9.9/src/de/mod.rs
third_party/rust/serde-0.9.9/src/de/private.rs
third_party/rust/serde-0.9.9/src/de/value.rs
third_party/rust/serde-0.9.9/src/error.rs
third_party/rust/serde-0.9.9/src/export.rs
third_party/rust/serde-0.9.9/src/iter.rs
third_party/rust/serde-0.9.9/src/lib.rs
third_party/rust/serde-0.9.9/src/macros.rs
third_party/rust/serde-0.9.9/src/ser/content.rs
third_party/rust/serde-0.9.9/src/ser/impls.rs
third_party/rust/serde-0.9.9/src/ser/impossible.rs
third_party/rust/serde-0.9.9/src/ser/mod.rs
third_party/rust/serde-0.9.9/src/ser/private.rs
third_party/rust/serde-0.9.9/src/utils.rs
third_party/rust/serde/Cargo.toml.orig
third_party/rust/serde_codegen_internals/.cargo-checksum.json
third_party/rust/serde_codegen_internals/.cargo-ok
third_party/rust/serde_codegen_internals/Cargo.toml
third_party/rust/serde_codegen_internals/LICENSE-APACHE
third_party/rust/serde_codegen_internals/LICENSE-MIT
third_party/rust/serde_codegen_internals/README.md
third_party/rust/serde_codegen_internals/src/ast.rs
third_party/rust/serde_codegen_internals/src/attr.rs
third_party/rust/serde_codegen_internals/src/case.rs
third_party/rust/serde_codegen_internals/src/ctxt.rs
third_party/rust/serde_codegen_internals/src/lib.rs
third_party/rust/simd/.gitignore
third_party/rust/siphasher/.gitignore
third_party/rust/slog-atomic/.gitignore
third_party/rust/slog-extra/.gitignore
third_party/rust/slog-stream/.gitignore
third_party/rust/slog-term/.gitignore
third_party/rust/slog/.gitignore
third_party/rust/smallvec-0.3.3/.gitignore
third_party/rust/smallvec/.gitignore
third_party/rust/strsim/.gitignore
third_party/rust/synstructure/.gitignore
third_party/rust/tempdir/.gitignore
third_party/rust/term/.gitignore
third_party/rust/term_size/.gitignore
third_party/rust/thread-id/.gitignore
third_party/rust/thread_local/.gitignore
third_party/rust/thread_profiler/.gitignore
third_party/rust/time/.gitignore
third_party/rust/toml/.gitignore
third_party/rust/traitobject/.gitignore
third_party/rust/typeable/.gitignore
third_party/rust/unicase/.gitignore
third_party/rust/unicode-bidi/.gitignore
third_party/rust/unicode-normalization/.gitignore
third_party/rust/unicode-segmentation/.gitignore
third_party/rust/unicode-width/.gitignore
third_party/rust/unicode-xid/.gitignore
third_party/rust/unreachable/.gitignore
third_party/rust/url/.gitignore
third_party/rust/url/Cargo.toml.orig
third_party/rust/utf8-ranges/.gitignore
third_party/rust/uuid/.gitignore
third_party/rust/vec_map/.gitignore
third_party/rust/void/.gitignore
third_party/rust/walkdir/.gitignore
third_party/rust/webdriver/.gitignore
third_party/rust/winreg/.gitignore
third_party/rust/xml-rs/.gitignore
third_party/rust/zip/.gitignore
toolkit/components/extensions/test/browser/browser_ext_themes_toolbars.js
toolkit/components/places/Bookmarks.jsm
toolkit/components/places/PlacesUtils.jsm
xpcom/tests/gtest/TestThreadUtils.cpp
--- a/.clang-format-ignore
+++ b/.clang-format-ignore
@@ -30,19 +30,19 @@
 ^gfx/webrender_traits.*
 ^gfx/ycbcr/.*
 ^intl/hyphenation/hyphen/.*
 ^intl/icu/.*
 ^ipc/chromium/.*
 ^js/src/ctypes/libffi/.*
 ^js/src/dtoa.c.*
 ^js/src/jit/arm64/vixl/.*
+^media/ffvpx/.*
 ^media/gmp-clearkey/0.1/openaes/.*
 ^media/kiss_fft/.*
-^media/ffvpx/.*
 ^media/libav/.*
 ^media/libcubeb/.*
 ^media/libjpeg/.*
 ^media/libmkv/.*
 ^media/libnestegg/.*
 ^media/libogg/.*
 ^media/libopus/.*
 ^media/libpng/.*
@@ -53,30 +53,53 @@
 ^media/libtremor/.*
 ^media/libvorbis/.*
 ^media/libvpx/.*
 ^media/libyuv/.*
 ^media/mtransport/third_party/.*
 ^media/openmax_dl/.*
 ^media/pocketsphinx/.*
 ^media/sphinxbase/.*
+^media/webrtc/signaling/src/sdp/sipcc/.*
 ^media/webrtc/trunk/.*
-^media/webrtc/signaling/src/sdp/sipcc/.*
 ^mfbt/decimal/.*
 ^mfbt/double-conversion/source/.*
 ^mfbt/lz4.*
+^mobile/android/geckoview/src/thirdparty/.*
 ^mobile/android/thirdparty/.*
 ^modules/brotli/.*
 ^modules/fdlibm/.*
 ^modules/freetype2/.*
 ^modules/libbz2/.*
 ^modules/libmar/.*
+^modules/woff2/.*
 ^modules/zlib/.*
 ^netwerk/sctp/src/.*
 ^netwerk/srtp/src/.*
 ^nsprpub/.*
 ^other-licenses/.*
 ^parser/expat/.*
+^security/nss/.*
 ^security/sandbox/chromium/.*
 ^testing/gtest/gmock/.*
 ^testing/gtest/gtest/.*
+^testing/talos/talos/tests/dromaeo/.*
+^third_party/aom/.*
+^third_party/python/blessings/.*
+^third_party/python/configobj/.*
+^third_party/python/futures/.*
+^third_party/python/jsmin/.*
+^third_party/python/mock-*/.*
+^third_party/python/psutil/.*
+^third_party/python/py/.*
+^third_party/python/pyasn1/.*
+^third_party/python/pyasn1-modules/.*
+^third_party/python/PyECC/.*
+^third_party/python/pytest/.*
+^third_party/python/pytoml/.*
+^third_party/python/pyyaml/.*
+^third_party/python/redo/.*
+^third_party/python/requests/.*
+^third_party/python/rsa/.*
+^third_party/python/which/.*
+^toolkit/components/jsoncpp/.*
 ^toolkit/components/protobuf/.*
 ^toolkit/crashreporter/google-breakpad/.*
--- a/browser/base/content/browser-sidebar.js
+++ b/browser/base/content/browser-sidebar.js
@@ -63,17 +63,16 @@ var SidebarUI = {
     });
   },
 
   uninit() {
     let enumerator = Services.wm.getEnumerator(null);
     enumerator.getNext();
     if (!enumerator.hasMoreElements()) {
       document.persist("sidebar-box", "sidebarcommand");
-      document.persist("sidebar-box", "checked");
       document.persist("sidebar-box", "width");
       document.persist("sidebar-title", "value");
     }
   },
 
   /**
    * Opens the switcher panel if it's closed, or closes it if it's open.
    */
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -7,35 +7,29 @@
 @namespace svg url("http://www.w3.org/2000/svg");
 
 :root {
   --identity-popup-expander-width: 38px;
   --panelui-subview-transition-duration: 150ms;
   --lwt-additional-images: none;
   --lwt-background-alignment: right top;
   --lwt-background-tiling: no-repeat;
-  --lwt-toolbar-color: inherit;
 }
 
 :root:-moz-lwtheme {
   color: var(--lwt-text-color) !important;
 }
 
 :root:-moz-lwtheme:not([customization-lwtheme]) {
   background-color: var(--lwt-accent-color) !important;
   background-image: var(--lwt-header-image), var(--lwt-additional-images) !important;
   background-position: var(--lwt-background-alignment) !important;
   background-repeat: var(--lwt-background-tiling) !important;
 }
 
-#navigator-toolbox > toolbar,
-findbar {
-  background: var(--lwt-toolbar-color) !important;
-}
-
 #main-window:not([chromehidden~="toolbar"]) {
 %ifdef XP_MACOSX
   min-width: 335px;
 %else
   min-width: 300px;
 %endif
 }
 
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1,24 +1,26 @@
 /* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 sw=2 sts=2 et 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/. */
 
 Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
-Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
-Components.utils.import("resource://gre/modules/LoginManagerContextMenu.jsm");
 Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 
+XPCOMUtils.defineLazyModuleGetter(this, "SpellCheckHelper",
+  "resource://gre/modules/InlineSpellChecker.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
   "resource://gre/modules/LoginHelper.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContextMenu",
+  "resource://gre/modules/LoginManagerContextMenu.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "WebNavigationFrames",
   "resource://gre/modules/WebNavigationFrames.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
   "resource://gre/modules/ContextualIdentityService.jsm");
 
 var gContextMenuContentData = null;
 
 function openContextMenu(aMessage) {
@@ -141,17 +143,19 @@ nsContextMenu.prototype = {
     this._checkTelemetryForMenu(aXulMenu);
   },
 
   hiding: function CM_hiding() {
     gContextMenuContentData = null;
     InlineSpellCheckerUI.clearSuggestionsFromMenu();
     InlineSpellCheckerUI.clearDictionaryListFromMenu();
     InlineSpellCheckerUI.uninit();
-    LoginManagerContextMenu.clearLoginsFromMenu(document);
+    if (Cu.isModuleLoaded("resource://gre/modules/LoginManagerContextMenu.jsm")) {
+      LoginManagerContextMenu.clearLoginsFromMenu(document);
+    }
 
     // This handler self-deletes, only run it if it is still there:
     if (this._onPopupHiding) {
       this._onPopupHiding();
     }
   },
 
   initItems: function CM_initItems() {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5638,16 +5638,26 @@
         ]]>
       </handler>
       <handler event="oop-browser-crashed">
         <![CDATA[
           if (!event.isTrusted)
             return;
 
           let browser = event.originalTarget;
+
+          // Preloaded browsers do not actually have any tabs. If one crashes,
+          // it should be released and removed.
+          if (browser === this._preloadedBrowser) {
+            // Calling _getPreloadedBrowser is necessary to actually consume the preloaded browser
+            let preloaded = this._getPreloadedBrowser();
+            preloaded.remove();
+            return;
+          }
+
           let icon = browser.mIconURL;
           let tab = this.getTabForBrowser(browser);
 
           if (this.selectedBrowser == browser) {
             TabCrashHandler.onSelectedBrowserCrash(browser);
           } else {
             this.updateBrowserRemoteness(browser, false);
             SessionStore.reviveCrashedTab(tab);
--- a/browser/base/content/test/performance/browser_startup.js
+++ b/browser/base/content/test/performance/browser_startup.js
@@ -85,17 +85,21 @@ const startupPhases = {
       "@mozilla.org/browser/nav-bookmarks-service;1",
       "@mozilla.org/browser/search-service;1",
     ])
   }},
 
   // We are at this phase once we are ready to handle user events.
   // Anything loaded at this phase or before gets in the way of the user
   // interacting with the first browser window.
-  "before handling user events": {},
+  "before handling user events": {blacklist: {
+    modules: new Set([
+      "resource://gre/modules/LoginManagerContextMenu.jsm",
+    ]),
+  }},
 };
 
 function test() {
   if (!AppConstants.NIGHTLY_BUILD && !AppConstants.DEBUG) {
     ok(!("@mozilla.org/test/startuprecorder;1" in Cc),
        "the startup recorder component shouldn't exist in this non-nightly non-debug build.");
     return;
   }
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1711,17 +1711,17 @@ BrowserGlue.prototype = {
         return;
       this._openPreferences("sync", { origin: "doorhanger" });
     }
     AlertsService.showAlertNotification(null, title, body, true, null, clickCallback);
   },
 
   // eslint-disable-next-line complexity
   _migrateUI: function BG__migrateUI() {
-    const UI_VERSION = 47;
+    const UI_VERSION = 48;
     const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
 
     let currentUIVersion;
     if (Services.prefs.prefHasUserValue("browser.migration.version")) {
       currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
     } else {
       // This is a new profile, nothing to migrate.
       Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
@@ -2041,16 +2041,24 @@ BrowserGlue.prototype = {
           // In this case just fallback to the safest side and disable suggestions.
           Services.prefs.setBoolPref("browser.urlbar.suggest.searches", false);
         }
       } catch (ex) {
         // A missing pref is not a fatal error.
       }
     }
 
+    if (currentUIVersion < 48) {
+      // Bug 1372954 - the checked value was persisted but the attribute removal wouldn't
+      // be persisted (Bug 15232). Turns out we can just not persist the value in this case.
+      // The situation was only happening for a few nightlies in 56, so this migration can
+      // be removed in version 58.
+      xulStore.removeValue(BROWSER_DOCURL, "sidebar-box", "checked");
+    }
+
     // Update the migration version.
     Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
   },
 
   // ------------------------------
   // public nsIBrowserGlue members
   // ------------------------------
 
--- a/browser/components/resistfingerprinting/test/browser/browser.ini
+++ b/browser/components/resistfingerprinting/test/browser/browser.ini
@@ -1,15 +1,18 @@
 [DEFAULT]
 tags = resistfingerprinting
 support-files =
   file_dummy.html
+  file_navigator.html
+  file_navigatorWorker.js
   file_workerPerformance.js
   head.js
 
+[browser_navigator.js]
 [browser_performanceAPI.js]
 [browser_roundedWindow_dialogWindow.js]
 [browser_roundedWindow_newWindow.js]
 [browser_roundedWindow_open_max.js]
 [browser_roundedWindow_open_mid.js]
 [browser_roundedWindow_open_min.js]
 [browser_roundedWindow_windowSetting_max.js]
 [browser_roundedWindow_windowSetting_mid.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_navigator.js
@@ -0,0 +1,122 @@
+/**
+ * Bug 1333651 - A test case for making sure the navigator object has been
+ *   spoofed/disabled correctly.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+const TEST_PATH = "http://example.net/browser/browser/" +
+                  "components/resistfingerprinting/test/browser/"
+
+var spoofedUserAgent;
+
+const SPOOFED_APPNAME        = "Netscape";
+const SPOOFED_APPVERSION     = "5.0 (Windows)";
+const SPOOFED_PLATFORM       = "Win32";
+const SPOOFED_OSCPU          = "Windows NT 6.1";
+const SPOOFED_BUILDID        = "20100101";
+const SPOOFED_HW_CONCURRENCY = 2;
+
+const CONST_APPCODENAME = "Mozilla";
+const CONST_PRODUCT     = "Gecko";
+const CONST_PRODUCTSUB  = "20100101";
+const CONST_VENDOR      = "";
+const CONST_VENDORSUB   = "";
+
+async function testNavigator() {
+  // Open a tab to collect result.
+  let tab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser, TEST_PATH + "file_navigator.html");
+
+  let result = await ContentTask.spawn(tab.linkedBrowser, null, function() {
+    return content.document.getElementById("result").innerHTML;
+  });
+
+  result = JSON.parse(result);
+
+  is(result.appName, SPOOFED_APPNAME, "Navigator.appName is correctly spoofed.");
+  is(result.appVersion, SPOOFED_APPVERSION, "Navigator.appVersion is correctly spoofed.");
+  is(result.platform, SPOOFED_PLATFORM, "Navigator.platform is correctly spoofed.");
+  is(result.userAgent, spoofedUserAgent, "Navigator.userAgent is correctly spoofed.");
+  is(result.mimeTypesLength, 0, "Navigator.mimeTypes has a length of 0.");
+  is(result.pluginsLength, 0, "Navigator.plugins has a length of 0.");
+  is(result.oscpu, SPOOFED_OSCPU, "Navigator.oscpu is correctly spoofed.");
+  is(result.buildID, SPOOFED_BUILDID, "Navigator.buildID is correctly spoofed.");
+  is(result.hardwareConcurrency, SPOOFED_HW_CONCURRENCY, "Navigator.hardwareConcurrency is correctly spoofed.")
+
+  is(result.appCodeName, CONST_APPCODENAME, "Navigator.appCodeName reports correct constant value.");
+  is(result.product, CONST_PRODUCT, "Navigator.product reports correct constant value.");
+  is(result.productSub, CONST_PRODUCTSUB, "Navigator.productSub reports correct constant value.");
+  is(result.vendor, CONST_VENDOR, "Navigator.vendor reports correct constant value.");
+  is(result.vendorSub, CONST_VENDORSUB, "Navigator.vendorSub reports correct constant value.");
+
+  await BrowserTestUtils.removeTab(tab);
+}
+
+async function testWorkerNavigator() {
+  // Open a tab to collect result from worker.
+  let tab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser, TEST_PATH + "file_dummy.html");
+
+  let result = await ContentTask.spawn(tab.linkedBrowser, null, async function() {
+    let worker = new content.SharedWorker("file_navigatorWorker.js", "WorkerNavigatorTest");
+
+    let res = await new Promise(resolve => {
+      worker.port.onmessage = function(e) {
+        resolve(e.data);
+      };
+    });
+
+    return res;
+  });
+
+  result = JSON.parse(result);
+
+  is(result.appName, SPOOFED_APPNAME, "Navigator.appName is correctly spoofed.");
+  is(result.appVersion, SPOOFED_APPVERSION, "Navigator.appVersion is correctly spoofed.");
+  is(result.platform, SPOOFED_PLATFORM, "Navigator.platform is correctly spoofed.");
+  is(result.userAgent, spoofedUserAgent, "Navigator.userAgent is correctly spoofed.");
+  is(result.hardwareConcurrency, SPOOFED_HW_CONCURRENCY, "Navigator.hardwareConcurrency is correctly spoofed.")
+
+  is(result.appCodeName, CONST_APPCODENAME, "Navigator.appCodeName reports correct constant value.");
+  is(result.product, CONST_PRODUCT, "Navigator.product reports correct constant value.");
+
+  await BrowserTestUtils.removeTab(tab);
+}
+
+add_task(async function setup() {
+  await SpecialPowers.pushPrefEnv({"set":
+    [["privacy.resistFingerprinting", true]]
+  });
+
+  let appInfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
+  let appVersion = parseInt(appInfo.version);
+  let spoofedVersion = appVersion - (appVersion % 10);
+  spoofedUserAgent = `Mozilla/5.0 (Windows NT 6.1; rv:${spoofedVersion}.0) Gecko/20100101 Firefox/${spoofedVersion}.0`;
+});
+
+add_task(async function runNavigatorTest() {
+  await testNavigator();
+});
+
+add_task(async function runWorkerNavigatorTest() {
+  await testWorkerNavigator();
+});
+
+// This tests that 'general.*.override' should not override spoofed values.
+add_task(async function runOverrideTest() {
+  await SpecialPowers.pushPrefEnv({"set":
+    [
+      ["general.appname.override", "appName overridden"],
+      ["general.appversion.override", "appVersion overridden"],
+      ["general.platform.override", "platform overridden"],
+      ["general.useragent.override", "userAgent overridden"],
+      ["general.oscpu.override", "oscpu overridden"],
+      ["general.buildID.override", "buildID overridden"],
+    ]
+  });
+
+  await testNavigator();
+
+  await testWorkerNavigator();
+});
--- a/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_newWindow.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_newWindow.js
@@ -1,17 +1,17 @@
 /*
  * Bug 1330882 - A test case for opening new windows as rounded size when
  *   fingerprinting resistance is enabled.
  */
 
 const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
 
 const TEST_DOMAIN = "http://example.net/";
-const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistFingerprinting/test/browser/";
+const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
 
 let gMaxAvailWidth;
 let gMaxAvailHeight;
 
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({"set":
     [["privacy.resistFingerprinting", true]]
   });
--- a/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_max.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_max.js
@@ -2,17 +2,17 @@
  * Bug 1330882 - A test case for opening new windows through window.open() as
  *   rounded size when fingerprinting resistance is enabled. This test is for
  *   maximum values.
  */
 
 const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
 
 const TEST_DOMAIN = "http://example.net/";
-const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistFingerprinting/test/browser/";
+const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
 
 let gMaxAvailWidth;
 let gMaxAvailHeight;
 
 // We need the chrome UI size of popup windows for testing outerWidth/Height.
 let gPopupChromeUIWidth;
 let gPopupChromeUIHeight;
 
--- a/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_mid.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_mid.js
@@ -2,17 +2,17 @@
  * Bug 1330882 - A test case for opening new windows through window.open() as
  *   rounded size when fingerprinting resistance is enabled. This test is for
  *   middle values.
  */
 
 const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
 
 const TEST_DOMAIN = "http://example.net/";
-const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistFingerprinting/test/browser/";
+const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
 
 let gMaxAvailWidth;
 let gMaxAvailHeight;
 
 // We need the chrome UI size of popup windows for testing outerWidth/Height.
 let gPopupChromeUIWidth;
 let gPopupChromeUIHeight;
 
--- a/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_min.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_min.js
@@ -2,17 +2,17 @@
  * Bug 1330882 - A test case for opening new windows through window.open() as
  *   rounded size when fingerprinting resistance is enabled. This test is for
  *   minimum values.
  */
 
 const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
 
 const TEST_DOMAIN = "http://example.net/";
-const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistFingerprinting/test/browser/";
+const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
 
 let gMaxAvailWidth;
 let gMaxAvailHeight;
 
 // We need the chrome UI size of popup windows for testing outerWidth/Height.
 let gPopupChromeUIWidth;
 let gPopupChromeUIHeight;
 
--- a/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_windowSetting_max.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_windowSetting_max.js
@@ -2,17 +2,17 @@
  * Bug 1330882 - A test case for setting window size through window.innerWidth/Height
  *   and window.outerWidth/Height when fingerprinting resistance is enabled. This
  *   test is for maximum values.
  */
 
 const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
 
 const TEST_DOMAIN = "http://example.net/";
-const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistFingerprinting/test/browser/";
+const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
 
 let gMaxAvailWidth;
 let gMaxAvailHeight;
 
 // We need the chrome UI size of popup windows for testing outerWidth/Height.
 let gPopupChromeUIWidth;
 let gPopupChromeUIHeight;
 
--- a/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_windowSetting_mid.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_windowSetting_mid.js
@@ -2,17 +2,17 @@
  * Bug 1330882 - A test case for setting window size through window.innerWidth/Height
  *   and window.outerWidth/Height when fingerprinting resistance is enabled. This
  *   test is for middle values.
  */
 
 const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
 
 const TEST_DOMAIN = "http://example.net/";
-const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistFingerprinting/test/browser/";
+const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
 
 let gMaxAvailWidth;
 let gMaxAvailHeight;
 
 // We need the chrome UI size of popup windows for testing outerWidth/Height.
 let gPopupChromeUIWidth;
 let gPopupChromeUIHeight;
 
--- a/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_windowSetting_min.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_windowSetting_min.js
@@ -2,17 +2,17 @@
  * Bug 1330882 - A test case for setting window size through window.innerWidth/Height
  *   and window.outerWidth/Height when fingerprinting resistance is enabled. This
  *   test is for minimum values.
  */
 
 const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
 
 const TEST_DOMAIN = "http://example.net/";
-const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistFingerprinting/test/browser/";
+const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
 
 let gMaxAvailWidth;
 let gMaxAvailHeight;
 
 // We need the chrome UI size of popup windows for testing outerWidth/Height.
 let gPopupChromeUIWidth;
 let gPopupChromeUIHeight;
 
--- a/browser/components/resistfingerprinting/test/browser/browser_timezone.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_timezone.js
@@ -1,15 +1,15 @@
 /**
  * Bug 1330890 - A test case for verifying Date() object of javascript will use
  *               UTC timezone after fingerprinting resistance is enabled.
  */
 
 const TEST_DOMAIN = "http://example.net/";
-const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistFingerprinting/test/browser/";
+const TEST_PATH = TEST_DOMAIN + "browser/browser/components/resistfingerprinting/test/browser/";
 
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({"set":
     [["privacy.resistFingerprinting", true]]
   });
 });
 
 add_task(async function test_timezone() {
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_navigator.html
@@ -0,0 +1,33 @@
+<html>
+<head>
+<title>Test page for navigator object</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
+<script>
+  // This page will collect information from the navigator object and store
+  // the result at a paragraph in the page.
+  function collect() {
+    let result = {};
+
+    result["appCodeName"] = navigator.appCodeName;
+    result["appName"] = navigator.appName;
+    result["appVersion"] = navigator.appVersion;
+    result["platform"] = navigator.platform;
+    result["userAgent"] = navigator.userAgent;
+    result["product"] = navigator.product;
+    result["productSub"] = navigator.productSub;
+    result["vendor"] = navigator.vendor;
+    result["vendorSub"] = navigator.vendorSub;
+    result["mimeTypesLength"] = navigator.mimeTypes.length;
+    result["pluginsLength"] = navigator.plugins.length;
+    result["oscpu"] = navigator.oscpu;
+    result["buildID"] = navigator.buildID;
+    result["hardwareConcurrency"] = navigator.hardwareConcurrency;
+
+    document.getElementById("result").innerHTML = JSON.stringify(result);
+  }
+</script>
+</head>
+<body onload="collect();">
+<p id="result"></p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/file_navigatorWorker.js
@@ -0,0 +1,19 @@
+/* eslint-env worker */
+
+onconnect = function(e) {
+  let port = e.ports[0];
+
+  let navigatorObj = self.navigator;
+  let result = {};
+
+  result["appCodeName"] = navigatorObj.appCodeName;
+  result["appName"] = navigatorObj.appName;
+  result["appVersion"] = navigatorObj.appVersion;
+  result["platform"] = navigatorObj.platform;
+  result["userAgent"] = navigatorObj.userAgent;
+  result["product"] = navigatorObj.product;
+  result["hardwareConcurrency"] = navigatorObj.hardwareConcurrency;
+
+  port.postMessage(JSON.stringify(result));
+  port.start();
+};
--- a/browser/components/resistfingerprinting/test/browser/head.js
+++ b/browser/components/resistfingerprinting/test/browser/head.js
@@ -6,17 +6,17 @@
 
 
 // This function calculates the maximum available window dimensions and returns
 // them as an object.
 async function calcMaximumAvailSize(aChromeWidth, aChromeHeight) {
   let chromeUIWidth;
   let chromeUIHeight;
   let testPath = "http://example.net/browser/browser/" +
-                 "components/resistFingerprinting/test/browser/"
+                 "components/resistfingerprinting/test/browser/"
 
   // If the chrome UI dimensions is not given, we will calculate it.
   if (!aChromeWidth || !aChromeHeight) {
     let win = await BrowserTestUtils.openNewBrowserWindow();
 
     let tab = await BrowserTestUtils.openNewForegroundTab(
       win.gBrowser, testPath + "file_dummy.html");
 
--- a/browser/components/sessionstore/test/browser_background_tab_crash.js
+++ b/browser/components/sessionstore/test/browser_background_tab_crash.js
@@ -214,8 +214,35 @@ add_task(async function test_background_
 
       // Selecting the third tab should restore it.
       tabRestored = promiseTabRestored(tab3);
       await BrowserTestUtils.switchTab(gBrowser, tab3);
       await tabRestored;
     });
   });
 });
+
+// Tests that crashed preloaded tabs are removed and no unexpected errors are
+// thrown.
+add_task(async function test_preload_crash() {
+  if (!Services.prefs.getBoolPref("browser.newtab.preload")) {
+    return;
+  }
+
+  // Since new tab is only crashable for the activity-stream version,
+  // we need to flip the pref
+  await SpecialPowers.pushPrefEnv({
+    set: [[ "browser.newtabpage.activity-stream.enabled", true ]]
+  });
+
+  // Release any existing preloaded browser
+  let preloaded = gBrowser._getPreloadedBrowser();
+  if (preloaded) {
+    preloaded.remove();
+  }
+
+  // Create a fresh preloaded browser
+  gBrowser._createPreloadBrowser();
+
+  await BrowserTestUtils.crashBrowser(gBrowser._preloadedBrowser, false);
+
+  Assert.ok(!gBrowser._preloadedBrowser);
+});
--- a/browser/extensions/followonsearch/content/followonsearch-fs.js
+++ b/browser/extensions/followonsearch/content/followonsearch-fs.js
@@ -138,24 +138,31 @@ function log(message) {
   // console.log(message);
 }
 
 // Hack to handle the most common reload case.
 // If gLastSearch is the same as the current URL, ignore the search.
 // This also prevents us from handling reloads with hashes twice
 let gLastSearch = null;
 
+// Keep track of the original window we were loaded in
+// so we don't handle requests for other windows.
+let gOriginalWindow = null;
+
 /**
  * Since most codes are in the URL, we can handle them via
  * a progress listener.
  */
 var webProgressListener = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
   onLocationChange(aWebProgress, aRequest, aLocation, aFlags)
   {
+    if (aWebProgress.DOMWindow && (aWebProgress.DOMWindow != gOriginalWindow)) {
+      return;
+    }
     try {
       if (!aWebProgress.isTopLevel ||
           // Not a URL
           (!aLocation.schemeIs("http") && !aLocation.schemeIs("https")) ||
           // Doesn't have a query string or a ref
           (!aLocation.query && !aLocation.ref) ||
           // Is the same as our last search (avoids reloads)
           aLocation.spec == gLastSearch) {
@@ -226,19 +233,19 @@ function onPageLoad(event) {
   var uri = doc.documentURIObject;
   if (!(uri instanceof Ci.nsIStandardURL) ||
       (!uri.schemeIs("http") && !uri.schemeIs("https")) ||
        uri.host != "www.bing.com" ||
       !doc.location.search ||
       uri.spec == gLastSearch) {
     return;
   }
-  var queries = new URLSearchParams(doc.location.search);
+  var queries = new URLSearchParams(doc.location.search.toLowerCase());
   // For Bing, QBRE form code is used for all follow-on search
-  if (queries.get("form") != "QBRE") {
+  if (queries.get("form") != "qbre") {
     return;
   }
   if (parseCookies(doc.cookie).SRCHS == "PC=MOZI") {
     log(`${uri.host} search with code MOZI - Follow on`);
     sendSaveTelemetryMsg("MOZI", "bing", "follow-on");
     gLastSearch = uri.spec;
   }
 }
@@ -258,16 +265,18 @@ function sendSaveTelemetryMsg(code, sap,
     type,
   });
 }
 
 addEventListener("DOMContentLoaded", onPageLoad, false);
 docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress)
         .addProgressListener(webProgressListener, Ci.nsIWebProgress.NOTIFY_LOCATION);
 
+gOriginalWindow = content;
+
 let gDisabled = false;
 
 addMessageListener(kShutdownMsg, () => {
   if (!gDisabled) {
     removeEventListener("DOMContentLoaded", onPageLoad, false);
     docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress)
             .removeProgressListener(webProgressListener);
     gDisabled = true;
--- a/browser/extensions/followonsearch/install.rdf
+++ b/browser/extensions/followonsearch/install.rdf
@@ -2,17 +2,17 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
   <Description about="urn:mozilla:install-manifest">
     <em:id>followonsearch@mozilla.com</em:id>
     <em:name>Follow-on Search Telemetry</em:name>
-    <em:version>0.9.0</em:version>
+    <em:version>0.9.1</em:version>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:multiprocessCompatible>true</em:multiprocessCompatible>
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
         <em:minVersion>52.0</em:minVersion>
         <em:maxVersion>59.*</em:maxVersion>
--- a/browser/themes/linux/customizableui/panelUI.css
+++ b/browser/themes/linux/customizableui/panelUI.css
@@ -97,13 +97,17 @@ menu.subviewbutton > .menu-right:-moz-lo
   -moz-appearance: none;
 }
 
 /* START photon adjustments */
 
 photonpanelmultiview .subviewbutton > .toolbarbutton-text,
 photonpanelmultiview .subviewbutton > .toolbarbutton-icon,
 photonpanelmultiview .panel-banner-item > .toolbarbutton-multiline-text {
-  padding: 0;
   margin: 0;
 }
 
+photonpanelmultiview .subviewbutton > .toolbarbutton-icon,
+photonpanelmultiview .panel-banner-item > .toolbarbutton-multiline-text {
+  padding: 0;
+}
+
 /* END photon adjustments */
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -1249,16 +1249,17 @@ photonpanelmultiview .PanelUI-subView .s
   padding: 4px 12px;
 }
 
 photonpanelmultiview .subviewbutton:focus {
   outline: 0;
 }
 
 photonpanelmultiview .subviewbutton > .toolbarbutton-text {
+  padding: 0;
   padding-inline-start: 24px; /* This is 16px for the icon + 8px for the padding as defined above. */
 }
 
 photonpanelmultiview .subviewbutton-iconic:not(.subviewbutton-back) > .toolbarbutton-text,
 photonpanelmultiview .cui-withicon > .toolbarbutton-text,
 photonpanelmultiview .subviewbutton[image] > .toolbarbutton-text,
 photonpanelmultiview .subviewbutton[checked="true"] > .toolbarbutton-text,
 photonpanelmultiview .panel-banner-item > .toolbarbutton-multiline-text {
--- a/browser/themes/shared/icons/back-large.svg
+++ b/browser/themes/shared/icons/back-large.svg
@@ -1,6 +1,6 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <path fill="context-fill" d="M14 6H6.5l3-3a.967.967 0 0 0 0-1.4L8.7.7a.967.967 0 0 0-1.4 0L.7 7.3a.967.967 0 0 0 0 1.4l6.6 6.6a.967.967 0 0 0 1.4 0l.8-.8a.965.965 0 0 0 0-1.4l-3-3.1H14c.6 0 1-.2 1-.8V7a.945.945 0 0 0-1-1z"/>
+  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M14 6H6.5l3-3a.967.967 0 0 0 0-1.4L8.7.7a.967.967 0 0 0-1.4 0L.7 7.3a.967.967 0 0 0 0 1.4l6.6 6.6a.967.967 0 0 0 1.4 0l.8-.8a.965.965 0 0 0 0-1.4l-3-3.1H14c.6 0 1-.2 1-.8V7a.945.945 0 0 0-1-1z"/>
 </svg>
--- a/browser/themes/windows/customizableui/panelUI.css
+++ b/browser/themes/windows/customizableui/panelUI.css
@@ -147,13 +147,17 @@ menu.subviewbutton > .menu-right:-moz-lo
   }
 }
 
 /* START photon adjustments */
 
 photonpanelmultiview .subviewbutton > .toolbarbutton-text,
 photonpanelmultiview .subviewbutton > .toolbarbutton-icon,
 photonpanelmultiview .panel-banner-item > .toolbarbutton-multiline-text {
-  padding: 0;
   margin: 0;
 }
 
+photonpanelmultiview .subviewbutton > .toolbarbutton-icon,
+photonpanelmultiview .panel-banner-item > .toolbarbutton-multiline-text {
+  padding: 0;
+}
+
 /* END photon adjustments */
--- a/devtools/client/framework/toolbox-options.js
+++ b/devtools/client/framework/toolbox-options.js
@@ -317,21 +317,16 @@ OptionsPanel.prototype = {
       label: "Enable new console frontend",
       id: "devtools-new-webconsole",
       parentId: "webconsole-options"
     }, {
       pref: "devtools.debugger.new-debugger-frontend",
       label: "Enable new debugger frontend",
       id: "devtools-new-debugger",
       parentId: "debugger-options"
-    }, {
-      pref: "devtools.layoutview.enabled",
-      label: "Enable layout panel",
-      id: "devtools-layout-panel",
-      parentId: "inspector-options"
     }];
 
     let createPreferenceOption = ({pref, label, id}) => {
       let inputLabel = this.panelDoc.createElement("label");
       let checkbox = this.panelDoc.createElement("input");
       checkbox.setAttribute("type", "checkbox");
       if (GetPref(pref)) {
         checkbox.setAttribute("checked", "checked");
--- a/devtools/client/inspector/boxmodel/test/head.js
+++ b/devtools/client/inspector/boxmodel/test/head.js
@@ -6,20 +6,18 @@
 /* import-globals-from ../../test/head.js */
 "use strict";
 
 // Import the inspector's head.js first (which itself imports shared-head.js).
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
   this);
 
-Services.prefs.setBoolPref("devtools.layoutview.enabled", true);
 Services.prefs.setIntPref("devtools.toolbox.footer.height", 350);
 registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("devtools.layoutview.enabled");
   Services.prefs.clearUserPref("devtools.toolbox.footer.height");
 });
 
 /**
  * Highlight a node and set the inspector's current selection to the node or
  * the first match of the given css selector.
  *
  * @param  {String|NodeFront} selectorOrNodeFront
--- a/devtools/client/inspector/grids/test/head.js
+++ b/devtools/client/inspector/grids/test/head.js
@@ -11,21 +11,19 @@ Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
   this);
 
 // Load the shared Redux helpers into this compartment.
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js",
   this);
 
-Services.prefs.setBoolPref("devtools.layoutview.enabled", true);
 Services.prefs.setBoolPref("devtools.promote.layoutview.showPromoteBar", false);
 Services.prefs.setIntPref("devtools.toolbox.footer.height", 350);
 registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("devtools.layoutview.enabled");
   Services.prefs.clearUserPref("devtools.promote.layoutview.showPromoteBar");
   Services.prefs.clearUserPref("devtools.toolbox.footer.height");
 });
 
 const HIGHLIGHTER_TYPE = "CssGridHighlighter";
 
 /**
  * Simulate a color change in a given color picker tooltip.
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -615,24 +615,22 @@ Inspector.prototype = {
       INSPECTOR_L10N.getStr("inspector.sidebar.ruleViewTitle"),
       defaultTab == "ruleview");
 
     this.sidebar.addExistingTab(
       "computedview",
       INSPECTOR_L10N.getStr("inspector.sidebar.computedViewTitle"),
       defaultTab == "computedview");
 
-    if (Services.prefs.getBoolPref("devtools.layoutview.enabled")) {
-      // Grid and layout panels aren't lazy-loaded as their module end up
-      // calling inspector.addSidebarTab
-      this.gridInspector = new GridInspector(this, this.panelWin);
+    // Grid and layout panels aren't lazy-loaded as their module end up
+    // calling inspector.addSidebarTab
+    this.gridInspector = new GridInspector(this, this.panelWin);
 
-      const LayoutView = this.browserRequire("devtools/client/inspector/layout/layout");
-      this.layoutview = new LayoutView(this, this.panelWin);
-    }
+    const LayoutView = this.browserRequire("devtools/client/inspector/layout/layout");
+    this.layoutview = new LayoutView(this, this.panelWin);
 
     if (this.target.form.animationsActor) {
       this.sidebar.addFrameTab(
         "animationinspector",
         INSPECTOR_L10N.getStr("inspector.sidebar.animationInspectorTitle"),
         "chrome://devtools/content/animationinspector/animation-inspector.xhtml",
         defaultTab == "animationinspector");
     }
--- a/devtools/client/inspector/rules/test/browser_rules_eyedropper.js
+++ b/devtools/client/inspector/rules/test/browser_rules_eyedropper.js
@@ -109,15 +109,15 @@ function* testSelect(view, swatch, inspe
 function* openEyedropper(view, swatch) {
   let tooltip = view.tooltips.getTooltip("colorPicker").tooltip;
 
   info("Click on the swatch");
   let onColorPickerReady = view.tooltips.getTooltip("colorPicker").once("ready");
   swatch.click();
   yield onColorPickerReady;
 
-  let dropperButton = tooltip.doc.querySelector("#eyedropper-button");
+  let dropperButton = tooltip.container.querySelector("#eyedropper-button");
 
   info("Click on the eyedropper icon");
   let onOpened = tooltip.once("eyedropper-opened");
   dropperButton.click();
   yield onOpened;
 }
--- a/devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js
@@ -26,17 +26,17 @@ add_task(function* () {
   let swatchEl = ruleViewDocument.querySelector(".ruleview-colorswatch");
 
   info("Open the color picker");
   let cPicker = ruleView.tooltips.getTooltip("colorPicker");
   let onColorPickerReady = cPicker.once("ready");
   swatchEl.click();
   yield onColorPickerReady;
 
-  button = cPicker.tooltip.doc.querySelector("#eyedropper-button");
+  button = cPicker.tooltip.container.querySelector("#eyedropper-button");
   ok(isDisabled(button), "The button is disabled in the color picker");
 
   info("Navigate to a HTML document");
   yield navigateTo(inspector, TEST_URL_2);
 
   info("Check the inspector toolbar in HTML document");
   button = inspector.panelDoc.querySelector("#inspector-eyedropper-toggle");
   ok(!isDisabled(button), "The button is enabled in the toolbar");
@@ -50,15 +50,15 @@ add_task(function* () {
   swatchEl = ruleViewDocument.querySelector(".ruleview-colorswatch");
 
   info("Open the color picker in HTML document");
   cPicker = ruleView.tooltips.getTooltip("colorPicker");
   onColorPickerReady = cPicker.once("ready");
   swatchEl.click();
   yield onColorPickerReady;
 
-  button = cPicker.tooltip.doc.querySelector("#eyedropper-button");
+  button = cPicker.tooltip.container.querySelector("#eyedropper-button");
   ok(!isDisabled(button), "The button is enabled in the color picker");
 });
 
 function isDisabled(button) {
   return button.disabled;
 }
--- a/devtools/client/locales/en-US/layout.properties
+++ b/devtools/client/locales/en-US/layout.properties
@@ -1,16 +1,14 @@
 # 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/.
 
 # LOCALIZATION NOTE This file contains the Layout Inspector strings.
 # The Layout Inspector is a panel accessible in the Inspector sidebar.
-# The Layout Inspector may need to be enabled in about:config by setting
-# devtools.layoutview.enabled to true.
 
 # LOCALIZATION NOTE (layout.cannotShowGridOutline, layout.cannotSHowGridOutline.title):
 # In the case where the grid outline cannot be effectively displayed.
 layout.cannotShowGridOutline=Cannot show outline for this grid
 layout.cannotShowGridOutline.title=The selected grid’s outline cannot effectively fit inside the layout panel for it to be usable.
 
 # LOCALIZATION NOTE (layout.displayGridAreas): Label of the display grid areas setting
 # option in the CSS Grid pane.
--- a/devtools/client/netmonitor/src/har/har-automation.js
+++ b/devtools/client/netmonitor/src/har/har-automation.js
@@ -3,20 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-disable mozilla/reject-some-requires */
 
 "use strict";
 
 const { Ci } = require("chrome");
 const Services = require("Services");
-
-loader.lazyRequireGetter(this, "HarCollector", "devtools/client/netmonitor/har/har-collector", true);
-loader.lazyRequireGetter(this, "HarExporter", "devtools/client/netmonitor/har/har-exporter", true);
-loader.lazyRequireGetter(this, "HarUtils", "devtools/client/netmonitor/har/har-utils", true);
+const { HarCollector } = require("./har-collector");
+const { HarExporter } = require("./har-exporter");
+const { HarUtils } = require("./har-utils");
 
 const prefDomain = "devtools.netmonitor.har.";
 
 // Helper tracer. Should be generic sharable by other modules (bug 1171927)
 const trace = {
   log: function (...args) {
   }
 };
--- a/devtools/client/netmonitor/src/har/har-builder.js
+++ b/devtools/client/netmonitor/src/har/har-builder.js
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const appInfo = Services.appinfo;
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const { CurlUtils } = require("devtools/client/shared/curl");
-const { getLongString } = require("../connector/index");
 const {
   getFormDataSections,
   getUrlQuery,
   parseQueryString,
 } = require("../utils/request-utils");
 
 const L10N = new LocalizationHelper("devtools/client/locales/har.properties");
 const HAR_VERSION = "1.1";
@@ -265,17 +264,17 @@ HarBuilder.prototype = {
       if (CurlUtils.isUrlEncodedRequest({ headers, postDataText })) {
         postData.mimeType = "application/x-www-form-urlencoded";
 
         // Extract form parameters and produce nice HAR array.
         getFormDataSections(
           file.requestHeaders,
           file.requestHeadersFromUploadStream,
           file.requestPostData,
-          getLongString,
+          this._options.getString,
         ).then(formDataSections => {
           formDataSections.forEach(section => {
             let paramsArray = parseQueryString(section);
             if (paramsArray) {
               postData.params = [...postData.params, ...paramsArray];
             }
           });
         });
--- a/devtools/client/netmonitor/src/har/har-collector.js
+++ b/devtools/client/netmonitor/src/har/har-collector.js
@@ -79,25 +79,26 @@ HarCollector.prototype = {
     // process of HTTP data collection.
     return waitForAll(this.requests).then(() => {
       // All responses are received from the backend now. We yet need to
       // wait for a little while to see if a new request appears. If yes,
       // lets's start gathering HTTP data again. If no, we can declare
       // the page loaded.
       // If some new requests appears in the meantime the promise will
       // be rejected and we need to wait for responses all over again.
-      return this.waitForTimeout().then(() => {
+
+      this.pageLoadDeferred = this.waitForTimeout().then(() => {
         // Page loaded!
       }, () => {
         trace.log("HarCollector.waitForResponses; NEW requests " +
           "appeared during page timeout!");
-
         // New requests executed, let's wait again.
         return this.waitForResponses();
       });
+      return this.pageLoadDeferred;
     });
   },
 
   // Page Loaded Timeout
 
   /**
    * The page is loaded when there are no new requests within given period
    * of time. The time is set in preferences:
@@ -107,20 +108,21 @@ HarCollector.prototype = {
     // The auto-export is not done if the timeout is set to zero (or less).
     // This is useful in cases where the export is done manually through
     // API exposed to the content.
     let timeout = Services.prefs.getIntPref(
       "devtools.netmonitor.har.pageLoadedTimeout");
 
     trace.log("HarCollector.waitForTimeout; " + timeout);
 
-    return new Promise((resolve) => {
+    return new Promise((resolve, reject) => {
       if (timeout <= 0) {
         resolve();
       }
+      this.pageLoadReject = reject;
       this.pageLoadTimeout = setTimeout(() => {
         trace.log("HarCollector.onPageLoadTimeout;");
         resolve();
       }, timeout);
     });
   },
 
   resetPageLoadTimeout: function () {
@@ -128,19 +130,19 @@ HarCollector.prototype = {
     if (this.pageLoadTimeout) {
       trace.log("HarCollector.resetPageLoadTimeout;");
 
       clearTimeout(this.pageLoadTimeout);
       this.pageLoadTimeout = null;
     }
 
     // Reject the current page load promise
-    if (this.pageLoadDeferred) {
-      this.pageLoadDeferred.reject();
-      this.pageLoadDeferred = null;
+    if (this.pageLoadReject) {
+      this.pageLoadReject();
+      this.pageLoadReject = null;
     }
   },
 
   // Collected Data
 
   getFile: function (actorId) {
     return this.files.get(actorId);
   },
@@ -435,14 +437,15 @@ function waitForAll(promises) {
 
   // Wait for all promises in the given array.
   return Promise.all(clone).then(() => {
     // If there are new promises (in the original array)
     // to wait for - chain them!
     if (promises.length) {
       return waitForAll(promises);
     }
+
     return undefined;
   });
 }
 
 // Exports from this module
 exports.HarCollector = HarCollector;
--- a/devtools/client/netmonitor/src/har/toolbox-overlay.js
+++ b/devtools/client/netmonitor/src/har/toolbox-overlay.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 
-loader.lazyRequireGetter(this, "HarAutomation", "devtools/client/netmonitor/har/har-automation", true);
+loader.lazyRequireGetter(this, "HarAutomation", "devtools/client/netmonitor/src/har/har-automation", true);
 
 // Map of all created overlays. There is always one instance of
 // an overlay per Toolbox instance (i.e. one per browser tab).
 const overlays = new WeakMap();
 
 /**
  * This object is responsible for initialization and cleanup for HAR
  * export feature. It represents an overlay for the Toolbox
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -63,19 +63,16 @@ pref("devtools.inspector.showAllAnonymou
 // Enable the MDN docs tooltip
 pref("devtools.inspector.mdnDocsTooltip.enabled", false);
 // Enable the new color widget
 pref("devtools.inspector.colorWidget.enabled", false);
 
 // Enable the Font Inspector
 pref("devtools.fontinspector.enabled", true);
 
-// Enable the Layout View
-pref("devtools.layoutview.enabled", false);
-
 // Counter to promote the inspector layout view.
 // @remove after release 56 (See Bug 1355747)
 pref("devtools.promote.layoutview", 1);
 // Whether or not to show the promote bar in the layout view
 // @remove after release 56 (See Bug 1355747)
 pref("devtools.promote.layoutview.showPromoteBar", true);
 
 // Grid highlighter preferences
--- a/devtools/client/shared/widgets/TableWidget.js
+++ b/devtools/client/shared/widgets/TableWidget.js
@@ -138,17 +138,22 @@ TableWidget.prototype = {
     return null;
   },
 
   /**
    * Select the row corresponding to the json object `id`
    */
   set selectedRow(id) {
     for (let column of this.columns.values()) {
-      column.selectRow(id[this.uniqueId] || id);
+      if (id) {
+        column.selectRow(id[this.uniqueId] || id);
+      } else {
+        column.selectedRow = null;
+        column.selectRow(null);
+      }
     }
   },
 
 /**
  * Is a row currently selected?
  *
  * @return {Boolean}
  *         true or false.
@@ -848,17 +853,18 @@ TableWidget.prototype = {
 
     if (!removed) {
       return;
     }
     for (let column of this.columns.values()) {
       column.remove(item);
       column.updateZebra();
     }
-    if (this.items.size == 0) {
+    if (this.items.size === 0) {
+      this.selectedRow = null;
       this.tbody.setAttribute("empty", "empty");
     }
 
     this.emit(EVENTS.ROW_REMOVED, item);
   },
 
   /**
    * Updates the items in the row corresponding to the `item` object previously
@@ -891,16 +897,18 @@ TableWidget.prototype = {
   clear: function () {
     this.items.clear();
     for (let column of this.columns.values()) {
       column.clear();
     }
     this.tbody.setAttribute("empty", "empty");
     this.setPlaceholderText(this.emptyText);
 
+    this.selectedRow = null;
+
     this.emit(EVENTS.TABLE_CLEARED, this);
   },
 
   /**
    * Sorts the table by a given column.
    *
    * @param {string} column
    *        The id of the column on which the table should be sorted.
@@ -1242,25 +1250,26 @@ Column.prototype = {
     this.selectedRow = null;
   },
 
   /**
    * Selects the row at the `index` index
    */
   selectRowAt: function (index) {
     if (this.selectedRow != null) {
-      this.cells[this.items[this.selectedRow]].toggleClass("theme-selected");
+      this.cells[this.items[this.selectedRow]].classList.remove("theme-selected");
     }
-    if (index < 0) {
+
+    let cell = this.cells[index];
+    if (cell) {
+      cell.classList.add("theme-selected");
+      this.selectedRow = cell.id;
+    } else {
       this.selectedRow = null;
-      return;
     }
-    let cell = this.cells[index];
-    cell.toggleClass("theme-selected");
-    this.selectedRow = cell.id;
   },
 
   /**
    * Selects the row with the object having the `uniqueId` value as `id`
    */
   selectRow: function (id) {
     this._updateItems();
     this.selectRowAt(this.items[id]);
@@ -1414,17 +1423,16 @@ Column.prototype = {
 
   /**
    * Clears the current column
    */
   clear: function () {
     this.cells = [];
     this.items = {};
     this._itemsDirty = false;
-    this.selectedRow = null;
     while (this.header.nextSibling) {
       this.header.nextSibling.remove();
     }
   },
 
   /**
    * Sorts the given items and returns the sorted list if the table was sorted
    * by this column.
@@ -1445,41 +1453,43 @@ Column.prototype = {
             a[this.id].textContent : a[this.id];
         let val2 = (b[this.id] instanceof Node) ?
             b[this.id].textContent : b[this.id];
         return naturalSortCaseInsensitive(val2, val1);
       });
     }
 
     if (this.selectedRow) {
-      this.cells[this.items[this.selectedRow]].toggleClass("theme-selected");
+      this.cells[this.items[this.selectedRow]].classList.remove("theme-selected");
     }
     this.items = {};
     // Otherwise, just use the sorted array passed to update the cells value.
     items.forEach((item, i) => {
       this.items[item[this.uniqueId]] = i;
       this.cells[i].value = item[this.id];
       this.cells[i].id = item[this.uniqueId];
     });
     if (this.selectedRow) {
-      this.cells[this.items[this.selectedRow]].toggleClass("theme-selected");
+      this.cells[this.items[this.selectedRow]].classList.add("theme-selected");
     }
     this._itemsDirty = false;
     this.updateZebra();
     return items;
   },
 
   updateZebra() {
     this._updateItems();
     let i = 0;
     for (let cell of this.cells) {
       if (!cell.hidden) {
         i++;
       }
-      cell.toggleClass("even", !(i % 2));
+
+      let even = !(i % 2);
+      cell.classList.toggle("even", even);
     }
   },
 
   /**
    * Click event handler for the column. Used to detect click on header for
    * for sorting.
    */
   onClick: function (event) {
@@ -1605,18 +1615,18 @@ Cell.prototype = {
       this.label.setAttribute("value", value + "");
     }
   },
 
   get value() {
     return this._value;
   },
 
-  toggleClass: function (className, condition) {
-    this.label.classList.toggle(className, condition);
+  get classList() {
+    return this.label.classList;
   },
 
   /**
    * Flashes the cell for a brief time. This when done for with cells in all
    * columns, makes it look like the row is being highlighted/flashed.
    */
   flash: function () {
     if (!this.label.parentNode) {
--- a/devtools/client/storage/test/browser_storage_sidebar_update.js
+++ b/devtools/client/storage/test/browser_storage_sidebar_update.js
@@ -21,17 +21,17 @@ add_task(function* () {
 
   is(gUI.sidebar.hidden, false, "sidebar is visible");
 
   // do several updates in a row and wait for them to finish
   let updates = [];
   for (let i = 0; i < UPDATE_COUNT; i++) {
     info(`Performing update #${i}`);
     updates.push(gUI.once("sidebar-updated"));
-    gUI.displayObjectSidebar();
+    gUI.updateObjectSidebar();
   }
   yield promise.all(updates);
 
   info("Updates performed, going to verify result");
   let parsedScope = gUI.view.getScopeAtIndex(1);
   let elements = parsedScope.target.querySelectorAll(
     `.name[value="${ITEM_NAME}"]`);
   is(elements.length, 1,
--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -108,18 +108,18 @@ function StorageUI(front, target, panelW
 
   let tableNode = this._panelDoc.getElementById("storage-table");
   this.table = new TableWidget(tableNode, {
     emptyText: L10N.getStr("table.emptyText"),
     highlightUpdated: true,
     cellContextMenuId: "storage-table-popup"
   });
 
-  this.displayObjectSidebar = this.displayObjectSidebar.bind(this);
-  this.table.on(TableWidget.EVENTS.ROW_SELECTED, this.displayObjectSidebar);
+  this.updateObjectSidebar = this.updateObjectSidebar.bind(this);
+  this.table.on(TableWidget.EVENTS.ROW_SELECTED, this.updateObjectSidebar);
 
   this.handleScrollEnd = this.handleScrollEnd.bind(this);
   this.table.on(TableWidget.EVENTS.SCROLL_END, this.handleScrollEnd);
 
   this.editItem = this.editItem.bind(this);
   this.table.on(TableWidget.EVENTS.CELL_EDIT, this.editItem);
 
   this.sidebar = this._panelDoc.getElementById("storage-sidebar");
@@ -211,17 +211,17 @@ StorageUI.prototype = {
   sidebarToggledOpen: null,
   shouldLoadMoreItems: true,
 
   set animationsEnabled(value) {
     this._panelDoc.documentElement.classList.toggle("no-animate", !value);
   },
 
   destroy: function () {
-    this.table.off(TableWidget.EVENTS.ROW_SELECTED, this.displayObjectSidebar);
+    this.table.off(TableWidget.EVENTS.ROW_SELECTED, this.updateObjectSidebar);
     this.table.off(TableWidget.EVENTS.SCROLL_END, this.handleScrollEnd);
     this.table.off(TableWidget.EVENTS.CELL_EDIT, this.editItem);
     this.table.destroy();
 
     this.front.off("stores-update", this.onUpdate);
     this.front.off("stores-cleared", this.onCleared);
     this._panelDoc.removeEventListener("keypress", this.handleKeypress);
     this.searchBox.removeEventListener("input", this.filterItems);
@@ -314,27 +314,26 @@ StorageUI.prototype = {
   },
 
   /**
    * Removes the given item from the storage table. Reselects the next item in
    * the table and repopulates the sidebar with that item's data if the item
    * being removed was selected.
    */
   removeItemFromTable: function (name) {
-    if (this.table.isSelected(name)) {
+    if (this.table.isSelected(name) && this.table.items.size > 1) {
       if (this.table.selectedIndex == 0) {
         this.table.selectNextRow();
       } else {
         this.table.selectPreviousRow();
       }
-      this.table.remove(name);
-      this.displayObjectSidebar();
-    } else {
-      this.table.remove(name);
     }
+
+    this.table.remove(name);
+    this.updateObjectSidebar();
   },
 
   /**
    * Event handler for "stores-cleared" event coming from the storage actor.
    *
    * @param {object} response
    *        An object containing which storage types were cleared
    */
@@ -661,29 +660,33 @@ StorageUI.prototype = {
       }
     }
   },
 
   /**
    * Populates the selected entry from the table in the sidebar for a more
    * detailed view.
    */
-  displayObjectSidebar: Task.async(function* () {
+  updateObjectSidebar: Task.async(function* () {
     let item = this.table.selectedRow;
-    if (!item) {
-      // Make sure that sidebar is hidden and return
-      this.sidebar.hidden = true;
-      this.updateSidebarToggleButton();
-      return;
+    let value;
+
+    // Get the string value (async action) and the update the UI synchronously.
+    if (item && item.name && item.valueActor) {
+      value = yield item.valueActor.string();
     }
 
-    // Get the string value (async action) and the update the UI synchronously.
-    let value;
-    if (item.name && item.valueActor) {
-      value = yield item.valueActor.string();
+    // Bail if the selectedRow is no longer selected, the item doesn't exist or the state
+    // changed in another way during the above yield.
+    if (this.table.items.size === 0 ||
+        !item ||
+        !this.table.selectedRow ||
+        item.uniqueKey !== this.table.selectedRow.uniqueKey) {
+      this.hideSidebar();
+      return;
     }
 
     // Start updating the UI. Everything is sync beyond this point.
     if (this.sidebarToggledOpen === null || this.sidebarToggledOpen === true) {
       this.sidebar.hidden = false;
     }
 
     this.updateSidebarToggleButton();
@@ -974,17 +977,17 @@ StorageUI.prototype = {
         case REASON.NEW_ROW:
         case REASON.NEXT_50_ITEMS:
           // Update and flash the row.
           this.table.push(item, false);
           break;
         case REASON.UPDATE:
           this.table.update(item);
           if (item == this.table.selectedRow && !this.sidebar.hidden) {
-            this.displayObjectSidebar();
+            this.updateObjectSidebar();
           }
           break;
       }
 
       this.shouldLoadMoreItems = true;
     }
   },
 
--- a/devtools/shared/webconsole/client.js
+++ b/devtools/shared/webconsole/client.js
@@ -3,18 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const EventEmitter = require("devtools/shared/event-emitter");
-const promise = require("promise");
-const defer = require("devtools/shared/defer");
 const {LongStringClient} = require("devtools/shared/client/main");
 
 /**
  * A WebConsoleClient is used as a front end for the WebConsoleActor that is
  * created on the server, hiding implementation details.
  *
  * @param object debuggerClient
  *        The DebuggerClient instance we live for.
@@ -681,36 +679,33 @@ WebConsoleClient.prototype = {
    *        If you pass in a plain string (by accident or because you're lazy),
    *        then a promise of the same string is simply returned.
    * @return object Promise
    *         A promise that is resolved when the full string contents
    *         are available, or rejected if something goes wrong.
    */
   getString: function (stringGrip) {
     // Make sure this is a long string.
-    if (typeof stringGrip != "object" || stringGrip.type != "longString") {
+    if (typeof stringGrip !== "object" || stringGrip.type !== "longString") {
       // Go home string, you're drunk.
-      return promise.resolve(stringGrip);
+      return Promise.resolve(stringGrip);
     }
 
     // Fetch the long string only once.
     if (stringGrip._fullText) {
-      return stringGrip._fullText.promise;
+      return stringGrip._fullText;
     }
 
-    let deferred = stringGrip._fullText = defer();
-    let { initial, length } = stringGrip;
-    let longStringClient = this.longString(stringGrip);
+    return new Promise((resolve, reject) => {
+      let { initial, length } = stringGrip;
+      let longStringClient = this.longString(stringGrip);
 
-    longStringClient.substring(initial.length, length, response => {
-      if (response.error) {
-        DevToolsUtils.reportException("getString",
-            response.error + ": " + response.message);
-
-        deferred.reject(response);
-        return;
-      }
-      deferred.resolve(initial + response.substring);
+      longStringClient.substring(initial.length, length, response => {
+        if (response.error) {
+          DevToolsUtils.reportException("getString",
+              response.error + ": " + response.message);
+          reject(response);
+        }
+        resolve(initial + response.substring);
+      });
     });
-
-    return deferred.promise;
   }
 };
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -344,17 +344,17 @@ KeyframeEffectReadOnly::DoUpdateProperti
 
   for (const AnimationProperty& property : mProperties) {
     if (property.mIsRunningOnCompositor) {
       runningOnCompositorProperties.AddProperty(property.mProperty);
     }
   }
 
   mProperties = Move(properties);
-  UpadateEffectSet();
+  UpdateEffectSet();
 
   for (AnimationProperty& property : mProperties) {
     property.mIsRunningOnCompositor =
       runningOnCompositorProperties.HasProperty(property.mProperty);
   }
 
   CalculateCumulativeChangeHint(aStyle);
 
@@ -956,17 +956,17 @@ KeyframeEffectReadOnly::UpdateTargetRegi
   // Animation::IsRelevant() should be up-to-date by the time we get here.
   MOZ_ASSERT(isRelevant == IsCurrent() || IsInEffect(),
              "Out of date Animation::IsRelevant value");
 
   if (isRelevant) {
     EffectSet* effectSet =
       EffectSet::GetOrCreateEffectSet(mTarget->mElement, mTarget->mPseudoType);
     effectSet->AddEffect(*this);
-    UpadateEffectSet(effectSet);
+    UpdateEffectSet(effectSet);
   } else {
     UnregisterTarget();
   }
 }
 
 void
 KeyframeEffectReadOnly::UnregisterTarget()
 {
@@ -1827,17 +1827,17 @@ KeyframeEffectReadOnly::ContainsAnimated
       }
     }
   }
 
   return false;
 }
 
 void
-KeyframeEffectReadOnly::UpadateEffectSet(EffectSet* aEffectSet) const
+KeyframeEffectReadOnly::UpdateEffectSet(EffectSet* aEffectSet) const
 {
   EffectSet* effectSet =
     aEffectSet ? aEffectSet
                : EffectSet::GetEffectSet(mTarget->mElement,
                                          mTarget->mPseudoType);
   if (!effectSet) {
     return;
   }
--- a/dom/animation/KeyframeEffectReadOnly.h
+++ b/dom/animation/KeyframeEffectReadOnly.h
@@ -459,17 +459,17 @@ private:
   // limitation is stored in |aOutPerformanceWarning|.
   static bool CanAnimateTransformOnCompositor(
     const nsIFrame* aFrame,
     AnimationPerformanceWarning::Type& aPerformanceWarning);
   static bool IsGeometricProperty(const nsCSSPropertyID aProperty);
 
   static const TimeDuration OverflowRegionRefreshInterval();
 
-  void UpadateEffectSet(mozilla::EffectSet* aEffectSet = nullptr) const;
+  void UpdateEffectSet(mozilla::EffectSet* aEffectSet = nullptr) const;
 
   // FIXME: This flag will be removed in bug 1324966.
   bool mIsComposingStyle = false;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/base/ChildIterator.cpp
+++ b/dom/base/ChildIterator.cpp
@@ -365,45 +365,18 @@ AllChildrenIterator::Seek(nsIContent* aC
   } while (child && child != aChildToFind);
 
   return child == aChildToFind;
 }
 
 void
 AllChildrenIterator::AppendNativeAnonymousChildren()
 {
-  if (nsIFrame* primaryFrame = mOriginalContent->GetPrimaryFrame()) {
-    // NAC created by the element's primary frame.
-    AppendNativeAnonymousChildrenFromFrame(primaryFrame);
-
-    // NAC created by any other non-primary frames for the element.
-    AutoTArray<nsIFrame::OwnedAnonBox,8> ownedAnonBoxes;
-    primaryFrame->AppendOwnedAnonBoxes(ownedAnonBoxes);
-    for (nsIFrame::OwnedAnonBox& box : ownedAnonBoxes) {
-      MOZ_ASSERT(box.mAnonBoxFrame->GetContent() == mOriginalContent);
-      AppendNativeAnonymousChildrenFromFrame(box.mAnonBoxFrame);
-    }
-  }
-
-  // The root scroll frame is not the primary frame of the root element.
-  // Detect and handle this case.
-  if (!(mFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
-      mOriginalContent == mOriginalContent->OwnerDoc()->GetRootElement()) {
-    nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
-        mOriginalContent->OwnerDoc(), mAnonKids);
-  }
-}
-
-void
-AllChildrenIterator::AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame)
-{
-  nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame);
-  if (ac) {
-    ac->AppendAnonymousContentTo(mAnonKids, mFlags);
-  }
+  nsContentUtils::AppendNativeAnonymousChildren(
+      mOriginalContent, mAnonKids, mFlags);
 }
 
 nsIContent*
 AllChildrenIterator::GetNextChild()
 {
   if (mPhase == eAtBegin) {
     mPhase = eAtExplicitKids;
     Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
@@ -537,17 +510,16 @@ StyleChildrenIterator::IsNeeded(const El
   nsIAnonymousContentCreator* ac = do_QueryFrame(aElement->GetPrimaryFrame());
   if (ac) {
     return true;
   }
 
   return false;
 }
 
-
 nsIContent*
 StyleChildrenIterator::GetNextChild()
 {
   return AllChildrenIterator::GetNextChild();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/ChildIterator.h
+++ b/dom/base/ChildIterator.h
@@ -226,18 +226,16 @@ public:
     eAtAfterKid,
     eAtEnd
   };
   IteratorPhase Phase() const { return mPhase; }
 
 private:
   // Helpers.
   void AppendNativeAnonymousChildren();
-  void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame);
-
   const nsIContent* mOriginalContent;
 
   // mAnonKids is an array of native anonymous children, mAnonKidsIdx is index
   // in the array. If mAnonKidsIdx < mAnonKids.Length() and mPhase is
   // eAtAnonKids then the iterator points at a child at mAnonKidsIdx index. If
   // mAnonKidsIdx == mAnonKids.Length() then the iterator is somewhere after
   // the last native anon child. If mAnonKidsIdx == UINT32_MAX then the iterator
   // is somewhere before the first native anon child.
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -1810,25 +1810,26 @@ nsresult                                
   nsresult rv = const_cast<_elementName*>(this)->CopyInnerTo(it, aPreallocateChildren); \
   if (NS_SUCCEEDED(rv)) {                                                   \
     kungFuDeathGrip.swap(*aResult);                                         \
   }                                                                         \
                                                                             \
   return rv;                                                                \
 }
 
-#define NS_IMPL_ELEMENT_CLONE_WITH_INIT(_elementName)                       \
+#define EXPAND(...) __VA_ARGS__
+#define NS_IMPL_ELEMENT_CLONE_WITH_INIT_HELPER(_elementName, extra_args_)   \
 nsresult                                                                    \
 _elementName::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,   \
                     bool aPreallocateChildren) const                        \
 {                                                                           \
   *aResult = nullptr;                                                       \
   already_AddRefed<mozilla::dom::NodeInfo> ni =                             \
-    RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget();                   \
-  _elementName *it = new _elementName(ni);                                  \
+    RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget();                     \
+  _elementName *it = new _elementName(ni EXPAND extra_args_);               \
   if (!it) {                                                                \
     return NS_ERROR_OUT_OF_MEMORY;                                          \
   }                                                                         \
                                                                             \
   nsCOMPtr<nsINode> kungFuDeathGrip = it;                                   \
   nsresult rv = it->Init();                                                 \
   nsresult rv2 = const_cast<_elementName*>(this)->CopyInnerTo(it, aPreallocateChildren); \
   if (NS_FAILED(rv2)) {                                                     \
@@ -1836,16 +1837,21 @@ nsresult                                
   }                                                                         \
   if (NS_SUCCEEDED(rv)) {                                                   \
     kungFuDeathGrip.swap(*aResult);                                         \
   }                                                                         \
                                                                             \
   return rv;                                                                \
 }
 
+#define NS_IMPL_ELEMENT_CLONE_WITH_INIT(_elementName) \
+  NS_IMPL_ELEMENT_CLONE_WITH_INIT_HELPER(_elementName, ())
+#define NS_IMPL_ELEMENT_CLONE_WITH_INIT_AND_PARSER(_elementName) \
+  NS_IMPL_ELEMENT_CLONE_WITH_INIT_HELPER(_elementName, (, NOT_FROM_PARSER))
+
 /**
  * A macro to implement the getter and setter for a given string
  * valued content property. The method uses the generic GetAttr and
  * SetAttr methods.  We use the 5-argument form of SetAttr, because
  * some consumers only implement that one, hiding superclass
  * 4-argument forms.
  */
 #define NS_IMPL_STRING_ATTR(_class, _method, _atom)                     \
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -56,16 +56,17 @@
 #include "mozilla/StaticPtr.h"
 #include "Connection.h"
 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
 #include "nsGlobalWindow.h"
 #include "nsIIdleObserver.h"
 #include "nsIPermissionManager.h"
 #include "nsMimeTypes.h"
 #include "nsNetUtil.h"
+#include "nsRFPService.h"
 #include "nsStringStream.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIStringStream.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "TimeManager.h"
 #include "nsStreamUtils.h"
 #include "WidgetUtils.h"
@@ -460,16 +461,23 @@ Navigator::GetPlatform(nsAString& aPlatf
   }
 }
 
 void
 Navigator::GetOscpu(nsAString& aOSCPU, CallerType aCallerType,
                     ErrorResult& aRv) const
 {
   if (aCallerType != CallerType::System) {
+    // If fingerprinting resistance is on, we will spoof this value. See nsRFPService.h
+    // for details about spoofed values.
+    if (nsContentUtils::ShouldResistFingerprinting()) {
+      aOSCPU.AssignLiteral(SPOOFED_OSCPU);
+      return;
+    }
+
     const nsAdoptingString& override =
       Preferences::GetString("general.oscpu.override");
 
     if (override) {
       aOSCPU = override;
       return;
     }
   }
@@ -512,17 +520,17 @@ Navigator::GetProduct(nsAString& aProduc
   aProduct.AssignLiteral("Gecko");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Navigator::GetProductSub(nsAString& aProductSub)
 {
   // Legacy build ID hardcoded for backward compatibility (bug 776376)
-  aProductSub.AssignLiteral("20100101");
+  aProductSub.AssignLiteral(LEGACY_BUILD_ID);
   return NS_OK;
 }
 
 nsMimeTypeArray*
 Navigator::GetMimeTypes(ErrorResult& aRv)
 {
   if (!mMimeTypes) {
     if (!mWindow) {
@@ -634,16 +642,22 @@ Navigator::OnLine()
   return !NS_IsOffline();
 }
 
 void
 Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType,
                       ErrorResult& aRv) const
 {
   if (aCallerType != CallerType::System) {
+    // If fingerprinting resistance is on, we will spoof this value. See nsRFPService.h
+    // for details about spoofed values.
+    if (nsContentUtils::ShouldResistFingerprinting()) {
+      aBuildID.AssignLiteral(LEGACY_BUILD_ID);
+      return;
+    }
     const nsAdoptingString& override =
       Preferences::GetString("general.buildID.override");
 
     if (override) {
       aBuildID = override;
       return;
     }
   }
@@ -1764,16 +1778,22 @@ Navigator::GetWindowFromGlobal(JSObject*
 }
 
 nsresult
 Navigator::GetPlatform(nsAString& aPlatform, bool aUsePrefOverriddenValue)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aUsePrefOverriddenValue) {
+    // If fingerprinting resistance is on, we will spoof this value. See nsRFPService.h
+    // for details about spoofed values.
+    if (nsContentUtils::ShouldResistFingerprinting()) {
+      aPlatform.AssignLiteral(SPOOFED_PLATFORM);
+      return NS_OK;
+    }
     const nsAdoptingString& override =
       mozilla::Preferences::GetString("general.platform.override");
 
     if (override) {
       aPlatform = override;
       return NS_OK;
     }
   }
@@ -1809,16 +1829,22 @@ Navigator::GetPlatform(nsAString& aPlatf
 }
 
 /* static */ nsresult
 Navigator::GetAppVersion(nsAString& aAppVersion, bool aUsePrefOverriddenValue)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aUsePrefOverriddenValue) {
+    // If fingerprinting resistance is on, we will spoof this value. See nsRFPService.h
+    // for details about spoofed values.
+    if (nsContentUtils::ShouldResistFingerprinting()) {
+      aAppVersion.AssignLiteral(SPOOFED_APPVERSION);
+      return NS_OK;
+    }
     const nsAdoptingString& override =
       mozilla::Preferences::GetString("general.appversion.override");
 
     if (override) {
       aAppVersion = override;
       return NS_OK;
     }
   }
@@ -1846,16 +1872,23 @@ Navigator::GetAppVersion(nsAString& aApp
 }
 
 /* static */ void
 Navigator::AppName(nsAString& aAppName, bool aUsePrefOverriddenValue)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aUsePrefOverriddenValue) {
+    // If fingerprinting resistance is on, we will spoof this value. See nsRFPService.h
+    // for details about spoofed values.
+    if (nsContentUtils::ShouldResistFingerprinting()) {
+      aAppName.AssignLiteral(SPOOFED_APPNAME);
+      return;
+    }
+
     const nsAdoptingString& override =
       mozilla::Preferences::GetString("general.appname.override");
 
     if (override) {
       aAppName = override;
       return;
     }
   }
@@ -1871,17 +1904,20 @@ Navigator::ClearUserAgentCache()
 
 nsresult
 Navigator::GetUserAgent(nsPIDOMWindowInner* aWindow,
                         bool aIsCallerChrome,
                         nsAString& aUserAgent)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (!aIsCallerChrome) {
+  // We will skip the override and pass to httpHandler to get spoofed userAgent
+  // when 'privacy.resistFingerprinting' is true.
+  if (!aIsCallerChrome &&
+      !nsContentUtils::ShouldResistFingerprinting()) {
     const nsAdoptingString& override =
       mozilla::Preferences::GetString("general.useragent.override");
 
     if (override) {
       aUserAgent = override;
       return NS_OK;
     }
   }
@@ -1896,17 +1932,21 @@ Navigator::GetUserAgent(nsPIDOMWindowInn
   nsAutoCString ua;
   rv = service->GetUserAgent(ua);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   CopyASCIItoUTF16(ua, aUserAgent);
 
-  if (!aWindow) {
+  // When the caller is content, we will always return spoofed userAgent and
+  // ignore the User-Agent header from the document channel when
+  // 'privacy.resistFingerprinting' is true.
+  if (!aWindow ||
+      (nsContentUtils::ShouldResistFingerprinting() && !aIsCallerChrome)) {
     return NS_OK;
   }
 
   // Copy the User-Agent header from the document channel which has already been
   // subject to UA overrides.
   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
   if (!doc) {
     return NS_OK;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -10238,16 +10238,55 @@ nsContentUtils::AppendDocumentLevelNativ
     if (nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame()) {
       if (Element* container = canvasFrame->GetCustomContentContainer()) {
         aElements.AppendElement(container);
       }
     }
   }
 }
 
+static void
+AppendNativeAnonymousChildrenFromFrame(
+    nsIFrame* aFrame,
+    nsTArray<nsIContent*>& aKids,
+    uint32_t aFlags)
+{
+  if (nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame)) {
+    ac->AppendAnonymousContentTo(aKids, aFlags);
+  }
+}
+
+/* static */ void
+nsContentUtils::AppendNativeAnonymousChildren(
+    const nsIContent* aContent,
+    nsTArray<nsIContent*>& aKids,
+    uint32_t aFlags)
+{
+  if (nsIFrame* primaryFrame = aContent->GetPrimaryFrame()) {
+    // NAC created by the element's primary frame.
+    AppendNativeAnonymousChildrenFromFrame(primaryFrame, aKids, aFlags);
+
+    // NAC created by any other non-primary frames for the element.
+    AutoTArray<nsIFrame::OwnedAnonBox, 8> ownedAnonBoxes;
+    primaryFrame->AppendOwnedAnonBoxes(ownedAnonBoxes);
+    for (nsIFrame::OwnedAnonBox& box : ownedAnonBoxes) {
+      MOZ_ASSERT(box.mAnonBoxFrame->GetContent() == aContent);
+      AppendNativeAnonymousChildrenFromFrame(box.mAnonBoxFrame, aKids, aFlags);
+    }
+  }
+
+  // The root scroll frame is not the primary frame of the root element.
+  // Detect and handle this case.
+  if (!(aFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
+      aContent == aContent->OwnerDoc()->GetRootElement()) {
+    AppendDocumentLevelNativeAnonymousContentTo(aContent->OwnerDoc(), aKids);
+  }
+}
+
+
 /* static */ void
 nsContentUtils::GetContentPolicyTypeForUIImageLoading(nsIContent* aLoadingNode,
                                                       nsIPrincipal** aLoadingPrincipal,
                                                       nsContentPolicyType& aContentPolicyType)
 {
   // Use the serialized loadingPrincipal from the image element. Fall back
   // to mContent's principal (SystemPrincipal) if not available.
   aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2927,16 +2927,26 @@ public:
    * by ancestor frames of the document element's primary frame, such as
    * the scrollbar elements created by the root scroll frame.
    */
   static void AppendDocumentLevelNativeAnonymousContentTo(
       nsIDocument* aDocument,
       nsTArray<nsIContent*>& aElements);
 
   /**
+   * Appends all native anonymous content subtree roots generated by `aContent`
+   * to `aKids`.
+   *
+   * See `AllChildrenIterator` for the description of the `aFlags` parameter.
+   */
+  static void AppendNativeAnonymousChildren(const nsIContent* aContent,
+                                            nsTArray<nsIContent*>& aKids,
+                                            uint32_t aFlags);
+
+  /**
    * Returns the content policy type that should be used for loading images
    * for displaying in the UI.  The sources of such images can be <xul:image>,
    * <xul:menuitem> on OSX where we load the image through nsMenuItemIconX, etc.
    */
   static void
   GetContentPolicyTypeForUIImageLoading(nsIContent* aLoadingNode,
                                         nsIPrincipal** aLoadingPrincipal,
                                         nsContentPolicyType& aContentPolicyType);
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -239,16 +239,24 @@ public:
     NS_ASSERTION(!IsInNativeAnonymousSubtree() || GetBindingParent() ||
                  (!IsInUncomposedDoc() &&
                   static_cast<nsIContent*>(SubtreeRoot())->IsInNativeAnonymousSubtree()),
                  "Must have binding parent when in native anonymous subtree which is in document.\n"
                  "Native anonymous subtree which is not in document must have native anonymous root.");
     return IsInNativeAnonymousSubtree() || (!IsInShadowTree() && GetBindingParent() != nullptr);
   }
 
+  /*
+   * Return true if this node is the shadow root of an use-element shadow tree.
+   */
+  bool IsRootOfUseElementShadowTree() const {
+    return GetParent() && GetParent()->IsSVGElement(nsGkAtoms::use) &&
+           IsRootOfAnonymousSubtree();
+  }
+
   /**
    * Return true iff this node is in an HTML document (in the HTML5 sense of
    * the term, i.e. not in an XHTML/XML document).
    */
   inline bool IsInHTMLDocument() const;
 
 
   /**
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -82,16 +82,17 @@
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/ContentEvents.h"
 
 #include "nsCycleCollectionNoteRootCallback.h"
 #include "GeckoProfiler.h"
+#include "mozilla/IdleTaskRunner.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 const size_t gStackSize = 8192;
 
 // Thank you Microsoft!
 #ifdef CompareString
@@ -115,17 +116,17 @@ const size_t gStackSize = 8192;
 
 // The amount of time we wait between a request to CC (after GC ran)
 // and doing the actual CC.
 #define NS_CC_DELAY                 6000 // ms
 
 #define NS_CC_SKIPPABLE_DELAY       250 // ms
 
 // ForgetSkippable is usually fast, so we can use small budgets.
-// This isn't a real budget but a hint to CollectorRunner whether there
+// This isn't a real budget but a hint to IdleTaskRunner whether there
 // is enough time to call ForgetSkippable.
 static const int64_t kForgetSkippableSliceDuration = 2;
 
 // Maximum amount of time that should elapse between incremental CC slices
 static const int64_t kICCIntersliceDelay = 32; // ms
 
 // Time budget for an incremental CC slice when using timer to run it.
 static const int64_t kICCSliceBudget = 5; // ms
@@ -144,26 +145,24 @@ static const uint32_t kMaxICCDuration = 
 #define NS_MAX_CC_LOCKEDOUT_TIME    (30 * PR_USEC_PER_SEC) // 30 seconds
 
 // Trigger a CC if the purple buffer exceeds this size when we check it.
 #define NS_CC_PURPLE_LIMIT          200
 
 // Large value used to specify that a script should run essentially forever
 #define NS_UNLIMITED_SCRIPT_RUNTIME (0x40000000LL << 32)
 
-class CollectorRunner;
-
 // if you add statics here, add them to the list in StartupJSEnvironment
 
 static nsITimer *sGCTimer;
 static nsITimer *sShrinkingGCTimer;
-static StaticRefPtr<CollectorRunner> sCCRunner;
-static StaticRefPtr<CollectorRunner> sICCRunner;
+static StaticRefPtr<IdleTaskRunner> sCCRunner;
+static StaticRefPtr<IdleTaskRunner> sICCRunner;
 static nsITimer *sFullGCTimer;
-static StaticRefPtr<CollectorRunner> sInterSliceGCRunner;
+static StaticRefPtr<IdleTaskRunner> sInterSliceGCRunner;
 
 static TimeStamp sLastCCEndTime;
 
 static bool sCCLockedOut;
 static PRTime sCCLockedOutTime;
 
 static JS::GCSliceCallback sPrevGCSliceCallback;
 
@@ -223,193 +222,16 @@ static bool sIsCompactingOnUserInactive 
 
 // In testing, we call RunNextCollectorTimer() to ensure that the collectors are run more
 // aggressively than they would be in regular browsing. sExpensiveCollectorPokes keeps
 // us from triggering expensive full collections too frequently.
 static int32_t sExpensiveCollectorPokes = 0;
 static const int32_t kPokesBetweenExpensiveCollectorTriggers = 5;
 
 static TimeDuration sGCUnnotifiedTotalTime;
-
-// Return true if some meaningful work was done.
-typedef bool (*CollectorRunnerCallback) (TimeStamp aDeadline, void* aData);
-
-// Repeating callback runner for CC and GC.
-class CollectorRunner final : public IdleRunnable
-{
-public:
-  static already_AddRefed<CollectorRunner>
-  Create(CollectorRunnerCallback aCallback, uint32_t aDelay,
-         int64_t aBudget, bool aRepeating, void* aData = nullptr)
-  {
-    if (sShuttingDown) {
-      return nullptr;
-    }
-
-    RefPtr<CollectorRunner> runner =
-      new CollectorRunner(aCallback, aDelay, aBudget, aRepeating, aData);
-    runner->Schedule(false); // Initial scheduling shouldn't use idle dispatch.
-    return runner.forget();
-  }
-
-  NS_IMETHOD Run() override
-  {
-    if (!mCallback) {
-      return NS_OK;
-    }
-
-    // Deadline is null when called from timer.
-    bool deadLineWasNull = mDeadline.IsNull();
-    bool didRun = false;
-    if (deadLineWasNull || ((TimeStamp::Now() + mBudget) < mDeadline)) {
-      CancelTimer();
-      didRun = mCallback(mDeadline, mData);
-    }
-
-    if (mCallback && (mRepeating || !didRun)) {
-      // If we didn't do meaningful work, don't schedule using immediate
-      // idle dispatch, since that could lead to a loop until the idle
-      // period ends.
-      Schedule(didRun);
-    }
-
-    return NS_OK;
-  }
-
-  static void
-  TimedOut(nsITimer* aTimer, void* aClosure)
-  {
-    RefPtr<CollectorRunner> runnable = static_cast<CollectorRunner*>(aClosure);
-    runnable->Run();
-  }
-
-  void SetDeadline(mozilla::TimeStamp aDeadline) override
-  {
-    mDeadline = aDeadline;
-  };
-
-  void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) override
-  {
-    if (mTimerActive) {
-      return;
-    }
-
-    mTarget = aTarget;
-    if (!mTimer) {
-      mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-    } else {
-      mTimer->Cancel();
-    }
-
-    if (mTimer) {
-      mTimer->SetTarget(mTarget);
-      mTimer->InitWithFuncCallback(TimedOut, this, aDelay,
-                                   nsITimer::TYPE_ONE_SHOT);
-      mTimerActive = true;
-    }
-  }
-
-  nsresult Cancel() override
-  {
-    CancelTimer();
-    mTimer = nullptr;
-    mScheduleTimer = nullptr;
-    mCallback = nullptr;
-    return NS_OK;
-  }
-
-  static void
-  ScheduleTimedOut(nsITimer* aTimer, void* aClosure)
-  {
-    RefPtr<CollectorRunner> runnable = static_cast<CollectorRunner*>(aClosure);
-    runnable->Schedule(true);
-  }
-
-  void Schedule(bool aAllowIdleDispatch)
-  {
-    if (!mCallback) {
-      return;
-    }
-
-    if (sShuttingDown) {
-      Cancel();
-      return;
-    }
-
-    mDeadline = TimeStamp();
-    TimeStamp now = TimeStamp::Now();
-    TimeStamp hint = nsRefreshDriver::GetIdleDeadlineHint(now);
-    if (hint != now) {
-      // RefreshDriver is ticking, let it schedule the idle dispatch.
-      nsRefreshDriver::DispatchIdleRunnableAfterTick(this, mDelay);
-      // Ensure we get called at some point, even if RefreshDriver is stopped.
-      SetTimer(mDelay, mTarget);
-    } else {
-      // RefreshDriver doesn't seem to be running.
-      if (aAllowIdleDispatch) {
-        nsCOMPtr<nsIRunnable> runnable = this;
-        NS_IdleDispatchToCurrentThread(runnable.forget(), mDelay);
-        SetTimer(mDelay, mTarget);
-      } else {
-        if (!mScheduleTimer) {
-          mScheduleTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-          if (!mScheduleTimer) {
-            return;
-          }
-        } else {
-          mScheduleTimer->Cancel();
-        }
-
-        // We weren't allowed to do idle dispatch immediately, do it after a
-        // short timeout.
-        mScheduleTimer->InitWithFuncCallback(ScheduleTimedOut, this, 16,
-                                             nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY);
-      }
-    }
-  }
-
-private:
-  explicit CollectorRunner(CollectorRunnerCallback aCallback,
-                           uint32_t aDelay, int64_t aBudget,
-                           bool aRepeating, void* aData)
-    : mCallback(aCallback), mDelay(aDelay)
-    , mBudget(TimeDuration::FromMilliseconds(aBudget))
-    , mRepeating(aRepeating), mTimerActive(false), mData(aData)
-  {
-  }
-
-  ~CollectorRunner()
-  {
-    CancelTimer();
-  }
-
-  void CancelTimer()
-  {
-    nsRefreshDriver::CancelIdleRunnable(this);
-    if (mTimer) {
-      mTimer->Cancel();
-    }
-    if (mScheduleTimer) {
-      mScheduleTimer->Cancel();
-    }
-    mTimerActive = false;
-  }
-
-  nsCOMPtr<nsITimer> mTimer;
-  nsCOMPtr<nsITimer> mScheduleTimer;
-  nsCOMPtr<nsIEventTarget> mTarget;
-  CollectorRunnerCallback mCallback;
-  uint32_t mDelay;
-  TimeStamp mDeadline;
-  TimeDuration mBudget;
-  bool mRepeating;
-  bool mTimerActive;
-  void* mData;
-};
-
 static const char*
 ProcessNameForCollectorLog()
 {
   return XRE_GetProcessType() == GeckoProcessType_Default ?
     "default" : "content";
 }
 
 namespace xpc {
@@ -1775,17 +1597,17 @@ nsJSContext::ClearMaxCCSliceTime()
 
 uint32_t
 nsJSContext::GetMaxCCSliceTimeSinceClear()
 {
   return gCCStats.mMaxSliceTimeSinceClear;
 }
 
 static bool
-ICCRunnerFired(TimeStamp aDeadline, void* aData)
+ICCRunnerFired(TimeStamp aDeadline)
 {
   if (sDidShutdown) {
     return false;
   }
 
   // Ignore ICC timer fires during IGC. Running ICC during an IGC will cause us
   // to synchronously finish the GC, which is bad.
 
@@ -1816,18 +1638,18 @@ nsJSContext::BeginCycleCollectionCallbac
   KillCCRunner();
 
   gCCStats.RunForgetSkippable();
 
   MOZ_ASSERT(!sICCRunner, "Tried to create a new ICC timer when one already existed.");
 
   // Create an ICC timer even if ICC is globally disabled, because we could be manually triggering
   // an incremental collection, and we want to be sure to finish it.
-  sICCRunner = CollectorRunner::Create(ICCRunnerFired, kICCIntersliceDelay,
-                                       kIdleICCSliceBudget, true);
+  sICCRunner = IdleTaskRunner::Create(ICCRunnerFired, kICCIntersliceDelay,
+                                      kIdleICCSliceBudget, true, []{ return sShuttingDown; });
 }
 
 static_assert(NS_GC_DELAY > kMaxICCDuration, "A max duration ICC shouldn't reduce GC delay to 0");
 
 //static
 void
 nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
 {
@@ -2029,21 +1851,19 @@ InterSliceGCRunnerFired(TimeStamp aDeadl
 }
 
 // static
 void
 GCTimerFired(nsITimer *aTimer, void *aClosure)
 {
   nsJSContext::KillGCTimer();
   // Now start the actual GC after initial timer has fired.
-  sInterSliceGCRunner = CollectorRunner::Create(InterSliceGCRunnerFired,
-                                                NS_INTERSLICE_GC_DELAY,
-                                                sActiveIntersliceGCBudget,
-                                                false,
-                                                aClosure);
+  sInterSliceGCRunner = IdleTaskRunner::Create([aClosure](TimeStamp aDeadline) {
+    return InterSliceGCRunnerFired(aDeadline, aClosure);
+  }, NS_INTERSLICE_GC_DELAY, sActiveIntersliceGCBudget, false, []{ return sShuttingDown; });
 }
 
 // static
 void
 ShrinkingGCTimerFired(nsITimer* aTimer, void* aClosure)
 {
   nsJSContext::KillShrinkingGCTimer();
   sIsCompactingOnUserInactive = true;
@@ -2057,17 +1877,17 @@ ShouldTriggerCC(uint32_t aSuspected)
 {
   return sNeedsFullCC ||
          aSuspected > NS_CC_PURPLE_LIMIT ||
          (aSuspected > NS_CC_FORCED_PURPLE_LIMIT &&
           TimeUntilNow(sLastCCEndTime) > NS_CC_FORCED);
 }
 
 static bool
-CCRunnerFired(TimeStamp aDeadline, void* aData)
+CCRunnerFired(TimeStamp aDeadline)
 {
   if (sDidShutdown) {
     return false;
   }
 
   static uint32_t ccDelay = NS_CC_DELAY;
   if (sCCLockedOut) {
     ccDelay = NS_CC_DELAY / 3;
@@ -2209,23 +2029,23 @@ nsJSContext::RunNextCollectorTimer()
   }
 
   // Check the CC timers after the GC timers, because the CC timers won't do
   // anything if a GC is in progress.
   MOZ_ASSERT(!sCCLockedOut, "Don't check the CC timers if the CC is locked out.");
 
   if (sCCRunner) {
     if (ReadyToTriggerExpensiveCollectorTimer()) {
-      CCRunnerFired(TimeStamp(), nullptr);
+      CCRunnerFired(TimeStamp());
     }
     return;
   }
 
   if (sICCRunner) {
-    ICCRunnerFired(TimeStamp(), nullptr);
+    ICCRunnerFired(TimeStamp());
     return;
   }
 }
 
 // static
 void
 nsJSContext::PokeGC(JS::gcreason::Reason aReason,
                     JSObject* aObj,
@@ -2322,18 +2142,19 @@ nsJSContext::MaybePokeCC()
 
   if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
     sCCRunnerFireCount = 0;
 
     // We can kill some objects before running forgetSkippable.
     nsCycleCollector_dispatchDeferredDeletion();
 
     sCCRunner =
-      CollectorRunner::Create(CCRunnerFired, NS_CC_SKIPPABLE_DELAY,
-                              kForgetSkippableSliceDuration, true);
+      IdleTaskRunner::Create(CCRunnerFired, NS_CC_SKIPPABLE_DELAY,
+                             kForgetSkippableSliceDuration, true,
+                             []{ return sShuttingDown; });
   }
 }
 
 //static
 void
 nsJSContext::KillGCTimer()
 {
   if (sGCTimer) {
@@ -2503,18 +2324,19 @@ DOMGCSliceCallback(JSContext* aCx, JS::G
     case JS::GC_SLICE_END:
       sGCUnnotifiedTotalTime +=
         aDesc.lastSliceEnd(aCx) - aDesc.lastSliceStart(aCx);
 
       // Schedule another GC slice if the GC has more work to do.
       nsJSContext::KillInterSliceGCRunner();
       if (!sShuttingDown && !aDesc.isComplete_) {
         sInterSliceGCRunner =
-          CollectorRunner::Create(InterSliceGCRunnerFired, NS_INTERSLICE_GC_DELAY,
-                                  sActiveIntersliceGCBudget, false);
+          IdleTaskRunner::Create([](TimeStamp aDeadline) {
+            return InterSliceGCRunnerFired(aDeadline, nullptr);
+          }, NS_INTERSLICE_GC_DELAY, sActiveIntersliceGCBudget, false, []{ return sShuttingDown; });
       }
 
       if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
         nsCycleCollector_dispatchDeferredDeletion();
       }
 
       if (sPostGCEventsToConsole) {
         nsString prefix, gcstats;
--- a/dom/cache/Connection.cpp
+++ b/dom/cache/Connection.cpp
@@ -58,16 +58,23 @@ Connection::Close()
 NS_IMETHODIMP
 Connection::AsyncClose(mozIStorageCompletionCallback*)
 {
   // async methods are not supported
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+Connection::SpinningSynchronousClose()
+{
+  // not supported
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 Connection::AsyncClone(bool, mozIStorageCompletionCallback*)
 {
   // async methods are not supported
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 Connection::GetDatabaseFile(nsIFile** aFileOut)
--- a/dom/media/MediaShutdownManager.cpp
+++ b/dom/media/MediaShutdownManager.cpp
@@ -19,32 +19,39 @@ extern LazyLogModule gMediaDecoderLog;
 #define DECODER_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
 #define LOGW(...) NS_WARNING(nsPrintfCString(__VA_ARGS__).get())
 
 NS_IMPL_ISUPPORTS(MediaShutdownManager, nsIAsyncShutdownBlocker)
 
 MediaShutdownManager::MediaShutdownManager()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_DIAGNOSTIC_ASSERT(sInitPhase == NotInited);
 }
 
 MediaShutdownManager::~MediaShutdownManager()
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 // Note that we don't use ClearOnShutdown() on this StaticRefPtr, as that
 // may interfere with our shutdown listener.
 StaticRefPtr<MediaShutdownManager> MediaShutdownManager::sInstance;
 
+MediaShutdownManager::InitPhase MediaShutdownManager::sInitPhase = MediaShutdownManager::NotInited;
+
 MediaShutdownManager&
 MediaShutdownManager::Instance()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_DIAGNOSTIC_ASSERT(sInstance);
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+  if (!sInstance) {
+    MOZ_CRASH_UNSAFE_PRINTF("sInstance is null. sInitPhase=%d", int(sInitPhase));
+  }
+#endif
   return *sInstance;
 }
 
 static nsCOMPtr<nsIAsyncShutdownClient>
 GetShutdownBarrier()
 {
   nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
   MOZ_RELEASE_ASSERT(svc);
@@ -60,54 +67,56 @@ GetShutdownBarrier()
   MOZ_RELEASE_ASSERT(barrier);
   return barrier.forget();
 }
 
 void
 MediaShutdownManager::InitStatics()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  static bool sInitDone = false;
-  if (sInitDone) {
+  if (sInitPhase != NotInited) {
     return;
   }
 
-  sInitDone = true;
   sInstance = new MediaShutdownManager();
+  MOZ_DIAGNOSTIC_ASSERT(sInstance);
 
   nsresult rv = GetShutdownBarrier()->AddBlocker(
     sInstance, NS_LITERAL_STRING(__FILE__), __LINE__,
     NS_LITERAL_STRING("MediaShutdownManager shutdown"));
   if (NS_FAILED(rv)) {
     LOGW("Failed to add shutdown blocker! rv=%x", uint32_t(rv));
-    sInstance->mError = rv;
+    sInitPhase = InitFailed;
+    return;
   }
+  sInitPhase = InitSucceeded;
 }
 
 void
 MediaShutdownManager::RemoveBlocker()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mIsDoingXPCOMShutDown);
+  MOZ_DIAGNOSTIC_ASSERT(sInitPhase == XPCOMShutdownStarted);
   MOZ_ASSERT(mDecoders.Count() == 0);
   GetShutdownBarrier()->RemoveBlocker(this);
   // Clear our singleton reference. This will probably delete
   // this instance, so don't deref |this| clearing sInstance.
+  sInitPhase = XPCOMShutdownEnded;
   sInstance = nullptr;
   DECODER_LOG(LogLevel::Debug, ("MediaShutdownManager::BlockShutdown() end."));
 }
 
 nsresult
 MediaShutdownManager::Register(MediaDecoder* aDecoder)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (NS_FAILED(mError)) {
+  if (sInitPhase == InitFailed) {
     return NS_ERROR_NOT_INITIALIZED;
   }
-  if (mIsDoingXPCOMShutDown) {
+  if (sInitPhase == XPCOMShutdownStarted) {
     return NS_ERROR_ABORT;
   }
   // Don't call Register() after you've Unregistered() all the decoders,
   // that's not going to work.
   MOZ_ASSERT(!mDecoders.Contains(aDecoder));
   mDecoders.PutEntry(aDecoder);
   MOZ_ASSERT(mDecoders.Contains(aDecoder));
   MOZ_ASSERT(mDecoders.Count() > 0);
@@ -117,17 +126,17 @@ MediaShutdownManager::Register(MediaDeco
 void
 MediaShutdownManager::Unregister(MediaDecoder* aDecoder)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mDecoders.Contains(aDecoder)) {
     return;
   }
   mDecoders.RemoveEntry(aDecoder);
-  if (mIsDoingXPCOMShutDown && mDecoders.Count() == 0) {
+  if (sInitPhase == XPCOMShutdownStarted && mDecoders.Count() == 0) {
     RemoveBlocker();
   }
 }
 
 NS_IMETHODIMP
 MediaShutdownManager::GetName(nsAString& aName)
 {
   aName = NS_LITERAL_STRING("MediaShutdownManager: shutdown");
@@ -139,22 +148,23 @@ MediaShutdownManager::GetState(nsIProper
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MediaShutdownManager::BlockShutdown(nsIAsyncShutdownClient*)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(sInstance);
+  MOZ_DIAGNOSTIC_ASSERT(sInitPhase == InitSucceeded);
+  MOZ_DIAGNOSTIC_ASSERT(sInstance);
 
   DECODER_LOG(LogLevel::Debug, ("MediaShutdownManager::BlockShutdown() start..."));
 
   // Set this flag to ensure no Register() is allowed when Shutdown() begins.
-  mIsDoingXPCOMShutDown = true;
+  sInitPhase = XPCOMShutdownStarted;
 
   auto oldCount = mDecoders.Count();
   if (oldCount == 0) {
     RemoveBlocker();
     return NS_OK;
   }
 
   // Iterate over the decoders and shut them down.
--- a/dom/media/MediaShutdownManager.h
+++ b/dom/media/MediaShutdownManager.h
@@ -65,27 +65,34 @@ public:
   nsresult Register(MediaDecoder* aDecoder);
 
   // Notifies the MediaShutdownManager that a MediaDecoder that it was
   // tracking has shutdown, and it no longer needs to be shutdown in the
   // xpcom-shutdown listener.
   void Unregister(MediaDecoder* aDecoder);
 
 private:
+  enum InitPhase
+  {
+    NotInited,
+    InitSucceeded,
+    InitFailed,
+    XPCOMShutdownStarted,
+    XPCOMShutdownEnded
+  };
+
+  static InitPhase sInitPhase;
 
   MediaShutdownManager();
   virtual ~MediaShutdownManager();
   void RemoveBlocker();
 
   static StaticRefPtr<MediaShutdownManager> sInstance;
 
   // References to the MediaDecoder. The decoders unregister themselves
   // in their Shutdown() method, so we'll drop the reference naturally when
   // we're shutting down (in the non xpcom-shutdown case).
   nsTHashtable<nsRefPtrHashKey<MediaDecoder>> mDecoders;
-
-  bool mIsDoingXPCOMShutDown = false;
-  nsresult mError = NS_OK;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/ipc/VideoDecoderManagerParent.cpp
+++ b/dom/media/ipc/VideoDecoderManagerParent.cpp
@@ -21,17 +21,16 @@
 
 #if XP_WIN
 #include <objbase.h>
 #endif
 
 namespace mozilla {
 namespace dom {
 
-using base::Thread;
 using namespace ipc;
 using namespace layers;
 using namespace gfx;
 
 SurfaceDescriptorGPUVideo
 VideoDecoderManagerParent::StoreImage(Image* aImage, TextureClient* aTexture)
 {
   mImageMap[aTexture->GetSerial()] = aImage;
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -5,17 +5,17 @@ load 466945-1.html
 load 468763-1.html
 load 474744-1.html
 HTTP load 481136-1.html # needs to be HTTP to recognize the ogg as an audio file?
 load 492286-1.xhtml
 load 493915-1.html
 load 495794-1.html
 load 576612-1.html
 load 752784-1.html
-skip-if(webrender) load 789075-1.html # bug 1366502 comment 42
+skip-if(Android) skip-if(webrender) load 789075-1.html # bug 1366502 comment 42, bug 1374405
 skip-if(Android&&AndroidVersion=='22') HTTP load 795892-1.html # bug 1358718
 load 844563.html
 load 846612.html
 load 852838.html
 load 865537-1.html
 load 868504.html
 load 874869.html
 load 874915.html
--- a/dom/svg/SVGSVGElement.cpp
+++ b/dom/svg/SVGSVGElement.cpp
@@ -1,85 +1,70 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include <stdint.h>
-#include "mozilla/ArrayUtils.h"
 #include "mozilla/ContentEvents.h"
-#include "mozilla/EventDispatcher.h"
-#include "mozilla/Likely.h"
-
-#include "nsGkAtoms.h"
-#include "nsLayoutUtils.h"
-#include "nsLayoutStylesheetCache.h"
-#include "DOMSVGNumber.h"
-#include "DOMSVGLength.h"
-#include "nsSVGAngle.h"
-#include "nsCOMPtr.h"
-#include "nsIPresShell.h"
-#include "nsContentUtils.h"
-#include "nsIDocument.h"
-#include "mozilla/dom/SVGMatrix.h"
-#include "DOMSVGPoint.h"
-#include "nsIFrame.h"
-#include "nsFrameSelection.h"
-#include "nsISVGSVGFrame.h" //XXX
-#include "mozilla/dom/SVGRect.h"
-#include "nsError.h"
-#include "nsSVGDisplayableFrame.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/dom/SVGSVGElementBinding.h"
-#include "nsSVGUtils.h"
+#include "mozilla/dom/SVGMatrix.h"
 #include "mozilla/dom/SVGViewElement.h"
-#include "nsStyleUtil.h"
-#include "SVGContentUtils.h"
+#include "mozilla/EventDispatcher.h"
 
+#include "DOMSVGLength.h"
+#include "DOMSVGNumber.h"
+#include "DOMSVGPoint.h"
+#include "nsLayoutStylesheetCache.h"
+#include "nsSVGAngle.h"
+#include "nsFrameSelection.h"
+#include "nsIFrame.h"
+#include "nsISVGSVGFrame.h"
+#include "nsSMILAnimationController.h"
 #include "nsSMILTimeContainer.h"
-#include "nsSMILAnimationController.h"
-#include "nsSMILTypes.h"
+#include "nsSVGDisplayableFrame.h"
+#include "nsSVGUtils.h"
 #include "SVGAngle.h"
-#include <algorithm>
-#include "prtime.h"
 
 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT_CHECK_PARSER(SVG)
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace dom {
 
-class SVGAnimatedLength;
+nsSVGEnumMapping SVGSVGElement::sZoomAndPanMap[] = {
+  {&nsGkAtoms::disable, SVG_ZOOMANDPAN_DISABLE},
+  {&nsGkAtoms::magnify, SVG_ZOOMANDPAN_MAGNIFY},
+  {nullptr, 0}
+};
 
-JSObject*
-SVGSVGElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
+nsSVGElement::EnumInfo SVGSVGElement::sEnumInfo[1] =
 {
-  return SVGSVGElementBinding::Wrap(aCx, this, aGivenProto);
-}
+  { &nsGkAtoms::zoomAndPan,
+    sZoomAndPanMap,
+    SVG_ZOOMANDPAN_MAGNIFY
+  }
+};
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMSVGTranslatePoint, nsISVGPoint,
                                    mElement)
 
 NS_IMPL_ADDREF_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
 NS_IMPL_RELEASE_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGTranslatePoint)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   // We have to qualify nsISVGPoint because NS_GET_IID looks for a class in the
   // global namespace
   NS_INTERFACE_MAP_ENTRY(mozilla::nsISVGPoint)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-SVGSVGElement::~SVGSVGElement()
-{
-}
-
 DOMSVGPoint*
 DOMSVGTranslatePoint::Copy()
 {
   return new DOMSVGPoint(mPt.GetX(), mPt.GetY());
 }
 
 nsISupports*
 DOMSVGTranslatePoint::GetParentObject()
@@ -106,45 +91,21 @@ DOMSVGTranslatePoint::MatrixTransform(SV
   float d = matrix.D(), e = matrix.E(), f = matrix.F();
   float x = mPt.GetX();
   float y = mPt.GetY();
 
   nsCOMPtr<nsISVGPoint> point = new DOMSVGPoint(a*x + c*y + e, b*x + d*y + f);
   return point.forget();
 }
 
-SVGView::SVGView()
-{
-  mZoomAndPan.Init(SVGSVGElement::ZOOMANDPAN,
-                   SVG_ZOOMANDPAN_MAGNIFY);
-  mViewBox.Init();
-  mPreserveAspectRatio.Init();
-}
-
-nsSVGElement::LengthInfo SVGSVGElement::sLengthInfo[4] =
+JSObject*
+SVGSVGElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
-  { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
-  { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
-  { &nsGkAtoms::width, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X },
-  { &nsGkAtoms::height, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y },
-};
-
-nsSVGEnumMapping SVGSVGElement::sZoomAndPanMap[] = {
-  {&nsGkAtoms::disable, SVG_ZOOMANDPAN_DISABLE},
-  {&nsGkAtoms::magnify, SVG_ZOOMANDPAN_MAGNIFY},
-  {nullptr, 0}
-};
-
-nsSVGElement::EnumInfo SVGSVGElement::sEnumInfo[1] =
-{
-  { &nsGkAtoms::zoomAndPan,
-    sZoomAndPanMap,
-    SVG_ZOOMANDPAN_MAGNIFY
-  }
-};
+  return SVGSVGElementBinding::Wrap(aCx, this, aGivenProto);
+}
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(SVGSVGElement)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGSVGElement,
                                                 SVGSVGElementBase)
@@ -162,58 +123,49 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_ADDREF_INHERITED(SVGSVGElement,SVGSVGElementBase)
 NS_IMPL_RELEASE_INHERITED(SVGSVGElement,SVGSVGElementBase)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGSVGElement)
   NS_INTERFACE_TABLE_INHERITED(SVGSVGElement, nsIDOMNode, nsIDOMElement,
                                nsIDOMSVGElement)
 NS_INTERFACE_TABLE_TAIL_INHERITING(SVGSVGElementBase)
 
+SVGView::SVGView()
+{
+  mZoomAndPan.Init(SVGSVGElement::ZOOMANDPAN,
+                   SVG_ZOOMANDPAN_MAGNIFY);
+  mViewBox.Init();
+  mPreserveAspectRatio.Init();
+}
+
 //----------------------------------------------------------------------
 // Implementation
 
 SVGSVGElement::SVGSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                              FromParser aFromParser)
   : SVGSVGElementBase(aNodeInfo),
-    mViewportWidth(0),
-    mViewportHeight(0),
     mCurrentTranslate(0.0f, 0.0f),
     mCurrentScale(1.0f),
     mPreviousTranslate(0.0f, 0.0f),
     mPreviousScale(1.0f),
     mStartAnimationOnBindToTree(aFromParser == NOT_FROM_PARSER ||
                                 aFromParser == FROM_PARSER_FRAGMENT ||
                                 aFromParser == FROM_PARSER_XSLT),
-    mImageNeedsTransformInvalidation(false),
-    mHasChildrenOnlyTransform(false)
+    mImageNeedsTransformInvalidation(false)
+{
+}
+
+SVGSVGElement::~SVGSVGElement()
 {
 }
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
-// From NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSVGElement)
-nsresult
-SVGSVGElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
-                     bool aPreallocateChildren) const
-{
-  *aResult = nullptr;
-  already_AddRefed<mozilla::dom::NodeInfo> ni = RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget();
-  SVGSVGElement *it = new SVGSVGElement(ni, NOT_FROM_PARSER);
-
-  nsCOMPtr<nsINode> kungFuDeathGrip = it;
-  nsresult rv1 = it->Init();
-  nsresult rv2 = const_cast<SVGSVGElement*>(this)->CopyInnerTo(it, aPreallocateChildren);
-  if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
-    kungFuDeathGrip.swap(*aResult);
-  }
-
-  return NS_FAILED(rv1) ? rv1 : rv2;
-}
-
+NS_IMPL_ELEMENT_CLONE_WITH_INIT_AND_PARSER(SVGSVGElement)
 
 //----------------------------------------------------------------------
 // nsIDOMSVGSVGElement methods:
 
 already_AddRefed<SVGAnimatedLength>
 SVGSVGElement::X()
 {
   return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
@@ -231,17 +183,16 @@ SVGSVGElement::Width()
   return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
 }
 
 already_AddRefed<SVGAnimatedLength>
 SVGSVGElement::Height()
 {
   return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
 }
-
 float
 SVGSVGElement::PixelUnitToMillimeterX()
 {
   return MM_PER_INCH_FLOAT / 96;
 }
 
 float
 SVGSVGElement::PixelUnitToMillimeterY()
@@ -255,17 +206,16 @@ SVGSVGElement::ScreenPixelToMillimeterX(
   return MM_PER_INCH_FLOAT / 96;
 }
 
 float
 SVGSVGElement::ScreenPixelToMillimeterY()
 {
   return ScreenPixelToMillimeterX();
 }
-
 bool
 SVGSVGElement::UseCurrentView()
 {
   return mSVGView || mCurrentViewID;
 }
 
 float
 SVGSVGElement::CurrentScale()
@@ -438,78 +388,46 @@ SVGSVGElement::CreateSVGTransform()
 already_AddRefed<SVGTransform>
 SVGSVGElement::CreateSVGTransformFromMatrix(SVGMatrix& matrix)
 {
   RefPtr<SVGTransform> transform = new SVGTransform(matrix.GetMatrix());
   return transform.forget();
 }
 
 //----------------------------------------------------------------------
-
-already_AddRefed<SVGAnimatedRect>
-SVGSVGElement::ViewBox()
-{
-  return mViewBox.ToSVGAnimatedRect(this);
-}
-
-already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
-SVGSVGElement::PreserveAspectRatio()
-{
-  return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);
-}
-
-uint16_t
-SVGSVGElement::ZoomAndPan()
-{
-  return mEnumAttributes[ZOOMANDPAN].GetAnimValue();
-}
-
-void
-SVGSVGElement::SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv)
-{
-  if (aZoomAndPan == SVG_ZOOMANDPAN_DISABLE ||
-      aZoomAndPan == SVG_ZOOMANDPAN_MAGNIFY) {
-    mEnumAttributes[ZOOMANDPAN].SetBaseValue(aZoomAndPan, this);
-    return;
-  }
-
-  rv.ThrowRangeError<MSG_INVALID_ZOOMANDPAN_VALUE_ERROR>();
-}
-
-//----------------------------------------------------------------------
 // helper method for implementing SetCurrentScale/Translate
 
 void
 SVGSVGElement::SetCurrentScaleTranslate(float s, float x, float y)
 {
   if (s == mCurrentScale &&
       x == mCurrentTranslate.GetX() && y == mCurrentTranslate.GetY()) {
     return;
   }
 
   // Prevent bizarre behaviour and maxing out of CPU and memory by clamping
   if (s < CURRENT_SCALE_MIN)
     s = CURRENT_SCALE_MIN;
   else if (s > CURRENT_SCALE_MAX)
     s = CURRENT_SCALE_MAX;
-  
+
   // IMPORTANT: If either mCurrentTranslate *or* mCurrentScale is changed then
   // mPreviousTranslate_x, mPreviousTranslate_y *and* mPreviousScale must all
   // be updated otherwise SVGZoomEvents will end up with invalid data. I.e. an
   // SVGZoomEvent's properties previousScale and previousTranslate must contain
   // the state of currentScale and currentTranslate immediately before the
   // change that caused the event's dispatch, which is *not* necessarily the
   // same thing as the values of currentScale and currentTranslate prior to
   // their own last change.
   //
   // XXX This comment is out-of-date due to removal of SVGZoomEvent.  Can we
   // remove some of this code?
   mPreviousScale = mCurrentScale;
   mPreviousTranslate = mCurrentTranslate;
-  
+
   mCurrentScale = s;
   mCurrentTranslate = SVGPoint(x, y);
 
   // now dispatch the appropriate event if we are the root element
   nsIDocument* doc = GetUncomposedDoc();
   if (doc) {
     nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
     if (presShell && IsRoot()) {
@@ -524,16 +442,37 @@ SVGSVGElement::SetCurrentScaleTranslate(
 }
 
 void
 SVGSVGElement::SetCurrentTranslate(float x, float y)
 {
   SetCurrentScaleTranslate(mCurrentScale, x, y);
 }
 
+//----------------------------------------------------------------------
+// SVGZoomAndPanValues
+uint16_t
+SVGSVGElement::ZoomAndPan()
+{
+  return mEnumAttributes[ZOOMANDPAN].GetAnimValue();
+}
+
+void
+SVGSVGElement::SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv)
+{
+  if (aZoomAndPan == SVG_ZOOMANDPAN_DISABLE ||
+      aZoomAndPan == SVG_ZOOMANDPAN_MAGNIFY) {
+    mEnumAttributes[ZOOMANDPAN].SetBaseValue(aZoomAndPan, this);
+    return;
+  }
+
+  rv.ThrowRangeError<MSG_INVALID_ZOOMANDPAN_VALUE_ERROR>();
+}
+
+//----------------------------------------------------------------------
 nsSMILTimeContainer*
 SVGSVGElement::GetTimedDocumentRoot()
 {
   if (mTimedDocumentRoot) {
     return mTimedDocumentRoot;
   }
 
   // We must not be the outermost <svg> element, try to find it
@@ -541,185 +480,18 @@ SVGSVGElement::GetTimedDocumentRoot()
     SVGContentUtils::GetOuterSVGElement(this);
 
   if (outerSVGElement) {
     return outerSVGElement->GetTimedDocumentRoot();
   }
   // invalid structure
   return nullptr;
 }
-
 //----------------------------------------------------------------------
-// nsIContent methods
-
-NS_IMETHODIMP_(bool)
-SVGSVGElement::IsAttributeMapped(const nsIAtom* name) const
-{
-  // We want to map the 'width' and 'height' attributes into style for
-  // outer-<svg>, except when the attributes aren't set (since their default
-  // values of '100%' can cause unexpected and undesirable behaviour for SVG
-  // inline in HTML). We rely on nsSVGElement::UpdateContentStyleRule() to
-  // prevent mapping of the default values into style (it only maps attributes
-  // that are set). We also rely on a check in nsSVGElement::
-  // UpdateContentStyleRule() to prevent us mapping the attributes when they're
-  // given a <length> value that is not currently recognized by the SVG
-  // specification.
-
-  if (!IsInner() && (name == nsGkAtoms::width || name == nsGkAtoms::height)) {
-    return true;
-  }
-
-  static const MappedAttributeEntry* const map[] = {
-    sColorMap,
-    sFEFloodMap,
-    sFillStrokeMap,
-    sFiltersMap,
-    sFontSpecificationMap,
-    sGradientStopMap,
-    sGraphicsMap,
-    sLightingEffectsMap,
-    sMarkersMap,
-    sTextContentElementsMap,
-    sViewportsMap
-  };
-
-  return FindAttributeDependence(name, map) ||
-    SVGSVGElementBase::IsAttributeMapped(name);
-}
-
-//----------------------------------------------------------------------
-// nsIContent methods:
-
-nsresult
-SVGSVGElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
-{
-  if (aVisitor.mEvent->mMessage == eSVGLoad) {
-    if (mTimedDocumentRoot) {
-      mTimedDocumentRoot->Begin();
-      // Set 'resample needed' flag, so that if any script calls a DOM method
-      // that requires up-to-date animations before our first sample callback,
-      // we'll force a synchronous sample.
-      AnimationNeedsResample();
-    }
-  }
-  return SVGSVGElementBase::GetEventTargetParent(aVisitor);
-}
-
-bool
-SVGSVGElement::IsEventAttributeName(nsIAtom* aName)
-{
-  /* The events in EventNameType_SVGSVG are for events that are only
-     applicable to outermost 'svg' elements. We don't check if we're an outer
-     'svg' element in case we're not inserted into the document yet, but since
-     the target of the events in question will always be the outermost 'svg'
-     element, this shouldn't cause any real problems.
-  */
-  return nsContentUtils::IsEventAttributeName(aName,
-         (EventNameType_SVGGraphic | EventNameType_SVGSVG));
-}
-
-//----------------------------------------------------------------------
-// nsSVGElement overrides
-
-// Helper for GetViewBoxTransform on root <svg> node
-// * aLength: internal value for our <svg> width or height attribute.
-// * aViewportLength: length of the corresponding dimension of the viewport.
-// * aSelf: the outermost <svg> node itself.
-// NOTE: aSelf is not an ancestor viewport element, so it can't be used to
-// resolve percentage lengths. (It can only be used to resolve
-// 'em'/'ex'-valued units).
-inline float
-ComputeSynthesizedViewBoxDimension(const nsSVGLength2& aLength,
-                                   float aViewportLength,
-                                   const SVGSVGElement* aSelf)
-{
-  if (aLength.IsPercentage()) {
-    return aViewportLength * aLength.GetAnimValInSpecifiedUnits() / 100.0f;
-  }
-
-  return aLength.GetAnimValue(const_cast<SVGSVGElement*>(aSelf));
-}
-
-//----------------------------------------------------------------------
-// public helpers:
-
-gfx::Matrix
-SVGSVGElement::GetViewBoxTransform() const
-{
-  float viewportWidth, viewportHeight;
-  if (IsInner()) {
-    SVGSVGElement *ctx = GetCtx();
-    viewportWidth = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
-    viewportHeight = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx);
-  } else {
-    viewportWidth = mViewportWidth;
-    viewportHeight = mViewportHeight;
-  }
-
-  if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
-    return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
-  }
-
-  nsSVGViewBoxRect viewBox =
-    GetViewBoxWithSynthesis(viewportWidth, viewportHeight);
-
-  if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
-    return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
-  }
-
-  return SVGContentUtils::GetViewBoxTransform(viewportWidth, viewportHeight,
-                                              viewBox.x, viewBox.y,
-                                              viewBox.width, viewBox.height,
-                                              GetPreserveAspectRatioWithOverride());
-}
-
-void
-SVGSVGElement::UpdateHasChildrenOnlyTransform()
-{
-  bool hasChildrenOnlyTransform =
-    HasViewBoxOrSyntheticViewBox() ||
-    (IsRoot() && (mCurrentTranslate != SVGPoint(0.0f, 0.0f) ||
-                  mCurrentScale != 1.0f));
-  mHasChildrenOnlyTransform = hasChildrenOnlyTransform;
-}
-
-void
-SVGSVGElement::ChildrenOnlyTransformChanged(uint32_t aFlags)
-{
-  // Avoid wasteful calls:
-  MOZ_ASSERT(!(GetPrimaryFrame()->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
-             "Non-display SVG frames don't maintain overflow rects");
-
-  nsChangeHint changeHint;
-
-  bool hadChildrenOnlyTransform = mHasChildrenOnlyTransform;
-
-  UpdateHasChildrenOnlyTransform();
-
-  if (hadChildrenOnlyTransform != mHasChildrenOnlyTransform) {
-    // Reconstruct the frame tree to handle stacking context changes:
-    // XXXjwatt don't do this for root-<svg> or even outer-<svg>?
-    changeHint = nsChangeHint_ReconstructFrame;
-  } else {
-    // We just assume the old and new transforms are different.
-    changeHint = nsChangeHint(nsChangeHint_UpdateOverflow |
-                              nsChangeHint_ChildrenOnlyTransform);
-  }
-
-  // If we're not reconstructing the frame tree, then we only call
-  // PostRestyleEvent if we're not being called under reflow to avoid recursing
-  // to death. See bug 767056 comments 10 and 12. Since our nsSVGOuterSVGFrame
-  // is being reflowed we're going to invalidate and repaint its entire area
-  // anyway (which will include our children).
-  if ((changeHint & nsChangeHint_ReconstructFrame) ||
-      !(aFlags & eDuringReflow)) {
-    nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint);
-  }
-}
-
+// nsSVGElement
 nsresult
 SVGSVGElement::BindToTree(nsIDocument* aDocument,
                           nsIContent* aParent,
                           nsIContent* aBindingParent,
                           bool aCompileEventHandlers)
 {
   nsSMILAnimationController* smilController = nullptr;
 
@@ -737,17 +509,17 @@ SVGSVGElement::BindToTree(nsIDocument* a
         // time container. However, we need to make sure that we'll get a
         // kick-start if we get promoted to be outermost later on.
         mTimedDocumentRoot = nullptr;
         mStartAnimationOnBindToTree = true;
       }
     }
   }
 
-  nsresult rv = SVGSVGElementBase::BindToTree(aDocument, aParent,
+  nsresult rv = SVGGraphicsElement::BindToTree(aDocument, aParent,
                                               aBindingParent,
                                               aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv,rv);
 
   nsIDocument* doc = GetComposedDoc();
   if (doc) {
     // Setup the style sheet during binding, not element construction,
     // because we could move the root SVG element from the document
@@ -769,17 +541,98 @@ SVGSVGElement::BindToTree(nsIDocument* a
 
 void
 SVGSVGElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   if (mTimedDocumentRoot) {
     mTimedDocumentRoot->SetParent(nullptr);
   }
 
-  SVGSVGElementBase::UnbindFromTree(aDeep, aNullParent);
+  SVGGraphicsElement::UnbindFromTree(aDeep, aNullParent);
+}
+
+nsSVGAnimatedTransformList*
+SVGSVGElement::GetAnimatedTransformList(uint32_t aFlags)
+{
+  if (!(aFlags & DO_ALLOCATE) && mSVGView && mSVGView->mTransforms) {
+    return mSVGView->mTransforms;
+  }
+  return SVGGraphicsElement::GetAnimatedTransformList(aFlags);
+}
+
+nsresult
+SVGSVGElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
+{
+  if (aVisitor.mEvent->mMessage == eSVGLoad) {
+    if (mTimedDocumentRoot) {
+      mTimedDocumentRoot->Begin();
+      // Set 'resample needed' flag, so that if any script calls a DOM method
+      // that requires up-to-date animations before our first sample callback,
+      // we'll force a synchronous sample.
+      AnimationNeedsResample();
+    }
+  }
+  return SVGSVGElementBase::GetEventTargetParent(aVisitor);
+}
+
+bool
+SVGSVGElement::IsEventAttributeName(nsIAtom* aName)
+{
+  /* The events in EventNameType_SVGSVG are for events that are only
+     applicable to outermost 'svg' elements. We don't check if we're an outer
+     'svg' element in case we're not inserted into the document yet, but since
+     the target of the events in question will always be the outermost 'svg'
+     element, this shouldn't cause any real problems.
+  */
+  return nsContentUtils::IsEventAttributeName(aName,
+         (EventNameType_SVGGraphic | EventNameType_SVGSVG));
+}
+
+//----------------------------------------------------------------------
+// public helpers:
+
+int32_t
+SVGSVGElement::GetIntrinsicWidth()
+{
+  if (mLengthAttributes[ATTR_WIDTH].IsPercentage()) {
+    return -1;
+  }
+  // Passing |this| as a SVGViewportElement* invokes the variant of GetAnimValue
+  // that uses the passed argument as the context, but that's fine since we
+  // know the length isn't a percentage so the context won't be used (and we
+  // need to pass the element to be able to resolve em/ex units).
+  float width = mLengthAttributes[ATTR_WIDTH].GetAnimValue(this);
+  return nsSVGUtils::ClampToInt(width);
+}
+
+int32_t
+SVGSVGElement::GetIntrinsicHeight()
+{
+  if (mLengthAttributes[ATTR_HEIGHT].IsPercentage()) {
+    return -1;
+  }
+  // Passing |this| as a SVGViewportElement* invokes the variant of GetAnimValue
+  // that uses the passed argument as the context, but that's fine since we
+  // know the length isn't a percentage so the context won't be used (and we
+  // need to pass the element to be able to resolve em/ex units).
+  float height = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(this);
+  return nsSVGUtils::ClampToInt(height);
+}
+
+void
+SVGSVGElement::FlushImageTransformInvalidation()
+{
+  MOZ_ASSERT(!GetParent(), "Should only be called on root node");
+  MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
+             "Should only be called on image documents");
+
+  if (mImageNeedsTransformInvalidation) {
+    InvalidateTransformNotifyFrame();
+    mImageNeedsTransformInvalidation = false;
+  }
 }
 
 //----------------------------------------------------------------------
 // implementation helpers
 
 bool
 SVGSVGElement::WillBeOutermostSVG(nsIContent* aParent,
                                   nsIContent* aBindingParent) const
@@ -806,308 +659,23 @@ SVGSVGElement::InvalidateTransformNotify
   nsISVGSVGFrame* svgframe = do_QueryFrame(GetPrimaryFrame());
   // might fail this check if we've failed conditional processing
   if (svgframe) {
     svgframe->NotifyViewportOrTransformChanged(
                 nsSVGDisplayableFrame::TRANSFORM_CHANGED);
   }
 }
 
-bool
-SVGSVGElement::HasPreserveAspectRatio()
-{
-  return HasAttr(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio) ||
-    mPreserveAspectRatio.IsAnimated();
-}
-
-SVGViewElement*
-SVGSVGElement::GetCurrentViewElement() const
-{
-  if (mCurrentViewID) {
-    //XXXsmaug It is unclear how this should work in case we're in Shadow DOM.
-    nsIDocument* doc = GetUncomposedDoc();
-    if (doc) {
-      Element *element = doc->GetElementById(*mCurrentViewID);
-      if (element && element->IsSVGElement(nsGkAtoms::view)) {
-        return static_cast<SVGViewElement*>(element);
-      }
-    }
-  }
-  return nullptr;
-}
-
-nsSVGViewBoxRect
-SVGSVGElement::GetViewBoxWithSynthesis(
-  float aViewportWidth, float aViewportHeight) const
-{
-  // The logic here should match HasViewBoxRect().
-  SVGViewElement* viewElement = GetCurrentViewElement();
-  if (viewElement && viewElement->mViewBox.HasRect()) {
-    return viewElement->mViewBox.GetAnimValue();
-  }
-  if (mSVGView && mSVGView->mViewBox.HasRect()) {
-    return mSVGView->mViewBox.GetAnimValue();
-  }
-  if (mViewBox.HasRect()) {
-    return mViewBox.GetAnimValue();
-  }
-
-  if (ShouldSynthesizeViewBox()) {
-    // Special case -- fake a viewBox, using height & width attrs.
-    // (Use |this| as context, since if we get here, we're outermost <svg>.)
-    return nsSVGViewBoxRect(0, 0,
-              ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
-                                                 mViewportWidth, this),
-              ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
-                                                 mViewportHeight, this));
-
-  }
-
-  // No viewBox attribute, so we shouldn't auto-scale. This is equivalent
-  // to having a viewBox that exactly matches our viewport size.
-  return nsSVGViewBoxRect(0, 0, aViewportWidth, aViewportHeight);
-}
-
-SVGPreserveAspectRatio
-SVGSVGElement::GetPreserveAspectRatioWithOverride() const
-{
-  nsIDocument* doc = GetUncomposedDoc();
-  if (doc && doc->IsBeingUsedAsImage()) {
-    const SVGPreserveAspectRatio *pAROverridePtr = GetPreserveAspectRatioProperty();
-    if (pAROverridePtr) {
-      return *pAROverridePtr;
-    }
-  }
-
-  SVGViewElement* viewElement = GetCurrentViewElement();
-
-  // This check is equivalent to "!HasViewBoxRect() && ShouldSynthesizeViewBox()".
-  // We're just holding onto the viewElement that HasViewBoxRect() would look up,
-  // so that we don't have to look it up again later.
-  if (!((viewElement && viewElement->mViewBox.HasRect()) ||
-        (mSVGView && mSVGView->mViewBox.HasRect()) ||
-        mViewBox.HasRect()) &&
-      ShouldSynthesizeViewBox()) {
-    // If we're synthesizing a viewBox, use preserveAspectRatio="none";
-    return SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE, SVG_MEETORSLICE_SLICE);
-  }
-
-  if (viewElement && viewElement->mPreserveAspectRatio.IsExplicitlySet()) {
-    return viewElement->mPreserveAspectRatio.GetAnimValue();
-  }
-  if (mSVGView && mSVGView->mPreserveAspectRatio.IsExplicitlySet()) {
-    return mSVGView->mPreserveAspectRatio.GetAnimValue();
-  }
-  return mPreserveAspectRatio.GetAnimValue();
-}
-
-//----------------------------------------------------------------------
-// SVGSVGElement
-
-float
-SVGSVGElement::GetLength(uint8_t aCtxType)
-{
-  float h, w;
-
-  SVGViewElement* viewElement = GetCurrentViewElement();
-  const nsSVGViewBoxRect* viewbox = nullptr;
-
-  // The logic here should match HasViewBoxRect().
-  if (viewElement && viewElement->mViewBox.HasRect()) {
-    viewbox = &viewElement->mViewBox.GetAnimValue();
-  } else if (mSVGView && mSVGView->mViewBox.HasRect()) {
-    viewbox = &mSVGView->mViewBox.GetAnimValue();
-  } else if (mViewBox.HasRect()) {
-    viewbox = &mViewBox.GetAnimValue();
-  }
-
-  if (viewbox) {
-    w = viewbox->width;
-    h = viewbox->height;
-  } else if (IsInner()) {
-    SVGSVGElement *ctx = GetCtx();
-    w = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
-    h = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx);
-  } else if (ShouldSynthesizeViewBox()) {
-    w = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
-                                           mViewportWidth, this);
-    h = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
-                                           mViewportHeight, this);
-  } else {
-    w = mViewportWidth;
-    h = mViewportHeight;
-  }
-
-  w = std::max(w, 0.0f);
-  h = std::max(h, 0.0f);
-
-  switch (aCtxType) {
-  case SVGContentUtils::X:
-    return w;
-  case SVGContentUtils::Y:
-    return h;
-  case SVGContentUtils::XY:
-    return float(SVGContentUtils::ComputeNormalizedHypotenuse(w, h));
-  }
-  return 0;
-}
-
-//----------------------------------------------------------------------
-// nsSVGElement methods
-
-/* virtual */ gfxMatrix
-SVGSVGElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
-                                        SVGTransformTypes aWhich) const
-{
-  // 'transform' attribute (or an override from a fragment identifier):
-  gfxMatrix userToParent;
-
-  if (aWhich == eUserSpaceToParent || aWhich == eAllTransforms) {
-    userToParent = GetUserToParentTransform(mAnimateMotionTransform,
-                                            mSVGView && mSVGView->mTransforms
-                                              ? mSVGView->mTransforms
-                                              : mTransforms);
-    if (aWhich == eUserSpaceToParent) {
-      return userToParent * aMatrix;
-    }
-  }
-
-  gfxMatrix childToUser;
-
-  if (IsInner()) {
-    float x, y;
-    const_cast<SVGSVGElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
-    childToUser = ThebesMatrix(GetViewBoxTransform().PostTranslate(x, y));
-  } else if (IsRoot()) {
-    childToUser = ThebesMatrix(GetViewBoxTransform()
-                                 .PostScale(mCurrentScale, mCurrentScale)
-                                 .PostTranslate(mCurrentTranslate.GetX(),
-                                                mCurrentTranslate.GetY()));
-  } else {
-    // outer-<svg>, but inline in some other content:
-    childToUser = ThebesMatrix(GetViewBoxTransform());
-  }
-
-  if (aWhich == eAllTransforms) {
-    return childToUser * userToParent * aMatrix;
-  }
-
-  MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
-
-  // The following may look broken because pre-multiplying our eChildToUserSpace
-  // transform with another matrix without including our eUserSpaceToParent
-  // transform between the two wouldn't make sense.  We don't expect that to
-  // ever happen though.  We get here either when the identity matrix has been
-  // passed because our caller just wants our eChildToUserSpace transform, or
-  // when our eUserSpaceToParent transform has already been multiplied into the
-  // matrix that our caller passes (such as when we're called from PaintSVG).
-  return childToUser * aMatrix;
-}
-
-nsSVGAnimatedTransformList*
-SVGSVGElement::GetAnimatedTransformList(uint32_t aFlags)
-{
-  if (!(aFlags & DO_ALLOCATE) && mSVGView && mSVGView->mTransforms) {
-    return mSVGView->mTransforms;
-  }
-  return SVGSVGElementBase::GetAnimatedTransformList(aFlags);
-}
-
-/* virtual */ bool
-SVGSVGElement::HasValidDimensions() const
-{
-  return !IsInner() ||
-    ((!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
-       mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
-     (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
-       mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0));
-}
-
-nsSVGElement::LengthAttributesInfo
-SVGSVGElement::GetLengthInfo()
-{
-  return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
-                              ArrayLength(sLengthInfo));
-}
-
 nsSVGElement::EnumAttributesInfo
 SVGSVGElement::GetEnumInfo()
 {
   return EnumAttributesInfo(mEnumAttributes, sEnumInfo,
                             ArrayLength(sEnumInfo));
 }
 
-nsSVGViewBox*
-SVGSVGElement::GetViewBox()
-{
-  return &mViewBox;
-}
-
-SVGAnimatedPreserveAspectRatio *
-SVGSVGElement::GetPreserveAspectRatio()
-{
-  return &mPreserveAspectRatio;
-}
-
-bool
-SVGSVGElement::HasViewBoxRect() const
-{
-  SVGViewElement* viewElement = GetCurrentViewElement();
-  if ((viewElement && viewElement->mViewBox.HasRect()) ||
-      (mSVGView && mSVGView->mViewBox.HasRect())) {
-    return true;
-  }
-  return mViewBox.HasRect();
-}
-
-bool
-SVGSVGElement::ShouldSynthesizeViewBox() const
-{
-  MOZ_ASSERT(!HasViewBoxRect(), "Should only be called if we lack a viewBox");
-
-  return IsRoot() && OwnerDoc()->IsBeingUsedAsImage();
-}
-
-bool
-SVGSVGElement::SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR)
-{
-  SVGPreserveAspectRatio* pAROverridePtr = new SVGPreserveAspectRatio(aPAR);
-  nsresult rv = SetProperty(nsGkAtoms::overridePreserveAspectRatio,
-                            pAROverridePtr,
-                            nsINode::DeleteProperty<SVGPreserveAspectRatio>,
-                            true);
-  MOZ_ASSERT(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
-             "Setting override value when it's already set...?");
-
-  if (MOZ_UNLIKELY(NS_FAILED(rv))) {
-    // property-insertion failed (e.g. OOM in property-table code)
-    delete pAROverridePtr;
-    return false;
-  }
-  return true;
-}
-
-const SVGPreserveAspectRatio*
-SVGSVGElement::GetPreserveAspectRatioProperty() const
-{
-  void* valPtr = GetProperty(nsGkAtoms::overridePreserveAspectRatio);
-  if (valPtr) {
-    return static_cast<SVGPreserveAspectRatio*>(valPtr);
-  }
-  return nullptr;
-}
-
-bool
-SVGSVGElement::ClearPreserveAspectRatioProperty()
-{
-  void* valPtr = UnsetProperty(nsGkAtoms::overridePreserveAspectRatio);
-  delete static_cast<SVGPreserveAspectRatio*>(valPtr);
-  return valPtr;
-}
-
 void
 SVGSVGElement::
   SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR)
 {
 #ifdef DEBUG
   MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
              "should only override preserveAspectRatio in images");
 #endif
@@ -1144,51 +712,118 @@ SVGSVGElement::ClearImageOverridePreserv
     mImageNeedsTransformInvalidation = true;
   }
 
   if (ClearPreserveAspectRatioProperty()) {
     mImageNeedsTransformInvalidation = true;
   }
 }
 
-void
-SVGSVGElement::FlushImageTransformInvalidation()
+bool
+SVGSVGElement::SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR)
 {
-  MOZ_ASSERT(!GetParent(), "Should only be called on root node");
-  MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
-             "Should only be called on image documents");
+  SVGPreserveAspectRatio* pAROverridePtr = new SVGPreserveAspectRatio(aPAR);
+  nsresult rv = SetProperty(nsGkAtoms::overridePreserveAspectRatio,
+                            pAROverridePtr,
+                            nsINode::DeleteProperty<SVGPreserveAspectRatio>,
+                            true);
+  MOZ_ASSERT(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
+             "Setting override value when it's already set...?");
+
+  if (MOZ_UNLIKELY(NS_FAILED(rv))) {
+    // property-insertion failed (e.g. OOM in property-table code)
+    delete pAROverridePtr;
+    return false;
+  }
+  return true;
+}
 
-  if (mImageNeedsTransformInvalidation) {
-    InvalidateTransformNotifyFrame();
-    mImageNeedsTransformInvalidation = false;
+const SVGPreserveAspectRatio*
+SVGSVGElement::GetPreserveAspectRatioProperty() const
+{
+  void* valPtr = GetProperty(nsGkAtoms::overridePreserveAspectRatio);
+  if (valPtr) {
+    return static_cast<SVGPreserveAspectRatio*>(valPtr);
   }
+  return nullptr;
+}
+
+bool
+SVGSVGElement::ClearPreserveAspectRatioProperty()
+{
+  void* valPtr = UnsetProperty(nsGkAtoms::overridePreserveAspectRatio);
+  delete static_cast<SVGPreserveAspectRatio*>(valPtr);
+  return valPtr;
 }
 
-int32_t
-SVGSVGElement::GetIntrinsicWidth()
+
+SVGPreserveAspectRatio
+SVGSVGElement::GetPreserveAspectRatioWithOverride() const
 {
-  if (mLengthAttributes[ATTR_WIDTH].IsPercentage()) {
-    return -1;
+  nsIDocument* doc = GetUncomposedDoc();
+  if (doc && doc->IsBeingUsedAsImage()) {
+    const SVGPreserveAspectRatio *pAROverridePtr = GetPreserveAspectRatioProperty();
+    if (pAROverridePtr) {
+      return *pAROverridePtr;
+    }
   }
-  // Passing |this| as a SVGSVGElement* invokes the variant of GetAnimValue
-  // that uses the passed argument as the context, but that's fine since we
-  // know the length isn't a percentage so the context won't be used (and we
-  // need to pass the element to be able to resolve em/ex units).
-  float width = mLengthAttributes[ATTR_WIDTH].GetAnimValue(this);
-  return nsSVGUtils::ClampToInt(width);
+
+  SVGViewElement* viewElement = GetCurrentViewElement();
+
+  // This check is equivalent to "!HasViewBoxRect() && ShouldSynthesizeViewBox()".
+  // We're just holding onto the viewElement that HasViewBoxRect() would look up,
+  // so that we don't have to look it up again later.
+  if (!((viewElement && viewElement->mViewBox.HasRect()) ||
+        (mSVGView && mSVGView->mViewBox.HasRect()) ||
+        mViewBox.HasRect()) &&
+      ShouldSynthesizeViewBox()) {
+    // If we're synthesizing a viewBox, use preserveAspectRatio="none";
+    return SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE, SVG_MEETORSLICE_SLICE);
+  }
+
+  if (viewElement && viewElement->mPreserveAspectRatio.IsExplicitlySet()) {
+    return viewElement->mPreserveAspectRatio.GetAnimValue();
+  }
+  if (mSVGView && mSVGView->mPreserveAspectRatio.IsExplicitlySet()) {
+    return mSVGView->mPreserveAspectRatio.GetAnimValue();
+  }
+  return mPreserveAspectRatio.GetAnimValue();
 }
 
-int32_t
-SVGSVGElement::GetIntrinsicHeight()
+SVGViewElement*
+SVGSVGElement::GetCurrentViewElement() const
 {
-  if (mLengthAttributes[ATTR_HEIGHT].IsPercentage()) {
-    return -1;
+  if (mCurrentViewID) {
+    //XXXsmaug It is unclear how this should work in case we're in Shadow DOM.
+    nsIDocument* doc = GetUncomposedDoc();
+    if (doc) {
+      Element *element = doc->GetElementById(*mCurrentViewID);
+      if (element && element->IsSVGElement(nsGkAtoms::view)) {
+        return static_cast<SVGViewElement*>(element);
+      }
+    }
   }
-  // Passing |this| as a SVGSVGElement* invokes the variant of GetAnimValue
-  // that uses the passed argument as the context, but that's fine since we
-  // know the length isn't a percentage so the context won't be used (and we
-  // need to pass the element to be able to resolve em/ex units).
-  float height = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(this);
-  return nsSVGUtils::ClampToInt(height);
+  return nullptr;
+}
+
+const nsSVGViewBox&
+SVGSVGElement::GetViewBoxInternal() const
+{
+  SVGViewElement* viewElement = GetCurrentViewElement();
+
+  if (viewElement && viewElement->mViewBox.HasRect()) {
+    return viewElement->mViewBox;
+  } else if (mSVGView && mSVGView->mViewBox.HasRect()) {
+    return mSVGView->mViewBox;
+  }
+
+  return mViewBox;
+}
+
+nsSVGAnimatedTransformList*
+SVGSVGElement::GetTransformInternal() const
+{
+  return (mSVGView && mSVGView->mTransforms)
+         ? mSVGView->mTransforms: mTransforms;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/svg/SVGSVGElement.h
+++ b/dom/svg/SVGSVGElement.h
@@ -2,60 +2,51 @@
 /* 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_SVGSVGElement_h
 #define mozilla_dom_SVGSVGElement_h
 
-#include "mozilla/dom/FromParser.h"
-#include "nsAutoPtr.h"
-#include "nsIContentInlines.h"
-#include "nsISVGPoint.h"
-#include "nsSVGEnum.h"
-#include "nsSVGLength2.h"
-#include "SVGGraphicsElement.h"
-#include "SVGImageContext.h"
-#include "nsSVGViewBox.h"
-#include "SVGPreserveAspectRatio.h"
-#include "SVGAnimatedPreserveAspectRatio.h"
-#include "mozilla/Attributes.h"
+#include "SVGViewportElement.h"
 
 nsresult NS_NewSVGSVGElement(nsIContent **aResult,
                              already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                              mozilla::dom::FromParser aFromParser);
 
 class nsSMILTimeContainer;
-class nsSVGOuterSVGFrame;
-class nsSVGInnerSVGFrame;
 
 namespace mozilla {
-class AutoPreserveAspectRatioOverride;
-class AutoSVGTimeSetRestore;
-class DOMSVGAnimatedPreserveAspectRatio;
+class AutoSVGViewHandler;
+class SVGFragmentIdentifier;
+class EventChainPreVisitor;
 class DOMSVGLength;
 class DOMSVGNumber;
-class EventChainPreVisitor;
-class SVGFragmentIdentifier;
-class AutoSVGViewHandler;
 
 namespace dom {
 class SVGAngle;
-class SVGAnimatedRect;
 class SVGMatrix;
-class SVGTransform;
-class SVGViewElement;
 class SVGIRect;
+class SVGSVGElement;
 
-class SVGSVGElement;
+// Stores svgView arguments of SVG fragment identifiers.
+class SVGView {
+public:
+  SVGView();
+
+  nsSVGEnum                             mZoomAndPan;
+  nsSVGViewBox                          mViewBox;
+  SVGAnimatedPreserveAspectRatio        mPreserveAspectRatio;
+  nsAutoPtr<nsSVGAnimatedTransformList> mTransforms;
+};
 
 class DOMSVGTranslatePoint final : public nsISVGPoint {
 public:
-  DOMSVGTranslatePoint(SVGPoint* aPt, SVGSVGElement *aElement)
+  DOMSVGTranslatePoint(SVGPoint* aPt, SVGSVGElement* aElement)
     : nsISVGPoint(aPt, true), mElement(aElement) {}
 
   explicit DOMSVGTranslatePoint(DOMSVGTranslatePoint* aPt)
     : nsISVGPoint(&aPt->mPt, true), mElement(aPt->mElement) {}
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
 
@@ -71,55 +62,27 @@ public:
   virtual nsISupports* GetParentObject() override;
 
   RefPtr<SVGSVGElement> mElement;
 
 private:
   ~DOMSVGTranslatePoint() {}
 };
 
-class svgFloatSize {
-public:
-  svgFloatSize(float aWidth, float aHeight)
-    : width(aWidth)
-    , height(aHeight)
-  {}
-  bool operator!=(const svgFloatSize& rhs) {
-    return width != rhs.width || height != rhs.height;
-  }
-  float width;
-  float height;
-};
-
-// Stores svgView arguments of SVG fragment identifiers.
-class SVGView {
-  friend class mozilla::AutoSVGViewHandler;
-  friend class mozilla::dom::SVGSVGElement;
-public:
-  SVGView();
-
-private:
-  nsSVGEnum                             mZoomAndPan;
-  nsSVGViewBox                          mViewBox;
-  SVGAnimatedPreserveAspectRatio        mPreserveAspectRatio;
-  nsAutoPtr<nsSVGAnimatedTransformList> mTransforms;
-};
-
-typedef SVGGraphicsElement SVGSVGElementBase;
+typedef SVGViewportElement SVGSVGElementBase;
 
 class SVGSVGElement final : public SVGSVGElementBase
 {
   friend class ::nsSVGOuterSVGFrame;
-  friend class ::nsSVGInnerSVGFrame;
-  friend class mozilla::AutoPreserveAspectRatioOverride;
-  friend class mozilla::AutoSVGTimeSetRestore;
-  friend class mozilla::dom::SVGView;
   friend class mozilla::SVGFragmentIdentifier;
   friend class mozilla::AutoSVGViewHandler;
+  friend class mozilla::AutoPreserveAspectRatioOverride;
+  friend class mozilla::dom::SVGView;
 
+protected:
   SVGSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                 FromParser aFromParser);
   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   friend nsresult (::NS_NewSVGSVGElement(nsIContent **aResult,
                                          already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                                          mozilla::dom::FromParser aFromParser));
 
@@ -134,134 +97,25 @@ public:
    * For use by zoom controls to allow currentScale, currentTranslate.x and
    * currentTranslate.y to be set by a single operation that dispatches a
    * single SVGZoom event (instead of one SVGZoom and two SVGScroll events).
    *
    * XXX SVGZoomEvent is no more, is this needed?
    */
   void SetCurrentScaleTranslate(float s, float x, float y);
 
-  /**
-   * Retrieve the value of currentScale and currentTranslate.
-   */
-  const SVGPoint& GetCurrentTranslate() { return mCurrentTranslate; }
-  float GetCurrentScale() { return mCurrentScale; }
-
-  /**
-   * Retrieve the value of currentScale, currentTranslate.x or
-   * currentTranslate.y prior to the last change made to any one of them.
-   */
-  const SVGPoint& GetPreviousTranslate() { return mPreviousTranslate; }
-  float GetPreviousScale() { return mPreviousScale; }
-
-  nsSMILTimeContainer* GetTimedDocumentRoot();
-
   // nsIContent interface
-  NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override;
   virtual nsresult GetEventTargetParent(
                      EventChainPreVisitor& aVisitor) override;
-
   virtual bool IsEventAttributeName(nsIAtom* aName) override;
 
-  // nsSVGElement specializations:
-  virtual gfxMatrix PrependLocalTransformsTo(
-    const gfxMatrix &aMatrix,
-    SVGTransformTypes aWhich = eAllTransforms) const override;
-  virtual nsSVGAnimatedTransformList*
-	  GetAnimatedTransformList(uint32_t aFlags = 0) override;
-  virtual bool HasValidDimensions() const override;
-
-  // SVGSVGElement methods:
-  float GetLength(uint8_t mCtxType);
-
-  // public helpers:
-
-  /**
-   * Returns -1 if the width/height is a percentage, else returns the user unit
-   * length clamped to fit in a int32_t.
-   * XXX see bug 1112533 comment 3 - we should fix drawImage so that we can
-   * change these methods to make zero the error flag for percentages.
-   */
-  int32_t GetIntrinsicWidth();
-  int32_t GetIntrinsicHeight();
-
-  /**
-   * Returns true if this element has a base/anim value for its "viewBox"
-   * attribute that defines a viewBox rectangle with finite values, or
-   * if there is a view element overriding this element's viewBox and it
-   * has a valid viewBox.
-   *
-   * Note that this does not check whether we need to synthesize a viewBox,
-   * so you must call ShouldSynthesizeViewBox() if you need to check that too.
-   *
-   * Note also that this method does not pay attention to whether the width or
-   * height values of the viewBox rect are positive!
-   */
-  bool HasViewBoxRect() const;
-
-  /**
-   * Returns true if we should synthesize a viewBox for ourselves (that is, if
-   * we're the root element in an image document, and we're not currently being
-   * painted for an <svg:image> element).
-   *
-   * Only call this method if HasViewBoxRect() returns false.
-   */
-  bool ShouldSynthesizeViewBox() const;
-
-  bool HasViewBoxOrSyntheticViewBox() const {
-    return HasViewBoxRect() || ShouldSynthesizeViewBox();
-  }
-
-  gfx::Matrix GetViewBoxTransform() const;
-
-  bool HasChildrenOnlyTransform() const {
-    return mHasChildrenOnlyTransform;
-  }
-
-  void UpdateHasChildrenOnlyTransform();
-
-  enum ChildrenOnlyTransformChangedFlags {
-    eDuringReflow = 1
-  };
-
-  /**
-   * This method notifies the style system that the overflow rects of our
-   * immediate childrens' frames need to be updated. It is called by our own
-   * frame when changes (e.g. to currentScale) cause our children-only
-   * transform to change.
-   *
-   * The reason we have this method instead of overriding
-   * GetAttributeChangeHint is because we need to act on non-attribute (e.g.
-   * currentScale) changes in addition to attribute (e.g. viewBox) changes.
-   */
-  void ChildrenOnlyTransformChanged(uint32_t aFlags = 0);
-
-  // This services any pending notifications for the transform on on this root
-  // <svg> node needing to be recalculated.  (Only applicable in
-  // SVG-as-an-image documents.)
-  virtual void FlushImageTransformInvalidation();
-
+  // nsINode methods:
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
-  // Returns true IFF our attributes are currently overridden by a <view>
-  // element and that element's ID matches the passed-in string.
-  bool IsOverriddenBy(const nsAString &aViewID) const {
-    return mCurrentViewID && mCurrentViewID->Equals(aViewID);
-  }
-
-  svgFloatSize GetViewportSize() const {
-    return svgFloatSize(mViewportWidth, mViewportHeight);
-  }
-
-  void SetViewportSize(const svgFloatSize& aSize) {
-    mViewportWidth  = aSize.width;
-    mViewportHeight = aSize.height;
-  }
-
   // WebIDL
   already_AddRefed<SVGAnimatedLength> X();
   already_AddRefed<SVGAnimatedLength> Y();
   already_AddRefed<SVGAnimatedLength> Width();
   already_AddRefed<SVGAnimatedLength> Height();
   float PixelUnitToMillimeterX();
   float PixelUnitToMillimeterY();
   float ScreenPixelToMillimeterX();
@@ -285,64 +139,72 @@ public:
   already_AddRefed<DOMSVGLength> CreateSVGLength();
   already_AddRefed<SVGAngle> CreateSVGAngle();
   already_AddRefed<nsISVGPoint> CreateSVGPoint();
   already_AddRefed<SVGMatrix> CreateSVGMatrix();
   already_AddRefed<SVGIRect> CreateSVGRect();
   already_AddRefed<SVGTransform> CreateSVGTransform();
   already_AddRefed<SVGTransform> CreateSVGTransformFromMatrix(SVGMatrix& matrix);
   using nsINode::GetElementById; // This does what we want
-  already_AddRefed<SVGAnimatedRect> ViewBox();
-  already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
   uint16_t ZoomAndPan();
   void SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv);
-  virtual nsSVGViewBox* GetViewBox() override;
 
-private:
   // nsSVGElement overrides
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers) override;
   virtual void UnbindFromTree(bool aDeep, bool aNullParent) override;
+  virtual nsSVGAnimatedTransformList*
+    GetAnimatedTransformList(uint32_t aFlags = 0) override;
+
+  // SVGSVGElement methods:
+
+  // Returns true IFF our attributes are currently overridden by a <view>
+  // element and that element's ID matches the passed-in string.
+  bool IsOverriddenBy(const nsAString &aViewID) const {
+    return mCurrentViewID && mCurrentViewID->Equals(aViewID);
+  }
+
+  nsSMILTimeContainer* GetTimedDocumentRoot();
+
+  // public helpers:
+
+  /**
+   * Returns -1 if the width/height is a percentage, else returns the user unit
+   * length clamped to fit in a int32_t.
+   * XXX see bug 1112533 comment 3 - we should fix drawImage so that we can
+   * change these methods to make zero the error flag for percentages.
+   */
+  int32_t GetIntrinsicWidth();
+  int32_t GetIntrinsicHeight();
+
+  // This services any pending notifications for the transform on on this root
+  // <svg> node needing to be recalculated.  (Only applicable in
+  // SVG-as-an-image documents.)
+  virtual void FlushImageTransformInvalidation();
+
+  svgFloatSize GetViewportSize() const {
+    return svgFloatSize(mViewportWidth, mViewportHeight);
+  }
+
+  void SetViewportSize(const svgFloatSize& aSize) {
+    mViewportWidth  = aSize.width;
+    mViewportHeight = aSize.height;
+  }
+
+private:
+  // SVGViewportElement methods:
+
+  virtual SVGViewElement* GetCurrentViewElement() const;
+  virtual SVGPreserveAspectRatio GetPreserveAspectRatioWithOverride() const override;
 
   // implementation helpers:
 
-  SVGViewElement* GetCurrentViewElement() const;
-
-  // Methods for <image> elements to override my "PreserveAspectRatio" value.
-  // These are private so that only our friends
-  // (AutoPreserveAspectRatioOverride in particular) have access.
-  void SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR);
-  void ClearImageOverridePreserveAspectRatio();
-
-  // Set/Clear properties to hold old version of preserveAspectRatio
-  // when it's being overridden by an <image> element that we are inside of.
-  bool SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR);
-  const SVGPreserveAspectRatio* GetPreserveAspectRatioProperty() const;
-  bool ClearPreserveAspectRatioProperty();
-
-  bool IsRoot() const {
-    NS_ASSERTION((IsInUncomposedDoc() && !GetParent()) ==
-                 (OwnerDoc() && (OwnerDoc()->GetRootElement() == this)),
-                 "Can't determine if we're root");
-    return IsInUncomposedDoc() && !GetParent();
-  }
-
-  /**
-   * Returns true if this is an SVG <svg> element that is the child of
-   * another non-foreignObject SVG element.
-   */
-  bool IsInner() const {
-    const nsIContent *parent = GetFlattenedTreeParent();
-    return parent && parent->IsSVGElement() &&
-           !parent->IsSVGElement(nsGkAtoms::foreignObject);
-  }
-
-  /* 
+  /*
    * While binding to the tree we need to determine if we will be the outermost
    * <svg> element _before_ the children are bound (as they want to know what
    * timed document root to register with) and therefore _before_ our parent is
    * set (both actions are performed by Element::BindToTree) so we
    * can't use GetOwnerSVGElement() as it relies on GetParent(). This code is
    * basically a simplified version of GetOwnerSVGElement that uses the parent
    * parameters passed in instead.
    */
@@ -352,62 +214,43 @@ private:
   // invalidate viewbox -> viewport xform & inform frames
   void InvalidateTransformNotifyFrame();
 
   // Returns true if we have at least one of the following:
   // - a (valid or invalid) value for the preserveAspectRatio attribute
   // - a SMIL-animated value for the preserveAspectRatio attribute
   bool HasPreserveAspectRatio();
 
- /**
-  * Returns the explicit viewBox rect, if specified, or else a synthesized
-  * viewBox, if appropriate, or else a viewBox matching the dimensions of the
-  * SVG viewport.
-  */
-  nsSVGViewBoxRect GetViewBoxWithSynthesis(
-      float aViewportWidth, float aViewportHeight) const;
-  /**
-   * Returns the explicit or default preserveAspectRatio, unless we're
-   * synthesizing a viewBox, in which case it returns the "none" value.
-   */
-  SVGPreserveAspectRatio GetPreserveAspectRatioWithOverride() const;
+  // Methods for <image> elements to override my "PreserveAspectRatio" value.
+  // These are private so that only our friends
+  // (AutoPreserveAspectRatioOverride in particular) have access.
+  void SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR);
+  void ClearImageOverridePreserveAspectRatio();
 
-  virtual LengthAttributesInfo GetLengthInfo() override;
+  // Set/Clear properties to hold old version of preserveAspectRatio
+  // when it's being overridden by an <image> element that we are inside of.
+  bool SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR);
+  const SVGPreserveAspectRatio* GetPreserveAspectRatioProperty() const;
+  bool ClearPreserveAspectRatioProperty();
 
-  enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT };
-  nsSVGLength2 mLengthAttributes[4];
-  static LengthInfo sLengthInfo[4];
+  virtual SVGPoint GetCurrentTranslate() const override
+  { return mCurrentTranslate; }
+  virtual float GetCurrentScale() const override
+  { return mCurrentScale; }
+
+  virtual const nsSVGViewBox& GetViewBoxInternal() const override;
+  virtual nsSVGAnimatedTransformList* GetTransformInternal() const override;
 
   virtual EnumAttributesInfo GetEnumInfo() override;
 
   enum { ZOOMANDPAN };
   nsSVGEnum mEnumAttributes[1];
   static nsSVGEnumMapping sZoomAndPanMap[];
   static EnumInfo sEnumInfo[1];
 
-  virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override;
-
-  nsSVGViewBox                   mViewBox;
-  SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
-
-  // mCurrentViewID and mSVGView are mutually exclusive; we can have
-  // at most one non-null.
-  nsAutoPtr<nsString>            mCurrentViewID;
-  nsAutoPtr<SVGView>             mSVGView;
-
-  // The size of the rectangular SVG viewport into which we render. This is
-  // not (necessarily) the same as the content area. See:
-  //
-  //   http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
-  //
-  // XXXjwatt Currently only used for outer <svg>, but maybe we could use -1 to
-  // flag this as an inner <svg> to save the overhead of GetCtx calls?
-  // XXXjwatt our frame should probably reset these when it's destroyed.
-  float mViewportWidth, mViewportHeight;
-
   // The time container for animations within this SVG document fragment. Set
   // for all outermost <svg> elements (not nested <svg> elements).
   nsAutoPtr<nsSMILTimeContainer> mTimedDocumentRoot;
 
   // zoom and pan
   // IMPORTANT: see the comment in RecordCurrentScaleTranslate before writing
   // code to change any of these!
   SVGPoint mCurrentTranslate;
@@ -415,33 +258,62 @@ private:
   SVGPoint mPreviousTranslate;
   float    mPreviousScale;
 
   // For outermost <svg> elements created from parsing, animation is started by
   // the onload event in accordance with the SVG spec, but for <svg> elements
   // created by script or promoted from inner <svg> to outermost <svg> we need
   // to manually kick off animation when they are bound to the tree.
   bool     mStartAnimationOnBindToTree;
+
   bool     mImageNeedsTransformInvalidation;
-  bool     mHasChildrenOnlyTransform;
+
+  // mCurrentViewID and mSVGView are mutually exclusive; we can have
+  // at most one non-null.
+  nsAutoPtr<nsString>            mCurrentViewID;
+  nsAutoPtr<SVGView>             mSVGView;
 };
 
 } // namespace dom
 
+class MOZ_RAII AutoSVGTimeSetRestore
+{
+public:
+  AutoSVGTimeSetRestore(dom::SVGSVGElement* aRootElem,
+                        float aFrameTime
+                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    : mRootElem(aRootElem)
+    , mOriginalTime(mRootElem->GetCurrentTime())
+  {
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    mRootElem->SetCurrentTime(aFrameTime); // Does nothing if there's no change.
+  }
+
+  ~AutoSVGTimeSetRestore()
+  {
+    mRootElem->SetCurrentTime(mOriginalTime);
+  }
+
+private:
+  const RefPtr<dom::SVGSVGElement> mRootElem;
+  const float mOriginalTime;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
 class MOZ_RAII AutoPreserveAspectRatioOverride
 {
 public:
   AutoPreserveAspectRatioOverride(const Maybe<SVGImageContext>& aSVGContext,
                                   dom::SVGSVGElement* aRootElem
                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mRootElem(aRootElem)
     , mDidOverride(false)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    MOZ_ASSERT(mRootElem, "No SVG node to manage?");
+    MOZ_ASSERT(mRootElem, "No SVG/Symbol node to manage?");
 
     if (aSVGContext.isSome() &&
         aSVGContext->GetPreserveAspectRatio().isSome()) {
       // Override preserveAspectRatio in our helper document.
       // XXXdholbert We should technically be overriding the helper doc's clip
       // and overflow properties here, too. See bug 272288 comment 36.
       mRootElem->SetImageOverridePreserveAspectRatio(
                    *aSVGContext->GetPreserveAspectRatio());
@@ -457,35 +329,11 @@ public:
   }
 
 private:
   const RefPtr<dom::SVGSVGElement> mRootElem;
   bool mDidOverride;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-class MOZ_RAII AutoSVGTimeSetRestore
-{
-public:
-  AutoSVGTimeSetRestore(dom::SVGSVGElement* aRootElem,
-                        float aFrameTime
-                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-    : mRootElem(aRootElem)
-    , mOriginalTime(mRootElem->GetCurrentTime())
-  {
-    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    mRootElem->SetCurrentTime(aFrameTime); // Does nothing if there's no change.
-  }
-
-  ~AutoSVGTimeSetRestore()
-  {
-    mRootElem->SetCurrentTime(mOriginalTime);
-  }
-
-private:
-  const RefPtr<dom::SVGSVGElement> mRootElem;
-  const float mOriginalTime;
-  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 } // namespace mozilla
 
 #endif // SVGSVGElement_h
--- a/dom/svg/SVGSymbolElement.cpp
+++ b/dom/svg/SVGSymbolElement.cpp
@@ -38,72 +38,18 @@ SVGSymbolElement::~SVGSymbolElement()
 }
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSymbolElement)
 
 //----------------------------------------------------------------------
-
-already_AddRefed<SVGAnimatedRect>
-SVGSymbolElement::ViewBox()
-{
-  return mViewBox.ToSVGAnimatedRect(this);
-}
-
-already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
-SVGSymbolElement::PreserveAspectRatio()
-{
-  return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);
-}
-
-//----------------------------------------------------------------------
-// nsIContent methods
-
-NS_IMETHODIMP_(bool)
-SVGSymbolElement::IsAttributeMapped(const nsIAtom* name) const
-{
-  static const MappedAttributeEntry* const map[] = {
-    sColorMap,
-    sFEFloodMap,
-    sFillStrokeMap,
-    sFiltersMap,
-    sFontSpecificationMap,
-    sGradientStopMap,
-    sGraphicsMap,
-    sLightingEffectsMap,
-    sMarkersMap,
-    sTextContentElementsMap,
-    sViewportsMap
-   };
-
-  return FindAttributeDependence(name, map) ||
-    SVGSymbolElementBase::IsAttributeMapped(name);
-}
-
-//----------------------------------------------------------------------
 // SVGTests methods
 
 bool
 SVGSymbolElement::IsInChromeDoc() const
 {
   return nsContentUtils::IsChromeDoc(OwnerDoc());
 }
 
-
-//----------------------------------------------------------------------
-// nsSVGElement methods
-
-nsSVGViewBox *
-SVGSymbolElement::GetViewBox()
-{
-  return &mViewBox;
-}
-
-SVGAnimatedPreserveAspectRatio *
-SVGSymbolElement::GetPreserveAspectRatio()
-{
-  return &mPreserveAspectRatio;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/svg/SVGSymbolElement.h
+++ b/dom/svg/SVGSymbolElement.h
@@ -2,61 +2,42 @@
 /* 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_SVGSymbolElement_h
 #define mozilla_dom_SVGSymbolElement_h
 
-#include "mozilla/dom/SVGTests.h"
-#include "nsSVGElement.h"
-#include "nsSVGViewBox.h"
-#include "SVGAnimatedPreserveAspectRatio.h"
+#include "SVGViewportElement.h"
 
 nsresult NS_NewSVGSymbolElement(nsIContent **aResult,
                                 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
-typedef nsSVGElement SVGSymbolElementBase;
+typedef SVGViewportElement SVGSymbolElementBase;
 
-class SVGSymbolElement final : public SVGSymbolElementBase,
-                               public SVGTests
+class SVGSymbolElement final : public SVGSymbolElementBase
 {
 protected:
   friend nsresult (::NS_NewSVGSymbolElement(nsIContent **aResult,
                                             already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
   explicit SVGSymbolElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   ~SVGSymbolElement();
   virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
 
 public:
   // interfaces:
-
   NS_DECL_ISUPPORTS_INHERITED
 
-  // nsIContent interface
-  NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override;
-
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
-  // WebIDL
-  already_AddRefed<SVGAnimatedRect> ViewBox();
-  already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
-
   // SVGTests
   bool IsInChromeDoc() const override;
-
-protected:
-  virtual nsSVGViewBox *GetViewBox() override;
-  virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override;
-
-  nsSVGViewBox mViewBox;
-  SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_SVGSymbolElement_h
--- a/dom/svg/SVGUseElement.cpp
+++ b/dom/svg/SVGUseElement.cpp
@@ -270,61 +270,16 @@ SVGUseElement::CreateAnonymousContent()
   nsNodeUtils::Clone(targetContent, true, nodeInfoManager, unused,
                      getter_AddRefs(newnode));
 
   nsCOMPtr<nsIContent> newcontent = do_QueryInterface(newnode);
 
   if (!newcontent)
     return nullptr;
 
-  if (newcontent->IsSVGElement(nsGkAtoms::symbol)) {
-    nsIDocument *document = GetComposedDoc();
-    if (!document)
-      return nullptr;
-
-    nsNodeInfoManager *nodeInfoManager = document->NodeInfoManager();
-    if (!nodeInfoManager)
-      return nullptr;
-
-    RefPtr<mozilla::dom::NodeInfo> nodeInfo;
-    nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::svg, nullptr,
-                                            kNameSpaceID_SVG,
-                                            nsIDOMNode::ELEMENT_NODE);
-
-    nsCOMPtr<nsIContent> svgNode;
-    NS_NewSVGSVGElement(getter_AddRefs(svgNode), nodeInfo.forget(),
-                        NOT_FROM_PARSER);
-
-    if (!svgNode)
-      return nullptr;
-
-    // copy attributes
-    BorrowedAttrInfo info;
-    uint32_t i;
-    for (i = 0; (info = newcontent->GetAttrInfoAt(i)); i++) {
-      nsAutoString value;
-      int32_t nsID = info.mName->NamespaceID();
-      nsIAtom* lname = info.mName->LocalName();
-
-      info.mValue->ToString(value);
-
-      svgNode->SetAttr(nsID, lname, info.mName->GetPrefix(), value, false);
-    }
-
-    // move the children over
-    uint32_t num = newcontent->GetChildCount();
-    for (i = 0; i < num; i++) {
-      nsCOMPtr<nsIContent> child = newcontent->GetFirstChild();
-      newcontent->RemoveChildAt(0, false);
-      svgNode->InsertChildAt(child, i, true);
-    }
-
-    newcontent = svgNode;
-  }
-
   if (newcontent->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol)) {
     nsSVGElement *newElement = static_cast<nsSVGElement*>(newcontent.get());
 
     if (mLengthAttributes[ATTR_WIDTH].IsExplicitlySet())
       newElement->SetLength(nsGkAtoms::width, mLengthAttributes[ATTR_WIDTH]);
     if (mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet())
       newElement->SetLength(nsGkAtoms::height, mLengthAttributes[ATTR_HEIGHT]);
   }
--- a/dom/svg/SVGViewElement.h
+++ b/dom/svg/SVGViewElement.h
@@ -23,23 +23,24 @@ class nsSVGOuterSVGFrame;
 
 nsresult NS_NewSVGViewElement(nsIContent **aResult,
                               already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 class SVGFragmentIdentifier;
 
 namespace dom {
-class SVGSVGElement;
+class SVGViewportElement;
 
 class SVGViewElement : public SVGViewElementBase
 {
 protected:
   friend class mozilla::SVGFragmentIdentifier;
   friend class SVGSVGElement;
+  friend class SVGViewportElement;
   friend class ::nsSVGOuterSVGFrame;
   explicit SVGViewElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   friend nsresult (::NS_NewSVGViewElement(nsIContent **aResult,
                                           already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
   virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
 
 public:
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
new file mode 100644
--- /dev/null
+++ b/dom/svg/SVGViewportElement.cpp
@@ -0,0 +1,374 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h>
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/ContentEvents.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/Likely.h"
+#include "mozilla/dom/SVGMatrix.h"
+#include "mozilla/dom/SVGViewportElement.h"
+#include "mozilla/dom/SVGViewElement.h"
+
+#include "DOMSVGLength.h"
+#include "DOMSVGPoint.h"
+#include "nsCOMPtr.h"
+#include "nsContentUtils.h"
+#include "nsFrameSelection.h"
+#include "nsError.h"
+#include "nsGkAtoms.h"
+#include "nsIDocument.h"
+#include "nsIFrame.h"
+#include "nsIPresShell.h"
+#include "nsISVGSVGFrame.h" //XXX
+#include "nsLayoutUtils.h"
+#include "nsStyleUtil.h"
+#include "nsSMILTypes.h"
+#include "SVGContentUtils.h"
+
+#include <algorithm>
+#include "prtime.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace dom {
+
+nsSVGElement::LengthInfo SVGViewportElement::sLengthInfo[4] =
+{
+  { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
+  { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
+  { &nsGkAtoms::width, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X },
+  { &nsGkAtoms::height, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y },
+};
+
+//----------------------------------------------------------------------
+// Implementation
+
+SVGViewportElement::SVGViewportElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+  : SVGGraphicsElement(aNodeInfo),
+    mViewportWidth(0),
+    mViewportHeight(0),
+    mHasChildrenOnlyTransform(false)
+{
+}
+
+SVGViewportElement::~SVGViewportElement()
+{
+}
+
+//----------------------------------------------------------------------
+
+already_AddRefed<SVGAnimatedRect>
+SVGViewportElement::ViewBox()
+{
+  return mViewBox.ToSVGAnimatedRect(this);
+}
+
+already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
+SVGViewportElement::PreserveAspectRatio()
+{
+  return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);
+}
+
+//----------------------------------------------------------------------
+// nsIContent methods
+
+NS_IMETHODIMP_(bool)
+SVGViewportElement::IsAttributeMapped(const nsIAtom* name) const
+{
+  // We want to map the 'width' and 'height' attributes into style for
+  // outer-<svg>, except when the attributes aren't set (since their default
+  // values of '100%' can cause unexpected and undesirable behaviour for SVG
+  // inline in HTML). We rely on nsSVGElement::UpdateContentStyleRule() to
+  // prevent mapping of the default values into style (it only maps attributes
+  // that are set). We also rely on a check in nsSVGElement::
+  // UpdateContentStyleRule() to prevent us mapping the attributes when they're
+  // given a <length> value that is not currently recognized by the SVG
+  // specification.
+
+  if (!IsInner() && (name == nsGkAtoms::width || name == nsGkAtoms::height)) {
+    return true;
+  }
+
+  static const MappedAttributeEntry* const map[] = {
+    sColorMap,
+    sFEFloodMap,
+    sFillStrokeMap,
+    sFiltersMap,
+    sFontSpecificationMap,
+    sGradientStopMap,
+    sGraphicsMap,
+    sLightingEffectsMap,
+    sMarkersMap,
+    sTextContentElementsMap,
+    sViewportsMap
+  };
+
+  return FindAttributeDependence(name, map) ||
+    SVGGraphicsElement::IsAttributeMapped(name);
+}
+
+//----------------------------------------------------------------------
+// nsSVGElement overrides
+
+// Helper for GetViewBoxTransform on root <svg> node
+// * aLength: internal value for our <svg> width or height attribute.
+// * aViewportLength: length of the corresponding dimension of the viewport.
+// * aSelf: the outermost <svg> node itself.
+// NOTE: aSelf is not an ancestor viewport element, so it can't be used to
+// resolve percentage lengths. (It can only be used to resolve
+// 'em'/'ex'-valued units).
+inline float
+ComputeSynthesizedViewBoxDimension(const nsSVGLength2& aLength,
+                                   float aViewportLength,
+                                   const SVGViewportElement* aSelf)
+{
+  if (aLength.IsPercentage()) {
+    return aViewportLength * aLength.GetAnimValInSpecifiedUnits() / 100.0f;
+  }
+
+  return aLength.GetAnimValue(const_cast<SVGViewportElement*>(aSelf));
+}
+
+//----------------------------------------------------------------------
+// public helpers:
+
+void
+SVGViewportElement::UpdateHasChildrenOnlyTransform()
+{
+  bool hasChildrenOnlyTransform =
+    HasViewBoxOrSyntheticViewBox() ||
+    (IsRoot() && (GetCurrentTranslate() != SVGPoint(0.0f, 0.0f) ||
+                  GetCurrentScale() != 1.0f));
+  mHasChildrenOnlyTransform = hasChildrenOnlyTransform;
+}
+
+void
+SVGViewportElement::ChildrenOnlyTransformChanged(uint32_t aFlags)
+{
+  // Avoid wasteful calls:
+  MOZ_ASSERT(!(GetPrimaryFrame()->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
+             "Non-display SVG frames don't maintain overflow rects");
+
+  nsChangeHint changeHint;
+
+  bool hadChildrenOnlyTransform = mHasChildrenOnlyTransform;
+
+  UpdateHasChildrenOnlyTransform();
+
+  if (hadChildrenOnlyTransform != mHasChildrenOnlyTransform) {
+    // Reconstruct the frame tree to handle stacking context changes:
+    // XXXjwatt don't do this for root-<svg> or even outer-<svg>?
+    changeHint = nsChangeHint_ReconstructFrame;
+  } else {
+    // We just assume the old and new transforms are different.
+    changeHint = nsChangeHint(nsChangeHint_UpdateOverflow |
+                              nsChangeHint_ChildrenOnlyTransform);
+  }
+
+  // If we're not reconstructing the frame tree, then we only call
+  // PostRestyleEvent if we're not being called under reflow to avoid recursing
+  // to death. See bug 767056 comments 10 and 12. Since our nsSVGOuterSVGFrame
+  // is being reflowed we're going to invalidate and repaint its entire area
+  // anyway (which will include our children).
+  if ((changeHint & nsChangeHint_ReconstructFrame) ||
+      !(aFlags & eDuringReflow)) {
+    nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint);
+  }
+}
+
+gfx::Matrix
+SVGViewportElement::GetViewBoxTransform() const
+{
+  float viewportWidth, viewportHeight;
+  if (IsInner()) {
+    SVGViewportElement *ctx = GetCtx();
+    viewportWidth = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
+    viewportHeight = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx);
+  } else {
+    viewportWidth = mViewportWidth;
+    viewportHeight = mViewportHeight;
+  }
+
+  if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
+    return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
+  }
+
+  nsSVGViewBoxRect viewBox =
+    GetViewBoxWithSynthesis(viewportWidth, viewportHeight);
+
+  if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
+    return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
+  }
+
+  return SVGContentUtils::GetViewBoxTransform(viewportWidth, viewportHeight,
+                                              viewBox.x, viewBox.y,
+                                              viewBox.width, viewBox.height,
+                                              GetPreserveAspectRatioWithOverride());
+}
+//----------------------------------------------------------------------
+// SVGViewportElement
+
+float
+SVGViewportElement::GetLength(uint8_t aCtxType)
+{
+  const nsSVGViewBoxRect* viewbox =
+    GetViewBoxInternal().HasRect() ? &GetViewBoxInternal().GetAnimValue()
+                                   : nullptr;
+
+  float h, w;
+  if (viewbox) {
+    w = viewbox->width;
+    h = viewbox->height;
+  } else if (IsInner()) {
+    SVGViewportElement *ctx = GetCtx();
+    w = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
+    h = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx);
+  } else if (ShouldSynthesizeViewBox()) {
+    w = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
+                                           mViewportWidth, this);
+    h = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
+                                           mViewportHeight, this);
+  } else {
+    w = mViewportWidth;
+    h = mViewportHeight;
+  }
+
+  w = std::max(w, 0.0f);
+  h = std::max(h, 0.0f);
+
+  switch (aCtxType) {
+  case SVGContentUtils::X:
+    return w;
+  case SVGContentUtils::Y:
+    return h;
+  case SVGContentUtils::XY:
+    return float(SVGContentUtils::ComputeNormalizedHypotenuse(w, h));
+  }
+  return 0;
+}
+
+//----------------------------------------------------------------------
+// nsSVGElement methods
+
+/* virtual */ gfxMatrix
+SVGViewportElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
+                                        SVGTransformTypes aWhich) const
+{
+  // 'transform' attribute (or an override from a fragment identifier):
+  gfxMatrix userToParent;
+
+  if (aWhich == eUserSpaceToParent || aWhich == eAllTransforms) {
+    userToParent = GetUserToParentTransform(mAnimateMotionTransform,
+                                            GetTransformInternal());
+    if (aWhich == eUserSpaceToParent) {
+      return userToParent * aMatrix;
+    }
+  }
+
+  gfxMatrix childToUser;
+
+  if (IsInner()) {
+    float x, y;
+    const_cast<SVGViewportElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
+    childToUser = ThebesMatrix(GetViewBoxTransform().PostTranslate(x, y));
+  } else if (IsRoot()) {
+    SVGPoint translate = GetCurrentTranslate();
+    float scale = GetCurrentScale();
+    childToUser = ThebesMatrix(GetViewBoxTransform()
+                                 .PostScale(scale, scale)
+                                 .PostTranslate(translate.GetX(),
+                                                translate.GetY()));
+  } else {
+    // outer-<svg>, but inline in some other content:
+    childToUser = ThebesMatrix(GetViewBoxTransform());
+  }
+
+  if (aWhich == eAllTransforms) {
+    return childToUser * userToParent * aMatrix;
+  }
+
+  MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
+
+  // The following may look broken because pre-multiplying our eChildToUserSpace
+  // transform with another matrix without including our eUserSpaceToParent
+  // transform between the two wouldn't make sense.  We don't expect that to
+  // ever happen though.  We get here either when the identity matrix has been
+  // passed because our caller just wants our eChildToUserSpace transform, or
+  // when our eUserSpaceToParent transform has already been multiplied into the
+  // matrix that our caller passes (such as when we're called from PaintSVG).
+  return childToUser * aMatrix;
+}
+
+/* virtual */ bool
+SVGViewportElement::HasValidDimensions() const
+{
+  return !IsInner() ||
+    ((!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
+       mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
+     (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
+       mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0));
+}
+
+
+
+nsSVGViewBox*
+SVGViewportElement::GetViewBox()
+{
+  return &mViewBox;
+}
+
+SVGAnimatedPreserveAspectRatio *
+SVGViewportElement::GetPreserveAspectRatio()
+{
+  return &mPreserveAspectRatio;
+}
+
+bool
+SVGViewportElement::ShouldSynthesizeViewBox() const
+{
+  MOZ_ASSERT(!HasViewBoxRect(), "Should only be called if we lack a viewBox");
+
+  return IsRoot() && OwnerDoc()->IsBeingUsedAsImage();
+}
+
+//----------------------------------------------------------------------
+// implementation helpers
+
+nsSVGViewBoxRect
+SVGViewportElement::GetViewBoxWithSynthesis(
+  float aViewportWidth, float aViewportHeight) const
+{
+  if (GetViewBoxInternal().HasRect()) {
+    return GetViewBoxInternal().GetAnimValue();
+  }
+
+  if (ShouldSynthesizeViewBox()) {
+    // Special case -- fake a viewBox, using height & width attrs.
+    // (Use |this| as context, since if we get here, we're outermost <svg>.)
+    return nsSVGViewBoxRect(0, 0,
+              ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
+                                                 mViewportWidth, this),
+              ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
+                                                 mViewportHeight, this));
+
+  }
+
+  // No viewBox attribute, so we shouldn't auto-scale. This is equivalent
+  // to having a viewBox that exactly matches our viewport size.
+  return nsSVGViewBoxRect(0, 0, aViewportWidth, aViewportHeight);
+}
+
+nsSVGElement::LengthAttributesInfo
+SVGViewportElement::GetLengthInfo()
+{
+  return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
+                              ArrayLength(sLengthInfo));
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/svg/SVGViewportElement.h
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_SVGViewportElement_h
+#define mozilla_dom_SVGViewportElement_h
+
+#include "mozilla/dom/FromParser.h"
+#include "nsAutoPtr.h"
+#include "nsIContentInlines.h"
+#include "nsISVGPoint.h"
+#include "nsSVGEnum.h"
+#include "nsSVGLength2.h"
+#include "SVGGraphicsElement.h"
+#include "SVGImageContext.h"
+#include "nsSVGViewBox.h"
+#include "SVGPreserveAspectRatio.h"
+#include "SVGAnimatedPreserveAspectRatio.h"
+#include "mozilla/Attributes.h"
+
+class nsSVGOuterSVGFrame;
+class nsSVGViewportFrame;
+
+namespace mozilla {
+class AutoPreserveAspectRatioOverride;
+class DOMSVGAnimatedPreserveAspectRatio;
+
+namespace dom {
+class SVGAnimatedRect;
+class SVGTransform;
+class SVGViewElement;
+class SVGViewportElement;
+
+class svgFloatSize {
+public:
+  svgFloatSize(float aWidth, float aHeight)
+    : width(aWidth)
+    , height(aHeight)
+  {}
+  bool operator!=(const svgFloatSize& rhs) {
+    return width != rhs.width || height != rhs.height;
+  }
+  float width;
+  float height;
+};
+
+class SVGViewportElement : public SVGGraphicsElement
+{
+  friend class ::nsSVGOuterSVGFrame;
+  friend class ::nsSVGViewportFrame;
+
+protected:
+
+  SVGViewportElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
+  ~SVGViewportElement();
+
+public:
+
+  // nsIContent interface
+  NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override;
+
+  // nsSVGElement specializations:
+  virtual gfxMatrix PrependLocalTransformsTo(
+    const gfxMatrix &aMatrix,
+    SVGTransformTypes aWhich = eAllTransforms) const override;
+
+  virtual bool HasValidDimensions() const override;
+
+  // SVGViewportElement methods:
+
+  float GetLength(uint8_t mCtxType);
+
+  // public helpers:
+
+  /**
+   * Returns true if this element has a base/anim value for its "viewBox"
+   * attribute that defines a viewBox rectangle with finite values, or
+   * if there is a view element overriding this element's viewBox and it
+   * has a valid viewBox.
+   *
+   * Note that this does not check whether we need to synthesize a viewBox,
+   * so you must call ShouldSynthesizeViewBox() if you need to chck that too.
+   *
+   * Note also that this method does not pay attention to whether the width or
+   * height values of the viewBox rect are positive!
+   */
+  bool HasViewBoxRect() const {
+    return GetViewBoxInternal().HasRect();
+  }
+
+  /**
+   * Returns true if we should synthesize a viewBox for ourselves (that is, if
+   * we're the root element in an image document, and we're not currently being
+   * painted for an <svg:image> element).
+   *
+   * Only call this method if HasViewBoxRect() returns false.
+   */
+  bool ShouldSynthesizeViewBox() const;
+
+  bool HasViewBoxOrSyntheticViewBox() const {
+    return HasViewBoxRect() || ShouldSynthesizeViewBox();
+  }
+
+  bool HasChildrenOnlyTransform() const {
+    return mHasChildrenOnlyTransform;
+  }
+
+  void UpdateHasChildrenOnlyTransform();
+
+  enum ChildrenOnlyTransformChangedFlags {
+    eDuringReflow = 1
+  };
+
+  /**
+   * This method notifies the style system that the overflow rects of our
+   * immediate childrens' frames need to be updated. It is called by our own
+   * frame when changes (e.g. to currentScale) cause our children-only
+   * transform to change.
+   *
+   * The reason we have this method instead of overriding
+   * GetAttributeChangeHint is because we need to act on non-attribute (e.g.
+   * currentScale) changes in addition to attribute (e.g. viewBox) changes.
+   */
+  void ChildrenOnlyTransformChanged(uint32_t aFlags = 0);
+
+  gfx::Matrix GetViewBoxTransform() const;
+
+  // WebIDL
+  already_AddRefed<SVGAnimatedRect> ViewBox();
+  already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
+  virtual nsSVGViewBox* GetViewBox() override;
+
+protected:
+
+  // implementation helpers:
+
+  bool IsRoot() const {
+    NS_ASSERTION((IsInUncomposedDoc() && !GetParent()) ==
+                 (OwnerDoc() && (OwnerDoc()->GetRootElement() == this)),
+                 "Can't determine if we're root");
+    return IsInUncomposedDoc() && !GetParent();
+  }
+
+  /**
+   * Returns true if either this is an SVG <svg> element that is the child of
+   * another non-foreignObject SVG element, or this is a SVG <symbol> element
+   * this is the root of a use-element shadow tree.
+   */
+  bool IsInner() const {
+    const nsIContent *parent = GetFlattenedTreeParent();
+    return parent && parent->IsSVGElement() &&
+           !parent->IsSVGElement(nsGkAtoms::foreignObject);
+  }
+
+  /**
+   * Returns the explicit or default preserveAspectRatio, unless we're
+   * synthesizing a viewBox, in which case it returns the "none" value.
+   */
+  virtual SVGPreserveAspectRatio GetPreserveAspectRatioWithOverride() const {
+    return mPreserveAspectRatio.GetAnimValue();
+  }
+
+  /**
+   * Returns the explicit viewBox rect, if specified, or else a synthesized
+   * viewBox, if appropriate, or else a viewBox matching the dimensions of the
+   * SVG viewport.
+   */
+  nsSVGViewBoxRect GetViewBoxWithSynthesis(
+      float aViewportWidth, float aViewportHeight) const;
+
+  /**
+   * Retrieve the value of currentScale and currentTranslate.
+   */
+  virtual SVGPoint GetCurrentTranslate() const
+  { return SVGPoint(0.0f, 0.0f); }
+  virtual float GetCurrentScale() const
+  { return 1.0f; }
+
+  enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT };
+  nsSVGLength2 mLengthAttributes[4];
+  static LengthInfo sLengthInfo[4];
+  virtual LengthAttributesInfo GetLengthInfo() override;
+
+  virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override;
+
+  virtual const nsSVGViewBox& GetViewBoxInternal() const { return mViewBox; }
+  virtual nsSVGAnimatedTransformList* GetTransformInternal() const {
+    return mTransforms;
+  }
+  nsSVGViewBox                   mViewBox;
+  SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
+
+  // The size of the rectangular SVG viewport into which we render. This is
+  // not (necessarily) the same as the content area. See:
+  //
+  //   http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
+  //
+  // XXXjwatt Currently only used for outer <svg>, but maybe we could use -1 to
+  // flag this as an inner <svg> to save the overhead of GetCtx calls?
+  // XXXjwatt our frame should probably reset these when it's destroyed.
+  float mViewportWidth, mViewportHeight;
+
+  bool     mHasChildrenOnlyTransform;
+};
+
+} // namespace dom
+
+} // namespace mozilla
+
+#endif // SVGViewportElement_h
--- a/dom/svg/moz.build
+++ b/dom/svg/moz.build
@@ -97,16 +97,17 @@ EXPORTS.mozilla.dom += [
     'SVGTextPathElement.h',
     'SVGTextPositioningElement.h',
     'SVGTitleElement.h',
     'SVGTransform.h',
     'SVGTransformableElement.h',
     'SVGTSpanElement.h',
     'SVGUseElement.h',
     'SVGViewElement.h',
+    'SVGViewportElement.h',
 ]
 
 UNIFIED_SOURCES += [
     'DOMSVGAnimatedLengthList.cpp',
     'DOMSVGAnimatedNumberList.cpp',
     'DOMSVGLength.cpp',
     'DOMSVGLengthList.cpp',
     'DOMSVGNumber.cpp',
@@ -243,16 +244,17 @@ UNIFIED_SOURCES += [
     'SVGTransformableElement.cpp',
     'SVGTransformList.cpp',
     'SVGTransformListParser.cpp',
     'SVGTransformListSMILType.cpp',
     'SVGTSpanElement.cpp',
     'SVGUseElement.cpp',
     'SVGViewBoxSMILType.cpp',
     'SVGViewElement.cpp',
+    'SVGViewportElement.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/dom',
     '/dom/base',
--- a/dom/workers/WorkerNavigator.cpp
+++ b/dom/workers/WorkerNavigator.cpp
@@ -8,16 +8,17 @@
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/dom/StorageManager.h"
 #include "mozilla/dom/WorkerNavigator.h"
 #include "mozilla/dom/WorkerNavigatorBinding.h"
 #include "mozilla/dom/network/Connection.h"
 
 #include "nsProxyRelease.h"
+#include "nsRFPService.h"
 #include "RuntimeService.h"
 
 #include "nsIDocument.h"
 
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
@@ -74,49 +75,61 @@ WorkerNavigator::SetLanguages(const nsTA
 }
 
 void
 WorkerNavigator::GetAppName(nsString& aAppName, CallerType aCallerType) const
 {
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
 
-  if (!mProperties.mAppNameOverridden.IsEmpty() &&
+  if ((!mProperties.mAppNameOverridden.IsEmpty() ||
+       workerPrivate->ResistFingerprintingEnabled()) &&
       !workerPrivate->UsesSystemPrincipal()) {
-    aAppName = mProperties.mAppNameOverridden;
+    // We will spoof this value when 'privacy.resistFingerprinting' is true.
+    // See nsRFPService.h for spoofed value.
+    aAppName = workerPrivate->ResistFingerprintingEnabled() ?
+      NS_LITERAL_STRING(SPOOFED_APPNAME) : mProperties.mAppNameOverridden;
   } else {
     aAppName = mProperties.mAppName;
   }
 }
 
 void
 WorkerNavigator::GetAppVersion(nsString& aAppVersion, CallerType aCallerType,
                                ErrorResult& aRv) const
 {
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
 
-  if (!mProperties.mAppVersionOverridden.IsEmpty() &&
+  if ((!mProperties.mAppVersionOverridden.IsEmpty() ||
+       workerPrivate->ResistFingerprintingEnabled()) &&
       !workerPrivate->UsesSystemPrincipal()) {
-    aAppVersion = mProperties.mAppVersionOverridden;
+    // We will spoof this value when 'privacy.resistFingerprinting' is true.
+    // See nsRFPService.h for spoofed value.
+    aAppVersion = workerPrivate->ResistFingerprintingEnabled() ?
+      NS_LITERAL_STRING(SPOOFED_APPVERSION) : mProperties.mAppVersionOverridden;
   } else {
     aAppVersion = mProperties.mAppVersion;
   }
 }
 
 void
 WorkerNavigator::GetPlatform(nsString& aPlatform, CallerType aCallerType,
                              ErrorResult& aRv) const
 {
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
 
-  if (!mProperties.mPlatformOverridden.IsEmpty() &&
+  if ((!mProperties.mPlatformOverridden.IsEmpty() ||
+       workerPrivate->ResistFingerprintingEnabled()) &&
       !workerPrivate->UsesSystemPrincipal()) {
-    aPlatform = mProperties.mPlatformOverridden;
+    // We will spoof this value when 'privacy.resistFingerprinting' is true.
+    // See nsRFPService.h for spoofed value.
+    aPlatform = workerPrivate->ResistFingerprintingEnabled() ?
+      NS_LITERAL_STRING(SPOOFED_PLATFORM) : mProperties.mPlatformOverridden;
   } else {
     aPlatform = mProperties.mPlatform;
   }
 }
 
 namespace {
 
 class GetUserAgentRunnable final : public WorkerMainThreadRunnable
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -20,16 +20,17 @@
 #include "nsCRT.h"
 #include "nsCRTGlue.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsGkAtoms.h"
 #include "nsIContent.h"
+#include "nsIDocumentEncoder.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeFilter.h"
 #include "nsNameSpaceManager.h"
 #include "nsINode.h"
 #include "nsIPlaintextEditor.h"
 #include "nsISupportsBase.h"
 #include "nsLiteralString.h"
@@ -291,17 +292,17 @@ TextEditRules::WillDoAction(Selection* a
     case EditAction::redo:
       return WillRedo(aSelection, aCancel, aHandled);
     case EditAction::setTextProperty:
       return WillSetTextProperty(aSelection, aCancel, aHandled);
     case EditAction::removeTextProperty:
       return WillRemoveTextProperty(aSelection, aCancel, aHandled);
     case EditAction::outputText:
       return WillOutputText(aSelection, info->outputFormat, info->outString,
-                            aCancel, aHandled);
+                            info->flags, aCancel, aHandled);
     case EditAction::insertElement:
       // i had thought this would be html rules only.  but we put pre elements
       // into plaintext mail when doing quoting for reply!  doh!
       WillInsert(*aSelection, aCancel);
       return NS_OK;
     default:
       return NS_ERROR_FAILURE;
   }
@@ -1181,39 +1182,124 @@ TextEditRules::DidRedo(Selection* aSelec
   }
   return NS_OK;
 }
 
 nsresult
 TextEditRules::WillOutputText(Selection* aSelection,
                               const nsAString* aOutputFormat,
                               nsAString* aOutString,
+                              uint32_t aFlags,
                               bool* aCancel,
                               bool* aHandled)
 {
   // null selection ok
-  if (!aOutString || !aOutputFormat || !aCancel || !aHandled) {
+  if (NS_WARN_IF(!aOutString) || NS_WARN_IF(!aOutputFormat) ||
+      NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
     return NS_ERROR_NULL_POINTER;
   }
 
   // initialize out param
   *aCancel = false;
   *aHandled = false;
 
-  if (aOutputFormat->LowerCaseEqualsLiteral("text/plain")) {
-    // Only use these rules for plain text output.
-    if (IsPasswordEditor()) {
-      *aOutString = mPasswordText;
-      *aHandled = true;
-    } else if (mBogusNode) {
-      // This means there's no content, so output null string.
-      aOutString->Truncate();
-      *aHandled = true;
-    }
+  if (!aOutputFormat->LowerCaseEqualsLiteral("text/plain")) {
+    return NS_OK;
+  }
+
+  // XXX Looks like that even if it's password field, we need to use the
+  //     expensive path if the caller requests some complicated handling.
+  //     However, changing the behavior for password field might cause
+  //     security issue.  So, be careful when you touch here.
+  if (IsPasswordEditor()) {
+    *aOutString = mPasswordText;
+    *aHandled = true;
+    return NS_OK;
+  }
+
+  // If there is a bogus node, there's no content.  So output empty string.
+  if (mBogusNode) {
+    aOutString->Truncate();
+    *aHandled = true;
+    return NS_OK;
+  }
+
+  // If it's necessary to check selection range or the editor wraps hard,
+  // we need some complicated handling.  In such case, we need to use the
+  // expensive path.
+  // XXX Anything else what we cannot return plain text simply?
+  if (aFlags & nsIDocumentEncoder::OutputSelectionOnly ||
+      aFlags & nsIDocumentEncoder::OutputWrap) {
+    return NS_OK;
+  }
+
+  // If it's neither <input type="text"> nor <textarea>, e.g., an HTML editor
+  // which is in plaintext mode (e.g., plaintext email composer on Thunderbird),
+  // it should be handled by the expensive path.
+  if (NS_WARN_IF(!mTextEditor) || mTextEditor->AsHTMLEditor()) {
+    return NS_OK;
+  }
+
+  RefPtr<Element> root = mTextEditor->GetRoot();
+  if (!root) { // Don't warn it, this is possible, e.g., 997805.html
+    aOutString->Truncate();
+    *aHandled = true;
+    return NS_OK;
   }
+
+  nsIContent* firstChild = root->GetFirstChild();
+  if (!firstChild) {
+    aOutString->Truncate();
+    *aHandled = true;
+    return NS_OK;
+  }
+
+  // If it's an <input type="text"> element, the DOM tree should be:
+  // <div class="anonymous-div">
+  //   #text
+  // </div>
+  //
+  // If it's a <textarea> element, the DOM tree should be:
+  // <div class="anonymous-div">
+  //   #text (if there is)
+  //   <br type="_moz">
+  //   <scrollbar orient="horizontal">
+  //   ...
+  // </div>
+
+  Text* text = firstChild->GetAsText();
+  nsIContent* firstChildExceptText =
+    text ? firstChild->GetNextSibling() : firstChild;
+  // If the DOM tree is unexpected, fall back to the expensive path.
+  bool isInput = IsSingleLineEditor();
+  bool isTextarea = !isInput;
+  if (NS_WARN_IF(isInput && firstChildExceptText) ||
+      NS_WARN_IF(isTextarea && !firstChildExceptText) ||
+      NS_WARN_IF(isTextarea &&
+                 !TextEditUtils::IsMozBR(firstChildExceptText) &&
+                 !firstChildExceptText->IsXULElement(nsGkAtoms::scrollbar))) {
+    return NS_OK;
+  }
+
+  // If there is no text node in the expected DOM tree, we can say that it's
+  // just empty.
+  if (!text) {
+    aOutString->Truncate();
+    *aHandled = true;
+    return NS_OK;
+  }
+
+  // Otherwise, the text is the value.
+  nsresult rv = text->GetData(*aOutString);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    // Fall back to the expensive path if it fails.
+    return NS_OK;
+  }
+
+  *aHandled = true;
   return NS_OK;
 }
 
 nsresult
 TextEditRules::DidOutputText(Selection* aSelection,
                              nsresult aResult)
 {
   return NS_OK;
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -166,16 +166,17 @@ protected:
    * @param aInFormat  The format requested for the output, a MIME type.
    * @param aOutText   The string to use for output, if aCancel is set to true.
    * @param aOutCancel If set to true, the caller should cancel the operation
    *                   and use aOutText as the result.
    */
   nsresult WillOutputText(Selection* aSelection,
                           const nsAString* aInFormat,
                           nsAString* aOutText,
+                          uint32_t aFlags,
                           bool* aOutCancel,
                           bool* aHandled);
 
   nsresult DidOutputText(Selection* aSelection, nsresult aResult);
 
   /**
    * Check for and replace a redundant trailing break.
    */
@@ -256,40 +257,46 @@ protected:
   nsCOMPtr<nsITimer> mTimer;
   uint32_t mLastStart;
   uint32_t mLastLength;
 
   // friends
   friend class AutoLockRulesSniffing;
 };
 
+// TODO: This class (almost struct, though) is ugly and its size isn't
+//       optimized.  Should be refined later.
 class TextRulesInfo final : public RulesInfo
 {
 public:
   explicit TextRulesInfo(EditAction aAction)
     : RulesInfo(aAction)
     , inString(nullptr)
     , outString(nullptr)
     , outputFormat(nullptr)
     , maxLength(-1)
+    , flags(0)
     , collapsedAction(nsIEditor::eNext)
     , stripWrappers(nsIEditor::eStrip)
     , bOrdered(false)
     , entireList(false)
     , bulletType(nullptr)
     , alignType(nullptr)
     , blockType(nullptr)
   {}
 
   // EditAction::insertText / EditAction::insertIMEText
   const nsAString* inString;
   nsAString* outString;
   const nsAString* outputFormat;
   int32_t maxLength;
 
+  // EditAction::outputText
+  uint32_t flags;
+
   // EditAction::deleteSelection
   nsIEditor::EDirection collapsedAction;
   nsIEditor::EStripWrappers stripWrappers;
 
   // EditAction::removeList
   bool bOrdered;
 
   // EditAction::makeList
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -1308,16 +1308,17 @@ TextEditor::OutputToString(const nsAStri
                            nsAString& aOutputString)
 {
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> rules(mRules);
 
   nsString resultString;
   TextRulesInfo ruleInfo(EditAction::outputText);
   ruleInfo.outString = &resultString;
+  ruleInfo.flags = aFlags;
   // XXX Struct should store a nsAReadable*
   nsAutoString str(aFormatType);
   ruleInfo.outputFormat = &str;
   bool cancel, handled;
   nsresult rv = rules->WillDoAction(nullptr, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/crashtests/1343918.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <script type="application/javascript">
+      let form = document.createElement('form');
+      let input1 = document.createElement('input');
+      let input2 = document.createElement('input');
+      document.documentElement.appendChild(form);
+      document.documentElement.appendChild(input1);
+      form.appendChild(input2);
+      form.contentEditable = true
+      input1.focus();
+
+      let range = document.createRange();
+      range.selectNode(input2);
+      window.getSelection().addRange(range);
+      document.execCommand("italic", false, null);
+    </script>
+  </head>
+  <body></body>
+</html>
--- a/editor/libeditor/crashtests/crashtests.list
+++ b/editor/libeditor/crashtests/crashtests.list
@@ -67,11 +67,12 @@ load 1134545.html
 load 1158452.html
 load 1158651.html
 load 1244894.xhtml
 load 1264921.html
 load 1272490.html
 load 1317704.html
 load 1317718.html
 load 1324505.html
+needs-focus load 1343918.html
 load 1348851.html
 load 1350772.html
 load 1366176.html
--- a/extensions/cookie/test/unit/test_permmanager_migrate_4-7.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_4-7.js
@@ -8,20 +8,16 @@ var PERMISSIONS_FILE_NAME = "permissions
 
 function GetPermissionsFile(profile)
 {
   let file = profile.clone();
   file.append(PERMISSIONS_FILE_NAME);
   return file;
 }
 
-function run_test() {
-  run_next_test();
-}
-
 add_task(function* test() {
   /* Create and set up the permissions database */
   let profile = do_get_profile();
 
   let db = Services.storage.openDatabase(GetPermissionsFile(profile));
   db.schemaVersion = 4;
 
   db.executeSimpleSQL(
@@ -54,17 +50,21 @@ add_task(function* test() {
     stmtInsert.bindByName("type", type);
     stmtInsert.bindByName("permission", permission);
     stmtInsert.bindByName("expireType", expireType);
     stmtInsert.bindByName("expireTime", expireTime);
     stmtInsert.bindByName("modificationTime", modificationTime);
     stmtInsert.bindByName("appId", appId);
     stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
 
-    stmtInsert.execute();
+    try {
+      stmtInsert.execute();
+    } finally {
+      stmtInsert.reset();
+    }
 
     return {
       id: thisId,
       host: host,
       type: type,
       permission: permission,
       expireType: expireType,
       expireTime: expireTime,
@@ -194,14 +194,18 @@ add_task(function* test() {
     let db = Services.storage.openDatabase(GetPermissionsFile(profile));
     do_check_true(db.tableExists("moz_perms"));
     do_check_true(db.tableExists("moz_hosts"));
     do_check_false(db.tableExists("moz_hosts_is_backup"));
     do_check_false(db.tableExists("moz_perms_v6"));
 
     // The moz_hosts table should still exist but be empty
     let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
-    mozHostsCount.executeStep();
-    do_check_eq(mozHostsCount.getInt64(0), 0);
+    try {
+      mozHostsCount.executeStep();
+      do_check_eq(mozHostsCount.getInt64(0), 0);
+    } finally {
+      mozHostsCount.finalize();
+    }
 
     db.close();
   }
 });
--- a/extensions/cookie/test/unit/test_permmanager_migrate_4-7_no_history.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_4-7_no_history.js
@@ -37,20 +37,16 @@ function GetPermissionsFile(profile)
   file.append(PERMISSIONS_FILE_NAME);
   return file;
 }
 
 /*
  * Done nsINavHistoryService code
  */
 
-function run_test() {
-  run_next_test();
-}
-
 add_task(function test() {
   /* Create and set up the permissions database */
   let profile = do_get_profile();
 
   // Make sure that we can't resolve the nsINavHistoryService
   try {
     Cc['@mozilla.org/browser/nav-history-service;1'].getService(Ci.nsINavHistoryService);
     do_check_true(false, "There shouldn't have been a nsINavHistoryService");
@@ -91,17 +87,21 @@ add_task(function test() {
     stmtInsert.bindByName("type", type);
     stmtInsert.bindByName("permission", permission);
     stmtInsert.bindByName("expireType", expireType);
     stmtInsert.bindByName("expireTime", expireTime);
     stmtInsert.bindByName("modificationTime", modificationTime);
     stmtInsert.bindByName("appId", appId);
     stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
 
-    stmtInsert.execute();
+    try {
+      stmtInsert.execute();
+    } finally {
+      stmtInsert.reset();
+    }
 
     return {
       id: thisId,
       host: host,
       type: type,
       permission: permission,
       expireType: expireType,
       expireTime: expireTime,
@@ -211,16 +211,20 @@ add_task(function test() {
     let db = Services.storage.openDatabase(GetPermissionsFile(profile));
     do_check_true(db.tableExists("moz_perms"));
     do_check_true(db.tableExists("moz_hosts"));
     do_check_false(db.tableExists("moz_hosts_is_backup"));
     do_check_false(db.tableExists("moz_perms_v6"));
 
     // The moz_hosts table should still exist but be empty
     let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
-    mozHostsCount.executeStep();
-    do_check_eq(mozHostsCount.getInt64(0), 0);
+    try {
+      mozHostsCount.executeStep();
+      do_check_eq(mozHostsCount.getInt64(0), 0);
+    } finally {
+      mozHostsCount.finalize();
+    }
 
     db.close();
   }
 
   cleanupFactory();
 });
--- a/extensions/cookie/test/unit/test_permmanager_migrate_5-7a.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_5-7a.js
@@ -8,20 +8,16 @@ var PERMISSIONS_FILE_NAME = "permissions
 
 function GetPermissionsFile(profile)
 {
   let file = profile.clone();
   file.append(PERMISSIONS_FILE_NAME);
   return file;
 }
 
-function run_test() {
-  run_next_test();
-}
-
 add_task(function* test() {
   /* Create and set up the permissions database */
   let profile = do_get_profile();
 
   let db = Services.storage.openDatabase(GetPermissionsFile(profile));
   db.schemaVersion = 5;
 
   /*
@@ -76,17 +72,21 @@ add_task(function* test() {
     stmt5Insert.bindByName("id", thisId);
     stmt5Insert.bindByName("origin", origin);
     stmt5Insert.bindByName("type", type);
     stmt5Insert.bindByName("permission", permission);
     stmt5Insert.bindByName("expireType", expireType);
     stmt5Insert.bindByName("expireTime", expireTime);
     stmt5Insert.bindByName("modificationTime", modificationTime);
 
-    stmt5Insert.execute();
+    try {
+      stmt5Insert.execute();
+    } finally {
+      stmt5Insert.reset();
+    }
 
     return {
       id: thisId,
       origin: origin,
       type: type,
       permission: permission,
       expireType: expireType,
       expireTime: expireTime,
@@ -102,17 +102,21 @@ add_task(function* test() {
     stmtInsert.bindByName("type", type);
     stmtInsert.bindByName("permission", permission);
     stmtInsert.bindByName("expireType", expireType);
     stmtInsert.bindByName("expireTime", expireTime);
     stmtInsert.bindByName("modificationTime", modificationTime);
     stmtInsert.bindByName("appId", appId);
     stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
 
-    stmtInsert.execute();
+    try {
+      stmtInsert.execute();
+    } finally {
+      stmtInsert.reset();
+    }
 
     return {
       id: thisId,
       host: host,
       type: type,
       permission: permission,
       expireType: expireType,
       expireTime: expireTime,
@@ -146,16 +150,17 @@ add_task(function* test() {
     insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
     insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false),
     insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false),
     insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
     insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
   ];
 
   // CLose the db connection
+  stmt5Insert.finalize();
   stmtInsert.finalize();
   db.close();
   stmtInsert = null;
   db = null;
 
   let expected = [
     // The http:// entries under foo.com won't be inserted, as there are history entries for foo.com,
     // and http://foo.com or a subdomain are never visited.
@@ -248,37 +253,48 @@ add_task(function* test() {
     let db = Services.storage.openDatabase(GetPermissionsFile(profile));
     do_check_true(db.tableExists("moz_perms"));
     do_check_true(db.tableExists("moz_hosts"));
     do_check_false(db.tableExists("moz_hosts_is_backup"));
     do_check_true(db.tableExists("moz_perms_v6"));
 
     // The moz_hosts table should still exist but be empty
     let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
-    mozHostsCount.executeStep();
-    do_check_eq(mozHostsCount.getInt64(0), 0);
+    try {
+      mozHostsCount.executeStep();
+      do_check_eq(mozHostsCount.getInt64(0), 0);
+    } finally {
+      mozHostsCount.finalize();
+    }
 
     // Check that the moz_perms_v6 table contains the backup of the entry we created
     let mozPermsV6Stmt = db.createStatement("SELECT " +
                                             "origin, type, permission, expireType, expireTime, modificationTime " +
                                             "FROM moz_perms_v6 WHERE id = :id");
-
-    // Check that the moz_hosts table still contains the correct values.
-    created5.forEach((it) => {
-      mozPermsV6Stmt.reset();
-      mozPermsV6Stmt.bindByName("id", it.id);
-      mozPermsV6Stmt.executeStep();
-      do_check_eq(mozPermsV6Stmt.getUTF8String(0), it.origin);
-      do_check_eq(mozPermsV6Stmt.getUTF8String(1), it.type);
-      do_check_eq(mozPermsV6Stmt.getInt64(2), it.permission);
-      do_check_eq(mozPermsV6Stmt.getInt64(3), it.expireType);
-      do_check_eq(mozPermsV6Stmt.getInt64(4), it.expireTime);
-      do_check_eq(mozPermsV6Stmt.getInt64(5), it.modificationTime);
-    });
+    try {
+      // Check that the moz_hosts table still contains the correct values.
+      created5.forEach((it) => {
+        mozPermsV6Stmt.reset();
+        mozPermsV6Stmt.bindByName("id", it.id);
+        mozPermsV6Stmt.executeStep();
+        do_check_eq(mozPermsV6Stmt.getUTF8String(0), it.origin);
+        do_check_eq(mozPermsV6Stmt.getUTF8String(1), it.type);
+        do_check_eq(mozPermsV6Stmt.getInt64(2), it.permission);
+        do_check_eq(mozPermsV6Stmt.getInt64(3), it.expireType);
+        do_check_eq(mozPermsV6Stmt.getInt64(4), it.expireTime);
+        do_check_eq(mozPermsV6Stmt.getInt64(5), it.modificationTime);
+      });
+    } finally {
+      mozPermsV6Stmt.finalize();
+    }
 
     // Check that there are the right number of values
     let mozPermsV6Count = db.createStatement("SELECT count(*) FROM moz_perms_v6");
-    mozPermsV6Count.executeStep();
-    do_check_eq(mozPermsV6Count.getInt64(0), created5.length);
+    try {
+      mozPermsV6Count.executeStep();
+      do_check_eq(mozPermsV6Count.getInt64(0), created5.length);
+    } finally {
+      mozPermsV6Count.finalize();
+    }
 
     db.close();
   }
 });
--- a/extensions/cookie/test/unit/test_permmanager_migrate_5-7b.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_5-7b.js
@@ -8,20 +8,16 @@ var PERMISSIONS_FILE_NAME = "permissions
 
 function GetPermissionsFile(profile)
 {
   let file = profile.clone();
   file.append(PERMISSIONS_FILE_NAME);
   return file;
 }
 
-function run_test() {
-  run_next_test();
-}
-
 add_task(function test() {
   /* Create and set up the permissions database */
   let profile = do_get_profile();
 
   let db = Services.storage.openDatabase(GetPermissionsFile(profile));
   db.schemaVersion = 5;
 
   /*
@@ -53,17 +49,21 @@ add_task(function test() {
     stmt5Insert.bindByName("id", thisId);
     stmt5Insert.bindByName("origin", origin);
     stmt5Insert.bindByName("type", type);
     stmt5Insert.bindByName("permission", permission);
     stmt5Insert.bindByName("expireType", expireType);
     stmt5Insert.bindByName("expireTime", expireTime);
     stmt5Insert.bindByName("modificationTime", modificationTime);
 
-    stmt5Insert.execute();
+    try {
+      stmt5Insert.execute();
+    } finally {
+      stmt5Insert.reset();
+    }
 
     return {
       id: thisId,
       host: origin,
       type: type,
       permission: permission,
       expireType: expireType,
       expireTime: expireTime,
@@ -137,32 +137,39 @@ add_task(function test() {
     do_check_true(db.tableExists("moz_hosts"));
     do_check_false(db.tableExists("moz_hosts_is_backup"));
     do_check_false(db.tableExists("moz_perms_v6"));
 
     let mozHostsStmt = db.createStatement("SELECT " +
                                           "host, type, permission, expireType, expireTime, " +
                                           "modificationTime, appId, isInBrowserElement " +
                                           "FROM moz_hosts WHERE id = :id");
-
-    // Check that the moz_hosts table still contains the correct values.
-    created4.forEach((it) => {
-      mozHostsStmt.reset();
-      mozHostsStmt.bindByName("id", it.id);
-      mozHostsStmt.executeStep();
-      do_check_eq(mozHostsStmt.getUTF8String(0), it.host);
-      do_check_eq(mozHostsStmt.getUTF8String(1), it.type);
-      do_check_eq(mozHostsStmt.getInt64(2), it.permission);
-      do_check_eq(mozHostsStmt.getInt64(3), it.expireType);
-      do_check_eq(mozHostsStmt.getInt64(4), it.expireTime);
-      do_check_eq(mozHostsStmt.getInt64(5), it.modificationTime);
-      do_check_eq(mozHostsStmt.getInt64(6), it.appId);
-      do_check_eq(mozHostsStmt.getInt64(7), it.isInBrowserElement);
-    });
+    try {
+      // Check that the moz_hosts table still contains the correct values.
+      created4.forEach((it) => {
+        mozHostsStmt.reset();
+        mozHostsStmt.bindByName("id", it.id);
+        mozHostsStmt.executeStep();
+        do_check_eq(mozHostsStmt.getUTF8String(0), it.host);
+        do_check_eq(mozHostsStmt.getUTF8String(1), it.type);
+        do_check_eq(mozHostsStmt.getInt64(2), it.permission);
+        do_check_eq(mozHostsStmt.getInt64(3), it.expireType);
+        do_check_eq(mozHostsStmt.getInt64(4), it.expireTime);
+        do_check_eq(mozHostsStmt.getInt64(5), it.modificationTime);
+        do_check_eq(mozHostsStmt.getInt64(6), it.appId);
+        do_check_eq(mozHostsStmt.getInt64(7), it.isInBrowserElement);
+      });
+    } finally {
+      mozHostsStmt.finalize();
+    }
 
     // Check that there are the right number of values
     let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
-    mozHostsCount.executeStep();
-    do_check_eq(mozHostsCount.getInt64(0), created4.length);
+    try {
+      mozHostsCount.executeStep();
+      do_check_eq(mozHostsCount.getInt64(0), created4.length);
+    } finally {
+      mozHostsCount.finalize();
+    }
 
     db.close();
   }
 });
--- a/extensions/cookie/test/unit/test_permmanager_migrate_6-7a.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_6-7a.js
@@ -8,20 +8,16 @@ var PERMISSIONS_FILE_NAME = "permissions
 
 function GetPermissionsFile(profile)
 {
   let file = profile.clone();
   file.append(PERMISSIONS_FILE_NAME);
   return file;
 }
 
-function run_test() {
-  run_next_test();
-}
-
 add_task(function* test() {
   /* Create and set up the permissions database */
   let profile = do_get_profile();
 
   let db = Services.storage.openDatabase(GetPermissionsFile(profile));
   db.schemaVersion = 6;
 
   /*
@@ -76,17 +72,21 @@ add_task(function* test() {
     stmt6Insert.bindByName("id", thisId);
     stmt6Insert.bindByName("origin", origin);
     stmt6Insert.bindByName("type", type);
     stmt6Insert.bindByName("permission", permission);
     stmt6Insert.bindByName("expireType", expireType);
     stmt6Insert.bindByName("expireTime", expireTime);
     stmt6Insert.bindByName("modificationTime", modificationTime);
 
-    stmt6Insert.execute();
+    try {
+      stmt6Insert.execute();
+    } finally {
+      stmt6Insert.reset();
+    }
 
     return {
       id: thisId,
       origin: origin,
       type: type,
       permission: permission,
       expireType: expireType,
       expireTime: expireTime,
@@ -102,17 +102,21 @@ add_task(function* test() {
     stmtInsert.bindByName("type", type);
     stmtInsert.bindByName("permission", permission);
     stmtInsert.bindByName("expireType", expireType);
     stmtInsert.bindByName("expireTime", expireTime);
     stmtInsert.bindByName("modificationTime", modificationTime);
     stmtInsert.bindByName("appId", appId);
     stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
 
-    stmtInsert.execute();
+    try {
+      stmtInsert.execute();
+    } finally {
+      stmtInsert.reset();
+    }
 
     return {
       id: thisId,
       host: host,
       type: type,
       permission: permission,
       expireType: expireType,
       expireTime: expireTime,
@@ -146,16 +150,17 @@ add_task(function* test() {
     insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
     insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false),
     insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false),
     insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
     insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
   ];
 
   // CLose the db connection
+  stmt6Insert.finalize();
   stmtInsert.finalize();
   db.close();
   stmtInsert = null;
   db = null;
 
   let expected = [
     // The http:// entries under foo.com won't be inserted, as there are history entries for foo.com,
     // and http://foo.com or a subdomain are never visited.
@@ -248,37 +253,48 @@ add_task(function* test() {
     let db = Services.storage.openDatabase(GetPermissionsFile(profile));
     do_check_true(db.tableExists("moz_perms"));
     do_check_true(db.tableExists("moz_hosts"));
     do_check_false(db.tableExists("moz_hosts_is_backup"));
     do_check_true(db.tableExists("moz_perms_v6"));
 
     // The moz_hosts table should still exist but be empty
     let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
-    mozHostsCount.executeStep();
-    do_check_eq(mozHostsCount.getInt64(0), 0);
+    try {
+      mozHostsCount.executeStep();
+      do_check_eq(mozHostsCount.getInt64(0), 0);
+    } finally {
+      mozHostsCount.finalize();
+    }
 
     // Check that the moz_perms_v6 table contains the backup of the entry we created
     let mozPermsV6Stmt = db.createStatement("SELECT " +
                                             "origin, type, permission, expireType, expireTime, modificationTime " +
                                             "FROM moz_perms_v6 WHERE id = :id");
-
-    // Check that the moz_hosts table still contains the correct values.
-    created6.forEach((it) => {
-      mozPermsV6Stmt.reset();
-      mozPermsV6Stmt.bindByName("id", it.id);
-      mozPermsV6Stmt.executeStep();
-      do_check_eq(mozPermsV6Stmt.getUTF8String(0), it.origin);
-      do_check_eq(mozPermsV6Stmt.getUTF8String(1), it.type);
-      do_check_eq(mozPermsV6Stmt.getInt64(2), it.permission);
-      do_check_eq(mozPermsV6Stmt.getInt64(3), it.expireType);
-      do_check_eq(mozPermsV6Stmt.getInt64(4), it.expireTime);
-      do_check_eq(mozPermsV6Stmt.getInt64(5), it.modificationTime);
-    });
+    try {
+      // Check that the moz_hosts table still contains the correct values.
+      created6.forEach((it) => {
+        mozPermsV6Stmt.reset();
+        mozPermsV6Stmt.bindByName("id", it.id);
+        mozPermsV6Stmt.executeStep();
+        do_check_eq(mozPermsV6Stmt.getUTF8String(0), it.origin);
+        do_check_eq(mozPermsV6Stmt.getUTF8String(1), it.type);
+        do_check_eq(mozPermsV6Stmt.getInt64(2), it.permission);
+        do_check_eq(mozPermsV6Stmt.getInt64(3), it.expireType);
+        do_check_eq(mozPermsV6Stmt.getInt64(4), it.expireTime);
+        do_check_eq(mozPermsV6Stmt.getInt64(5), it.modificationTime);
+      });
+    } finally {
+      mozPermsV6Stmt.finalize();
+    }
 
     // Check that there are the right number of values
     let mozPermsV6Count = db.createStatement("SELECT count(*) FROM moz_perms_v6");
-    mozPermsV6Count.executeStep();
-    do_check_eq(mozPermsV6Count.getInt64(0), created6.length);
+    try {
+      mozPermsV6Count.executeStep();
+      do_check_eq(mozPermsV6Count.getInt64(0), created6.length);
+    } finally {
+      mozPermsV6Count.finalize();
+    }
 
     db.close();
   }
 });
--- a/extensions/cookie/test/unit/test_permmanager_migrate_6-7b.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_6-7b.js
@@ -8,20 +8,16 @@ var PERMISSIONS_FILE_NAME = "permissions
 
 function GetPermissionsFile(profile)
 {
   let file = profile.clone();
   file.append(PERMISSIONS_FILE_NAME);
   return file;
 }
 
-function run_test() {
-  run_next_test();
-}
-
 add_task(function test() {
   /* Create and set up the permissions database */
   let profile = do_get_profile();
 
   let db = Services.storage.openDatabase(GetPermissionsFile(profile));
   db.schemaVersion = 6;
 
   /*
@@ -53,17 +49,21 @@ add_task(function test() {
     stmt6Insert.bindByName("id", thisId);
     stmt6Insert.bindByName("origin", origin);
     stmt6Insert.bindByName("type", type);
     stmt6Insert.bindByName("permission", permission);
     stmt6Insert.bindByName("expireType", expireType);
     stmt6Insert.bindByName("expireTime", expireTime);
     stmt6Insert.bindByName("modificationTime", modificationTime);
 
-    stmt6Insert.execute();
+    try {
+      stmt6Insert.execute();
+    } finally {
+      stmt6Insert.reset();
+    }
 
     return {
       id: thisId,
       host: origin,
       type: type,
       permission: permission,
       expireType: expireType,
       expireTime: expireTime,
@@ -131,32 +131,39 @@ add_task(function test() {
     do_check_true(db.tableExists("moz_hosts"));
     do_check_false(db.tableExists("moz_hosts_is_backup"));
     do_check_false(db.tableExists("moz_perms_v6"));
 
     let mozHostsStmt = db.createStatement("SELECT " +
                                           "host, type, permission, expireType, expireTime, " +
                                           "modificationTime, appId, isInBrowserElement " +
                                           "FROM moz_hosts WHERE id = :id");
-
-    // Check that the moz_hosts table still contains the correct values.
-    created4.forEach((it) => {
-      mozHostsStmt.reset();
-      mozHostsStmt.bindByName("id", it.id);
-      mozHostsStmt.executeStep();
-      do_check_eq(mozHostsStmt.getUTF8String(0), it.host);
-      do_check_eq(mozHostsStmt.getUTF8String(1), it.type);
-      do_check_eq(mozHostsStmt.getInt64(2), it.permission);
-      do_check_eq(mozHostsStmt.getInt64(3), it.expireType);
-      do_check_eq(mozHostsStmt.getInt64(4), it.expireTime);
-      do_check_eq(mozHostsStmt.getInt64(5), it.modificationTime);
-      do_check_eq(mozHostsStmt.getInt64(6), it.appId);
-      do_check_eq(mozHostsStmt.getInt64(7), it.isInBrowserElement);
-    });
+    try {
+      // Check that the moz_hosts table still contains the correct values.
+      created4.forEach((it) => {
+        mozHostsStmt.reset();
+        mozHostsStmt.bindByName("id", it.id);
+        mozHostsStmt.executeStep();
+        do_check_eq(mozHostsStmt.getUTF8String(0), it.host);
+        do_check_eq(mozHostsStmt.getUTF8String(1), it.type);
+        do_check_eq(mozHostsStmt.getInt64(2), it.permission);
+        do_check_eq(mozHostsStmt.getInt64(3), it.expireType);
+        do_check_eq(mozHostsStmt.getInt64(4), it.expireTime);
+        do_check_eq(mozHostsStmt.getInt64(5), it.modificationTime);
+        do_check_eq(mozHostsStmt.getInt64(6), it.appId);
+        do_check_eq(mozHostsStmt.getInt64(7), it.isInBrowserElement);
+      });
+    } finally {
+      mozHostsStmt.finalize();
+    }
 
     // Check that there are the right number of values
     let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
-    mozHostsCount.executeStep();
-    do_check_eq(mozHostsCount.getInt64(0), created4.length);
+    try {
+      mozHostsCount.executeStep();
+      do_check_eq(mozHostsCount.getInt64(0), created4.length);
+    } finally {
+      mozHostsCount.finalize();
+    }
 
     db.close();
   }
 });
--- a/extensions/cookie/test/unit/test_permmanager_migrate_7-8.js
+++ b/extensions/cookie/test/unit/test_permmanager_migrate_7-8.js
@@ -8,20 +8,16 @@ var PERMISSIONS_FILE_NAME = "permissions
 
 function GetPermissionsFile(profile)
 {
   let file = profile.clone();
   file.append(PERMISSIONS_FILE_NAME);
   return file;
 }
 
-function run_test() {
-  run_next_test();
-}
-
 add_task(function* test() {
   /* Create and set up the permissions database */
   let profile = do_get_profile();
 
   let db = Services.storage.openDatabase(GetPermissionsFile(profile));
   db.schemaVersion = 7;
 
   /*
@@ -81,17 +77,21 @@ add_task(function* test() {
     stmt6Insert.bindByName("id", thisId);
     stmt6Insert.bindByName("origin", origin);
     stmt6Insert.bindByName("type", type);
     stmt6Insert.bindByName("permission", permission);
     stmt6Insert.bindByName("expireType", expireType);
     stmt6Insert.bindByName("expireTime", expireTime);
     stmt6Insert.bindByName("modificationTime", modificationTime);
 
-    stmt6Insert.execute();
+    try {
+      stmt6Insert.execute();
+    } finally {
+      stmt6Insert.reset();
+    }
 
     return {
       id: thisId,
       origin: origin,
       type: type,
       permission: permission,
       expireType: expireType,
       expireTime: expireTime,
@@ -107,17 +107,21 @@ add_task(function* test() {
     stmtInsert.bindByName("type", type);
     stmtInsert.bindByName("permission", permission);
     stmtInsert.bindByName("expireType", expireType);
     stmtInsert.bindByName("expireTime", expireTime);
     stmtInsert.bindByName("modificationTime", modificationTime);
     stmtInsert.bindByName("appId", appId);
     stmtInsert.bindByName("isInBrowserElement", isInBrowserElement);
 
-    stmtInsert.execute();
+    try {
+      stmtInsert.execute();
+    } finally {
+      stmtInsert.reset();
+    }
 
     return {
       id: thisId,
       host: host,
       type: type,
       permission: permission,
       expireType: expireType,
       expireTime: expireTime,
@@ -156,16 +160,17 @@ add_task(function* test() {
     insertHost("file:///another/file.html", "A", 1, 0, 0, 0, 0, false),
     insertHost("moz-nullprincipal:{8695105a-adbe-4e4e-8083-851faa5ca2d7}", "A", 1, 0, 0, 0, 0, false),
     insertHost("moz-nullprincipal:{12ahjksd-akjs-asd3-8393-asdu2189asdu}", "B", 1, 0, 0, 0, 0, false),
     insertHost("<file>", "A", 1, 0, 0, 0, 0, false),
     insertHost("<file>", "B", 1, 0, 0, 0, 0, false),
   ];
 
   // CLose the db connection
+  stmt6Insert.finalize();
   stmtInsert.finalize();
   db.close();
   stmtInsert = null;
   db = null;
 
   let expected = [
     // We should have kept the previously migrated entries
     ["https://foo.com", "A", 2, 0, 0, 0],
@@ -228,19 +233,27 @@ add_task(function* test() {
     let db = Services.storage.openDatabase(GetPermissionsFile(profile));
     do_check_true(db.tableExists("moz_perms"));
     do_check_true(db.tableExists("moz_hosts"));
     do_check_false(db.tableExists("moz_hosts_is_backup"));
     do_check_false(db.tableExists("moz_perms_v6"));
 
     // The moz_hosts table should still exist but be empty
     let mozHostsCount = db.createStatement("SELECT count(*) FROM moz_hosts");
-    mozHostsCount.executeStep();
-    do_check_eq(mozHostsCount.getInt64(0), 0);
+    try {
+      mozHostsCount.executeStep();
+      do_check_eq(mozHostsCount.getInt64(0), 0);
+    } finally {
+      mozHostsCount.finalize();
+    }
 
     // Check that there are the right number of values in the permissions database
     let mozPermsCount = db.createStatement("SELECT count(*) FROM moz_perms");
-    mozPermsCount.executeStep();
-    do_check_eq(mozPermsCount.getInt64(0), expected.length);
+    try {
+      mozPermsCount.executeStep();
+      do_check_eq(mozPermsCount.getInt64(0), expected.length);
+    } finally {
+      mozPermsCount.finalize();
+    }
 
     db.close();
   }
 });
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,9 @@ there is another crate in m-c called moz
 the same folder to store its rust dependencies. If one of the libraries that is
 required by both mozjs_sys and webrender is updated without updating the other
 project's Cargo.lock file, that results in build bustage.
 This means that any time you do this sort of manual update of packages, you need
 to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
 the need to run the cargo update command in js/src as well. Hopefully this will
 be resolved soon.
 
-Latest Commit: 6752684fcc7402b0a5480e0b9f73152b2f9ed1e5
+Latest Commit: 1d6348023a4a4fdd89dce038640c5da906005acc
--- a/gfx/layers/wr/WebRenderMessageUtils.h
+++ b/gfx/layers/wr/WebRenderMessageUtils.h
@@ -211,25 +211,23 @@ struct ParamTraits<WrMixBlendMode>
 };
 
 template<>
 struct ParamTraits<WrBuiltDisplayListDescriptor>
 {
   static void
   Write(Message* aMsg, const WrBuiltDisplayListDescriptor& aParam)
   {
-    WriteParam(aMsg, aParam.display_list_items_size);
     WriteParam(aMsg, aParam.builder_start_time);
     WriteParam(aMsg, aParam.builder_finish_time);
   }
 
   static bool
   Read(const Message* aMsg, PickleIterator* aIter, WrBuiltDisplayListDescriptor* aResult)
   {
-    return ReadParam(aMsg, aIter, &aResult->display_list_items_size)
-        && ReadParam(aMsg, aIter, &aResult->builder_start_time)
+    return ReadParam(aMsg, aIter, &aResult->builder_start_time)
         && ReadParam(aMsg, aIter, &aResult->builder_finish_time);
   }
 };
 
 } // namespace IPC
 
 #endif // GFX_WEBRENDERMESSAGEUTILS_H
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -1,48 +1,48 @@
 [package]
 name = "webrender"
-version = "0.40.0"
+version = "0.43.0"
 authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/webrender"
 build = "build.rs"
 
 [features]
 default = ["freetype-lib", "webgl"]
 freetype-lib = ["freetype/servo-freetype-sys"]
 profiler = ["thread_profiler/thread_profiler"]
 webgl = ["offscreen_gl_context", "webrender_traits/webgl"]
 
 [dependencies]
-app_units = "0.4"
-bincode = "1.0.0-alpha6"
+app_units = "0.5"
+bincode = "0.8"
 bit-set = "0.4"
 byteorder = "1.0"
-euclid = "0.14.4"
+euclid = "0.15"
 fnv = "1.0"
 gleam = "0.4.3"
 lazy_static = "0.2"
 log = "0.3"
 num-traits = "0.1.32"
-offscreen_gl_context = {version = "0.9.0", features = ["serde", "osmesa"], optional = true}
+offscreen_gl_context = {version = "0.11", features = ["serde", "osmesa"], optional = true}
 time = "0.1"
 rayon = "0.8"
 webrender_traits = {path = "../webrender_traits"}
 bitflags = "0.7"
 gamma-lut = "0.2"
 thread_profiler = "0.1.1"
-plane-split = "0.5"
+plane-split = "0.6"
 
 [dev-dependencies]
 angle = {git = "https://github.com/servo/angle", branch = "servo"}
 rand = "0.3"                # for the benchmarks
-servo-glutin = "0.10.1"     # for the example apps
+servo-glutin = "0.11"     # for the example apps
 
 [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
 freetype = { version = "0.2", default-features = false }
 
 [target.'cfg(target_os = "windows")'.dependencies]
-dwrote = "0.3"
+dwrote = "0.4"
 
 [target.'cfg(target_os = "macos")'.dependencies]
-core-graphics = "0.7.0"
-core-text = "4.0"
+core-graphics = "0.8.0"
+core-text = { version = "5.0.1", features = ["lion"] }
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -72,17 +72,17 @@ fn render_blob(
             let y2 = y + descriptor.offset.y as u32;
 
             // Render a simple checkerboard pattern
             let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) { 1 } else { 0 };
             // ..nested in the per-tile cherkerboard pattern
             let tc = if tile_checker { 0 } else { (1 - checker) * 40 };
 
             match descriptor.format {
-                wt::ImageFormat::RGBA8 => {
+                wt::ImageFormat::BGRA8 => {
                     texels.push(color.b * checker + tc);
                     texels.push(color.g * checker + tc);
                     texels.push(color.r * checker + tc);
                     texels.push(color.a * checker + tc);
                 }
                 wt::ImageFormat::A8 => {
                     texels.push(color.a * checker + tc);
                 }
@@ -216,25 +216,25 @@ impl wt::BlobImageRenderer for Checkerbo
 fn body(api: &wt::RenderApi,
         builder: &mut wt::DisplayListBuilder,
         _pipeline_id: &wt::PipelineId,
         layout_size: &wt::LayoutSize)
 {
     let blob_img1 = api.generate_image_key();
     api.add_image(
         blob_img1,
-        wt::ImageDescriptor::new(500, 500, wt::ImageFormat::RGBA8, true),
+        wt::ImageDescriptor::new(500, 500, wt::ImageFormat::BGRA8, true),
         wt::ImageData::new_blob_image(serialize_blob(wt::ColorU::new(50, 50, 150, 255))),
         Some(128),
     );
 
     let blob_img2 = api.generate_image_key();
     api.add_image(
         blob_img2,
-        wt::ImageDescriptor::new(200, 200, wt::ImageFormat::RGBA8, true),
+        wt::ImageDescriptor::new(200, 200, wt::ImageFormat::BGRA8, true),
         wt::ImageData::new_blob_image(serialize_blob(wt::ColorU::new(50, 150, 50, 255))),
         None,
     );
 
     let bounds = wt::LayoutRect::new(wt::LayoutPoint::zero(), *layout_size);
     builder.push_stacking_context(wt::ScrollPolicy::Scrollable,
                                   bounds,
                                   None,
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/examples/nested_display_list.rs
@@ -0,0 +1,132 @@
+/* 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/. */
+
+extern crate gleam;
+extern crate glutin;
+extern crate webrender;
+extern crate webrender_traits;
+
+#[macro_use]
+extern crate lazy_static;
+
+#[path="common/boilerplate.rs"]
+mod boilerplate;
+
+use boilerplate::HandyDandyRectBuilder;
+use std::sync::Mutex;
+use webrender_traits::*;
+
+fn body(_api: &RenderApi,
+        builder: &mut DisplayListBuilder,
+        pipeline_id: &PipelineId,
+        layout_size: &LayoutSize)
+{
+    let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
+    builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
+                                  bounds,
+                                  None,
+                                  TransformStyle::Flat,
+                                  None,
+                                  webrender_traits::MixBlendMode::Normal,
+                                  Vec::new());
+
+    let outer_scroll_frame_rect = (100, 100).to(600, 400);
+    let token = builder.push_clip_region(&outer_scroll_frame_rect, vec![], None);
+    builder.push_rect(outer_scroll_frame_rect,
+                      token, ColorF::new(1.0, 1.0, 1.0, 1.0));
+    let token = builder.push_clip_region(&outer_scroll_frame_rect, vec![], None);
+    let nested_clip_id = builder.define_clip((100, 100).to(1000, 1000), token, None);
+    builder.push_clip_id(nested_clip_id);
+
+    let mut builder2 = webrender_traits::DisplayListBuilder::new(*pipeline_id, *layout_size);
+    let mut builder3 = webrender_traits::DisplayListBuilder::new(*pipeline_id, *layout_size);
+
+    let rect = (110, 110).to(210, 210);
+    let token = builder3.push_clip_region(&rect, vec![], None);
+    builder3.push_rect(rect, token, ColorF::new(0.0, 1.0, 0.0, 1.0));
+
+    // A fixed position rectangle should be fixed to the reference frame that starts
+    // in the outer display list.
+    builder3.push_stacking_context(webrender_traits::ScrollPolicy::Fixed,
+                                  (220, 110).to(320, 210),
+                                  None,
+                                  TransformStyle::Flat,
+                                  None,
+                                  webrender_traits::MixBlendMode::Normal,
+                                  Vec::new());
+    let rect = (0, 0).to(100, 100);
+    let token = builder3.push_clip_region(&rect, vec![], None);
+    builder3.push_rect(rect, token, ColorF::new(0.0, 1.0, 0.0, 1.0));
+    builder3.pop_stacking_context();
+
+    // Now we push an inner scroll frame that should have the same id as the outer one,
+    // but the WebRender nested display list replacement code should convert it into
+    // a unique ClipId.
+    let inner_scroll_frame_rect = (330, 110).to(530, 360);
+    let token = builder3.push_clip_region(&inner_scroll_frame_rect, vec![], None);
+    builder3.push_rect(inner_scroll_frame_rect, token, ColorF::new(1.0, 0.0, 1.0, 0.5));
+    let token = builder3.push_clip_region(&inner_scroll_frame_rect, vec![], None);
+    let inner_nested_clip_id = builder3.define_clip((330, 110).to(2000, 2000), token, None);
+    builder3.push_clip_id(inner_nested_clip_id);
+    let rect = (340, 120).to(440, 220);
+    let token = builder3.push_clip_region(&rect, vec![], None);
+    builder3.push_rect(rect, token, ColorF::new(0.0, 1.0, 0.0, 1.0));
+    builder3.pop_clip_id();
+
+    let (_, _, built_list) = builder3.finalize();
+    builder2.push_nested_display_list(&built_list);
+    let (_, _, built_list) = builder2.finalize();
+    builder.push_nested_display_list(&built_list);
+
+    builder.pop_clip_id();
+
+    builder.pop_stacking_context();
+}
+
+lazy_static! {
+    static ref CURSOR_POSITION: Mutex<WorldPoint> = Mutex::new(WorldPoint::zero());
+}
+
+fn event_handler(event: &glutin::Event,
+                 api: &RenderApi)
+{
+    match *event {
+        glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
+            let offset = match key {
+                 glutin::VirtualKeyCode::Down => (0.0, -10.0),
+                 glutin::VirtualKeyCode::Up => (0.0, 10.0),
+                 glutin::VirtualKeyCode::Right => (-10.0, 0.0),
+                 glutin::VirtualKeyCode::Left => (10.0, 0.0),
+                 _ => return,
+            };
+
+            api.scroll(ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
+                       *CURSOR_POSITION.lock().unwrap(),
+                       ScrollEventPhase::Start);
+        }
+        glutin::Event::MouseMoved(x, y) => {
+            *CURSOR_POSITION.lock().unwrap() = WorldPoint::new(x as f32, y as f32);
+        }
+        glutin::Event::MouseWheel(delta, _, event_cursor_position) => {
+            if let Some((x, y)) = event_cursor_position {
+                *CURSOR_POSITION.lock().unwrap() = WorldPoint::new(x as f32, y as f32);
+            }
+
+            const LINE_HEIGHT: f32 = 38.0;
+            let (dx, dy) = match delta {
+                glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
+                glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
+            };
+
+            api.scroll(ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
+                       *CURSOR_POSITION.lock().unwrap(),
+                       ScrollEventPhase::Start);
+        }
+        _ => ()
+    }
+}
+
+fn main() {
+    boilerplate::main_wrapper(body, event_handler, None);
+}
--- a/gfx/webrender/res/clip_shared.glsl
+++ b/gfx/webrender/res/clip_shared.glsl
@@ -10,31 +10,34 @@
 #define SEGMENT_CORNER_TR   2
 #define SEGMENT_CORNER_BL   3
 #define SEGMENT_CORNER_BR   4
 
 in int aClipRenderTaskIndex;
 in int aClipLayerIndex;
 in int aClipDataIndex;
 in int aClipSegmentIndex;
+in int aClipResourceAddress;
 
 struct CacheClipInstance {
     int render_task_index;
     int layer_index;
     int data_index;
     int segment_index;
+    int resource_address;
 };
 
 CacheClipInstance fetch_clip_item(int index) {
     CacheClipInstance cci;
 
     cci.render_task_index = aClipRenderTaskIndex;
     cci.layer_index = aClipLayerIndex;
     cci.data_index = aClipDataIndex;
     cci.segment_index = aClipSegmentIndex;
+    cci.resource_address = aClipResourceAddress;
 
     return cci;
 }
 
 struct ClipVertexInfo {
     vec3 local_pos;
     vec2 screen_pos;
     RectWithSize clipped_local_rect;
--- a/gfx/webrender/res/cs_clip_image.vs.glsl
+++ b/gfx/webrender/res/cs_clip_image.vs.glsl
@@ -1,37 +1,36 @@
 #line 1
 /* 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/. */
 
 struct ImageMaskData {
-    RectWithSize uv_rect;
     RectWithSize local_rect;
 };
 
 ImageMaskData fetch_mask_data(int index) {
     vec4 data[2] = fetch_data_2(index);
-    return ImageMaskData(RectWithSize(data[0].xy, data[0].zw),
-                         RectWithSize(data[1].xy, data[1].zw));
+    return ImageMaskData(RectWithSize(data[0].xy, data[0].zw));
 }
 
 void main(void) {
     CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
     ClipArea area = fetch_clip_area(cci.render_task_index);
     Layer layer = fetch_layer(cci.layer_index);
     ImageMaskData mask = fetch_mask_data(cci.data_index);
     RectWithSize local_rect = mask.local_rect;
+    ImageResource res = fetch_image_resource(cci.resource_address);
 
     ClipVertexInfo vi = write_clip_tile_vertex(local_rect,
                                                layer,
                                                area,
                                                cci.segment_index);
 
     vPos = vi.local_pos;
 
     vClipMaskUv = vec3((vPos.xy / vPos.z - local_rect.p0) / local_rect.size, 0.0);
     vec2 texture_size = vec2(textureSize(sColor0, 0));
-    vClipMaskUvRect = vec4(mask.uv_rect.p0, mask.uv_rect.size) / texture_size.xyxy;
+    vClipMaskUvRect = vec4(res.uv_rect.xy, res.uv_rect.zw - res.uv_rect.xy) / texture_size.xyxy;
     // applying a half-texel offset to the UV boundaries to prevent linear samples from the outside
-    vec4 inner_rect = vec4(mask.uv_rect.p0, mask.uv_rect.p0 + mask.uv_rect.size);
+    vec4 inner_rect = vec4(res.uv_rect.xy, res.uv_rect.zw);
     vClipMaskUvInnerRect = (inner_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
 }
--- a/gfx/webrender/res/cs_text_run.vs.glsl
+++ b/gfx/webrender/res/cs_text_run.vs.glsl
@@ -7,25 +7,26 @@
 // drawn un-transformed. These are used for effects such
 // as text-shadow.
 
 void main(void) {
     Primitive prim = load_primitive();
     TextRun text = fetch_text_run(prim.specific_prim_address);
 
     int glyph_index = prim.user_data0;
-    int resource_address = prim.user_data1;
+    int resource_address = prim.user_data2;
     Glyph glyph = fetch_glyph(prim.specific_prim_address, glyph_index);
-    ResourceRect res = fetch_resource_rect(resource_address + glyph_index);
+    GlyphResource res = fetch_glyph_resource(resource_address);
 
     // Glyphs size is already in device-pixels.
     // The render task origin is in device-pixels. Offset that by
     // the glyph offset, relative to its primitive bounding rect.
     vec2 size = res.uv_rect.zw - res.uv_rect.xy;
-    vec2 origin = prim.task.screen_space_origin + uDevicePixelRatio * (glyph.offset - prim.local_rect.p0);
+    vec2 local_pos = glyph.offset + vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio;
+    vec2 origin = prim.task.screen_space_origin + uDevicePixelRatio * (local_pos - prim.local_rect.p0);
     vec4 local_rect = vec4(origin, size);
 
     vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = res.uv_rect.xy / texture_size;
     vec2 st1 = res.uv_rect.zw / texture_size;
 
     vec2 pos = mix(local_rect.xy,
                    local_rect.xy + local_rect.zw,
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -5,17 +5,26 @@
 
 #if defined(GL_ES)
     #if GL_ES == 1
         #ifdef GL_FRAGMENT_PRECISION_HIGH
         precision highp sampler2DArray;
         #else
         precision mediump sampler2DArray;
         #endif
+
+        // Sampler default precision is lowp on mobile GPUs.
+        // This causes RGBA32F texture data to be clamped to 16 bit floats on some GPUs (e.g. Mali-T880).
+        // Define highp precision macro to allow lossless FLOAT texture sampling.
+        #define HIGHP_SAMPLER_FLOAT highp
+    #else
+        #define HIGHP_SAMPLER_FLOAT
     #endif
+#else
+    #define HIGHP_SAMPLER_FLOAT
 #endif
 
 #define PST_TOP_LEFT     0
 #define PST_TOP          1
 #define PST_TOP_RIGHT    2
 #define PST_RIGHT        3
 #define PST_BOTTOM_RIGHT 4
 #define PST_BOTTOM       5
@@ -75,27 +84,18 @@ RectWithSize to_rect_with_size(RectWithE
 
     return result;
 }
 
 vec2 clamp_rect(vec2 point, RectWithSize rect) {
     return clamp(point, rect.p0, rect.p0 + rect.size);
 }
 
-vec2 clamp_rect(vec2 point, RectWithEndpoint rect) {
-    return clamp(point, rect.p0, rect.p1);
-}
-
-// Clamp 2 points at once.
-vec4 clamp_rect(vec4 points, RectWithSize rect) {
-    return clamp(points, rect.p0.xyxy, rect.p0.xyxy + rect.size.xyxy);
-}
-
 RectWithSize intersect_rect(RectWithSize a, RectWithSize b) {
-    vec4 p = clamp_rect(vec4(a.p0, a.p0 + a.size), b);
+    vec4 p = clamp(vec4(a.p0, a.p0 + a.size), b.p0.xyxy, b.p0.xyxy + b.size.xyxy);
     return RectWithSize(p.xy, max(vec2(0.0), p.zw - p.xy));
 }
 
 float distance_to_line(vec2 p0, vec2 perp_dir, vec2 p) {
     vec2 dir_to_p0 = p0 - p;
     return dot(normalize(perp_dir), dir_to_p0);
 }
 
@@ -113,17 +113,17 @@ varying vec3 vClipMaskUv;
 //           use 2x unsigned shorts as vertex attributes
 //           instead of an int, and encode the UV directly
 //           in the vertices.
 ivec2 get_resource_cache_uv(int address) {
     return ivec2(address % WR_MAX_VERTEX_TEXTURE_WIDTH,
                  address / WR_MAX_VERTEX_TEXTURE_WIDTH);
 }
 
-uniform sampler2D sResourceCache;
+uniform HIGHP_SAMPLER_FLOAT sampler2D sResourceCache;
 
 vec4[2] fetch_from_resource_cache_2(int address) {
     ivec2 uv = get_resource_cache_uv(address);
     return vec4[2](
         texelFetchOffset(sResourceCache, uv, 0, ivec2(0, 0)),
         texelFetchOffset(sResourceCache, uv, 0, ivec2(1, 0))
     );
 }
@@ -132,38 +132,31 @@ vec4[2] fetch_from_resource_cache_2(int 
 
 #define VECS_PER_LAYER              9
 #define VECS_PER_RENDER_TASK        3
 #define VECS_PER_PRIM_HEADER        2
 #define VECS_PER_TEXT_RUN           1
 #define VECS_PER_GRADIENT           3
 #define VECS_PER_GRADIENT_STOP      2
 
-uniform sampler2D sLayers;
-uniform sampler2D sRenderTasks;
+uniform HIGHP_SAMPLER_FLOAT sampler2D sLayers;
+uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
 
-uniform sampler2D sData16;
-uniform sampler2D sData32;
-uniform sampler2D sResourceRects;
+uniform HIGHP_SAMPLER_FLOAT sampler2D sData32;
 
 // Instanced attributes
 in ivec4 aData0;
 in ivec4 aData1;
 
 // get_fetch_uv is a macro to work around a macOS Intel driver parsing bug.
 // TODO: convert back to a function once the driver issues are resolved, if ever.
 // https://github.com/servo/webrender/pull/623
 // https://github.com/servo/servo/issues/13953
 #define get_fetch_uv(i, vpi)  ivec2(vpi * (i % (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)), i / (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))
 
-vec4 fetch_data_1(int index) {
-    ivec2 uv = get_fetch_uv(index, 1);
-    return texelFetch(sData16, uv, 0);
-}
-
 vec4[2] fetch_data_2(int index) {
     ivec2 uv = get_fetch_uv(index, 2);
     return vec4[2](
         texelFetchOffset(sData32, uv, 0, ivec2(0, 0)),
         texelFetchOffset(sData32, uv, 0, ivec2(1, 0))
     );
 }
 
@@ -450,29 +443,31 @@ struct PrimitiveInstance {
     int prim_address;
     int specific_prim_address;
     int render_task_index;
     int clip_task_index;
     int layer_index;
     int z;
     int user_data0;
     int user_data1;
+    int user_data2;
 };
 
 PrimitiveInstance fetch_prim_instance() {
     PrimitiveInstance pi;
 
     pi.prim_address = aData0.x;
     pi.specific_prim_address = pi.prim_address + VECS_PER_PRIM_HEADER;
     pi.render_task_index = aData0.y;
     pi.clip_task_index = aData0.z;
     pi.layer_index = aData0.w;
     pi.z = aData1.x;
     pi.user_data0 = aData1.y;
     pi.user_data1 = aData1.z;
+    pi.user_data2 = aData1.w;
 
     return pi;
 }
 
 struct CompositeInstance {
     int render_task_index;
     int src_task_index;
     int backdrop_task_index;
@@ -499,16 +494,17 @@ struct Primitive {
     Layer layer;
     ClipArea clip_area;
     AlphaBatchTask task;
     RectWithSize local_rect;
     RectWithSize local_clip_rect;
     int specific_prim_address;
     int user_data0;
     int user_data1;
+    int user_data2;
     float z;
 };
 
 Primitive load_primitive() {
     PrimitiveInstance pi = fetch_prim_instance();
 
     Primitive prim;
 
@@ -518,16 +514,17 @@ Primitive load_primitive() {
 
     vec4 geom[2] = fetch_from_resource_cache_2(pi.prim_address);
     prim.local_rect = RectWithSize(geom[0].xy, geom[0].zw);
     prim.local_clip_rect = RectWithSize(geom[1].xy, geom[1].zw);
 
     prim.specific_prim_address = pi.specific_prim_address;
     prim.user_data0 = pi.user_data0;
     prim.user_data1 = pi.user_data1;
+    prim.user_data2 = pi.user_data2;
     prim.z = float(pi.z);
 
     return prim;
 }
 
 // Return the intersection of the plane (set up by "normal" and "point")
 // with the ray (set up by "ray_origin" and "ray_dir"),
 // writing the resulting scaler into "t".
@@ -566,58 +563,81 @@ vec4 get_layer_pos(vec2 pos, Layer layer
     // get a point on the layer plane
     vec4 ah = layer.transform * vec4(0.0, 0.0, 0.0, 1.0);
     vec3 a = ah.xyz / ah.w;
     // get the normal to the layer plane
     vec3 n = transpose(mat3(layer.inv_transform)) * vec3(0.0, 0.0, 1.0);
     return untransform(pos, n, a, layer.inv_transform);
 }
 
+// Compute a snapping offset in world space (adjusted to pixel ratio),
+// given local position on the layer and a snap rectangle.
+vec2 compute_snap_offset(vec2 local_pos,
+                         RectWithSize local_clip_rect,
+                         Layer layer,
+                         RectWithSize snap_rect) {
+    // Ensure that the snap rect is at *least* one device pixel in size.
+    // TODO(gw): It's not clear to me that this is "correct". Specifically,
+    //           how should it interact with sub-pixel snap rects when there
+    //           is a layer transform with scale present? But it does fix
+    //           the test cases we have in Servo that are failing without it
+    //           and seem better than not having this at all.
+    snap_rect.size = max(snap_rect.size, vec2(1.0 / uDevicePixelRatio));
+
+    // Transform the snap corners to the world space.
+    vec4 world_snap_p0 = layer.transform * vec4(snap_rect.p0, 0.0, 1.0);
+    vec4 world_snap_p1 = layer.transform * vec4(snap_rect.p0 + snap_rect.size, 0.0, 1.0);
+    // Snap bounds in world coordinates, adjusted for pixel ratio. XY = top left, ZW = bottom right
+    vec4 world_snap = uDevicePixelRatio * vec4(world_snap_p0.xy, world_snap_p1.xy) /
+                                          vec4(world_snap_p0.ww, world_snap_p1.ww);
+    /// World offsets applied to the corners of the snap rectangle.
+    vec4 snap_offsets = floor(world_snap + 0.5) - world_snap;
+
+    /// Compute the position of this vertex inside the snap rectangle.
+    vec2 normalized_snap_pos = (local_pos - snap_rect.p0) / snap_rect.size;
+    /// Compute the actual world offset for this vertex needed to make it snap.
+    return mix(snap_offsets.xy, snap_offsets.zw, normalized_snap_pos);
+}
+
 struct VertexInfo {
     vec2 local_pos;
     vec2 screen_pos;
 };
 
 VertexInfo write_vertex(RectWithSize instance_rect,
                         RectWithSize local_clip_rect,
                         float z,
                         Layer layer,
                         AlphaBatchTask task,
-                        vec2 snap_ref) {
+                        RectWithSize snap_rect) {
+
     // Select the corner of the local rect that we are processing.
     vec2 local_pos = instance_rect.p0 + instance_rect.size * aPosition.xy;
 
-    // xy = top left corner of the local rect, zw = position of current vertex.
-    vec4 local_p0_pos = vec4(snap_ref, local_pos);
-
     // Clamp to the two local clip rects.
-    local_p0_pos = clamp_rect(local_p0_pos, local_clip_rect);
-    local_p0_pos = clamp_rect(local_p0_pos, layer.local_clip_rect);
+    vec2 clamped_local_pos = clamp_rect(clamp_rect(local_pos, local_clip_rect),
+                                        layer.local_clip_rect);
 
-    // Transform the top corner and current vertex to world space.
-    vec4 world_p0 = layer.transform * vec4(local_p0_pos.xy, 0.0, 1.0);
-    world_p0.xyz /= world_p0.w;
-    vec4 world_pos = layer.transform * vec4(local_p0_pos.zw, 0.0, 1.0);
-    world_pos.xyz /= world_pos.w;
+    /// Compute the snapping offset.
+    vec2 snap_offset = compute_snap_offset(clamped_local_pos, local_clip_rect, layer, snap_rect);
 
-    // Convert the world positions to device pixel space. xy=top left corner. zw=current vertex.
-    vec4 device_p0_pos = vec4(world_p0.xy, world_pos.xy) * uDevicePixelRatio;
+    // Transform the current vertex to the world cpace.
+    vec4 world_pos = layer.transform * vec4(clamped_local_pos, 0.0, 1.0);
 
-    // Calculate the distance to snap the vertex by (snap top left corner).
-    vec2 snap_delta = device_p0_pos.xy - floor(device_p0_pos.xy + 0.5);
+    // Convert the world positions to device pixel space.
+    vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
 
     // Apply offsets for the render task to get correct screen location.
-    vec2 final_pos = device_p0_pos.zw -
-                     snap_delta -
+    vec2 final_pos = device_pos + snap_offset -
                      task.screen_space_origin +
                      task.render_target_origin;
 
     gl_Position = uTransform * vec4(final_pos, z, 1.0);
 
-    VertexInfo vi = VertexInfo(local_p0_pos.zw, device_p0_pos.zw);
+    VertexInfo vi = VertexInfo(clamped_local_pos, device_pos);
     return vi;
 }
 
 #ifdef WR_FEATURE_TRANSFORM
 
 struct TransformVertexInfo {
     vec3 local_pos;
     vec2 screen_pos;
@@ -642,17 +662,17 @@ vec2 intersect_lines(vec2 p0, vec2 p1, v
     return vec2(nx / d, ny / d);
 }
 
 TransformVertexInfo write_transform_vertex(RectWithSize instance_rect,
                                            RectWithSize local_clip_rect,
                                            float z,
                                            Layer layer,
                                            AlphaBatchTask task,
-                                           vec2 snap_ref) {
+                                           RectWithSize snap_rect) {
     RectWithEndpoint local_rect = to_rect_with_endpoint(instance_rect);
 
     vec2 current_local_pos, prev_local_pos, next_local_pos;
 
     // Select the current vertex and the previous/next vertices,
     // based on the vertex ID that is known based on the instance rect.
     switch (gl_VertexID) {
         case 0:
@@ -701,50 +721,53 @@ TransformVertexInfo write_transform_vert
     vec2 adjusted_next_p1 = next_device_pos + norm_next * amount;
 
     // Intersect those adjusted lines to find the actual vertex position.
     vec2 device_pos = intersect_lines(adjusted_prev_p0,
                                       adjusted_prev_p1,
                                       adjusted_next_p0,
                                       adjusted_next_p1);
 
-    // Calculate the snap amount based on the first vertex as a reference point.
-    vec4 world_p0 = layer.transform * vec4(snap_ref, 0.0, 1.0);
-    vec2 device_p0 = uDevicePixelRatio * world_p0.xy / world_p0.w;
-    vec2 snap_delta = device_p0 - floor(device_p0 + 0.5);
+    vec4 layer_pos = get_layer_pos(device_pos / uDevicePixelRatio, layer);
+
+    /// Compute the snapping offset.
+    vec2 snap_offset = compute_snap_offset(layer_pos.xy / layer_pos.w,
+                                           local_clip_rect, layer, snap_rect);
 
     // Apply offsets for the render task to get correct screen location.
-    vec2 final_pos = device_pos -
-                     snap_delta -
+    vec2 final_pos = device_pos + snap_offset -
                      task.screen_space_origin +
                      task.render_target_origin;
 
     gl_Position = uTransform * vec4(final_pos, z, 1.0);
 
     vLocalBounds = vec4(local_rect.p0, local_rect.p1);
 
-    vec4 layer_pos = get_layer_pos(device_pos / uDevicePixelRatio, layer);
-
     return TransformVertexInfo(layer_pos.xyw, device_pos);
 }
 
 #endif //WR_FEATURE_TRANSFORM
 
-struct ResourceRect {
+struct GlyphResource {
+    vec4 uv_rect;
+    vec2 offset;
+};
+
+GlyphResource fetch_glyph_resource(int address) {
+    vec4 data[2] = fetch_from_resource_cache_2(address);
+    return GlyphResource(data[0], data[1].xy);
+}
+
+struct ImageResource {
     vec4 uv_rect;
 };
 
-ResourceRect fetch_resource_rect(int index) {
-    ResourceRect rect;
-
-    ivec2 uv = get_fetch_uv(index, 1);
-
-    rect.uv_rect = texelFetchOffset(sResourceRects, uv, 0, ivec2(0, 0));
-
-    return rect;
+ImageResource fetch_image_resource(int address) {
+    vec4 data = fetch_from_resource_cache_1(address);
+    return ImageResource(data);
 }
 
 struct Rectangle {
     vec4 color;
 };
 
 Rectangle fetch_rectangle(int address) {
     vec4 data = fetch_from_resource_cache_1(address);
@@ -758,21 +781,22 @@ struct TextRun {
 TextRun fetch_text_run(int address) {
     vec4 data = fetch_from_resource_cache_1(address);
     return TextRun(data);
 }
 
 struct Image {
     vec4 stretch_size_and_tile_spacing;  // Size of the actual image and amount of space between
                                          //     tiled instances of this image.
+    vec4 sub_rect;                          // If negative, ignored.
 };
 
 Image fetch_image(int address) {
-    vec4 data = fetch_from_resource_cache_1(address);
-    return Image(data);
+    vec4 data[2] = fetch_from_resource_cache_2(address);
+    return Image(data[0], data[1]);
 }
 
 struct YuvImage {
     vec2 size;
 };
 
 YuvImage fetch_yuv_image(int address) {
     vec4 data = fetch_from_resource_cache_1(address);
--- a/gfx/webrender/res/ps_angle_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_angle_gradient.vs.glsl
@@ -7,17 +7,17 @@ void main(void) {
     Primitive prim = load_primitive();
     Gradient gradient = fetch_gradient(prim.specific_prim_address);
 
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
-                                 prim.local_rect.p0);
+                                 prim.local_rect);
 
     vPos = vi.local_pos - prim.local_rect.p0;
 
     vec2 start_point = gradient.start_end_point.xy;
     vec2 end_point = gradient.start_end_point.zw;
     vec2 dir = end_point - start_point;
 
     vStartPoint = start_point;
--- a/gfx/webrender/res/ps_border_corner.vs.glsl
+++ b/gfx/webrender/res/ps_border_corner.vs.glsl
@@ -266,21 +266,21 @@ void main(void) {
     segment_rect.size = p1 - p0;
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(segment_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
                                                     prim.task,
-                                                    prim.local_rect.p0);
+                                                    prim.local_rect);
 #else
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
-                                 prim.local_rect.p0);
+                                 prim.local_rect);
 #endif
 
     vLocalPos = vi.local_pos;
     write_clip(vi.screen_pos, prim.clip_area);
 }
--- a/gfx/webrender/res/ps_border_edge.vs.glsl
+++ b/gfx/webrender/res/ps_border_edge.vs.glsl
@@ -179,21 +179,21 @@ void main(void) {
     write_color(color, style, color_flip);
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(segment_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
                                                     prim.task,
-                                                    prim.local_rect.p0);
+                                                    prim.local_rect);
 #else
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
-                                 prim.local_rect.p0);
+                                 prim.local_rect);
 #endif
 
     vLocalPos = vi.local_pos;
     write_clip(vi.screen_pos, prim.clip_area);
 }
--- a/gfx/webrender/res/ps_box_shadow.vs.glsl
+++ b/gfx/webrender/res/ps_box_shadow.vs.glsl
@@ -10,17 +10,17 @@ void main(void) {
     BoxShadow bs = fetch_boxshadow(prim.specific_prim_address);
     RectWithSize segment_rect = fetch_instance_geometry(prim.specific_prim_address + BS_HEADER_VECS + prim.user_data0);
 
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
-                                 prim.local_rect.p0);
+                                 prim.local_rect);
 
     RenderTaskData child_task = fetch_render_task(prim.user_data1);
     vUv.z = child_task.data1.x;
 
     // Constant offsets to inset from bilinear filtering border.
     vec2 patch_origin = child_task.data0.xy + vec2(1.0);
     vec2 patch_size_device_pixels = child_task.data0.zw - vec2(2.0);
     vec2 patch_size = patch_size_device_pixels / uDevicePixelRatio;
--- a/gfx/webrender/res/ps_cache_image.vs.glsl
+++ b/gfx/webrender/res/ps_cache_image.vs.glsl
@@ -9,17 +9,17 @@
 void main(void) {
     Primitive prim = load_primitive();
 
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
-                                 prim.local_rect.p0);
+                                 prim.local_rect);
 
     RenderTaskData child_task = fetch_render_task(prim.user_data1);
     vUv.z = child_task.data1.x;
 
     vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
     vec2 uv0 = child_task.data0.xy / texture_size;
     vec2 uv1 = (child_task.data0.xy + child_task.data0.zw) / texture_size;
 
--- a/gfx/webrender/res/ps_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_gradient.vs.glsl
@@ -61,26 +61,26 @@ void main(void) {
     }
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(segment_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
                                                     prim.task,
-                                                    prim.local_rect.p0);
+                                                    prim.local_rect);
     vLocalPos = vi.local_pos;
     vec2 f = (vi.local_pos.xy - prim.local_rect.p0) / prim.local_rect.size;
 #else
     VertexInfo vi = write_vertex(segment_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
-                                 prim.local_rect.p0);
+                                 prim.local_rect);
 
     vec2 f = (vi.local_pos - segment_rect.p0) / segment_rect.size;
     vPos = vi.local_pos;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
     vColor = mix(adjusted_color_g0, adjusted_color_g1, dot(f, axis));
--- a/gfx/webrender/res/ps_image.vs.glsl
+++ b/gfx/webrender/res/ps_image.vs.glsl
@@ -1,49 +1,59 @@
 #line 1
 /* 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/. */
 
 void main(void) {
     Primitive prim = load_primitive();
     Image image = fetch_image(prim.specific_prim_address);
-    ResourceRect res = fetch_resource_rect(prim.user_data0);
+    ImageResource res = fetch_image_resource(prim.user_data0);
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
                                                     prim.task,
-                                                    prim.local_rect.p0);
+                                                    prim.local_rect);
     vLocalPos = vi.local_pos;
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
-                                 prim.local_rect.p0);
+                                 prim.local_rect);
     vLocalPos = vi.local_pos - prim.local_rect.p0;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
     // If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
     // non-normalized texture coordinates.
 #ifdef WR_FEATURE_TEXTURE_RECT
     vec2 texture_size_normalization_factor = vec2(1, 1);
 #else
     vec2 texture_size_normalization_factor = vec2(textureSize(sColor0, 0));
 #endif
 
+    vec2 uv0, uv1;
+
+    if (image.sub_rect.x < 0.0) {
+        uv0 = res.uv_rect.xy;
+        uv1 = res.uv_rect.zw;
+    } else {
+        uv0 = res.uv_rect.xy + image.sub_rect.xy;
+        uv1 = res.uv_rect.xy + image.sub_rect.zw;
+    }
+
     // vUv will contain how many times this image has wrapped around the image size.
-    vec2 st0 = res.uv_rect.xy / texture_size_normalization_factor;
-    vec2 st1 = res.uv_rect.zw / texture_size_normalization_factor;
+    vec2 st0 = uv0 / texture_size_normalization_factor;
+    vec2 st1 = uv1 / texture_size_normalization_factor;
 
     vTextureSize = st1 - st0;
     vTextureOffset = st0;
     vTileSpacing = image.stretch_size_and_tile_spacing.zw;
     vStretchSize = image.stretch_size_and_tile_spacing.xy;
 
     // We clamp the texture coordinates to the half-pixel offset from the borders
     // in order to avoid sampling outside of the texture area.
--- a/gfx/webrender/res/ps_radial_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_radial_gradient.vs.glsl
@@ -7,17 +7,17 @@ void main(void) {
     Primitive prim = load_primitive();
     RadialGradient gradient = fetch_radial_gradient(prim.specific_prim_address);
 
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
-                                 prim.local_rect.p0);
+                                 prim.local_rect);
 
     vPos = vi.local_pos - prim.local_rect.p0;
 
     vStartCenter = gradient.start_end_center.xy;
     vEndCenter = gradient.start_end_center.zw;
 
     vStartRadius = gradient.start_end_radius_ratio_xy_extend_mode.x;
     vEndRadius = gradient.start_end_radius_ratio_xy_extend_mode.y;
--- a/gfx/webrender/res/ps_rectangle.vs.glsl
+++ b/gfx/webrender/res/ps_rectangle.vs.glsl
@@ -8,23 +8,23 @@ void main(void) {
     Rectangle rect = fetch_rectangle(prim.specific_prim_address);
     vColor = rect.color;
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
                                                     prim.task,
-                                                    prim.local_rect.p0);
+                                                    prim.local_rect);
     vLocalPos = vi.local_pos;
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
-                                 prim.local_rect.p0);
+                                 prim.local_rect);
 #endif
 
 #ifdef WR_FEATURE_CLIP
     write_clip(vi.screen_pos, prim.clip_area);
 #endif
 }
--- a/gfx/webrender/res/ps_text_run.vs.glsl
+++ b/gfx/webrender/res/ps_text_run.vs.glsl
@@ -3,39 +3,41 @@
  * 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/. */
 
 void main(void) {
     Primitive prim = load_primitive();
     TextRun text = fetch_text_run(prim.specific_prim_address);
 
     int glyph_index = prim.user_data0;
-    int resource_address = prim.user_data1;
+    int resource_address = prim.user_data2;
     Glyph glyph = fetch_glyph(prim.specific_prim_address, glyph_index);
-    ResourceRect res = fetch_resource_rect(resource_address + glyph_index);
+    GlyphResource res = fetch_glyph_resource(resource_address);
 
-    RectWithSize local_rect = RectWithSize(glyph.offset,
+    vec2 local_pos = glyph.offset + vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio;
+
+    RectWithSize local_rect = RectWithSize(local_pos,
                                            (res.uv_rect.zw - res.uv_rect.xy) / uDevicePixelRatio);
 
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
                                                     prim.task,
-                                                    local_rect.p0);
+                                                    local_rect);
     vLocalPos = vi.local_pos;
     vec2 f = (vi.local_pos.xy / vi.local_pos.z - local_rect.p0) / local_rect.size;
 #else
     VertexInfo vi = write_vertex(local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
-                                 local_rect.p0);
+                                 local_rect);
     vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
     vec2 texture_size = vec2(textureSize(sColor0, 0));
     vec2 st0 = res.uv_rect.xy / texture_size;
     vec2 st1 = res.uv_rect.zw / texture_size;
--- a/gfx/webrender/res/ps_yuv_image.vs.glsl
+++ b/gfx/webrender/res/ps_yuv_image.vs.glsl
@@ -6,35 +6,35 @@
 void main(void) {
     Primitive prim = load_primitive();
 #ifdef WR_FEATURE_TRANSFORM
     TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
                                                     prim.local_clip_rect,
                                                     prim.z,
                                                     prim.layer,
                                                     prim.task,
-                                                    prim.local_rect.p0);
+                                                    prim.local_rect);
     vLocalPos = vi.local_pos;
 #else
     VertexInfo vi = write_vertex(prim.local_rect,
                                  prim.local_clip_rect,
                                  prim.z,
                                  prim.layer,
                                  prim.task,
-                                 prim.local_rect.p0);
+                                 prim.local_rect);
     vLocalPos = vi.local_pos - prim.local_rect.p0;
 #endif
 
     write_clip(vi.screen_pos, prim.clip_area);
 
-    ResourceRect y_rect = fetch_resource_rect(prim.user_data0);
+    ImageResource y_rect = fetch_image_resource(prim.user_data0);
 #ifndef WR_FEATURE_INTERLEAVED_Y_CB_CR  // only 1 channel
-    ResourceRect u_rect = fetch_resource_rect(prim.user_data0 + 1);
+    ImageResource u_rect = fetch_image_resource(prim.user_data1);
 #ifndef WR_FEATURE_NV12 // 2 channel
-    ResourceRect v_rect = fetch_resource_rect(prim.user_data0 + 2);
+    ImageResource v_rect = fetch_image_resource(prim.user_data2);
 #endif
 #endif
 
     // If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
     // non-normalized texture coordinates.
 #ifdef WR_FEATURE_TEXTURE_RECT
     vec2 y_texture_size_normalization_factor = vec2(1, 1);
 #else
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -271,16 +271,17 @@ impl VertexFormat {
                                           0);
 
                 instance.bind(gl);
 
                 for (i, &attrib) in [ClipAttribute::RenderTaskIndex,
                                      ClipAttribute::LayerIndex,
                                      ClipAttribute::DataIndex,
                                      ClipAttribute::SegmentIndex,
+                                     ClipAttribute::ResourceAddress,
                                     ].into_iter().enumerate() {
                     gl.enable_vertex_attrib_array(attrib as gl::GLuint);
                     gl.vertex_attrib_divisor(attrib as gl::GLuint, 1);
                     gl.vertex_attrib_i_pointer(attrib as gl::GLuint,
                                                 1,
                                                 gl::INT,
                                                 instance_stride,
                                                 (i * 4) as gl::GLuint);
@@ -406,16 +407,17 @@ impl Program {
                 self.gl.bind_attrib_location(self.id, BlurAttribute::Direction as gl::GLuint, "aBlurDirection");
             }
             VertexFormat::Clip => {
                 self.gl.bind_attrib_location(self.id, ClipAttribute::Position as gl::GLuint, "aPosition");
                 self.gl.bind_attrib_location(self.id, ClipAttribute::RenderTaskIndex as gl::GLuint, "aClipRenderTaskIndex");
                 self.gl.bind_attrib_location(self.id, ClipAttribute::LayerIndex as gl::GLuint, "aClipLayerIndex");
                 self.gl.bind_attrib_location(self.id, ClipAttribute::DataIndex as gl::GLuint, "aClipDataIndex");
                 self.gl.bind_attrib_location(self.id, ClipAttribute::SegmentIndex as gl::GLuint, "aClipSegmentIndex");
+                self.gl.bind_attrib_location(self.id, ClipAttribute::ResourceAddress as gl::GLuint, "aClipResourceAddress");
             }
         }
 
         self.gl.link_program(self.id);
         if self.gl.get_program_iv(self.id, gl::LINK_STATUS) == (0 as gl::GLint) {
             let error_log = self.gl.get_program_info_log(self.id);
             println!("Failed to link shader program: {:?}\n{}", self.name, error_log);
             self.gl.detach_shader(self.id, vs_id);
@@ -1559,21 +1561,16 @@ impl Device {
             self.gl.uniform_1i(u_data32, TextureSampler::Data32 as i32);
         }
 
         let u_resource_cache = self.gl.get_uniform_location(program.id, "sResourceCache");
         if u_resource_cache != -1 {
             self.gl.uniform_1i(u_resource_cache, TextureSampler::ResourceCache as i32);
         }
 
-        let u_resource_rects = self.gl.get_uniform_location(program.id, "sResourceRects");
-        if u_resource_rects != -1 {
-            self.gl.uniform_1i(u_resource_rects, TextureSampler::ResourceRects as i32);
-        }
-
         Ok(())
     }
 
 /*
     pub fn refresh_shader(&mut self, path: PathBuf) {
         let mut vs_preamble_path = self.resource_path.clone();
         vs_preamble_path.push(VERTEX_SHADER_PREAMBLE);
 
@@ -1648,17 +1645,17 @@ impl Device {
                 if cfg!(any(target_arch="arm", target_arch="aarch64")) {
                     expanded_data.extend(data.iter().flat_map(|byte| repeat(*byte).take(4)));
                     (get_gl_format_bgra(self.gl()), 4, expanded_data.as_slice(), gl::UNSIGNED_BYTE)
                 } else {
                     (GL_FORMAT_A, 1, data, gl::UNSIGNED_BYTE)
                 }
             }
             ImageFormat::RGB8 => (gl::RGB, 3, data, gl::UNSIGNED_BYTE),
-            ImageFormat::RGBA8 => (get_gl_format_bgra(self.gl()), 4, data, gl::UNSIGNED_BYTE),
+            ImageFormat::BGRA8 => (get_gl_format_bgra(self.gl()), 4, data, gl::UNSIGNED_BYTE),
             ImageFormat::RG8 => (gl::RG, 2, data, gl::UNSIGNED_BYTE),
             ImageFormat::RGBAF32 => (gl::RGBA, 16, data, gl::FLOAT),
             ImageFormat::Invalid => unreachable!(),
         };
 
         let row_length = match stride {
             Some(value) => value / bpp,
             None => width,
@@ -2028,17 +2025,17 @@ fn gl_texture_formats_for_image_format(g
         ImageFormat::A8 => {
             if cfg!(any(target_arch="arm", target_arch="aarch64")) {
                 (get_gl_format_bgra(gl) as gl::GLint, get_gl_format_bgra(gl))
             } else {
                 (GL_FORMAT_A as gl::GLint, GL_FORMAT_A)
             }
         },
         ImageFormat::RGB8 => (gl::RGB as gl::GLint, gl::RGB),
-        ImageFormat::RGBA8 => {
+        ImageFormat::BGRA8 => {
             match gl.get_type() {
                 gl::GlType::Gl =>  {
                     (gl::RGBA as gl::GLint, get_gl_format_bgra(gl))
                 }
                 gl::GlType::Gles => {
                     (get_gl_format_bgra(gl) as gl::GLint, get_gl_format_bgra(gl))
                 }
             }
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -27,37 +27,131 @@ use webrender_traits::{MixBlendMode, Pip
 use webrender_traits::{ScrollLayerState, ScrollLocation, ScrollPolicy, SpecificDisplayItem};
 use webrender_traits::{StackingContext, TileOffset, TransformStyle, WorldPoint};
 
 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
 pub struct FrameId(pub u32);
 
 static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF { r: 0.3, g: 0.3, b: 0.3, a: 0.6 };
 
+/// Nested display lists cause two types of replacements to ClipIds inside the nesting:
+///     1. References to the root scroll frame are replaced by the ClipIds that
+///        contained the nested display list.
+///     2. Other ClipIds (that aren't custom or reference frames) are assumed to be
+///        local to the nested display list and are converted to an id that is unique
+///        outside of the nested display list as well.
+///
+/// This structure keeps track of what ids are the "root" for one particular level of
+/// nesting as well as keeping and index, which can make ClipIds used internally unique
+/// in the full ClipScrollTree.
+struct NestedDisplayListInfo {
+    /// The index of this nested display list, which is used to generate
+    /// new ClipIds for clips that are defined inside it.
+    nest_index: u64,
+
+    /// The ClipId of the scroll frame node which contains this nested
+    /// display list. This is used to replace references to the root with
+    /// the proper ClipId.
+    scroll_node_id: ClipId,
+
+    /// The ClipId of the clip node which contains this nested display list.
+    /// This is used to replace references to the root with the proper ClipId.
+    clip_node_id: ClipId,
+}
+
+impl NestedDisplayListInfo {
+    fn convert_id_to_nested(&self, id: &ClipId) -> ClipId {
+        match *id {
+            ClipId::Clip(id, _, pipeline_id) => ClipId::Clip(id, self.nest_index, pipeline_id),
+            _ => *id,
+        }
+    }
+
+    fn convert_scroll_id_to_nested(&self, id: &ClipId) -> ClipId {
+        if id.is_root_scroll_node() {
+            self.scroll_node_id
+        } else {
+            self.convert_id_to_nested(id)
+        }
+    }
+
+    fn convert_clip_id_to_nested(&self, id: &ClipId) -> ClipId {
+        if id.is_root_scroll_node() {
+            self.clip_node_id
+        } else {
+            self.convert_id_to_nested(id)
+        }
+    }
+}
+
 struct FlattenContext<'a> {
     scene: &'a Scene,
     builder: &'a mut FrameBuilder,
     resource_cache: &'a mut ResourceCache,
     replacements: Vec<(ClipId, ClipId)>,
+    nested_display_list_info: Vec<NestedDisplayListInfo>,
+    current_nested_display_list_index: u64,
 }
 
 impl<'a> FlattenContext<'a> {
     fn new(scene: &'a Scene,
            builder: &'a mut FrameBuilder,
            resource_cache: &'a mut ResourceCache)
            -> FlattenContext<'a> {
         FlattenContext {
             scene: scene,
             builder: builder,
             resource_cache: resource_cache,
             replacements: Vec::new(),
+            nested_display_list_info: Vec::new(),
+            current_nested_display_list_index: 0,
         }
     }
 
-    fn clip_id_with_replacement(&self, id: ClipId) -> ClipId {
+    fn push_nested_display_list_ids(&mut self, info: ClipAndScrollInfo) {
+        self.current_nested_display_list_index += 1;
+        self.nested_display_list_info.push(NestedDisplayListInfo {
+            nest_index: self.current_nested_display_list_index,
+            scroll_node_id: info.scroll_node_id,
+            clip_node_id: info.clip_node_id(),
+        });
+    }
+
+    fn pop_nested_display_list_ids(&mut self) {
+        self.nested_display_list_info.pop();
+    }
+
+    fn convert_new_id_to_neested(&self, id: &ClipId) -> ClipId {
+        if let Some(nested_info) = self.nested_display_list_info.last() {
+            nested_info.convert_id_to_nested(id)
+        } else {
+            *id
+        }
+    }
+
+    fn convert_clip_scroll_info_to_nested(&self, info: &mut ClipAndScrollInfo) {
+        if let Some(nested_info) = self.nested_display_list_info.last() {
+            info.scroll_node_id = nested_info.convert_scroll_id_to_nested(&info.scroll_node_id);
+            info.clip_node_id =
+                info.clip_node_id.map(|ref id| nested_info.convert_clip_id_to_nested(id));
+        }
+
+        // We only want to produce nested ClipIds if we are in a nested display
+        // list situation.
+        debug_assert!(!info.scroll_node_id.is_nested() ||
+                      !self.nested_display_list_info.is_empty());
+        debug_assert!(!info.clip_node_id().is_nested() ||
+                      !self.nested_display_list_info.is_empty());
+    }
+
+    /// Since WebRender still handles fixed position and reference frame content internally
+    /// we need to apply this table of id replacements only to the id that affects the
+    /// position of a node. We can eventually remove this when clients start handling
+    /// reference frames themselves. This method applies these replacements.
+    fn apply_scroll_frame_id_replacement(&self, id: ClipId) -> ClipId {
         match self.replacements.last() {
             Some(&(to_replace, replacement)) if to_replace == id => replacement,
             _ => id,
         }
     }
 }
 
 // TODO: doc
@@ -270,17 +364,18 @@ impl Frame {
         let clip_viewport = LayerRect::new(content_rect.origin, clip.main.size);
         let new_clip_id = self.clip_scroll_tree.generate_new_clip_id(pipeline_id);
         context.builder.add_clip_scroll_node(new_clip_id,
                                              parent_id,
                                              pipeline_id,
                                              &clip_viewport,
                                              clip,
                                              &mut self.clip_scroll_tree);
-        context.builder.add_scroll_frame(item.id,
+        let new_id = context.convert_new_id_to_neested(&item.id);
+        context.builder.add_scroll_frame(new_id,
                                          new_clip_id,
                                          pipeline_id,
                                          &content_rect,
                                          &clip_viewport,
                                          &mut self.clip_scroll_tree);
 
     }
 
@@ -309,18 +404,16 @@ impl Frame {
                 stacking_context.mix_blend_mode_for_compositing())
         };
 
         if composition_operations.will_make_invisible() {
             traversal.skip_current_stacking_context();
             return;
         }
 
-        let mut clip_id = context.clip_id_with_replacement(context_scroll_node_id);
-
         if stacking_context.scroll_policy == ScrollPolicy::Fixed {
             context.replacements.push((context_scroll_node_id,
                                        context.builder.current_reference_frame_id()));
         }
 
         // If we have a transformation, we establish a new reference frame. This means
         // that fixed position stacking contexts are positioned relative to us.
         let is_reference_frame = stacking_context.transform.is_some() ||
@@ -334,16 +427,17 @@ impl Frame {
                 LayerToScrollTransform::create_translation(reference_frame_relative_offset.x,
                                                            reference_frame_relative_offset.y,
                                                            0.0)
                                         .pre_translate(bounds.origin.to_vector().to_3d())
                                         .pre_mul(&transform)
                                         .pre_mul(&perspective);
 
             let reference_frame_bounds = LayerRect::new(LayerPoint::zero(), bounds.size);
+            let mut clip_id = context.apply_scroll_frame_id_replacement(context_scroll_node_id);
             clip_id = context.builder.push_reference_frame(Some(clip_id),
                                                            pipeline_id,
                                                            &reference_frame_bounds,
                                                            &transform,
                                                            &mut self.clip_scroll_tree);
             context.replacements.push((context_scroll_node_id, clip_id));
             reference_frame_relative_offset = LayerVector2D::zero();
         } else {
@@ -430,18 +524,21 @@ impl Frame {
 
     fn flatten_item<'a, 'b>(&mut self,
                             item: DisplayItemRef<'a, 'b>,
                             pipeline_id: PipelineId,
                             context: &mut FlattenContext,
                             reference_frame_relative_offset: LayerVector2D)
                             -> Option<BuiltDisplayListIter<'a>> {
         let mut clip_and_scroll = item.clip_and_scroll();
+        context.convert_clip_scroll_info_to_nested(&mut clip_and_scroll);
+
+        let unreplaced_scroll_id = clip_and_scroll.scroll_node_id;
         clip_and_scroll.scroll_node_id =
-            context.clip_id_with_replacement(clip_and_scroll.scroll_node_id);
+            context.apply_scroll_frame_id_replacement(clip_and_scroll.scroll_node_id);
 
         match *item.item() {
             SpecificDisplayItem::WebGL(ref info) => {
                 context.builder.add_webgl_rectangle(clip_and_scroll,
                                                     item.rect(),
                                                     item.clip_region(),
                                                     info.context_id);
             }
@@ -567,17 +664,17 @@ impl Frame {
                                            item.display_list()
                                                .get(item.gradient_stops()).count());
             }
             SpecificDisplayItem::PushStackingContext(ref info) => {
                 let mut subtraversal = item.sub_iter();
                 self.flatten_stacking_context(&mut subtraversal,
                                               pipeline_id,
                                               context,
-                                              item.clip_and_scroll().scroll_node_id,
+                                              unreplaced_scroll_id,
                                               reference_frame_relative_offset,
                                               &item.rect(),
                                               &info.stacking_context,
                                               item.filters());
                 return Some(subtraversal);
             }
             SpecificDisplayItem::Iframe(ref info) => {
                 self.flatten_iframe(info.pipeline_id,
@@ -591,16 +688,24 @@ impl Frame {
                 let content_rect = &item.rect().translate(&reference_frame_relative_offset);
                 self.flatten_clip(context,
                                   pipeline_id,
                                   clip_and_scroll.scroll_node_id,
                                   &info,
                                   &content_rect,
                                   item.clip_region());
             }
+            SpecificDisplayItem::PushNestedDisplayList => {
+                // Using the clip and scroll already processed for nesting here
+                // means that in the case of multiple nested display lists, we
+                // will enter the outermost ids into the table and avoid having
+                // to do a replacement for every level of nesting.
+                context.push_nested_display_list_ids(clip_and_scroll);
+            }
+            SpecificDisplayItem::PopNestedDisplayList => context.pop_nested_display_list_ids(),
 
             // Do nothing; these are dummy items for the display list parser
             SpecificDisplayItem::SetGradientStops | SpecificDisplayItem::SetClipRegion(_) => { }
 
             SpecificDisplayItem::PopStackingContext =>
                 unreachable!("Should have returned in parent method."),
         }
         None
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use app_units::Au;
 use frame::FrameId;
 use gpu_cache::GpuCache;
 use gpu_store::GpuStoreAddress;
-use internal_types::{HardwareCompositeOp, SourceTexture};
+use internal_types::HardwareCompositeOp;
 use mask_cache::{ClipMode, ClipSource, MaskCacheInfo, RegionMode};
 use plane_split::{BspSplitter, Polygon, Splitter};
 use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu};
 use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveIndex};
 use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
 use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu};
 use prim_store::{BoxShadowPrimitiveCpu, TexelRect, YuvImagePrimitiveCpu};
 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
@@ -779,21 +779,19 @@ impl FrameBuilder {
 
         let prim_cpu = TextRunPrimitiveCpu {
             font_key: font_key,
             logical_font_size: size,
             blur_radius: blur_radius,
             glyph_range: glyph_range,
             glyph_count: glyph_count,
             glyph_instances: Vec::new(),
-            color_texture_id: SourceTexture::Invalid,
             color: *color,
             render_mode: render_mode,
             glyph_options: glyph_options,
-            resource_address: GpuStoreAddress(0),
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
                            clip_region,
                            &[],
                            PrimitiveContainer::TextRun(prim_cpu));
     }
@@ -1009,20 +1007,18 @@ impl FrameBuilder {
 
     pub fn add_webgl_rectangle(&mut self,
                                clip_and_scroll: ClipAndScrollInfo,
                                rect: LayerRect,
                                clip_region: &ClipRegion,
                                context_id: WebGLContextId) {
         let prim_cpu = ImagePrimitiveCpu {
             kind: ImagePrimitiveKind::WebGL(context_id),
-            color_texture_id: SourceTexture::Invalid,
-            resource_address: GpuStoreAddress(0),
-            sub_rect: None,
-            gpu_block: [rect.size.width, rect.size.height, 0.0, 0.0].into(),
+            gpu_blocks: [ [rect.size.width, rect.size.height, 0.0, 0.0].into(),
+                          TexelRect::invalid().into() ],
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
                            clip_region,
                            &[],
                            PrimitiveContainer::Image(prim_cpu));
     }
@@ -1032,28 +1028,29 @@ impl FrameBuilder {
                      rect: LayerRect,
                      clip_region: &ClipRegion,
                      stretch_size: &LayerSize,
                      tile_spacing: &LayerSize,
                      sub_rect: Option<TexelRect>,
                      image_key: ImageKey,
                      image_rendering: ImageRendering,
                      tile: Option<TileOffset>) {
+        let sub_rect_block = sub_rect.unwrap_or(TexelRect::invalid()).into();
+
         let prim_cpu = ImagePrimitiveCpu {
             kind: ImagePrimitiveKind::Image(image_key,
                                             image_rendering,
                                             tile,
                                             *tile_spacing),
-            color_texture_id: SourceTexture::Invalid,
-            resource_address: GpuStoreAddress(0),
-            sub_rect: sub_rect,
-            gpu_block: [ stretch_size.width,
-                         stretch_size.height,
-                         tile_spacing.width,
-                         tile_spacing.height ].into(),
+            gpu_blocks: [ [ stretch_size.width,
+                            stretch_size.height,
+                            tile_spacing.width,
+                            tile_spacing.height ].into(),
+                            sub_rect_block,
+                        ],
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
                            clip_region,
                            &[],
                            PrimitiveContainer::Image(prim_cpu));
     }
@@ -1071,18 +1068,16 @@ impl FrameBuilder {
             YuvData::PlanarYCbCr(plane_0, plane_1, plane_2) =>
                 [plane_0, plane_1, plane_2],
             YuvData::InterleavedYCbCr(plane_0) =>
                 [plane_0, ImageKey::new(0, 0), ImageKey::new(0, 0)],
         };
 
         let prim_cpu = YuvImagePrimitiveCpu {
             yuv_key: yuv_key,
-            yuv_texture_id: [SourceTexture::Invalid, SourceTexture::Invalid, SourceTexture::Invalid],
-            yuv_resource_address: GpuStoreAddress(0),
             format: format,
             color_space: color_space,
             image_rendering: image_rendering,
             gpu_block: [rect.size.width, rect.size.height, 0.0, 0.0].into(),
         };
 
         self.add_primitive(clip_and_scroll,
                            &rect,
@@ -1408,72 +1403,62 @@ impl FrameBuilder {
                                                       device_pixel_ratio);
 
         let (main_render_task, static_render_task_count) = self.build_render_task(clip_scroll_tree, gpu_cache);
         let mut render_tasks = RenderTaskCollection::new(static_render_task_count);
 
         let mut required_pass_count = 0;
         main_render_task.max_depth(0, &mut required_pass_count);
 
-        resource_cache.block_until_all_resources_added(texture_cache_profile);
+        resource_cache.block_until_all_resources_added(gpu_cache, texture_cache_profile);
 
-        for node in clip_scroll_tree.nodes.values() {
-            if let NodeType::Clip(ref clip_info) = node.node_type {
-                if let Some(ref mask_info) = clip_info.mask_cache_info {
-                    self.prim_store.resolve_clip_cache(mask_info, resource_cache);
-                }
-            }
-        }
-
-        let deferred_resolves = self.prim_store.resolve_primitives(resource_cache,
-                                                                   device_pixel_ratio);
-
-        let gpu_cache_updates = gpu_cache.end_frame(gpu_cache_profile);
+        let mut deferred_resolves = vec![];
 
         let mut passes = Vec::new();
 
         // Do the allocations now, assigning each tile's tasks to a render
         // pass and target as required.
         for index in 0..required_pass_count {
             passes.push(RenderPass::new(index as isize,
                                         index == required_pass_count-1,
                                         cache_size));
         }
 
         main_render_task.assign_to_passes(passes.len() - 1, &mut passes);
 
         for pass in &mut passes {
             let ctx = RenderTargetContext {
+                device_pixel_ratio: device_pixel_ratio,
                 stacking_context_store: &self.stacking_context_store,
                 clip_scroll_group_store: &self.clip_scroll_group_store,
                 prim_store: &self.prim_store,
                 resource_cache: resource_cache,
-                gpu_cache: gpu_cache,
             };
 
-            pass.build(&ctx, &mut render_tasks);
+            pass.build(&ctx, gpu_cache, &mut render_tasks, &mut deferred_resolves);
 
             profile_counters.passes.inc();
             profile_counters.color_targets.add(pass.color_targets.target_count());
             profile_counters.alpha_targets.add(pass.alpha_targets.target_count());
         }
 
+        let gpu_cache_updates = gpu_cache.end_frame(gpu_cache_profile);
+
         resource_cache.end_frame();
 
         Frame {
             device_pixel_ratio: device_pixel_ratio,
             background_color: self.background_color,
             window_size: self.screen_size,
             profile_counters: profile_counters,
             passes: passes,
             cache_size: cache_size,
             layer_texture_data: self.packed_layers.clone(),
             render_task_data: render_tasks.render_task_data,
             gpu_data32: self.prim_store.gpu_data32.build(),
-            gpu_resource_rects: self.prim_store.gpu_resource_rects.build(),
             deferred_resolves: deferred_resolves,
             gpu_cache_updates: Some(gpu_cache_updates),
         }
     }
 
 }
 
 struct LayerRectCalculationAndCullingPass<'a> {
--- a/gfx/webrender/src/freelist.rs
+++ b/gfx/webrender/src/freelist.rs
@@ -78,16 +78,21 @@ impl<T: FreeListItem> FreeList<T> {
         }
     }
 
     pub fn get(&self, id: FreeListItemId) -> &T {
         debug_assert_eq!(self.free_iter().find(|&fid| fid==id), None);
         &self.items[id.0 as usize]
     }
 
+    pub fn get_mut(&mut self, id: FreeListItemId) -> &mut T {
+        debug_assert_eq!(self.free_iter().find(|&fid| fid==id), None);
+        &mut self.items[id.0 as usize]
+    }
+
     #[allow(dead_code)]
     pub fn len(&self) -> usize {
         self.alloc_count
     }
 
     pub fn free(&mut self, id: FreeListItemId) -> T {
         self.alloc_count -= 1;
         let FreeListItemId(index) = id;
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -1,22 +1,25 @@
 /* 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 app_units::Au;
 use device::TextureFilter;
+use fnv::FnvHasher;
 use frame::FrameId;
 use platform::font::{FontContext, RasterizedGlyph};
 use profiler::TextureCacheProfileCounters;
 use rayon::ThreadPool;
 use rayon::prelude::*;
 use resource_cache::ResourceClassCache;
+use std::hash::BuildHasherDefault;
 use std::sync::{Arc, Mutex, MutexGuard};
 use std::sync::mpsc::{channel, Receiver, Sender};
+use std::collections::hash_map::Entry;
 use std::collections::HashSet;
 use std::mem;
 use texture_cache::{TextureCacheItemId, TextureCache};
 use webrender_traits::FontTemplate;
 use webrender_traits::{FontKey, FontRenderMode, ImageData, ImageFormat};
 use webrender_traits::{ImageDescriptor, ColorF, LayoutPoint};
 use webrender_traits::{GlyphKey, GlyphOptions, GlyphInstance, GlyphDimensions};
 
@@ -145,16 +148,17 @@ impl GlyphRasterizer {
         glyph_cache: &mut GlyphCache,
         current_frame_id: FrameId,
         font_key: FontKey,
         size: Au,
         color: ColorF,
         glyph_instances: &[GlyphInstance],
         render_mode: FontRenderMode,
         glyph_options: Option<GlyphOptions>,
+        requested_items: &mut HashSet<TextureCacheItemId, BuildHasherDefault<FnvHasher>>,
     ) {
         assert!(self.font_contexts.lock_shared_context().has_font(&font_key));
 
         let mut glyphs = Vec::with_capacity(glyph_instances.len());
 
         // select glyphs that have not been requested yet.
         for glyph in glyph_instances {
             let glyph_request = GlyphRequest::new(
@@ -162,20 +166,28 @@ impl GlyphRasterizer {
                 size,
                 color,
                 glyph.index,
                 glyph.point,
                 render_mode,
                 glyph_options,
             );
 
-            glyph_cache.mark_as_needed(&glyph_request, current_frame_id);
-            if !glyph_cache.contains_key(&glyph_request) && !self.pending_glyphs.contains(&glyph_request) {
-                self.pending_glyphs.insert(glyph_request.clone());
-                glyphs.push(glyph_request);
+            match glyph_cache.entry(glyph_request.clone(), current_frame_id) {
+                Entry::Occupied(entry) => {
+                    if let &Some(texture_cache_item_id) = entry.get() {
+                        requested_items.insert(texture_cache_item_id);
+                    }
+                }
+                Entry::Vacant(..) => {
+                    if !self.pending_glyphs.contains(&glyph_request) {
+                        self.pending_glyphs.insert(glyph_request.clone());
+                        glyphs.push(glyph_request);
+                    }
+                }
             }
         }
 
         if glyphs.is_empty() {
             return;
         }
 
         let font_contexts = Arc::clone(&self.font_contexts);
@@ -213,16 +225,17 @@ impl GlyphRasterizer {
         self.font_contexts.lock_shared_context().get_glyph_dimensions(glyph_key)
     }
 
     pub fn resolve_glyphs(
         &mut self,
         current_frame_id: FrameId,
         glyph_cache: &mut GlyphCache,
         texture_cache: &mut TextureCache,
+        requested_items: &mut HashSet<TextureCacheItemId, BuildHasherDefault<FnvHasher>>,
         texture_cache_profile: &mut TextureCacheProfileCounters,
     ) {
         let mut rasterized_glyphs = Vec::with_capacity(self.pending_glyphs.len());
 
         // Pull rasterized glyphs from the queue.
         while !self.pending_glyphs.is_empty() {
             // TODO: rather than blocking until all pending glyphs are available
             // we could try_recv and steal work from the thread pool to take advantage
@@ -248,24 +261,26 @@ impl GlyphRasterizer {
         for job in rasterized_glyphs {
             let image_id = job.result.and_then(
                 |glyph| if glyph.width > 0 && glyph.height > 0 {
                     let image_id = texture_cache.insert(
                         ImageDescriptor {
                             width: glyph.width,
                             height: glyph.height,
                             stride: None,
-                            format: ImageFormat::RGBA8,
+                            format: ImageFormat::BGRA8,
                             is_opaque: false,
                             offset: 0,
                         },
                         TextureFilter::Linear,
                         ImageData::Raw(Arc::new(glyph.bytes)),
+                        [glyph.left, glyph.top],
                         texture_cache_profile,
                     );
+                    requested_items.insert(image_id);
                     Some(image_id)
                 } else {
                     None
                 }
             );
 
             glyph_cache.insert(job.request, image_id, current_frame_id);
         }
@@ -340,16 +355,17 @@ fn raterize_200_glyphs() {
 
     use rayon::Configuration;
     use std::fs::File;
     use std::io::Read;
 
     let workers = Arc::new(ThreadPool::new(Configuration::new()).unwrap());
     let mut glyph_rasterizer = GlyphRasterizer::new(workers);
     let mut glyph_cache = GlyphCache::new();
+    let mut requested_items = HashSet::default();
 
     let mut font_file = File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file");
     let mut font_data = vec![];
     font_file.read_to_end(&mut font_data).expect("failed to read font file");
 
     let font_key = FontKey::new(0, 0);
     glyph_rasterizer.add_font(font_key, FontTemplate::Raw(Arc::new(font_data), 0));
 
@@ -368,20 +384,22 @@ fn raterize_200_glyphs() {
             &mut glyph_cache,
             frame_id,
             font_key,
             Au::from_px(32),
             ColorF::new(0.0, 0.0, 0.0, 1.0),
             &glyph_instances[(50 * i)..(50 * (i + 1))],
             FontRenderMode::Subpixel,
             None,
+            &mut requested_items,
         );
     }
 
     glyph_rasterizer.delete_font(font_key);
 
     glyph_rasterizer.resolve_glyphs(
         frame_id,
         &mut glyph_cache,
         &mut TextureCache::new(4096),
+        &mut requested_items,
         &mut TextureCacheProfileCounters::new(),
     );
 }
--- a/gfx/webrender/src/gpu_cache.rs
+++ b/gfx/webrender/src/gpu_cache.rs
@@ -20,16 +20,17 @@
 //! will be invoked to build the data.
 //!
 //! After ```end_frame``` has occurred, callers can
 //! use the ```get_address``` API to get the allocated
 //! address in the GPU cache of a given resource slot
 //! for this frame.
 
 use device::FrameId;
+use internal_types::UvRect;
 use profiler::GpuCacheProfileCounters;
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use std::{mem, u32};
 use webrender_traits::{ColorF, LayerRect};
 
 pub const GPU_CACHE_INITIAL_HEIGHT: u32 = 512;
 const FRAMES_BEFORE_EVICTION: usize = 10;
 const NEW_ROWS_PER_RESIZE: u32 = 512;
@@ -78,16 +79,27 @@ impl Into<GpuBlockData> for LayerRect {
             data: [ self.origin.x,
                     self.origin.y,
                     self.size.width,
                     self.size.height ],
         }
     }
 }
 
+impl Into<GpuBlockData> for UvRect {
+    fn into(self) -> GpuBlockData {
+        GpuBlockData {
+            data: [ self.uv0.x,
+                    self.uv0.y,
+                    self.uv1.x,
+                    self.uv1.y ],
+        }
+    }
+}
+
 // Any data type that can be stored in the GPU cache should
 // implement this trait.
 pub trait ToGpuBlocks {
     // Request an arbitrary number of GPU data blocks.
     fn write_gpu_blocks(&self, GpuDataRequest);
 }
 
 // A handle to a GPU resource.
@@ -280,17 +292,17 @@ impl Texture {
             allocated_block_count: 0,
         }
     }
 
     // Push new data into the cache. The ```pending_block_index``` field represents
     // where the data was pushed into the texture ```pending_blocks``` array.
     // Return the allocated address for this data.
     fn push_data(&mut self,
-                 pending_block_index: usize,
+                 pending_block_index: Option<usize>,
                  block_count: usize,
                  frame_id: FrameId) -> CacheLocation {
         // Find the appropriate free list to use based on the block size.
         let (alloc_size, free_list) = self.free_lists
                                           .get_actual_block_count_and_free_list(block_count);
 
         // See if we need a new row (if free-list has nothing available)
         if free_list.is_none() {
@@ -326,23 +338,25 @@ impl Texture {
         *free_list = block.next;
 
         // Add the block to the occupied linked list.
         block.next = self.occupied_list_head;
         block.last_access_time = frame_id;
         self.occupied_list_head = Some(free_block_index);
         self.allocated_block_count += alloc_size;
 
-        // Add this update to the pending list of blocks that need
-        // to be updated on the GPU.
-        self.updates.push(GpuCacheUpdate::Copy {
-            block_index: pending_block_index,
-            block_count: block_count,
-            address: block.address,
-        });
+        if let Some(pending_block_index) = pending_block_index {
+            // Add this update to the pending list of blocks that need
+            // to be updated on the GPU.
+            self.updates.push(GpuCacheUpdate::Copy {
+                block_index: pending_block_index,
+                block_count: block_count,
+                address: block.address,
+            });
+        }
 
         CacheLocation {
             block_index: free_block_index,
             epoch: block.epoch,
         }
     }
 
     // Run through the list of occupied cache blocks and evict
@@ -425,17 +439,17 @@ impl<'a> GpuDataRequest<'a> {
         self.texture.pending_blocks.extend_from_slice(blocks);
     }
 }
 
 impl<'a> Drop for GpuDataRequest<'a> {
     fn drop(&mut self) {
         // Push the data to the texture pending updates list.
         let block_count = self.texture.pending_blocks.len() - self.start_index;
-        let location = self.texture.push_data(self.start_index,
+        let location = self.texture.push_data(Some(self.start_index),
                                               block_count,
                                               self.frame_id);
         self.handle.location = Some(location);
     }
 }
 
 
 /// The main LRU cache interface.
@@ -496,24 +510,36 @@ impl GpuCache {
     // unconditionally for this frame. The cache handle will
     // assert if the caller tries to retrieve the address
     // of this handle on a subsequent frame. This is typically
     // used for uploading data that changes every frame, and
     // therefore makes no sense to try and cache.
     pub fn push_per_frame_blocks(&mut self, blocks: &[GpuBlockData]) -> GpuCacheHandle {
         let start_index = self.texture.pending_blocks.len();
         self.texture.pending_blocks.extend_from_slice(blocks);
-        let location = self.texture.push_data(start_index,
+        let location = self.texture.push_data(Some(start_index),
                                               blocks.len(),
                                               self.frame_id);
         GpuCacheHandle {
             location: Some(location),
         }
     }
 
+    // Reserve space in the cache for per-frame blocks that
+    // will be resolved by the render thread via the
+    // external image callback.
+    pub fn push_deferred_per_frame_blocks(&mut self, block_count: usize) -> GpuCacheHandle {
+        let location = self.texture.push_data(None,
+                                              block_count,
+                                              self.frame_id);
+        GpuCacheHandle {
+            location: Some(location),
+        }
+    }
+
     /// End the frame. Return the list of updates to apply to the
     /// device specific cache texture.
     pub fn end_frame(&mut self,
                      profile_counters: &mut GpuCacheProfileCounters) -> GpuCacheUpdateList {
         profile_counters.allocated_rows.set(self.texture.rows.len());
         profile_counters.allocated_blocks.set(self.texture.allocated_block_count);
 
         GpuCacheUpdateList {
--- a/gfx/webrender/src/gpu_store.rs
+++ b/gfx/webrender/src/gpu_store.rs
@@ -33,17 +33,17 @@ pub trait GpuStoreLayout {
     fn image_format() -> ImageFormat;
 
     fn texture_width<T>() -> usize;
 
     fn texture_filter() -> TextureFilter;
 
     fn texel_size() -> usize {
         match Self::image_format() {
-            ImageFormat::RGBA8 => 4,
+            ImageFormat::BGRA8 => 4,
             ImageFormat::RGBAF32 => 16,
             _ => unreachable!(),
         }
     }
 
     fn texels_per_item<T>() -> usize {
         let item_size = mem::size_of::<T>();
         let texel_size = Self::texel_size();
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -1,27 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use app_units::Au;
 use device::TextureFilter;
-use euclid::{TypedPoint2D, UnknownUnit};
 use fnv::FnvHasher;
 use profiler::BackendProfileCounters;
 use std::collections::{HashMap, HashSet};
 use std::f32;
 use std::hash::BuildHasherDefault;
 use std::{i32, usize};
 use std::path::PathBuf;
 use std::sync::Arc;
 use tiling;
 use renderer::BlendMode;
 use webrender_traits::{ClipId, ColorF, DeviceUintRect, Epoch, ExternalImageData, ExternalImageId};
-use webrender_traits::{ImageData, ImageFormat, NativeFontHandle, PipelineId};
+use webrender_traits::{DevicePoint, ImageData, ImageFormat, PipelineId};
 
 // An ID for a texture that is owned by the
 // texture cache module. This can include atlases
 // or standalone textures allocated via the
 // texture cache (e.g. if an image is too large
 // to be added to an atlas). The texture cache
 // manages the allocation and freeing of these
 // IDs, and the rendering thread maintains a
@@ -59,17 +58,16 @@ pub enum TextureSampler {
     Color1,
     Color2,
     CacheA8,
     CacheRGBA8,
     Data32,
     ResourceCache,
     Layers,
     RenderTasks,
-    ResourceRects,
     Dither,
 }
 
 impl TextureSampler {
     pub fn color(n: usize) -> TextureSampler {
         match n {
             0 => TextureSampler::Color0,
             1 => TextureSampler::Color1,
@@ -124,16 +122,17 @@ pub enum BlurAttribute {
 pub enum ClipAttribute {
     // vertex frequency
     Position,
     // instance frequency
     RenderTaskIndex,
     LayerIndex,
     DataIndex,
     SegmentIndex,
+    ResourceAddress,
 }
 
 // A packed RGBA8 color ordered for vertex data or similar.
 
 #[derive(Debug, Clone, Copy)]
 #[repr(C)]
 pub struct PackedColor {
     pub r: u8,
@@ -298,21 +297,19 @@ pub enum AxisDirection {
     Horizontal,
     Vertical,
 }
 
 #[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
 pub struct StackingContextIndex(pub usize);
 
 #[derive(Clone, Copy, Debug)]
-pub struct RectUv<T, U = UnknownUnit> {
-    pub top_left: TypedPoint2D<T, U>,
-    pub top_right: TypedPoint2D<T, U>,
-    pub bottom_left: TypedPoint2D<T, U>,
-    pub bottom_right: TypedPoint2D<T, U>,
+pub struct UvRect {
+    pub uv0: DevicePoint,
+    pub uv1: DevicePoint,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 pub enum LowLevelFilterOp {
     Blur(Au, AxisDirection),
     Brightness(Au),
     Contrast(Au),
     Grayscale(Au),
--- a/gfx/webrender/src/mask_cache.rs
+++ b/gfx/webrender/src/mask_cache.rs
@@ -1,21 +1,21 @@
 /* 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 border::BorderCornerClipSource;
 use gpu_store::GpuStoreAddress;
-use prim_store::{ClipData, GpuBlock32, PrimitiveStore};
+use prim_store::{ClipData, GpuBlock32, ImageMaskData, PrimitiveStore};
 use prim_store::{CLIP_DATA_GPU_SIZE, MASK_DATA_GPU_SIZE};
 use renderer::VertexDataStore;
 use util::{ComplexClipRegionHelpers, MatrixHelpers, TransformedRect};
 use webrender_traits::{BorderRadius, BuiltDisplayList, ClipRegion, ComplexClipRegion, ImageMask};
 use webrender_traits::{DeviceIntRect, LayerToWorldTransform};
-use webrender_traits::{LayerRect, LayerPoint, LayerSize};
+use webrender_traits::{DeviceRect, LayerRect, LayerPoint, LayerSize};
 use std::ops::Not;
 
 const MAX_CLIP: f32 = 1000000.0;
 
 #[repr(C)]
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum ClipMode {
     Clip,           // Pixels inside the region are visible.
@@ -267,16 +267,24 @@ impl MaskCacheInfo {
 
             for &mut (ref mut source, gpu_address) in &mut self.border_corners {
                 has_border_clip = true;
                 let slice = clip_store.get_slice_mut(gpu_address,
                                                      1 + source.max_clip_count);
                 source.populate_gpu_data(slice);
             }
 
+            if let Some((ref mask, gpu_address)) = self.image {
+                let mask_data = clip_store.get_slice_mut(gpu_address, MASK_DATA_GPU_SIZE);
+                mask_data[0] = GpuBlock32::from(ImageMaskData {
+                    padding: DeviceRect::zero(),
+                    local_rect: mask.rect,
+                });
+            }
+
             // Work out the type of mask geometry we have, based on the
             // list of clip sources above.
             if has_clip_out || has_border_clip {
                 // For clip-out, the mask rect is not known.
                 self.bounds = Some(MaskBounds::None);
             } else {
                 // TODO(gw): local inner is only valid if there's a single clip (for now).
                 // This can be improved in the future, with some proper
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -26,24 +26,28 @@ pub struct FontContext {
     gamma_lut: GammaLut,
 }
 
 // core text is safe to use on multiple threads and non-shareable resources are
 // all hidden inside their font context.
 unsafe impl Send for FontContext {}
 
 pub struct RasterizedGlyph {
+    pub top: f32,
+    pub left: f32,
     pub width: u32,
     pub height: u32,
     pub bytes: Vec<u8>,
 }
 
 impl RasterizedGlyph {
     pub fn blank() -> RasterizedGlyph {
         RasterizedGlyph {
+            top: 0.0,
+            left: 0.0,
             width: 0,
             height: 0,
             bytes: vec![],
         }
     }
 }
 
 struct GlyphMetrics {
@@ -54,17 +58,17 @@ struct GlyphMetrics {
     rasterized_height: u32,
 }
 
 // According to the Skia source code, there's no public API to
 // determine if subpixel AA is supported. So jrmuizel ported
 // this function from Skia which is used to check if a glyph
 // can be rendered with subpixel AA.
 fn supports_subpixel_aa() -> bool {
-    let mut cg_context = CGContext::create_bitmap_context(1, 1, 8, 4,
+    let mut cg_context = CGContext::create_bitmap_context(None, 1, 1, 8, 4,
                                                           &CGColorSpace::create_device_rgb(),
                                                           kCGImageAlphaNoneSkipFirst |
                                                           kCGBitmapByteOrder32Little);
     let ct_font = core_text::font::new_from_name("Helvetica", 16.).unwrap();
     cg_context.set_should_smooth_fonts(true);
     cg_context.set_should_antialias(true);
     cg_context.set_allows_font_smoothing(true);
     cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0);
@@ -280,17 +284,17 @@ impl FontContext {
             return Some(RasterizedGlyph::blank())
         }
 
         let context_flags = match render_mode {
             FontRenderMode::Subpixel => kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
             FontRenderMode::Alpha | FontRenderMode::Mono => kCGImageAlphaPremultipliedLast,
         };
 
-        let mut cg_context = CGContext::create_bitmap_context(metrics.rasterized_width as usize,
+        let mut cg_context = CGContext::create_bitmap_context(None, metrics.rasterized_width as usize,
                                                               metrics.rasterized_height as usize,
                                                               8,
                                                               metrics.rasterized_width as usize * 4,
                                                               &CGColorSpace::create_device_rgb(),
                                                               context_flags);
 
 
         // Tested on mac OS Sierra, 10.12
@@ -395,15 +399,17 @@ impl FontContext {
 
         self.gamma_correct_pixels(&mut rasterized_pixels,
                                   metrics.rasterized_width as usize,
                                   metrics.rasterized_height as usize,
                                   render_mode,
                                   key.color);
 
         Some(RasterizedGlyph {
+            left: metrics.rasterized_left as f32,
+            top: metrics.rasterized_ascent as f32,
             width: metrics.rasterized_width,
             height: metrics.rasterized_height,
             bytes: rasterized_pixels,
         })
     }
 }
 
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -28,16 +28,18 @@ pub struct FontContext {