Merge mozilla-central to mozilla-inbound
authorarthur.iakab <aiakab@mozilla.com>
Tue, 03 Apr 2018 12:41:24 +0300
changeset 411443 99a953f1823fcf96d6f312b356a11f245a9b0c78
parent 411412 92366b8353b3e187c4653937077841003329426c (current diff)
parent 411442 4a3275936ddf871103b53e00608e2b8d5aee7e69 (diff)
child 411444 4b45a79dd2a322bb36bf1b1eedfd7c5125f747ff
push id101651
push useraiakab@mozilla.com
push dateTue, 03 Apr 2018 09:42:02 +0000
treeherdermozilla-inbound@99a953f1823f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
build/prebuilt-interfaces.manifest
layout/base/nsCSSFrameConstructor.cpp
layout/style/StylePrefs.cpp
layout/style/StylePrefs.h
layout/style/crashtests/crashtests.list
modules/libpref/init/all.js
xpcom/typelib/xpt/xpt_struct.cpp
xpcom/typelib/xpt/xpt_xdr.cpp
xpcom/typelib/xpt/xpt_xdr.h
--- a/browser/base/content/test/performance/browser_preferences_usage.js
+++ b/browser/base/content/test/performance/browser_preferences_usage.js
@@ -73,17 +73,17 @@ add_task(async function startup() {
   let max = 40;
 
   let whitelist = {
     "browser.startup.record": {
       min: 200,
       max: 350,
     },
     "layout.css.prefixes.webkit": {
-      min: 140,
+      min: 135,
       max: 150,
     },
     "browser.search.log": {
       min: 100,
       max: 150,
     },
     "layout.css.dpi": {
       min: 45,
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -144,200 +144,24 @@
 #ifdef _AMD64_
 @BINPATH@/@DLL_PREFIX@qipcap64@DLL_SUFFIX@
 #else
 @BINPATH@/@DLL_PREFIX@qipcap@DLL_SUFFIX@
 #endif
 #endif
 
 ; [Components]
-#ifdef MOZ_ARTIFACT_BUILDS
-@RESPATH@/components/prebuilt-interfaces.manifest
-@RESPATH@/components/interfaces.xpt
-@RESPATH@/browser/components/prebuilt-interfaces.manifest
-@RESPATH@/browser/components/interfaces.xpt
-#endif
-@RESPATH@/components/alerts.xpt
 #ifdef ACCESSIBILITY
 #ifdef XP_WIN32
 @BINPATH@/Accessible.tlb
 @BINPATH@/AccessibleHandler.dll
 @BINPATH@/AccessibleMarshal.dll
 @BINPATH@/IA2Marshal.dll
 #endif
-@RESPATH@/components/accessibility.xpt
 #endif
-@RESPATH@/components/appshell.xpt
-@RESPATH@/components/appstartup.xpt
-@RESPATH@/components/autocomplete.xpt
-@RESPATH@/components/autoconfig.xpt
-@RESPATH@/components/browser-element.xpt
-@RESPATH@/browser/components/browsercompsbase.xpt
-@RESPATH@/browser/components/browser-feeds.xpt
-@RESPATH@/components/caps.xpt
-@RESPATH@/components/chrome.xpt
-#ifdef MOZ_CODE_COVERAGE
-@RESPATH@/components/code-coverage.xpt
-#endif
-@RESPATH@/components/commandhandler.xpt
-@RESPATH@/components/commandlines.xpt
-@RESPATH@/components/composer.xpt
-@RESPATH@/components/content_events.xpt
-@RESPATH@/components/content_html.xpt
-@RESPATH@/components/content_geckomediaplugins.xpt
-#ifdef MOZ_WEBRTC
-@RESPATH@/components/content_webrtc.xpt
-#endif
-@RESPATH@/components/content_xslt.xpt
-@RESPATH@/components/directory.xpt
-@RESPATH@/components/docshell.xpt
-@RESPATH@/components/dom.xpt
-@RESPATH@/components/dom_base.xpt
-@RESPATH@/components/dom_bindings.xpt
-#ifdef MOZ_DEBUG
-@RESPATH@/components/dom_bindings_test.xpt
-#endif
-@RESPATH@/components/dom_file.xpt
-@RESPATH@/components/dom_system.xpt
-@RESPATH@/components/dom_core.xpt
-@RESPATH@/components/dom_events.xpt
-@RESPATH@/components/dom_geolocation.xpt
-@RESPATH@/components/dom_media.xpt
-@RESPATH@/components/dom_network.xpt
-@RESPATH@/components/dom_notification.xpt
-@RESPATH@/components/dom_html.xpt
-@RESPATH@/components/dom_offline.xpt
-@RESPATH@/components/dom_payments.xpt
-@RESPATH@/components/dom_power.xpt
-@RESPATH@/components/dom_push.xpt
-@RESPATH@/components/dom_quota.xpt
-@RESPATH@/components/dom_range.xpt
-@RESPATH@/components/dom_security.xpt
-@RESPATH@/components/dom_sidebar.xpt
-@RESPATH@/components/dom_storage.xpt
-@RESPATH@/components/dom_webauthn.xpt
-#ifdef MOZ_WEBSPEECH
-@RESPATH@/components/dom_webspeechrecognition.xpt
-#endif
-@RESPATH@/components/dom_workers.xpt
-@RESPATH@/components/dom_xul.xpt
-@RESPATH@/components/dom_presentation.xpt
-@RESPATH@/components/downloads.xpt
-@RESPATH@/components/editor.xpt
-@RESPATH@/components/enterprisepolicies.xpt
-@RESPATH@/components/extensions.xpt
-@RESPATH@/components/exthandler.xpt
-@RESPATH@/components/fastfind.xpt
-@RESPATH@/components/feeds.xpt
-@RESPATH@/components/gfx.xpt
-@RESPATH@/components/html5.xpt
-@RESPATH@/components/htmlparser.xpt
-@RESPATH@/components/imglib2.xpt
-@RESPATH@/components/inspector.xpt
-@RESPATH@/components/intl.xpt
-@RESPATH@/components/jar.xpt
-@RESPATH@/components/jsdebugger.xpt
-@RESPATH@/browser/components/jsinspector.xpt
-@RESPATH@/components/layout_base.xpt
-#ifdef MOZ_DEBUG
-@RESPATH@/components/layout_debug.xpt
-#endif
-#ifdef NS_PRINTING
-@RESPATH@/components/layout_printing.xpt
-#endif
-@RESPATH@/components/layout_xul_tree.xpt
-@RESPATH@/components/layout_xul.xpt
-@RESPATH@/components/locale.xpt
-@RESPATH@/browser/components/migration.xpt
-@RESPATH@/components/mimetype.xpt
-@RESPATH@/components/mozfind.xpt
-@RESPATH@/components/mozintl.xpt
-@RESPATH@/components/necko_about.xpt
-@RESPATH@/components/necko_cache.xpt
-@RESPATH@/components/necko_cache2.xpt
-@RESPATH@/components/necko_cookie.xpt
-@RESPATH@/components/necko_dns.xpt
-@RESPATH@/components/necko_file.xpt
-@RESPATH@/components/necko_ftp.xpt
-@RESPATH@/components/necko_http.xpt
-@RESPATH@/components/necko_mdns.xpt
-@RESPATH@/components/necko_res.xpt
-@RESPATH@/components/necko_socket.xpt
-@RESPATH@/components/necko_strconv.xpt
-@RESPATH@/components/necko_viewsource.xpt
-@RESPATH@/components/necko_websocket.xpt
-#ifdef NECKO_WIFI
-@RESPATH@/components/necko_wifi.xpt
-#endif
-@RESPATH@/components/necko_wyciwyg.xpt
-@RESPATH@/components/necko.xpt
-@RESPATH@/components/loginmgr.xpt
-@RESPATH@/components/parentalcontrols.xpt
-#ifdef MOZ_WEBRTC
-@RESPATH@/components/peerconnection.xpt
-#endif
-@RESPATH@/components/places.xpt
-@RESPATH@/components/plugin.xpt
-@RESPATH@/components/pref.xpt
-@RESPATH@/components/prefetch.xpt
-#ifdef MOZ_GECKO_PROFILER
-@RESPATH@/components/profiler.xpt
-#endif
-@RESPATH@/components/rdf.xpt
-@RESPATH@/components/reputationservice.xpt
-@RESPATH@/components/satchel.xpt
-@RESPATH@/components/saxparser.xpt
-@RESPATH@/browser/components/sessionstore.xpt
-@RESPATH@/components/services-crypto-component.xpt
-@RESPATH@/components/captivedetect.xpt
-@RESPATH@/browser/components/shellservice.xpt
-@RESPATH@/components/shistory.xpt
-@RESPATH@/components/spellchecker.xpt
-@RESPATH@/components/storage.xpt
-#ifdef ENABLE_MARIONETTE
-@RESPATH@/components/remote.xpt
-#endif
-@RESPATH@/components/toolkit_asyncshutdown.xpt
-@RESPATH@/components/toolkit_filewatcher.xpt
-@RESPATH@/components/toolkit_finalizationwitness.xpt
-@RESPATH@/components/toolkit_osfile.xpt
-@RESPATH@/components/toolkit_securityreporter.xpt
-@RESPATH@/components/toolkit_perfmonitoring.xpt
-@RESPATH@/components/toolkit_xulstore.xpt
-@RESPATH@/components/toolkitprofile.xpt
-#ifdef MOZ_ENABLE_XREMOTE
-@RESPATH@/components/toolkitremote.xpt
-#endif
-@RESPATH@/components/txtsvc.xpt
-@RESPATH@/components/txmgr.xpt
-@RESPATH@/components/uconv.xpt
-@RESPATH@/components/update.xpt
-@RESPATH@/components/uriloader.xpt
-@RESPATH@/components/urlformatter.xpt
-@RESPATH@/components/webBrowser_core.xpt
-@RESPATH@/components/webbrowserpersist.xpt
-@RESPATH@/components/webextensions.xpt
-@RESPATH@/components/widget.xpt
-#ifdef XP_MACOSX
-@RESPATH@/components/widget_cocoa.xpt
-#endif
-@RESPATH@/components/windowcreator.xpt
-@RESPATH@/components/windowwatcher.xpt
-@RESPATH@/components/xpcom_base.xpt
-@RESPATH@/components/xpcom_system.xpt
-@RESPATH@/components/xpcom_components.xpt
-@RESPATH@/components/xpcom_ds.xpt
-@RESPATH@/components/xpcom_io.xpt
-@RESPATH@/components/xpcom_threads.xpt
-@RESPATH@/components/xpcom_xpti.xpt
-@RESPATH@/components/xpconnect.xpt
-@RESPATH@/components/xulapp.xpt
-@RESPATH@/components/xul.xpt
-@RESPATH@/components/zipwriter.xpt
-@RESPATH@/components/telemetry.xpt
 
 ; JavaScript components
 @RESPATH@/components/ConsoleAPI.manifest
 @RESPATH@/components/ConsoleAPIStorage.js
 @RESPATH@/components/BrowserElementParent.manifest
 @RESPATH@/components/BrowserElementParent.js
 @RESPATH@/components/FeedProcessor.manifest
 @RESPATH@/components/FeedProcessor.js
@@ -366,25 +190,23 @@
 @RESPATH@/browser/components/aboutdevtools-registration.js
 @RESPATH@/browser/components/aboutdevtools.manifest
 @RESPATH@/browser/components/aboutdevtoolstoolbox-registration.js
 @RESPATH@/browser/components/aboutdevtoolstoolbox.manifest
 @RESPATH@/browser/components/nsAboutCapabilities.js
 @RESPATH@/browser/components/aboutcapabilities.manifest
 @RESPATH@/browser/components/Experiments.manifest
 @RESPATH@/browser/components/ExperimentsService.js
-@RESPATH@/browser/components/browser-newtab.xpt
 @RESPATH@/browser/components/aboutNewTabService.js
 @RESPATH@/browser/components/NewTabComponents.manifest
 @RESPATH@/browser/components/EnterprisePolicies.js
 @RESPATH@/browser/components/EnterprisePoliciesContent.js
 @RESPATH@/browser/components/EnterprisePolicies.manifest
 @RESPATH@/components/Downloads.manifest
 @RESPATH@/components/DownloadLegacy.js
-@RESPATH@/components/thumbnails.xpt
 @RESPATH@/components/PageThumbsComponents.manifest
 @RESPATH@/components/crashmonitor.manifest
 @RESPATH@/components/nsCrashMonitor.js
 @RESPATH@/components/toolkitsearch.manifest
 @RESPATH@/components/nsSearchService.js
 @RESPATH@/components/nsSearchSuggestions.js
 @RESPATH@/components/nsSidebar.js
 #ifdef NIGHTLY_BUILD
@@ -394,17 +216,16 @@
 @RESPATH@/components/passwordmgr.manifest
 @RESPATH@/components/nsLoginInfo.js
 @RESPATH@/components/nsLoginManager.js
 @RESPATH@/components/nsLoginManagerPrompter.js
 @RESPATH@/components/storage-json.js
 @RESPATH@/components/crypto-SDR.js
 @RESPATH@/components/TooltipTextProvider.js
 @RESPATH@/components/TooltipTextProvider.manifest
-@RESPATH@/components/webvtt.xpt
 @RESPATH@/components/WebVTT.manifest
 @RESPATH@/components/WebVTTParserWrapper.js
 @RESPATH@/components/nsHelperAppDlg.manifest
 @RESPATH@/components/nsHelperAppDlg.js
 @RESPATH@/components/NetworkGeolocationProvider.manifest
 @RESPATH@/components/NetworkGeolocationProvider.js
 @RESPATH@/components/extensions.manifest
 @RESPATH@/components/EditorUtils.manifest
@@ -514,20 +335,16 @@
 @RESPATH@/components/PeerConnection.manifest
 #endif
 
 @RESPATH@/chrome/marionette@JAREXT@
 @RESPATH@/chrome/marionette.manifest
 @RESPATH@/components/marionette.manifest
 @RESPATH@/components/marionette.js
 
-#ifdef MOZ_WEBSPEECH
-@RESPATH@/components/dom_webspeechsynth.xpt
-#endif
-
 @RESPATH@/components/nsAsyncShutdown.manifest
 @RESPATH@/components/nsAsyncShutdown.js
 
 @RESPATH@/components/BuiltinProviders.manifest
 @RESPATH@/components/PresentationControlService.js
 @RESPATH@/components/PresentationDataChannelSessionTransport.js
 @RESPATH@/components/PresentationDataChannelSessionTransport.manifest
 
@@ -554,20 +371,18 @@
 @RESPATH@/browser/modules/*
 @RESPATH@/modules/*
 
 ; Safe Browsing
 @RESPATH@/components/nsURLClassifier.manifest
 @RESPATH@/components/nsUrlClassifierHashCompleter.js
 @RESPATH@/components/nsUrlClassifierListManager.js
 @RESPATH@/components/nsUrlClassifierLib.js
-@RESPATH@/components/url-classifier.xpt
 
 ; Private Browsing
-@RESPATH@/components/privatebrowsing.xpt
 @RESPATH@/components/PrivateBrowsing.manifest
 @RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
 
 ; Security Reports
 @RESPATH@/components/SecurityReporter.manifest
 @RESPATH@/components/SecurityReporter.js
 
 ; ANGLE GLES-on-D3D rendering library
@@ -713,25 +528,22 @@
 @BINPATH@/@DLL_PREFIX@nssutil3@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@smime3@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@ssl3@DLL_SUFFIX@
 #endif
 @BINPATH@/@DLL_PREFIX@softokn3@DLL_SUFFIX@
 #endif
 @RESPATH@/chrome/pippki@JAREXT@
 @RESPATH@/chrome/pippki.manifest
-@RESPATH@/components/pipnss.xpt
-@RESPATH@/components/pippki.xpt
 
 ; For process sandboxing
 #if defined(MOZ_SANDBOX)
 #if defined(XP_LINUX)
 @BINPATH@/@DLL_PREFIX@mozsandbox@DLL_SUFFIX@
 #endif
-@RESPATH@/components/sandbox.xpt
 #endif
 
 ; for Solaris SPARC
 #ifdef SOLARIS
 bin/libfreebl_32fpu_3.so
 bin/libfreebl_32int_3.so
 bin/libfreebl_32int64_3.so
 #endif
@@ -753,17 +565,16 @@ bin/libfreebl_32int64_3.so
 @BINPATH@/maintenanceservice_installer.exe
 #endif
 
 ; [Crash Reporter]
 ;
 #ifdef MOZ_CRASHREPORTER
 @RESPATH@/components/CrashService.manifest
 @RESPATH@/components/CrashService.js
-@RESPATH@/components/toolkit_crashservice.xpt
 #ifdef XP_MACOSX
 @BINPATH@/crashreporter.app/
 #else
 @BINPATH@/crashreporter@BIN_SUFFIX@
 @RESPATH@/crashreporter.ini
 @BINPATH@/minidump-analyzer@BIN_SUFFIX@
 #ifdef XP_UNIX
 @RESPATH@/Throbber-small.gif
@@ -774,18 +585,16 @@ bin/libfreebl_32int64_3.so
 @BINPATH@/breakpadinjector.dll
 #endif
 #endif
 
 ; [ Ping Sender ]
 ;
 @BINPATH@/pingsender@BIN_SUFFIX@
 
-@RESPATH@/components/dom_audiochannel.xpt
-
 ; Shutdown Terminator
 @RESPATH@/components/nsTerminatorTelemetry.js
 @RESPATH@/components/terminator.manifest
 
 #ifdef LLVM_SYMBOLIZER
 @BINPATH@/@LLVM_SYMBOLIZER@
 #endif
 
@@ -811,19 +620,16 @@ bin/libfreebl_32int64_3.so
 #ifdef XP_MACOSX
 @RESPATH@/fix_macosx_stack.py
 #endif
 #ifdef XP_LINUX
 @RESPATH@/fix_linux_stack.py
 #endif
 #endif
 
-; Background Hang Monitor
-@RESPATH@/components/backgroundhangmonitor.xpt
-
 ; NOTE: This must match the config checks in
 ; /toolkit/components/backgroundhangmonitor/moz.build.
 #if defined(NIGHTLY_BUILD) && !defined(MOZ_DEBUG) && !defined(MOZ_TSAN)
 @RESPATH@/components/BHRTelemetryService.js
 @RESPATH@/components/BHRTelemetryService.manifest
 #endif
 
 #ifdef PKG_LOCALE_MANIFEST
--- a/browser/moz.build
+++ b/browser/moz.build
@@ -31,24 +31,16 @@ if CONFIG['MAKENSISU']:
 
 TEST_DIRS += [
     'tools/mozscreenshots',
 ]
 
 DIST_SUBDIR = 'browser'
 export('DIST_SUBDIR')
 
-if CONFIG['MOZ_ARTIFACT_BUILDS']:
-    # Ensure a pre-built interfaces.xpt installed to the objdir by the artifact
-    # code is included by the top-level chrome.manifest.
-    EXTRA_COMPONENTS += [
-        '../build/prebuilt-interfaces.manifest',
-    ]
-
-
 # These defines are read in firefox.js
 DEFINES['APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 
 for cdm in CONFIG['MOZ_EME_MODULES']:
     DEFINES['MOZ_%s_EME' % cdm.upper()] = True
 
 if CONFIG['MOZ_GPSD']:
     DEFINES['MOZ_GPSD'] = True
new file mode 100644
--- /dev/null
+++ b/build/debian-packages/python3.5-wheezy.diff
@@ -0,0 +1,192 @@
+diff --git a/debian/changelog b/debian/changelog
+index e19964f..0bc60c2 100644
+--- a/debian/changelog
++++ b/debian/changelog
+@@ -1,3 +1,21 @@
++python3.5 (3.5.3-1.deb7moz1) wheezy; urgency=medium
++
++  * Mozilla backport for wheezy.
++  * debian/control.in:
++    - Remove libmpdec-dev dependency; wheezy doesn't have it; Python
++      vendors it.
++    - Remove libexpat1-dev dependency; it prevents co-installing some
++      i386 -dev packages.
++  * debian/rules:
++    - Adapt ar, ranlib, and objcopy paths to work on wheezy.
++    - Remove --with-system-libmpdec because wheezy doesn't have it.
++    - Disable PGO builds because they segfault.
++    - Remove -Og from debug builds because GCC 4.7 doesn't like it.
++  * debian/control.in, debian/rules: Don't generate the -doc package
++    and HTML documentation.
++
++ -- Gregory Szorc <gps@mozilla.com>  Thu, 29 Mar 2018 20:00:00 -0700
++
+ python3.5 (3.5.3-1) unstable; urgency=medium
+ 
+   * Python 3.5.3 release.
+diff --git a/debian/control.in b/debian/control.in
+index 7d36556..ec1af78 100644
+--- a/debian/control.in
++++ b/debian/control.in
+@@ -9,7 +9,6 @@ Build-Depends: debhelper (>= 9), @bd_dpkgdev@
+   zlib1g-dev, libbz2-dev, liblzma-dev,
+   libgdbm-dev, libdb-dev,
+   tk-dev, blt-dev (>= 2.4z), libssl-dev,
+-  libexpat1-dev, libmpdec-dev (>= 2.4),
+   libbluetooth-dev [!hurd-i386 !kfreebsd-i386 !kfreebsd-amd64],
+   locales [!armel !avr32 !hppa !ia64 !mipsel],
+   libsqlite3-dev, libffi-dev (>= 3.0.5) [!or1k !avr32],
+@@ -27,7 +26,7 @@ Architecture: any
+ Multi-Arch: allowed
+ Priority: @PRIO@
+ Depends: @PVER@-minimal (= ${binary:Version}), lib@PVER@-stdlib (= ${binary:Version}), mime-support, ${shlibs:Depends}, ${misc:Depends}
+-Suggests: @PVER@-venv, @PVER@-doc, binutils
++Suggests: @PVER@-venv, binutils
+ Description: Interactive high-level object-oriented language (version @VER@)
+  Python is a high-level, interactive, object-oriented language. Its @VER@ version
+  includes an extensive class library with lots of goodies for
+@@ -115,7 +114,7 @@ Description: Examples for the Python language (v@VER@)
+ Package: @PVER@-dev
+ Architecture: any
+ Multi-Arch: allowed
+-Depends: @PVER@ (= ${binary:Version}), lib@PVER@-dev (= ${binary:Version}), lib@PVER@ (= ${binary:Version}), libexpat1-dev, ${shlibs:Depends}, ${misc:Depends}
++Depends: @PVER@ (= ${binary:Version}), lib@PVER@-dev (= ${binary:Version}), lib@PVER@ (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}
+ Recommends: libc6-dev | libc-dev
+ Description: Header files and a static library for Python (v@VER@)
+  Header files, a static library and development tools for building
+@@ -129,7 +128,7 @@ Section: libdevel
+ Architecture: any
+ Multi-Arch: same
+ Pre-Depends: ${misc:Pre-Depends}
+-Depends: lib@PVER@-stdlib (= ${binary:Version}), lib@PVER@ (= ${binary:Version}), libexpat1-dev, ${shlibs:Depends}, ${misc:Depends}
++Depends: lib@PVER@-stdlib (= ${binary:Version}), lib@PVER@ (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}
+ Recommends: libc6-dev | libc-dev
+ Description: Header files and a static library for Python (v@VER@)
+  Header files, a static library and development tools for building
+@@ -161,28 +160,6 @@ Description: IDE for Python (v@VER@) using Tkinter
+  IDLE is an Integrated Development Environment for Python (v@VER@).
+  IDLE is written using Tkinter and therefore quite platform-independent.
+ 
+-Package: @PVER@-doc
+-Section: doc
+-Architecture: all
+-Multi-Arch: foreign
+-Depends: libjs-jquery, libjs-underscore, ${misc:Depends}
+-Suggests: @PVER@
+-Description: Documentation for the high-level object-oriented language Python (v@VER@)
+- These is the official set of documentation for the interactive high-level
+- object-oriented language Python (v@VER@). All documents are provided
+- in HTML format. The package consists of ten documents:
+- .
+-   * What's New in Python@VER@
+-   * Tutorial
+-   * Python Library Reference
+-   * Macintosh Module Reference
+-   * Python Language Reference
+-   * Extending and Embedding Python
+-   * Python/C API Reference
+-   * Installing Python Modules
+-   * Documenting Python
+-   * Distributing Python Modules
+-
+ Package: @PVER@-dbg
+ Section: debug
+ Architecture: any
+diff --git a/debian/rules b/debian/rules
+index 05895da..294d432 100755
+--- a/debian/rules
++++ b/debian/rules
+@@ -136,14 +136,14 @@ endif
+ CC=$(DEB_HOST_GNU_TYPE)-gcc
+ CXX=$(DEB_HOST_GNU_TYPE)-g++
+ 
+-AR=$(DEB_HOST_GNU_TYPE)-ar
+-RANLIB=$(DEB_HOST_GNU_TYPE)-ranlib
++AR=$(DEB_HOST_GNU_TYPE)-gcc-ar-4.7
++RANLIB=$(DEB_HOST_GNU_TYPE)-gcc-ranlib-4.7
+ 
+ DPKG_CPPFLAGS:= $(shell dpkg-buildflags --get CPPFLAGS)
+ DPKG_CFLAGS  := $(shell dpkg-buildflags --get CFLAGS)
+ DPKG_LDFLAGS := $(shell dpkg-buildflags --get LDFLAGS)
+ OPT_CFLAGS   := $(filter-out -O%,$(DPKG_CFLAGS)) # default is -O3
+-DEBUG_CFLAGS := $(patsubst -O%,-Og,$(DPKG_CFLAGS))
++DEBUG_CFLAGS := $(DPKG_CFLAGS)
+ 
+ # on alpha, use -O2 only, use -mieee
+ ifeq ($(DEB_HOST_ARCH),alpha)
+@@ -155,14 +155,6 @@ ifeq ($(DEB_HOST_ARCH),m68k)
+     EXTRA_OPT_FLAGS += -O2
+ endif
+ 
+-ifeq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE))
+-  ifeq ($(DEB_HOST_ARCH_OS),linux)
+-    ifneq (,$(findstring $(DEB_HOST_ARCH), amd64 armel armhf i386 powerpc ppc64 ppc64el s390x))
+-      with_pgo := yes
+-    endif
+-  endif
+-endif
+-
+ ifneq (,$(findstring $(DEB_HOST_ARCH), amd64 armel armhf i386 powerpc ppc64 ppc64el s390x))
+   with_lto := yes
+ endif
+@@ -187,8 +179,6 @@ ifeq ($(with_lto),yes)
+       LTO_CFLAGS += -ffat-lto-objects
+     endif
+     EXTRA_OPT_CFLAGS += $(LTO_CFLAGS)
+-    AR=$(DEB_HOST_GNU_TYPE)-gcc-ar
+-    RANLIB=$(DEB_HOST_GNU_TYPE)-gcc-ranlib
+ endif
+ 
+ make_build_target = $(if $(with_pgo),profile-opt)
+@@ -322,7 +312,6 @@ common_configure_args = \
+ 		--with-computed-gotos \
+ 		--without-ensurepip \
+ 		--with-system-expat \
+-		--with-system-libmpdec \
+ 
+ ifneq (,$(filter $(DEB_HOST_ARCH), avr32 or1k))
+   common_configure_args += --without-ffi
+@@ -659,7 +648,6 @@ minimal-test:
+ 
+ stamps/stamp-doc-html:
+ 	dh_testdir
+-	$(MAKE) -C Doc html
+ 	@mkdir -p stamps
+ 	touch stamps/stamp-doc-html
+ 
+@@ -1299,26 +1287,6 @@ binary-indep: build-indep install stamps/stamp-control
+ 	dh_testdir -i
+ 	dh_testroot -i
+ 
+-	: # $(p_doc) package
+-	dh_installdirs -p$(p_doc) \
+-		usr/share/doc/$(p_base) \
+-		usr/share/doc/$(p_doc)
+-	dh_installdocs -p$(p_doc)
+-	cp -a Doc/build/html $(d_doc)/usr/share/doc/$(p_base)/
+-	rm -f $(d_doc)/usr/share/doc/$(p_base)/html/_static/jquery.js
+-	dh_link -p$(p_doc) \
+-		/usr/share/doc/$(p_base)/html /usr/share/doc/$(p_doc)/html \
+-		/usr/share/javascript/jquery/jquery.js /usr/share/doc/$(p_base)/html/_static/jquery.js \
+-		/usr/share/javascript/underscore/underscore.js /usr/share/doc/$(p_base)/html/_static/underscore.js
+-
+-	: # devhelp docs
+-	cd $(buildd_static) && ./python ../debian/pyhtml2devhelp.py \
+-		../$(d_doc)/usr/share/doc/$(p_base)/html index.html $(VER) \
+-		> ../$(d_doc)/usr/share/doc/$(p_base)/html/$(PVER).devhelp
+-	gzip -9nv $(d_doc)/usr/share/doc/$(p_base)/html/$(PVER).devhelp
+-	dh_link -p$(p_doc) \
+-		/usr/share/doc/$(p_base)/html /usr/share/devhelp/books/$(PVER)
+-
+ 	for i in $(p_ltst); do \
+ 	  rm -rf debian/$$i/usr/share/doc/$$i; \
+ 	  ln -s $(p_base) debian/$$i/usr/share/doc/$$i; \
+@@ -1377,7 +1345,7 @@ ifneq ($(with_gdbm),yes)
+ endif
+ 
+ 	find $(d_ldbg) $(d_ldev) -name '*.a' ! -type l \
+-		| xargs -n 1 $(DEB_HOST_GNU_TYPE)-objcopy -p --remove-section=.gnu.lto_.*
++		| xargs -n 1 objcopy -p --remove-section=.gnu.lto_.*
+ 	dh_strip -a -N$(p_dbg) -N$(p_ldbg) -Xdebug -Xdbg --dbg-package=$(p_dbg)
+ 	mkdir -p $(d_dbg)/usr/share/gdb/auto-load/usr/bin
+ 	cp Tools/gdb/libpython.py $(d_dbg)/usr/share/gdb/auto-load/usr/bin/$(PVER)m-gdb.py
--- a/build/moz.build
+++ b/build/moz.build
@@ -112,15 +112,8 @@ OBJDIR_FILES += ['!/dist/bin/.lldbinit']
 OBJDIR_FILES += ['/.ycm_extra_conf.py']
 
 if CONFIG['MOZ_VALGRIND']:
     OBJDIR_FILES._valgrind += [
         'valgrind/cross-architecture.sup',
         'valgrind/i386-pc-linux-gnu.sup',
         'valgrind/x86_64-pc-linux-gnu.sup',
     ]
-
-if CONFIG['MOZ_ARTIFACT_BUILDS']:
-    # Ensure a pre-built interfaces.xpt installed to the objdir by the artifact
-    # code is included by the top-level chrome.manifest.
-    EXTRA_COMPONENTS += [
-        'prebuilt-interfaces.manifest',
-    ]
deleted file mode 100644
--- a/build/prebuilt-interfaces.manifest
+++ /dev/null
@@ -1,1 +0,0 @@
-interfaces interfaces.xpt
--- a/config/faster/rules.mk
+++ b/config/faster/rules.mk
@@ -99,19 +99,16 @@ ACDEFINES += -DBUILD_FASTER
 
 # The xpidl target in config/makefiles/xpidl requires the install manifest for
 # dist/idl to have been processed. When using the hybrid
 # FasterMake/RecursiveMake backend, this dependency is handled in the top-level
 # Makefile.
 ifndef FASTER_RECURSIVE_MAKE
 $(TOPOBJDIR)/config/makefiles/xpidl/xpidl: $(TOPOBJDIR)/install-dist_idl
 endif
-# It also requires all the install manifests for dist/bin to have been processed
-# because it adds interfaces.manifest references with buildlist.py.
-$(TOPOBJDIR)/config/makefiles/xpidl/xpidl: $(addprefix install-,$(filter dist/bin%,$(INSTALL_MANIFESTS)))
 
 $(TOPOBJDIR)/build/application.ini: $(TOPOBJDIR)/buildid.h $(TOPOBJDIR)/source-repo.h
 
 # The manifest of allowed system add-ons should be re-built when using
 # "build faster".
 ifeq ($(MOZ_BUILD_APP),browser/app)
 default: $(TOPOBJDIR)/browser/app/features
 endif
--- a/config/makefiles/xpidl/Makefile.in
+++ b/config/makefiles/xpidl/Makefile.in
@@ -25,16 +25,18 @@ include $(topsrcdir)/config/rules.mk
 
 # For dependency files.
 idl_deps_dir := .deps
 
 dist_idl_dir := $(DIST)/idl
 dist_include_dir := $(DIST)/include
 dist_xpcrs_dir := $(DIST)/xpcrs
 process_py := $(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py
+generated_file := $(topobjdir)/xpcom/typelib/xpt/XPTInfo.cpp
+code_gen_py := $(topsrcdir)/xpcom/typelib/xpt/tools/xpt.py
 
 # TODO we should use py_action, but that would require extra directories to be
 # in the virtualenv.
 %.xpt:
 	$(REPORT_BUILD)
 	$(PYTHON_PATH) $(PLY_INCLUDE) -I$(topsrcdir)/xpcom/idl-parser -I$(DEPTH)/xpcom/idl-parser/xpidl \
 		$(process_py) --cache-dir $(DEPTH)/xpcom/idl-parser/xpidl --depsdir $(idl_deps_dir) \
 		$(dist_idl_dir) $(dist_include_dir) $(dist_xpcrs_dir) $(@D) \
@@ -43,47 +45,37 @@ process_py := $(topsrcdir)/python/mozbui
 # still is, in the tree, simple dependencies can't detect that the XPT needs
 # to be rebuilt.
 # Add the current value of $($(xpidl_module)_deps) in the depend file, such that
 # we can later check if the value has changed since last build, which will
 # indicate whether IDLs were added or removed.
 # Note that removing previously built files is not covered.
 	@echo $(basename $(notdir $@))_deps_built = $($(basename $(notdir $@))_deps) >> $(idl_deps_dir)/$(basename $(notdir $@)).pp
 
-# Chrome manifests may be written from several Makefiles at various times during
-# the build. The 'buildlist' action adds to the file if it already exists, but
-# if it does exist, make considers it to be up-to-date (as we have no inputs to
-# depend on). We use FORCE to ensure that we always add the interface manifest,
-# whether or not the chrome manifest already exists.
-%/chrome.manifest: FORCE
-	$(call py_action,buildlist,$@ 'manifest components/interfaces.manifest')
-
-chrome_manifests := @chrome_manifests@
-
-%/interfaces.manifest: Makefile
-	$(call py_action,buildlist,$@ $(foreach xpt,$(filter $*/%,$(registered_xpt_files)),'interfaces $(notdir $(xpt))'))
-
-interfaces_manifests := @interfaces_manifests@
-
 xpidl_modules := @xpidl_modules@
-registered_xpt_files := @registered_xpt_files@
-xpt_files := $(registered_xpt_files) @xpt_files@
+xpt_files := $(addsuffix .xpt,$(xpidl_modules))
 
 @xpidl_rules@
 
 depends_files := $(foreach root,$(xpidl_modules),$(idl_deps_dir)/$(root).pp)
 
-GARBAGE += $(xpt_files) $(depends_files)
+GARBAGE += $(xpt_files) $(depends_files) $(generated_file)
 
 ifdef COMPILE_ENVIRONMENT
-xpidl:: $(xpt_files) $(chrome_manifests) $(interfaces_manifests)
+xpidl:: $(generated_file)
 endif
 
 $(xpt_files): $(process_py) $(call mkdir_deps,$(idl_deps_dir) $(dist_include_dir) $(dist_xpcrs_dir))
 
+$(generated_file): $(xpt_files) $(code_gen_py)
+	$(REPORT_BUILD)
+	$(PYTHON_PATH) $(PLY_INCLUDE) \
+		$(code_gen_py) linkgen \
+		$(generated_file) $(xpt_files)
+
 -include $(depends_files)
 
 define xpt_deps
 $(1): $(call mkdir_deps,$(dir $(1)))
 $(1): $(addsuffix .idl,$(addprefix $(dist_idl_dir)/,$($(basename $(notdir $(1)))_deps)))
 ifneq ($($(basename $(notdir $(1)))_deps),$($(basename $(notdir $(1)))_deps_built))
 $(1): FORCE
 endif
--- a/devtools/shared/gcli/command-state.js
+++ b/devtools/shared/gcli/command-state.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const EventEmitter = require("devtools/shared/old-event-emitter");
+const EventEmitter = require("devtools/shared/event-emitter");
 
 const getTargetId = ({tab}) => tab.linkedBrowser.outerWindowID;
 const enabledCommands = new Map();
 
 /**
  * The `CommandState` is a singleton that provides utility methods to keep the commands'
  * state in sync between the toolbox, the toolbar and the content.
  */
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -3214,16 +3214,26 @@ XULDocument::OnScriptCompileComplete(JSS
         NS_ASSERTION(doc->mCurrentScriptProto == scriptProto,
                      "waiting for wrong script to load?");
         doc->mCurrentScriptProto = nullptr;
 
         // Unlink doc from scriptProto's list before executing and resuming
         *docp = doc->mNextSrcLoadWaiter;
         doc->mNextSrcLoadWaiter = nullptr;
 
+        if (aStatus == NS_BINDING_ABORTED && !scriptProto->HasScriptObject()) {
+            // If the previous doc load was aborted, we want to try loading
+            // again for the next doc. Otherwise, one abort would lead to all
+            // subsequent waiting docs to abort as well.
+            bool block = false;
+            doc->LoadScript(scriptProto, &block);
+            NS_RELEASE(doc);
+            return rv;
+        }
+
         // Execute only if we loaded and compiled successfully, then resume
         if (NS_SUCCEEDED(aStatus) && scriptProto->HasScriptObject()) {
             doc->ExecuteScript(scriptProto);
         }
         doc->ResumeWalk();
         NS_RELEASE(doc);
     }
 
--- a/js/xpconnect/tests/idl/moz.build
+++ b/js/xpconnect/tests/idl/moz.build
@@ -9,17 +9,8 @@ XPIDL_SOURCES += [
     'xpctest_bug809674.idl',
     'xpctest_interfaces.idl',
     'xpctest_params.idl',
     'xpctest_returncode.idl',
     'xpctest_utils.idl',
 ]
 
 XPIDL_MODULE = 'xpctest'
-
-# XXX: This relies on xpctest.xpt being created in dist/bin/components/ during
-# the export tier AND TEST_HARNESS_FILES being processed after that.
-TEST_HARNESS_FILES.xpcshell.js.xpconnect.tests.components.native.components += [
-    '!/dist/bin/components/xpctest.xpt',
-]
-TEST_HARNESS_FILES.xpcshell.js.xpconnect.tests.components.js += [
-    '!/dist/bin/components/xpctest.xpt',
-]
--- a/layout/base/AccessibleCaretEventHub.cpp
+++ b/layout/base/AccessibleCaretEventHub.cpp
@@ -395,18 +395,25 @@ AccessibleCaretEventHub::Init()
   nsPresContext* presContext = mPresShell->GetPresContext();
   MOZ_ASSERT(presContext, "PresContext should be given in PresShell::Init()");
 
   nsIDocShell* docShell = presContext->GetDocShell();
   if (!docShell) {
     return;
   }
 
-  docShell->AddWeakReflowObserver(this);
-  docShell->AddWeakScrollObserver(this);
+  nsCOMPtr<nsIDocShell> curDocShell = docShell;
+  do {
+    curDocShell->AddWeakReflowObserver(this);
+    curDocShell->AddWeakScrollObserver(this);
+
+    nsCOMPtr<nsIDocShellTreeItem> tmp;
+    curDocShell->GetSameTypeParent(getter_AddRefs(tmp));
+    curDocShell = do_QueryInterface(tmp);
+  } while (curDocShell);
 
   mDocShell = static_cast<nsDocShell*>(docShell);
 
   if (sUseLongTapInjector) {
     mLongTapInjectorTimer = NS_NewTimer();
   }
 
   mManager = MakeUnique<AccessibleCaretManager>(mPresShell);
@@ -416,20 +423,24 @@ AccessibleCaretEventHub::Init()
 
 void
 AccessibleCaretEventHub::Terminate()
 {
   if (!mInitialized) {
     return;
   }
 
-  RefPtr<nsDocShell> docShell(mDocShell.get());
-  if (docShell) {
-    docShell->RemoveWeakReflowObserver(this);
-    docShell->RemoveWeakScrollObserver(this);
+  nsCOMPtr<nsIDocShell> curDocShell = mDocShell.get();
+  while (curDocShell) {
+    curDocShell->RemoveWeakReflowObserver(this);
+    curDocShell->RemoveWeakScrollObserver(this);
+
+    nsCOMPtr<nsIDocShellTreeItem> tmp;
+    curDocShell->GetSameTypeParent(getter_AddRefs(tmp));
+    curDocShell = do_QueryInterface(tmp);
   }
 
   if (mLongTapInjectorTimer) {
     mLongTapInjectorTimer->Cancel();
   }
 
   mManager->Terminate();
   mPresShell = nullptr;
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1443027-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<body>
+  <div id="first" style="position:absolute; width: 200px; height: 200px; background-color:blue; z-index: 1"></div>
+  <div style="position:absolute; left: 400px; width: 200px; height: 200px; background-color:green; z-index: 2"></div>
+  <div id="overlay" style="position:absolute; top: 100px; width: 600px; height: 200px; background-color:orange; z-index: 10"></div>
+</body>
+<script>
+  function doTest2() {
+    document.getElementById("overlay").style.zIndex= 11;
+    document.documentElement.removeAttribute('class');
+  }
+  function doTest() {
+    document.getElementById("first").style.zIndex = 3;
+    setTimeout(doTest2, 1000);
+  }
+
+  window.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</html>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -522,9 +522,10 @@ load 1425893.html
 load 1428353.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1429088.html
 load 1429961.html
 load 1435015.html
 load 1429962.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1439016.html
 load 1442506.html
 load 1437155.html
+load 1443027-1.html
 load 1448841-1.html
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/dom/HTMLSummaryElement.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/Likely.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/ServoStyleSetInlines.h"
+#include "mozilla/StaticPrefs.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "nsCSSPseudoElements.h"
 #include "nsAtom.h"
 #include "nsIFrameInlines.h"
 #include "nsGkAtoms.h"
 #include "nsPresContext.h"
 #include "nsIDocument.h"
 #include "nsIDocumentInlines.h"
@@ -84,17 +85,16 @@
 #include "mozilla/dom/CharacterData.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ElementInlines.h"
 #include "nsAutoLayoutPhase.h"
 #include "nsStyleStructInlines.h"
 #include "nsPageContentFrame.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/RestyleManagerInlines.h"
-#include "mozilla/StylePrefs.h"
 #include "StickyScrollContainer.h"
 #include "nsFieldSetFrame.h"
 #include "nsInlineFrame.h"
 #include "nsBlockFrame.h"
 #include "nsCanvasFrame.h"
 #include "nsFirstLetterFrame.h"
 #include "nsGfxScrollFrame.h"
 #include "nsPageFrame.h"
@@ -2553,17 +2553,17 @@ nsCSSFrameConstructor::ConstructDocEleme
     contentFrame = static_cast<nsContainerFrame*>(
       ConstructOuterSVG(state, item, mDocElementContainingBlock,
                         computedStyle->StyleDisplay(),
                         frameItems));
     newFrame = frameItems.FirstChild();
     NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
   } else if (display->mDisplay == StyleDisplay::Flex ||
              display->mDisplay == StyleDisplay::WebkitBox ||
-             (StylePrefs::sEmulateMozBoxWithFlex &&
+             (StaticPrefs::layout_css_emulate_moz_box_with_flex() &&
               display->mDisplay == StyleDisplay::MozBox)) {
     contentFrame = NS_NewFlexContainerFrame(mPresShell, computedStyle);
     InitAndRestoreFrame(state, aDocElement, mDocElementContainingBlock,
                         contentFrame);
     newFrame = contentFrame;
     processChildren = true;
 
     newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
@@ -4245,19 +4245,19 @@ nsCSSFrameConstructor::GetAnonymousConte
 
   return NS_OK;
 }
 
 static
 bool IsXULDisplayType(const nsStyleDisplay* aDisplay)
 {
   // -moz-{inline-}box is XUL, unless we're emulating it with flexbox.
-  if  (!StylePrefs::sEmulateMozBoxWithFlex &&
-       (aDisplay->mDisplay == StyleDisplay::MozInlineBox ||
-        aDisplay->mDisplay == StyleDisplay::MozBox)) {
+  if (!StaticPrefs::layout_css_emulate_moz_box_with_flex() &&
+      (aDisplay->mDisplay == StyleDisplay::MozInlineBox ||
+       aDisplay->mDisplay == StyleDisplay::MozBox)) {
     return true;
   }
 
 #ifdef MOZ_XUL
   return (aDisplay->mDisplay == StyleDisplay::MozInlineGrid ||
           aDisplay->mDisplay == StyleDisplay::MozInlineStack ||
           aDisplay->mDisplay == StyleDisplay::MozGrid ||
           aDisplay->mDisplay == StyleDisplay::MozStack ||
@@ -4516,17 +4516,17 @@ nsCSSFrameConstructor::FindXULDisplayDat
   if (aDisplay->mDisplay < StyleDisplay::MozBox) {
     return nullptr;
   }
 
   // If we're emulating -moz-box with flexbox, then treat it as non-XUL and
   // return null (except for scrollcorners which have to be XUL becuase their
   // parent reflows them with BoxReflow() which means they have to get
   // actual-XUL frames).
-  if (StylePrefs::sEmulateMozBoxWithFlex &&
+  if (StaticPrefs::layout_css_emulate_moz_box_with_flex() &&
       aElement && !aElement->IsXULElement(nsGkAtoms::scrollcorner) &&
       (aDisplay->mDisplay == StyleDisplay::MozBox ||
        aDisplay->mDisplay == StyleDisplay::MozInlineBox)) {
     return nullptr;
   }
 
   MOZ_ASSERT(aDisplay->mDisplay <= StyleDisplay::MozPopup,
              "Someone added a new display value?");
@@ -4744,17 +4744,17 @@ nsCSSFrameConstructor::FindDisplayData(c
   }
 
   // If this is for a <body> node and we've propagated the scroll-frame to the
   // viewport, we need to make sure not to add another layer of scrollbars, so
   // we use a different FCData struct without FCDATA_MAY_NEED_SCROLLFRAME.
   if (propagatedScrollToViewport && aDisplay->IsScrollableOverflow()) {
     if (aDisplay->mDisplay == StyleDisplay::Flex ||
         aDisplay->mDisplay == StyleDisplay::WebkitBox ||
-        (StylePrefs::sEmulateMozBoxWithFlex &&
+        (StaticPrefs::layout_css_emulate_moz_box_with_flex() &&
          aDisplay->mDisplay == StyleDisplay::MozBox)) {
       static const FrameConstructionData sNonScrollableFlexData =
         FCDATA_DECL(0, NS_NewFlexContainerFrame);
       return &sNonScrollableFlexData;
     }
     if (aDisplay->mDisplay == StyleDisplay::Grid) {
       static const FrameConstructionData sNonScrollableGridData =
         FCDATA_DECL(0, NS_NewGridContainerFrame);
@@ -4845,17 +4845,17 @@ nsCSSFrameConstructor::FindDisplayData(c
       FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
     FCDATA_FOR_DISPLAY(StyleDisplay::MozBox,
       FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
     FCDATA_FOR_DISPLAY(StyleDisplay::MozInlineBox,
       FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
   };
   static_assert(ArrayLength(sDisplayData) == size_t(StyleDisplay::MozInlineBox) + 1,
                 "Be sure to update sDisplayData if you touch StyleDisplay");
-  MOZ_ASSERT(StylePrefs::sEmulateMozBoxWithFlex ||
+  MOZ_ASSERT(StaticPrefs::layout_css_emulate_moz_box_with_flex() ||
              (aDisplay->mDisplay != StyleDisplay::MozBox &&
               aDisplay->mDisplay != StyleDisplay::MozInlineBox),
              "-moz-{inline-}box as XUL should have already been handled");
   MOZ_ASSERT(size_t(aDisplay->mDisplay) < ArrayLength(sDisplayData),
              "XUL display data should have already been handled");
 
   // See the mDisplay fixup code in nsRuleNode::ComputeDisplayData.
   MOZ_ASSERT(aDisplay->mDisplay != StyleDisplay::Contents ||
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/layers/PAPZ.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/StaticPrefs.h"
 #include "mozilla/Unused.h"
 #include "nsCharTraits.h"
 #include "nsDocument.h"
 #include "nsFontMetrics.h"
 #include "nsPresContext.h"
 #include "nsIContent.h"
 #include "nsFrameList.h"
 #include "nsGenericHTMLElement.h"
@@ -123,17 +124,16 @@
 #include "DisplayItemClip.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "prenv.h"
 #include "RetainedDisplayListBuilder.h"
 #include "DisplayListChecker.h"
 #include "TextDrawTarget.h"
 #include "nsDeckFrame.h"
-#include "mozilla/StylePrefs.h"
 #include "mozilla/dom/InspectorFontFace.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
 #include "GeckoProfiler.h"
 #include "nsAnimationManager.h"
@@ -10126,17 +10126,17 @@ nsLayoutUtils::ComputeOffsetToUserSpace(
             nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
 
   return (offsetToBoundingBox - toUserSpace);
 }
 
 /* static */ uint8_t
 nsLayoutUtils::ControlCharVisibilityDefault()
 {
-  return StylePrefs::sControlCharVisibility
+  return StaticPrefs::layout_css_control_characters_visible()
     ? NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE
     : NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN;
 }
 
 /* static */
 already_AddRefed<nsFontMetrics>
 nsLayoutUtils::GetMetricsFor(nsPresContext* aPresContext,
                              bool aIsVertical,
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -104,17 +104,16 @@
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "TouchManager.h"
 #include "DecoderDoctorLogger.h"
 #include "MediaDecoder.h"
 #include "MediaPrefs.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/StaticPresData.h"
-#include "mozilla/StylePrefs.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamStorage.h"
 #include "mozilla/dom/U2FTokenManager.h"
 #include "mozilla/dom/PointerEventHandler.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsThreadManager.h"
 
 using namespace mozilla;
@@ -214,18 +213,16 @@ nsLayoutStatics::Initialize()
   }
 
   rv = nsCCUncollectableMarker::Init();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize nsCCUncollectableMarker");
     return rv;
   }
 
-  StylePrefs::Init();
-
 #ifdef MOZ_XUL
   rv = nsXULPopupManager::Init();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize nsXULPopupManager");
     return rv;
   }
 #endif
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3086,22 +3086,21 @@ nsIFrame::BuildDisplayListForStackingCon
     // repeating display list building if it changed.
 
     // If we changed whether we're going to build a blend mode item,
     // then we need to make sure we're marked as invalid and we've built
     // the full display list.
     if (aBuilder->ContainsBlendMode() != BuiltBlendContainer() &&
         aBuilder->IsRetainingDisplayList()) {
       SetBuiltBlendContainer(aBuilder->ContainsBlendMode());
-      aBuilder->MarkCurrentFrameModifiedDuringBuilding();
 
       // If we did a partial build then delete all the items we just built
       // and repeat building with the full area.
       if (!aBuilder->GetDirtyRect().Contains(aBuilder->GetVisibleRect())) {
-        aBuilder->SetDirtyRect(aBuilder->GetVisibleRect());
+        aBuilder->MarkCurrentFrameModifiedDuringBuilding();
         set.DeleteAll(aBuilder);
 
         if (eventRegions) {
           eventRegions->Destroy(aBuilder);
           eventRegions = MakeDisplayItem<nsDisplayLayerEventRegions>(aBuilder, this);
           eventRegions->AddFrame(aBuilder, this);
           aBuilder->SetLayerEventRegions(eventRegions);
         }
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -104,193 +104,85 @@ SelectAGRForFrame(nsIFrame* aFrame, Anim
 
 // Removes any display items that belonged to a frame that was deleted,
 // and mark frames that belong to a different AGR so that get their
 // items built again.
 // TODO: We currently descend into all children even if we don't have an AGR
 // to mark, as child stacking contexts might. It would be nice if we could
 // jump into those immediately rather than walking the entire thing.
 bool
-RetainedDisplayListBuilder::PreProcessDisplayList(nsDisplayList* aList,
+RetainedDisplayListBuilder::PreProcessDisplayList(RetainedDisplayList* aList,
                                                   AnimatedGeometryRoot* aAGR)
 {
-  bool modified = false;
+  // The DAG merging algorithm does not have strong mechanisms in place to keep the
+  // complexity of the resulting DAG under control. In some cases we can build up
+  // edges very quickly. Detect those cases and force a full display list build if
+  // we hit them.
+  static const uint32_t kMaxEdgeRatio = 5;
+  bool initializeDAG = !aList->mDAG.Length();
+  if (!initializeDAG &&
+      aList->mDAG.mDirectPredecessorList.Length() >
+      (aList->mDAG.mNodesInfo.Length() * kMaxEdgeRatio)) {
+    return false;
+
+  }
+
   nsDisplayList saved;
-  while (nsDisplayItem* i = aList->RemoveBottom()) {
-    if (i->HasDeletedFrame() || !i->CanBeReused()) {
-      i->Destroy(&mBuilder);
-      modified = true;
+  aList->mOldItems.SetCapacity(aList->Count());
+  size_t i = 0;
+  while (nsDisplayItem* item = aList->RemoveBottom()) {
+    if (item->HasDeletedFrame() || !item->CanBeReused()) {
+      // If we haven't yet initialized the DAG, then we can
+      // just drop this item. Otherwise we need to keep it
+      // around to preserve indices, and merging will
+      // get rid of it.
+      if (initializeDAG) {
+        item->Destroy(&mBuilder);
+      } else {
+        aList->mOldItems.AppendElement(OldItemInfo(item));
+      }
       continue;
     }
 
-    nsIFrame* f = i->Frame();
+    aList->mOldItems.AppendElement(OldItemInfo(item));
+    if (initializeDAG) {
+      if (i == 0) {
+        aList->mDAG.AddNode(Span<const MergedListIndex>());
+      } else {
+        MergedListIndex previous(i - 1);
+        aList->mDAG.AddNode(Span<const MergedListIndex>(&previous, 1));
+      }
 
-    if (i->GetChildren()) {
-      if (PreProcessDisplayList(i->GetChildren(), SelectAGRForFrame(f, aAGR))) {
-        modified = true;
+      aList->mKeyLookup.Put({ item->Frame(), item->GetPerFrameKey() }, i);
+      i++;
+    }
+
+    nsIFrame* f = item->Frame();
+
+    if (item->GetChildren()) {
+      if (!PreProcessDisplayList(item->GetChildren(), SelectAGRForFrame(f, aAGR))) {
+        mBuilder.MarkFrameForDisplayIfVisible(f, mBuilder.RootReferenceFrame());
+        mBuilder.MarkFrameModifiedDuringBuilding(f);
       }
     }
 
     // TODO: We should be able to check the clipped bounds relative
     // to the common AGR (of both the existing item and the invalidated
     // frame) and determine if they can ever intersect.
-    if (aAGR && i->GetAnimatedGeometryRoot()->GetAsyncAGR() != aAGR) {
+    if (aAGR && item->GetAnimatedGeometryRoot()->GetAsyncAGR() != aAGR) {
       mBuilder.MarkFrameForDisplayIfVisible(f, mBuilder.RootReferenceFrame());
-      modified = true;
     }
 
     // TODO: This is here because we sometimes reuse the previous display list
     // completely. For optimization, we could only restore the state for reused
     // display items.
-    i->RestoreState();
-
-    saved.AppendToTop(i);
-  }
-  aList->AppendToTop(&saved);
-  aList->RestoreState();
-  return modified;
-}
-
-struct DisplayItemKey
-{
-  bool operator ==(const DisplayItemKey& aOther) const {
-    return mFrame == aOther.mFrame &&
-           mPerFrameKey == aOther.mPerFrameKey;
-  }
-
-  nsIFrame* mFrame;
-  uint32_t mPerFrameKey;
-};
-
-class DisplayItemHashEntry : public PLDHashEntryHdr
-{
-public:
-  typedef DisplayItemKey KeyType;
-  typedef const DisplayItemKey* KeyTypePointer;
-
-  explicit DisplayItemHashEntry(KeyTypePointer aKey)
-    : mKey(*aKey) {}
-  explicit DisplayItemHashEntry(const DisplayItemHashEntry& aCopy)=default;
-
-  ~DisplayItemHashEntry() = default;
-
-  KeyType GetKey() const { return mKey; }
-  bool KeyEquals(KeyTypePointer aKey) const
-  {
-    return mKey == *aKey;
-  }
-
-  static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
-  static PLDHashNumber HashKey(KeyTypePointer aKey)
-  {
-    if (!aKey)
-      return 0;
-
-    return mozilla::HashGeneric(aKey->mFrame, aKey->mPerFrameKey);
-  }
-  enum { ALLOW_MEMMOVE = true };
-
-  DisplayItemKey mKey;
-};
-
-template<typename T>
-void SwapAndRemove(nsTArray<T>& aArray, uint32_t aIndex)
-{
-  if (aIndex != (aArray.Length() - 1)) {
-    T last = aArray.LastElement();
-    aArray.LastElement() = aArray[aIndex];
-    aArray[aIndex] = last;
+    item->RestoreState();
   }
-
-  aArray.RemoveLastElement();
-}
-
-static bool
-MergeFrameRects(nsDisplayLayerEventRegions* aOldItem,
-                nsDisplayLayerEventRegions* aNewItem,
-                nsDisplayLayerEventRegions::FrameRects nsDisplayLayerEventRegions::*aRectList,
-                nsTArray<nsIFrame*>& aAddedFrames)
-{
-  bool modified = false;
-  // Go through the old item's rect list and remove any rectangles
-  // belonging to invalidated frames (deleted frames should
-  // already be gone at this point)
-  nsDisplayLayerEventRegions::FrameRects& oldRects = aOldItem->*aRectList;
-  uint32_t i = 0;
-  while (i < oldRects.mFrames.Length()) {
-    // TODO: As mentioned in nsDisplayLayerEventRegions, this
-    // operation might perform really poorly on a vector.
-    nsIFrame* f = oldRects.mFrames[i];
-    if (IsAnyAncestorModified(f)) {
-      MOZ_ASSERT(f != aOldItem->Frame());
-      f->RemoveDisplayItem(aOldItem);
-      SwapAndRemove(oldRects.mFrames, i);
-      SwapAndRemove(oldRects.mBoxes, i);
-      modified = true;
-    } else {
-      i++;
-    }
-  }
-  if (!aNewItem) {
-    return modified;
-  }
-
-  // Copy items from the source list to the dest list, but
-  // only if the dest doesn't already include them.
-  nsDisplayItem* destItem = aOldItem;
-  nsDisplayLayerEventRegions::FrameRects* destRects = &(aOldItem->*aRectList);
-  nsDisplayLayerEventRegions::FrameRects* srcRects = &(aNewItem->*aRectList);
-
-  for (uint32_t i = 0; i < srcRects->mFrames.Length(); i++) {
-    nsIFrame* f = srcRects->mFrames[i];
-    if (!f->HasDisplayItem(destItem)) {
-      // If this frame isn't already in the destination item,
-      // then add it!
-      destRects->Add(f, srcRects->mBoxes[i]);
-
-      // We also need to update RealDisplayItemData for 'f',
-      // but that'll mess up this check for the following
-      // FrameRects lists, so defer that until the end.
-      aAddedFrames.AppendElement(f);
-      MOZ_ASSERT(f != aOldItem->Frame());
-
-      modified = true;
-    }
-
-  }
-  return modified;
-}
-
-bool MergeLayerEventRegions(nsDisplayItem* aOldItem,
-                            nsDisplayItem* aNewItem)
-{
-  nsDisplayLayerEventRegions* oldItem =
-    static_cast<nsDisplayLayerEventRegions*>(aOldItem);
-  nsDisplayLayerEventRegions* newItem =
-    static_cast<nsDisplayLayerEventRegions*>(aNewItem);
-
-  nsTArray<nsIFrame*> addedFrames;
-
-  bool modified = false;
-  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHitRegion, addedFrames);
-  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mMaybeHitRegion, addedFrames);
-  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mDispatchToContentHitRegion, addedFrames);
-  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mNoActionRegion, addedFrames);
-  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHorizontalPanRegion, addedFrames);
-  modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mVerticalPanRegion, addedFrames);
-
-  // MergeFrameRects deferred updating the display item data list during
-  // processing so that earlier calls didn't change the result of later
-  // ones. Fix that up now.
-  for (nsIFrame* f : addedFrames) {
-    if (!f->HasDisplayItem(aOldItem)) {
-      f->AddDisplayItem(aOldItem);
-    }
-  }
-  return modified;
+  aList->RestoreState();
+  return true;
 }
 
 void
 RetainedDisplayListBuilder::IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem)
 {
   MOZ_ASSERT(aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT);
 
   nsSubDocumentFrame* subDocFrame =
@@ -317,276 +209,268 @@ UpdateASR(nsDisplayItem* aItem,
     return;
   }
 
   wrapList->SetActiveScrolledRoot(
     ActiveScrolledRoot::PickAncestor(wrapList->GetFrameActiveScrolledRoot(),
                                      aContainerASR.value()));
 }
 
+void
+OldItemInfo::AddedMatchToMergedList(RetainedDisplayListBuilder* aBuilder,
+                                    MergedListIndex aIndex)
+{
+  mItem->Destroy(aBuilder->Builder());
+  AddedToMergedList(aIndex);
+}
+
+void
+OldItemInfo::Discard(RetainedDisplayListBuilder* aBuilder,
+                     nsTArray<MergedListIndex>&& aDirectPredecessors)
+{
+  MOZ_ASSERT(!IsUsed());
+  mUsed = mDiscarded = true;
+  mDirectPredecessors = Move(aDirectPredecessors);
+  mItem->Destroy(aBuilder->Builder());
+  mItem = nullptr;
+}
+
 /**
- * Takes two display lists and merges them into an output list.
- *
- * The basic algorithm is:
- *
- * For-each item i in the new list:
- *     If the item has a matching item in the old list:
- *         Remove items from the start of the old list up until we reach an item that also exists in the new list (leaving the matched item in place):
- *             Add valid items to the merged list, destroy invalid items.
- *     Add i into the merged list.
- *     If the start of the old list matches i, remove and destroy it, otherwise mark the old version of i as used.
- * Add all remaining valid items from the old list into the merged list, skipping over (and destroying) any that are marked as used.
- *
- * If any item has a child display list, then we recurse into the merge
- * algorithm once we match up the new/old versions (if present).
- *
- * Example 1:
- *
- * Old List: A,B,C,D
- * Modified List: A,D
- * Invalidations: C,D
- *
- * We first match the A items, and add the new one to the merged list.
- * We then match the D items, copy B into the merged list, but not C
- * (since it's invalid). We then add the new D to the list and we're
- * finished.
- *
- * Merged List: A,B,D
- *
- * Example 2 (layout/reftests/retained-dl-zindex-1.html):
- *
- * Old List: A, B
- * Modified List: B, A
- * Invalidations: A
- *
- * In this example A has been explicitly moved to the back.
- *
- * We match the B items, but don't copy A since it's invalid, and then add the
- * new B into the merged list. We then add A, and we're done.
- *
- * Merged List: B, A
- *
- * Example 3:
+ * A C++ implementation of Markus Stange's merge-dags algorthim.
+ * https://github.com/mstange/merge-dags
  *
- * Old List: A, B
- * Modified List: B, A
- * Invalidations: -
- *
- * This can happen because a prior merge might have changed the ordering
- * for non-intersecting items.
- *
- * We match the B items, but don't copy A since it's also present in the new list
- * and then add the new B into the merged list. We then add A, and we're done.
- *
- * Merged List: B, A
- *
- * Example 4 (layout/reftests/retained-dl-zindex-2.html):
- *
- * Element A has two elements covering it (B and C), that don't intersect each
- * other. We then move C to the back.
- *
- * The correct initial ordering has B and C after A, in any order.
- *
- * Old List: A, B, C
- * Modified List: C, A
- * Invalidations: C
- *
- * We match the C items, but don't add anything from the old list because A is present
- * in both lists. We add C to the merged list, and mark the old version of C as reused.
- *
- * We then match A, add the new version the merged list and delete the old version.
- *
- * We then process the remainder of the old list, B is added (since it is valid,
- * and hasn't been mark as reused), C is destroyed since it's marked as reused and
- * is already present in the merged list.
- *
- * Merged List: C, A, B
+ * MergeState handles combining a new list of display items into an existing
+ * DAG and computes the new DAG in a single pass.
+ * Each time we add a new item, we resolve all dependencies for it, so that the resulting
+ * list and DAG are built in topological ordering.
  */
-bool
-RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
-                                              nsDisplayList* aOldList,
-                                              nsDisplayList* aOutList,
-                                              Maybe<const ActiveScrolledRoot*>& aOutContainerASR)
-{
-  bool modified = false;
+class MergeState {
+public:
+  MergeState(RetainedDisplayListBuilder* aBuilder, RetainedDisplayList&& aOldList)
+    : mBuilder(aBuilder)
+    , mOldItems(Move(aOldList.mOldItems))
+    , mOldDAG(Move(*reinterpret_cast<DirectedAcyclicGraph<OldListUnits>*>(&aOldList.mDAG)))
+    , mResultIsModified(false)
+  {
+    mOldKeyLookup.SwapElements(aOldList.mKeyLookup);
+    mMergedDAG.EnsureCapacityFor(mOldDAG);
+  }
+
+  MergedListIndex ProcessItemFromNewList(nsDisplayItem* aNewItem, const Maybe<MergedListIndex>& aPreviousItem) {
+    OldListIndex oldIndex;
+    if (mOldKeyLookup.Get({ aNewItem->Frame(), aNewItem->GetPerFrameKey() }, &oldIndex.val)) {
+      if (!IsChanged(aNewItem)) {
+        MOZ_ASSERT(!mOldItems[oldIndex.val].IsUsed());
+        if (aNewItem->GetChildren()) {
+          Maybe<const ActiveScrolledRoot*> containerASRForChildren;
+          if (mBuilder->MergeDisplayLists(aNewItem->GetChildren(),
+                                          mOldItems[oldIndex.val].mItem->GetChildren(),
+                                          aNewItem->GetChildren(),
+                                          containerASRForChildren)) {
+            mResultIsModified = true;
+
+          }
+          UpdateASR(aNewItem, containerASRForChildren);
+          aNewItem->UpdateBounds(mBuilder->Builder());
+        }
 
-  nsDisplayList merged;
-  const auto UseItem = [&](nsDisplayItem* aItem) {
+        AutoTArray<MergedListIndex, 2> directPredecessors = ProcessPredecessorsOfOldNode(oldIndex);
+        MergedListIndex newIndex = AddNewNode(aNewItem, Some(oldIndex), directPredecessors, aPreviousItem);
+        mOldItems[oldIndex.val].AddedMatchToMergedList(mBuilder, newIndex);
+        return newIndex;
+      }
+    }
+    mResultIsModified = true;
+    return AddNewNode(aNewItem, Nothing(), Span<MergedListIndex>(), aPreviousItem);
+  }
+
+  RetainedDisplayList Finalize() {
+    for (size_t i = 0; i < mOldDAG.Length(); i++) {
+      if (mOldItems[i].IsUsed()) {
+        continue;
+      }
+
+      AutoTArray<MergedListIndex, 2> directPredecessors =
+        ResolveNodeIndexesOldToMerged(mOldDAG.GetDirectPredecessors(OldListIndex(i)));
+      ProcessOldNode(OldListIndex(i), Move(directPredecessors));
+    }
+
+    RetainedDisplayList result;
+    result.AppendToTop(&mMergedItems);
+    result.mDAG = Move(mMergedDAG);
+    result.mKeyLookup.SwapElements(mMergedKeyLookup);
+    return result;
+  }
+
+  bool IsChanged(nsDisplayItem* aItem) {
+    return aItem->HasDeletedFrame() || !aItem->CanBeReused() ||
+           IsAnyAncestorModified(aItem->FrameForInvalidation());
+  }
+
+  void UpdateContainerASR(nsDisplayItem* aItem)
+  {
     const ActiveScrolledRoot* itemClipASR =
       aItem->GetClipChain() ? aItem->GetClipChain()->mASR : nullptr;
 
     const ActiveScrolledRoot* finiteBoundsASR = ActiveScrolledRoot::PickDescendant(
       itemClipASR, aItem->GetActiveScrolledRoot());
-    if (!aOutContainerASR) {
-      aOutContainerASR = Some(finiteBoundsASR);
+    if (!mContainerASR) {
+      mContainerASR = Some(finiteBoundsASR);
     } else {
-      aOutContainerASR =
-        Some(ActiveScrolledRoot::PickAncestor(aOutContainerASR.value(), finiteBoundsASR));
-    }
-
-    merged.AppendToTop(aItem);
-  };
-
-  const auto ReuseItem = [&](nsDisplayItem* aItem) {
-    UseItem(aItem);
-    aItem->SetReused(true);
-
-    if (aItem->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
-      IncrementSubDocPresShellPaintCount(aItem);
-    }
-  };
-
-  const bool newListIsEmpty = aNewList->IsEmpty();
-  if (!newListIsEmpty) {
-    // Build a hashtable of items in the old list so we can look for them quickly.
-    // We have similar data in the nsIFrame DisplayItems() property, but it doesn't
-    // know which display list items are in, and we only want to match items in
-    // this list.
-    nsDataHashtable<DisplayItemHashEntry, nsDisplayItem*> oldListLookup(aOldList->Count());
-
-    for (nsDisplayItem* i = aOldList->GetBottom(); i != nullptr; i = i->GetAbove()) {
-      i->SetReused(false);
-      oldListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i);
-    }
-
-    nsDataHashtable<DisplayItemHashEntry, nsDisplayItem*> newListLookup(aNewList->Count());
-    for (nsDisplayItem* i = aNewList->GetBottom(); i != nullptr; i = i->GetAbove()) {
-#ifdef DEBUG
-      if (newListLookup.Get({ i->Frame(), i->GetPerFrameKey() }, nullptr)) {
-        MOZ_CRASH_UNSAFE_PRINTF("Duplicate display items detected!: %s(0x%p) type=%d key=%d",
-                                  i->Name(), i->Frame(),
-                                  static_cast<int>(i->GetType()), i->GetPerFrameKey());
-      }
-#endif
-      newListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i);
+      mContainerASR = Some(ActiveScrolledRoot::PickAncestor(mContainerASR.value(), finiteBoundsASR));
     }
 
-    while (nsDisplayItem* newItem = aNewList->RemoveBottom()) {
-      if (nsDisplayItem* oldItem = oldListLookup.Get({ newItem->Frame(), newItem->GetPerFrameKey() })) {
-        // The new item has a matching counterpart in the old list that we haven't yet reached,
-        // so copy all valid items from the old list into the merged list until we get to the
-        // matched item.
-        nsDisplayItem* old = nullptr;
-        while ((old = aOldList->GetBottom()) && old != oldItem) {
-          if (IsAnyAncestorModified(old->FrameForInvalidation())) {
-            // The old item is invalid, discard it.
-            oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
-            aOldList->RemoveBottom();
-            old->Destroy(&mBuilder);
-            modified = true;
-          } else if (newListLookup.Get({ old->Frame(), old->GetPerFrameKey() })) {
-            // This old item is also in the new list, but we haven't got to it yet.
-            // Stop now, and we'll deal with it when we get to the new entry.
-            modified = true;
-            break;
-          } else {
-            // Recurse into the child list (without a matching new list) to
-            // ensure that we find and remove any invalidated items.
-            if (old->GetChildren()) {
-              nsDisplayList empty;
-              Maybe<const ActiveScrolledRoot*> containerASRForChildren;
-              if (MergeDisplayLists(&empty, old->GetChildren(),
-                                    old->GetChildren(), containerASRForChildren)) {
-                modified = true;
-              }
-              UpdateASR(old, containerASRForChildren);
-              old->UpdateBounds(&mBuilder);
-            }
-            aOldList->RemoveBottom();
-            ReuseItem(old);
-          }
+  }
+
+  MergedListIndex AddNewNode(nsDisplayItem* aItem,
+                             const Maybe<OldListIndex>& aOldIndex,
+                             Span<const MergedListIndex> aDirectPredecessors,
+                             const Maybe<MergedListIndex>& aExtraDirectPredecessor) {
+    UpdateContainerASR(aItem);
+    mMergedItems.AppendToTop(aItem);
+    MergedListIndex newIndex = mMergedDAG.AddNode(aDirectPredecessors, aExtraDirectPredecessor);
+    mMergedKeyLookup.Put({ aItem->Frame(), aItem->GetPerFrameKey() }, newIndex.val);
+    return newIndex;
+  }
+
+  void ProcessOldNode(OldListIndex aNode, nsTArray<MergedListIndex>&& aDirectPredecessors) {
+    nsDisplayItem* item = mOldItems[aNode.val].mItem;
+    if (IsChanged(item)) {
+      mOldItems[aNode.val].Discard(mBuilder, Move(aDirectPredecessors));
+      mResultIsModified = true;
+    } else {
+      if (item->GetChildren()) {
+        Maybe<const ActiveScrolledRoot*> containerASRForChildren;
+        nsDisplayList empty;
+        if (mBuilder->MergeDisplayLists(&empty, item->GetChildren(), item->GetChildren(),
+                                        containerASRForChildren)) {
+          mResultIsModified = true;
         }
-        bool destroy = false;
-        if (old == oldItem) {
-          // If we advanced the old list until the matching item then we can pop
-          // the matching item off the old list and make sure we clean it up.
-          aOldList->RemoveBottom();
-          destroy = true;
-        } else {
-          // If we didn't get to the matching item, then mark the old item
-          // as being reused (since we're adding the new version to the new
-          // list now) so that we don't add it twice at the end.
-          oldItem->SetReused(true);
-        }
+        UpdateASR(item, containerASRForChildren);
+        item->UpdateBounds(mBuilder->Builder());
+      }
+      if (item->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
+        mBuilder->IncrementSubDocPresShellPaintCount(item);
+      }
+      item->SetReused(true);
+      mOldItems[aNode.val].AddedToMergedList(
+        AddNewNode(item, Some(aNode), aDirectPredecessors, Nothing()));
+    }
+  }
+
+  struct PredecessorStackItem {
+    PredecessorStackItem(OldListIndex aNode, Span<OldListIndex> aPredecessors)
+     : mNode(aNode)
+     , mDirectPredecessors(aPredecessors)
+     , mCurrentPredecessorIndex(0)
+    {}
+
+    bool IsFinished() {
+      return mCurrentPredecessorIndex == mDirectPredecessors.Length();
+    }
 
-        // Recursively merge any child lists, destroy the old item and add
-        // the new one to the list.
-        if (destroy &&
-            oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
-            !IsAnyAncestorModified(oldItem->FrameForInvalidation())) {
-          // Event regions items don't have anything interesting other than
-          // the lists of regions and frames, so we have no need to use the
-          // newer item. Always use the old item instead since we assume it's
-          // likely to have the bigger lists and merging will be quicker.
-          if (MergeLayerEventRegions(oldItem, newItem)) {
-            modified = true;
-          }
-          ReuseItem(oldItem);
-          newItem->Destroy(&mBuilder);
+    OldListIndex GetAndIncrementCurrentPredecessor() { return mDirectPredecessors[mCurrentPredecessorIndex++]; }
+
+    OldListIndex mNode;
+    Span<OldListIndex> mDirectPredecessors;
+    size_t mCurrentPredecessorIndex;
+  };
+
+  AutoTArray<MergedListIndex, 2> ProcessPredecessorsOfOldNode(OldListIndex aNode) {
+    AutoTArray<PredecessorStackItem,256> mStack;
+    mStack.AppendElement(PredecessorStackItem(aNode, mOldDAG.GetDirectPredecessors(aNode)));
+
+    while (true) {
+      if (mStack.LastElement().IsFinished()) {
+        // If we've finished processing all the entries in the current set, then pop
+        // it off the processing stack and process it.
+        PredecessorStackItem item = mStack.PopLastElement();
+        AutoTArray<MergedListIndex,2> result =
+          ResolveNodeIndexesOldToMerged(item.mDirectPredecessors);
+        if (mStack.IsEmpty()) {
+          return result;
         } else {
-          if (IsAnyAncestorModified(oldItem->FrameForInvalidation())) {
-            modified = true;
-          } else if (oldItem->GetChildren()) {
-            MOZ_ASSERT(newItem->GetChildren());
-            Maybe<const ActiveScrolledRoot*> containerASRForChildren;
-            if (MergeDisplayLists(newItem->GetChildren(), oldItem->GetChildren(),
-                                  newItem->GetChildren(), containerASRForChildren)) {
-              modified = true;
-            }
-            UpdateASR(newItem, containerASRForChildren);
-            newItem->UpdateBounds(&mBuilder);
-          }
-
-          if (destroy) {
-            oldItem->Destroy(&mBuilder);
-          }
-          UseItem(newItem);
+          ProcessOldNode(item.mNode, Move(result));
         }
       } else {
-        // If there was no matching item in the old list, then we only need to
-        // add the new item to the merged list.
-        modified = true;
-        UseItem(newItem);
+        // Grab the current predecessor, push predecessors of that onto the processing
+        // stack (if it hasn't already been processed), and then advance to the next entry.
+        OldListIndex currentIndex = mStack.LastElement().GetAndIncrementCurrentPredecessor();
+        if (!mOldItems[currentIndex.val].IsUsed()) {
+          mStack.AppendElement(
+            PredecessorStackItem(currentIndex, mOldDAG.GetDirectPredecessors(currentIndex)));
+        }
       }
     }
   }
 
-  // Reuse the remaining valid items from the old display list.
-  while (nsDisplayItem* old = aOldList->RemoveBottom()) {
-    if (!IsAnyAncestorModified(old->FrameForInvalidation()) &&
-        (!old->IsReused() || newListIsEmpty)) {
-      if (old->GetChildren()) {
-        // We are calling MergeDisplayLists() to ensure that the display items
-        // with modified or deleted children will be correctly handled.
-        // Passing an empty new display list as an argument skips the merging
-        // loop above and jumps back here.
-        nsDisplayList empty;
-        Maybe<const ActiveScrolledRoot*> containerASRForChildren;
-
-        if (MergeDisplayLists(&empty, old->GetChildren(),
-                              old->GetChildren(), containerASRForChildren)) {
-          modified = true;
+  AutoTArray<MergedListIndex, 2> ResolveNodeIndexesOldToMerged(Span<OldListIndex> aDirectPredecessors) {
+    AutoTArray<MergedListIndex, 2> result;
+    result.SetCapacity(aDirectPredecessors.Length());
+    for (OldListIndex index : aDirectPredecessors) {
+      OldItemInfo& oldItem = mOldItems[index.val];
+      if (oldItem.IsDiscarded()) {
+        for (MergedListIndex inner : oldItem.mDirectPredecessors) {
+          if (!result.Contains(inner)) {
+            result.AppendElement(inner);
+          }
         }
-        UpdateASR(old, containerASRForChildren);
-        old->UpdateBounds(&mBuilder);
+      } else {
+        result.AppendElement(oldItem.mIndex);
       }
-      if (old->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
-        if (MergeLayerEventRegions(old, nullptr)) {
-          modified = true;
-        }
-      }
-      ReuseItem(old);
-    } else {
-      old->Destroy(&mBuilder);
-      modified = true;
     }
+    return result;
   }
 
-  aOutList->AppendToTop(&merged);
-  return modified;
+  RetainedDisplayListBuilder* mBuilder;
+  Maybe<const ActiveScrolledRoot*> mContainerASR;
+  nsTArray<OldItemInfo> mOldItems;
+  DirectedAcyclicGraph<OldListUnits> mOldDAG;
+  // Unfortunately we can't use strong typing for the hashtables
+  // since they internally encode the type with the mOps pointer,
+  // and assert when we try swap the contents
+  nsDataHashtable<DisplayItemHashEntry, size_t> mOldKeyLookup;
+  nsDisplayList mMergedItems;
+  DirectedAcyclicGraph<MergedListUnits> mMergedDAG;
+  nsDataHashtable<DisplayItemHashEntry, size_t> mMergedKeyLookup;
+  bool mResultIsModified;
+};
+
+void RetainedDisplayList::ClearDAG()
+{
+  mDAG.Clear();
+  mKeyLookup.Clear();
+}
+
+/**
+ * Takes two display lists and merges them into an output list.
+ *
+ * Display lists wthout an explicit DAG are interpreted as linear DAGs (with a maximum
+ * of one direct predecessor and one direct successor per node). We add the two DAGs
+ * together, and then output the topological sorted ordering as the final display list.
+ *
+ * Once we've merged a list, we then retain the DAG (as part of the RetainedDisplayList
+ * object) to use for future merges.
+ */
+bool
+RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
+                                              RetainedDisplayList* aOldList,
+                                              RetainedDisplayList* aOutList,
+                                              mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR)
+{
+  MergeState merge(this, Move(*aOldList));
+
+  Maybe<MergedListIndex> previousItemIndex;
+  while (nsDisplayItem* item = aNewList->RemoveBottom()) {
+    previousItemIndex = Some(merge.ProcessItemFromNewList(item, previousItemIndex));
+  }
+
+  *aOutList = Move(merge.Finalize());
+  aOutContainerASR = merge.mContainerASR;
+  return merge.mResultIsModified;
 }
 
 static void
 TakeAndAddModifiedAndFramesWithPropsFromRootFrame(
   nsTArray<nsIFrame*>* aModifiedFrames,
   nsTArray<nsIFrame*>* aFramesWithProps,
   nsIFrame* aRootFrame)
 {
@@ -1090,39 +974,31 @@ auto
 RetainedDisplayListBuilder::AttemptPartialUpdate(
   nscolor aBackstop,
   mozilla::DisplayListChecker* aChecker) -> PartialUpdateResult
 {
   mBuilder.RemoveModifiedWindowRegions();
   mBuilder.ClearWindowOpaqueRegion();
 
   if (mBuilder.ShouldSyncDecodeImages()) {
-    MarkFramesWithItemsAndImagesModified(&mList);
+    MarkFramesWithItemsAndImagesModified(List());
   }
 
   mBuilder.EnterPresShell(mBuilder.RootReferenceFrame());
 
   // We set the override dirty regions during ComputeRebuildRegion or in
   // nsLayoutUtils::InvalidateForDisplayPortChange. The display port change also
   // marks the frame modified, so those regions are cleared here as well.
   AutoClearFramePropsArray modifiedFrames;
   AutoClearFramePropsArray framesWithProps;
   GetModifiedAndFramesWithProps(&mBuilder, &modifiedFrames.Frames(), &framesWithProps.Frames());
 
   // Do not allow partial builds if the retained display list is empty, or if
   // ShouldBuildPartial heuristic fails.
-  bool shouldBuildPartial = !List()->IsEmpty() && ShouldBuildPartial(modifiedFrames.Frames());
-
-  // We don't support retaining with overlay scrollbars, since they require
-  // us to look at the display list and pick the highest z-index, which
-  // we can't do during partial building.
-  if (mBuilder.BuiltOverlayScrollbars()) {
-    shouldBuildPartial = false;
-    mBuilder.SetBuiltOverlayScrollbars(false);
-  }
+  const bool shouldBuildPartial = !List()->IsEmpty() && ShouldBuildPartial(modifiedFrames.Frames());
 
   if (mPreviousCaret != mBuilder.GetCaretFrame()) {
     if (mPreviousCaret) {
       if (mBuilder.MarkFrameModifiedDuringBuilding(mPreviousCaret)) {
         modifiedFrames.Frames().AppendElement(mPreviousCaret);
       }
     }
 
@@ -1134,26 +1010,27 @@ RetainedDisplayListBuilder::AttemptParti
 
     mPreviousCaret = mBuilder.GetCaretFrame();
   }
 
   nsRect modifiedDirty;
   AnimatedGeometryRoot* modifiedAGR = nullptr;
   if (!shouldBuildPartial ||
       !ComputeRebuildRegion(modifiedFrames.Frames(), &modifiedDirty,
-                           &modifiedAGR, framesWithProps.Frames())) {
-    mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), &mList);
+                           &modifiedAGR, framesWithProps.Frames()) ||
+      !PreProcessDisplayList(&mList, modifiedAGR)) {
+    mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), List());
+    mList.ClearDAG();
     return PartialUpdateResult::Failed;
   }
 
   modifiedDirty.IntersectRect(modifiedDirty, mBuilder.RootReferenceFrame()->GetVisualOverflowRectRelativeToSelf());
 
   PartialUpdateResult result = PartialUpdateResult::NoChange;
-  if (PreProcessDisplayList(&mList, modifiedAGR) ||
-      !modifiedDirty.IsEmpty() ||
+  if (!modifiedDirty.IsEmpty() ||
       !framesWithProps.IsEmpty()) {
     result = PartialUpdateResult::Updated;
   }
 
   mBuilder.SetDirtyRect(modifiedDirty);
   mBuilder.SetPartialUpdate(true);
 
   nsDisplayList modifiedDL;
@@ -1186,11 +1063,11 @@ RetainedDisplayListBuilder::AttemptParti
   Maybe<const ActiveScrolledRoot*> dummy;
   if (MergeDisplayLists(&modifiedDL, &mList, &mList, dummy)) {
     result = PartialUpdateResult::Updated;
   }
 
   //printf_stderr("Painting --- Merged list:\n");
   //nsFrame::PrintDisplayList(&mBuilder, mList);
 
-  mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), &mList);
+  mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), List());
   return result;
 }
--- a/layout/painting/RetainedDisplayListBuilder.h
+++ b/layout/painting/RetainedDisplayListBuilder.h
@@ -44,29 +44,30 @@ struct RetainedDisplayListBuilder {
    * Also clears the frame properties set by RetainedDisplayListBuilder for all
    * the frames in the modified frame lists.
    */
   void ClearFramesWithProps();
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder)
 
 private:
-  bool PreProcessDisplayList(nsDisplayList* aList, AnimatedGeometryRoot* aAGR);
-
+  bool PreProcessDisplayList(RetainedDisplayList* aList, AnimatedGeometryRoot* aAGR);
   bool MergeDisplayLists(nsDisplayList* aNewList,
-                         nsDisplayList* aOldList,
-                         nsDisplayList* aOutList,
+                         RetainedDisplayList* aOldList,
+                         RetainedDisplayList* aOutList,
                          mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR);
 
   bool ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedFrames,
                             nsRect* aOutDirty,
                             AnimatedGeometryRoot** aOutModifiedAGR,
                             nsTArray<nsIFrame*>& aOutFramesWithProps);
 
   void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem);
 
+  friend class MergeState;
+
   nsDisplayListBuilder mBuilder;
-  nsDisplayList mList;
+  RetainedDisplayList mList;
   WeakFrame mPreviousCaret;
 
 };
 
 #endif // RETAINEDDISPLAYLISTBUILDER_H_
new file mode 100644
--- /dev/null
+++ b/layout/painting/RetainedDisplayListHelpers.h
@@ -0,0 +1,199 @@
+/* -*- 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 RETAINEDDISPLAYLISTHELPERS_H_
+#define RETAINEDDISPLAYLISTHELPERS_H_
+
+#include "PLDHashTable.h"
+
+struct DisplayItemKey
+{
+  bool operator ==(const DisplayItemKey& aOther) const {
+    return mFrame == aOther.mFrame &&
+           mPerFrameKey == aOther.mPerFrameKey;
+  }
+
+  nsIFrame* mFrame;
+  uint32_t mPerFrameKey;
+};
+
+class DisplayItemHashEntry : public PLDHashEntryHdr
+{
+public:
+  typedef DisplayItemKey KeyType;
+  typedef const DisplayItemKey* KeyTypePointer;
+
+  explicit DisplayItemHashEntry(KeyTypePointer aKey)
+    : mKey(*aKey) {}
+  explicit DisplayItemHashEntry(const DisplayItemHashEntry& aCopy)=default;
+
+  ~DisplayItemHashEntry() = default;
+
+  KeyType GetKey() const { return mKey; }
+  bool KeyEquals(KeyTypePointer aKey) const
+  {
+    return mKey == *aKey;
+  }
+
+  static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; }
+  static PLDHashNumber HashKey(KeyTypePointer aKey)
+  {
+    if (!aKey)
+      return 0;
+
+    return mozilla::HashGeneric(aKey->mFrame, aKey->mPerFrameKey);
+  }
+  enum { ALLOW_MEMMOVE = true };
+
+  DisplayItemKey mKey;
+};
+
+template <typename T>
+bool SpanContains(mozilla::Span<const T>& aSpan, T aItem)
+{
+  for (const T& i : aSpan) {
+    if (i == aItem) {
+      return true;
+    }
+  }
+  return false;
+}
+
+class OldListUnits {};
+class MergedListUnits {};
+
+template <typename Units>
+struct Index {
+  Index()
+    : val(0)
+  {}
+  explicit Index(size_t aVal)
+    : val(aVal)
+  {}
+
+  bool operator==(const Index<Units>& aOther) const
+  {
+    return val == aOther.val;
+  }
+
+  size_t val;
+};
+typedef Index<OldListUnits> OldListIndex;
+typedef Index<MergedListUnits> MergedListIndex;
+
+
+template <typename T>
+class DirectedAcyclicGraph {
+public:
+  DirectedAcyclicGraph() {}
+  DirectedAcyclicGraph(DirectedAcyclicGraph&& aOther)
+    : mNodesInfo(mozilla::Move(aOther.mNodesInfo))
+    , mDirectPredecessorList(mozilla::Move(aOther.mDirectPredecessorList))
+  {}
+
+  DirectedAcyclicGraph& operator=(DirectedAcyclicGraph&& aOther)
+  {
+    mNodesInfo = mozilla::Move(aOther.mNodesInfo);
+    mDirectPredecessorList = mozilla::Move(aOther.mDirectPredecessorList);
+    return *this;
+  }
+
+  Index<T> AddNode(mozilla::Span<const Index<T>> aDirectPredecessors,
+                   const mozilla::Maybe<Index<T>>& aExtraPredecessor = mozilla::Nothing())
+  {
+    size_t index = mNodesInfo.Length();
+    mNodesInfo.AppendElement(NodeInfo(mDirectPredecessorList.Length(), aDirectPredecessors.Length()));
+    if (aExtraPredecessor && !SpanContains(aDirectPredecessors, aExtraPredecessor.value())) {
+      mNodesInfo.LastElement().mDirectPredecessorCount++;
+      mDirectPredecessorList.SetCapacity(mDirectPredecessorList.Length() + aDirectPredecessors.Length() + 1);
+      mDirectPredecessorList.AppendElements(aDirectPredecessors);
+      mDirectPredecessorList.AppendElement(aExtraPredecessor.value());
+    } else {
+      mDirectPredecessorList.AppendElements(aDirectPredecessors);
+    }
+    return Index<T>(index);
+  }
+
+  size_t Length()
+  {
+    return mNodesInfo.Length();
+  }
+
+  mozilla::Span<Index<T>> GetDirectPredecessors(Index<T> aNodeIndex)
+  {
+    NodeInfo& node = mNodesInfo[aNodeIndex.val];
+    return mozilla::MakeSpan(mDirectPredecessorList).Subspan(node.mIndexInDirectPredecessorList,
+                                                             node.mDirectPredecessorCount);
+  }
+
+  template<typename OtherUnits>
+  void EnsureCapacityFor(const DirectedAcyclicGraph<OtherUnits>& aOther)
+  {
+    mNodesInfo.SetCapacity(aOther.mNodesInfo.Length());
+    mDirectPredecessorList.SetCapacity(aOther.mDirectPredecessorList.Length());
+  }
+
+  void Clear()
+  {
+    mNodesInfo.Clear();
+    mDirectPredecessorList.Clear();
+  }
+
+  struct NodeInfo {
+    NodeInfo(size_t aIndexInDirectPredecessorList,
+             size_t aDirectPredecessorCount)
+      : mIndexInDirectPredecessorList(aIndexInDirectPredecessorList)
+      , mDirectPredecessorCount(aDirectPredecessorCount)
+    {}
+    size_t mIndexInDirectPredecessorList;
+    size_t mDirectPredecessorCount;
+  };
+
+  nsTArray<NodeInfo> mNodesInfo;
+  nsTArray<Index<T>> mDirectPredecessorList;
+};
+
+struct RetainedDisplayListBuilder;
+class nsDisplayItem;
+
+struct OldItemInfo {
+  explicit OldItemInfo(nsDisplayItem* aItem)
+    : mItem(aItem)
+    , mUsed(false)
+    , mDiscarded(false)
+  {}
+
+  void AddedToMergedList(MergedListIndex aIndex)
+  {
+    MOZ_ASSERT(!IsUsed());
+    mUsed = true;
+    mIndex = aIndex;
+    mItem = nullptr;
+  }
+
+  void AddedMatchToMergedList(RetainedDisplayListBuilder* aBuilder,
+                              MergedListIndex aIndex);
+  void Discard(RetainedDisplayListBuilder* aBuilder,
+               nsTArray<MergedListIndex>&& aDirectPredecessors);
+  bool IsUsed()
+  {
+    return mUsed;
+  }
+
+  bool IsDiscarded()
+  {
+    MOZ_ASSERT(IsUsed());
+    return mDiscarded;
+  }
+
+  nsDisplayItem* mItem;
+  bool mUsed;
+  bool mDiscarded;
+  MergedListIndex mIndex;
+  nsTArray<MergedListIndex> mDirectPredecessors;
+};
+
+#endif // RETAINEDDISPLAYLISTHELPERS_H_
--- a/layout/painting/moz.build
+++ b/layout/painting/moz.build
@@ -17,16 +17,17 @@ EXPORTS += [
     'nsCSSRenderingBorders.h',
     'nsCSSRenderingGradients.h',
     'nsDisplayItemTypes.h',
     'nsDisplayItemTypesList.h',
     'nsDisplayList.h',
     'nsDisplayListInvalidation.h',
     'nsImageRenderer.h',
     'RetainedDisplayListBuilder.h',
+    'RetainedDisplayListHelpers.h',
 ]
 
 EXPORTS.mozilla += [
     'PaintTracker.h',
 ]
 
 UNIFIED_SOURCES += [
     'ActiveLayerTracker.cpp',
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -39,16 +39,17 @@
 #include "mozilla/UniquePtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/gfx/UserData.h"
 #include "mozilla/layers/LayerAttributes.h"
 #include "nsCSSRenderingBorders.h"
 #include "nsPresArena.h"
 #include "nsAutoLayoutPhase.h"
 #include "nsDisplayItemTypes.h"
+#include "RetainedDisplayListHelpers.h"
 
 #include <stdint.h>
 #include "nsTHashtable.h"
 
 #include <stdlib.h>
 #include <algorithm>
 #include <unordered_set>
 
@@ -1091,21 +1092,21 @@ public:
       if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
         if (mCurrentAGRState == AGR_YES) {
           aBuilder->mCurrentAGR = aBuilder->WrapAGRForFrame(aForChild, isAsync, aBuilder->mCurrentAGR);
         }
       } else if (aForChild != aBuilder->mCurrentFrame) {
         aBuilder->mCurrentAGR = aBuilder->FindAnimatedGeometryRootFor(aForChild);
       }
       MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(aBuilder->RootReferenceFrame(), *aBuilder->mCurrentAGR));
+      aBuilder->mInInvalidSubtree = aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
       aBuilder->mCurrentFrame = aForChild;
       aBuilder->mVisibleRect = aVisibleRect;
-      aBuilder->mDirtyRect = aDirtyRect;
+      aBuilder->mDirtyRect = aBuilder->mInInvalidSubtree ? aVisibleRect : aDirtyRect;
       aBuilder->mIsAtRootOfPseudoStackingContext = aIsRoot;
-      aBuilder->mInInvalidSubtree = aBuilder->mInInvalidSubtree || aForChild->IsFrameModified();
     }
     void SetReferenceFrameAndCurrentOffset(const nsIFrame* aFrame, const nsPoint& aOffset) {
       mBuilder->mCurrentReferenceFrame = aFrame;
       mBuilder->mCurrentOffsetToReferenceFrame = aOffset;
     }
     bool IsAnimatedGeometryRoot() const {
       return mCurrentAGRState == AGR_YES;
     }
@@ -1735,16 +1736,17 @@ public:
     }
     return false;
   }
 
   bool MarkCurrentFrameModifiedDuringBuilding()
   {
     if (MarkFrameModifiedDuringBuilding(const_cast<nsIFrame*>(mCurrentFrame))) {
       mInInvalidSubtree = true;
+      mDirtyRect = mVisibleRect;
       return true;
     }
     return false;
   }
 
   /**
    * This is a convenience function to ease the transition until AGRs and ASRs
    * are unified.
@@ -2011,16 +2013,17 @@ private:
   bool                           mInInvalidSubtree;
   bool                           mBuildCompositorHitTestInfo;
   bool                           mLessEventRegionItems;
   bool                           mBuiltOverlayScrollbars;
 };
 
 class nsDisplayItem;
 class nsDisplayList;
+class RetainedDisplayList;
 /**
  * nsDisplayItems are put in singly-linked lists rooted in an nsDisplayList.
  * nsDisplayItemLink holds the link. The lists are linked from lowest to
  * highest in z-order.
  */
 class nsDisplayItemLink {
   // This is never instantiated directly, so no need to count constructors and
   // destructors.
@@ -2650,17 +2653,17 @@ public:
    * the same 3d rendering context.
    */
   virtual void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) {}
 
   /**
    * If this has a child list, return it, even if the children are in
    * a different coordinate system to this item.
    */
-  virtual nsDisplayList* GetChildren() const { return nullptr; }
+  virtual RetainedDisplayList* GetChildren() const { return nullptr; }
 
   /**
    * Returns the visible rect.
    */
   const nsRect& GetVisibleRect() const { return mVisibleRect; }
 
   void SetVisibleRect(const nsRect& aVisibleRect, bool aStore)
   {
@@ -3321,16 +3324,69 @@ struct nsDisplayListCollection : public 
 private:
   // This class is only used on stack, so we don't have to worry about leaking
   // it.  Don't let us be heap-allocated!
   void* operator new(size_t sz) CPP_THROW_NEW;
 
   nsDisplayList mLists[6];
 };
 
+/**
+ * A display list that also retains the partial build
+ * information (in the form of a DAG) used to create it.
+ *
+ * Display lists built from a partial list aren't necessarily
+ * in the same order as a full build, and the DAG retains
+ * the information needing to interpret the current
+ * order correctly.
+ */
+class RetainedDisplayList : public nsDisplayList {
+public:
+  RetainedDisplayList() {}
+  RetainedDisplayList(RetainedDisplayList&& aOther)
+  {
+    AppendToTop(&aOther);
+    mDAG = mozilla::Move(aOther.mDAG);
+    mKeyLookup.SwapElements(aOther.mKeyLookup);
+  }
+  ~RetainedDisplayList()
+  {
+    MOZ_ASSERT(mOldItems.IsEmpty(), "Must empty list before destroying");
+  }
+
+  RetainedDisplayList& operator=(RetainedDisplayList&& aOther)
+  {
+    MOZ_ASSERT(!Count(), "Can only move into an empty list!");
+    MOZ_ASSERT(mOldItems.IsEmpty(), "Can only move into an empty list!");
+    AppendToTop(&aOther);
+    mDAG = mozilla::Move(aOther.mDAG);
+    mKeyLookup.SwapElements(aOther.mKeyLookup);
+    return *this;
+  }
+
+  void DeleteAll(nsDisplayListBuilder* aBuilder)
+  {
+    for (OldItemInfo& i : mOldItems) {
+      if (i.mItem) {
+        i.mItem->Destroy(aBuilder);
+      }
+    }
+    mOldItems.Clear();
+    nsDisplayList::DeleteAll(aBuilder);
+  }
+
+  void ClearDAG();
+
+  DirectedAcyclicGraph<MergedListUnits> mDAG;
+  nsDataHashtable<DisplayItemHashEntry, size_t> mKeyLookup;
+
+  // Temporary state initialized during the preprocess pass
+  // of RetainedDisplayListBuilder and then used during merging.
+  nsTArray<OldItemInfo> mOldItems;
+};
 
 class nsDisplayImageContainer : public nsDisplayItem {
 public:
   typedef mozilla::LayerIntPoint LayerIntPoint;
   typedef mozilla::LayoutDeviceRect LayoutDeviceRect;
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::layers::ImageLayer ImageLayer;
 
@@ -5000,17 +5056,17 @@ public:
   {
     NS_ASSERTION(mListPtr->IsEmpty() || !ReferenceFrame() ||
                  !mListPtr->GetBottom()->ReferenceFrame() ||
                  mListPtr->GetBottom()->ReferenceFrame() == ReferenceFrame(),
                  "Children must have same reference frame");
     return mListPtr;
   }
 
-  virtual nsDisplayList* GetChildren() const override { return mListPtr; }
+  virtual RetainedDisplayList* GetChildren() const override { return mListPtr; }
 
   virtual int32_t ZIndex() const override
   {
     return (mHasZIndexOverride) ? mOverrideZIndex : nsDisplayItem::ZIndex();
   }
 
   void SetOverrideZIndex(int32_t aZIndex)
   {
@@ -5046,18 +5102,18 @@ protected:
   void MergeFromTrackingMergedFrames(const nsDisplayWrapList* aOther)
   {
     mBounds.UnionRect(mBounds, aOther->mBounds);
     mVisibleRect.UnionRect(mVisibleRect, aOther->mVisibleRect);
     mMergedFrames.AppendElement(aOther->mFrame);
     mMergedFrames.AppendElements(aOther->mMergedFrames);
   }
 
-  nsDisplayList mList;
-  nsDisplayList* mListPtr;
+  RetainedDisplayList mList;
+  RetainedDisplayList* mListPtr;
   // The active scrolled root for the frame that created this
   // wrap list.
   RefPtr<const ActiveScrolledRoot> mFrameActiveScrolledRoot;
   // The frames from items that have been merged into this item, excluding
   // this item's own frame.
   nsTArray<nsIFrame*> mMergedFrames;
   nsRect mBounds;
   // Visible rect contributed by this display item itself.
@@ -6181,17 +6237,17 @@ public:
   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) const override
   {
     if (mStoredList.GetComponentAlphaBounds(aBuilder).IsEmpty())
       return nsRect();
     bool snap;
     return GetBounds(aBuilder, &snap);
   }
 
-  virtual nsDisplayList* GetChildren() const override
+  virtual RetainedDisplayList* GetChildren() const override
   {
     return mStoredList.GetChildren();
   }
 
   virtual void SetActiveScrolledRoot(const ActiveScrolledRoot* aActiveScrolledRoot) override
   {
     nsDisplayItem::SetActiveScrolledRoot(aActiveScrolledRoot);
     mStoredList.SetActiveScrolledRoot(aActiveScrolledRoot);
@@ -6581,17 +6637,17 @@ public:
     return true;
   }
 
   virtual nsDisplayList* GetSameCoordinateSystemChildren() const override
   {
     return mList.GetChildren();
   }
 
-  virtual nsDisplayList* GetChildren() const override
+  virtual RetainedDisplayList* GetChildren() const override
   {
     return mList.GetChildren();
   }
 
   virtual void SetActiveScrolledRoot(const ActiveScrolledRoot* aActiveScrolledRoot) override
   {
     nsDisplayItem::SetActiveScrolledRoot(aActiveScrolledRoot);
     mList.SetActiveScrolledRoot(aActiveScrolledRoot);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1443027-1.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
+<title>Bug 1443027 - Test merging across multiple paints</title>
+
+<style>
+
+body { opacity: 0.9; }
+
+div {
+  position: absolute;
+}
+
+#A {
+  left: 250px;
+  top: 50px;
+  width: 100px;
+  height: 100px;
+  background-color: red;
+}
+
+#B {
+  left: 200px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: yellow;
+}
+
+#C {
+  left: 0px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+
+#D {
+  left: 80px;
+  top: 20px;
+  width: 140px;
+  height: 100px;
+  background-color: blue;
+}
+
+</style>
+</head>
+<body>
+<div id="A"></div>
+<div id="B"></div>
+<div id="C"></div>
+<div id="D"></div>
+
+<script>
+
+var A = document.getElementById("A");
+var B = document.getElementById("B");
+var C = document.getElementById("C");
+var D = document.getElementById("D");
+
+A.style.visibility = "hidden";
+B.style.visibility = "hidden";
+C.style.visibility = "hidden";
+D.style.visibility = "hidden";
+
+window.addEventListener("MozReftestInvalidate", step1);
+
+function step1() {
+  C.style.visibility = "visible";
+  C.style.transform = "translatez(1px)";
+  D.style.visibility = "visible";
+  D.style.transform = "translatez(1px)";
+
+  window.requestAnimationFrame(function() { window.requestAnimationFrame(step2); });
+}
+
+function step2() {
+  A.style.visibility = "visible";
+  A.style.transform = "translatez(1px)";
+  B.style.visibility = "visible";
+  B.style.transform = "translatez(1px)";
+
+  window.requestAnimationFrame(step3);
+}
+
+function step3() {
+  D.style.visibility = "hidden";
+  D.style.transform = "";
+  document.documentElement.removeAttribute('class');
+}
+
+</script>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1443027-2.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
+<title>Bug 1443027 - Test merging across multiple paints</title>
+
+<style>
+
+body { opacity: 0.9; }
+
+div {
+  position: absolute;
+}
+
+#A {
+  left: 250px;
+  top: 50px;
+  width: 100px;
+  height: 100px;
+  background-color: red;
+}
+
+#B {
+  left: 200px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: yellow;
+}
+
+#C {
+  left: 0px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+
+#D {
+  left: 80px;
+  top: 20px;
+  width: 140px;
+  height: 100px;
+  background-color: blue;
+}
+
+</style>
+</head>
+<body>
+<div id="A"></div>
+<div id="B"></div>
+<div id="C"></div>
+<div id="D"></div>
+
+<script>
+
+var A = document.getElementById("A");
+var B = document.getElementById("B");
+var C = document.getElementById("C");
+var D = document.getElementById("D");
+
+A.style.visibility = "hidden";
+B.style.visibility = "hidden";
+C.style.visibility = "hidden";
+D.style.visibility = "hidden";
+
+window.addEventListener("MozReftestInvalidate", step1);
+
+function step1() {
+  A.style.visibility = "visible";
+  A.style.transform = "translatez(1px)";
+  B.style.visibility = "visible";
+  B.style.transform = "translatez(1px)";
+  D.style.visibility = "visible";
+  D.style.transform = "translatez(1px)";
+
+  window.requestAnimationFrame(function() { window.requestAnimationFrame(step2); });
+}
+
+function step2() {
+  C.style.visibility = "visible";
+  C.style.transform = "translatez(1px)";
+
+  window.requestAnimationFrame(step3);
+}
+
+function step3() {
+  D.style.visibility = "hidden";
+  D.style.transform = "";
+  document.documentElement.removeAttribute('class');
+}
+
+</script>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1443027-3-ref.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html lang="en"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
+<title>Bug 1443027 - Test merging across multiple paints</title>
+
+<style>
+
+body { opacity: 0.9; }
+
+div {
+  position: absolute;
+  transform: translatez(1px);
+}
+
+#A {
+  left: 250px;
+  top: 50px;
+  width: 100px;
+  height: 100px;
+  background-color: red;
+}
+
+#B {
+  left: 200px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: yellow;
+}
+
+#C {
+  left: 0px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+
+#D {
+  left: 80px;
+  top: 20px;
+  width: 140px;
+  height: 100px;
+  background-color: blue;
+}
+
+</style>
+</head>
+<body>
+<div id="A"></div>
+<div id="B"></div>
+<div id="D"></div>
+<div id="C"></div>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1443027-3.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
+<title>Bug 1443027 - Test merging across multiple paints</title>
+
+<style>
+
+body { opacity: 0.9; }
+
+div {
+  position: absolute;
+}
+
+#A {
+  left: 250px;
+  top: 50px;
+  width: 100px;
+  height: 100px;
+  background-color: red;
+}
+
+#B {
+  left: 200px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: yellow;
+}
+
+#C {
+  left: 0px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+
+#D {
+  left: 80px;
+  top: 20px;
+  width: 140px;
+  height: 100px;
+  background-color: blue;
+}
+
+</style>
+</head>
+<body>
+<div id="A"></div>
+<div id="B"></div>
+<div id="D"></div>
+<div id="C"></div>
+
+<script>
+
+var A = document.getElementById("A");
+var B = document.getElementById("B");
+var C = document.getElementById("C");
+var D = document.getElementById("D");
+
+A.style.visibility = "hidden";
+B.style.visibility = "hidden";
+C.style.visibility = "hidden";
+D.style.visibility = "hidden";
+
+window.addEventListener("MozReftestInvalidate", step1);
+
+function step1() {
+  A.style.visibility = "visible";
+  A.style.transform = "translatez(1px)";
+
+  window.requestAnimationFrame(function() { window.requestAnimationFrame(step2); });
+}
+
+function step2() {
+  C.style.visibility = "visible";
+  C.style.transform = "translatez(1px)";
+  D.style.visibility = "visible";
+  D.style.transform = "translatez(1px)";
+
+  window.requestAnimationFrame(step3);
+}
+
+function step3() {
+  B.style.visibility = "visible";
+  B.style.transform = "translatez(1px)";
+  document.documentElement.removeAttribute('class');
+}
+
+</script>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/1443027-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"><meta charset="utf-8">
+<title>Bug 1443027 - Test merging across multiple paints</title>
+
+<style>
+
+body { opacity: 0.9; }
+
+div {
+  position: absolute;
+  transform: translatez(1px);
+}
+
+#A {
+  left: 250px;
+  top: 50px;
+  width: 100px;
+  height: 100px;
+  background-color: red;
+}
+
+#B {
+  left: 200px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: yellow;
+}
+
+#C {
+  left: 0px;
+  top: 0px;
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+
+</style>
+</head>
+<body>
+<div id="A"></div>
+<div id="B"></div>
+<div id="C"></div>
+</body></html>
--- a/layout/reftests/display-list/reftest.list
+++ b/layout/reftests/display-list/reftest.list
@@ -18,9 +18,11 @@ skip-if(Android) == 1428993-1.html 14289
 == 1420480-1.html 1420480-1-ref.html
 == 1428993-2.html 1428993-2-ref.html
 needs-focus == 1429027-1.html 1429027-1-ref.html
 == 1432553-1.html 1432553-1-ref.html
 == 1432553-2.html 1432553-2-ref.html
 == 1436189-1.html 1436189-1-ref.html
 skip-if(!asyncPan) == 1437374-1.html 1437374-1-ref.html
 == 1439809-1.html 1439809-1-ref.html
-
+== 1443027-1.html 1443027-ref.html
+== 1443027-2.html 1443027-ref.html
+== 1443027-3.html 1443027-3-ref.html
--- a/layout/reftests/position-dynamic-changes/relative/reftest.list
+++ b/layout/reftests/position-dynamic-changes/relative/reftest.list
@@ -1,5 +1,5 @@
-fuzzy-if(cocoaWidget,1,2) fuzzy-if(d2d,47,26) fuzzy-if(asyncPan&&!layersGPUAccelerated,149,716) == move-right-bottom.html move-right-bottom-ref.html
-fuzzy-if(cocoaWidget,1,2) fuzzy-if(asyncPan&&!layersGPUAccelerated,149,716) == move-top-left.html move-top-left-ref.html # Bug 688545
+fuzzy-if(cocoaWidget,1,2) fuzzy-if(d2d,47,26) fuzzy-if(asyncPan&&!layersGPUAccelerated,169,970) == move-right-bottom.html move-right-bottom-ref.html
+fuzzy-if(cocoaWidget,1,2) fuzzy-if(asyncPan&&!layersGPUAccelerated,169,970) == move-top-left.html move-top-left-ref.html # Bug 688545
 fuzzy-if(cocoaWidget,1,3) fuzzy-if(asyncPan&&!layersGPUAccelerated,144,580) == move-right-bottom-table.html move-right-bottom-table-ref.html
 fuzzy-if(cocoaWidget,1,3) fuzzy-if(asyncPan&&!layersGPUAccelerated,144,580) == move-top-left-table.html move-top-left-table-ref.html # Bug 688545
 == percent.html percent-ref.html
--- a/layout/reftests/w3c-css/submitted/ruby/reftest.list
+++ b/layout/reftests/w3c-css/submitted/ruby/reftest.list
@@ -1,11 +1,11 @@
 # Tests for inlinizing block-level boxes
 == ruby-inlinize-blocks-001.html ruby-inlinize-blocks-001-ref.html
-== ruby-inlinize-blocks-002.html ruby-inlinize-blocks-002-ref.html
+fuzzy-if(winWidget,144,1) == ruby-inlinize-blocks-002.html ruby-inlinize-blocks-002-ref.html
 == ruby-inlinize-blocks-003.html ruby-inlinize-blocks-003-ref.html
 == ruby-inlinize-blocks-004.html ruby-inlinize-blocks-004-ref.html
 == ruby-inlinize-blocks-005.html ruby-inlinize-blocks-005-ref.html
 
 # Tests for autohiding base-identical annotations
 == ruby-autohide-001.html ruby-autohide-001-ref.html
 == ruby-autohide-002.html ruby-autohide-002-ref.html
 == ruby-autohide-003.html ruby-autohide-003-ref.html
--- a/layout/style/FontFace.cpp
+++ b/layout/style/FontFace.cpp
@@ -11,21 +11,21 @@
 #include "mozilla/dom/FontFaceSet.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/ServoCSSParser.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/ServoUtils.h"
+#include "mozilla/StaticPrefs.h"
 #include "nsCSSFontFaceRule.h"
 #include "nsCSSParser.h"
 #include "nsIDocument.h"
 #include "nsStyleUtil.h"
-#include "StylePrefs.h"
 
 namespace mozilla {
 namespace dom {
 
 // -- FontFaceBufferSource ---------------------------------------------------
 
 /**
  * An object that wraps a FontFace object and exposes its ArrayBuffer
@@ -593,17 +593,17 @@ FontFace::SetDescriptors(const nsAString
                        aDescriptors.mStretch,
                        mDescriptors->mStretch) ||
       !ParseDescriptor(eCSSFontDesc_UnicodeRange,
                        aDescriptors.mUnicodeRange,
                        mDescriptors->mUnicodeRange) ||
       !ParseDescriptor(eCSSFontDesc_FontFeatureSettings,
                        aDescriptors.mFeatureSettings,
                        mDescriptors->mFontFeatureSettings) ||
-      (StylePrefs::sFontVariationsEnabled &&
+      (StaticPrefs::layout_css_font_variations_enabled() &&
        !ParseDescriptor(eCSSFontDesc_FontVariationSettings,
                         aDescriptors.mVariationSettings,
                         mDescriptors->mFontVariationSettings)) ||
       !ParseDescriptor(eCSSFontDesc_Display,
                        aDescriptors.mDisplay,
                        mDescriptors->mDisplay)) {
     // XXX Handle font-variant once we support it (bug 1055385).
 
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/net/ReferrerPolicy.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ServoCSSParser.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/ServoUtils.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/StaticPrefs.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/LoadInfo.h"
 #include "nsAutoPtr.h"
 #include "nsContentPolicyUtils.h"
 #include "nsCSSParser.h"
 #include "nsDeviceContext.h"
 #include "nsFontFaceLoader.h"
 #include "nsIConsoleService.h"
@@ -45,17 +46,16 @@
 #include "nsNetUtil.h"
 #include "nsIProtocolHandler.h"
 #include "nsIInputStream.h"
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
 #include "nsPrintfCString.h"
 #include "nsUTF8Utils.h"
 #include "nsDOMNavigationTiming.h"
-#include "StylePrefs.h"
 
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::dom;
 
 #define LOG(args) MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
 #define LOG_ENABLED() MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), \
                                   LogLevel::Debug)
@@ -1137,27 +1137,27 @@ FontFaceSet::FindOrCreateUserFontEntryFr
             } else if (valueString.LowerCaseEqualsASCII("truetype")) {
               face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE;
             } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
               face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT;
             } else if (valueString.LowerCaseEqualsASCII("embedded-opentype")) {
               face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_EOT;
             } else if (valueString.LowerCaseEqualsASCII("svg")) {
               face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_SVG;
-            } else if (StylePrefs::sFontVariationsEnabled &&
+            } else if (StaticPrefs::layout_css_font_variations_enabled() &&
                        valueString.LowerCaseEqualsASCII("woff-variations")) {
               face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_WOFF_VARIATIONS;
-            } else if (StylePrefs::sFontVariationsEnabled &&
+            } else if (StaticPrefs::layout_css_font_variations_enabled() &&
                        Preferences::GetBool(GFX_PREF_WOFF2_ENABLED) &&
                        valueString.LowerCaseEqualsASCII("woff2-variations")) {
               face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_WOFF2_VARIATIONS;
-            } else if (StylePrefs::sFontVariationsEnabled &&
+            } else if (StaticPrefs::layout_css_font_variations_enabled() &&
                        valueString.LowerCaseEqualsASCII("opentype-variations")) {
               face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_OPENTYPE_VARIATIONS;
-            } else if (StylePrefs::sFontVariationsEnabled &&
+            } else if (StaticPrefs::layout_css_font_variations_enabled() &&
                        valueString.LowerCaseEqualsASCII("truetype-variations")) {
               face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE_VARIATIONS;
             } else {
               // unknown format specified, mark to distinguish from the
               // case where no format hints are specified
               face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_UNKNOWN;
             }
             i++;
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -66,17 +66,17 @@ headers = [
     "mozilla/Keyframe.h",
     "mozilla/ServoElementSnapshot.h",
     "mozilla/ServoElementSnapshotTable.h",
     "mozilla/css/ErrorReporter.h",
     "mozilla/dom/Element.h",
     "mozilla/dom/ChildIterator.h",
     "mozilla/dom/NameSpaceConstants.h",
     "mozilla/LookAndFeel.h",
-    "mozilla/StylePrefs.h",
+    "mozilla/StaticPrefs.h",
     "mozilla/ServoBindings.h",
     "mozilla/ServoMediaList.h",
     "mozilla/ComputedStyle.h",
     "mozilla/ServoDeclarationBlock.h",
     "mozilla/ServoTraversalStatistics.h",
     "mozilla/SizeOfState.h",
     "nsCSSCounterStyleRule.h",
     "nsCSSFontFaceRule.h",
@@ -238,17 +238,17 @@ whitelist-types = [
     "mozilla::css::URLMatchingFunction",
     "mozilla::dom::IterationCompositeOperation",
     "mozilla::dom::StyleChildrenIterator",
     "mozilla::HalfCorner",
     "mozilla::MallocSizeOf",
     "mozilla::OriginFlags",
     "mozilla::PropertyStyleAnimationValuePair",
     "mozilla::ServoTraversalFlags",
-    "mozilla::StylePrefs",
+    "mozilla::StaticPrefs",
     "mozilla::StyleShapeRadius",
     "mozilla::StyleGrid.*",
     "mozilla::UpdateAnimationsTasks",
     "mozilla::LookAndFeel",
     "mozilla::gfx::Float",
     "mozilla::gfx::FontVariation",
     ".*ThreadSafe.*Holder",
     "AnonymousContent",
deleted file mode 100644
--- a/layout/style/StylePrefs.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*- 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 "mozilla/StylePrefs.h"
-
-#include "mozilla/Preferences.h"
-
-namespace mozilla {
-
-bool StylePrefs::sFontDisplayEnabled;
-bool StylePrefs::sOpentypeSVGEnabled;
-bool StylePrefs::sWebkitPrefixedAliasesEnabled;
-bool StylePrefs::sWebkitDevicePixelRatioEnabled;
-bool StylePrefs::sMozGradientsEnabled;
-bool StylePrefs::sControlCharVisibility;
-bool StylePrefs::sFramesTimingFunctionEnabled;
-bool StylePrefs::sUnprefixedFullscreenApiEnabled;
-bool StylePrefs::sVisitedLinksEnabled;
-bool StylePrefs::sMozDocumentEnabledInContent;
-bool StylePrefs::sMozDocumentURLPrefixHackEnabled;
-bool StylePrefs::sGridTemplateSubgridValueEnabled;
-bool StylePrefs::sFontVariationsEnabled;
-bool StylePrefs::sEmulateMozBoxWithFlex;
-
-/* static */ void
-StylePrefs::Init()
-{
-  Preferences::AddBoolVarCache(&sFontDisplayEnabled,
-                               "layout.css.font-display.enabled");
-  Preferences::AddBoolVarCache(&sOpentypeSVGEnabled,
-                               "gfx.font_rendering.opentype_svg.enabled");
-  Preferences::AddBoolVarCache(&sWebkitPrefixedAliasesEnabled,
-                               "layout.css.prefixes.webkit");
-  Preferences::AddBoolVarCache(&sWebkitDevicePixelRatioEnabled,
-                               "layout.css.prefixes.device-pixel-ratio-webkit");
-  Preferences::AddBoolVarCache(&sMozGradientsEnabled,
-                               "layout.css.prefixes.gradients");
-  Preferences::AddBoolVarCache(&sControlCharVisibility,
-                               "layout.css.control-characters.visible");
-  Preferences::AddBoolVarCache(&sFramesTimingFunctionEnabled,
-                               "layout.css.frames-timing.enabled");
-  Preferences::AddBoolVarCache(&sUnprefixedFullscreenApiEnabled,
-                               "full-screen-api.unprefix.enabled");
-  Preferences::AddBoolVarCache(&sVisitedLinksEnabled,
-                               "layout.css.visited_links_enabled");
-  Preferences::AddBoolVarCache(&sMozDocumentEnabledInContent,
-                               "layout.css.moz-document.content.enabled");
-  Preferences::AddBoolVarCache(&sMozDocumentURLPrefixHackEnabled,
-                               "layout.css.moz-document.url-prefix-hack.enabled");
-  Preferences::AddBoolVarCache(&sGridTemplateSubgridValueEnabled,
-                               "layout.css.grid-template-subgrid-value.enabled");
-  Preferences::AddBoolVarCache(&sFontVariationsEnabled,
-                               "layout.css.font-variations.enabled");
-
-  // Only honor layout.css.emulate-moz-box-with-flex in prerelease builds.
-  // (In release builds, sEmulateMozBoxWithFlex will be implicitly false.)
-#ifndef RELEASE_OR_BETA
-  Preferences::AddBoolVarCache(&sEmulateMozBoxWithFlex,
-                               "layout.css.emulate-moz-box-with-flex");
-#endif
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/layout/style/StylePrefs.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- 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/. */
-
-/* A namespace class for style system prefs */
-
-#ifndef mozilla_StylePrefs_h
-#define mozilla_StylePrefs_h
-
-namespace mozilla {
-
-struct StylePrefs
-{
-  static bool sFontDisplayEnabled;
-  static bool sOpentypeSVGEnabled;
-  static bool sWebkitPrefixedAliasesEnabled;
-  static bool sWebkitDevicePixelRatioEnabled;
-  static bool sMozGradientsEnabled;
-  static bool sControlCharVisibility;
-  static bool sFramesTimingFunctionEnabled;
-  static bool sUnprefixedFullscreenApiEnabled;
-  static bool sVisitedLinksEnabled;
-  static bool sMozDocumentEnabledInContent;
-  static bool sMozDocumentURLPrefixHackEnabled;
-  static bool sGridTemplateSubgridValueEnabled;
-  static bool sFontVariationsEnabled;
-  static bool sEmulateMozBoxWithFlex;
-
-  static void Init();
-};
-
-} // namespace mozilla
-
-#endif // mozilla_StylePrefs_h
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -113,18 +113,18 @@ load 972199-1.html
 load 989965-1.html
 load 992333-1.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1017798-1.html
 load 1028514-1.html
 load 1066089-1.html
 load 1074651-1.html
 load 1135534.html
 pref(dom.webcomponents.shadowdom.enabled,true) load 1089463-1.html
-pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1136010-1.html
-pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1146101-1.html
+load 1136010-1.html
+load 1146101-1.html
 load 1153693-1.html
 load 1161320-1.html
 pref(dom.animations-api.core.enabled,true) load 1161320-2.html
 load 1161366-1.html
 load 1163446-1.html
 load 1164813-1.html
 load 1167782-1.html
 load 1186768-1.xhtml
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -106,17 +106,16 @@ EXPORTS.mozilla += [
     'ServoStyleSheet.h',
     'ServoSupportsRule.h',
     'ServoTraversalStatistics.h',
     'ServoTypes.h',
     'ServoUtils.h',
     'SheetType.h',
     'StyleAnimationValue.h',
     'StyleComplexColor.h',
-    'StylePrefs.h',
     'StyleSheet.h',
     'StyleSheetInfo.h',
     'StyleSheetInlines.h',
     'URLExtraData.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'CSS.h',
@@ -223,17 +222,16 @@ UNIFIED_SOURCES += [
     'ServoPageRule.cpp',
     'ServoSpecifiedValues.cpp',
     'ServoStyleRule.cpp',
     'ServoStyleSet.cpp',
     'ServoStyleSheet.cpp',
     'ServoSupportsRule.cpp',
     'StreamLoader.cpp',
     'StyleAnimationValue.cpp',
-    'StylePrefs.cpp',
     'StyleSheet.cpp',
     'URLExtraData.cpp',
 ]
 
 SOURCES += [
     'nsCSSAnonBoxes.cpp',
     'nsCSSPseudoElements.cpp',
     # nsLayoutStylesheetCache.cpp uses nsExceptionHandler.h, which includes
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -22,17 +22,17 @@
 
 #include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for PlaybackDirection
 #include "mozilla/LookAndFeel.h" // for system colors
 
 #include "nsString.h"
 #include "nsStaticNameTable.h"
 
 #include "mozilla/Preferences.h"
-#include "mozilla/StylePrefs.h"
+#include "mozilla/StaticPrefs.h"
 
 using namespace mozilla;
 
 typedef nsCSSProps::KTableEntry KTableEntry;
 
 // By wrapping internal-only properties in this macro, we are not
 // exposing them in the CSSOM. Since currently it is not necessary to
 // allow accessing them in that way, it is easier and cheaper to just
@@ -362,17 +362,18 @@ nsCSSProps::LookupPropertyByIDLName(cons
 }
 
 nsCSSFontDesc
 nsCSSProps::LookupFontDesc(const nsAString& aFontDesc)
 {
   MOZ_ASSERT(gFontDescTable, "no lookup table, needs addref");
   nsCSSFontDesc which = nsCSSFontDesc(gFontDescTable->Lookup(aFontDesc));
 
-  if (which == eCSSFontDesc_Display && !StylePrefs::sFontDisplayEnabled) {
+  if (which == eCSSFontDesc_Display &&
+      !StaticPrefs::layout_css_font_display_enabled()) {
     which = eCSSFontDesc_UNKNOWN;
   }
   return which;
 }
 
 const nsCString&
 nsCSSProps::GetStringValue(nsCSSPropertyID aProperty)
 {
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -9,17 +9,17 @@
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Logging.h"
 
 #include "nsFontFaceLoader.h"
 
 #include "nsError.h"
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
-#include "mozilla/StylePrefs.h"
+#include "mozilla/StaticPrefs.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Unused.h"
 #include "FontFaceSet.h"
 #include "nsPresContext.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIHttpChannel.h"
 #include "nsIContentPolicy.h"
@@ -333,13 +333,13 @@ nsFontFaceLoader::Cancel()
   }
   mChannel->Cancel(NS_BINDING_ABORTED);
 }
 
 uint8_t
 nsFontFaceLoader::GetFontDisplay()
 {
   uint8_t fontDisplay = NS_FONT_DISPLAY_AUTO;
-  if (StylePrefs::sFontDisplayEnabled) {
+  if (StaticPrefs::layout_css_font_display_enabled()) {
     fontDisplay = mUserFontEntry->GetFontDisplay();
   }
   return fontDisplay;
 }
--- a/media/libcubeb/cubeb-pulse-rs/README_MOZILLA
+++ b/media/libcubeb/cubeb-pulse-rs/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb-pulse-rs
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb-pulse-rs git repository is: https://github.com/djg/cubeb-pulse-rs.git
 
-The git commit ID used was 247b01d8e971d0680fa1cc39e63d17e0fc030556 (2018-03-23 08:27:05 +1000)
+The git commit ID used was 55ce985ad6c53049355907e3df09f172827aecfd (2018-03-29 13:31:04 +1000)
--- a/media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs
+++ b/media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs
@@ -10,16 +10,47 @@ use cubeb_backend::{ffi, log_enabled, Ch
 use pulse::{self, CVolumeExt, ChannelMapExt, SampleSpecExt, StreamLatency, USecExt};
 use pulse_ffi::*;
 use std::{mem, ptr};
 use std::ffi::{CStr, CString};
 use std::os::raw::{c_long, c_void};
 
 const PULSE_NO_GAIN: f32 = -1.0;
 
+/// Iterator interface to `ChannelLayout`.
+///
+/// Iterates each channel in the set represented by `ChannelLayout`.
+struct ChannelLayoutIter {
+    /// The layout set being iterated
+    layout: ChannelLayout,
+    /// The next flag to test
+    index: u8,
+}
+
+fn channel_layout_iter(layout: ChannelLayout) -> ChannelLayoutIter {
+    let index = 0;
+    ChannelLayoutIter { layout, index }
+}
+
+impl Iterator for ChannelLayoutIter {
+    type Item = ChannelLayout;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        while !self.layout.is_empty() {
+            let test = Self::Item::from_bits_truncate(1 << self.index);
+            self.index += 1;
+            if self.layout.contains(test) {
+                self.layout.remove(test);
+                return Some(test);
+            }
+        }
+        None
+    }
+}
+
 fn cubeb_channel_to_pa_channel(channel: ffi::cubeb_channel) -> pa_channel_position_t {
     match channel {
         ffi::CHANNEL_FRONT_LEFT => PA_CHANNEL_POSITION_FRONT_LEFT,
         ffi::CHANNEL_FRONT_RIGHT => PA_CHANNEL_POSITION_FRONT_RIGHT,
         ffi::CHANNEL_FRONT_CENTER => PA_CHANNEL_POSITION_FRONT_CENTER,
         ffi::CHANNEL_LOW_FREQUENCY => PA_CHANNEL_POSITION_LFE,
         ffi::CHANNEL_BACK_LEFT => PA_CHANNEL_POSITION_REAR_LEFT,
         ffi::CHANNEL_BACK_RIGHT => PA_CHANNEL_POSITION_REAR_RIGHT,
@@ -38,28 +69,19 @@ fn cubeb_channel_to_pa_channel(channel: 
         _ => PA_CHANNEL_POSITION_INVALID,
     }
 }
 
 fn layout_to_channel_map(layout: ChannelLayout) -> pulse::ChannelMap {
     assert_ne!(layout, ChannelLayout::UNDEFINED);
 
     let mut cm = pulse::ChannelMap::init();
-
-    let mut channel_map = layout.bits();
-    let mut i = 0;
-    while channel_map != 0 {
-        let channel = (channel_map & 1) << i;
-        if channel != 0 {
-            cm.map[i] = cubeb_channel_to_pa_channel(channel);
-            i += 1;
-        }
-        channel_map = channel_map >> 1;
+    for (i, channel) in channel_layout_iter(layout).enumerate() {
+        cm.map[i] = cubeb_channel_to_pa_channel(channel.into());
     }
-
     cm.channels = layout.num_channels() as _;
     cm
 }
 
 pub struct Device(ffi::cubeb_device);
 
 impl Drop for Device {
     fn drop(&mut self) {
@@ -914,8 +936,340 @@ fn set_buffering_attribute(latency_frame
 
 fn invalid_format() -> Error {
     unsafe { Error::from_raw(ffi::CUBEB_ERROR_INVALID_FORMAT) }
 }
 
 fn not_supported() -> Error {
     unsafe { Error::from_raw(ffi::CUBEB_ERROR_NOT_SUPPORTED) }
 }
+
+#[cfg(all(test, not(feature = "pulse-dlopen")))]
+mod test {
+    use cubeb_backend::ChannelLayout;
+    use pulse_ffi::*;
+    use super::layout_to_channel_map;
+
+    macro_rules! channel_tests {
+        {$($name: ident, $layout: ident => [ $($channels: ident),* ]),+} => {
+            $(
+            #[test]
+            fn $name() {
+                let layout = ChannelLayout::$layout;
+                let mut iter = super::channel_layout_iter(layout);
+                $(
+                assert_eq!(Some(ChannelLayout::$channels), iter.next());
+                )*
+                assert_eq!(None, iter.next());
+            }
+
+            )*
+        }
+    }
+
+    channel_tests! {
+        channels_unknown, UNDEFINED => [ ],
+        channels_mono, MONO => [
+            FRONT_CENTER
+        ],
+        channels_mono_lfe, MONO_LFE => [
+            FRONT_CENTER,
+            LOW_FREQUENCY
+        ],
+        channels_stereo, STEREO => [
+            FRONT_LEFT,
+            FRONT_RIGHT
+        ],
+        channels_stereo_lfe, STEREO_LFE => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            LOW_FREQUENCY
+        ],
+        channels_3f, _3F => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            FRONT_CENTER
+        ],
+        channels_3f_lfe, _3F_LFE => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            FRONT_CENTER,
+            LOW_FREQUENCY
+        ],
+        channels_2f1, _2F1 => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            BACK_CENTER
+        ],
+        channels_2f1_lfe, _2F1_LFE => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            LOW_FREQUENCY,
+            BACK_CENTER
+        ],
+        channels_3f1, _3F1 => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            FRONT_CENTER,
+            BACK_CENTER
+        ],
+        channels_3f1_lfe, _3F1_LFE => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            FRONT_CENTER,
+            LOW_FREQUENCY,
+            BACK_CENTER
+        ],
+        channels_2f2, _2F2 => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            SIDE_LEFT,
+            SIDE_RIGHT
+        ],
+        channels_2f2_lfe, _2F2_LFE => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            LOW_FREQUENCY,
+            SIDE_LEFT,
+            SIDE_RIGHT
+        ],
+        channels_quad, QUAD => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            BACK_LEFT,
+            BACK_RIGHT
+        ],
+        channels_quad_lfe, QUAD_LFE => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            LOW_FREQUENCY,
+            BACK_LEFT,
+            BACK_RIGHT
+        ],
+        channels_3f2, _3F2 => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            FRONT_CENTER,
+            SIDE_LEFT,
+            SIDE_RIGHT
+        ],
+        channels_3f2_lfe, _3F2_LFE => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            FRONT_CENTER,
+            LOW_FREQUENCY,
+            SIDE_LEFT,
+            SIDE_RIGHT
+        ],
+        channels_3f2_back, _3F2_BACK => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            FRONT_CENTER,
+            BACK_LEFT,
+            BACK_RIGHT
+        ],
+        channels_3f2_lfe_back, _3F2_LFE_BACK => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            FRONT_CENTER,
+            LOW_FREQUENCY,
+            BACK_LEFT,
+            BACK_RIGHT
+        ],
+        channels_3f3r_lfe, _3F3R_LFE => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            FRONT_CENTER,
+            LOW_FREQUENCY,
+            BACK_CENTER,
+            SIDE_LEFT,
+            SIDE_RIGHT
+        ],
+        channels_3f4_lfe, _3F4_LFE => [
+            FRONT_LEFT,
+            FRONT_RIGHT,
+            FRONT_CENTER,
+            LOW_FREQUENCY,
+            BACK_LEFT,
+            BACK_RIGHT,
+            SIDE_LEFT,
+            SIDE_RIGHT
+        ]
+    }
+
+    #[test]
+    fn mono_channels_enumerate() {
+        let layout = ChannelLayout::MONO;
+        let mut iter = super::channel_layout_iter(layout).enumerate();
+        assert_eq!(Some((0, ChannelLayout::FRONT_CENTER)), iter.next());
+        assert_eq!(None, iter.next());
+    }
+
+    #[test]
+    fn stereo_channels_enumerate() {
+        let layout = ChannelLayout::STEREO;
+        let mut iter = super::channel_layout_iter(layout).enumerate();
+        assert_eq!(Some((0, ChannelLayout::FRONT_LEFT)), iter.next());
+        assert_eq!(Some((1, ChannelLayout::FRONT_RIGHT)), iter.next());
+        assert_eq!(None, iter.next());
+    }
+
+    #[test]
+    fn quad_channels_enumerate() {
+        let layout = ChannelLayout::QUAD;
+        let mut iter = super::channel_layout_iter(layout).enumerate();
+        assert_eq!(Some((0, ChannelLayout::FRONT_LEFT)), iter.next());
+        assert_eq!(Some((1, ChannelLayout::FRONT_RIGHT)), iter.next());
+        assert_eq!(Some((2, ChannelLayout::BACK_LEFT)), iter.next());
+        assert_eq!(Some((3, ChannelLayout::BACK_RIGHT)), iter.next());
+        assert_eq!(None, iter.next());
+    }
+
+    macro_rules! map_channel_tests {
+        {$($name: ident, $layout: ident => [ $($channels: ident),* ]),+} => {
+            $(
+            #[test]
+            fn $name() {
+                let map = layout_to_channel_map(ChannelLayout::$layout);
+                assert_eq!(map.channels, map_channel_tests!(__COUNT__ $($channels)*));
+                map_channel_tests!(__EACH__ map, 0, $($channels)*);
+            }
+
+            )*
+        };
+        (__COUNT__) => (0u8);
+        (__COUNT__ $x:ident $($xs: ident)*) => (1u8 + map_channel_tests!(__COUNT__ $($xs)*));
+        (__EACH__ $map:expr, $i:expr, ) => {};
+        (__EACH__ $map:expr, $i:expr, $x:ident $($xs: ident)*) => {
+            assert_eq!($map.map[$i], $x);
+            map_channel_tests!(__EACH__ $map, $i+1, $($xs)* );
+        };
+    }
+
+    map_channel_tests! {
+        map_channel_mono, MONO => [
+            PA_CHANNEL_POSITION_FRONT_CENTER
+        ],
+        map_channel_mono_lfe, MONO_LFE => [
+            PA_CHANNEL_POSITION_FRONT_CENTER,
+            PA_CHANNEL_POSITION_LFE
+        ],
+        map_channel_stereo, STEREO => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT
+        ],
+        map_channel_stereo_lfe, STEREO_LFE => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_LFE
+        ],
+        map_channel_3f, _3F => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER
+        ],
+        map_channel_3f_lfe, _3F_LFE => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER,
+            PA_CHANNEL_POSITION_LFE
+        ],
+        map_channel_2f1, _2F1 => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_REAR_CENTER
+        ],
+        map_channel_2f1_lfe, _2F1_LFE => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_REAR_CENTER
+        ],
+        map_channel_3f1, _3F1 => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER,
+            PA_CHANNEL_POSITION_REAR_CENTER
+        ],
+        map_channel_3f1_lfe, _3F1_LFE =>[
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER,
+            PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_REAR_CENTER
+        ],
+        map_channel_2f2, _2F2 => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_SIDE_LEFT,
+            PA_CHANNEL_POSITION_SIDE_RIGHT
+        ],
+        map_channel_2f2_lfe, _2F2_LFE => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_SIDE_LEFT,
+            PA_CHANNEL_POSITION_SIDE_RIGHT
+        ],
+        map_channel_quad, QUAD => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_REAR_LEFT,
+            PA_CHANNEL_POSITION_REAR_RIGHT
+        ],
+        map_channel_quad_lfe, QUAD_LFE => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_REAR_LEFT,
+            PA_CHANNEL_POSITION_REAR_RIGHT
+        ],
+        map_channel_3f2, _3F2 => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER,
+            PA_CHANNEL_POSITION_SIDE_LEFT,
+            PA_CHANNEL_POSITION_SIDE_RIGHT
+        ],
+        map_channel_3f2_lfe, _3F2_LFE => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER,
+            PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_SIDE_LEFT,
+            PA_CHANNEL_POSITION_SIDE_RIGHT
+        ],
+        map_channel_3f2_back, _3F2_BACK => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER,
+            PA_CHANNEL_POSITION_REAR_LEFT,
+            PA_CHANNEL_POSITION_REAR_RIGHT
+        ],
+        map_channel_3f2_lfe_back, _3F2_LFE_BACK => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER,
+            PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_REAR_LEFT,
+            PA_CHANNEL_POSITION_REAR_RIGHT
+        ],
+        map_channel_3f3r_lfe, _3F3R_LFE => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER,
+            PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_REAR_CENTER,
+            PA_CHANNEL_POSITION_SIDE_LEFT,
+            PA_CHANNEL_POSITION_SIDE_RIGHT
+        ],
+        map_channel_3f4_lfe, _3F4_LFE => [
+            PA_CHANNEL_POSITION_FRONT_LEFT,
+            PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_FRONT_CENTER,
+            PA_CHANNEL_POSITION_LFE,
+            PA_CHANNEL_POSITION_REAR_LEFT,
+            PA_CHANNEL_POSITION_REAR_RIGHT,
+            PA_CHANNEL_POSITION_SIDE_LEFT,
+            PA_CHANNEL_POSITION_SIDE_RIGHT
+        ]
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/chrome/geckoview/GeckoViewSelectionActionContent.js
@@ -0,0 +1,249 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ChromeUtils.import("resource://gre/modules/GeckoViewContentModule.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+  Services: "resource://gre/modules/Services.jsm",
+});
+
+XPCOMUtils.defineLazyGetter(this, "dump", () =>
+    ChromeUtils.import("resource://gre/modules/AndroidLog.jsm",
+                       {}).AndroidLog.d.bind(null, "ViewSelectionActionContent"));
+
+function debug(aMsg) {
+  // dump(aMsg);
+}
+
+// Dispatches GeckoView:ShowSelectionAction and GeckoView:HideSelectionAction to
+// the GeckoSession on accessible caret changes.
+class GeckoViewSelectionActionContent extends GeckoViewContentModule {
+  constructor(aModuleName, aMessageManager) {
+    super(aModuleName, aMessageManager);
+
+    this._seqNo = 0;
+    this._isActive = false;
+    this._previousMessage = {};
+
+    this._actions = [{
+      id: "org.mozilla.geckoview.CUT",
+      predicate: e => !e.collapsed && e.selectionEditable,
+      perform: _ => this._domWindowUtils.sendContentCommandEvent("cut"),
+    }, {
+      id: "org.mozilla.geckoview.COPY",
+      predicate: e => !e.collapsed,
+      perform: _ => this._domWindowUtils.sendContentCommandEvent("copy"),
+    }, {
+      id: "org.mozilla.geckoview.PASTE",
+      predicate: e => e.selectionEditable &&
+                      Services.clipboard.hasDataMatchingFlavors(
+                          ["text/unicode"], 1, Ci.nsIClipboard.kGlobalClipboard),
+      perform: _ => this._domWindowUtils.sendContentCommandEvent("paste"),
+    }, {
+      id: "org.mozilla.geckoview.DELETE",
+      predicate: e => !e.collapsed && e.selectionEditable,
+      perform: _ => this._domWindowUtils.sendContentCommandEvent("delete"),
+    }, {
+      id: "org.mozilla.geckoview.COLLAPSE_TO_START",
+      predicate: e => !e.collapsed && e.selectionEditable,
+      perform: e => this._getSelection(e).collapseToStart(),
+    }, {
+      id: "org.mozilla.geckoview.COLLAPSE_TO_END",
+      predicate: e => !e.collapsed && e.selectionEditable,
+      perform: e => this._getSelection(e).collapseToEnd(),
+    }, {
+      id: "org.mozilla.geckoview.UNSELECT",
+      predicate: e => !e.collapsed && !e.selectionEditable,
+      perform: e => this._getSelection(e).removeAllRanges(),
+    }, {
+      id: "org.mozilla.geckoview.SELECT_ALL",
+      predicate: e => e.reason !== "longpressonemptycontent",
+      perform: e => this._getSelectionController(e).selectAll(),
+    }];
+  }
+
+  get _domWindowUtils() {
+    return content.QueryInterface(Ci.nsIInterfaceRequestor)
+                  .getInterface(Ci.nsIDOMWindowUtils);
+  }
+
+  _getSelectionController(aEvent) {
+    if (aEvent.selectionEditable) {
+      const focus = aEvent.target.activeElement;
+      if (focus instanceof Ci.nsIDOMNSEditableElement && focus.editor) {
+        return focus.editor.selectionController;
+      }
+    }
+
+    return aEvent.target.defaultView
+                 .QueryInterface(Ci.nsIInterfaceRequestor)
+                 .getInterface(Ci.nsIDocShell)
+                 .QueryInterface(Ci.nsIInterfaceRequestor)
+                 .getInterface(Ci.nsISelectionDisplay)
+                 .QueryInterface(Ci.nsISelectionController);
+  }
+
+  _getSelection(aEvent) {
+    return this._getSelectionController(aEvent)
+               .getSelection(Ci.nsISelectionController.SELECTION_NORMAL);
+  }
+
+  _getFrameOffset(aEvent) {
+    // Get correct offset in case of nested iframe.
+    const offset = {
+      left: 0,
+      top: 0,
+    };
+
+    let currentWindow = aEvent.target.defaultView;
+    while (currentWindow.realFrameElement) {
+      const currentRect = currentWindow.realFrameElement.getBoundingClientRect();
+      currentWindow = currentWindow.realFrameElement.ownerGlobal;
+
+      offset.left += currentRect.left;
+      offset.top += currentRect.top;
+
+      let targetDocShell = currentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                        .getInterface(Ci.nsIWebNavigation);
+      if (targetDocShell.isMozBrowser) {
+        break;
+      }
+    }
+
+    return offset;
+  }
+
+  register() {
+    debug("register");
+    addEventListener("mozcaretstatechanged", this);
+  }
+
+  unregister() {
+    debug("unregister");
+    removeEventListener("mozcaretstatechanged", this);
+  }
+
+  /**
+   * Receive and act on AccessibleCarets caret state-change
+   * (mozcaretstatechanged) events.
+   */
+  handleEvent(aEvent) {
+    let reason = aEvent.reason;
+
+    if (this._isActive && !aEvent.caretVisible) {
+      // For mozcaretstatechanged, "visibilitychange" means the caret is hidden.
+      reason = "visibilitychange";
+    } else if (this._isActive &&
+               !aEvent.collapsed &&
+               !aEvent.selectionVisible) {
+      reason = "invisibleselection";
+    } else if (aEvent.selectionEditable &&
+               aEvent.collapsed &&
+               reason !== "longpressonemptycontent" &&
+               reason !== "taponcaret") {
+      // Don't show selection actions when merely focusing on an editor or
+      // repositioning the cursor. Wait until long press or the caret is tapped
+      // in order to match Android behavior.
+      reason = "visibilitychange";
+    }
+
+    debug("handleEvent " + reason + " " + aEvent);
+
+    if (["longpressonemptycontent",
+         "releasecaret",
+         "taponcaret",
+         "updateposition"].includes(reason)) {
+
+      const actions = this._actions.filter(
+          action => action.predicate.call(this, aEvent));
+
+      const offset = this._getFrameOffset(aEvent);
+
+      const msg = {
+        type: "GeckoView:ShowSelectionAction",
+        seqNo: this._seqNo,
+        collapsed: aEvent.collapsed,
+        editable: aEvent.selectionEditable,
+        selection: aEvent.selectedTextContent,
+        clientRect: !aEvent.boundingClientRect ? null : {
+          left: aEvent.boundingClientRect.left + offset.left,
+          top: aEvent.boundingClientRect.top + offset.top,
+          right: aEvent.boundingClientRect.right + offset.left,
+          bottom: aEvent.boundingClientRect.bottom + offset.top,
+        },
+        actions: actions.map(action => action.id),
+      };
+
+      try {
+        if (msg.clientRect) {
+          msg.clientRect.bottom += parseFloat(Services.prefs.getCharPref(
+              "layout.accessiblecaret.height", "0"));
+        }
+      } catch (e) {
+      }
+
+      if (this._isActive && JSON.stringify(msg) === this._previousMessage) {
+        // Don't call again if we're already active and things haven't changed.
+        return;
+      }
+
+      msg.seqNo = ++this._seqNo;
+      this._isActive = true;
+      this._previousMessage = JSON.stringify(msg);
+
+      debug("onShowSelectionAction " + JSON.stringify(msg));
+
+      // This event goes to GeckoViewSelectionAction.jsm, where the data is
+      // further transformed and then sent to GeckoSession.
+      this.eventDispatcher.sendRequest(msg, {
+        onSuccess: response => {
+          if (response.seqNo !== this._seqNo) {
+            // Stale action.
+            return;
+          }
+          let action = actions.find(action => action.id === response.id);
+          if (action) {
+            action.perform.call(this, aEvent, response);
+          } else {
+            dump("Invalid action " + response.id);
+          }
+        },
+        onError: _ => {
+          // Do nothing; we can get here if the delegate was just unregistered.
+        },
+      });
+
+    } else if (["invisibleselection",
+                "presscaret",
+                "scroll",
+                "visibilitychange"].includes(reason)) {
+
+      if (!this._isActive) {
+        return;
+      }
+
+      this._isActive = false;
+
+      // Mark previous actions as stale. Don't do this for "invisibleselection"
+      // or "scroll" because previous actions should still be valid even after
+      // these events occur.
+      if (reason !== "invisibleselection" && reason !== "scroll") {
+        this._seqNo++;
+      }
+
+      this.eventDispatcher.sendRequest({
+        type: "GeckoView:HideSelectionAction",
+        reason: reason,
+      });
+
+    } else {
+      dump("Unknown reason: " + reason);
+    }
+  }
+}
+
+var selectionActionListener =
+    new GeckoViewSelectionActionContent("GeckoViewSelectionAction", this);
--- a/mobile/android/chrome/geckoview/geckoview.js
+++ b/mobile/android/chrome/geckoview/geckoview.js
@@ -79,13 +79,15 @@ function startup() {
   ModuleManager.add("resource://gre/modules/GeckoViewScroll.jsm",
                     "GeckoViewScroll");
   ModuleManager.add("resource://gre/modules/GeckoViewTab.jsm",
                     "GeckoViewTab");
   ModuleManager.add("resource://gre/modules/GeckoViewRemoteDebugger.jsm",
                     "GeckoViewRemoteDebugger");
   ModuleManager.add("resource://gre/modules/GeckoViewTrackingProtection.jsm",
                     "GeckoViewTrackingProtection");
+  ModuleManager.add("resource://gre/modules/GeckoViewSelectionAction.jsm",
+                    "GeckoViewSelectionAction");
 
   // Move focus to the content window at the end of startup,
   // so things like text selection can work properly.
   browser.focus();
 }
--- a/mobile/android/chrome/geckoview/jar.mn
+++ b/mobile/android/chrome/geckoview/jar.mn
@@ -7,8 +7,9 @@ geckoview.jar:
 
   content/ErrorPageEventHandler.js
   content/geckoview.xul
   content/geckoview.js
   content/GeckoViewContent.js
   content/GeckoViewContentSettings.js
   content/GeckoViewNavigationContent.js
   content/GeckoViewScrollContent.js
+  content/GeckoViewSelectionActionContent.js
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Callbacks.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/util/Callbacks.kt
@@ -5,17 +5,18 @@ package org.mozilla.geckoview.test.util
 
 import org.mozilla.geckoview.GeckoSession
 
 class Callbacks private constructor() {
     object Default : All {
     }
 
     interface All : ContentDelegate, NavigationDelegate, PermissionDelegate, ProgressDelegate,
-                    PromptDelegate, ScrollDelegate, TrackingProtectionDelegate {
+                    PromptDelegate, ScrollDelegate, SelectionActionDelegate,
+                    TrackingProtectionDelegate {
     }
 
     interface ContentDelegate : GeckoSession.ContentDelegate {
         override fun onTitleChange(session: GeckoSession, title: String) {
         }
 
         override fun onFocusRequest(session: GeckoSession) {
         }
@@ -115,9 +116,17 @@ class Callbacks private constructor() {
         override fun onScrollChanged(session: GeckoSession, scrollX: Int, scrollY: Int) {
         }
     }
 
     interface TrackingProtectionDelegate : GeckoSession.TrackingProtectionDelegate {
         override fun onTrackerBlocked(session: GeckoSession, uri: String, categories: Int) {
         }
     }
+
+    interface SelectionActionDelegate : GeckoSession.SelectionActionDelegate {
+        override fun onShowActionRequest(session: GeckoSession, selection: GeckoSession.SelectionActionDelegate.Selection, actions: Array<out String>, response: GeckoSession.Response<String>) {
+        }
+
+        override fun onHideAction(session: GeckoSession, reason: Int) {
+        }
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -71,16 +71,17 @@ import android.hardware.SensorManager;
 import android.location.Criteria;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -1867,11 +1868,26 @@ public class GeckoAppShell
         if (prop == null) {
             return 0;
         }
         return Integer.parseInt(prop);
     }
 
     @WrapForJNI
     public static String getDefaultLocale() {
-        return Locale.getDefault().toString();
+        final Locale locale = Locale.getDefault();
+        if (Build.VERSION.SDK_INT >= 21) {
+            return locale.toLanguageTag();
+        }
+
+        final StringBuilder out = new StringBuilder(locale.getLanguage());
+        final String country = locale.getCountry();
+        final String variant = locale.getVariant();
+        if (!TextUtils.isEmpty(country)) {
+            out.append('-').append(country);
+        }
+        if (!TextUtils.isEmpty(variant)) {
+            out.append('-').append(variant);
+        }
+        // e.g. "en", "en-US", or "en-US-POSIX".
+        return out.toString();
     }
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/BasicSelectionActionDelegate.java
@@ -0,0 +1,255 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * vim: ts=4 sw=4 expandtab:
+ * 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/. */
+
+package org.mozilla.geckoview;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Build;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class BasicSelectionActionDelegate implements ActionMode.Callback,
+                                             GeckoSession.SelectionActionDelegate {
+    private static final String LOGTAG = "GeckoBasicSelectionAction";
+
+    private static final String[] FLOATING_TOOLBAR_ACTIONS = new String[] {
+            ACTION_CUT, ACTION_COPY, ACTION_PASTE, ACTION_SELECT_ALL
+    };
+    private static final String[] FIXED_TOOLBAR_ACTIONS = new String[] {
+            ACTION_PASTE, ACTION_COPY, ACTION_CUT, ACTION_SELECT_ALL
+    };
+
+    protected final Activity mActivity;
+    protected final boolean mUseFloatingToolbar;
+    protected final Matrix mTempMatrix = new Matrix();
+    protected final RectF mTempRect = new RectF();
+
+    protected ActionMode mActionMode;
+    protected GeckoSession mSession;
+    protected Selection mSelection;
+    protected List<String> mActions;
+    protected GeckoSession.Response<String> mResponse;
+
+    @TargetApi(Build.VERSION_CODES.M)
+    private class Callback2Wrapper extends ActionMode.Callback2 {
+        @Override
+        public boolean onCreateActionMode(final ActionMode actionMode, final Menu menu) {
+            return BasicSelectionActionDelegate.this.onCreateActionMode(actionMode, menu);
+        }
+
+        @Override
+        public boolean onPrepareActionMode(final ActionMode actionMode, final Menu menu) {
+            return BasicSelectionActionDelegate.this.onPrepareActionMode(actionMode, menu);
+        }
+
+        @Override
+        public boolean onActionItemClicked(final ActionMode actionMode, final MenuItem menuItem) {
+            return BasicSelectionActionDelegate.this.onActionItemClicked(actionMode, menuItem);
+        }
+
+        @Override
+        public void onDestroyActionMode(final ActionMode actionMode) {
+            BasicSelectionActionDelegate.this.onDestroyActionMode(actionMode);
+        }
+
+        @Override
+        public void onGetContentRect(final ActionMode mode, final View view, final Rect outRect) {
+            super.onGetContentRect(mode, view, outRect);
+            BasicSelectionActionDelegate.this.onGetContentRect(mode, view, outRect);
+        }
+    }
+
+    public BasicSelectionActionDelegate(final Activity activity) {
+        this(activity, Build.VERSION.SDK_INT >= 23);
+    }
+
+    public BasicSelectionActionDelegate(final Activity activity, final boolean useFloatingToolbar) {
+        mActivity = activity;
+        mUseFloatingToolbar = useFloatingToolbar;
+    }
+
+    /**
+     * Return list of all actions in proper order, regardless of their availability at present.
+     * Override to add to or remove from the default set.
+     *
+     * @return Array of action IDs in proper order.
+     */
+    protected String[] getAllActions() {
+        return mUseFloatingToolbar ? FLOATING_TOOLBAR_ACTIONS
+                                   : FIXED_TOOLBAR_ACTIONS;
+    }
+
+    /**
+     * Return whether an action is presently available. Override to indicate
+     * availability for custom actions.
+     *
+     * @param id Action ID.
+     * @return True if the action is presently available.
+     */
+    protected boolean isActionAvailable(final String id) {
+        return mActions.contains(id);
+    }
+
+    /**
+     * Prepare a menu item corresponding to a certain action. Override to prepare
+     * menu item for custom action.
+     *
+     * @param id Action ID.
+     * @param item New menu item to prepare.
+     */
+    protected void prepareAction(final String id, final MenuItem item) {
+        switch (id) {
+            case ACTION_CUT:
+                item.setTitle(android.R.string.cut);
+                break;
+            case ACTION_COPY:
+                item.setTitle(android.R.string.copy);
+                break;
+            case ACTION_PASTE:
+                item.setTitle(android.R.string.paste);
+                break;
+            case ACTION_SELECT_ALL:
+                item.setTitle(android.R.string.selectAll);
+                break;
+        }
+    }
+
+    /**
+     * Perform the specified action. Override to perform custom actions.
+     *
+     * @param id Action ID.
+     * @return True if the action was performed.
+     */
+    protected boolean performAction(final String id) {
+        mResponse.respond(id);
+
+        // Android behavior is to clear selection on copy.
+        if (ACTION_COPY.equals(id)) {
+            if (isActionAvailable(ACTION_COLLAPSE_TO_END)) {
+                mResponse.respond(ACTION_COLLAPSE_TO_END);
+            } else {
+                mResponse.respond(ACTION_UNSELECT);
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onCreateActionMode(final ActionMode actionMode, final Menu menu) {
+        final String[] allActions = getAllActions();
+        for (final String actionId : allActions) {
+            if (isActionAvailable(actionId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onPrepareActionMode(final ActionMode actionMode, final Menu menu) {
+        final String[] allActions = getAllActions();
+        boolean changed = false;
+
+        // For each action, see if it's available at present, and if necessary,
+        // add to or remove from menu.
+        for (int menuId = 0; menuId < allActions.length; menuId++) {
+            final String actionId = allActions[menuId];
+            if (isActionAvailable(actionId)) {
+                if (menu.findItem(menuId) == null) {
+                    prepareAction(actionId, menu.add(/* group */ Menu.NONE, menuId,
+                                                     menuId, /* title */ ""));
+                    changed = true;
+                }
+            } else if (menu.findItem(menuId) != null) {
+                menu.removeItem(menuId);
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    @Override
+    public boolean onActionItemClicked(final ActionMode actionMode, final MenuItem menuItem) {
+        final String[] allActions = getAllActions();
+        return performAction(allActions[menuItem.getItemId()]);
+    }
+
+    @Override
+    public void onDestroyActionMode(final ActionMode actionMode) {
+        mSession = null;
+        mSelection = null;
+        mActions = null;
+        mResponse = null;
+        mActionMode = null;
+    }
+
+    public void onGetContentRect(final ActionMode mode, final View view, final Rect outRect) {
+        if (mSelection.clientRect == null) {
+            return;
+        }
+        mSession.getClientToScreenMatrix(mTempMatrix);
+        mTempMatrix.mapRect(mTempRect, mSelection.clientRect);
+        mTempRect.roundOut(outRect);
+    }
+
+    @TargetApi(Build.VERSION_CODES.M)
+    @Override
+    public void onShowActionRequest(final GeckoSession session, final Selection selection,
+                                    final String[] actions,
+                                    final GeckoSession.Response<String> response) {
+        mSession = session;
+        mSelection = selection;
+        mActions = Arrays.asList(actions);
+        mResponse = response;
+
+        if (mActionMode != null) {
+            if (actions.length > 0) {
+                mActionMode.invalidate();
+            } else {
+                mActionMode.finish();
+            }
+            return;
+        }
+
+        if (mUseFloatingToolbar) {
+            mActionMode = mActivity.startActionMode(new Callback2Wrapper(),
+                                                    ActionMode.TYPE_FLOATING);
+        } else {
+            mActionMode = mActivity.startActionMode(this);
+        }
+    }
+
+    @Override
+    public void onHideAction(GeckoSession session, int reason) {
+        if (mActionMode == null) {
+            return;
+        }
+
+        switch (reason) {
+            case HIDE_REASON_ACTIVE_SCROLL:
+            case HIDE_REASON_ACTIVE_SELECTION:
+            case HIDE_REASON_INVISIBLE_SELECTION:
+                if (mUseFloatingToolbar) {
+                    // Hide the floating toolbar when scrolling/selecting.
+                    mActionMode.finish();
+                }
+                break;
+
+            case HIDE_REASON_NO_SELECTION:
+                mActionMode.finish();
+                break;
+        }
+    }
+}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -1,16 +1,18 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * vim: ts=4 sw=4 expandtab:
  * 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/. */
 
 package org.mozilla.geckoview;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.UUID;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.gfx.LayerSession;
@@ -24,27 +26,29 @@ import org.mozilla.gecko.util.BundleEven
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.graphics.RectF;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.annotation.NonNull;
+import android.support.annotation.StringDef;
 import android.util.Base64;
 import android.util.Log;
 
 public class GeckoSession extends LayerSession
                           implements Parcelable {
     private static final String LOGTAG = "GeckoSession";
     private static final boolean DEBUG = false;
 
@@ -329,21 +333,73 @@ public class GeckoSession extends LayerS
 
                     delegate.onMediaPermissionRequest(
                             GeckoSession.this, message.getString("uri"),
                             videos, audios, new PermissionCallback("media", callback));
                 }
             }
         };
 
+    private final GeckoSessionHandler<SelectionActionDelegate> mSelectionActionDelegate =
+        new GeckoSessionHandler<SelectionActionDelegate>(
+            "GeckoViewSelectionAction", this,
+            new String[] {
+                "GeckoView:HideSelectionAction",
+                "GeckoView:ShowSelectionAction",
+            }
+        ) {
+            @Override
+            public void handleMessage(final SelectionActionDelegate delegate,
+                                      final String event,
+                                      final GeckoBundle message,
+                                      final EventCallback callback) {
+                if ("GeckoView:ShowSelectionAction".equals(event)) {
+                    final SelectionActionDelegate.Selection selection =
+                            new SelectionActionDelegate.Selection(message);
+
+                    final String[] actions = message.getStringArray("actions");
+                    final int seqNo = message.getInt("seqNo");
+                    final Response<String> response = new Response<String>() {
+                        @Override
+                        public void respond(final String action) {
+                            final GeckoBundle response = new GeckoBundle(2);
+                            response.putString("id", action);
+                            response.putInt("seqNo", seqNo);
+                            callback.sendSuccess(response);
+                        }
+                    };
+
+                    delegate.onShowActionRequest(GeckoSession.this, selection,
+                                                 actions, response);
+
+                } else if ("GeckoView:HideSelectionAction".equals(event)) {
+                    final String reasonString = message.getString("reason");
+                    final int reason;
+                    if ("invisibleselection".equals(reasonString)) {
+                        reason = SelectionActionDelegate.HIDE_REASON_INVISIBLE_SELECTION;
+                    } else if ("presscaret".equals(reasonString)) {
+                        reason = SelectionActionDelegate.HIDE_REASON_ACTIVE_SELECTION;
+                    } else if ("scroll".equals(reasonString)) {
+                        reason = SelectionActionDelegate.HIDE_REASON_ACTIVE_SCROLL;
+                    } else if ("visibilitychange".equals(reasonString)) {
+                        reason = SelectionActionDelegate.HIDE_REASON_NO_SELECTION;
+                    } else {
+                        throw new IllegalArgumentException();
+                    }
+
+                    delegate.onHideAction(GeckoSession.this, reason);
+                }
+            }
+        };
+
     /* package */ int handlersCount;
 
     private final GeckoSessionHandler<?>[] mSessionHandlers = new GeckoSessionHandler<?>[] {
         mContentHandler, mNavigationHandler, mProgressHandler, mScrollHandler,
-        mTrackingProtectionHandler, mPermissionHandler
+        mTrackingProtectionHandler, mPermissionHandler, mSelectionActionDelegate
     };
 
     private static class PermissionCallback implements
         PermissionDelegate.Callback, PermissionDelegate.MediaCallback {
 
         private final String mType;
         private EventCallback mCallback;
 
@@ -1126,16 +1182,34 @@ public class GeckoSession extends LayerS
     /**
      * Get the current prompt delegate for this GeckoSession.
      * @return PromptDelegate instance or null if using built-in delegate.
      */
     public PromptDelegate getPromptDelegate() {
         return mPromptDelegate;
     }
 
+    /**
+     * Set the current selection action delegate for this GeckoSession.
+     *
+     * @param delegate SelectionActionDelegate instance or null to unset.
+     */
+    public void setSelectionActionDelegate(@Nullable SelectionActionDelegate delegate) {
+        mSelectionActionDelegate.setDelegate(delegate, this);
+    }
+
+    /**
+     * Get the current selection action delegate for this GeckoSession.
+     *
+     * @return SelectionActionDelegate instance or null if not set.
+     */
+    public @Nullable SelectionActionDelegate getSelectionActionDelegate() {
+        return mSelectionActionDelegate.getDelegate();
+    }
+
     private static class PromptCallback implements
         PromptDelegate.AlertCallback, PromptDelegate.ButtonCallback,
         PromptDelegate.TextCallback, PromptDelegate.AuthCallback,
         PromptDelegate.ChoiceCallback, PromptDelegate.FileCallback {
 
         private final String mType;
         private final String mMode;
         private final boolean mHasCheckbox;
@@ -1719,16 +1793,186 @@ public class GeckoSession extends LayerS
          * by Gecko (e.g., a download).
          *
          * @param session the GeckoSession that received the external response.
          * @param response the WebResponseInfo for the external response
          */
         void onExternalResponse(GeckoSession session, WebResponseInfo response);
     }
 
+    public interface SelectionActionDelegate {
+        @IntDef(flag = true, value = {FLAG_IS_COLLAPSED,
+                                      FLAG_IS_EDITABLE})
+        @interface Flag {}
+
+        /**
+         * The selection is collapsed at a single position.
+         */
+        final int FLAG_IS_COLLAPSED = 1;
+        /**
+         * The selection is inside editable content such as an input element or
+         * contentEditable node.
+         */
+        final int FLAG_IS_EDITABLE = 2;
+
+        @StringDef({ACTION_CUT,
+                    ACTION_COPY,
+                    ACTION_DELETE,
+                    ACTION_PASTE,
+                    ACTION_SELECT_ALL,
+                    ACTION_UNSELECT,
+                    ACTION_COLLAPSE_TO_START,
+                    ACTION_COLLAPSE_TO_END})
+        @interface Action {}
+
+        /**
+         * Copy onto the clipboard then delete the selected content. Selection
+         * must be editable.
+         */
+        final String ACTION_CUT = "org.mozilla.geckoview.CUT";
+        /**
+         * Copy the selected content onto the clipboard.
+         */
+        final String ACTION_COPY = "org.mozilla.geckoview.COPY";
+        /**
+         * Delete the selected content. Selection must be editable.
+         */
+        final String ACTION_DELETE = "org.mozilla.geckoview.DELETE";
+        /**
+         * Replace the selected content with the clipboard content. Selection
+         * must be editable.
+         */
+        final String ACTION_PASTE = "org.mozilla.geckoview.PASTE";
+        /**
+         * Select the entire content of the document or editor.
+         */
+        final String ACTION_SELECT_ALL = "org.mozilla.geckoview.SELECT_ALL";
+        /**
+         * Clear the current selection. Selection must not be editable.
+         */
+        final String ACTION_UNSELECT = "org.mozilla.geckoview.UNSELECT";
+        /**
+         * Collapse the current selection to its start position.
+         * Selection must be editable.
+         */
+        final String ACTION_COLLAPSE_TO_START = "org.mozilla.geckoview.COLLAPSE_TO_START";
+        /**
+         * Collapse the current selection to its end position.
+         * Selection must be editable.
+         */
+        final String ACTION_COLLAPSE_TO_END = "org.mozilla.geckoview.COLLAPSE_TO_END";
+
+        /**
+         * Represents attributes of a selection.
+         */
+        class Selection {
+            /**
+             * Flags describing the current selection, as a bitwise combination
+             * of the {@link #FLAG_IS_COLLAPSED FLAG_*} constants.
+             */
+            public final @Flag int flags;
+
+            /**
+             * Text content of the current selection. An empty string indicates the selection
+             * is collapsed or the selection cannot be represented as plain text.
+             */
+            public final String text;
+
+            /**
+             * The bounds of the current selection in client coordinates. Use {@link
+             * GeckoSession#getClientToScreenMatrix} to perform transformation to screen
+             * coordinates.
+             */
+            public final RectF clientRect;
+
+            /* package */ Selection(final GeckoBundle bundle) {
+                flags = (bundle.getBoolean("collapsed") ?
+                         SelectionActionDelegate.FLAG_IS_COLLAPSED : 0) |
+                        (bundle.getBoolean("editable") ?
+                         SelectionActionDelegate.FLAG_IS_EDITABLE : 0);
+                text = bundle.getString("selection");
+
+                final GeckoBundle rectBundle = bundle.getBundle("clientRect");
+                if (rectBundle == null) {
+                    clientRect = null;
+                } else {
+                    clientRect = new RectF((float) rectBundle.getDouble("left"),
+                                           (float) rectBundle.getDouble("top"),
+                                           (float) rectBundle.getDouble("right"),
+                                           (float) rectBundle.getDouble("bottom"));
+                }
+            }
+        }
+
+        /**
+         * Selection actions are available. Selection actions become available when the
+         * user selects some content in the document or editor. Inside an editor,
+         * selection actions can also become available when the user explicitly requests
+         * editor action UI, for example by tapping on the caret handle.
+         *
+         * In response to this callback, applications typically display a toolbar
+         * containing the selection actions. To perform a certain action, pass the Action
+         * object back through the response parameter, which may be used multiple times to
+         * perform multiple actions at once.
+         *
+         * Once a {@link #onHideAction} call (with particular reasons) or another {@link
+         * #onShowActionRequest} call is received, any previously received actions are no
+         * longer unavailable.
+         *
+         * @param session The GeckoSession that initiated the callback.
+         * @param selection Current selection attributes.
+         * @param actions List of built-in actions available.
+         * @param response Callback object for performing built-in actions. For example,
+         * {@code response.respond(actions[0])} performs the first action. May be used
+         * multiple times to perform multiple actions at once.
+         */
+        void onShowActionRequest(GeckoSession session, Selection selection,
+                                 @Action String[] actions, Response<String> response);
+
+        @IntDef({HIDE_REASON_NO_SELECTION,
+                 HIDE_REASON_INVISIBLE_SELECTION,
+                 HIDE_REASON_ACTIVE_SELECTION,
+                 HIDE_REASON_ACTIVE_SCROLL})
+        @interface HideReason {}
+
+        /**
+         * Actions are no longer available due to the user clearing the selection.
+         */
+        final int HIDE_REASON_NO_SELECTION = 0;
+        /**
+         * Actions are no longer available due to the user moving the selection becoming
+         * out of view. Previous actions are still available after a callback with this
+         * reason.
+         */
+        final int HIDE_REASON_INVISIBLE_SELECTION = 1;
+        /**
+         * Actions are no longer available due to the user actively changing the
+         * selection. {@link #onShowActionRequest} may be called again once the user has
+         * set a selection, if the new selection has available actions.
+         */
+        final int HIDE_REASON_ACTIVE_SELECTION = 2;
+        /**
+         * Actions are no longer available due to the user actively scrolling the page.
+         * {@link #onShowActionRequest} may be called again once the user has stopped
+         * scrolling the page, if the selection is still visible. Until then, previous
+         * actions are still available after a callback with this reason.
+         */
+        final int HIDE_REASON_ACTIVE_SCROLL = 3;
+
+        /**
+         * Previous actions are no longer available due to the user interacting with the
+         * page. Applications typically hide the action toolbar in response.
+         *
+         * @param session The GeckoSession that initiated the callback.
+         * @param reason The reason that actions are no longer available, as one of the
+         * {@link #HIDE_REASON_NO_SELECTION HIDE_REASON_*} constants.
+         */
+        void onHideAction(GeckoSession session, @HideReason int reason);
+    }
+
     /**
      * This is used to send responses in delegate methods that have asynchronous responses.
      */
     public interface Response<T> {
         /**
          * @param val The value contained in the response
          */
         void respond(T val);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoView.java
@@ -7,17 +7,19 @@
 package org.mozilla.geckoview;
 
 import org.mozilla.gecko.AndroidGamepadManager;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
 import org.mozilla.gecko.gfx.PanZoomController;
 import org.mozilla.gecko.gfx.GeckoDisplay;
 import org.mozilla.gecko.InputMethods;
+import org.mozilla.gecko.util.ActivityUtils;
 
+import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Parcel;
@@ -46,16 +48,18 @@ public class GeckoView extends FrameLayo
     protected final Display mDisplay = new Display();
     protected GeckoSession mSession;
     private boolean mStateSaved;
 
     protected SurfaceView mSurfaceView;
 
     private boolean mIsResettingFocus;
 
+    private GeckoSession.SelectionActionDelegate mSelectionActionDelegate;
+
     private static class SavedState extends BaseSavedState {
         public final GeckoSession session;
 
         public SavedState(final Parcelable superState, final GeckoSession session) {
             super(superState);
             this.session = session;
         }
 
@@ -172,16 +176,21 @@ public class GeckoView extends FrameLayo
 
         mSurfaceView = new SurfaceView(getContext());
         mSurfaceView.setBackgroundColor(Color.WHITE);
         addView(mSurfaceView,
                 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                            ViewGroup.LayoutParams.MATCH_PARENT));
 
         mSurfaceView.getHolder().addCallback(mDisplay);
+
+        final Activity activity = ActivityUtils.getActivityFromContext(getContext());
+        if (activity != null) {
+            mSelectionActionDelegate = new BasicSelectionActionDelegate(activity);
+        }
     }
 
     /**
      * Set a color to cover the display surface while a document is being shown. The color
      * is automatically cleared once the new document starts painting. Set to
      * Color.TRANSPARENT to undo the cover.
      *
      * @param color Cover color.
@@ -196,32 +205,36 @@ public class GeckoView extends FrameLayo
         if (mSession == null) {
             return null;
         }
 
         GeckoSession session = mSession;
         mSession.releaseDisplay(mDisplay.release());
         mSession.getOverscrollEdgeEffect().setInvalidationCallback(null);
         mSession.getCompositorController().setFirstPaintCallback(null);
+        if (session.getSelectionActionDelegate() == mSelectionActionDelegate) {
+            mSession.setSelectionActionDelegate(null);
+        }
         mSession = null;
         return session;
     }
 
     public void setSession(final GeckoSession session) {
         if (mSession != null && mSession.isOpen()) {
             throw new IllegalStateException("Current session is open");
         }
 
         releaseSession();
         mSession = session;
         if (mSession == null) {
           return;
         }
 
         mDisplay.acquire(session.acquireDisplay());
+
         final Context context = getContext();
         session.getOverscrollEdgeEffect().setTheme(context);
         session.getOverscrollEdgeEffect().setInvalidationCallback(new Runnable() {
             @Override
             public void run() {
                 if (Build.VERSION.SDK_INT >= 16) {
                     GeckoView.this.postInvalidateOnAnimation();
                 } else {
@@ -240,16 +253,20 @@ public class GeckoView extends FrameLayo
         }
 
         session.getCompositorController().setFirstPaintCallback(new Runnable() {
             @Override
             public void run() {
                 coverUntilFirstPaint(Color.TRANSPARENT);
             }
         });
+
+        if (session.getSelectionActionDelegate() == null && mSelectionActionDelegate != null) {
+            session.setSelectionActionDelegate(mSelectionActionDelegate);
+        }
     }
 
     public GeckoSession getSession() {
         return mSession;
     }
 
     public EventDispatcher getEventDispatcher() {
         return mSession.getEventDispatcher();
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -84,175 +84,17 @@
 
 [browser]
 ; [Base Browser Files]
 @BINPATH@/application.ini
 @BINPATH@/platform.ini
 @BINPATH@/blocklist.xml
 
 ; [Components]
-#ifdef MOZ_ARTIFACT_BUILDS
-@BINPATH@/components/interfaces.xpt
-@BINPATH@/components/prebuilt-interfaces.manifest
-#endif
 @BINPATH@/components/components.manifest
-@BINPATH@/components/alerts.xpt
-#ifdef ACCESSIBILITY
-@BINPATH@/components/accessibility.xpt
-#endif
-@BINPATH@/components/appshell.xpt
-@BINPATH@/components/appstartup.xpt
-@BINPATH@/components/autocomplete.xpt
-@BINPATH@/components/autoconfig.xpt
-@BINPATH@/components/browser-element.xpt
-@BINPATH@/components/caps.xpt
-@BINPATH@/components/chrome.xpt
-@BINPATH@/components/commandhandler.xpt
-@BINPATH@/components/commandlines.xpt
-@BINPATH@/components/composer.xpt
-@BINPATH@/components/content_events.xpt
-@BINPATH@/components/content_geckomediaplugins.xpt
-@BINPATH@/components/content_html.xpt
-@BINPATH@/components/content_webrtc.xpt
-@BINPATH@/components/content_xslt.xpt
-@BINPATH@/components/directory.xpt
-@BINPATH@/components/docshell.xpt
-@BINPATH@/components/dom.xpt
-@BINPATH@/components/dom_base.xpt
-@BINPATH@/components/dom_bindings.xpt
-#ifdef MOZ_DEBUG
-@BINPATH@/components/dom_bindings_test.xpt
-#endif
-@BINPATH@/components/dom_core.xpt
-@BINPATH@/components/dom_events.xpt
-@BINPATH@/components/dom_file.xpt
-@BINPATH@/components/dom_geolocation.xpt
-@BINPATH@/components/dom_media.xpt
-@BINPATH@/components/dom_network.xpt
-@BINPATH@/components/dom_notification.xpt
-@BINPATH@/components/dom_html.xpt
-@BINPATH@/components/dom_offline.xpt
-@BINPATH@/components/dom_payments.xpt
-@BINPATH@/components/dom_power.xpt
-#ifdef MOZ_ANDROID_GCM
-@BINPATH@/components/dom_push.xpt
-#endif
-@BINPATH@/components/dom_quota.xpt
-@BINPATH@/components/dom_range.xpt
-@BINPATH@/components/dom_security.xpt
-@BINPATH@/components/dom_sidebar.xpt
-@BINPATH@/components/dom_storage.xpt
-@BINPATH@/components/dom_system.xpt
-#ifdef MOZ_WEBSPEECH
-@BINPATH@/components/dom_webspeechrecognition.xpt
-#endif
-@BINPATH@/components/dom_workers.xpt
-@BINPATH@/components/dom_xhr.xpt
-@BINPATH@/components/dom_xul.xpt
-@BINPATH@/components/dom_presentation.xpt
-@BINPATH@/components/downloads.xpt
-@BINPATH@/components/editor.xpt
-@BINPATH@/components/extensions.xpt
-@BINPATH@/components/exthandler.xpt
-@BINPATH@/components/fastfind.xpt
-@BINPATH@/components/feeds.xpt
-@BINPATH@/components/gfx.xpt
-@BINPATH@/components/html5.xpt
-@BINPATH@/components/htmlparser.xpt
-@BINPATH@/components/imglib2.xpt
-@BINPATH@/components/inspector.xpt
-@BINPATH@/components/intl.xpt
-@BINPATH@/components/jar.xpt
-@BINPATH@/components/jsdebugger.xpt
-@BINPATH@/components/jsinspector.xpt
-@BINPATH@/components/layout_base.xpt
-#ifdef NS_PRINTING
-@BINPATH@/components/layout_printing.xpt
-#endif
-@BINPATH@/components/layout_xul_tree.xpt
-@BINPATH@/components/layout_xul.xpt
-@BINPATH@/components/locale.xpt
-@BINPATH@/components/mimetype.xpt
-@BINPATH@/components/mozfind.xpt
-@BINPATH@/components/mozintl.xpt
-@BINPATH@/components/necko_about.xpt
-@BINPATH@/components/necko_cache.xpt
-@BINPATH@/components/necko_cache2.xpt
-@BINPATH@/components/necko_cookie.xpt
-@BINPATH@/components/necko_dns.xpt
-@BINPATH@/components/necko_file.xpt
-@BINPATH@/components/necko_ftp.xpt
-@BINPATH@/components/necko_http.xpt
-@BINPATH@/components/necko_mdns.xpt
-@BINPATH@/components/necko_res.xpt
-@BINPATH@/components/necko_socket.xpt
-@BINPATH@/components/necko_strconv.xpt
-@BINPATH@/components/necko_viewsource.xpt
-@BINPATH@/components/necko_websocket.xpt
-#ifdef NECKO_WIFI
-@BINPATH@/components/necko_wifi.xpt
-#endif
-@BINPATH@/components/necko_wyciwyg.xpt
-@BINPATH@/components/necko.xpt
-@BINPATH@/components/loginmgr.xpt
-@BINPATH@/components/parentalcontrols.xpt
-#ifdef MOZ_WEBRTC
-@BINPATH@/components/peerconnection.xpt
-#endif
-@BINPATH@/components/plugin.xpt
-@BINPATH@/components/pref.xpt
-@BINPATH@/components/prefetch.xpt
-#ifdef MOZ_GECKO_PROFILER
-@BINPATH@/components/profiler.xpt
-#endif
-@BINPATH@/components/rdf.xpt
-@BINPATH@/components/reputationservice.xpt
-@BINPATH@/components/satchel.xpt
-@BINPATH@/components/saxparser.xpt
-@BINPATH@/components/services-crypto-component.xpt
-@BINPATH@/components/captivedetect.xpt
-@BINPATH@/components/shistory.xpt
-@BINPATH@/components/spellchecker.xpt
-@BINPATH@/components/storage.xpt
-@BINPATH@/components/telemetry.xpt
-@BINPATH@/components/toolkit_asyncshutdown.xpt
-@BINPATH@/components/toolkit_filewatcher.xpt
-@BINPATH@/components/toolkit_finalizationwitness.xpt
-@BINPATH@/components/toolkit_osfile.xpt
-@BINPATH@/components/toolkit_securityreporter.xpt
-@BINPATH@/components/toolkit_perfmonitoring.xpt
-@BINPATH@/components/toolkit_xulstore.xpt
-@BINPATH@/components/toolkitprofile.xpt
-#ifdef MOZ_ENABLE_XREMOTE
-@BINPATH@/components/toolkitremote.xpt
-#endif
-@BINPATH@/components/txtsvc.xpt
-@BINPATH@/components/txmgr.xpt
-@BINPATH@/components/uconv.xpt
-@BINPATH@/components/update.xpt
-@BINPATH@/components/uriloader.xpt
-@BINPATH@/components/urlformatter.xpt
-@BINPATH@/components/webBrowser_core.xpt
-@BINPATH@/components/webbrowserpersist.xpt
-@BINPATH@/components/webextensions.xpt
-@BINPATH@/components/widget.xpt
-@BINPATH@/components/widget_android.xpt
-@BINPATH@/components/windowcreator.xpt
-@BINPATH@/components/windowwatcher.xpt
-@BINPATH@/components/xpcom_base.xpt
-@BINPATH@/components/xpcom_system.xpt
-@BINPATH@/components/xpcom_components.xpt
-@BINPATH@/components/xpcom_ds.xpt
-@BINPATH@/components/xpcom_io.xpt
-@BINPATH@/components/xpcom_threads.xpt
-@BINPATH@/components/xpcom_xpti.xpt
-@BINPATH@/components/xpconnect.xpt
-@BINPATH@/components/xulapp.xpt
-@BINPATH@/components/xul.xpt
-@BINPATH@/components/zipwriter.xpt
 
 ; JavaScript components
 @BINPATH@/components/ConsoleAPI.manifest
 @BINPATH@/components/ConsoleAPIStorage.js
 @BINPATH@/components/NotificationStorage.js
 @BINPATH@/components/NotificationStorage.manifest
 #ifdef MOZ_ANDROID_GCM
 @BINPATH@/components/Push.js
@@ -299,17 +141,16 @@
 @BINPATH@/components/amContentHandler.js
 @BINPATH@/components/amWebAPI.js
 @BINPATH@/components/amInstallTrigger.js
 #ifndef RELEASE_OR_BETA
 @BINPATH@/components/TabSource.js
 #endif
 #endif
 
-@BINPATH@/components/webvtt.xpt
 @BINPATH@/components/WebVTT.manifest
 @BINPATH@/components/WebVTTParserWrapper.js
 
 #ifndef MOZ_GECKOVIEW_JAR
 #ifdef MOZ_UPDATER
 @BINPATH@/components/nsUpdateService.manifest
 @BINPATH@/components/nsUpdateService.js
 @BINPATH@/components/nsUpdateServiceStub.js
@@ -361,20 +202,16 @@
 #ifdef MOZ_WEBRTC
 @BINPATH@/components/PeerConnection.js
 @BINPATH@/components/PeerConnection.manifest
 #endif
 
 @BINPATH@/components/CaptivePortalDetectComponents.manifest
 @BINPATH@/components/captivedetect.js
 
-#ifdef MOZ_WEBSPEECH
-@BINPATH@/components/dom_webspeechsynth.xpt
-#endif
-
 #if defined(ENABLE_TESTS) && defined(MOZ_DEBUG)
 @BINPATH@/components/TestInterfaceJS.js
 @BINPATH@/components/TestInterfaceJS.manifest
 @BINPATH@/components/TestInterfaceJSMaplike.js
 #endif
 
 @BINPATH@/components/nsAsyncShutdown.manifest
 @BINPATH@/components/nsAsyncShutdown.js
@@ -399,20 +236,18 @@
 ; Modules
 @BINPATH@/modules/*
 
 ; Safe Browsing
 @BINPATH@/components/nsURLClassifier.manifest
 @BINPATH@/components/nsUrlClassifierHashCompleter.js
 @BINPATH@/components/nsUrlClassifierListManager.js
 @BINPATH@/components/nsUrlClassifierLib.js
-@BINPATH@/components/url-classifier.xpt
 
 ; Private Browsing
-@BINPATH@/components/privatebrowsing.xpt
 @BINPATH@/components/PrivateBrowsing.manifest
 @BINPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
 
 ; Security Reports
 @BINPATH@/components/SecurityReporter.manifest
 @BINPATH@/components/SecurityReporter.js
 
 ; [Browser Chrome Files]
@@ -477,35 +312,29 @@
 #endif
 
 ; Content-accessible resources.
 @BINPATH@/contentaccessible/*
 
 ; svg
 @BINPATH@/res/svg.css
 
-; [Personal Security Manager]
-;
-@BINPATH@/components/pipnss.xpt
-
 ; For process sandboxing
 #if defined(MOZ_SANDBOX)
 @BINPATH@/@DLL_PREFIX@mozsandbox@DLL_SUFFIX@
 #endif
 
 ; [Crash Reporter]
 ; CrashService is not used on Android but the ini files are required for L10N
 ; strings, see bug 1191351.
 #ifdef MOZ_CRASHREPORTER
 @BINPATH@/crashreporter.ini
 @BINPATH@/crashreporter-override.ini
 #endif
 
-@BINPATH@/components/dom_audiochannel.xpt
-
 @BINPATH@/components/RemoteWebNavigation.js
 @BINPATH@/components/remotebrowserutils.manifest
 
 [mobile]
 @BINPATH@/chrome/geckoview@JAREXT@
 @BINPATH@/chrome/geckoview.manifest
 
 #ifdef MOZ_GECKOVIEW_JAR
@@ -526,17 +355,16 @@
 @BINPATH@/components/ContentPermissionPrompt.js
 @BINPATH@/components/ImageBlockingPolicy.js
 @BINPATH@/components/DirectoryProvider.js
 @BINPATH@/components/FilePicker.js
 @BINPATH@/components/FxAccountsPush.js
 @BINPATH@/components/HelperAppDialog.js
 @BINPATH@/components/LoginManagerPrompter.js
 @BINPATH@/components/MobileComponents.manifest
-@BINPATH@/components/MobileComponents.xpt
 @BINPATH@/components/NSSDialogService.js
 @BINPATH@/components/PersistentNotificationHandler.js
 @BINPATH@/components/PresentationDevicePrompt.js
 @BINPATH@/components/PresentationRequestUIGlue.js
 @BINPATH@/components/PromptService.js
 @BINPATH@/components/SessionStore.js
 @BINPATH@/components/Snippets.js
 @BINPATH@/components/XPIDialogService.js
@@ -548,17 +376,14 @@
 @BINPATH@/components/marionette.manifest
 @BINPATH@/components/marionette.js
 #endif
 
 #ifdef PKG_LOCALE_MANIFEST
 #include @PKG_LOCALE_MANIFEST@
 #endif
 
-; Background Hang Monitor
-@BINPATH@/components/backgroundhangmonitor.xpt
-
 ; NOTE: This must match the config checks in
 ; /toolkit/components/backgroundhangmonitor/moz.build.
 #if defined(NIGHTLY_BUILD) && !defined(MOZ_DEBUG) && !defined(MOZ_TSAN)
 @BINPATH@/components/BHRTelemetryService.js
 @BINPATH@/components/BHRTelemetryService.manifest
 #endif
new file mode 100644
--- /dev/null
+++ b/mobile/android/modules/geckoview/GeckoViewSelectionAction.jsm
@@ -0,0 +1,38 @@
+/* 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";
+
+var EXPORTED_SYMBOLS = ["GeckoViewSelectionAction"];
+
+ChromeUtils.import("resource://gre/modules/GeckoViewModule.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "dump", () =>
+    ChromeUtils.import("resource://gre/modules/AndroidLog.jsm",
+                       {}).AndroidLog.d.bind(null, "ViewSelectionAction"));
+
+function debug(aMsg) {
+  // dump(aMsg);
+}
+
+// Handles inter-op between accessible carets and GeckoSession.
+class GeckoViewSelectionAction extends GeckoViewModule {
+  init() {
+  }
+
+  register() {
+    debug("register");
+    this.registerContent("chrome://geckoview/content/GeckoViewSelectionActionContent.js");
+  }
+
+  unregister() {
+    debug("unregister");
+  }
+
+  // Message manager event handler.
+  receiveMessage(aMsg) {
+    debug("receiveMessage " + aMsg.name);
+  }
+}
--- a/mobile/android/modules/geckoview/moz.build
+++ b/mobile/android/modules/geckoview/moz.build
@@ -8,15 +8,16 @@ EXTRA_JS_MODULES += [
     'AndroidLog.jsm',
     'GeckoViewContent.jsm',
     'GeckoViewContentModule.jsm',
     'GeckoViewModule.jsm',
     'GeckoViewNavigation.jsm',
     'GeckoViewProgress.jsm',
     'GeckoViewRemoteDebugger.jsm',
     'GeckoViewScroll.jsm',
+    'GeckoViewSelectionAction.jsm',
     'GeckoViewSettings.jsm',
     'GeckoViewTab.jsm',
     'GeckoViewTrackingProtection.jsm',
     'GeckoViewUtils.jsm',
     'LoadURIDelegate.jsm',
     'Messaging.jsm',
 ]
--- a/mobile/android/themes/geckoview/content.css
+++ b/mobile/android/themes/geckoview/content.css
@@ -111,18 +111,20 @@ select[size][multiple],
 textarea,
 * > input:not(:-moz-any([type="image"], [type="checkbox"], [type="radio"])) {
   border-style: solid;
   border-color: var(--form_border);
   color: var(--form_text);
   background-color: var(--form_background);
 }
 
-/* Selects are handled by the form helper, see bug 685197 */
-select option, select optgroup {
+/* These elements are handled by the prompt module. */
+select option, select optgroup,
+input[type="date"] div:-moz-native-anonymous.datetime-input-box-wrapper,
+input[type="time"] div:-moz-native-anonymous.datetime-input-box-wrapper {
   pointer-events: none;
 }
 
 select:not([size]):not([multiple]),
 select[size="0"],
 select[size="1"],
 * > input[type="button"],
 * > input[type="submit"],
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -73,16 +73,42 @@
 // - <default-value> is the same as for normal prefs.
 //
 // Note that Rust code must access the global variable directly, rather than via
 // the getter.
 
 // clang-format off
 
 //---------------------------------------------------------------------------
+// Full-screen prefs
+//---------------------------------------------------------------------------
+
+#ifdef RELEASE_OR_BETA
+# define PREF_VALUE false
+#else
+# define PREF_VALUE true
+#endif
+VARCACHE_PREF(
+  "full-screen-api.unprefix.enabled",
+   full_screen_api_unprefix_enabled,
+  bool, PREF_VALUE
+)
+#undef PREF_VALUE
+
+//---------------------------------------------------------------------------
+// Graphics prefs
+//---------------------------------------------------------------------------
+
+VARCACHE_PREF(
+  "gfx.font_rendering.opentype_svg.enabled",
+   gfx_font_rendering_opentype_svg_enabled,
+  bool, true
+)
+
+//---------------------------------------------------------------------------
 // HTML5 parser prefs
 //---------------------------------------------------------------------------
 
 // Toggle which thread the HTML5 parser uses for stream parsing.
 VARCACHE_PREF(
   "html5.offmainthread",
    html5_offmainthread,
   bool, true
@@ -101,16 +127,125 @@ VARCACHE_PREF(
 // firing when the timer has already fired previously in this parse.
 VARCACHE_PREF(
   "html5.flushtimer.subsequentdelay",
    html5_flushtimer_subsequentdelay,
   int32_t, 120
 )
 
 //---------------------------------------------------------------------------
+// Layout prefs
+//---------------------------------------------------------------------------
+
+// Is support for the font-display @font-face descriptor enabled?
+VARCACHE_PREF(
+  "layout.css.font-display.enabled",
+   layout_css_font_display_enabled,
+  bool, true
+)
+
+// Are webkit-prefixed properties & property-values supported?
+VARCACHE_PREF(
+  "layout.css.prefixes.webkit",
+   layout_css_prefixes_webkit,
+  bool, true
+)
+
+// Are "-webkit-{min|max}-device-pixel-ratio" media queries supported? (Note:
+// this pref has no effect if the master 'layout.css.prefixes.webkit' pref is
+// set to false.)
+VARCACHE_PREF(
+  "layout.css.prefixes.device-pixel-ratio-webkit",
+   layout_css_prefixes_device_pixel_ratio_webkit,
+  bool, false
+)
+
+// Is -moz-prefixed gradient functions enabled?
+VARCACHE_PREF(
+  "layout.css.prefixes.gradients",
+   layout_css_prefixes_gradients,
+  bool, true
+)
+
+// Should stray control characters be rendered visibly?
+#ifdef RELEASE_OR_BETA
+# define PREF_VALUE false
+#else
+# define PREF_VALUE true
+#endif
+VARCACHE_PREF(
+  "layout.css.control-characters.visible",
+   layout_css_control_characters_visible,
+  bool, PREF_VALUE
+)
+#undef PREF_VALUE
+
+// Is support for the frames() timing function enabled?
+#ifdef RELEASE_OR_BETA
+# define PREF_VALUE false
+#else
+# define PREF_VALUE true
+#endif
+VARCACHE_PREF(
+  "layout.css.frames-timing.enabled",
+   layout_css_frames_timing_enabled,
+  bool, PREF_VALUE
+)
+#undef PREF_VALUE
+
+// Should the :visited selector ever match (otherwise :link matches instead)?
+VARCACHE_PREF(
+  "layout.css.visited_links_enabled",
+   layout_css_visited_links_enabled,
+  bool, true
+)
+
+// Pref to control whether @-moz-document rules are enabled in content pages.
+VARCACHE_PREF(
+  "layout.css.moz-document.content.enabled",
+   layout_css_moz_document_content_enabled,
+  bool, false
+)
+
+// Pref to control whether @-moz-document url-prefix() is parsed in content
+// pages. Only effective when layout.css.moz-document.content.enabled is false.
+#ifdef EARLY_BETA_OR_EARLIER
+#define PREF_VALUE false
+#else
+#define PREF_VALUE true
+#endif
+VARCACHE_PREF(
+  "layout.css.moz-document.url-prefix-hack.enabled",
+   layout_css_moz_document_url_prefix_hack_enabled,
+  bool, PREF_VALUE
+)
+#undef PREF_VALUE
+
+// Is support for CSS "grid-template-{columns,rows}: subgrid X" enabled?
+VARCACHE_PREF(
+  "layout.css.grid-template-subgrid-value.enabled",
+   layout_css_grid_template_subgrid_value_enabled,
+  bool, false
+)
+
+// Is support for variation fonts enabled?
+VARCACHE_PREF(
+  "layout.css.font-variations.enabled",
+   layout_css_font_variations_enabled,
+  bool, true
+)
+
+// Are we emulating -moz-{inline}-box layout using CSS flexbox?
+VARCACHE_PREF(
+  "layout.css.emulate-moz-box-with-flex",
+   layout_css_emulate_moz_box_with_flex,
+  bool, false
+)
+
+//---------------------------------------------------------------------------
 // Network prefs
 //---------------------------------------------------------------------------
 
 // Sub-resources HTTP-authentication:
 //   0 - don't allow sub-resources to open HTTP authentication credentials
 //       dialogs
 //   1 - allow sub-resources to open HTTP authentication credentials dialogs,
 //       but don't allow it for cross-origin sub-resources
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -869,18 +869,16 @@ pref("gfx.font_rendering.wordcache.charl
 pref("gfx.font_rendering.wordcache.maxentries", 10000);
 
 pref("gfx.font_rendering.graphite.enabled", true);
 
 #ifdef XP_WIN
 pref("gfx.font_rendering.directwrite.use_gdi_table_loading", true);
 #endif
 
-pref("gfx.font_rendering.opentype_svg.enabled", true);
-
 #ifdef XP_WIN
 // comma separated list of backends to use in order of preference
 // e.g., pref("gfx.canvas.azure.backends", "direct2d,skia,cairo");
 pref("gfx.canvas.azure.backends", "direct2d1.1,skia,cairo");
 pref("gfx.content.azure.backends", "direct2d1.1,skia,cairo");
 #else
 #ifdef XP_MACOSX
 pref("gfx.content.azure.backends", "skia");
@@ -2924,30 +2922,16 @@ pref("layout.word_select.stop_at_undersc
 // 3 = caret moves and blinks as when there is no selection; word delete
 //     deletes the selection
 // Windows default is 1 for word delete behavior, the rest as for 2.
 pref("layout.selection.caret_style", 0);
 
 // pref to report CSS errors to the error console
 pref("layout.css.report_errors", true);
 
-// Should the :visited selector ever match (otherwise :link matches instead)?
-pref("layout.css.visited_links_enabled", true);
-
-// Pref to control whether @-moz-document rules are enabled in content pages.
-pref("layout.css.moz-document.content.enabled",  false);
-
-// Pref to control whether @-moz-document url-prefix() is parsed in content
-// pages. Only effective when layout.css.moz-document.content.enabled is false.
-#ifdef EARLY_BETA_OR_EARLIER
-pref("layout.css.moz-document.url-prefix-hack.enabled", false);
-#else
-pref("layout.css.moz-document.url-prefix-hack.enabled", true);
-#endif
-
 #ifdef NIGHTLY_BUILD
 pref("layout.css.getPropertyCSSValue.enabled", false);
 #else
 pref("layout.css.getPropertyCSSValue.enabled", true);
 #endif
 
 // Override DPI. A value of -1 means use the maximum of 96 and the system DPI.
 // A value of 0 means use the system DPI. A positive value is used as the DPI.
@@ -3021,59 +3005,28 @@ pref("layout.css.text-justify.enabled", 
 
 // Is support for CSS "float: inline-{start,end}" and
 // "clear: inline-{start,end}" enabled?
 pref("layout.css.float-logical-values.enabled", true);
 
 // Is support for the CSS4 image-orientation property enabled?
 pref("layout.css.image-orientation.enabled", true);
 
-// Is support for the font-display @font-face descriptor enabled?
-pref("layout.css.font-display.enabled", true);
-
-// Is support for variation fonts enabled?
-pref("layout.css.font-variations.enabled", true);
-
-// Is support for the frames() timing function enabled?
-#ifdef RELEASE_OR_BETA
-pref("layout.css.frames-timing.enabled", false);
-#else
-pref("layout.css.frames-timing.enabled", true);
-#endif
-
-// Are we emulating -moz-{inline}-box layout using CSS flexbox?
-// (This pref only takes effect in prerelease builds, so we only
-// bother specifying a default in prerelease builds as well.)
-#ifndef RELEASE_OR_BETA
-pref("layout.css.emulate-moz-box-with-flex", false);
-#endif
-
 // Is the paint-order property supported for HTML text?
 // (It is always supported for SVG.)
 pref("layout.css.paint-order.enabled", true);
 
 // Are sets of prefixed properties supported?
 pref("layout.css.prefixes.border-image", true);
 pref("layout.css.prefixes.transforms", true);
 pref("layout.css.prefixes.transitions", true);
 pref("layout.css.prefixes.animations", true);
 pref("layout.css.prefixes.box-sizing", true);
 pref("layout.css.prefixes.font-features", true);
 
-// Is -moz-prefixed gradient functions enabled?
-pref("layout.css.prefixes.gradients", true);
-
-// Are webkit-prefixed properties & property-values supported?
-pref("layout.css.prefixes.webkit", true);
-
-// Are "-webkit-{min|max}-device-pixel-ratio" media queries supported?
-// (Note: this pref has no effect if the master 'layout.css.prefixes.webkit'
-// pref is set to false.)
-pref("layout.css.prefixes.device-pixel-ratio-webkit", false);
-
 // Is support for background-blend-mode enabled?
 pref("layout.css.background-blend-mode.enabled", true);
 
 // Is support for CSS text-combine-upright (tate-chu-yoko) enabled?
 pref("layout.css.text-combine-upright.enabled", true);
 // Is support for CSS text-combine-upright: digits 2-4 enabled?
 pref("layout.css.text-combine-upright-digits.enabled", false);
 
@@ -3089,19 +3042,16 @@ pref("layout.css.osx-font-smoothing.enab
 pref("layout.css.unset-value.enabled", true);
 
 // Is support for the "all" shorthand enabled?
 pref("layout.css.all-shorthand.enabled", true);
 
 // Is support for CSS overflow-clip-box enabled for non-UA sheets?
 pref("layout.css.overflow-clip-box.enabled", false);
 
-// Is support for CSS "grid-template-{columns,rows}: subgrid X" enabled?
-pref("layout.css.grid-template-subgrid-value.enabled", false);
-
 // Is support for CSS contain enabled?
 pref("layout.css.contain.enabled", false);
 
 // Is support for CSS box-decoration-break enabled?
 pref("layout.css.box-decoration-break.enabled", true);
 
 // Is layout of CSS outline-style:auto enabled?
 pref("layout.css.outline-style-auto.enabled", false);
@@ -3132,23 +3082,16 @@ pref("layout.css.scroll-behavior.damping
 pref("layout.css.scroll-snap.enabled", true);
 
 // Is support for CSS shape-outside enabled?
 pref("layout.css.shape-outside.enabled", false);
 
 // Is support for document.fonts enabled?
 pref("layout.css.font-loading-api.enabled", true);
 
-// Should stray control characters be rendered visibly?
-#ifdef RELEASE_OR_BETA
-pref("layout.css.control-characters.visible", false);
-#else
-pref("layout.css.control-characters.visible", true);
-#endif
-
 // Is support for column-span enabled?
 pref("layout.css.column-span.enabled", false);
 
 // Are inter-character ruby annotations enabled?
 pref("layout.css.ruby.intercharacter.enabled", false);
 
 // Is support for overscroll-behavior enabled?
 pref("layout.css.overscroll-behavior.enabled", true);
@@ -5123,21 +5066,16 @@ pref("alerts.showFavicons", false);
 
 // Whether to use platform-specific backends for showing desktop notifications.
 // If no such backend is available, or if the pref is false, then XUL
 // notifications are used.
 pref("alerts.useSystemBackend", true);
 
 // DOM full-screen API.
 pref("full-screen-api.enabled", false);
-#ifdef RELEASE_OR_BETA
-pref("full-screen-api.unprefix.enabled", false);
-#else
-pref("full-screen-api.unprefix.enabled", true);
-#endif
 pref("full-screen-api.allow-trusted-requests-only", true);
 // whether to prevent the top level widget from going fullscreen
 pref("full-screen-api.ignore-widgets", false);
 pref("full-screen-api.pointer-lock.enabled", true);
 // transition duration of fade-to-black and fade-from-black, unit: ms
 #ifndef MOZ_WIDGET_GTK
 pref("full-screen-api.transition-duration.enter", "200 200");
 pref("full-screen-api.transition-duration.leave", "200 200");
@@ -5257,21 +5195,16 @@ pref("media.ondevicechange.fakeDeviceCha
 
 // W3C touch-action css property (related to touch and pointer events)
 // Note that we turn this on even on platforms/configurations where touch
 // events are not supported (e.g. OS X, or Windows with e10s disabled). For
 // those platforms we don't handle touch events anyway so it's conceptually
 // a no-op.
 pref("layout.css.touch_action.enabled", true);
 
-// Enables some assertions in ComputedStyle that are too expensive
-// for general use, but might be useful to enable for specific tests.
-// This only has an effect in DEBUG-builds.
-pref("layout.css.expensive-style-struct-assertions.enabled", false);
-
 #if defined(MOZ_WIDGET_ANDROID)
 // Network Information API
 pref("dom.netinfo.enabled", true);
 #else
 pref("dom.netinfo.enabled", false);
 #endif
 
 // How long must we wait before declaring that a window is a "ghost" (i.e., a
--- a/netwerk/test/httpserver/moz.build
+++ b/netwerk/test/httpserver/moz.build
@@ -5,19 +5,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
     'nsIHttpServer.idl',
 ]
 
 XPIDL_MODULE = 'test_necko'
 
-# Don't add our test-only .xpt files to the normal manifests
-XPIDL_NO_MANIFEST = True
-
 XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell.ini']
 
 EXTRA_COMPONENTS += [
     'httpd.js',
     'httpd.manifest',
 ]
 
 TESTING_JS_MODULES += [
--- a/python/mozbuild/mozbuild/artifacts.py
+++ b/python/mozbuild/mozbuild/artifacts.py
@@ -240,17 +240,16 @@ class ArtifactJob(object):
 class AndroidArtifactJob(ArtifactJob):
 
     product = 'mobile'
 
     package_artifact_patterns = {
         'application.ini',
         'platform.ini',
         '**/*.so',
-        '**/interfaces.xpt',
     }
 
     def process_package_artifact(self, filename, processed_filename):
         # Extract all .so files into the root, which will get copied into dist/bin.
         with JarWriter(file=processed_filename, optimize=False, compress_level=5) as writer:
             for p, f in UnpackFinder(JarFinder(filename, JarReader(filename))):
                 if not any(mozpath.match(p, pat) for pat in self.package_artifact_patterns):
                     continue
@@ -278,17 +277,16 @@ class LinuxArtifactJob(ArtifactJob):
         'firefox/firefox',
         'firefox/firefox-bin',
         'firefox/minidump-analyzer',
         'firefox/pingsender',
         'firefox/platform.ini',
         'firefox/plugin-container',
         'firefox/updater',
         'firefox/**/*.so',
-        'firefox/**/interfaces.xpt',
     }
 
     def process_package_artifact(self, filename, processed_filename):
         added_entry = False
 
         with JarWriter(file=processed_filename, optimize=False, compress_level=5) as writer:
             with tarfile.open(filename) as reader:
                 for p, f in UnpackFinder(TarFinder(filename, reader)):
@@ -377,17 +375,16 @@ class MacArtifactJob(ArtifactJob):
                 ]),
                 ('Contents/Resources', [
                     'browser/components/libbrowsercomps.dylib',
                     'dependentlibs.list',
                     # 'firefox',
                     'gmp-clearkey/0.1/libclearkey.dylib',
                     # 'gmp-fake/1.0/libfake.dylib',
                     # 'gmp-fakeopenh264/1.0/libfakeopenh264.dylib',
-                    '**/interfaces.xpt',
                 ]),
             ]
 
             with JarWriter(file=processed_filename, optimize=False, compress_level=5) as writer:
                 root, paths = paths_no_keep_path
                 finder = UnpackFinder(mozpath.join(source, root))
                 for path in paths:
                     for p, f in finder.find(path):
@@ -420,17 +417,16 @@ class MacArtifactJob(ArtifactJob):
 
 class WinArtifactJob(ArtifactJob):
     package_artifact_patterns = {
         'firefox/dependentlibs.list',
         'firefox/platform.ini',
         'firefox/application.ini',
         'firefox/**/*.dll',
         'firefox/*.exe',
-        'firefox/**/interfaces.xpt',
         'firefox/*.tlb',
     }
 
     product = 'firefox'
 
     # These are a subset of TEST_HARNESS_BINS in testing/mochitest/Makefile.in.
     test_artifact_patterns = {
         ('bin/BadCertServer.exe', ('bin', 'bin')),
--- a/python/mozbuild/mozbuild/backend/common.py
+++ b/python/mozbuild/mozbuild/backend/common.py
@@ -53,50 +53,40 @@ class XPIDLManager(object):
     """Helps manage XPCOM IDLs in the context of the build system."""
     def __init__(self, config):
         self.config = config
         self.topsrcdir = config.topsrcdir
         self.topobjdir = config.topobjdir
 
         self.idls = {}
         self.modules = {}
-        self.interface_manifests = {}
-        self.chrome_manifests = set()
 
     def register_idl(self, idl, allow_existing=False):
         """Registers an IDL file with this instance.
 
         The IDL file will be built, installed, etc.
         """
         basename = mozpath.basename(idl.source_path)
         root = mozpath.splitext(basename)[0]
         xpt = '%s.xpt' % idl.module
-        manifest = mozpath.join(idl.install_target, 'components', 'interfaces.manifest')
-        chrome_manifest = mozpath.join(idl.install_target, 'chrome.manifest')
 
         entry = {
             'source': idl.source_path,
             'module': idl.module,
             'basename': basename,
             'root': root,
-            'manifest': manifest,
         }
 
         if not allow_existing and entry['basename'] in self.idls:
             raise Exception('IDL already registered: %s' % entry['basename'])
 
         self.idls[entry['basename']] = entry
         t = self.modules.setdefault(entry['module'], (idl.install_target, set()))
         t[1].add(entry['root'])
 
-        if idl.add_to_manifest:
-            self.interface_manifests.setdefault(manifest, set()).add(xpt)
-            self.chrome_manifests.add(chrome_manifest)
-
-
 class BinariesCollection(object):
     """Tracks state of binaries produced by the build."""
 
     def __init__(self):
         self.shared_libraries = []
         self.programs = []
 
 class CommonBackend(BuildBackend):
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -1058,18 +1058,16 @@ class RecursiveMakeBackend(CommonBackend
                 % idl['root'])
 
         for module in manager.modules:
             build_files.add_optional_exists(mozpath.join('.deps',
                 '%s.pp' % module))
 
         modules = manager.modules
         xpt_modules = sorted(modules.keys())
-        xpt_files = set()
-        registered_xpt_files = set()
 
         mk = Makefile()
 
         for module in xpt_modules:
             install_target, sources = modules[module]
             deps = sorted(sources)
 
             # It may seem strange to have the .idl files listed as
@@ -1077,67 +1075,39 @@ class RecursiveMakeBackend(CommonBackend
             # It is necessary to list them here to handle the case where a
             # new .idl is added to an xpt. If we add a new .idl and nothing
             # else has changed, the new .idl won't be referenced anywhere
             # except in the command invocation. Therefore, the .xpt won't
             # be rebuilt because the dependencies say it is up to date. By
             # listing the .idls here, we ensure the make file has a
             # reference to the new .idl. Since the new .idl presumably has
             # an mtime newer than the .xpt, it will trigger xpt generation.
-            xpt_path = '$(DEPTH)/%s/components/%s.xpt' % (install_target, module)
-            xpt_files.add(xpt_path)
             mk.add_statement('%s_deps = %s' % (module, ' '.join(deps)))
 
-            if install_target.startswith('dist/'):
-                path = mozpath.relpath(xpt_path, '$(DEPTH)/dist')
-                prefix, subpath = path.split('/', 1)
-                key = 'dist_%s' % prefix
-
-                self._install_manifests[key].add_optional_exists(subpath)
+            build_files.add_optional_exists('%s.xpt' % module)
 
         rules = StringIO()
         mk.dump(rules, removal_guard=False)
 
-        interfaces_manifests = []
-        dist_dir = mozpath.join(self.environment.topobjdir, 'dist')
-        for manifest, entries in manager.interface_manifests.items():
-            interfaces_manifests.append(mozpath.join('$(DEPTH)', manifest))
-            for xpt in sorted(entries):
-                registered_xpt_files.add(mozpath.join(
-                    '$(DEPTH)', mozpath.dirname(manifest), xpt))
-
-            if install_target.startswith('dist/'):
-                path = mozpath.join(self.environment.topobjdir, manifest)
-                path = mozpath.relpath(path, dist_dir)
-                prefix, subpath = path.split('/', 1)
-                key = 'dist_%s' % prefix
-                self._install_manifests[key].add_optional_exists(subpath)
-
-        chrome_manifests = [mozpath.join('$(DEPTH)', m) for m in sorted(manager.chrome_manifests)]
-
         # Create dependency for output header so we force regeneration if the
         # header was deleted. This ideally should not be necessary. However,
         # some processes (such as PGO at the time this was implemented) wipe
         # out dist/include without regard to our install manifests.
 
         obj = self.Substitution()
         obj.output_path = mozpath.join(self.environment.topobjdir, 'config',
             'makefiles', 'xpidl', 'Makefile')
         obj.input_path = mozpath.join(self.environment.topsrcdir, 'config',
             'makefiles', 'xpidl', 'Makefile.in')
         obj.topsrcdir = self.environment.topsrcdir
         obj.topobjdir = self.environment.topobjdir
         obj.config = self.environment
         self._create_makefile(obj, extra=dict(
-            chrome_manifests = ' '.join(chrome_manifests),
-            interfaces_manifests = ' '.join(interfaces_manifests),
             xpidl_rules=rules.getvalue(),
             xpidl_modules=' '.join(xpt_modules),
-            xpt_files=' '.join(sorted(xpt_files - registered_xpt_files)),
-            registered_xpt_files=' '.join(sorted(registered_xpt_files)),
         ))
 
     def _process_program(self, obj, backend_file):
         backend_file.write('PROGRAM = %s\n' % self._pretty_path(obj.output_path, backend_file))
         if not obj.cxx_link and not self.environment.bin_suffix:
             backend_file.write('PROG_IS_C_ONLY_%s := 1\n' % obj.program)
 
     def _process_host_program(self, program, backend_file):
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -1774,24 +1774,16 @@ VARIABLES = {
     'XPIDL_MODULE': (unicode, unicode,
         """XPCOM Interface Definition Module Name.
 
         This is the name of the ``.xpt`` file that is created by linking
         ``XPIDL_SOURCES`` together. If unspecified, it defaults to be the same
         as ``MODULE``.
         """),
 
-    'XPIDL_NO_MANIFEST': (bool, bool,
-        """Indicate that the XPIDL module should not be added to a manifest.
-
-        This flag exists primarily to prevent test-only XPIDL modules from being
-        added to the application's chrome manifest. Most XPIDL modules should
-        not use this flag.
-        """),
-
     'PREPROCESSED_IPDL_SOURCES': (StrictOrderingOnAppendList, list,
         """Preprocessed IPDL source files.
 
         These files will be preprocessed, then parsed and converted to
         ``.cpp`` files.
         """),
 
     'IPDL_SOURCES': (StrictOrderingOnAppendList, list,
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -191,26 +191,24 @@ class ComputedFlags(ContextDerived):
 
 class XPIDLFile(ContextDerived):
     """Describes an XPIDL file to be compiled."""
 
     __slots__ = (
         'source_path',
         'basename',
         'module',
-        'add_to_manifest',
     )
 
-    def __init__(self, context, source, module, add_to_manifest):
+    def __init__(self, context, source, module):
         ContextDerived.__init__(self, context)
 
         self.source_path = source
         self.basename = mozpath.basename(source)
         self.module = module
-        self.add_to_manifest = add_to_manifest
 
 class BaseDefines(ContextDerived):
     """Context derived container object for DEFINES/HOST_DEFINES,
     which are OrderedDicts.
     """
     __slots__ = ('defines')
 
     def __init__(self, context, defines):
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -1339,17 +1339,17 @@ class TreeMetadataEmitter(LoggingMixin):
 
         if context['XPIDL_SOURCES'] and context['DIST_INSTALL'] is False:
             self.log(logging.WARN, 'mozbuild_warning', dict(
                 path=context.main_path),
                 '{path}: DIST_INSTALL = False has no effect on XPIDL_SOURCES.')
 
         for idl in context['XPIDL_SOURCES']:
             yield XPIDLFile(context, mozpath.join(context.srcdir, idl),
-                xpidl_module, add_to_manifest=not context['XPIDL_NO_MANIFEST'])
+                xpidl_module)
 
     def _process_generated_files(self, context):
         for path in context['CONFIGURE_DEFINE_FILES']:
             script = mozpath.join(mozpath.dirname(mozpath.dirname(__file__)),
                                   'action', 'process_define_files.py')
             yield GeneratedFile(context, script, 'process_define_file',
                                 unicode(path),
                                 [Path(context, path + '.in')])
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -682,19 +682,18 @@ class TestRecursiveMakeBackend(BackendTe
         m = InstallManifest(path=mozpath.join(install_dir, 'dist_idl'))
         self.assertEqual(len(m), 2)
         self.assertIn('bar.idl', m)
         self.assertIn('foo.idl', m)
 
         m = InstallManifest(path=mozpath.join(install_dir, 'xpidl'))
         self.assertIn('.deps/my_module.pp', m)
 
-        m = InstallManifest(path=os.path.join(install_dir, 'dist_bin'))
-        self.assertIn('components/my_module.xpt', m)
-        self.assertIn('components/interfaces.manifest', m)
+        m = InstallManifest(path=mozpath.join(install_dir, 'xpidl'))
+        self.assertIn('my_module.xpt', m)
 
         m = InstallManifest(path=mozpath.join(install_dir, 'dist_include'))
         self.assertIn('foo.h', m)
 
         p = mozpath.join(env.topobjdir, 'config/makefiles/xpidl')
         self.assertTrue(os.path.isdir(p))
 
         self.assertTrue(os.path.isfile(mozpath.join(p, 'Makefile')))
--- a/servo/components/canvas/canvas_paint_thread.rs
+++ b/servo/components/canvas/canvas_paint_thread.rs
@@ -59,16 +59,17 @@ pub struct CanvasPaintThread<'a> {
     state: CanvasPaintState<'a>,
     saved_states: Vec<CanvasPaintState<'a>>,
     webrender_api: webrender_api::RenderApi,
     image_key: Option<webrender_api::ImageKey>,
     /// An old webrender image key that can be deleted when the next epoch ends.
     old_image_key: Option<webrender_api::ImageKey>,
     /// An old webrender image key that can be deleted when the current epoch ends.
     very_old_image_key: Option<webrender_api::ImageKey>,
+    canvas_id: CanvasId,
 }
 
 #[derive(Clone)]
 struct CanvasPaintState<'a> {
     draw_options: DrawOptions,
     fill_style: Pattern,
     stroke_style: Pattern,
     stroke_opts: StrokeOptions<'a>,
@@ -94,50 +95,54 @@ impl<'a> CanvasPaintState<'a> {
             shadow_color: Color::transparent(),
         }
     }
 }
 
 impl<'a> CanvasPaintThread<'a> {
     fn new(size: Size2D<i32>,
            webrender_api_sender: webrender_api::RenderApiSender,
-           antialias: AntialiasMode) -> CanvasPaintThread<'a> {
+           antialias: AntialiasMode,
+           canvas_id: CanvasId) -> CanvasPaintThread<'a> {
         let draw_target = CanvasPaintThread::create(size);
         let path_builder = draw_target.create_path_builder();
         let webrender_api = webrender_api_sender.create_api();
         CanvasPaintThread {
             drawtarget: draw_target,
             path_builder: path_builder,
             state: CanvasPaintState::new(antialias),
             saved_states: vec![],
             webrender_api: webrender_api,
             image_key: None,
             old_image_key: None,
             very_old_image_key: None,
+            canvas_id: canvas_id,
         }
     }
 
     /// Creates a new `CanvasPaintThread` and returns an `IpcSender` to
     /// communicate with it.
     pub fn start(size: Size2D<i32>,
                  webrender_api_sender: webrender_api::RenderApiSender,
-                 antialias: bool)
+                 antialias: bool,
+                 canvas_id: CanvasId)
                  -> IpcSender<CanvasMsg> {
         let (sender, receiver) = ipc::channel::<CanvasMsg>().unwrap();
         let antialias = if antialias {
             AntialiasMode::Default
         } else {
             AntialiasMode::None
         };
         thread::Builder::new().name("CanvasThread".to_owned()).spawn(move || {
-            let mut painter = CanvasPaintThread::new(size, webrender_api_sender, antialias);
+            let mut painter = CanvasPaintThread::new(size, webrender_api_sender, antialias, canvas_id);
             loop {
                 let msg = receiver.recv();
                 match msg.unwrap() {
-                    CanvasMsg::Canvas2d(message) => {
+                    CanvasMsg::Canvas2d(message, canvas_id) => {
+                        assert!(canvas_id == painter.canvas_id);
                         match message {
                             Canvas2dMsg::FillText(text, x, y, max_width) => painter.fill_text(text, x, y, max_width),
                             Canvas2dMsg::FillRect(ref rect) => painter.fill_rect(rect),
                             Canvas2dMsg::StrokeRect(ref rect) => painter.stroke_rect(rect),
                             Canvas2dMsg::ClearRect(ref rect) => painter.clear_rect(rect),
                             Canvas2dMsg::BeginPath => painter.begin_path(),
                             Canvas2dMsg::ClosePath => painter.close_path(),
                             Canvas2dMsg::Fill => painter.fill(),
@@ -160,20 +165,20 @@ impl<'a> CanvasPaintThread<'a> {
                                     source_rect,
                                     smoothing_enabled,
                                 )
                             }
                             Canvas2dMsg::DrawImageSelf(image_size, dest_rect, source_rect, smoothing_enabled) => {
                                 painter.draw_image_self(image_size, dest_rect, source_rect, smoothing_enabled)
                             }
                             Canvas2dMsg::DrawImageInOther(
-                                renderer, image_size, dest_rect, source_rect, smoothing, sender
+                                renderer, other_canvas_id, image_size, dest_rect, source_rect, smoothing, sender
                             ) => {
                                 painter.draw_image_in_other(
-                                    renderer, image_size, dest_rect, source_rect, smoothing, sender)
+                                    renderer, other_canvas_id, image_size, dest_rect, source_rect, smoothing, sender)
                             }
                             Canvas2dMsg::MoveTo(ref point) => painter.move_to(point),
                             Canvas2dMsg::LineTo(ref point) => painter.line_to(point),
                             Canvas2dMsg::Rect(ref rect) => painter.rect(rect),
                             Canvas2dMsg::QuadraticCurveTo(ref cp, ref pt) => {
                                 painter.quadratic_curve_to(cp, pt)
                             }
                             Canvas2dMsg::BezierCurveTo(ref cp1, ref cp2, ref pt) => {
@@ -215,32 +220,40 @@ impl<'a> CanvasPaintThread<'a> {
                                 )
                             }
                             Canvas2dMsg::SetShadowOffsetX(value) => painter.set_shadow_offset_x(value),
                             Canvas2dMsg::SetShadowOffsetY(value) => painter.set_shadow_offset_y(value),
                             Canvas2dMsg::SetShadowBlur(value) => painter.set_shadow_blur(value),
                             Canvas2dMsg::SetShadowColor(ref color) => painter.set_shadow_color(color.to_azure_style()),
                         }
                     },
-                    CanvasMsg::Close => break,
-                    CanvasMsg::Recreate(size) => painter.recreate(size),
-                    CanvasMsg::FromScript(message) => {
+                    CanvasMsg::Close(canvas_id) =>{
+                        assert!(canvas_id == painter.canvas_id);
+                        break;
+                    },
+                    CanvasMsg::Recreate(size, canvas_id) =>{
+                        assert!(canvas_id == painter.canvas_id);
+                        painter.recreate(size);
+                    },
+                    CanvasMsg::FromScript(message, canvas_id) => {
+                        assert!(canvas_id == painter.canvas_id);
                         match message {
                             FromScriptMsg::SendPixels(chan) => {
                                 painter.send_pixels(chan)
                             }
                         }
-                    }
-                    CanvasMsg::FromLayout(message) => {
+                    },
+                    CanvasMsg::FromLayout(message, canvas_id) => {
+                        assert!(canvas_id == painter.canvas_id);
                         match message {
                             FromLayoutMsg::SendData(chan) => {
                                 painter.send_data(chan)
                             }
                         }
-                    }
+                    },
                 }
             }
         }).expect("Thread spawning failed");
 
         sender
     }
 
     fn save_context_state(&mut self) {
@@ -410,32 +423,35 @@ impl<'a> CanvasPaintThread<'a> {
             write_image(&self.drawtarget, image_data, image_size, dest_rect,
                         smoothing_enabled, self.state.draw_options.composition,
                         self.state.draw_options.alpha);
         }
     }
 
     fn draw_image_in_other(&self,
                            renderer: IpcSender<CanvasMsg>,
+                           other_canvas_id: CanvasId,
                            image_size: Size2D<f64>,
                            dest_rect: Rect<f64>,
                            source_rect: Rect<f64>,
                            smoothing_enabled: bool,
                            sender: IpcSender<()>) {
         let mut image_data = self.read_pixels(source_rect.to_i32(), image_size);
         // TODO: avoid double byte_swap.
         byte_swap(&mut image_data);
 
         let msg = CanvasMsg::Canvas2d(Canvas2dMsg::DrawImage(
             image_data.into(),
             source_rect.size,
             dest_rect,
             source_rect,
             smoothing_enabled,
-        ));
+            ),
+            other_canvas_id,
+        );
         renderer.send(msg).unwrap();
         // We acknowledge to the caller here that the data was sent to the
         // other canvas so that if JS immediately afterwards try to get the
         // pixels of the other one, it won't retrieve the other values.
         sender.send(()).unwrap();
     }
 
     fn move_to(&self, point: &Point2D<AzFloat>) {
--- a/servo/components/canvas_traits/canvas.rs
+++ b/servo/components/canvas_traits/canvas.rs
@@ -11,38 +11,41 @@ use std::str::FromStr;
 use webrender_api;
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum FillRule {
     Nonzero,
     Evenodd,
 }
 
+#[derive(Clone, Deserialize, MallocSizeOf, PartialEq, Serialize)]
+pub struct CanvasId(pub u64);
+
 #[derive(Clone, Deserialize, Serialize)]
 pub enum CanvasMsg {
-    Canvas2d(Canvas2dMsg),
-    FromLayout(FromLayoutMsg),
-    FromScript(FromScriptMsg),
-    Recreate(Size2D<i32>),
-    Close,
+    Canvas2d(Canvas2dMsg, CanvasId),
+    FromLayout(FromLayoutMsg, CanvasId),
+    FromScript(FromScriptMsg, CanvasId),
+    Recreate(Size2D<i32>, CanvasId),
+    Close(CanvasId),
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub struct CanvasImageData {
     pub image_key: webrender_api::ImageKey,
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub enum Canvas2dMsg {
     Arc(Point2D<f32>, f32, f32, f32, bool),
     ArcTo(Point2D<f32>, Point2D<f32>, f32),
     DrawImage(ByteBuf, Size2D<f64>, Rect<f64>, Rect<f64>, bool),
     DrawImageSelf(Size2D<f64>, Rect<f64>, Rect<f64>, bool),
     DrawImageInOther(
-        IpcSender<CanvasMsg>, Size2D<f64>, Rect<f64>, Rect<f64>, bool, IpcSender<()>),
+        IpcSender<CanvasMsg>, CanvasId, Size2D<f64>, Rect<f64>, Rect<f64>, bool, IpcSender<()>),
     BeginPath,
     BezierCurveTo(Point2D<f32>, Point2D<f32>, Point2D<f32>),
     ClearRect(Rect<f32>),
     Clip,
     ClosePath,
     Ellipse(Point2D<f32>, f32, f32, f32, f32, f32, bool),
     Fill,
     FillText(String, f64, f64, Option<f64>),
--- a/servo/components/constellation/constellation.rs
+++ b/servo/components/constellation/constellation.rs
@@ -90,16 +90,17 @@
 //! See https://github.com/servo/servo/issues/14704
 
 use backtrace::Backtrace;
 use bluetooth_traits::BluetoothRequest;
 use browsingcontext::{BrowsingContext, SessionHistoryChange, SessionHistoryEntry};
 use browsingcontext::{FullyActiveBrowsingContextsIterator, AllBrowsingContextsIterator};
 use canvas::canvas_paint_thread::CanvasPaintThread;
 use canvas::webgl_thread::WebGLThreads;
+use canvas_traits::canvas::CanvasId;
 use canvas_traits::canvas::CanvasMsg;
 use clipboard::{ClipboardContext, ClipboardProvider};
 use compositing::SendableFrameTree;
 use compositing::compositor_thread::{CompositorProxy, EmbedderMsg, EmbedderProxy};
 use compositing::compositor_thread::Msg as ToCompositorMsg;
 use debugger;
 use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg};
 use euclid::{Size2D, TypedSize2D, TypedScale};
@@ -324,16 +325,19 @@ pub struct Constellation<Message, LTF, S
     /// Phantom data that keeps the Rust type system happy.
     phantom: PhantomData<(Message, LTF, STF)>,
 
     /// Entry point to create and get channels to a WebGLThread.
     webgl_threads: Option<WebGLThreads>,
 
     /// A channel through which messages can be sent to the webvr thread.
     webvr_chan: Option<IpcSender<WebVRMsg>>,
+
+    /// An Id for the next canvas to use.
+    canvas_id: CanvasId,
 }
 
 /// State needed to construct a constellation.
 pub struct InitialConstellationState {
     /// A channel through which messages can be sent to the embedder.
     pub embedder_proxy: EmbedderProxy,
 
     /// A channel through which messages can be sent to the compositor.
@@ -621,16 +625,17 @@ impl<Message, LTF, STF> Constellation<Me
                     let seed = opts::get().random_pipeline_closure_seed.unwrap_or_else(random);
                     let rng = ServoRng::from_seed(&[seed]);
                     warn!("Randomly closing pipelines.");
                     info!("Using seed {} for random pipeline closure.", seed);
                     (rng, prob)
                 }),
                 webgl_threads: state.webgl_threads,
                 webvr_chan: state.webvr_chan,
+                canvas_id: CanvasId(0),
             };
 
             constellation.run();
         }).expect("Thread spawning failed");
 
         (compositor_sender, swmanager_sender)
     }
 
@@ -2198,21 +2203,22 @@ impl<Message, LTF, STF> Constellation<Me
                 self.handle_send_error(parent_pipeline_id, e);
             }
         }
     }
 
     fn handle_create_canvas_paint_thread_msg(
             &mut self,
             size: &Size2D<i32>,
-            response_sender: IpcSender<IpcSender<CanvasMsg>>) {
+            response_sender: IpcSender<(IpcSender<CanvasMsg>, CanvasId)>) {
+        self.canvas_id.0 += 1;
         let webrender_api = self.webrender_api_sender.clone();
         let sender = CanvasPaintThread::start(*size, webrender_api,
-                                              opts::get().enable_canvas_antialiasing);
-        if let Err(e) = response_sender.send(sender) {
+                                              opts::get().enable_canvas_antialiasing, self.canvas_id.clone());
+        if let Err(e) = response_sender.send((sender, self.canvas_id.clone())) {
             warn!("Create canvas paint thread response failed ({})", e);
         }
     }
 
     fn handle_webdriver_msg(&mut self, msg: WebDriverCommandMsg) {
         // Find the script channel for the given parent pipeline,
         // and pass the event to that script thread.
         match msg {
--- a/servo/components/layout/display_list/builder.rs
+++ b/servo/components/layout/display_list/builder.rs
@@ -1795,17 +1795,18 @@ impl FragmentDisplayListBuilding for Fra
 
                 let (image_key, format) = match canvas_fragment_info.source {
                     CanvasFragmentSource::WebGL(image_key) => (image_key, PixelFormat::BGRA8),
                     CanvasFragmentSource::Image(ref ipc_renderer) => match *ipc_renderer {
                         Some(ref ipc_renderer) => {
                             let ipc_renderer = ipc_renderer.lock().unwrap();
                             let (sender, receiver) = ipc::channel().unwrap();
                             ipc_renderer
-                                .send(CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender)))
+                                .send(CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender),
+                                canvas_fragment_info.canvas_id.clone()))
                                 .unwrap();
                             (receiver.recv().unwrap().image_key, PixelFormat::BGRA8)
                         },
                         None => return,
                     },
                 };
 
                 let base = create_base_display_item(state);
--- a/servo/components/layout/fragment.rs
+++ b/servo/components/layout/fragment.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! The `Fragment` type, which represents the leaves of the layout tree.
 
 #![deny(unsafe_code)]
 
 use ServoArc;
 use app_units::Au;
-use canvas_traits::canvas::CanvasMsg;
+use canvas_traits::canvas::{CanvasMsg, CanvasId};
 use context::{LayoutContext, with_thread_local_font_context};
 use display_list::ToLayout;
 use euclid::{Point2D, Vector2D, Rect, Size2D};
 use floats::ClearType;
 use flow::{GetBaseFlow, ImmutableFlowUtils};
 use flow_ref::FlowRef;
 use gfx;
 use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode};
@@ -340,16 +340,17 @@ pub enum CanvasFragmentSource {
     Image(Option<Arc<Mutex<IpcSender<CanvasMsg>>>>)
 }
 
 #[derive(Clone)]
 pub struct CanvasFragmentInfo {
     pub source: CanvasFragmentSource,
     pub dom_width: Au,
     pub dom_height: Au,
+    pub canvas_id: CanvasId,
 }
 
 impl CanvasFragmentInfo {
     pub fn new(data: HTMLCanvasData) -> CanvasFragmentInfo {
         let source = match data.source {
             HTMLCanvasDataSource::WebGL(texture_id) => {
                 CanvasFragmentSource::WebGL(texture_id)
             },
@@ -357,16 +358,17 @@ impl CanvasFragmentInfo {
                 CanvasFragmentSource::Image(ipc_sender.map(|renderer| Arc::new(Mutex::new(renderer))))
             }
         };
 
         CanvasFragmentInfo {
             source: source,
             dom_width: Au::from_px(data.width as i32),
             dom_height: Au::from_px(data.height as i32),
+            canvas_id: data.canvas_id,
         }
     }
 }
 
 #[derive(Clone)]
 pub struct SvgFragmentInfo {
     pub dom_width: Au,
     pub dom_height: Au,
--- a/servo/components/script/dom/bindings/trace.rs
+++ b/servo/components/script/dom/bindings/trace.rs
@@ -25,17 +25,17 @@
 //!    will add the object to the graph, and will trace that object as well.
 //! 5. When the GC finishes tracing, it [`finalizes`](../index.html#destruction)
 //!    any reflectors that were not reachable.
 //!
 //! The `unsafe_no_jsmanaged_fields!()` macro adds an empty implementation of
 //! `JSTraceable` to a datatype.
 
 use app_units::Au;
-use canvas_traits::canvas::{CanvasGradientStop, LinearGradientStyle, RadialGradientStyle};
+use canvas_traits::canvas::{CanvasGradientStop, CanvasId, LinearGradientStyle, RadialGradientStyle};
 use canvas_traits::canvas::{CompositionOrBlending, LineCapStyle, LineJoinStyle, RepetitionStyle};
 use canvas_traits::webgl::{WebGLBufferId, WebGLFramebufferId, WebGLProgramId, WebGLRenderbufferId};
 use canvas_traits::webgl::{WebGLChan, WebGLContextShareMode, WebGLError, WebGLPipeline, WebGLMsgSender};
 use canvas_traits::webgl::{WebGLReceiver, WebGLSender, WebGLShaderId, WebGLTextureId, WebGLVertexArrayId};
 use canvas_traits::webgl::{WebGLSLVersion, WebGLVersion};
 use cssparser::RGBA;
 use devtools_traits::{CSSError, TimelineMarkerType, WorkerId};
 use dom::abstractworker::SharedRt;
@@ -418,16 +418,17 @@ unsafe_no_jsmanaged_fields!(WebGLTexture
 unsafe_no_jsmanaged_fields!(WebGLVertexArrayId);
 unsafe_no_jsmanaged_fields!(WebGLVersion);
 unsafe_no_jsmanaged_fields!(WebGLSLVersion);
 unsafe_no_jsmanaged_fields!(MediaList);
 unsafe_no_jsmanaged_fields!(WebVRGamepadHand);
 unsafe_no_jsmanaged_fields!(ScriptToConstellationChan);
 unsafe_no_jsmanaged_fields!(InteractiveMetrics);
 unsafe_no_jsmanaged_fields!(InteractiveWindow);
+unsafe_no_jsmanaged_fields!(CanvasId);
 
 unsafe impl<'a> JSTraceable for &'a str {
     #[inline]
     unsafe fn trace(&self, _: *mut JSTracer) {
         // Do nothing
     }
 }
 
--- a/servo/components/script/dom/canvasrenderingcontext2d.rs
+++ b/servo/components/script/dom/canvasrenderingcontext2d.rs
@@ -1,13 +1,13 @@
 /* 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 canvas_traits::canvas::{Canvas2dMsg, CanvasMsg};
+use canvas_traits::canvas::{Canvas2dMsg, CanvasMsg, CanvasId};
 use canvas_traits::canvas::{CompositionOrBlending, FillOrStrokeStyle, FillRule};
 use canvas_traits::canvas::{LineCapStyle, LineJoinStyle, LinearGradientStyle};
 use canvas_traits::canvas::{RadialGradientStyle, RepetitionStyle, byte_swap_and_premultiply};
 use cssparser::{Parser, ParserInput, RGBA};
 use cssparser::Color as CSSColor;
 use dom::bindings::cell::DomRefCell;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasFillRule;
@@ -73,16 +73,17 @@ pub struct CanvasRenderingContext2D {
     /// Any missing image URLs.
     missing_image_urls: DomRefCell<Vec<ServoUrl>>,
     /// The base URL for resolving CSS image URL values.
     /// Needed because of https://github.com/servo/servo/issues/17625
     base_url: ServoUrl,
     state: DomRefCell<CanvasContextState>,
     saved_states: DomRefCell<Vec<CanvasContextState>>,
     origin_clean: Cell<bool>,
+    canvas_id: CanvasId,
 }
 
 #[must_root]
 #[derive(Clone, JSTraceable, MallocSizeOf)]
 struct CanvasContextState {
     global_alpha: f64,
     global_composition: CompositionOrBlending,
     image_smoothing_enabled: bool,
@@ -128,28 +129,29 @@ impl CanvasRenderingContext2D {
                          base_url: ServoUrl,
                          size: Size2D<i32>)
                          -> CanvasRenderingContext2D {
         debug!("Creating new canvas rendering context.");
         let (sender, receiver) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
         let script_to_constellation_chan = global.script_to_constellation_chan();
         debug!("Asking constellation to create new canvas thread.");
         script_to_constellation_chan.send(ScriptMsg::CreateCanvasPaintThread(size, sender)).unwrap();
-        let ipc_renderer = receiver.recv().unwrap();
+        let (ipc_renderer, canvas_id) = receiver.recv().unwrap();
         debug!("Done.");
         CanvasRenderingContext2D {
             reflector_: Reflector::new(),
             ipc_renderer: ipc_renderer,
             canvas: canvas.map(Dom::from_ref),
             image_cache: image_cache,
             missing_image_urls: DomRefCell::new(Vec::new()),
             base_url: base_url,
             state: DomRefCell::new(CanvasContextState::new()),
             saved_states: DomRefCell::new(Vec::new()),
             origin_clean: Cell::new(true),
+            canvas_id: canvas_id,
         }
     }
 
     pub fn new(global: &GlobalScope,
                canvas: &HTMLCanvasElement,
                size: Size2D<i32>)
                -> DomRoot<CanvasRenderingContext2D> {
         let window = window_from_node(canvas);
@@ -160,36 +162,34 @@ impl CanvasRenderingContext2D {
         ));
         reflect_dom_object(boxed, global, CanvasRenderingContext2DBinding::Wrap)
     }
 
     // https://html.spec.whatwg.org/multipage/#concept-canvas-set-bitmap-dimensions
     pub fn set_bitmap_dimensions(&self, size: Size2D<i32>) {
         self.reset_to_initial_state();
         self.ipc_renderer
-            .send(CanvasMsg::Recreate(size))
+            .send(CanvasMsg::Recreate(size, self.get_canvas_id()))
             .unwrap();
     }
 
     // https://html.spec.whatwg.org/multipage/#reset-the-rendering-context-to-its-default-state
     fn reset_to_initial_state(&self) {
         self.saved_states.borrow_mut().clear();
         *self.state.borrow_mut() = CanvasContextState::new();
     }
 
     fn mark_as_dirty(&self) {
         if let Some(ref canvas) = self.canvas {
             canvas.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
         }
     }
 
     fn update_transform(&self) {
-        self.ipc_renderer
-            .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetTransform(self.state.borrow().transform)))
-            .unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetTransform(self.state.borrow().transform))
     }
 
     // It is used by DrawImage to calculate the size of the source and destination rectangles based
     // on the drawImage call arguments
     // source rectangle = area of the original image to be copied
     // destination rectangle = area of the destination canvas where the source image is going to be drawn
     fn adjust_source_dest_rects(&self,
                                 image_size: Size2D<f64>,
@@ -358,33 +358,34 @@ impl CanvasRenderingContext2D {
 
         if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) {
             return Ok(());
         }
 
         let smoothing_enabled = self.state.borrow().image_smoothing_enabled;
 
         if self.canvas.as_ref().map_or(false, |c| &**c == canvas) {
-            let msg = CanvasMsg::Canvas2d(Canvas2dMsg::DrawImageSelf(
+            self.send_canvas_2d_msg(Canvas2dMsg::DrawImageSelf(
                 image_size, dest_rect, source_rect, smoothing_enabled));
-            self.ipc_renderer.send(msg).unwrap();
         } else {
             let context = match canvas.get_or_init_2d_context() {
                 Some(context) => context,
                 None => return Err(Error::InvalidState),
             };
 
             let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
             let msg = CanvasMsg::Canvas2d(Canvas2dMsg::DrawImageInOther(
                 self.ipc_renderer.clone(),
+                self.get_canvas_id(),
                 image_size,
                 dest_rect,
                 source_rect,
                 smoothing_enabled,
-                sender));
+                sender),
+                context.get_canvas_id());
 
             let renderer = context.get_ipc_renderer();
             renderer.send(msg).unwrap();
             receiver.recv().unwrap();
         };
 
         self.mark_as_dirty();
         Ok(())
@@ -448,23 +449,23 @@ impl CanvasRenderingContext2D {
                                                                      dw,
                                                                      dh);
 
         if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) {
             return Ok(());
         }
 
         let smoothing_enabled = self.state.borrow().image_smoothing_enabled;
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::DrawImage(
+        self.send_canvas_2d_msg(Canvas2dMsg::DrawImage(
             image_data.into(),
             image_size,
             dest_rect,
             source_rect,
             smoothing_enabled,
-        ))).unwrap();
+        ));
         self.mark_as_dirty();
         Ok(())
     }
 
     fn fetch_image_data(&self, url: ServoUrl) -> Option<(Vec<u8>, Size2D<i32>)> {
         let img = match self.request_image_from_cache(url) {
             ImageResponse::Loaded(img, _) => img,
             ImageResponse::PlaceholderLoaded(_, _) |
@@ -552,39 +553,54 @@ impl CanvasRenderingContext2D {
                 },
                 _ => Err(())
             }
         } else {
             Err(())
         }
     }
 
+    pub fn get_canvas_id(&self) -> CanvasId {
+        self.canvas_id.clone()
+    }
+
+    pub fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
+        self.ipc_renderer.send(CanvasMsg::Canvas2d(msg, self.get_canvas_id())).unwrap()
+    }
+
     pub fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> {
         self.ipc_renderer.clone()
     }
 
     pub fn origin_is_clean(&self) -> bool {
         self.origin_clean.get()
     }
 
     fn set_origin_unclean(&self) {
         self.origin_clean.set(false)
     }
 }
 
 pub trait LayoutCanvasRenderingContext2DHelpers {
     #[allow(unsafe_code)]
     unsafe fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg>;
+    #[allow(unsafe_code)]
+    unsafe fn get_canvas_id(&self) -> CanvasId;
 }
 
 impl LayoutCanvasRenderingContext2DHelpers for LayoutDom<CanvasRenderingContext2D> {
     #[allow(unsafe_code)]
     unsafe fn get_ipc_renderer(&self) -> IpcSender<CanvasMsg> {
         (*self.unsafe_get()).ipc_renderer.clone()
     }
+
+    #[allow(unsafe_code)]
+    unsafe fn get_canvas_id(&self) -> CanvasId {
+        (*self.unsafe_get()).canvas_id.clone()
+    }
 }
 
 // We add a guard to each of methods by the spec:
 // http://www.w3.org/html/wg/drafts/2dcontext/html5_canvas_CR/
 //
 // > Except where otherwise specified, for the 2D context interface,
 // > any method call with a numeric argument whose value is infinite or a NaN value must be ignored.
 //
@@ -597,26 +613,26 @@ impl CanvasRenderingContext2DMethods for
         // This method is not called from a paint worklet rendering context,
         // so it's OK to panic if self.canvas is None.
         DomRoot::from_ref(self.canvas.as_ref().expect("No canvas."))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-save
     fn Save(&self) {
         self.saved_states.borrow_mut().push(self.state.borrow().clone());
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SaveContext)).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::SaveContext);
     }
 
     #[allow(unrooted_must_root)]
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-restore
     fn Restore(&self) {
         let mut saved_states = self.saved_states.borrow_mut();
         if let Some(state) = saved_states.pop() {
             self.state.borrow_mut().clone_from(&state);
-            self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::RestoreContext)).unwrap();
+            self.send_canvas_2d_msg(Canvas2dMsg::RestoreContext);
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-scale
     fn Scale(&self, x: f64, y: f64) {
         if !(x.is_finite() && y.is_finite()) {
             return;
         }
@@ -691,114 +707,104 @@ impl CanvasRenderingContext2DMethods for
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalalpha
     fn SetGlobalAlpha(&self, alpha: f64) {
         if !alpha.is_finite() || alpha > 1.0 || alpha < 0.0 {
             return;
         }
 
         self.state.borrow_mut().global_alpha = alpha;
-        self.ipc_renderer
-            .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetGlobalAlpha(alpha as f32)))
-            .unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetGlobalAlpha(alpha as f32))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation
     fn GlobalCompositeOperation(&self) -> DOMString {
         let state = self.state.borrow();
         match state.global_composition {
             CompositionOrBlending::Composition(op) => DOMString::from(op.to_str()),
             CompositionOrBlending::Blending(op) => DOMString::from(op.to_str()),
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-globalcompositeoperation
     fn SetGlobalCompositeOperation(&self, op_str: DOMString) {
         if let Ok(op) = CompositionOrBlending::from_str(&op_str) {
             self.state.borrow_mut().global_composition = op;
-            self.ipc_renderer
-                .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetGlobalComposition(op)))
-                .unwrap()
+            self.send_canvas_2d_msg(Canvas2dMsg::SetGlobalComposition(op))
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-fillrect
     fn FillRect(&self, x: f64, y: f64, width: f64, height: f64) {
         if let Some(rect) = self.create_drawable_rect(x, y, width, height) {
-            self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::FillRect(rect))).unwrap();
+            self.send_canvas_2d_msg(Canvas2dMsg::FillRect(rect));
             self.mark_as_dirty();
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-clearrect
     fn ClearRect(&self, x: f64, y: f64, width: f64, height: f64) {
         if let Some(rect) = self.create_drawable_rect(x, y, width, height) {
-            self.ipc_renderer
-                .send(CanvasMsg::Canvas2d(Canvas2dMsg::ClearRect(rect)))
-                .unwrap();
+            self.send_canvas_2d_msg(Canvas2dMsg::ClearRect(rect));
             self.mark_as_dirty();
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokerect
     fn StrokeRect(&self, x: f64, y: f64, width: f64, height: f64) {
         if let Some(rect) = self.create_drawable_rect(x, y, width, height) {
-            self.ipc_renderer
-                .send(CanvasMsg::Canvas2d(Canvas2dMsg::StrokeRect(rect)))
-                .unwrap();
+            self.send_canvas_2d_msg(Canvas2dMsg::StrokeRect(rect));
             self.mark_as_dirty();
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-beginpath
     fn BeginPath(&self) {
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::BeginPath)).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::BeginPath);
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath
     fn ClosePath(&self) {
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::ClosePath)).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::ClosePath);
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-fill
     fn Fill(&self, _: CanvasFillRule) {
         // TODO: Process fill rule
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::Fill)).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::Fill);
         self.mark_as_dirty();
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-stroke
     fn Stroke(&self) {
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::Stroke)).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::Stroke);
         self.mark_as_dirty();
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-clip
     fn Clip(&self, _: CanvasFillRule) {
         // TODO: Process fill rule
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::Clip)).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::Clip);
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-ispointinpath
     fn IsPointInPath(&self, x: f64, y: f64, fill_rule: CanvasFillRule) -> bool {
         let fill_rule = match fill_rule {
             CanvasFillRule::Nonzero => FillRule::Nonzero,
             CanvasFillRule::Evenodd => FillRule::Evenodd,
         };
         let (sender, receiver) = ipc::channel::<bool>(self.global().time_profiler_chan().clone()).unwrap();
-        self.ipc_renderer
-            .send(CanvasMsg::Canvas2d(Canvas2dMsg::IsPointInPath(x, y, fill_rule, sender)))
-            .unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::IsPointInPath(x, y, fill_rule, sender));
         receiver.recv().unwrap()
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-filltext
     fn FillText(&self, text: DOMString, x: f64, y: f64, max_width: Option<f64>) {
         let parsed_text: String = text.into();
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::FillText(parsed_text, x, y, max_width))).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::FillText(parsed_text, x, y, max_width));
         self.mark_as_dirty();
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-drawimage
     fn DrawImage(&self,
                  image: CanvasImageSource,
                  dx: f64,
                  dy: f64)
@@ -853,122 +859,109 @@ impl CanvasRenderingContext2DMethods for
                         Some(dh))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-moveto
     fn MoveTo(&self, x: f64, y: f64) {
         if !(x.is_finite() && y.is_finite()) {
             return;
         }
-
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::MoveTo(Point2D::new(x as f32, y as f32)));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::MoveTo(Point2D::new(x as f32, y as f32)));
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-lineto
     fn LineTo(&self, x: f64, y: f64) {
         if !(x.is_finite() && y.is_finite()) {
             return;
         }
-
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::LineTo(Point2D::new(x as f32, y as f32)));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::LineTo(Point2D::new(x as f32, y as f32)));
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-rect
     fn Rect(&self, x: f64, y: f64, width: f64, height: f64) {
         if [x, y, width, height].iter().all(|val| val.is_finite()) {
             let rect = Rect::new(Point2D::new(x as f32, y as f32),
                                  Size2D::new(width as f32, height as f32));
-            let msg = CanvasMsg::Canvas2d(Canvas2dMsg::Rect(rect));
-            self.ipc_renderer.send(msg).unwrap();
+            self.send_canvas_2d_msg(Canvas2dMsg::Rect(rect));
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-quadraticcurveto
     fn QuadraticCurveTo(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
         if !(cpx.is_finite() && cpy.is_finite() && x.is_finite() && y.is_finite()) {
             return;
         }
-
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::QuadraticCurveTo(Point2D::new(cpx as f32,
-                                                                                 cpy as f32),
-                                                                    Point2D::new(x as f32,
-                                                                                 y as f32)));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::QuadraticCurveTo(Point2D::new(cpx as f32,
+                                                                           cpy as f32),
+                                                              Point2D::new(x as f32,
+                                                                           y as f32)));
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-beziercurveto
     fn BezierCurveTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
         if !(cp1x.is_finite() && cp1y.is_finite() && cp2x.is_finite() && cp2y.is_finite() &&
              x.is_finite() && y.is_finite()) {
             return;
         }
-
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::BezierCurveTo(Point2D::new(cp1x as f32,
-                                                                              cp1y as f32),
-                                                                 Point2D::new(cp2x as f32,
-                                                                              cp2y as f32),
-                                                                 Point2D::new(x as f32, y as f32)));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::BezierCurveTo(Point2D::new(cp1x as f32,
+                                                                        cp1y as f32),
+                                                           Point2D::new(cp2x as f32,
+                                                                        cp2y as f32),
+                                                           Point2D::new(x as f32, y as f32)));
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-arc
     fn Arc(&self, x: f64, y: f64, r: f64, start: f64, end: f64, ccw: bool) -> ErrorResult {
         if !([x, y, r, start, end].iter().all(|x| x.is_finite())) {
             return Ok(());
         }
 
         if r < 0.0 {
             return Err(Error::IndexSize);
         }
 
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::Arc(Point2D::new(x as f32, y as f32),
-                                                       r as f32,
-                                                       start as f32,
-                                                       end as f32,
-                                                       ccw));
-
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::Arc(Point2D::new(x as f32, y as f32),
+                                                 r as f32,
+                                                 start as f32,
+                                                 end as f32,
+                                                 ccw));
         Ok(())
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-arcto
     fn ArcTo(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, r: f64) -> ErrorResult {
         if !([cp1x, cp1y, cp2x, cp2y, r].iter().all(|x| x.is_finite())) {
             return Ok(());
         }
         if r < 0.0 {
             return Err(Error::IndexSize);
         }
 
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::ArcTo(Point2D::new(cp1x as f32, cp1y as f32),
-                                                         Point2D::new(cp2x as f32, cp2y as f32),
-                                                         r as f32));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::ArcTo(Point2D::new(cp1x as f32, cp1y as f32),
+                                                   Point2D::new(cp2x as f32, cp2y as f32),
+                                                   r as f32));
         Ok(())
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-ellipse
     fn Ellipse(&self, x: f64, y: f64, rx: f64, ry: f64, rotation: f64, start: f64, end: f64, ccw: bool) -> ErrorResult {
         if !([x, y, rx, ry, rotation, start, end].iter().all(|x| x.is_finite())) {
             return Ok(());
         }
         if rx < 0.0 || ry < 0.0 {
             return Err(Error::IndexSize);
         }
 
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::Ellipse(Point2D::new(x as f32, y as f32),
-                                                       rx as f32,
-                                                       ry as f32,
-                                                       rotation as f32,
-                                                       start as f32,
-                                                       end as f32,
-                                                       ccw));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::Ellipse(Point2D::new(x as f32, y as f32),
+                                                     rx as f32,
+                                                     ry as f32,
+                                                     rotation as f32,
+                                                     start as f32,
+                                                     end as f32,
+                                                     ccw));
         Ok(())
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-imagesmoothingenabled
     fn ImageSmoothingEnabled(&self) -> bool {
         let state = self.state.borrow();
         state.image_smoothing_enabled
     }
@@ -996,35 +989,29 @@ impl CanvasRenderingContext2DMethods for
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
     fn SetStrokeStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
         match value {
             StringOrCanvasGradientOrCanvasPattern::String(string) => {
                 if let Ok(rgba) = self.parse_color(&string) {
                     self.state.borrow_mut().stroke_style = CanvasFillOrStrokeStyle::Color(rgba);
-                    self.ipc_renderer
-                        .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetStrokeStyle(
-                                    FillOrStrokeStyle::Color(rgba))))
-                        .unwrap();
+                    self.send_canvas_2d_msg(Canvas2dMsg::SetStrokeStyle(
+                        FillOrStrokeStyle::Color(rgba)));
                 }
             },
             StringOrCanvasGradientOrCanvasPattern::CanvasGradient(gradient) => {
                 self.state.borrow_mut().stroke_style =
                     CanvasFillOrStrokeStyle::Gradient(Dom::from_ref(&*gradient));
-                let msg = CanvasMsg::Canvas2d(
-                    Canvas2dMsg::SetStrokeStyle(gradient.to_fill_or_stroke_style()));
-                self.ipc_renderer.send(msg).unwrap();
+                self.send_canvas_2d_msg(Canvas2dMsg::SetStrokeStyle(gradient.to_fill_or_stroke_style()));
             },
             StringOrCanvasGradientOrCanvasPattern::CanvasPattern(pattern) => {
                 self.state.borrow_mut().stroke_style =
                     CanvasFillOrStrokeStyle::Pattern(Dom::from_ref(&*pattern));
-                let msg = CanvasMsg::Canvas2d(
-                    Canvas2dMsg::SetStrokeStyle(pattern.to_fill_or_stroke_style()));
-                self.ipc_renderer.send(msg).unwrap();
+                self.send_canvas_2d_msg(Canvas2dMsg::SetStrokeStyle(pattern.to_fill_or_stroke_style()));
                 if !pattern.origin_is_clean() {
                     self.set_origin_unclean();
                 }
             }
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
@@ -1045,35 +1032,29 @@ impl CanvasRenderingContext2DMethods for
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-strokestyle
     fn SetFillStyle(&self, value: StringOrCanvasGradientOrCanvasPattern) {
         match value {
             StringOrCanvasGradientOrCanvasPattern::String(string) => {
                 if let Ok(rgba) = self.parse_color(&string) {
                     self.state.borrow_mut().fill_style = CanvasFillOrStrokeStyle::Color(rgba);
-                    self.ipc_renderer
-                        .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetFillStyle(
-                                    FillOrStrokeStyle::Color(rgba))))
-                        .unwrap()
+                    self.send_canvas_2d_msg(Canvas2dMsg::SetFillStyle(
+                        FillOrStrokeStyle::Color(rgba)))
                 }
             }
             StringOrCanvasGradientOrCanvasPattern::CanvasGradient(gradient) => {
                 self.state.borrow_mut().fill_style =
                     CanvasFillOrStrokeStyle::Gradient(Dom::from_ref(&*gradient));
-                let msg = CanvasMsg::Canvas2d(
-                    Canvas2dMsg::SetFillStyle(gradient.to_fill_or_stroke_style()));
-                self.ipc_renderer.send(msg).unwrap();
+                self.send_canvas_2d_msg(Canvas2dMsg::SetFillStyle(gradient.to_fill_or_stroke_style()));
             }
             StringOrCanvasGradientOrCanvasPattern::CanvasPattern(pattern) => {
                 self.state.borrow_mut().fill_style =
                     CanvasFillOrStrokeStyle::Pattern(Dom::from_ref(&*pattern));
-                let msg = CanvasMsg::Canvas2d(
-                    Canvas2dMsg::SetFillStyle(pattern.to_fill_or_stroke_style()));
-                self.ipc_renderer.send(msg).unwrap();
+                self.send_canvas_2d_msg(Canvas2dMsg::SetFillStyle(pattern.to_fill_or_stroke_style()));
                 if !pattern.origin_is_clean() {
                     self.set_origin_unclean();
                 }
             }
         }
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-createimagedata
@@ -1127,30 +1108,28 @@ impl CanvasRenderingContext2DMethods for
         let sh = cmp::max(1, sh.to_u32().unwrap());
         let sw = cmp::max(1, sw.to_u32().unwrap());
 
         let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
         let dest_rect = Rect::new(Point2D::new(sx.to_i32().unwrap(), sy.to_i32().unwrap()),
                                   Size2D::new(sw as i32, sh as i32));
         let canvas_size = self.canvas.as_ref().map(|c| c.get_size()).unwrap_or(Size2D::zero());
         let canvas_size = Size2D::new(canvas_size.width as f64, canvas_size.height as f64);
-        self.ipc_renderer
-            .send(CanvasMsg::Canvas2d(Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender)))
-            .unwrap();
-        let mut data = Vec::from(receiver.recv().unwrap());
+        self.send_canvas_2d_msg(Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender));
+        let mut data = receiver.recv().unwrap();
 
         // Un-premultiply alpha
         for chunk in data.chunks_mut(4) {
             let alpha = chunk[3] as usize;
             chunk[0] = UNPREMULTIPLY_TABLE[256 * alpha + chunk[0] as usize];
             chunk[1] = UNPREMULTIPLY_TABLE[256 * alpha + chunk[1] as usize];
             chunk[2] = UNPREMULTIPLY_TABLE[256 * alpha + chunk[2] as usize];
         }
 
-        ImageData::new(&self.global(), sw, sh, Some(data))
+        ImageData::new(&self.global(), sw, sh, Some(data.to_vec()))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata
     fn PutImageData(&self, imagedata: &ImageData, dx: Finite<f64>, dy: Finite<f64>) {
         self.PutImageData_(imagedata,
                            dx,
                            dy,
                            Finite::wrap(0f64),
@@ -1169,23 +1148,20 @@ impl CanvasRenderingContext2DMethods for
                      dirty_width: Finite<f64>,
                      dirty_height: Finite<f64>) {
         let data = imagedata.get_data_array();
         let offset = Vector2D::new(*dx, *dy);
         let image_data_size = Size2D::new(imagedata.Width() as f64, imagedata.Height() as f64);
 
         let dirty_rect = Rect::new(Point2D::new(*dirty_x, *dirty_y),
                                    Size2D::new(*dirty_width, *dirty_height));
-        let msg = CanvasMsg::Canvas2d(Canvas2dMsg::PutImageData(
-            data.into(),
-            offset,
-            image_data_size,
-            dirty_rect,
-        ));
-        self.ipc_renderer.send(msg).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::PutImageData(data.into(),
+                                                                offset,
+                                                                image_data_size,
+                                                                dirty_rect));
         self.mark_as_dirty();
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-createlineargradient
     fn CreateLinearGradient(&self,
                             x0: Finite<f64>,
                             y0: Finite<f64>,
                             x1: Finite<f64>,
@@ -1277,19 +1253,17 @@ impl CanvasRenderingContext2DMethods for
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-linewidth
     fn SetLineWidth(&self, width: f64) {
         if !width.is_finite() || width <= 0.0 {
             return;
         }
 
         self.state.borrow_mut().line_width = width;
-        self.ipc_renderer
-            .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetLineWidth(width as f32)))
-            .unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetLineWidth(width as f32))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap
     fn LineCap(&self) -> CanvasLineCap {
         match self.state.borrow().line_cap {
             LineCapStyle::Butt => CanvasLineCap::Butt,
             LineCapStyle::Round => CanvasLineCap::Round,
             LineCapStyle::Square => CanvasLineCap::Square,
@@ -1299,17 +1273,17 @@ impl CanvasRenderingContext2DMethods for
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-linecap
     fn SetLineCap(&self, cap: CanvasLineCap) {
         let line_cap = match cap {
             CanvasLineCap::Butt => LineCapStyle::Butt,
             CanvasLineCap::Round => LineCapStyle::Round,
             CanvasLineCap::Square => LineCapStyle::Square,
         };
         self.state.borrow_mut().line_cap = line_cap;
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetLineCap(line_cap))).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::SetLineCap(line_cap));
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin
     fn LineJoin(&self) -> CanvasLineJoin {
         match self.state.borrow().line_join {
             LineJoinStyle::Round => CanvasLineJoin::Round,
             LineJoinStyle::Bevel => CanvasLineJoin::Bevel,
             LineJoinStyle::Miter => CanvasLineJoin::Miter,
@@ -1319,100 +1293,96 @@ impl CanvasRenderingContext2DMethods for
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-linejoin
     fn SetLineJoin(&self, join: CanvasLineJoin) {
         let line_join = match join {
             CanvasLineJoin::Round => LineJoinStyle::Round,
             CanvasLineJoin::Bevel => LineJoinStyle::Bevel,
             CanvasLineJoin::Miter => LineJoinStyle::Miter,
         };
         self.state.borrow_mut().line_join = line_join;
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetLineJoin(line_join))).unwrap();
+        self.send_canvas_2d_msg(Canvas2dMsg::SetLineJoin(line_join));
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit
     fn MiterLimit(&self) -> f64 {
         let state = self.state.borrow();
         state.miter_limit
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-miterlimit
     fn SetMiterLimit(&self, limit: f64) {
         if !limit.is_finite() || limit <= 0.0 {
             return;
         }
 
         self.state.borrow_mut().miter_limit = limit;
-        self.ipc_renderer
-            .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetMiterLimit(limit as f32)))
-            .unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetMiterLimit(limit as f32))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx
     fn ShadowOffsetX(&self) -> f64 {
         self.state.borrow().shadow_offset_x
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsetx
     fn SetShadowOffsetX(&self, value: f64) {
         if !value.is_finite() || value == self.state.borrow().shadow_offset_x {
             return;
         }
         self.state.borrow_mut().shadow_offset_x = value;
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowOffsetX(value))).unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetShadowOffsetX(value))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety
     fn ShadowOffsetY(&self) -> f64 {
         self.state.borrow().shadow_offset_y
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowoffsety
     fn SetShadowOffsetY(&self, value: f64) {
         if !value.is_finite() || value == self.state.borrow().shadow_offset_y {
             return;
         }
         self.state.borrow_mut().shadow_offset_y = value;
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowOffsetY(value))).unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetShadowOffsetY(value))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur
     fn ShadowBlur(&self) -> f64 {
         self.state.borrow().shadow_blur
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowblur
     fn SetShadowBlur(&self, value: f64) {
         if !value.is_finite() || value < 0f64 || value == self.state.borrow().shadow_blur {
             return;
         }
         self.state.borrow_mut().shadow_blur = value;
-        self.ipc_renderer.send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowBlur(value))).unwrap()
+        self.send_canvas_2d_msg(Canvas2dMsg::SetShadowBlur(value))
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor
     fn ShadowColor(&self) -> DOMString {
         let mut result = String::new();
         serialize(&self.state.borrow().shadow_color, &mut result).unwrap();
         DOMString::from(result)
     }
 
     // https://html.spec.whatwg.org/multipage/#dom-context-2d-shadowcolor
     fn SetShadowColor(&self, value: DOMString) {
         if let Ok(color) = parse_color(&value) {
             self.state.borrow_mut().shadow_color = color;
-            self.ipc_renderer
-                .send(CanvasMsg::Canvas2d(Canvas2dMsg::SetShadowColor(color)))
-                .unwrap()
+            self.send_canvas_2d_msg(Canvas2dMsg::SetShadowColor(color))
         }
     }
 }
 
 impl Drop for CanvasRenderingContext2D {
     fn drop(&mut self) {
-        if let Err(err) = self.ipc_renderer.send(CanvasMsg::Close) {
+        if let Err(err) = self.ipc_renderer.send(CanvasMsg::Close(self.get_canvas_id())) {
             warn!("Could not close canvas: {}", err)
         }
     }
 }
 
 pub fn parse_color(string: &str) -> Result<RGBA, ()> {
     let mut input = ParserInput::new(string);
     let mut parser = Parser::new(&mut input);
--- a/servo/components/script/dom/htmlcanvaselement.rs
+++ b/servo/components/script/dom/htmlcanvaselement.rs
@@ -1,14 +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/. */
 
 use base64;
-use canvas_traits::canvas::{CanvasMsg, FromScriptMsg};
+use canvas_traits::canvas::{CanvasMsg, CanvasId, FromScriptMsg};
 use canvas_traits::webgl::WebGLVersion;
 use dom::attr::Attr;
 use dom::bindings::cell::DomRefCell;
 use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
 use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding;
 use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::{HTMLCanvasElementMethods, RenderingContext};
 use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes;
 use dom::bindings::conversions::ConversionResult;
@@ -100,16 +100,17 @@ impl HTMLCanvasElement {
         }
     }
 }
 
 pub trait LayoutHTMLCanvasElementHelpers {
     fn data(&self) -> HTMLCanvasData;
     fn get_width(&self) -> LengthOrPercentageOrAuto;
     fn get_height(&self) -> LengthOrPercentageOrAuto;
+    fn get_canvas_id_for_layout(&self) -> CanvasId;
 }
 
 impl LayoutHTMLCanvasElementHelpers for LayoutDom<HTMLCanvasElement> {
     #[allow(unsafe_code)]
     fn data(&self) -> HTMLCanvasData {
         unsafe {
             let canvas = &*self.unsafe_get();
             let source = match canvas.context.borrow_for_layout().as_ref() {
@@ -128,16 +129,17 @@ impl LayoutHTMLCanvasElementHelpers for 
             };
 
             let width_attr = canvas.upcast::<Element>().get_attr_for_layout(&ns!(), &local_name!("width"));
             let height_attr = canvas.upcast::<Element>().get_attr_for_layout(&ns!(), &local_name!("height"));
             HTMLCanvasData {
                 source: source,
                 width: width_attr.map_or(DEFAULT_WIDTH, |val| val.as_uint()),
                 height: height_attr.map_or(DEFAULT_HEIGHT, |val| val.as_uint()),
+                canvas_id: self.get_canvas_id_for_layout(),
             }
         }
     }
 
     #[allow(unsafe_code)]
     fn get_width(&self) -> LengthOrPercentageOrAuto {
         unsafe {
             (&*self.upcast::<Element>().unsafe_get())
@@ -151,16 +153,28 @@ impl LayoutHTMLCanvasElementHelpers for 
     fn get_height(&self) -> LengthOrPercentageOrAuto {
         unsafe {
             (&*self.upcast::<Element>().unsafe_get())
                 .get_attr_for_layout(&ns!(), &local_name!("height"))
                 .map(AttrValue::as_uint_px_dimension)
                 .unwrap_or(LengthOrPercentageOrAuto::Auto)
         }
     }
+
+    #[allow(unsafe_code)]
+    fn get_canvas_id_for_layout(&self) -> CanvasId {
+        unsafe {
+            let canvas = &*self.unsafe_get();
+            if let &Some(CanvasContext::Context2d(ref context)) = canvas.context.borrow_for_layout() {
+                context.to_layout().get_canvas_id()
+            } else {
+                CanvasId(0)
+            }
+        }
+    }
 }
 
 
 impl HTMLCanvasElement {
     pub fn get_or_init_2d_context(&self) -> Option<DomRoot<CanvasRenderingContext2D>> {
         if self.context.borrow().is_none() {
             let window = window_from_node(self);
             let size = self.get_size();
@@ -256,17 +270,17 @@ impl HTMLCanvasElement {
 
         if size.width == 0 || size.height == 0 {
             return None
         }
 
         let data = match self.context.borrow().as_ref() {
             Some(&CanvasContext::Context2d(ref context)) => {
                 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
-                let msg = CanvasMsg::FromScript(FromScriptMsg::SendPixels(sender));
+                let msg = CanvasMsg::FromScript(FromScriptMsg::SendPixels(sender), context.get_canvas_id());
                 context.get_ipc_renderer().send(msg).unwrap();
 
                 receiver.recv().unwrap()?.into()
             },
             Some(&CanvasContext::WebGL(_)) => {
                 // TODO: add a method in WebGLRenderingContext to get the pixels.
                 return None;
             },
--- a/servo/components/script/dom/paintrenderingcontext2d.rs
+++ b/servo/components/script/dom/paintrenderingcontext2d.rs
@@ -54,17 +54,17 @@ impl PaintRenderingContext2D {
 
     pub fn new(global: &PaintWorkletGlobalScope) -> DomRoot<PaintRenderingContext2D> {
         reflect_dom_object(Box::new(PaintRenderingContext2D::new_inherited(global)),
                            global,
                            PaintRenderingContext2DBinding::Wrap)
     }
 
     pub fn send_data(&self, sender: IpcSender<CanvasImageData>) {
-        let msg = CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender));
+        let msg = CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender), self.context.get_canvas_id());
         let _ = self.context.get_ipc_renderer().send(msg);
     }
 
     pub fn take_missing_image_urls(&self) -> Vec<ServoUrl> {
         self.context.take_missing_image_urls()
     }
 
     pub fn set_bitmap_dimensions(&self,
--- a/servo/components/script_layout_interface/lib.rs
+++ b/servo/components/script_layout_interface/lib.rs
@@ -36,17 +36,17 @@ extern crate style;
 extern crate webrender_api;
 
 pub mod message;
 pub mod reporter;
 pub mod rpc;
 pub mod wrapper_traits;
 
 use atomic_refcell::AtomicRefCell;
-use canvas_traits::canvas::CanvasMsg;
+use canvas_traits::canvas::{CanvasMsg, CanvasId};
 use ipc_channel::ipc::IpcSender;
 use libc::c_void;
 use net_traits::image_cache::PendingImageId;
 use script_traits::UntrustedNodeAddress;
 use servo_url::ServoUrl;
 use std::ptr::NonNull;
 use std::sync::atomic::AtomicIsize;
 use style::data::ElementData;
@@ -128,16 +128,17 @@ pub enum HTMLCanvasDataSource {
     WebGL(webrender_api::ImageKey),
     Image(Option<IpcSender<CanvasMsg>>)
 }
 
 pub struct HTMLCanvasData {
     pub source: HTMLCanvasDataSource,
     pub width: u32,
     pub height: u32,
+    pub canvas_id: CanvasId,
 }
 
 pub struct SVGSVGData {
     pub width: u32,
     pub height: u32,
 }
 
 /// The address of a node known to be valid. These are sent from script to layout.
--- a/servo/components/script_traits/script_msg.rs
+++ b/servo/components/script_traits/script_msg.rs
@@ -6,17 +6,17 @@ use AnimationState;
 use CompositorEvent;
 use DocumentState;
 use IFrameLoadInfo;
 use IFrameLoadInfoWithData;
 use LayoutControlMsg;
 use LoadData;
 use WorkerGlobalScopeInit;
 use WorkerScriptLoadOrigin;
-use canvas_traits::canvas::CanvasMsg;
+use canvas_traits::canvas::{CanvasMsg, CanvasId};
 use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
 use euclid::{Size2D, TypedSize2D};
 use gfx_traits::Epoch;
 use ipc_channel::ipc::{IpcReceiver, IpcSender};
 use msg::constellation_msg::{BrowsingContextId, PipelineId, TraversalDirection};
 use msg::constellation_msg::{Key, KeyModifiers, KeyState};
 use net_traits::CoreResourceMsg;
 use net_traits::request::RequestInit;
@@ -74,17 +74,17 @@ pub enum ScriptMsg {
     InitiateNavigateRequest(RequestInit, /* cancellation_chan */ IpcReceiver<()>),
     /// Broadcast a storage event to every same-origin pipeline.
     /// The strings are key, old value and new value.
     BroadcastStorageEvent(StorageType, ServoUrl, Option<String>, Option<String>, Option<String>),
     /// Indicates whether this pipeline is currently running animations.
     ChangeRunningAnimationsState(AnimationState),
     /// Requests that a new 2D canvas thread be created. (This is done in the constellation because
     /// 2D canvases may use the GPU and we don't want to give untrusted content access to the GPU.)
-    CreateCanvasPaintThread(Size2D<i32>, IpcSender<IpcSender<CanvasMsg>>),
+    CreateCanvasPaintThread(Size2D<i32>, IpcSender<(IpcSender<CanvasMsg>, CanvasId)>),
     /// Notifies the constellation that this frame has received focus.
     Focus,
     /// Forward an event that was sent to the parent window.
     ForwardEvent(PipelineId, CompositorEvent),
     /// Requests that the constellation retrieve the current contents of the clipboard
     GetClipboardContents(IpcSender<String>),
     /// Get the browsing context id for a given pipeline.
     GetBrowsingContextId(PipelineId, IpcSender<Option<BrowsingContextId>>),
--- a/servo/components/style/font_face.rs
+++ b/servo/components/style/font_face.rs
@@ -219,23 +219,23 @@ impl Parse for Source {
         }))
     }
 }
 
 macro_rules! is_descriptor_enabled {
     ("font-display") => {
         unsafe {
             use gecko_bindings::structs::mozilla;
-            mozilla::StylePrefs_sFontDisplayEnabled
+            mozilla::StaticPrefs_sVarCache_layout_css_font_display_enabled
         }
     };
     ("font-variation-settings") => {
         unsafe {
             use gecko_bindings::structs::mozilla;
-            mozilla::StylePrefs_sFontVariationsEnabled
+            mozilla::StaticPrefs_sVarCache_layout_css_font_variations_enabled
         }
     };
     ($name: tt) => { true }
 }
 
 macro_rules! font_face_descriptors_common {
     (
         $( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty, )*
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -183,17 +183,17 @@ impl PerDocumentStyleDataImpl {
 
     /// Get the default computed values for this document.
     pub fn default_computed_values(&self) -> &Arc<ComputedValues> {
         self.stylist.device().default_computed_values_arc()
     }
 
     /// Returns whether visited links are enabled.
     fn visited_links_enabled(&self) -> bool {
-        unsafe { structs::StylePrefs_sVisitedLinksEnabled }
+        unsafe { structs::StaticPrefs_sVarCache_layout_css_visited_links_enabled }
     }
 
     /// Returns whether visited styles are enabled.
     pub fn visited_styles_enabled(&self) -> bool {
         if !self.visited_links_enabled() {
             return false;
         }
 
--- a/servo/components/style/gecko/generated/structs.rs
+++ b/servo/components/style/gecko/generated/structs.rs
@@ -11969,89 +11969,89 @@ pub mod root {
         }
         impl Clone for LookAndFeel {
             fn clone(&self) -> Self {
                 *self
             }
         }
         #[repr(C)]
         #[derive(Debug, Copy)]
-        pub struct StylePrefs {
+        pub struct StaticPrefs {
             pub _address: u8,
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs19sFontDisplayEnabledE"]
-            pub static mut StylePrefs_sFontDisplayEnabled: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs41sVarCache_layout_css_font_display_enabledE"]
+            pub static mut StaticPrefs_sVarCache_layout_css_font_display_enabled: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs19sOpentypeSVGEnabledE"]
-            pub static mut StylePrefs_sOpentypeSVGEnabled: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs49sVarCache_gfx_font_rendering_opentype_svg_enabledE"]
+            pub static mut StaticPrefs_sVarCache_gfx_font_rendering_opentype_svg_enabled: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs29sWebkitPrefixedAliasesEnabledE"]
-            pub static mut StylePrefs_sWebkitPrefixedAliasesEnabled: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs36sVarCache_layout_css_prefixes_webkitE"]
+            pub static mut StaticPrefs_sVarCache_layout_css_prefixes_webkit: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs30sWebkitDevicePixelRatioEnabledE"]
-            pub static mut StylePrefs_sWebkitDevicePixelRatioEnabled: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs55sVarCache_layout_css_prefixes_device_pixel_ratio_webkitE"]
+            pub static mut StaticPrefs_sVarCache_layout_css_prefixes_device_pixel_ratio_webkit: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs20sMozGradientsEnabledE"]
-            pub static mut StylePrefs_sMozGradientsEnabled: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs39sVarCache_layout_css_prefixes_gradientsE"]
+            pub static mut StaticPrefs_sVarCache_layout_css_prefixes_gradients: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs22sControlCharVisibilityE"]
-            pub static mut StylePrefs_sControlCharVisibility: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs47sVarCache_layout_css_control_characters_visibleE"]
+            pub static mut StaticPrefs_sVarCache_layout_css_control_characters_visible: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs28sFramesTimingFunctionEnabledE"]
-            pub static mut StylePrefs_sFramesTimingFunctionEnabled: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs42sVarCache_layout_css_frames_timing_enabledE"]
+            pub static mut StaticPrefs_sVarCache_layout_css_frames_timing_enabled: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs31sUnprefixedFullscreenApiEnabledE"]
-            pub static mut StylePrefs_sUnprefixedFullscreenApiEnabled: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs42sVarCache_full_screen_api_unprefix_enabledE"]
+            pub static mut StaticPrefs_sVarCache_full_screen_api_unprefix_enabled: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs20sVisitedLinksEnabledE"]
-            pub static mut StylePrefs_sVisitedLinksEnabled: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs42sVarCache_layout_css_visited_links_enabledE"]
+            pub static mut StaticPrefs_sVarCache_layout_css_visited_links_enabled: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs28sMozDocumentEnabledInContentE"]
-            pub static mut StylePrefs_sMozDocumentEnabledInContent: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs49sVarCache_layout_css_moz_document_content_enabledE"]
+            pub static mut StaticPrefs_sVarCache_layout_css_moz_document_content_enabled: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs32sMozDocumentURLPrefixHackEnabledE"]
-            pub static mut StylePrefs_sMozDocumentURLPrefixHackEnabled: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs57sVarCache_layout_css_moz_document_url_prefix_hack_enabledE"]
+            pub static mut StaticPrefs_sVarCache_layout_css_moz_document_url_prefix_hack_enabled: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs32sGridTemplateSubgridValueEnabledE"]
-            pub static mut StylePrefs_sGridTemplateSubgridValueEnabled: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs56sVarCache_layout_css_grid_template_subgrid_value_enabledE"]
+            pub static mut StaticPrefs_sVarCache_layout_css_grid_template_subgrid_value_enabled: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs22sFontVariationsEnabledE"]
-            pub static mut StylePrefs_sFontVariationsEnabled: bool;
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs44sVarCache_layout_css_font_variations_enabledE"]
+            pub static mut StaticPrefs_sVarCache_layout_css_font_variations_enabled: bool;
         }
         extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla10StylePrefs22sEmulateMozBoxWithFlexE"]
-            pub static mut StylePrefs_sEmulateMozBoxWithFlex: bool;
-        }
-        #[test]
-        fn bindgen_test_layout_StylePrefs() {
-            assert_eq!(
-                ::std::mem::size_of::<StylePrefs>(),
+            #[link_name = "\u{1}_ZN7mozilla11StaticPrefs46sVarCache_layout_css_emulate_moz_box_with_flexE"]
+            pub static mut StaticPrefs_sVarCache_layout_css_emulate_moz_box_with_flex: bool;
+        }
+        #[test]
+        fn bindgen_test_layout_StaticPrefs() {
+            assert_eq!(
+                ::std::mem::size_of::<StaticPrefs>(),
                 1usize,
-                concat!("Size of: ", stringify!(StylePrefs))
-            );
-            assert_eq!(
-                ::std::mem::align_of::<StylePrefs>(),
+                concat!("Size of: ", stringify!(StaticPrefs))
+            );
+            assert_eq!(
+                ::std::mem::align_of::<StaticPrefs>(),
                 1usize,
-                concat!("Alignment of ", stringify!(StylePrefs))
-            );
-        }
-        impl Clone for StylePrefs {
+                concat!("Alignment of ", stringify!(StaticPrefs))
+            );
+        }
+        impl Clone for StaticPrefs {
             fn clone(&self) -> Self {
                 *self
             }
         }
         #[repr(C)]
         #[derive(Debug, Copy)]
         pub struct NonOwningAnimationTarget {
             pub mElement: *mut root::mozilla::dom::Element,
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -605,21 +605,21 @@ impl Expression {
                 if context.chrome_rules_enabled() ||
                     context.stylesheet_origin == Origin::UserAgent {
                     flags |= structs::nsMediaFeature_RequirementFlags_eUserAgentAndChromeOnly;
                 }
 
                 let result = {
                     let mut feature_name = &**ident;
 
-                    if unsafe { structs::StylePrefs_sWebkitPrefixedAliasesEnabled } &&
+                    if unsafe { structs::StaticPrefs_sVarCache_layout_css_prefixes_webkit } &&
                        starts_with_ignore_ascii_case(feature_name, "-webkit-") {
                         feature_name = &feature_name[8..];
                         flags |= structs::nsMediaFeature_RequirementFlags_eHasWebkitPrefix;
-                        if unsafe { structs::StylePrefs_sWebkitDevicePixelRatioEnabled } {
+                        if unsafe { structs::StaticPrefs_sVarCache_layout_css_prefixes_device_pixel_ratio_webkit } {
                             flags |= structs::nsMediaFeature_RequirementFlags_eWebkitDevicePixelRatioPrefEnabled;
                         }
                     }
 
                     let range = if starts_with_ignore_ascii_case(feature_name, "min-") {
                         feature_name = &feature_name[4..];
                         Range::Min
                     } else if starts_with_ignore_ascii_case(feature_name, "max-") {
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -174,17 +174,17 @@ impl NonTSPseudoClass {
 
     /// Returns whether the pseudo-class is enabled in content sheets.
     fn is_enabled_in_content(&self) -> bool {
         use gecko_bindings::structs::mozilla;
         match *self {
             // For pseudo-classes with pref, the availability in content
             // depends on the pref.
             NonTSPseudoClass::Fullscreen =>
-                unsafe { mozilla::StylePrefs_sUnprefixedFullscreenApiEnabled },
+                unsafe { mozilla::StaticPrefs_sVarCache_full_screen_api_unprefix_enabled },
             // Otherwise, a pseudo-class is enabled in content when it
             // doesn't have any enabled flag.
             _ => !self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
         }
     }
 
     /// <https://drafts.csswg.org/selectors-4/#useraction-pseudos>
     ///
--- a/servo/components/style/stylesheets/document_rule.rs
+++ b/servo/components/style/stylesheets/document_rule.rs
@@ -215,21 +215,21 @@ impl DocumentCondition {
     fn allowed_in(&self, context: &ParserContext) -> bool {
         use gecko_bindings::structs;
         use stylesheets::Origin;
 
         if context.stylesheet_origin != Origin::Author {
             return true;
         }
 
-        if unsafe { structs::StylePrefs_sMozDocumentEnabledInContent } {
+        if unsafe { structs::StaticPrefs_sVarCache_layout_css_moz_document_content_enabled } {
             return true;
         }
 
-        if !unsafe { structs::StylePrefs_sMozDocumentURLPrefixHackEnabled } {
+        if !unsafe { structs::StaticPrefs_sVarCache_layout_css_moz_document_url_prefix_hack_enabled } {
             return false;
         }
 
         // Allow a single url-prefix() for compatibility.
         //
         // See bug 1446470 and dependencies.
         if self.0.len() != 1 {
             return false;
--- a/servo/components/style/values/specified/grid.rs
+++ b/servo/components/style/values/specified/grid.rs
@@ -324,17 +324,17 @@ impl ToComputedValue for TrackList<Lengt
         }
     }
 }
 
 #[cfg(feature = "gecko")]
 #[inline]
 fn allow_grid_template_subgrids() -> bool {
     use gecko_bindings::structs::mozilla;
-    unsafe { mozilla::StylePrefs_sGridTemplateSubgridValueEnabled }
+    unsafe { mozilla::StaticPrefs_sVarCache_layout_css_grid_template_subgrid_value_enabled }
 }
 
 #[cfg(feature = "servo")]
 #[inline]
 fn allow_grid_template_subgrids() -> bool {
     false
 }
 
--- a/servo/components/style/values/specified/svg.rs
+++ b/servo/components/style/values/specified/svg.rs
@@ -23,17 +23,17 @@ pub type SVGPaint = generic::SVGPaint<RG
 pub type SVGPaintKind = generic::SVGPaintKind<RGBAColor, SpecifiedUrl>;
 
 #[cfg(feature = "gecko")]
 fn is_context_value_enabled() -> bool {
     // The prefs can only be mutated on the main thread, so it is safe
     // to read whenever we are on the main thread or the main thread is
     // blocked.
     use gecko_bindings::structs::mozilla;
-    unsafe { mozilla::StylePrefs_sOpentypeSVGEnabled }
+    unsafe { mozilla::StaticPrefs_sVarCache_gfx_font_rendering_opentype_svg_enabled }
 }
 #[cfg(not(feature = "gecko"))]
 fn is_context_value_enabled() -> bool {
     false
 }
 
 fn parse_context_value<'i, 't, T>(
     input: &mut Parser<'i, 't>,
--- a/servo/components/style/values/specified/transform.rs
+++ b/servo/components/style/values/specified/transform.rs
@@ -350,17 +350,17 @@ impl<S> OriginComponent<S> {
         OriginComponent::Length(LengthOrPercentage::Percentage(ComputedPercentage::zero()))
     }
 }
 
 #[cfg(feature = "gecko")]
 #[inline]
 fn allow_frames_timing() -> bool {
     use gecko_bindings::structs::mozilla;
-    unsafe { mozilla::StylePrefs_sFramesTimingFunctionEnabled }
+    unsafe { mozilla::StaticPrefs_sVarCache_layout_css_frames_timing_enabled }
 }
 
 #[cfg(feature = "servo")]
 #[inline]
 fn allow_frames_timing() -> bool {
     true
 }
 
--- a/servo/tests/html/test_canvas_drawimage_canvas.html
+++ b/servo/tests/html/test_canvas_drawimage_canvas.html
@@ -1,9 +1,9 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
 <html>
 <head>
   <meta charset="utf-8">
 </head>
 <style type=''>
 html {
   font-family: helvetica, sans-serif;
 }
@@ -125,17 +125,17 @@ imageSource = {
   height: 50
 };
 
 destCanvas = {
   width: 100,
   height: 50
 };
 
-drawImage(0, 0);
+    drawImage(0, 0);
 
 function renderExample(title) {
   var container = document.createElement('div');
   container.id = 'example' + examplesNum++;
   container.setAttribute('class', 'example');
 
   var h2 = document.createElement('h2');
   h2.textContent = title;
new file mode 100644
--- /dev/null
+++ b/servo/tests/html/test_canvas_drawimage_canvas_timed.html
@@ -0,0 +1,165 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8">
+</head>
+<style type=''>
+html {
+  font-family: helvetica, sans-serif;
+}
+
+canvas {
+  padding: 15px;
+  margin: 10px;
+}
+
+.example {
+  display: inline-block;
+  width: 280px;
+  margin-top: 50px;
+  margin-right: 50px;
+}
+
+.title {
+  text-align: center;
+}
+
+.description {
+  width: 100%;
+}
+
+.description div {
+  display: inline-block;
+  text-align: center;
+  font-size: 13px;
+}
+
+.description .source {
+  width: 100px;
+}
+
+.description .target {
+  width: 100px;
+}
+
+.example-title {
+  text-align: center;
+  font-size: 16px;
+}
+
+.error {
+  color: red;
+}
+
+</style>
+<body>
+    <h1 class="title">Timed DrawImage canvas to canvas</h1>
+    <h4 class="title", id="Test Result">This text is where the timing result will go...</h4>
+</body>
+
+<script type="text/javascript">
+
+var smoothingEnabled = false;
+var examplesNum = 0;
+
+var imageSource = {
+  width: 50,
+  height: 50
+};
+
+var destCanvas = {
+  width: 100,
+  height: 100
+};
+
+    var t0 = performance.now();     //save starting time
+    drawImage(25, 25);            // The source canvas will copied to the 25,25 position of the destination canvas
+    var t1 = performance.now();     //save ending time
+    document.getElementById('Test Result').innerHTML = "DrawImage took " + (t1 - t0) + " milliseconds."; //print to browser
+    console.log("DrawImage took " + (t1 - t0) + " milliseconds.");  //print to console
+
+function renderExample(title) {
+  var container = document.createElement('div');
+  container.id = 'example' + examplesNum++;
+  container.setAttribute('class', 'example');
+
+  var h2 = document.createElement('h2');
+  h2.textContent = title;
+  h2.setAttribute('class', 'example-title');
+  container.appendChild(h2);
+
+  var div1 = document.createElement('div');
+  var canvas1 = document.createElement('canvas');
+  var canvas2 = document.createElement('canvas');
+  div1.appendChild(canvas1);
+  div1.appendChild(canvas2);
+  container.appendChild(div1);
+
+  var div2 = document.createElement('div');
+  div2.setAttribute('class', 'description');
+  var source = document.createElement('div');
+  source.textContent = ' Source (' + imageSource.width + ',' + imageSource.height + ')';
+  source.setAttribute('class', 'source');
+
+  var arrow = document.createElement('div');
+  arrow.textContent = ' -> ';
+  arrow.setAttribute('class', 'arrow');
+
+  var target = document.createElement('div');
+  target.textContent = 'Target (' + destCanvas.width + ',' + destCanvas.height + ')';
+  target.setAttribute('class', 'target');
+
+  div2.appendChild(source);
+  div2.appendChild(arrow);
+  div2.appendChild(target);
+  container.appendChild(div2);
+
+  return container;
+}
+
+function drawImage() {
+  var args = Array.prototype.slice.call(arguments);
+
+  var div = renderExample('drawImage(' + args.toString() + ')');
+  var canvasEls = div.querySelectorAll('canvas');
+  var canvas1 = canvasEls[0];
+  var canvas2 = canvasEls[1];
+
+  canvas1.width = imageSource.width;
+  canvas1.height = imageSource.height;
+
+  var ctx1 = canvas1.getContext('2d');
+  ctx1.fillStyle = "#00FFFF";
+  ctx1.fillRect(0, 0, imageSource.width, imageSource.height);
+
+  ctx1.fillStyle = "#000000";
+  ctx1.fillRect(5,5,40,40);
+  ctx1.clearRect(10,10,30,30);
+  ctx1.strokeRect(15,15,20,20);
+
+  canvas2.width = destCanvas.width;
+  canvas2.height = destCanvas.height;
+
+  var ctx2 = canvas2.getContext('2d');
+  ctx2.fillStyle = "#FF0000";
+  ctx2.fillRect(0, 0,  destCanvas.width, destCanvas.height);
+  ctx2.imageSmoothingEnabled = smoothingEnabled;
+
+  args.unshift(canvas1);
+  try {
+    ctx2.drawImage.apply(ctx2, args);
+  }
+  catch(err) {
+    var title = div.querySelector('.example-title');
+    var error = document.createElement('h2');
+    error.setAttribute('class', 'example-title error');
+    div.insertBefore(error, title);
+    error.textContent += "Call Failed: " + err.message;
+  }
+
+  document.body.appendChild(div);
+};
+
+</script>
+</body>
+</html>
--- a/taskcluster/ci/docker-image/kind.yml
+++ b/taskcluster/ci/docker-image/kind.yml
@@ -29,16 +29,17 @@ jobs:
       BASE_TAG: '20171210'
       SNAPSHOT: '20171210T214726Z'
     packages:
       - deb7-gdb
       - deb7-git
       - deb7-make
       - deb7-mercurial
       - deb7-python
+      - deb7-python3.5
       - deb7-xz-utils
   toolchain-build:
     symbol: I(toolchain)
     parent: debian7-base
     packages:
       - deb7-cmake
       - deb7-ninja
   debian7-amd64-build:
--- a/taskcluster/ci/packages/kind.yml
+++ b/taskcluster/ci/packages/kind.yml
@@ -30,16 +30,28 @@ jobs:
     run:
       using: debian-package
       dsc:
         url: http://snapshot.debian.org/archive/debian/20160813T164221Z/pool/main/p/python2.7/python2.7_2.7.9-2+deb8u1.dsc
         sha256: 274c293e7156edf59cb9f0a9d8cedcd94fa801df35adf39b8a9f3d776a250ead
       patch: python-wheezy.diff
       pre-build-command: debian/rules control-file
 
+  deb7-python3.5:
+    description: "Python 3.5 backport for Debian wheezy"
+    treeherder:
+      symbol: Deb7(python3.5)
+    run:
+      using: debian-package
+      dsc:
+        url: http://snapshot.debian.org/archive/debian/20170119T211826Z/pool/main/p/python3.5/python3.5_3.5.3-1.dsc
+        sha256: 5259cbb15bb93f7bdfbe9ce03a972ea47f81c86057d5939ef9ce578414b2f1de
+      patch: python3.5-wheezy.diff
+      pre-build-command: debian/rules control-file
+
   deb7-cmake:
     description: "Cmake backport for Debian wheezy"
     treeherder:
       symbol: Deb7(cmake)
     run:
       using: debian-package
       dsc:
         url: http://snapshot.debian.org/archive/debian/20161204T034107Z/pool/main/c/cmake/cmake_3.7.1-1.dsc
--- a/taskcluster/docker/debian-base/Dockerfile
+++ b/taskcluster/docker/debian-base/Dockerfile
@@ -49,16 +49,17 @@ COPY cloud-mirror-workaround.sh /usr/loc
 RUN /usr/local/sbin/setup_packages.sh $DOCKER_IMAGE_PACKAGES && \
     echo 'dir::bin::methods::https "/usr/local/sbin/cloud-mirror-workaround.sh";' > /etc/apt/apt.conf.d/99cloud-mirror-workaround && \
     apt-get update && \
     apt-get install \
       git \
       make \
       mercurial \
       python \
+      python3.5 \
       xz-utils
 
 # %include testing/mozharness/external_tools/robustcheckout.py
 COPY topsrcdir/testing/mozharness/external_tools/robustcheckout.py /usr/local/mercurial/robustcheckout.py
 
 # %include taskcluster/docker/recipes/hgrc
 COPY topsrcdir/taskcluster/docker/recipes/hgrc /etc/mercurial/hgrc.d/mozilla.rc
 
--- a/taskcluster/scripts/misc/build-sccache.sh
+++ b/taskcluster/scripts/misc/build-sccache.sh
@@ -40,17 +40,17 @@ git checkout $SCCACHE_REVISION
 
 # Link openssl statically so we don't have to bother with different sonames
 # across Linux distributions.  We can't use the system openssl; see the sad
 # story in https://bugzilla.mozilla.org/show_bug.cgi?id=1163171#c26.
 case "$(uname -s)" in
 Linux)
     OPENSSL_TARBALL=openssl-1.1.0g.tar.gz
 
-    curl -O https://www.openssl.org/source/$OPENSSL_TARBALL
+    curl -L -O https://www.openssl.org/source/$OPENSSL_TARBALL
 cat >$OPENSSL_TARBALL.sha256sum <<EOF
 de4d501267da39310905cb6dc8c6121f7a2cad45a7707f76df828fe1b85073af  openssl-1.1.0g.tar.gz
 EOF
     cat $OPENSSL_TARBALL.sha256sum
     sha256sum -c $OPENSSL_TARBALL.sha256sum
 
     tar zxf $OPENSSL_TARBALL
 
--- a/testing/xpcshell/remotexpcshelltests.py
+++ b/testing/xpcshell/remotexpcshelltests.py
@@ -423,20 +423,16 @@ class XPCShellRemote(xpcshell.XPCShellTe
         local = os.path.join(self.localBin, "components/httpd.js")
         remoteFile = posixpath.join(self.remoteComponentsDir, "httpd.js")
         self.device.push(local, remoteFile)
 
         local = os.path.join(self.localBin, "components/httpd.manifest")
         remoteFile = posixpath.join(self.remoteComponentsDir, "httpd.manifest")
         self.device.push(local, remoteFile)
 
-        local = os.path.join(self.localBin, "components/test_necko.xpt")
-        remoteFile = posixpath.join(self.remoteComponentsDir, "test_necko.xpt")
-        self.device.push(local, remoteFile)
-
         if self.options['localAPK']:
             remoteFile = posixpath.join(self.remoteBinDir,
                                         os.path.basename(self.options['localAPK']))
             self.device.push(self.options['localAPK'], remoteFile)
 
         self.pushLibs()
 
     def pushLibs(self):
--- a/toolkit/components/extensions/test/xpcshell/test_ext_redirects.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_redirects.js
@@ -7,16 +7,17 @@ Cu.importGlobalProperties(["XMLHttpReque
 
 const server = createHttpServer();
 const gServerUrl = `http://localhost:${server.identity.primaryPort}`;
 
 server.registerPathHandler("/redirect", (request, response) => {
   let params = new URLSearchParams(request.queryString);
   response.setStatusLine(request.httpVersion, 302, "Moved Temporarily");
   response.setHeader("Location", params.get("redirect_uri"));
+  response.write("redirecting");
 });
 
 server.registerPathHandler("/dummy", (request, response) => {
   response.setStatusLine(request.httpVersion, 200, "OK");
   response.write("ok");
 });
 
 function onStopListener(channel) {
@@ -51,24 +52,26 @@ async function onModifyListener(originUr
     let channel = subject.QueryInterface(Ci.nsIHttpChannel);
     if (redirectToUrl) {
       channel.redirectTo(Services.io.newURI(redirectToUrl));
     }
     return channel;
   });
 }
 
-function getExtension(accessible = false, background = undefined) {
+function getExtension(accessible = false, background = undefined, blocking = true) {
   let manifest = {
     "permissions": [
       "webRequest",
-      "webRequestBlocking",
       "<all_urls>",
     ],
   };
+  if (blocking) {
+    manifest.permissions.push("webRequestBlocking");
+  }
   if (accessible) {
     manifest.web_accessible_resources = ["finished.html"];
   }
   if (!background) {
     background = () => {
       // send the extensions public uri to the test.
       let exturi = browser.extension.getURL("finished.html");
       browser.test.sendMessage("redirectURI", exturi);
@@ -175,16 +178,159 @@ add_task(async function test_content_cha
   let url = `${gServerUrl}/dummy?r=${Math.random()}`;
   onModifyListener(url, redirectUrl);
   let contentPage = await ExtensionTestUtils.loadContentPage(url, {redirectUrl});
   equal(contentPage.browser.documentURI.spec, redirectUrl, `expected redirect`);
   await contentPage.close();
   await extension.unload();
 });
 
+// This test makes a request against a server and tests redirect to another server page.
+add_task(async function test_extension_302_redirect_web() {
+  function background(serverUrl) {
+    let expectedUrls = ["/redirect", "/dummy"];
+    let expected = ["onBeforeRequest", "onHeadersReceived", "onBeforeRedirect",
+                    "onBeforeRequest", "onHeadersReceived", "onResponseStarted", "onCompleted"];
+    browser.webRequest.onBeforeRequest.addListener(details => {
+      browser.test.assertTrue(details.url.includes(expectedUrls.shift()), "onBeforeRequest url matches");
+      browser.test.assertEq(expected.shift(), "onBeforeRequest", "onBeforeRequest matches");
+    }, {urls: [serverUrl]});
+    browser.webRequest.onHeadersReceived.addListener(details => {
+      browser.test.assertEq(expected.shift(), "onHeadersReceived", "onHeadersReceived matches");
+    }, {urls: [serverUrl]});
+    browser.webRequest.onResponseStarted.addListener(details => {
+      browser.test.assertEq(expected.shift(), "onResponseStarted", "onResponseStarted matches");
+    }, {urls: [serverUrl]});
+    browser.webRequest.onBeforeRedirect.addListener(details => {
+      browser.test.assertTrue(details.redirectUrl.includes("/dummy"), "onBeforeRedirect matches redirectUrl");
+      browser.test.assertEq(expected.shift(), "onBeforeRedirect", "onBeforeRedirect matches");
+    }, {urls: [serverUrl]});
+    browser.webRequest.onCompleted.addListener(details => {
+      browser.test.assertTrue(details.url.includes("/dummy"), "onCompleted expected url received");
+      browser.test.assertEq(expected.shift(), "onCompleted", "onCompleted matches");
+      browser.test.notifyPass("requestCompleted");
+    }, {urls: [serverUrl]});
+    browser.webRequest.onErrorOccurred.addListener(details => {
+      browser.test.log(`onErrorOccurred ${JSON.stringify(details)}`);
+      browser.test.notifyFail("requestCompleted");
+    }, {urls: [serverUrl]});
+  }
+  let extension = getExtension(false, `(${background})("*://${server.identity.primaryHost}/*")`, false);
+  await extension.startup();
+  let redirectUrl = `${gServerUrl}/dummy`;
+  let completed = extension.awaitFinish("requestCompleted");
+  let url = `${gServerUrl}/redirect?r=${Math.random()}&redirect_uri=${redirectUrl}`;
+  let contentPage = await ExtensionTestUtils.loadContentPage(url, {redirectUrl});
+  equal(contentPage.browser.documentURI.spec, redirectUrl, `expected content redirect`);
+  await completed;
+  await contentPage.close();
+  await extension.unload();
+});
+
+// This test makes a request against a server and tests redirect to another server page, without
+// onBeforeRedirect.  Bug 1448599
+add_task(async function test_extension_302_redirect_opening() {
+  let redirectUrl = `${gServerUrl}/dummy`;
+  let expectData = [
+    {
+      event: "onBeforeRequest",
+      url: `${gServerUrl}/redirect`,
+    },
+    {
+      event: "onBeforeRequest",
+      url: redirectUrl,
+    },
+  ];
+  function background(serverUrl, expected) {
+    browser.webRequest.onBeforeRequest.addListener(details => {
+      let expect = expected.shift();
+      browser.test.assertEq(expect.event, "onBeforeRequest", "onBeforeRequest event matches");
+      browser.test.assertTrue(details.url.startsWith(expect.url), "onBeforeRequest url matches");
+      if (expected.length === 0) {
+        browser.test.notifyPass("requestCompleted");
+      }
+    }, {urls: [serverUrl]});
+  }
+  let extension = getExtension(false, `(${background})("*://${server.identity.primaryHost}/*", ${JSON.stringify(expectData)})`, false);
+  await extension.startup();
+  let completed = extension.awaitFinish("requestCompleted");
+  let url = `${gServerUrl}/redirect?r=${Math.random()}&redirect_uri=${redirectUrl}`;
+  let contentPage = await ExtensionTestUtils.loadContentPage(url, {redirectUrl});
+  equal(contentPage.browser.documentURI.spec, redirectUrl, `expected content redirect`);
+  await completed;
+  await contentPage.close();
+  await extension.unload();
+});
+
+// This test makes a request against a server and tests redirect to another server page, without
+// onBeforeRedirect.  Bug 1448599
+add_task(async function test_extension_302_redirect_modify() {
+  let redirectUrl = `${gServerUrl}/dummy`;
+  let expectData = [
+    {
+      event: "onHeadersReceived",
+      url: `${gServerUrl}/redirect`,
+    },
+    {
+      event: "onHeadersReceived",
+      url: redirectUrl,
+    },
+  ];
+  function background(serverUrl, expected) {
+    browser.webRequest.onHeadersReceived.addListener(details => {
+      let expect = expected.shift();
+      browser.test.assertEq(expect.event, "onHeadersReceived", "onHeadersReceived event matches");
+      browser.test.assertTrue(details.url.startsWith(expect.url), "onHeadersReceived url matches");
+      if (expected.length === 0) {
+        browser.test.notifyPass("requestCompleted");
+      }
+    }, {urls: ["<all_urls>"]});
+  }
+  let extension = getExtension(false, `(${background})("*://${server.identity.primaryHost}/*", ${JSON.stringify(expectData)})`, false);
+  await extension.startup();
+  let completed = extension.awaitFinish("requestCompleted");
+  let url = `${gServerUrl}/redirect?r=${Math.random()}&redirect_uri=${redirectUrl}`;
+  let contentPage = await ExtensionTestUtils.loadContentPage(url, {redirectUrl});
+  equal(contentPage.browser.documentURI.spec, redirectUrl, `expected content redirect`);
+  await completed;
+  await contentPage.close();
+  await extension.unload();
+});
+
+// This test makes a request against a server and tests redirect to another server page, without
+// onBeforeRedirect.  Bug 1448599
+add_task(async function test_extension_302_redirect_tracing() {
+  let redirectUrl = `${gServerUrl}/dummy`;
+  let expectData = [
+    {
+      event: "onCompleted",
+      url: redirectUrl,
+    },
+  ];
+  function background(serverUrl, expected) {
+    browser.webRequest.onCompleted.addListener(details => {
+      let expect = expected.shift();
+      browser.test.assertEq(expect.event, "onCompleted", "onCompleted event matches");
+      browser.test.assertTrue(details.url.startsWith(expect.url), "onCompleted url matches");
+      if (expected.length === 0) {
+        browser.test.notifyPass("requestCompleted");
+      }
+    }, {urls: [serverUrl]});
+  }
+  let extension = getExtension(false, `(${background})("*://${server.identity.primaryHost}/*", ${JSON.stringify(expectData)})`, false);
+  await extension.startup();
+  let completed = extension.awaitFinish("requestCompleted");
+  let url = `${gServerUrl}/redirect?r=${Math.random()}&redirect_uri=${redirectUrl}`;
+  let contentPage = await ExtensionTestUtils.loadContentPage(url, {redirectUrl});
+  equal(contentPage.browser.documentURI.spec, redirectUrl, `expected content redirect`);
+  await completed;
+  await contentPage.close();
+  await extension.unload();
+});
+
 // This test makes a request against a server and tests webrequest.  Currently
 // disabled due to NS_BINDING_ABORTED happening.
 add_task(async function test_extension_302_redirect() {
   let extension = getExtension(true, () => {
     let myuri = browser.extension.getURL("*");
     let exturi = browser.extension.getURL("finished.html");
     browser.webRequest.onBeforeRedirect.addListener(details => {
       browser.test.assertEq(details.redirectUrl, exturi, "redirect matches");
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -501,16 +501,17 @@ class AuthRequestor {
 
 HttpObserverManager = {
   openingInitialized: false,
   modifyInitialized: false,
   examineInitialized: false,
   redirectInitialized: false,
   activityInitialized: false,
   needTracing: false,
+  hasRedirects: false,
 
   listeners: {
     opening: new Map(),
     modify: new Map(),
     afterModify: new Map(),
     headersReceived: new Map(),
     authRequired: new Map(),
     onRedirect: new Map(),
@@ -581,17 +582,21 @@ HttpObserverManager = {
       Services.obs.addObserver(this, "http-on-examine-merged-response");
     } else if (!needExamine && this.examineInitialized) {
       this.examineInitialized = false;
       Services.obs.removeObserver(this, "http-on-examine-response");
       Services.obs.removeObserver(this, "http-on-examine-cached-response");
       Services.obs.removeObserver(this, "http-on-examine-merged-response");
     }
 
-    let needRedirect = this.listeners.onRedirect.size || haveBlocking;
+    // If we have any listeners, we need the channelsink so the channelwrapper is
+    // updated properly. Otherwise events for channels that are redirected will not
+    // happen correctly.  If we have no listeners, shut it down.
+    this.hasRedirects = this.listeners.onRedirect.size > 0;
+    let needRedirect = this.hasRedirects || needExamine || needOpening || needModify;
     if (needRedirect && !this.redirectInitialized) {
       this.redirectInitialized = true;
       ChannelEventSink.register();
     } else if (!needRedirect && this.redirectInitialized) {
       this.redirectInitialized = false;
       ChannelEventSink.unregister();
     }
 
@@ -912,17 +917,19 @@ HttpObserverManager = {
     }
   },
 
   onChannelReplaced(oldChannel, newChannel) {
     let channel = this.getWrapper(oldChannel);
 
     // We want originalURI, this will provide a moz-ext rather than jar or file
     // uri on redirects.
-    this.runChannelListener(channel, "onRedirect", {redirectUrl: newChannel.originalURI.spec});
+    if (this.hasRedirects) {
+      this.runChannelListener(channel, "onRedirect", {redirectUrl: newChannel.originalURI.spec});
+    }
     channel.channel = newChannel;
   },
 };
 
 var onBeforeRequest = {
   allowedOptions: ["blocking", "requestBody"],
 
   addListener(callback, filter = null, options = null, optionsObject = null) {
--- a/toolkit/mozapps/installer/packager.py
+++ b/toolkit/mozapps/installer/packager.py
@@ -180,19 +180,16 @@ class NoPkgFilesRemover(object):
         if not any(mozpath.match(path, spec) for spec in self._files):
             self._formatter.add(path, content)
         else:
             self._error(self._msg % path)
 
     def add_manifest(self, entry):
         self._formatter.add_manifest(entry)
 
-    def add_interfaces(self, path, content):
-        self._formatter.add_interfaces(path, content)
-
     def contains(self, path):
         return self._formatter.contains(path)
 
 
 def main():
     parser = ArgumentParser()
     parser.add_argument('-D', dest='defines', action='append',
                         metavar="VAR[=VAL]", help='Define a variable')
--- a/xpcom/components/ManifestParser.cpp
+++ b/xpcom/components/ManifestParser.cpp
@@ -75,20 +75,16 @@ static const ManifestDirective kParsingT
     "manifest",         1, false, false, true, true, false,
     &nsComponentManagerImpl::ManifestManifest, nullptr,
   },
   {
     "binary-component", 1, true, true, false, false, false,
     &nsComponentManagerImpl::ManifestBinaryComponent, nullptr,
   },
   {
-    "interfaces",       1, false, true, false, false, false,
-    &nsComponentManagerImpl::ManifestXPT, nullptr,
-  },
-  {
     "component",        2, false, true, false, false, false,
     &nsComponentManagerImpl::ManifestComponent, nullptr,
   },
   {
     "contract",         2, false, true, false, false, false,
     &nsComponentManagerImpl::ManifestContract, nullptr,
   },
   {
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -12,32 +12,29 @@
 
 #include "nsCategoryManager.h"
 #include "nsCOMPtr.h"
 #include "nsComponentManager.h"
 #include "nsDirectoryService.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsCategoryManager.h"
 #include "nsCategoryManagerUtils.h"
-#include "xptiprivate.h"
 #include "mozilla/MemoryReporting.h"
-#include "mozilla/XPTInterfaceInfoManager.h"
 #include "nsIConsoleService.h"
 #include "nsIObserverService.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIStringEnumerator.h"
 #include "nsXPCOM.h"
 #include "nsXPCOMPrivate.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIClassInfo.h"
 #include "nsLocalFile.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
 #include "prcmon.h"
-#include "xptinfo.h" // this after nsISupports, to pick up IID so that xpt stuff doesn't try to define it itself...
 #include "nsThreadUtils.h"
 #include "prthread.h"
 #include "private/pprthred.h"
 #include "nsTArray.h"
 #include "prio.h"
 #include "ManifestParser.h"
 #include "nsNetUtil.h"
 #include "mozilla/Services.h"
@@ -570,47 +567,16 @@ void
 nsComponentManagerImpl::ManifestBinaryComponent(ManifestProcessingContext& aCx,
                                                 int aLineNo,
                                                 char* const* aArgv)
 {
   LogMessageWithContext(aCx.mFile, aLineNo,
                         "Binary XPCOM components are no longer supported.");
 }
 
-static void
-DoRegisterXPT(FileLocation& aFile)
-{
-  uint32_t len;
-  FileLocation::Data data;
-  UniquePtr<char[]> buf;
-  nsresult rv = aFile.GetData(data);
-  if (NS_SUCCEEDED(rv)) {
-    rv = data.GetSize(&len);
-  }
-  if (NS_SUCCEEDED(rv)) {
-    buf = MakeUnique<char[]>(len);
-    rv = data.Copy(buf.get(), len);
-  }
-  if (NS_SUCCEEDED(rv)) {
-    XPTInterfaceInfoManager::GetSingleton()->RegisterBuffer(buf.get(), len);
-  } else {
-    nsCString uri;
-    aFile.GetURIString(uri);
-    LogMessage("Could not read '%s'.", uri.get());
-  }
-}
-
-void
-nsComponentManagerImpl::ManifestXPT(ManifestProcessingContext& aCx,
-                                    int aLineNo, char* const* aArgv)
-{
-  FileLocation f(aCx.mFile, aArgv[0]);
-  DoRegisterXPT(f);
-}
-
 void
 nsComponentManagerImpl::ManifestComponent(ManifestProcessingContext& aCx,
                                           int aLineNo, char* const* aArgv)
 {
   mLock.AssertNotCurrentThreadOwns();
 
   char* id = aArgv[0];
   char* file = aArgv[1];
--- a/xpcom/components/nsComponentManager.h
+++ b/xpcom/components/nsComponentManager.h
@@ -285,18 +285,16 @@ public:
     mozilla::FileLocation mFile;
     bool mChromeOnly;
   };
 
   void ManifestManifest(ManifestProcessingContext& aCx, int aLineNo,
                         char* const* aArgv);
   void ManifestBinaryComponent(ManifestProcessingContext& aCx, int aLineNo,
                                char* const* aArgv);
-  void ManifestXPT(ManifestProcessingContext& aCx, int aLineNo,
-                   char* const* aArgv);
   void ManifestComponent(ManifestProcessingContext& aCx, int aLineNo,
                          char* const* aArgv);
   void ManifestContract(ManifestProcessingContext& aCx, int aLineNo,
                         char* const* aArgv);
   void ManifestCategory(ManifestProcessingContext& aCx, int aLineNo,
                         char* const* aArgv);
 
   void RereadChromeManifests(bool aChromeOnly = true);
--- a/xpcom/reflect/xptinfo/XPTInterfaceInfoManager.h
+++ b/xpcom/reflect/xptinfo/XPTInterfaceInfoManager.h
@@ -12,18 +12,17 @@
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsDataHashtable.h"
 
 template<typename T> class nsCOMArray;
 class nsIMemoryReporter;
-struct XPTHeader;
-struct XPTInterfaceDirectoryEntry;
+struct XPTInterfaceDescriptor;
 class xptiInterfaceEntry;
 class xptiInterfaceInfo;
 class xptiTypelibGuts;
 
 namespace mozilla {
 
 class XPTInterfaceInfoManager final
     : public nsIInterfaceInfoManager
@@ -35,37 +34,33 @@ class XPTInterfaceInfoManager final
 
 public:
     // GetSingleton() is infallible
     static XPTInterfaceInfoManager* GetSingleton();
     static void FreeInterfaceInfoManager();
 
     void GetScriptableInterfaces(nsCOMArray<nsIInterfaceInfo>& aInterfaces);
 
-    void RegisterBuffer(char *buf, uint32_t length);
-
     static Mutex& GetResolveLock()
     {
         return GetSingleton()->mResolveLock;
     }
 
     xptiInterfaceEntry* GetInterfaceEntryForIID(const nsIID *iid);
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
 
 private:
     XPTInterfaceInfoManager();
     ~XPTInterfaceInfoManager();
 
     void InitMemoryReporter();
 
-    void RegisterXPTHeader(const XPTHeader* aHeader);
-
     // idx is the index of this interface in the XPTHeader
-    void VerifyAndAddEntryIfNew(const XPTInterfaceDirectoryEntry* iface,
+    void VerifyAndAddEntryIfNew(const XPTInterfaceDescriptor* iface,
                                 uint16_t idx,
                                 xptiTypelibGuts* typelib);
 
 private:
 
     class xptiWorkingSet
     {
     public:
--- a/xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp
+++ b/xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp
@@ -11,42 +11,42 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/XPTInterfaceInfoManager.h"
 #include "mozilla/PodOperations.h"
 #include "jsapi.h"
 
 using namespace mozilla;
 
 /* static */ xptiInterfaceEntry*
-xptiInterfaceEntry::Create(const XPTInterfaceDirectoryEntry* aEntry,
+xptiInterfaceEntry::Create(const XPTInterfaceDescriptor* aIface,
                            xptiTypelibGuts* aTypelib)
 {
     void* place = XPT_CALLOC8(gXPTIStructArena, sizeof(xptiInterfaceEntry));
     if (!place) {
         return nullptr;
     }
-    return new (place) xptiInterfaceEntry(aEntry, aTypelib);
+    return new (place) xptiInterfaceEntry(aIface, aTypelib);
 }
 
-xptiInterfaceEntry::xptiInterfaceEntry(const XPTInterfaceDirectoryEntry* aEntry,
+xptiInterfaceEntry::xptiInterfaceEntry(const XPTInterfaceDescriptor* aIface,
                                        xptiTypelibGuts* aTypelib)
-    : mIID(aEntry->mIID)
-    , mDescriptor(aEntry->mInterfaceDescriptor)
+    : mIID(aIface->mIID)
+    , mDescriptor(aIface)
     , mTypelib(aTypelib)
     , mParent(nullptr)
     , mInfo(nullptr)
     , mMethodBaseIndex(0)
     , mConstantBaseIndex(0)
     , mFlags(0)
-    , mName(aEntry->mName)
+    , mName(aIface->Name())
 {
     SetResolvedState(PARTIALLY_RESOLVED);
-    SetScriptableFlag(mDescriptor->IsScriptable());
-    SetBuiltinClassFlag(mDescriptor->IsBuiltinClass());
-    SetMainProcessScriptableOnlyFlag(mDescriptor->IsMainProcessScriptableOnly());
+    SetScriptableFlag(aIface->IsScriptable());
+    SetBuiltinClassFlag(aIface->IsBuiltinClass());
+    SetMainProcessScriptableOnlyFlag(aIface->IsMainProcessScriptableOnly());
 }
 
 bool
 xptiInterfaceEntry::Resolve()
 {
     MutexAutoLock lock(XPTInterfaceInfoManager::GetResolveLock());
     return ResolveLocked();
 }
@@ -80,17 +80,17 @@ xptiInterfaceEntry::ResolveLocked()
         }
 
         mParent = parent;
         if (parent->GetHasNotXPCOMFlag()) {
             SetHasNotXPCOMFlag();
         } else {
             for (uint16_t idx = 0; idx < mDescriptor->mNumMethods; ++idx) {
                 const nsXPTMethodInfo* method = static_cast<const nsXPTMethodInfo*>(
-                    mDescriptor->mMethodDescriptors + idx);
+                    &mDescriptor->Method(idx));
                 if (method->IsNotXPCOM()) {
                     SetHasNotXPCOMFlag();
                     break;
                 }
             }
         }
 
 
@@ -184,32 +184,32 @@ xptiInterfaceEntry::GetMethodInfo(uint16
     {
         NS_ERROR("bad param");
         *info = nullptr;
         return NS_ERROR_INVALID_ARG;
     }
 
     // else...
     *info = static_cast<const nsXPTMethodInfo*>
-        (&mDescriptor->mMethodDescriptors[index - mMethodBaseIndex]);
+        (&mDescriptor->Method(index - mMethodBaseIndex));
     return NS_OK;
 }
 
 nsresult
 xptiInterfaceEntry::GetMethodInfoForName(const char* methodName, uint16_t *index,
                                          const nsXPTMethodInfo** result)
 {
     if(!EnsureResolved())
         return NS_ERROR_UNEXPECTED;
 
     // This is a slow algorithm, but this is not expected to be called much.
     for(uint16_t i = 0; i < mDescriptor->mNumMethods; ++i)
     {
         const nsXPTMethodInfo* info;
-        info = static_cast<const nsXPTMethodInfo*>(&mDescriptor->mMethodDescriptors[i]);
+        info = static_cast<const nsXPTMethodInfo*>(&mDescriptor->Method(i));
         if (PL_strcmp(methodName, info->GetName()) == 0) {
             *index = i + mMethodBaseIndex;
             *result = info;
             return NS_OK;
         }
     }
 
     if(mParent)
@@ -234,17 +234,17 @@ xptiInterfaceEntry::GetConstant(uint16_t
 
     if(index >= mConstantBaseIndex +
                 mDescriptor->mNumConstants)
     {
         NS_PRECONDITION(0, "bad param");
         return NS_ERROR_INVALID_ARG;
     }
 
-    const auto& c = mDescriptor->mConstDescriptors[index - mConstantBaseIndex];
+    const auto& c = mDescriptor->Const(index - mConstantBaseIndex);
     AutoJSContext cx;
     JS::Rooted<JS::Value> v(cx);
     v.setUndefined();
 
     switch (c.mType.mPrefix.mFlags) {
       case nsXPTType::T_I16:
       {
         v.setInt32(c.mValue.i16);
@@ -267,17 +267,17 @@ xptiInterfaceEntry::GetConstant(uint16_t
       }
       default:
       {
         MOZ_ASSERT(false, "Invalid constant type found in interface");
       }
     }
 
     constant.set(v);
-    *name = ToNewCString(nsDependentCString(c.mName));
+    *name = ToNewCString(nsDependentCString(c.Name()));
 
     return NS_OK;
 }
 
 // this is a private helper
 
 nsresult
 xptiInterfaceEntry::GetInterfaceIndexForParam(uint16_t methodIndex,
@@ -296,17 +296,17 @@ xptiInterfaceEntry::GetInterfaceIndexFor
     {
         NS_ERROR("bad param");
         return NS_ERROR_INVALID_ARG;
     }
 
     const XPTTypeDescriptor *td = &param->mType;
 
     while (td->Tag() == TD_ARRAY) {
-        td = td->ArrayElementType(mDescriptor);
+        td = td->ArrayElementType();
     }
 
     if (td->Tag() != TD_INTERFACE_TYPE) {
         NS_ERROR("not an interface");
         return NS_ERROR_INVALID_ARG;
     }
     *interfaceIndex = td->InterfaceIndex();
     return NS_OK;
@@ -436,17 +436,17 @@ xptiInterfaceEntry::GetTypeInArray(const
 
     const XPTTypeDescriptor *td = &param->mType;
 
     for (uint16_t i = 0; i < dimension; i++) {
         if (td->Tag() != TD_ARRAY) {
             NS_ERROR("bad dimension");
             return NS_ERROR_INVALID_ARG;
         }
-        td = td->ArrayElementType(mDescriptor);
+        td = td->ArrayElementType();
     }
 
     *type = td;
     return NS_OK;
 }
 
 nsresult
 xptiInterfaceEntry::GetTypeForParam(uint16_t methodIndex,
@@ -544,17 +544,17 @@ xptiInterfaceEntry::GetInterfaceIsArgNum
     {
         NS_ERROR("bad index");
         return NS_ERROR_INVALID_ARG;
     }
 
     const XPTTypeDescriptor *td = &param->mType;
 
     while (td->Tag() == TD_ARRAY) {
-        td = td->ArrayElementType(mDescriptor);
+        td = td->ArrayElementType();
     }
 
     if (td->Tag() != TD_INTERFACE_IS_TYPE) {
         NS_ERROR("not an iid_is");
         return NS_ERROR_INVALID_ARG;
     }
 
     *argnum = td->ArgNum();
--- a/xpcom/reflect/xptinfo/xptiInterfaceInfoManager.cpp
+++ b/xpcom/reflect/xptinfo/xptiInterfaceInfoManager.cpp
@@ -76,16 +76,22 @@ XPTInterfaceInfoManager::FreeInterfaceIn
 {
     gInterfaceInfoManager = nullptr;
 }
 
 XPTInterfaceInfoManager::XPTInterfaceInfoManager()
     :   mWorkingSet(),
         mResolveLock("XPTInterfaceInfoManager.mResolveLock")
 {
+    xptiTypelibGuts* typelib = xptiTypelibGuts::Create();
+
+    ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
+    for (uint16_t k = 0; k < XPTHeader::kNumInterfaces; k++) {
+        VerifyAndAddEntryIfNew(XPTHeader::kInterfaces + k, k, typelib);
+    }
 }
 
 XPTInterfaceInfoManager::~XPTInterfaceInfoManager()
 {
     // We only do this on shutdown of the service.
     mWorkingSet.InvalidateInterfaceInfos();
 
     UnregisterWeakMemoryReporter(this);
@@ -93,62 +99,33 @@ XPTInterfaceInfoManager::~XPTInterfaceIn
 
 void
 XPTInterfaceInfoManager::InitMemoryReporter()
 {
     RegisterWeakMemoryReporter(this);
 }
 
 void
-XPTInterfaceInfoManager::RegisterBuffer(char *buf, uint32_t length)
-{
-    XPTState state;
-    XPT_InitXDRState(&state, buf, length);
-
-    XPTCursor curs;
-    NotNull<XPTCursor*> cursor = WrapNotNull(&curs);
-    if (!XPT_MakeCursor(&state, XPT_HEADER, 0, cursor)) {
-        return;
-    }
-
-    XPTHeader *header = nullptr;
-    if (XPT_DoHeader(gXPTIStructArena, cursor, &header)) {
-        RegisterXPTHeader(header);
-    }
-}
-
-void
-XPTInterfaceInfoManager::RegisterXPTHeader(const XPTHeader* aHeader)
-{
-    if (aHeader->mMajorVersion >= XPT_MAJOR_INCOMPATIBLE_VERSION) {
-        MOZ_ASSERT(!aHeader->mNumInterfaces, "bad libxpt");
-    }
-
-    xptiTypelibGuts* typelib = xptiTypelibGuts::Create(aHeader);
-
-    ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
-    for(uint16_t k = 0; k < aHeader->mNumInterfaces; k++)
-        VerifyAndAddEntryIfNew(aHeader->mInterfaceDirectory + k, k, typelib);
-}
-
-void
-XPTInterfaceInfoManager::VerifyAndAddEntryIfNew(const XPTInterfaceDirectoryEntry* iface,
+XPTInterfaceInfoManager::VerifyAndAddEntryIfNew(const XPTInterfaceDescriptor* iface,
                                                 uint16_t idx,
                                                 xptiTypelibGuts* typelib)
 {
-    if (!iface->mInterfaceDescriptor)
+    static const nsID zeroIID =
+        { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } };
+
+    if (iface->mIID.Equals(zeroIID)) {
         return;
+    }
 
     // The number of maximum methods is not arbitrary. It is the same value as
     // in xpcom/reflect/xptcall/genstubs.pl; do not change this value
     // without changing that one or you WILL see problems.
-    if (iface->mInterfaceDescriptor->mNumMethods > 250 &&
-            !iface->mInterfaceDescriptor->IsBuiltinClass()) {
+    if (iface->mNumMethods > 250 && !iface->IsBuiltinClass()) {
         NS_ASSERTION(0, "Too many methods to handle for the stub, cannot load");
-        fprintf(stderr, "ignoring too large interface: %s\n", iface->mName);
+        fprintf(stderr, "ignoring too large interface: %s\n", iface->Name());
         return;
     }
 
     mWorkingSet.mTableReentrantMonitor.AssertCurrentThreadIn();
     xptiInterfaceEntry* entry = mWorkingSet.mIIDTable.Get(iface->mIID);
     if (entry) {
         // XXX validate this info to find possible inconsistencies
         return;
--- a/xpcom/reflect/xptinfo/xptiTypelibGuts.cpp
+++ b/xpcom/reflect/xptinfo/xptiTypelibGuts.cpp
@@ -15,61 +15,58 @@ using namespace mozilla;
 template <class T>
 class MOZ_NEEDS_NO_VTABLE_TYPE CheckNoVTable
 {
 };
 CheckNoVTable<xptiTypelibGuts> gChecker;
 
 // static
 xptiTypelibGuts*
-xptiTypelibGuts::Create(const XPTHeader* aHeader)
+xptiTypelibGuts::Create()
 {
-    NS_ASSERTION(aHeader, "bad param");
     size_t n = sizeof(xptiTypelibGuts) +
-               sizeof(xptiInterfaceEntry*) * (aHeader->mNumInterfaces - 1);
+               sizeof(xptiInterfaceEntry*) * (XPTHeader::kNumInterfaces - 1);
     void* place = XPT_CALLOC8(gXPTIStructArena, n);
     if (!place)
         return nullptr;
-    return new(place) xptiTypelibGuts(aHeader);
+    return new(place) xptiTypelibGuts();
 }
 
 xptiInterfaceEntry*
 xptiTypelibGuts::GetEntryAt(uint16_t i)
 {
     static const nsID zeroIID =
         { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } };
 
-    NS_ASSERTION(mHeader, "bad state");
     NS_ASSERTION(i < GetEntryCount(), "bad index");
 
     xptiInterfaceEntry* r = mEntryArray[i];
     if (r)
         return r;
 
-    const XPTInterfaceDirectoryEntry* iface = mHeader->mInterfaceDirectory + i;
+    const XPTInterfaceDescriptor* iface = XPTHeader::kInterfaces + i;
 
     XPTInterfaceInfoManager::xptiWorkingSet& set =
         XPTInterfaceInfoManager::GetSingleton()->mWorkingSet;
 
     {
         ReentrantMonitorAutoEnter monitor(set.mTableReentrantMonitor);
         if (iface->mIID.Equals(zeroIID))
-            r = set.mNameTable.Get(iface->mName);
+            r = set.mNameTable.Get(iface->Name());
         else
             r = set.mIIDTable.Get(iface->mIID);
     }
 
     if (r)
         SetEntryAt(i, r);
 
     return r;
 }
 
 const char*
 xptiTypelibGuts::GetEntryNameAt(uint16_t i)
 {
-    NS_ASSERTION(mHeader, "bad state");
     NS_ASSERTION(i < GetEntryCount(), "bad index");
 
-    const XPTInterfaceDirectoryEntry* iface = mHeader->mInterfaceDirectory + i;
+    const XPTInterfaceDescriptor* iface = XPTHeader::kInterfaces + i;
 
-    return iface->mName;
+    return iface->Name();
 }
--- a/xpcom/reflect/xptinfo/xptinfo.h
+++ b/xpcom/reflect/xptinfo/xptinfo.h
@@ -189,21 +189,21 @@ public:
         {*(XPTMethodDescriptor*)this = desc;}
 
     bool IsGetter() const { return !!(mFlags & kGetterMask); }
     bool IsSetter() const { return !!(mFlags & kSetterMask); }
     bool IsNotXPCOM() const { return !!(mFlags & kNotXPCOMMask); }
     bool IsHidden() const { return !!(mFlags & kHiddenMask); }
     bool WantsOptArgc() const { return !!(mFlags & kOptArgcMask); }
     bool WantsContext() const { return !!(mFlags & kContextMask); }
-    const char* GetName() const { return mName; }
+    const char* GetName() const { return Name(); }
     uint8_t GetParamCount() const { return mNumArgs; }
     const nsXPTParamInfo GetParam(uint8_t idx) const {
         MOZ_ASSERT(idx < GetParamCount(), "bad arg");
-        return mParams[idx];
+        return Param(idx);
     }
 
 private:
     static const uint8_t kGetterMask =   0x80;
     static const uint8_t kSetterMask =   0x40;
     static const uint8_t kNotXPCOMMask = 0x20;
     static const uint8_t kHiddenMask =   0x08;
     static const uint8_t kOptArgcMask =  0x04;
--- a/xpcom/reflect/xptinfo/xptiprivate.h
+++ b/xpcom/reflect/xptinfo/xptiprivate.h
@@ -10,17 +10,16 @@
 
 #include "nscore.h"
 #include <new>
 #include "nsISupports.h"
 
 // this after nsISupports, to pick up IID
 // so that xpt stuff doesn't try to define it itself...
 #include "xpt_struct.h"
-#include "xpt_xdr.h"
 
 #include "nsIInterfaceInfo.h"
 #include "nsIInterfaceInfoManager.h"
 #include "xptinfo.h"
 #include "ShimInterfaceInfo.h"
 
 #include "nsIServiceManager.h"
 #include "nsIFile.h"
@@ -57,51 +56,48 @@
 #include <stdarg.h>
 
 /***************************************************************************/
 
 class xptiInterfaceInfo;
 class xptiInterfaceEntry;
 class xptiTypelibGuts;
 
+struct XPTArena;
 extern XPTArena* gXPTIStructArena;
 
 /***************************************************************************/
 
 /***************************************************************************/
 
 // No virtuals.
 // These are always constructed in the struct arena using placement new.
 // dtor need not be called.
 
 class xptiTypelibGuts
 {
 public:
-    static xptiTypelibGuts* Create(const XPTHeader* aHeader);
+    static xptiTypelibGuts* Create();
 
-    uint16_t GetEntryCount() const {return mHeader->mNumInterfaces;}
+    uint16_t GetEntryCount() const { return XPTHeader::kNumInterfaces; }
 
     void                SetEntryAt(uint16_t i, xptiInterfaceEntry* ptr)
     {
-        NS_ASSERTION(mHeader,"bad state!");
         NS_ASSERTION(i < GetEntryCount(),"bad param!");
         mEntryArray[i] = ptr;
     }
 
     xptiInterfaceEntry* GetEntryAt(uint16_t i);
     const char* GetEntryNameAt(uint16_t i);
 
 private:
-    explicit xptiTypelibGuts(const XPTHeader* aHeader)
-        : mHeader(aHeader)
-    { }
+    xptiTypelibGuts() = default;
     ~xptiTypelibGuts();
 
 private:
-    const XPTHeader*     mHeader;        // hold pointer into arena
     xptiInterfaceEntry*  mEntryArray[1]; // Always last. Sized to fit.
 };
 
 /***************************************************************************/
 
 /***************************************************************************/
 
 // This class exists to help xptiInterfaceInfo store a 4-state (2 bit) value
@@ -146,17 +142,17 @@ private:
 
 // No virtual methods.
 // We always create in the struct arena and construct using "placement new".
 // No members need dtor calls.
 
 class xptiInterfaceEntry
 {
 public:
-    static xptiInterfaceEntry* Create(const XPTInterfaceDirectoryEntry* aEntry,
+    static xptiInterfaceEntry* Create(const XPTInterfaceDescriptor* aIface,
                                       xptiTypelibGuts* aTypelib);
 
     enum {
         PARTIALLY_RESOLVED    = 1,
         FULLY_RESOLVED        = 2,
         RESOLVE_FAILED        = 3
     };
 
@@ -242,17 +238,17 @@ public:
     nsresult IsIID(const nsIID * IID, bool *_retval);
     nsresult GetNameShared(const char **name);
     nsresult GetIIDShared(const nsIID * *iid);
     nsresult IsFunction(bool *_retval);
     nsresult HasAncestor(const nsIID * iid, bool *_retval);
     nsresult GetIIDForParamNoAlloc(uint16_t methodIndex, const nsXPTParamInfo * param, nsIID *iid);
 
 private:
-    xptiInterfaceEntry(const XPTInterfaceDirectoryEntry* aDescriptor,
+    xptiInterfaceEntry(const XPTInterfaceDescriptor* aIface,
                        xptiTypelibGuts* aTypelib);
     ~xptiInterfaceEntry();
 
     void SetResolvedState(int state)
         {mFlags.SetState(uint8_t(state));}
 
     bool Resolve();
 
--- a/xpcom/tests/moz.build
+++ b/xpcom/tests/moz.build
@@ -25,29 +25,23 @@ test_progs = [
 SimplePrograms(test_progs)
 
 USE_LIBS += ['mfbt']
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 
 if CONFIG['COMPILE_ENVIRONMENT']:
     TEST_HARNESS_FILES.xpcshell.xpcom.tests.unit += [
-        '!/dist/bin/components/xpcomtest.xpt',
-    ]
-    TEST_HARNESS_FILES.xpcshell.xpcom.tests.unit += [
         '!%s%s' % (f, CONFIG['BIN_SUFFIX']) for f in test_progs
     ]
 
 XPIDL_MODULE = 'xpcomtest'
 XPIDL_SOURCES += [
     'NotXPCOMTest.idl',
 ]
 
-# Don't add our test-only .xpt files to the normal manifests
-XPIDL_NO_MANIFEST = True
-
 LOCAL_INCLUDES += [
     '../ds',
 ]
 
 RESOURCE_FILES += [
     'test.properties',
 ]
--- a/xpcom/typelib/xpt/moz.build
+++ b/xpcom/typelib/xpt/moz.build
@@ -5,24 +5,25 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 Library('xpt')
 
 DIRS += ['tools']
 
 UNIFIED_SOURCES += [
     'xpt_arena.cpp',
-    'xpt_struct.cpp',
-    'xpt_xdr.cpp',
+]
+
+SOURCES += [
+    '!XPTInfo.cpp',
 ]
 
 EXPORTS += [
     'xpt_arena.h',
     'xpt_struct.h',
-    'xpt_xdr.h',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '!/xpcom/base',
     '/xpcom/base',
 ]
--- a/xpcom/typelib/xpt/tools/xpt.py
+++ b/xpcom/typelib/xpt/tools/xpt.py
@@ -29,19 +29,19 @@
 # are those of the authors and should not be interpreted as representing
 # official policies, either expressed or implied, of the Mozilla
 # Foundation.
 
 """
 A module for working with XPCOM Type Libraries.
 
 The XPCOM Type Library File Format is described at:
-http://www.mozilla.org/scriptable/typelib_file.html . It is used
-to provide type information for calling methods on XPCOM objects
-from scripting languages such as JavaScript.
+  https://www-archive.mozilla.org/scriptable/typelib_file.html
+It is used to provide type information for calling methods on XPCOM
+objects from scripting languages such as JavaScript.
 
 This module provides a set of classes representing the parts of
 a typelib in a high-level manner, as well as methods for reading
 and writing them from files.
 
 The usable public interfaces are currently:
 Typelib.read(input_file) - read a typelib from a file on disk or file-like
                            object, return a Typelib object.
@@ -129,16 +129,133 @@ class IndexedList(object):
 
     def __getitem__(self, index):
         return self._list[index]
 
     def __len__(self):
         return len(self._list)
 
 
+class CodeGenData(object):
+    """
+    This stores the top-level data needed to generate XPT information in C++.
+    |methods| and |constants| are the top-level declarations in the module.
+    These contain names, and so are not likely to benefit from deduplication.
+    |params| are the lists of parameters for |methods|, stored concatenated.
+    These are deduplicated if there are only a few. |types| and |strings| are
+    side data stores for the other things, and are deduplicated.
+
+    """
+
+    def __init__(self):
+        self.interfaces = []
+
+        self.types = []
+        self.type_indexes = {}
+
+        self.params = []
+        self.params_indexes = {}
+
+        self.methods = []
+
+        self.constants = []
+
+        self.strings = []
+        self.string_indexes = {}
+        self.curr_string_index = 0
+
+    @staticmethod
+    def write_array_body(fd, iterator):
+        fd.write("{\n")
+        for s in iterator:
+            fd.write("  %s,\n" % s)
+        fd.write("};\n\n")
+
+    def finish(self, fd):
+        fd.write("const uint16_t XPTHeader::kNumInterfaces = %s;\n\n" % len(self.interfaces))
+
+        fd.write("const XPTInterfaceDescriptor XPTHeader::kInterfaces[] = ")
+        CodeGenData.write_array_body(fd, self.interfaces)
+
+        fd.write("const XPTTypeDescriptor XPTHeader::kTypes[] = ")
+        CodeGenData.write_array_body(fd, self.types)
+
+        fd.write("const XPTParamDescriptor XPTHeader::kParams[] = ")
+        CodeGenData.write_array_body(fd, self.params)
+
+        fd.write("const XPTMethodDescriptor XPTHeader::kMethods[] = ")
+        CodeGenData.write_array_body(fd, self.methods)
+
+        fd.write("const XPTConstDescriptor XPTHeader::kConsts[] = ")
+        CodeGenData.write_array_body(fd, self.constants)
+
+        fd.write("const char XPTHeader::kStrings[] = {\n")
+        if self.strings:
+            for s in self.strings:
+                # Store each string as individual characters to work around
+                # MSVC's limit of 65k characters for a single string literal
+                # (error C1091).
+                s_index = self.string_indexes[s]
+                fd.write("    '%s', '\\0', // %s %d\n" % ("', '".join(list(s)), s, s_index))
+        else:
+            fd.write('""')
+        fd.write('};\n\n')
+
+    def add_interface(self, new_interface):
+        assert new_interface
+        self.interfaces.append(new_interface)
+
+    def add_type(self, new_type):
+        assert isinstance(new_type, basestring)
+        if new_type in self.type_indexes:
+            return self.type_indexes[new_type]
+        index = len(self.types)
+        self.types.append(new_type)
+        self.type_indexes[new_type] = index
+        return index
+
+    def add_params(self, new_params):
+        # Always represent empty parameter lists as being at 0, for no
+        # particular reason beside it being nicer.
+        if len(new_params) == 0:
+            return 0
+
+        index = len(self.params)
+        # The limit of 4 here is fairly arbitrary. The idea is to not
+        # spend time adding large things to the cache that have little
+        # chance of getting used again.
+        if len(new_params) <= 4:
+            params_key = "".join(new_params)
+            if params_key in self.params_indexes:
+                return self.params_indexes[params_key]
+            else:
+                self.params_indexes[params_key] = index
+        self.params += new_params
+        return index
+
+    def add_methods(self, new_methods):
+        index = len(self.methods)
+        self.methods += new_methods
+        return index
+
+    def add_constants(self, new_constants):
+        index = len(self.constants)
+        self.constants += new_constants
+        return index
+
+    def add_string(self, new_string):
+        if new_string in self.string_indexes:
+            return self.string_indexes[new_string]
+        index = self.curr_string_index
+        self.strings.append(new_string)
+        self.string_indexes[new_string] = index
+        self.curr_string_index += len(new_string) + 1
+        return index
+
+
 # Descriptor types as described in the spec
 class Type(object):
     """
     Data type of a method parameter or return value. Do not instantiate
     this class directly. Rather, use one of its subclasses.
 
     """
     _prefixdescriptor = struct.Struct(">B")
@@ -258,16 +375,23 @@ class Type(object):
         Write a TypeDescriptor to |file|, which is assumed
         to be seeked to the proper position. For types other than
         SimpleType, this is not sufficient for writing the TypeDescriptor,
         and the subclass method must be called.
 
         """
         file.write(Type._prefixdescriptor.pack(self.encodeflags() | self.tag))
 
+    def typeDescriptorPrefixString(self):
+        """
+        Return a string for the C++ code to represent the XPTTypeDescriptorPrefix.
+
+        """
+        return "{0x%x}" % (self.encodeflags() | self.tag)
+
 
 class SimpleType(Type):
     """
     A simple data type. (SimpleTypeDescriptor from the typelib specification.)
 
     """
     _cache = {}
 
@@ -306,16 +430,19 @@ class SimpleType(Type):
 
         if self.pointer:
             if self.reference:
                 s += " &"
             else:
                 s += " *"
         return s
 
+    def code_gen(self, typelib, cd):
+        return "{%s, 0, 0}" % self.typeDescriptorPrefixString()
+
 
 class InterfaceType(Type):
     """
     A type representing a pointer to an IDL-defined interface.
     (InterfaceTypeDescriptor from the typelib specification.)
 
     """
     _descriptor = struct.Struct(">H")
@@ -361,16 +488,22 @@ class InterfaceType(Type):
         Write an InterfaceTypeDescriptor to |file|, which is assumed
         to be seeked to the proper position.
 
         """
         Type.write(self, typelib, file)
         # write out the interface index (1-based)
         file.write(InterfaceType._descriptor.pack(typelib.interfaces.index(self.iface) + 1))
 
+    def code_gen(self, typelib, cd):
+        index = typelib.interfaces.index(self.iface) + 1
+        hi = int(index / 256)
+        lo = index - (hi * 256)
+        return "{%s, %d, %d}" % (self.typeDescriptorPrefixString(), hi, lo)
+
     def __str__(self):
         if self.iface:
             return self.iface.name
         return "unknown interface"
 
 
 class InterfaceIsType(Type):
     """
@@ -420,16 +553,20 @@ class InterfaceIsType(Type):
         """
         Write an InterfaceIsTypeDescriptor to |file|, which is assumed
         to be seeked to the proper position.
 
         """
         Type.write(self, typelib, file)
         file.write(InterfaceIsType._descriptor.pack(self.param_index))
 
+    def code_gen(self, typelib, cd):
+        return "{%s, %d, 0}" % (self.typeDescriptorPrefixString(),
+                                self.param_index)
+
     def __str__(self):
         return "InterfaceIs *"
 
 
 class ArrayType(Type):
     """
     A type representing an Array of elements of another type, whose
     size and length are passed as separate parameters to a method.
@@ -480,16 +617,22 @@ class ArrayType(Type):
         to be seeked to the proper position.
 
         """
         Type.write(self, typelib, file)
         file.write(ArrayType._descriptor.pack(self.size_is_arg_num,
                                               self.length_is_arg_num))
         self.element_type.write(typelib, file)
 
+    def code_gen(self, typelib, cd):
+        element_type_index = cd.add_type(self.element_type.code_gen(typelib, cd))
+        return "{%s, %d, %d}" % (self.typeDescriptorPrefixString(),
+                                 self.size_is_arg_num,
+                                 element_type_index)
+
     def __str__(self):
         return "%s []" % str(self.element_type)
 
 
 class StringWithSizeType(Type):
     """
     A type representing a UTF-8 encoded string whose size and length
     are passed as separate arguments to a method. (StringWithSizeTypeDescriptor
@@ -536,16 +679,20 @@ class StringWithSizeType(Type):
         Write a StringWithSizeTypeDescriptor to |file|, which is assumed
         to be seeked to the proper position.
 
         """
         Type.write(self, typelib, file)
         file.write(StringWithSizeType._descriptor.pack(self.size_is_arg_num,
                                                        self.length_is_arg_num))
 
+    def code_gen(self, typelib, cd):
+        return "{%s, %d, 0}" % (self.typeDescriptorPrefixString(),
+                                self.size_is_arg_num)
+
     def __str__(self):
         return "string_s"
 
 
 class WideStringWithSizeType(Type):
     """
     A type representing a UTF-16 encoded string whose size and length
     are passed as separate arguments to a method.
@@ -592,16 +739,20 @@ class WideStringWithSizeType(Type):
         Write a WideStringWithSizeTypeDescriptor to |file|, which is assumed
         to be seeked to the proper position.
 
         """
         Type.write(self, typelib, file)
         file.write(WideStringWithSizeType._descriptor.pack(self.size_is_arg_num,
                                                            self.length_is_arg_num))
 
+    def code_gen(self, typelib, cd):
+        return "{%s, %d, 0}" % (self.typeDescriptorPrefixString(),
+                                self.size_is_arg_num)
+
     def __str__(self):
         return "wstring_s"
 
 
 class CachedStringWriter(object):
     """
     A cache that sits in front of a file to avoid adding the same
     string multiple times.
@@ -720,16 +871,19 @@ class Param(object):
         """
         Write a ParamDescriptor to |file|, which is assumed to be seeked
         to the correct position.
 
         """
         file.write(Param._descriptorstart.pack(self.encodeflags()))
         self.type.write(typelib, file)
 
+    def code_gen(self, typelib, cd):
+        return "{0x%x, %s}" % (self.encodeflags(), self.type.code_gen(typelib, cd))
+
     def prefix(self):
         """
         Return a human-readable string representing the flags set
         on this Param.
 
         """
         s = ""
         if self.out:
@@ -900,29 +1054,48 @@ class Method(object):
         """
         Write this method's name to |string_writer|'s file.
         Assumes that this file is currently seeked to an unused
         portion of the data pool.
 
         """
         self._name_offset = string_writer.write(self.name)
 
+    def code_gen(self, typelib, cd):
+        # Don't store any extra info for methods that can't be called from JS.
+        if self.notxpcom or self.hidden:
+            string_index = 0
+            param_index = 0
+            num_params = 0
+        else:
+            string_index = cd.add_string(self.name)
+            param_index = cd.add_params([p.code_gen(typelib, cd) for p in self.params])
+            num_params = len(self.params)
+
+        return "{%d, %d, 0x%x, %d}" % (string_index,
+                                       param_index,
+                                       self.encodeflags(),
+                                       num_params)
 
 class Constant(object):
     """
     A constant value of a specific type defined on an interface.
     (ConstantDescriptor from the typelib specification.)
 
     """
     _descriptorstart = struct.Struct(">I")
     # Actual value is restricted to this set of types
     typemap = {Type.Tags.int16: '>h',
                Type.Tags.uint16: '>H',
                Type.Tags.int32: '>i',
                Type.Tags.uint32: '>I'}
+    memberTypeMap = {Type.Tags.int16: 'int16_t',
+                     Type.Tags.uint16: 'uint16_t',
+                     Type.Tags.int32: 'int32_t',
+                     Type.Tags.uint32: 'uint32_t'}
 
     def __init__(self, name, type, value):
         self.name = name
         self._name_offset = 0
         self.type = type
         self.value = value
 
     def __cmp__(self, other):
@@ -971,16 +1144,25 @@ class Constant(object):
         """
         Write this constants's name to |string_writer|'s file.
         Assumes that this file is currently seeked to an unused
         portion of the data pool.
 
         """
         self._name_offset = string_writer.write(self.name)
 
+    def code_gen(self, typelib, cd):
+        string_index = cd.add_string(self.name)
+
+        # The static cast is needed for disambiguation.
+        return "{%d, %s, XPTConstValue(static_cast<%s>(%d))}" % (string_index,
+                                                                 self.type.code_gen(typelib, cd),
+                                                                 Constant.memberTypeMap[self.type.tag],
+                                                                 self.value)
+
     def __repr__(self):
         return "Constant(%s, %s, %d)" % (self.name, str(self.type), self.value)
 
 
 class Interface(object):
     """
     An Interface represents an object, with its associated methods
     and constant values.
@@ -1166,16 +1348,45 @@ class Interface(object):
         """
         self._name_offset = string_writer.write(self.name)
         self._namespace_offset = string_writer.write(self.namespace)
         for m in self.methods:
             m.write_name(string_writer)
         for c in self.constants:
             c.write_name(string_writer)
 
+    def code_gen_interface(self, typelib, cd):
+        iid = Typelib.code_gen_iid(self.iid)
+        string_index = cd.add_string(self.name)
+
+        parent_idx = 0
+        if self.resolved:
+            methods_index = cd.add_methods([m.code_gen(typelib, cd) for m in self.methods])
+            constants_index = cd.add_constants([c.code_gen(typelib, cd) for c in self.constants])
+            if self.parent:
+                parent_idx = typelib.interfaces.index(self.parent) + 1
+        else:
+            # Unresolved interfaces only have their name and IID set to non-zero values.
+            methods_index = 0
+            constants_index = 0
+            assert len(self.methods) == 0
+            assert len(self.constants) == 0
+            assert self.encodeflags() == 0
+
+        return "{%s, %s, %d, %d, %d, %d, %d, 0x%x} /* %s */" % (
+            iid,
+            string_index,
+            methods_index,
+            constants_index,
+            parent_idx,
+            len(self.methods),
+            len(self.constants),
+            self.encodeflags(),
+            self.name)
+
 
 class Typelib(object):
     """
     A typelib represents one entire typelib file and all the interfaces
     referenced within, whether defined entirely within the typelib or
     merely referenced by name or IID.
 
     Typelib objects may be instantiated directly and populated with data,
@@ -1283,16 +1494,26 @@ class Typelib(object):
             iface = Interface(name, iid, namespace)
             iface._descriptor_offset = ide[3]
             iface.xpt_filename = xpt.filename
             xpt.interfaces.append(iface)
         for iface in xpt.interfaces:
             iface.read_descriptor(xpt, data, data_pool_offset)
         return xpt
 
+    @staticmethod
+    def code_gen_iid(iid):
+        chunks = iid.split('-')
+        return "{0x%s, 0x%s, 0x%s, {0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x}}" % (
+            chunks[0], chunks[1], chunks[2],
+            int(chunks[3][0:2], 16), int(chunks[3][2:4], 16),
+            int(chunks[4][0:2], 16), int(chunks[4][2:4], 16),
+            int(chunks[4][4:6], 16), int(chunks[4][6:8], 16),
+            int(chunks[4][8:10], 16), int(chunks[4][10:12], 16))
+
     def __repr__(self):
         return "<Typelib with %d interfaces>" % len(self.interfaces)
 
     def _sanityCheck(self):
         """
         Check certain assumptions about data contained in this typelib.
         Sort the interfaces array by IID, check that all interfaces
         referenced by methods exist in the array.
@@ -1357,16 +1578,49 @@ class Typelib(object):
         """
         self._sanityCheck()
         if isinstance(output_file, basestring):
             with open(output_file, "wb") as f:
                 self.writefd(f)
         else:
             self.writefd(output_file)
 
+    def code_gen_writefd(self, fd):
+        cd = CodeGenData()
+
+        for i in self.interfaces:
+            cd.add_interface(i.code_gen_interface(self, cd))
+
+        fd.write("""/* -*- 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/. */
+
+/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. */
+
+#include "xpt_struct.h"
+
+""")
+        cd.finish(fd)
+
+    def code_gen_write(self, output_file):
+        """
+        Write the contents of this typelib to |output_file|,
+        which can be either a filename or a file-like object.
+
+        """
+        self._sanityCheck()
+
+        if isinstance(output_file, basestring):
+            with open(output_file, "wb") as f:
+                self.code_gen_writefd(f)
+        else:
+            self.code_gen_writefd(output_file)
+
     def dump(self, out):
         """
         Print a human-readable listing of the contents of this typelib
         to |out|, in the format of xpt_dump.
 
         """
         out.write("""Header:
    Major version:         %d
@@ -1574,14 +1828,16 @@ def xpt_link(inputs):
     interfaces = list(required_interfaces)
 
     # Re-sort interfaces (by IID)
     interfaces.sort()
     return Typelib(interfaces=interfaces)
 
 if __name__ == '__main__':
     if len(sys.argv) < 3:
-        print >>sys.stderr, "xpt <dump|link> <files>"
+        print >>sys.stderr, "xpt <dump|link|linkgen> <files>"
         sys.exit(1)
     if sys.argv[1] == 'dump':
         xpt_dump(sys.argv[2])
     elif sys.argv[1] == 'link':
         xpt_link(sys.argv[3:]).write(sys.argv[2])
+    elif sys.argv[1] == 'linkgen':
+        xpt_link(sys.argv[3:]).code_gen_write(sys.argv[2])
deleted file mode 100644
--- a/xpcom/typelib/xpt/xpt_struct.cpp
+++ /dev/null
@@ -1,470 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim: set ts=8 sts=4 et sw=4 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/. */
-
-/* Implementation of XDR routines for typelib structures. */
-
-#include "xpt_xdr.h"
-#include "xpt_struct.h"
-#include <string.h>
-#include <stdint.h>
-#include <stdio.h>
-
-using mozilla::WrapNotNull;
-
-#define XPT_MAGIC "XPCOM\nTypeLib\r\n\032"
-#define XPT_MAGIC_STRING "XPCOM\\nTypeLib\\r\\n\\032"
-
-/*
- * Annotation records are variable-size records used to store secondary
- * information about the typelib, e.g. such as the name of the tool that
- * generated the typelib file, the date it was generated, etc.  The
- * information is stored with very loose format requirements so as to
- * allow virtually any private data to be stored in the typelib.
- *
- * There are two types of Annotations:
- *
- * EmptyAnnotation
- * PrivateAnnotation
- *
- * The tag field of the prefix discriminates among the variant record
- * types for Annotation's.  If the tag is 0, this record is an
- * EmptyAnnotation. EmptyAnnotation's are ignored - they're only used to
- * indicate an array of Annotation's that's completely empty.  If the tag
- * is 1, the record is a PrivateAnnotation.
- *
- * We don't actually store annotations; we just skip over them if they are
- * present.
- */
-
-#define XPT_ANN_LAST    0x80
-#define XPT_ANN_PRIVATE 0x40
-
-#define XPT_ANN_IS_LAST(flags) (flags & XPT_ANN_LAST)
-#define XPT_ANN_IS_PRIVATE(flags)(flags & XPT_ANN_PRIVATE)
-
-
-/***************************************************************************/
-/* Forward declarations. */
-
-static bool
-DoInterfaceDirectoryEntry(XPTArena *arena, NotNull<XPTCursor*> cursor,
-                          XPTInterfaceDirectoryEntry *ide);
-
-static bool
-DoConstDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
-                  XPTConstDescriptor *cd, XPTInterfaceDescriptor *id);
-
-static bool
-DoMethodDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
-                   XPTMethodDescriptor *md, XPTInterfaceDescriptor *id);
-
-static bool
-SkipAnnotation(NotNull<XPTCursor*> cursor, bool *isLast);
-
-static bool
-DoInterfaceDescriptor(XPTArena *arena, NotNull<XPTCursor*> outer,
-                      const XPTInterfaceDescriptor **idp);
-
-static bool
-DoTypeDescriptorPrefix(XPTArena *arena, NotNull<XPTCursor*> cursor,
-                       XPTTypeDescriptorPrefix *tdp);
-
-static bool
-DoTypeDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
-                 XPTTypeDescriptor *td, XPTInterfaceDescriptor *id);
-
-static bool
-DoParamDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
-                  XPTParamDescriptor *pd, XPTInterfaceDescriptor *id);
-
-/***************************************************************************/
-
-bool
-XPT_DoHeader(XPTArena *arena, NotNull<XPTCursor*> cursor, XPTHeader **headerp)
-{
-    unsigned int i;
-    uint32_t file_length = 0;
-    uint32_t ide_offset;
-
-    XPTHeader* header = XPT_NEWZAP(arena, XPTHeader);
-    if (!header)
-        return false;
-    *headerp = header;
-
-    uint8_t magic[16];
-    for (i = 0; i < sizeof(magic); i++) {
-        if (!XPT_Do8(cursor, &magic[i]))
-            return false;
-    }
-
-    if (strncmp((const char*)magic, XPT_MAGIC, 16) != 0) {
-        /* Require that the header contain the proper magic */
-        fprintf(stderr,
-                "libxpt: bad magic header in input file; "
-                "found '%s', expected '%s'\n",
-                magic, XPT_MAGIC_STRING);
-        return false;
-    }
-
-    uint8_t minor_version;
-    if (!XPT_Do8(cursor, &header->mMajorVersion) ||
-        !XPT_Do8(cursor, &minor_version)) {
-        return false;
-    }
-
-    if (header->mMajorVersion >= XPT_MAJOR_INCOMPATIBLE_VERSION) {
-        /* This file is newer than we are and set to an incompatible version
-         * number. We must set the header state thusly and return.
-         */
-        header->mNumInterfaces = 0;
-        return true;
-    }
-
-    if (!XPT_Do16(cursor, &header->mNumInterfaces) ||
-        !XPT_Do32(cursor, &file_length) ||
-        !XPT_Do32(cursor, &ide_offset)) {
-        return false;
-    }
-
-    /*
-     * Make sure the file length reported in the header is the same size as
-     * as our buffer unless it is zero (not set)
-     */
-    if (file_length != 0 &&
-        cursor->state->pool_allocated < file_length) {
-        fputs("libxpt: File length in header does not match actual length. File may be corrupt\n",
-            stderr);
-        return false;
-    }
-
-    uint32_t data_pool;
-    if (!XPT_Do32(cursor, &data_pool))
-        return false;
-
-    XPT_SetDataOffset(cursor->state, data_pool);
-
-    XPTInterfaceDirectoryEntry* interface_directory = nullptr;
-
-    if (header->mNumInterfaces) {
-        size_t n = header->mNumInterfaces * sizeof(XPTInterfaceDirectoryEntry);
-        interface_directory =
-            static_cast<XPTInterfaceDirectoryEntry*>(XPT_CALLOC8(arena, n));
-        if (!interface_directory)
-            return false;
-    }
-
-    /*
-     * Iterate through the annotations rather than recurring, to avoid blowing
-     * the stack on large xpt files. We don't actually store annotations, we
-     * just skip over them.
-     */
-    bool isLast;
-    do {
-        if (!SkipAnnotation(cursor, &isLast))
-            return false;
-    } while (!isLast);
-
-    /* shouldn't be necessary now, but maybe later */
-    XPT_SeekTo(cursor, ide_offset);
-
-    for (i = 0; i < header->mNumInterfaces; i++) {
-        if (!DoInterfaceDirectoryEntry(arena, cursor,
-                                       &interface_directory[i]))
-            return false;
-    }
-
-    header->mInterfaceDirectory = interface_directory;
-
-    return true;
-}
-
-/* InterfaceDirectoryEntry records go in the header */
-bool
-DoInterfaceDirectoryEntry(XPTArena *arena, NotNull<XPTCursor*> cursor,
-                          XPTInterfaceDirectoryEntry *ide)
-{
-    const char* dummy_name_space;
-
-    /* write the IID in our cursor space */
-    if (!XPT_DoIID(cursor, &(ide->mIID)) ||
-
-        /* write the name string in the data pool, and the offset in our
-           cursor space */
-        !XPT_DoCString(arena, cursor, &(ide->mName)) ||
-
-        /* don't write the name_space string in the data pool, because we don't
-         * need it. Do write the offset in our cursor space */
-        !XPT_DoCString(arena, cursor, &dummy_name_space, /* ignore = */ true) ||
-
-        /* do InterfaceDescriptors */
-        !DoInterfaceDescriptor(arena, cursor, &ide->mInterfaceDescriptor)) {
-        return false;
-    }
-
-    return true;
-}
-
-static bool
-InterfaceDescriptorAddType(XPTArena *arena,
-                           XPTInterfaceDescriptor *id,
-                           XPTTypeDescriptor *td)
-{
-    const XPTTypeDescriptor *old = id->mAdditionalTypes;
-    XPTTypeDescriptor *new_;
-    size_t old_size = id->mNumAdditionalTypes * sizeof(XPTTypeDescriptor);
-    size_t new_size = old_size + sizeof(XPTTypeDescriptor);
-
-    /* XXX should grow in chunks to minimize alloc overhead */
-    new_ = static_cast<XPTTypeDescriptor*>(XPT_CALLOC8(arena, new_size));
-    if (!new_)
-        return false;
-    if (old) {
-        memcpy(new_, old, old_size);
-    }
-
-    new_[id->mNumAdditionalTypes] = *td;
-    id->mAdditionalTypes = new_;
-
-    if (id->mNumAdditionalTypes == UINT8_MAX)
-        return false;
-
-    id->mNumAdditionalTypes += 1;
-    return true;
-}
-
-bool
-DoInterfaceDescriptor(XPTArena *arena, NotNull<XPTCursor*> outer,
-                      const XPTInterfaceDescriptor **idp)
-{
-    XPTInterfaceDescriptor *id;
-    XPTCursor curs;
-    NotNull<XPTCursor*> cursor = WrapNotNull(&curs);
-    uint32_t i, id_sz = 0;
-
-    id = XPT_NEWZAP(arena, XPTInterfaceDescriptor);
-    if (!id)
-        return false;
-    *idp = id;
-
-    if (!XPT_MakeCursor(outer->state, XPT_DATA, id_sz, cursor))
-        return false;
-
-    if (!XPT_Do32(outer, &cursor->offset))
-        return false;
-    if (!cursor->offset) {
-        *idp = NULL;
-        return true;
-    }
-    if(!XPT_Do16(cursor, &id->mParentInterface) ||
-       !XPT_Do16(cursor, &id->mNumMethods)) {
-        return false;
-    }
-
-    XPTMethodDescriptor* method_descriptors = nullptr;
-
-    if (id->mNumMethods) {
-        size_t n = id->mNumMethods * sizeof(XPTMethodDescriptor);
-        method_descriptors =
-            static_cast<XPTMethodDescriptor*>(XPT_CALLOC8(arena, n));
-        if (!method_descriptors)
-            return false;
-    }
-
-    for (i = 0; i < id->mNumMethods; i++) {
-        if (!DoMethodDescriptor(arena, cursor, &method_descriptors[i], id))
-            return false;
-    }
-
-    id->mMethodDescriptors = method_descriptors;
-
-    if (!XPT_Do16(cursor, &id->mNumConstants)) {
-        return false;
-    }
-
-    XPTConstDescriptor* const_descriptors = nullptr;
-
-    if (id->mNumConstants) {
-        size_t n = id->mNumConstants * sizeof(XPTConstDescriptor);
-        const_descriptors =
-            static_cast<XPTConstDescriptor*>(XPT_CALLOC8(arena, n));
-        if (!const_descriptors)
-            return false;
-    }
-
-    for (i = 0; i < id->mNumConstants; i++) {
-        if (!DoConstDescriptor(arena, cursor, &const_descriptors[i], id)) {
-            return false;
-        }
-    }
-
-    id->mConstDescriptors = const_descriptors;
-
-    if (!XPT_Do8(cursor, &id->mFlags)) {
-        return false;
-    }
-
-    return true;
-}
-
-bool
-DoConstDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
-                  XPTConstDescriptor *cd, XPTInterfaceDescriptor *id)
-{
-    bool ok = false;
-
-    if (!XPT_DoCString(arena, cursor, &cd->mName) ||
-        !DoTypeDescriptor(arena, cursor, &cd->mType, id)) {
-
-        return false;
-    }
-
-    switch (cd->mType.Tag()) {
-      case TD_INT16:
-        ok = XPT_Do16(cursor, (uint16_t*) &cd->mValue.i16);
-        break;
-      case TD_INT32:
-        ok = XPT_Do32(cursor, (uint32_t*) &cd->mValue.i32);
-        break;
-      case TD_UINT16:
-        ok = XPT_Do16(cursor, &cd->mValue.ui16);
-        break;
-      case TD_UINT32:
-        ok = XPT_Do32(cursor, &cd->mValue.ui32);
-        break;
-      default:
-        MOZ_ASSERT(false, "illegal type");
-        break;
-    }
-
-    return ok;
-}
-
-bool
-DoMethodDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
-                   XPTMethodDescriptor *md, XPTInterfaceDescriptor *id)
-{
-    int i;
-
-    if (!XPT_Do8(cursor, &md->mFlags) ||
-        !XPT_DoCString(arena, cursor, &md->mName) ||
-        !XPT_Do8(cursor, &md->mNumArgs))
-        return false;
-
-    XPTParamDescriptor* params = nullptr;
-
-    if (md->mNumArgs) {
-        size_t n = md->mNumArgs * sizeof(XPTParamDescriptor);
-        params = static_cast<XPTParamDescriptor*>(XPT_CALLOC8(arena, n));
-        if (!params)
-            return false;
-    }
-
-    for(i = 0; i < md->mNumArgs; i++) {
-        if (!DoParamDescriptor(arena, cursor, &params[i], id))
-            return false;
-    }
-
-    md->mParams = params;
-
-    // |result| appears in the on-disk format but it isn't used,
-    // because a method is either notxpcom, in which case it can't be
-    // called from script so the XPT information is irrelevant, or the
-    // result type is nsresult.
-    XPTParamDescriptor result;
-    if (!DoParamDescriptor(arena, cursor, &result, id))
-        return false;
-
-    return true;
-}
-
-bool
-DoParamDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
-                  XPTParamDescriptor *pd, XPTInterfaceDescriptor *id)
-{
-    if (!XPT_Do8(cursor, &pd->mFlags) ||
-        !DoTypeDescriptor(arena, cursor, &pd->mType, id))
-        return false;
-
-    return true;
-}
-
-bool
-DoTypeDescriptorPrefix(XPTArena *arena, NotNull<XPTCursor*> cursor,
-                       XPTTypeDescriptorPrefix *tdp)
-{
-    return XPT_Do8(cursor, &tdp->mFlags);
-}
-
-bool
-DoTypeDescriptor(XPTArena *arena, NotNull<XPTCursor*> cursor,
-                 XPTTypeDescriptor *td, XPTInterfaceDescriptor *id)
-{
-    if (!DoTypeDescriptorPrefix(arena, cursor, &td->mPrefix)) {
-        return false;
-    }
-
-    switch (td->Tag()) {
-      case TD_INTERFACE_TYPE:
-        uint16_t iface;
-        if (!XPT_Do16(cursor, &iface))
-            return false;
-        td->mData1 = (iface >> 8) & 0xff;
-        td->mData2 = iface & 0xff;
-        break;
-      case TD_INTERFACE_IS_TYPE:
-        if (!XPT_Do8(cursor, &td->mData1))
-            return false;
-        break;
-      case TD_ARRAY: {
-        // argnum2 appears in the on-disk format but it isn't used.
-        uint8_t argnum2 = 0;
-        if (!XPT_Do8(cursor, &td->mData1) ||
-            !XPT_Do8(cursor, &argnum2))
-            return false;
-
-        XPTTypeDescriptor elementTypeDescriptor;
-        if (!DoTypeDescriptor(arena, cursor, &elementTypeDescriptor, id))
-            return false;
-        if (!InterfaceDescriptorAddType(arena, id, &elementTypeDescriptor))
-            return false;
-        td->mData2 = id->mNumAdditionalTypes - 1;
-
-        break;
-      }
-      case TD_PSTRING_SIZE_IS:
-      case TD_PWSTRING_SIZE_IS: {
-        // argnum2 appears in the on-disk format but it isn't used.
-        uint8_t argnum2 = 0;
-        if (!XPT_Do8(cursor, &td->mData1) ||
-            !XPT_Do8(cursor, &argnum2))
-            return false;
-        break;
-      }
-      default:
-        /* nothing special */
-        break;
-    }
-    return true;
-}
-
-bool
-SkipAnnotation(NotNull<XPTCursor*> cursor, bool *isLast)
-{
-    uint8_t flags;
-    if (!XPT_Do8(cursor, &flags))
-        return false;
-
-    *isLast = XPT_ANN_IS_LAST(flags);
-
-    if (XPT_ANN_IS_PRIVATE(flags)) {
-        if (!XPT_SkipStringInline(cursor) ||
-            !XPT_SkipStringInline(cursor))
-            return false;
-    }
-
-    return true;
-}
-
--- a/xpcom/typelib/xpt/xpt_struct.h
+++ b/xpcom/typelib/xpt/xpt_struct.h
@@ -1,90 +1,44 @@
 /* -*- 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 https://mozilla.org/MPL/2.0/. */
 
 /*
- * Structures matching the in-memory representation of typelib structures.
+ * Structures for representing typelib structures in memory.
  * http://www.mozilla.org/scriptable/typelib_file.html
  */
 
 #ifndef xpt_struct_h
 #define xpt_struct_h
 
 #include "nsID.h"
 #include <stdint.h>
 #include "mozilla/Assertions.h"
 
-/*
- * Originally, I was going to have structures that exactly matched the on-disk
- * representation, but that proved difficult: different compilers can pack
- * their structs differently, and that makes overlaying them atop a
- * read-from-disk byte buffer troublesome.  So now I just have some structures
- * that are used in memory, and we're going to write a nice XDR library to
- * write them to disk and stuff.  It is pure joy. -- shaver
- */
-
-/* Structures for the typelib components */
-
-struct XPTHeader;
-struct XPTInterfaceDirectoryEntry;
 struct XPTInterfaceDescriptor;
 struct XPTConstDescriptor;
 struct XPTMethodDescriptor;
 struct XPTParamDescriptor;
 struct XPTTypeDescriptor;
 struct XPTTypeDescriptorPrefix;
 
-/*
- * Every XPCOM typelib file begins with a header.
- */
 struct XPTHeader {
-  // Some of these fields exists in the on-disk format but don't need to be
-  // stored in memory (other than very briefly, which can be done with local
-  // variables).
-
-  //uint8_t mMagic[16];
-  uint8_t mMajorVersion;
-  //uint8_t mMinorVersion;
-  uint16_t mNumInterfaces;
-  //uint32_t mFileLength;
-  const XPTInterfaceDirectoryEntry* mInterfaceDirectory;
-  //uint32_t mDataPool;
-};
+  static const uint16_t kNumInterfaces;
+  static const XPTInterfaceDescriptor kInterfaces[];
+  static const XPTTypeDescriptor kTypes[];
+  static const XPTParamDescriptor kParams[];
+  static const XPTMethodDescriptor kMethods[];
+  static const XPTConstDescriptor kConsts[];
 
-/*
- * Any file with a major version number of XPT_MAJOR_INCOMPATIBLE_VERSION
- * or higher is to be considered incompatible by this version of xpt and
- * we will refuse to read it. We will return a header with magic, major and
- * minor versions set from the file. num_interfaces will be set to zero to
- * confirm our inability to read the file; i.e. even if some client of this
- * library gets out of sync with us regarding the agreed upon value for
- * XPT_MAJOR_INCOMPATIBLE_VERSION, anytime num_interfaces is zero we *know*
- * that this library refused to read the file due to version incompatibility.
- */
-#define XPT_MAJOR_INCOMPATIBLE_VERSION 0x02
-
-/*
- * A contiguous array of fixed-size InterfaceDirectoryEntry records begins at
- * the byte offset identified by the mInterfaceDirectory field in the file
- * header.  The array is used to quickly locate an interface description
- * using its IID.  No interface should appear more than once in the array.
- */
-struct XPTInterfaceDirectoryEntry {
-  nsID mIID;
-  const char* mName;
-
-  // This field exists in the on-disk format. But it isn't used so we don't
-  // allocate space for it in memory.
-  //const char* mNameSpace;
-
-  const XPTInterfaceDescriptor* mInterfaceDescriptor;
+  // All of the strings for this header, including their null
+  // terminators, concatenated into a single string.
+  static const char kStrings[];
 };
 
 /*
  * An InterfaceDescriptor describes a single XPCOM interface, including all of
  * its methods.
  */
 struct XPTInterfaceDescriptor {
   static const uint8_t kScriptableMask =                0x80;
@@ -92,63 +46,47 @@ struct XPTInterfaceDescriptor {
   static const uint8_t kBuiltinClassMask =              0x20;
   static const uint8_t kMainProcessScriptableOnlyMask = 0x10;
 
   bool IsScriptable() const { return !!(mFlags & kScriptableMask); }
   bool IsFunction() const { return !!(mFlags & kFunctionMask); }
   bool IsBuiltinClass() const { return !!(mFlags & kBuiltinClassMask); }
   bool IsMainProcessScriptableOnly() const { return !!(mFlags & kMainProcessScriptableOnlyMask); }
 
+  inline const char* Name() const;
+  inline const XPTMethodDescriptor& Method(size_t aIndex) const;
+  inline const XPTConstDescriptor& Const(size_t aIndex) const;
+
   /*
    * This field ordering minimizes the size of this struct.
-   * The fields are serialized on disk in a different order.
-   * See DoInterfaceDescriptor().
    */
-  const XPTMethodDescriptor* mMethodDescriptors;
-  const XPTConstDescriptor* mConstDescriptors;
-  const XPTTypeDescriptor* mAdditionalTypes;
+  nsID mIID;
+  uint32_t mName; // Index into XPTHeader::mStrings.
+  uint16_t mMethodDescriptors; // Index into XPTHeader::mMethods.
+  uint16_t mConstDescriptors; // Index into XPTHeader::mConsts.
   uint16_t mParentInterface;
   uint16_t mNumMethods;
   uint16_t mNumConstants;
   uint8_t mFlags;
-
-  /*
-   * mAdditionalTypes are used for arrays where we may need multiple
-   * XPTTypeDescriptors for a single XPTMethodDescriptor. Since we still
-   * want to have a simple array of XPTMethodDescriptor (each with a single
-   * embedded XPTTypeDescriptor), a XPTTypeDescriptor can have a reference
-   * to an 'additional_type'. That reference is an index in this
-   * "mAdditionalTypes" array. So a given XPTMethodDescriptor might have
-   * a whole chain of these XPTTypeDescriptors to represent, say, a multi
-   * dimensional array.
-   *
-   * Note that in the typelib file these additional types are stored 'inline'
-   * in the MethodDescriptor. But, in the typelib MethodDescriptors can be
-   * of varying sizes, where in XPT's in memory mapping of the data we want
-   * them to be of fixed size. This mAdditionalTypes scheme is here to allow
-   * for that.
-   */
-  uint8_t mNumAdditionalTypes;
 };
 
 /*
- * A TypeDescriptor is a variable-size record used to identify the type of a
- * method argument or return value.
+ * A TypeDescriptor is a union used to identify the type of a method
+ * argument or return value.
  *
  * There are three types of TypeDescriptors:
  *
  * SimpleTypeDescriptor
  * InterfaceTypeDescriptor
  * InterfaceIsTypeDescriptor
  *
  * The tag field in the prefix indicates which of the variant TypeDescriptor
- * records is being used, and hence the way any remaining fields should be
- * parsed. Values from 0 to 17 refer to SimpleTypeDescriptors. The value 18
- * designates an InterfaceTypeDescriptor, while 19 represents an
- * InterfaceIsTypeDescriptor.
+ * records is being used, and hence which union members are valid. Values from 0
+ * to 17 refer to SimpleTypeDescriptors. The value 18 designates an
+ * InterfaceTypeDescriptor, while 19 represents an InterfaceIsTypeDescriptor.
  */
 
 /* why bother with a struct?  - other code relies on this being a struct */
 struct XPTTypeDescriptorPrefix {
   uint8_t TagPart() const {
     static const uint8_t kFlagMask = 0xe0;
     return (uint8_t) (mFlags & ~kFlagMask);
   }
@@ -198,19 +136,19 @@ struct XPTTypeDescriptor {
   uint8_t ArgNum() const {
     MOZ_ASSERT(Tag() == TD_INTERFACE_IS_TYPE ||
                Tag() == TD_PSTRING_SIZE_IS ||
                Tag() == TD_PWSTRING_SIZE_IS ||
                Tag() == TD_ARRAY);
     return mData1;
   }
 
-  const XPTTypeDescriptor* ArrayElementType(const XPTInterfaceDescriptor* aDescriptor) const {
+  const XPTTypeDescriptor* ArrayElementType() const {
     MOZ_ASSERT(Tag() == TD_ARRAY);
-    return &aDescriptor->mAdditionalTypes[mData2];
+    return &XPTHeader::kTypes[mData2];
   }
 
   // We store the 16-bit iface value as two 8-bit values in order to
   // avoid 16-bit alignment requirements for XPTTypeDescriptor, which
   // reduces its size and also the size of XPTParamDescriptor.
   uint16_t InterfaceIndex() const {
     MOZ_ASSERT(Tag() == TD_INTERFACE_TYPE);
     return (mData1 << 8) | mData2;
@@ -223,52 +161,83 @@ struct XPTTypeDescriptor {
   // assert if the tag is invalid. The memory layout here doesn't exactly match
   // the on-disk format. This is to save memory. Some fields for some cases are
   // smaller than they are on disk or omitted entirely.
   uint8_t mData1;
   uint8_t mData2;
 };
 
 /*
- * A ConstDescriptor is a variable-size record that records the name and
- * value of a scoped interface constant. This is allowed only for a subset
- * of types.
+ * A ConstDescriptor records the name and value of a scoped interface constant.
+ * This is allowed only for a subset of types.
  *
- * The type (and thus the size) of the value record is determined by the
- * contents of the associated TypeDescriptor record. For instance, if type
- * corresponds to int16_t, then value is a two-byte record consisting of a
- * 16-bit signed integer.
+ * The type of the value record is determined by the contents of the associated
+ * TypeDescriptor record. For instance, if type corresponds to int16_t, then
+ * value is a 16-bit signed integer.
  */
 union XPTConstValue {
   int16_t i16;
   uint16_t ui16;
   int32_t i32;
   uint32_t ui32;
-}; /* varies according to type */
+
+  // These constructors are needed to statically initialize different cases of
+  // the union because MSVC does not support the use of designated initializers
+  // in C++ code. They need to be constexpr to ensure that no initialization code
+  // is run at startup, to enable sharing of this memory between Firefox processes.
+  explicit constexpr XPTConstValue(int16_t aInt) : i16(aInt) {}
+  explicit constexpr XPTConstValue(uint16_t aInt) : ui16(aInt) {}
+  explicit constexpr XPTConstValue(int32_t aInt) : i32(aInt) {}
+  explicit constexpr XPTConstValue(uint32_t aInt) : ui32(aInt) {}
+};
 
 struct XPTConstDescriptor {
-  const char* mName;
+  const char* Name() const {
+    return &XPTHeader::kStrings[mName];
+  }
+
+  uint32_t mName; // Index into XPTHeader::mStrings.
   XPTTypeDescriptor mType;
-  union XPTConstValue mValue;
+  XPTConstValue mValue;
 };
 
 /*
- * A ParamDescriptor is a variable-size record used to describe either a
- * single argument to a method or a method's result.
+ * A ParamDescriptor is used to describe either a single argument to a method or
+ * a method's result.
  */
 struct XPTParamDescriptor {
   uint8_t mFlags;
   XPTTypeDescriptor mType;
 };
 
 /*
- * A MethodDescriptor is a variable-size record used to describe a single
- * interface method.
+ * A MethodDescriptor is used to describe a single interface method.