Merge inbound to mozilla-central r=merge a=merge
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Wed, 08 Nov 2017 12:51:09 +0200
changeset 390714 f63559d7e6a570e4e73ba367964099394248e18d
parent 390672 3e95c596ad5bc0c0b99608ed26380994973e7665 (current diff)
parent 390713 c376bb034ca65153a99e06ddd5dd9e69885823a1 (diff)
child 390715 3f797c2aedfb7be631be963e1a402fa9054076c7
child 390733 82812d1793af1bfc07606dc3efd17b63367aa568
child 390784 60fd4a5b01ec70ded9ddfd560fd5be191b1c74b9
push id32839
push usernbeleuzu@mozilla.com
push dateWed, 08 Nov 2017 10:51:56 +0000
treeherdermozilla-central@f63559d7e6a5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.0a1
first release with
nightly linux32
f63559d7e6a5 / 58.0a1 / 20171108110838 / files
nightly linux64
f63559d7e6a5 / 58.0a1 / 20171108110838 / files
nightly mac
f63559d7e6a5 / 58.0a1 / 20171108110838 / files
nightly win32
f63559d7e6a5 / 58.0a1 / 20171108110838 / files
nightly win64
f63559d7e6a5 / 58.0a1 / 20171108110838 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central r=merge a=merge
dom/webidl/ThreadSafeChromeUtils.webidl
testing/web-platform/meta/html/browsers/origin/cross-origin-objects/cross-origin-objects.html.ini
tools/lint/eslint/eslint-plugin-mozilla/lib/rules/avoid-nsISupportsString-preferences.js
tools/lint/eslint/eslint-plugin-mozilla/tests/avoid-nsISupportsString-preferences.js
--- a/accessible/tests/mochitest/events/docload/test_docload_root.html
+++ b/accessible/tests/mochitest/events/docload/test_docload_root.html
@@ -87,17 +87,20 @@
       };
 
       this.getID = () => `open dialog '${aURL}'`;
     }
 
     function closeDialogWnd() {
       this.eventSeq = [ new invokerChecker(EVENT_FOCUS, getAccessible(document)) ];
 
-      this.invoke = () => gDialog.close();
+      this.invoke = () => {
+        gDialog.close()
+        window.focus();
+      };
 
       this.finalCheck = () => {
         ok(!isAccessibleInCache(gDialogDoc),
           `The document accessible for dialog is in cache still!`);
 
         gDialog = gDialogDoc = gRootAcc = null;
       };
 
--- a/accessible/tests/mochitest/events/docload/test_docload_shutdown.html
+++ b/accessible/tests/mochitest/events/docload/test_docload_shutdown.html
@@ -105,17 +105,20 @@
       };
 
       this.getID = () => `open dialog '${aURL}'`;
     }
 
     function closeWndShutdownDoc() {
       this.eventSeq = [ new invokerChecker(EVENT_FOCUS, getAccessible(document)) ];
 
-      this.invoke = () => gDialog.close();
+      this.invoke = () => {
+        gDialog.close()
+        window.focus();
+      };
 
       this.finalCheck = () => {
         ok(!isAccessibleInCache(gDialogDoc),
           "The document accessible for dialog is in cache still!");
         // After the window is closed all alive subdocument accessibles should
         // be shut down.
         ok(!isAccessibleInCache(gIframeDoc),
           "The document accessible for iframe is in cache still!");
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1040,21 +1040,17 @@ pref("dom.ipc.plugins.sandbox-level.flas
 #endif
 
 #if defined(MOZ_CONTENT_SANDBOX)
 // This controls the strength of the Windows content process sandbox for testing
 // purposes. This will require a restart.
 // On windows these levels are:
 // See - security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
 // SetSecurityLevelForContentProcess() for what the different settings mean.
-#if defined(NIGHTLY_BUILD)
 pref("security.sandbox.content.level", 4);
-#else
-pref("security.sandbox.content.level", 3);
-#endif
 
 // This controls the depth of stack trace that is logged when Windows sandbox
 // logging is turned on.  This is only currently available for the content
 // process because the only other sandbox (for GMP) has too strict a policy to
 // allow stack tracing.  This does not require a restart to take effect.
 pref("security.sandbox.windows.log.stackTraceDepth", 0);
 #endif
 
--- a/build/moz.configure/android-ndk.configure
+++ b/build/moz.configure/android-ndk.configure
@@ -6,16 +6,19 @@
 
 
 js_option('--with-android-ndk', nargs=1,
           help='location where the Android NDK can be found')
 
 js_option('--with-android-toolchain', nargs=1,
           help='location of the Android toolchain')
 
+option('--with-android-googlevr-sdk', nargs=1,
+       help='location of the Android GoogleVR SDK')
+
 
 @depends(target)
 def min_android_version(target):
     if target.cpu in ['aarch64', 'x86_64', 'mips64']:
         # 64-bit support was added in API 21.
         return '21'
     return '9'
 
@@ -295,8 +298,50 @@ def android_clang_compiler(host, ndk):
     if not isdir(llvm_path):
         die("Couldn't find path to LLVM toolchain at %s" % llvm_path)
 
     clang = '%s/clang' % llvm_path
     if not exists(clang):
         die("Couln't find clang in LLVM toolchain at %s" % clang)
 
     return clang
+
+
+@depends('--with-android-googlevr-sdk', target)
+@checking('for GoogleVR SDK', lambda x: x.result)
+@imports(_from='os.path', _import='exists')
+@imports(_from='os.path', _import='abspath')
+def googlevr_sdk(value, target):
+    if not value:
+        return namespace(
+            result='Not specified'
+        )
+    path = abspath(value[0])
+    if not exists(path):
+        die('Could not find GoogleVR SDK %s', path)
+    include = '%s/libraries/headers/' % path
+    if 'arm' == target.cpu:
+        arch = 'armeabi-v7a'
+    elif 'aarch64' == target.cpu:
+        arch = 'arm64-v8a'
+    elif 'x86' == target.cpu:
+        arch = 'x86'
+    else:
+        die('Unsupported GoogleVR cpu architecture %s' % target.cpu)
+
+    libs = '{0}/libraries/jni/{1}/'.format(path, arch)
+
+    if not exists(libs):
+        die('Could not find GoogleVR NDK at %s. Did you try running '
+            '\'./gradlew :extractNdk\' in %s?', libs, path)
+
+    return namespace(
+        result=path,
+        include=include,
+        libs=libs,
+        enabled=True,
+    )
+
+
+set_define('MOZ_ANDROID_GOOGLE_VR', googlevr_sdk.enabled)
+set_config('MOZ_ANDROID_GOOGLE_VR', googlevr_sdk.enabled)
+set_config('MOZ_ANDROID_GOOGLE_VR_INCLUDE', googlevr_sdk.include)
+set_config('MOZ_ANDROID_GOOGLE_VR_LIBS', googlevr_sdk.libs)
--- a/config/system-headers
+++ b/config/system-headers
@@ -1183,16 +1183,20 @@ UTextEdit.h
 UTextTraits.h
 utime.h
 UWindows.h
 values.h
 varargs.h
 vcclr.h
 View.h
 Volume.h
+#ifdef ANDROID
+vr/gvr/capi/include/gvr.h
+vr/gvr/capi/include/gvr_controller.h
+#endif
 wab.h
 wait.h
 wchar.h
 wctype.h
 winbase.h
 win/compobj.h
 windef.h
 Window.h
--- a/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-04.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-04.js
@@ -139,17 +139,17 @@ function test() {
        "Child element should not have a parent.");
     is([...parent].length, 0,
        "Parent should have zero children.");
 
     testScope.remove();
 
     is([...variables].length, 0,
        "VariablesView should have been emptied.");
-    is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(variables._itemsByElement).length,
+    is(ChromeUtils.nondeterministicGetWeakMapKeys(variables._itemsByElement).length,
        0, "VariablesView _itemsByElement map has been emptied.");
     is(variables._currHierarchy.size, 0,
        "VariablesView _currHierarchy map has been emptied.");
     is(variables._list.children.length, 0,
        "VariablesView element should have no children.");
 
     closeDebuggerAndFinish(aPanel);
   });
--- a/devtools/client/memory/test/unit/head.js
+++ b/devtools/client/memory/test/unit/head.js
@@ -57,17 +57,17 @@ StubbedMemoryFront.prototype.attach = Ta
 });
 
 StubbedMemoryFront.prototype.detach = Task.async(function* () {
   this.state = "detached";
 });
 
 StubbedMemoryFront.prototype.saveHeapSnapshot = expectState("attached",
   Task.async(function* () {
-    return ThreadSafeChromeUtils.saveHeapSnapshot({ runtime: true });
+    return ChromeUtils.saveHeapSnapshot({ runtime: true });
   }), "saveHeapSnapshot");
 
 StubbedMemoryFront.prototype.startRecordingAllocations = expectState("attached",
   Task.async(function* () {
     this.recordingAllocations = true;
   }));
 
 StubbedMemoryFront.prototype.stopRecordingAllocations = expectState("attached",
--- a/devtools/client/themes/breadcrumbs.css
+++ b/devtools/client/themes/breadcrumbs.css
@@ -1,13 +1,21 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+ :root {
+  --breadcrumb-id-class-color: #909090;
+ }
+
+ .theme-dark:root {
+  --breadcrumb-id-class-color: var(--theme-content-color1);
+ }
+
 /* Inspector HTMLBreadcrumbs */
 
 .breadcrumbs-widget-container {
   margin-inline-end: 3px;
   max-height: 24px; /* Set max-height for proper sizing on linux */
   height: 24px; /* Set height to prevent starting small waiting for content */
 }
 
@@ -150,22 +158,19 @@
 .theme-dark .breadcrumbs-widget-item {
   color: var(--theme-selection-color);
 }
 
 .theme-light .breadcrumbs-widget-item {
   color: var(--theme-body-color);
 }
 
-.breadcrumbs-widget-item-id {
-  color: var(--theme-body-color-alt);
-}
-
+.breadcrumbs-widget-item-id,
 .breadcrumbs-widget-item-classes {
-  color: var(--theme-content-color1);
+  color: var(--breadcrumb-id-class-color);
 }
 
 .breadcrumbs-widget-item-pseudo-classes {
   color: var(--theme-highlight-lightorange);
 }
 
 /* Firebug theme support for breadcrumbs widget. */
 
--- a/devtools/docs/tools/memory-panel.md
+++ b/devtools/docs/tools/memory-panel.md
@@ -83,18 +83,18 @@ binary blob in memory, but instead strea
 
 Once all of that is accounted for, saving snapshots becomes pretty straight
 forward. We traverse the live heap graph with `JS::ubi::Node` and
 `JS::ubi::BreadthFirst`, create a protobuf message for each node and each node's
 edges, and write these messages to disk before continuing the traversal to the
 next node.
 
 This functionality is exposed to chrome JavaScript as the
-`[ThreadSafe]ChromeUtils.saveHeapSnapshot` function. See
-`dom/webidl/ThreadSafeChromeUtils.webidl` for API documentation.
+`ChromeUtils.saveHeapSnapshot` function. See `dom/webidl/ChromeUtils.webidl` for
+API documentation.
 
 ### Reading Heap Snapshots
 
 Reading heap snapshots has less restrictions than saving heap snapshots. The
 protobuf messages that make up the core dump are deserialized one by one, stored
 as a set of `DeserializedNode`s and a set of `DeserializedEdge`s, and the result
 is a `HeapSnapshot` instance.
 
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -7,17 +7,17 @@
 "use strict";
 
 const { Cu, Ci } = require("chrome");
 const { GeneratedLocation } = require("devtools/server/actors/common");
 const { DebuggerServer } = require("devtools/server/main");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { assert, dumpn } = DevToolsUtils;
 
-loader.lazyRequireGetter(this, "ThreadSafeChromeUtils");
+loader.lazyRequireGetter(this, "ChromeUtils");
 
 // Number of items to preview in objects, arrays, maps, sets, lists,
 // collections, etc.
 const OBJECT_PREVIEW_MAX_ITEMS = 10;
 
 /**
  * Creates an actor for the specified object.
  *
@@ -1050,17 +1050,17 @@ function enumWeakMapEntries(objectActor)
   // based on whether or not it's Xrayable and/or callable, which breaks
   // the for/of iteration.
   //
   // This code is designed to handle untrusted objects, so we can safely
   // waive Xrays on the iterable, and relying on the Debugger machinery to
   // make sure we handle the resulting objects carefully.
   let raw = objectActor.obj.unsafeDereference();
   let keys = Cu.waiveXrays(
-    ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(raw));
+    ChromeUtils.nondeterministicGetWeakMapKeys(raw));
 
   return {
     [Symbol.iterator]: function* () {
       for (let key of keys) {
         let value = WeakMap.prototype.get.call(raw, key);
         yield [ key, value ].map(val => gripFromEntry(objectActor, val));
       }
     },
@@ -1127,17 +1127,17 @@ function enumWeakSetEntries(objectActor)
   // based on whether or not it's Xrayable and/or callable, which breaks
   // the for/of iteration.
   //
   // This code is designed to handle untrusted objects, so we can safely
   // waive Xrays on the iterable, and relying on the Debugger machinery to
   // make sure we handle the resulting objects carefully.
   let raw = objectActor.obj.unsafeDereference();
   let keys = Cu.waiveXrays(
-    ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(raw));
+    ChromeUtils.nondeterministicGetWeakSetKeys(raw));
 
   return {
     [Symbol.iterator]: function* () {
       for (let item of keys) {
         yield gripFromEntry(objectActor, item);
       }
     },
     size: keys.length,
--- a/devtools/server/performance/memory.js
+++ b/devtools/server/performance/memory.js
@@ -8,17 +8,17 @@ const { Cc, Ci, Cu } = require("chrome")
 const { reportException } = require("devtools/shared/DevToolsUtils");
 const { expectState } = require("devtools/server/actors/common");
 
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 loader.lazyRequireGetter(this, "DeferredTask",
   "resource://gre/modules/DeferredTask.jsm", true);
 loader.lazyRequireGetter(this, "StackFrameCache",
   "devtools/server/actors/utils/stack", true);
-loader.lazyRequireGetter(this, "ThreadSafeChromeUtils");
+loader.lazyRequireGetter(this, "ChromeUtils");
 loader.lazyRequireGetter(this, "HeapSnapshotFileUtils",
   "devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
 loader.lazyRequireGetter(this, "ChromeActor", "devtools/server/actors/chrome",
                          true);
 loader.lazyRequireGetter(this, "ChildProcessActor",
                          "devtools/server/actors/child-process", true);
 
 /**
@@ -146,17 +146,17 @@ Memory.prototype = {
     if (!boundaries) {
       if (this.parent instanceof ChromeActor ||
           this.parent instanceof ChildProcessActor) {
         boundaries = { runtime: true };
       } else {
         boundaries = { debugger: this.dbg };
       }
     }
-    return ThreadSafeChromeUtils.saveHeapSnapshotGetId(boundaries);
+    return ChromeUtils.saveHeapSnapshotGetId(boundaries);
   }, "saveHeapSnapshot"),
 
   /**
    * Take a census of the heap. See js/src/doc/Debugger/Debugger.Memory.md for
    * more information.
    */
   takeCensus: expectState("attached", function () {
     return this.dbg.memory.takeCensus();
--- a/devtools/server/tests/unit/test_MemoryActor_saveHeapSnapshot_01.js
+++ b/devtools/server/tests/unit/test_MemoryActor_saveHeapSnapshot_01.js
@@ -7,12 +7,12 @@
 // and then create a HeapSnapshot instance from the resulting file.
 
 const { OS } = require("resource://gre/modules/osfile.jsm");
 
 const run_test = makeMemoryActorTest(function* (client, memoryFront) {
   const snapshotFilePath = yield memoryFront.saveHeapSnapshot();
   ok(!!(yield OS.File.stat(snapshotFilePath)),
      "Should have the heap snapshot file");
-  const snapshot = ThreadSafeChromeUtils.readHeapSnapshot(snapshotFilePath);
+  const snapshot = ChromeUtils.readHeapSnapshot(snapshotFilePath);
   ok(snapshot instanceof HeapSnapshot,
      "And we should be able to read a HeapSnapshot instance from the file");
 });
--- a/devtools/server/tests/unit/test_MemoryActor_saveHeapSnapshot_02.js
+++ b/devtools/server/tests/unit/test_MemoryActor_saveHeapSnapshot_02.js
@@ -9,12 +9,12 @@
 const { OS } = require("resource://gre/modules/osfile.jsm");
 
 const run_test = makeMemoryActorTest(function* (client, memoryFront) {
   const snapshotFilePath = yield memoryFront.saveHeapSnapshot({
     forceCopy: true
   });
   ok(!!(yield OS.File.stat(snapshotFilePath)),
      "Should have the heap snapshot file");
-  const snapshot = ThreadSafeChromeUtils.readHeapSnapshot(snapshotFilePath);
+  const snapshot = ChromeUtils.readHeapSnapshot(snapshotFilePath);
   ok(snapshot instanceof HeapSnapshot,
      "And we should be able to read a HeapSnapshot instance from the file");
 });
--- a/devtools/server/tests/unit/test_MemoryActor_saveHeapSnapshot_03.js
+++ b/devtools/server/tests/unit/test_MemoryActor_saveHeapSnapshot_03.js
@@ -7,12 +7,12 @@
 // ChromeActor or a ChildProcessActor.
 
 const { OS } = require("resource://gre/modules/osfile.jsm");
 
 const run_test = makeFullRuntimeMemoryActorTest(function* (client, memoryFront) {
   const snapshotFilePath = yield memoryFront.saveHeapSnapshot();
   ok(!!(yield OS.File.stat(snapshotFilePath)),
      "Should have the heap snapshot file");
-  const snapshot = ThreadSafeChromeUtils.readHeapSnapshot(snapshotFilePath);
+  const snapshot = ChromeUtils.readHeapSnapshot(snapshotFilePath);
   ok(snapshot instanceof HeapSnapshot,
      "And we should be able to read a HeapSnapshot instance from the file");
 });
--- a/devtools/shared/builtin-modules.js
+++ b/devtools/shared/builtin-modules.js
@@ -13,17 +13,17 @@
  * they would also miss them.
  */
 
 const { Cu, CC, Cc, Ci } = require("chrome");
 const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
 const jsmScope = Cu.import("resource://gre/modules/Services.jsm", {});
 const { Services } = jsmScope;
 // Steal various globals only available in JSM scope (and not Sandbox one)
-const { PromiseDebugging, ChromeUtils, ThreadSafeChromeUtils, HeapSnapshot,
+const { PromiseDebugging, ChromeUtils, HeapSnapshot,
         atob, btoa, TextEncoder, TextDecoder } = Cu.getGlobalForObject(jsmScope);
 
 // Create a single Sandbox to access global properties needed in this module.
 // Sandbox are memory expensive, so we should create as little as possible.
 const { CSS, FileReader, indexedDB, URL } =
     Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(), {
       wantGlobalProperties: ["CSS", "FileReader", "indexedDB", "URL"]
     });
@@ -173,17 +173,16 @@ exports.modules = {
   "Services": Object.create(Services),
   promise,
   // Expose "chrome" Promise, which aren't related to any document
   // and so are never frozen, even if the browser loader module which
   // pull it is destroyed. See bug 1402779.
   Promise,
   PromiseDebugging,
   ChromeUtils,
-  ThreadSafeChromeUtils,
   HeapSnapshot,
   FileReader,
 };
 
 defineLazyGetter(exports.modules, "Debugger", () => {
   // addDebuggerToGlobal only allows adding the Debugger object to a global. The
   // this object is not guaranteed to be a global (in particular on B2G, due to
   // compartment sharing), so add the Debugger object to a sandbox instead.
--- a/devtools/shared/fronts/memory.js
+++ b/devtools/shared/fronts/memory.js
@@ -32,17 +32,17 @@ const MemoryFront = protocol.FrontClassW
    * as we jump through the correct IPDL hoops.
    *
    * @params Boolean options.forceCopy
    *         Always force a bulk data copy of the saved heap snapshot, even when
    *         the server and client share a file system.
    *
    * @params {Object|undefined} options.boundaries
    *         The boundaries for the heap snapshot. See
-   *         ThreadSafeChromeUtils.webidl for more details.
+   *         ChromeUtils.webidl for more details.
    *
    * @returns Promise<String>
    */
   saveHeapSnapshot: protocol.custom(Task.async(function* (options = {}) {
     const snapshotId = yield this._saveHeapSnapshotImpl(options.boundaries);
 
     if (!options.forceCopy &&
         (yield HeapSnapshotFileUtils.haveHeapSnapshotTempFile(snapshotId))) {
--- a/devtools/shared/heapsnapshot/HeapAnalysesWorker.js
+++ b/devtools/shared/heapsnapshot/HeapAnalysesWorker.js
@@ -1,12 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-/* global ThreadSafeChromeUtils*/
+/* global ChromeUtils*/
 
 // This is a worker which reads offline heap snapshots into memory and performs
 // heavyweight analyses on them without blocking the main thread. A
 // HeapAnalysesWorker is owned and communicated with by a HeapAnalysesClient
 // instance. See HeapAnalysesClient.js.
 
 /* global importScripts, workerHelper, self */
 
@@ -42,17 +42,17 @@ const dominatorTrees = [];
  */
 const dominatorTreeSnapshots = [];
 
 /**
  * @see HeapAnalysesClient.prototype.readHeapSnapshot
  */
 workerHelper.createTask(self, "readHeapSnapshot", ({ snapshotFilePath }) => {
   snapshots[snapshotFilePath] =
-    ThreadSafeChromeUtils.readHeapSnapshot(snapshotFilePath);
+    ChromeUtils.readHeapSnapshot(snapshotFilePath);
   return true;
 });
 
 /**
  * @see HeapAnalysesClient.prototype.deleteHeapSnapshot
  */
 workerHelper.createTask(self, "deleteHeapSnapshot", ({ snapshotFilePath }) => {
   let snapshot = snapshots[snapshotFilePath];
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp
@@ -1563,21 +1563,21 @@ getCoreDumpOutputStream(ErrorResult& rv,
 } // namespace devtools
 
 namespace dom {
 
 using namespace JS;
 using namespace devtools;
 
 /* static */ void
-ThreadSafeChromeUtils::SaveHeapSnapshotShared(GlobalObject& global,
-                                              const HeapSnapshotBoundaries& boundaries,
-                                              nsAString& outFilePath,
-                                              nsAString& outSnapshotId,
-                                              ErrorResult& rv)
+ChromeUtils::SaveHeapSnapshotShared(GlobalObject& global,
+                                    const HeapSnapshotBoundaries& boundaries,
+                                    nsAString& outFilePath,
+                                    nsAString& outSnapshotId,
+                                    ErrorResult& rv)
 {
   auto start = TimeStamp::Now();
 
   bool wantNames = true;
   CompartmentSet compartments;
   uint32_t nodeCount = 0;
   uint32_t edgeCount = 0;
 
@@ -1632,39 +1632,39 @@ ThreadSafeChromeUtils::SaveHeapSnapshotS
                                  start);
   Telemetry::Accumulate(Telemetry::DEVTOOLS_HEAP_SNAPSHOT_NODE_COUNT,
                         nodeCount);
   Telemetry::Accumulate(Telemetry::DEVTOOLS_HEAP_SNAPSHOT_EDGE_COUNT,
                         edgeCount);
 }
 
 /* static */ void
-ThreadSafeChromeUtils::SaveHeapSnapshot(GlobalObject& global,
-                                        const HeapSnapshotBoundaries& boundaries,
-                                        nsAString& outFilePath,
-                                        ErrorResult& rv)
+ChromeUtils::SaveHeapSnapshot(GlobalObject& global,
+                              const HeapSnapshotBoundaries& boundaries,
+                              nsAString& outFilePath,
+                              ErrorResult& rv)
 {
   nsAutoString snapshotId;
   SaveHeapSnapshotShared(global, boundaries, outFilePath, snapshotId, rv);
 }
 
 /* static */ void
-ThreadSafeChromeUtils::SaveHeapSnapshotGetId(GlobalObject& global,
-                                             const HeapSnapshotBoundaries& boundaries,
-                                             nsAString& outSnapshotId,
-                                             ErrorResult& rv)
+ChromeUtils::SaveHeapSnapshotGetId(GlobalObject& global,
+                                   const HeapSnapshotBoundaries& boundaries,
+                                   nsAString& outSnapshotId,
+                                   ErrorResult& rv)
 {
   nsAutoString filePath;
   SaveHeapSnapshotShared(global, boundaries, filePath, outSnapshotId, rv);
 }
 
 /* static */ already_AddRefed<HeapSnapshot>
-ThreadSafeChromeUtils::ReadHeapSnapshot(GlobalObject& global,
-                                        const nsAString& filePath,
-                                        ErrorResult& rv)
+ChromeUtils::ReadHeapSnapshot(GlobalObject& global,
+                              const nsAString& filePath,
+                              ErrorResult& rv)
 {
   auto start = TimeStamp::Now();
 
   UniquePtr<char[]> path(ToNewCString(filePath));
   if (!path) {
     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
--- a/devtools/shared/heapsnapshot/tests/unit/dominator-tree-worker.js
+++ b/devtools/shared/heapsnapshot/tests/unit/dominator-tree-worker.js
@@ -3,18 +3,18 @@
 
 "use strict";
 
 console.log("Initializing worker.");
 
 self.onmessage = e => {
   console.log("Starting test.");
   try {
-    const path = ThreadSafeChromeUtils.saveHeapSnapshot({ runtime: true });
-    const snapshot = ThreadSafeChromeUtils.readHeapSnapshot(path);
+    const path = ChromeUtils.saveHeapSnapshot({ runtime: true });
+    const snapshot = ChromeUtils.readHeapSnapshot(path);
 
     const dominatorTree = snapshot.computeDominatorTree();
     ok(dominatorTree);
     ok(dominatorTree instanceof DominatorTree);
 
     let threw = false;
     try {
       new DominatorTree();
--- a/devtools/shared/heapsnapshot/tests/unit/heap-snapshot-worker.js
+++ b/devtools/shared/heapsnapshot/tests/unit/heap-snapshot-worker.js
@@ -3,27 +3,25 @@
 
 "use strict";
 
 console.log("Initializing worker.");
 
 self.onmessage = ex => {
   console.log("Starting test.");
   try {
-    ok(typeof ChromeUtils === "undefined",
-       "Should not have access to ChromeUtils in a worker.");
-    ok(ThreadSafeChromeUtils,
-       "Should have access to ThreadSafeChromeUtils in a worker.");
+    ok(ChromeUtils,
+       "Should have access to ChromeUtils in a worker.");
     ok(HeapSnapshot,
        "Should have access to HeapSnapshot in a worker.");
 
-    const filePath = ThreadSafeChromeUtils.saveHeapSnapshot({ globals: [this] });
+    const filePath = ChromeUtils.saveHeapSnapshot({ globals: [this] });
     ok(true, "Should be able to save a snapshot.");
 
-    const snapshot = ThreadSafeChromeUtils.readHeapSnapshot(filePath);
+    const snapshot = ChromeUtils.readHeapSnapshot(filePath);
     ok(snapshot, "Should be able to read a heap snapshot");
     ok(snapshot instanceof HeapSnapshot, "Should be an instanceof HeapSnapshot");
   } catch (e) {
     ok(false, "Unexpected error inside worker:\n" + e.toString() + "\n" + e.stack);
   } finally {
     done();
   }
 };
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -15,61 +15,61 @@
 #include "mozilla/dom/IdleDeadline.h"
 #include "mozilla/dom/WindowBinding.h" // For IdleRequestCallback/Options
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 /* static */ void
-ThreadSafeChromeUtils::NondeterministicGetWeakMapKeys(GlobalObject& aGlobal,
-                                                      JS::Handle<JS::Value> aMap,
-                                                      JS::MutableHandle<JS::Value> aRetval,
-                                                      ErrorResult& aRv)
+ChromeUtils::NondeterministicGetWeakMapKeys(GlobalObject& aGlobal,
+                                            JS::Handle<JS::Value> aMap,
+                                            JS::MutableHandle<JS::Value> aRetval,
+                                            ErrorResult& aRv)
 {
   if (!aMap.isObject()) {
     aRetval.setUndefined();
   } else {
     JSContext* cx = aGlobal.Context();
     JS::Rooted<JSObject*> objRet(cx);
     JS::Rooted<JSObject*> mapObj(cx, &aMap.toObject());
     if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &objRet)) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     } else {
       aRetval.set(objRet ? JS::ObjectValue(*objRet) : JS::UndefinedValue());
     }
   }
 }
 
 /* static */ void
-ThreadSafeChromeUtils::NondeterministicGetWeakSetKeys(GlobalObject& aGlobal,
-                                                      JS::Handle<JS::Value> aSet,
-                                                      JS::MutableHandle<JS::Value> aRetval,
-                                                      ErrorResult& aRv)
+ChromeUtils::NondeterministicGetWeakSetKeys(GlobalObject& aGlobal,
+                                            JS::Handle<JS::Value> aSet,
+                                            JS::MutableHandle<JS::Value> aRetval,
+                                            ErrorResult& aRv)
 {
   if (!aSet.isObject()) {
     aRetval.setUndefined();
   } else {
     JSContext* cx = aGlobal.Context();
     JS::Rooted<JSObject*> objRet(cx);
     JS::Rooted<JSObject*> setObj(cx, &aSet.toObject());
     if (!JS_NondeterministicGetWeakSetKeys(cx, setObj, &objRet)) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     } else {
       aRetval.set(objRet ? JS::ObjectValue(*objRet) : JS::UndefinedValue());
     }
   }
 }
 
 /* static */ void
-ThreadSafeChromeUtils::Base64URLEncode(GlobalObject& aGlobal,
-                                       const ArrayBufferViewOrArrayBuffer& aSource,
-                                       const Base64URLEncodeOptions& aOptions,
-                                       nsACString& aResult,
-                                       ErrorResult& aRv)
+ChromeUtils::Base64URLEncode(GlobalObject& aGlobal,
+                             const ArrayBufferViewOrArrayBuffer& aSource,
+                             const Base64URLEncodeOptions& aOptions,
+                             nsACString& aResult,
+                             ErrorResult& aRv)
 {
   size_t length = 0;
   uint8_t* data = nullptr;
   if (aSource.IsArrayBuffer()) {
     const ArrayBuffer& buffer = aSource.GetAsArrayBuffer();
     buffer.ComputeLengthAndData();
     length = buffer.Length();
     data = buffer.Data();
@@ -87,21 +87,21 @@ ThreadSafeChromeUtils::Base64URLEncode(G
   nsresult rv = mozilla::Base64URLEncode(length, data, paddingPolicy, aResult);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aResult.Truncate();
     aRv.Throw(rv);
   }
 }
 
 /* static */ void
-ThreadSafeChromeUtils::Base64URLDecode(GlobalObject& aGlobal,
-                                       const nsACString& aString,
-                                       const Base64URLDecodeOptions& aOptions,
-                                       JS::MutableHandle<JSObject*> aRetval,
-                                       ErrorResult& aRv)
+ChromeUtils::Base64URLDecode(GlobalObject& aGlobal,
+                             const nsACString& aString,
+                             const Base64URLDecodeOptions& aOptions,
+                             JS::MutableHandle<JSObject*> aRetval,
+                             ErrorResult& aRv)
 {
   Base64URLDecodePaddingPolicy paddingPolicy;
   switch (aOptions.mPadding) {
     case Base64URLDecodePadding::Require:
       paddingPolicy = Base64URLDecodePaddingPolicy::Require;
       break;
 
     case Base64URLDecodePadding::Ignore:
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -5,34 +5,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ChromeUtils__
 #define mozilla_dom_ChromeUtils__
 
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ChromeUtilsBinding.h"
-#include "mozilla/dom/ThreadSafeChromeUtilsBinding.h"
 #include "mozilla/ErrorResult.h"
 
 namespace mozilla {
 
 namespace devtools {
 class HeapSnapshot;
 } // namespace devtools
 
 namespace dom {
 
 class ArrayBufferViewOrArrayBuffer;
 class IdleRequestCallback;
 struct IdleRequestOptions;
 class PrecompiledScript;
 class Promise;
 
-class ThreadSafeChromeUtils
+class ChromeUtils
 {
 private:
   // Implemented in devtools/shared/heapsnapshot/HeapSnapshot.cpp
   static void SaveHeapSnapshotShared(GlobalObject& global,
                                      const HeapSnapshotBoundaries& boundaries,
                                      nsAString& filePath,
                                      nsAString& snapshotId,
                                      ErrorResult& rv);
@@ -71,21 +70,17 @@ public:
                               nsACString& aResult,
                               ErrorResult& aRv);
 
   static void Base64URLDecode(GlobalObject& aGlobal,
                               const nsACString& aString,
                               const Base64URLDecodeOptions& aOptions,
                               JS::MutableHandle<JSObject*> aRetval,
                               ErrorResult& aRv);
-};
 
-class ChromeUtils : public ThreadSafeChromeUtils
-{
-public:
   static void
   OriginAttributesToSuffix(GlobalObject& aGlobal,
                            const dom::OriginAttributesDictionary& aAttrs,
                            nsCString& aSuffix);
 
   static bool
   OriginAttributesMatchPattern(dom::GlobalObject& aGlobal,
                                const dom::OriginAttributesDictionary& aAttrs,
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -139,22 +139,16 @@ DOMInterfaces = {
     'nativeType': 'mozilla::extensions::ChannelWrapper',
 },
 
 'CharacterData': {
     'nativeType': 'nsGenericDOMDataNode',
     'concrete': False
 },
 
-'ChromeUtils': {
-    # The codegen is dumb, and doesn't understand that this interface is only a
-    # collection of static methods, so we have this `concrete: False` hack.
-    'concrete': False,
-},
-
 'ChromeWorker': {
     'headerFile': 'mozilla/dom/WorkerPrivate.h',
     'nativeType': 'mozilla::dom::workers::ChromeWorkerPrivate',
 },
 
 'Client': {
     'nativeType': 'mozilla::dom::workers::ServiceWorkerClient',
     'headerFile': 'mozilla/dom/workers/bindings/ServiceWorkerClient.h',
@@ -1077,23 +1071,16 @@ DOMInterfaces = {
 'TextMetrics': {
     'wrapperCache': False
 },
 
 'TCPSocket': {
     'implicitJSContext': ['send']
 },
 
-'ThreadSafeChromeUtils': {
-    # The codegen is dumb, and doesn't understand that this interface is only a
-    # collection of static methods, so we have this `concrete: False` hack.
-    'concrete': False,
-    'headerFile': 'mozilla/dom/ChromeUtils.h',
-},
-
 'TouchList': {
     'headerFile': 'mozilla/dom/TouchEvent.h',
 },
 
 'TreeColumn': {
     'nativeType': 'nsTreeColumn',
     'headerFile': 'nsTreeColumns.h',
 },
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1777,17 +1777,18 @@ class IDLNamespace(IDLInterfaceOrNamespa
                 convertExposedAttrToGlobalNameSet(attr,
                                                   self._exposureGlobalNames)
             elif identifier == "ClassString":
                 # Takes a string value to override the default "Object" if
                 # desired.
                 if not attr.hasValue():
                     raise WebIDLError("[%s] must have a value" % identifier,
                                       [attr.location])
-            elif identifier == "ProtoObjectHack":
+            elif (identifier == "ProtoObjectHack" or
+                  identifier == "ChromeOnly"):
                 if not attr.noArguments():
                     raise WebIDLError("[%s] must not have arguments" % identifier,
                                       [attr.location])
             else:
                 raise WebIDLError("Unknown extended attribute %s on namespace" %
                                   identifier,
                                   [attr.location])
 
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -29,17 +29,17 @@ function getIntPref(prefName, def) {
   }
   catch(err) {
     return def;
   }
 }
 
 function handleWindowEvent(e) {
   if (this._browserElementParents) {
-    let beps = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(this._browserElementParents);
+    let beps = ChromeUtils.nondeterministicGetWeakMapKeys(this._browserElementParents);
     beps.forEach(bep => bep._handleOwnerEvent(e));
   }
 }
 
 function defineNoReturnMethod(fn) {
   return function method() {
     if (!this._domRequestReady) {
       // Remote browser haven't been created, we just queue the API call.
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -21078,29 +21078,42 @@ FactoryOp::FinishSendResults()
 nsresult
 FactoryOp::CheckPermission(ContentParent* aContentParent,
                            PermissionRequestBase::PermissionValue* aPermission)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mState == State::Initial || mState == State::PermissionRetry);
 
   const PrincipalInfo& principalInfo = mCommonParams.principalInfo();
-  if (principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo &&
-      NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) {
-    if (aContentParent) {
-      // The DOM in the other process should have kept us from receiving any
-      // indexedDB messages so assume that the child is misbehaving.
-      aContentParent->KillHard("IndexedDB CheckPermission 1");
-    }
-    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
-  }
-
-  if (NS_WARN_IF(mCommonParams.privateBrowsingMode())) {
-    // XXX This is only temporary.
-    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
+  if (principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
+    if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo) {
+      if (aContentParent) {
+        // We just want ContentPrincipalInfo or SystemPrincipalInfo.
+        aContentParent->KillHard("IndexedDB CheckPermission 0");
+      }
+
+      return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
+    }
+
+    if (NS_WARN_IF(!Preferences::GetBool(kPrefIndexedDBEnabled, false))) {
+      if (aContentParent) {
+        // The DOM in the other process should have kept us from receiving any
+        // indexedDB messages so assume that the child is misbehaving.
+        aContentParent->KillHard("IndexedDB CheckPermission 1");
+      }
+
+      return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
+    }
+
+    const ContentPrincipalInfo& contentPrincipalInfo =
+      principalInfo.get_ContentPrincipalInfo();
+    if (contentPrincipalInfo.attrs().mPrivateBrowsingId != 0) {
+      // IndexedDB is currently disabled in privateBrowsing.
+      return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
+    }
   }
 
   mFileHandleDisabled = !Preferences::GetBool(kPrefFileHandleEnabled);
 
   PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
 
   MOZ_ASSERT(principalInfo.type() != PrincipalInfo::TNullPrincipalInfo);
 
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -607,17 +607,16 @@ IDBFactory::OpenInternal(JSContext* aCx,
                          bool aDeleting,
                          CallerType aCallerType,
                          ErrorResult& aRv)
 {
   MOZ_ASSERT(mWindow || mOwningObject);
   MOZ_ASSERT_IF(!mWindow, !mPrivateBrowsingMode);
 
   CommonFactoryRequestParams commonParams;
-  commonParams.privateBrowsingMode() = mPrivateBrowsingMode;
 
   PrincipalInfo& principalInfo = commonParams.principalInfo();
 
   if (aPrincipal) {
     if (!NS_IsMainThread()) {
       MOZ_CRASH("Figure out security checks for workers!  What's this "
                 "aPrincipal we have on a worker thread?");
     }
--- a/dom/indexedDB/PBackgroundIDBFactory.ipdl
+++ b/dom/indexedDB/PBackgroundIDBFactory.ipdl
@@ -17,17 +17,16 @@ using struct mozilla::void_t
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 struct CommonFactoryRequestParams
 {
   DatabaseMetadata metadata;
   PrincipalInfo principalInfo;
-  bool privateBrowsingMode;
 };
 
 struct OpenDatabaseRequestParams
 {
   CommonFactoryRequestParams commonParams;
 };
 
 struct DeleteDatabaseRequestParams
--- a/dom/ipc/ContentPrefs.cpp
+++ b/dom/ipc/ContentPrefs.cpp
@@ -176,21 +176,16 @@ const char* mozilla::dom::ContentPrefs::
   "media.webspeech.test.enable",
   "media.webspeech.test.fake_fsm_events",
   "media.webspeech.test.fake_recognition_service",
   "media.wmf.allow-unsupported-resolutions",
   "media.wmf.decoder.thread-count",
   "media.wmf.enabled",
   "media.wmf.skip-blacklist",
   "media.wmf.vp9.enabled",
-  "memory.free_dirty_pages",
-  "memory.low_commit_space_threshold_mb",
-  "memory.low_memory_notification_interval_ms",
-  "memory.low_physical_memory_threshold_mb",
-  "memory.low_virtual_mem_threshold_mb",
   "network.IDN.blacklist_chars",
   "network.IDN.restriction_profile",
   "network.IDN.use_whitelist",
   "network.IDN_show_punycode",
   "network.buffer.cache.count",
   "network.buffer.cache.size",
   "network.captive-portal-service.enabled",
   "network.cookie.cookieBehavior",
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -867,41 +867,21 @@ struct ParamTraits<mozilla::plugins::_Op
   {
     aLog->append(StringPrintf(L"[%S, %S, %S, %d, %d]", aParam.mCustomFilterOut.c_str(),
                               aParam.mFile.c_str(), aParam.mFileTitle.c_str(),
                               aParam.mFileOffset, aParam.mFileExtension));
   }
 };
 
 template <>
-struct ParamTraits<mozilla::plugins::GetFileNameFunc>
-{
-  typedef mozilla::plugins::GetFileNameFunc paramType;
-
-  static void Write(Message* aMsg, const paramType& aParam)
-  {
-    WriteParam(aMsg, static_cast<uint32_t>(aParam));
-  }
-
-  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
-  {
-    uint32_t result;
-    if (ReadParam(aMsg, aIter, &result)) {
-      *aResult = static_cast<paramType>(result);
-      return true;
-    }
-    return false;
-  }
-
-  static void Log(const paramType& aParam, std::wstring* aLog)
-  {
-    aLog->append(StringPrintf(L"[%S]",
-                 aParam == mozilla::plugins::OPEN_FUNC ? "GetOpenFileName" : "GetSaveFileName"));
-  }
-};
+struct ParamTraits<mozilla::plugins::GetFileNameFunc> :
+  public ContiguousEnumSerializerInclusive<mozilla::plugins::GetFileNameFunc,
+                                           mozilla::plugins::OPEN_FUNC,
+                                           mozilla::plugins::SAVE_FUNC>
+{};
 #endif  // XP_WIN
 
 } /* namespace IPC */
 
 
 // Serializing NPEvents is completely platform-specific and can be rather
 // intricate depending on the platform.  So for readability we split it
 // into separate files and have the only macro crud live here.
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -2942,16 +2942,19 @@ PluginModuleChromeParent::AnswerGetFileN
     aOfnIn.AddToOfn(&ofn);
     switch (aFunc) {
     case OPEN_FUNC:
         *aResult = GetOpenFileName(&ofn);
         break;
     case SAVE_FUNC:
         *aResult = GetSaveFileName(&ofn);
         break;
+    default:
+        *aResult = false;
+        break;
     }
     if (*aResult) {
         if (ofn.Flags & OFN_ALLOWMULTISELECT) {
             // We only support multiselect with the OFN_EXPLORER flag.
             // This guarantees that ofn.lpstrFile follows the pattern below.
             MOZ_ASSERT(ofn.Flags & OFN_EXPLORER);
 
             // lpstrFile is one of two things:
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -338,18 +338,16 @@ var interfaceNamesInGlobalScope =
     "ErrorEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Event",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "EventSource",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "EventTarget",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "External",
-// IMPORTANT: Do not change this list without review from a DOM peer!
     "File",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FileList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FileReader",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FileSystem",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/vr/VRDisplay.cpp
+++ b/dom/vr/VRDisplay.cpp
@@ -653,16 +653,21 @@ VRDisplay::GetLayers(nsTArray<VRLayer>& 
   }
 }
 
 void
 VRDisplay::SubmitFrame()
 {
   AUTO_PROFILER_TRACING("VR", "SubmitFrameAtVRDisplay");
 
+  if (mClient && !mClient->IsPresentationGenerationCurrent()) {
+    mPresentation = nullptr;
+    mClient->MakePresentationGenerationCurrent();
+  }
+
   if (mPresentation) {
     mPresentation->SubmitFrame();
   }
   mFrameInfo.Clear();
 }
 
 int32_t
 VRDisplay::RequestAnimationFrame(FrameRequestCallback& aCallback,
--- a/dom/webidl/ChromeUtils.webidl
+++ b/dom/webidl/ChromeUtils.webidl
@@ -1,120 +1,210 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /**
- * A collection of static utility methods that are only exposed to Chrome. This
- * interface is not exposed in workers, while ThreadSafeChromeUtils is.
+ * A collection of static utility methods that are only exposed to system code.
+ * This is exposed in all the system globals where we can expose stuff by
+ * default, so should only include methods that are **thread-safe**.
  */
-[ChromeOnly, Exposed=(Window,System)]
-interface ChromeUtils : ThreadSafeChromeUtils {
+[ChromeOnly, Exposed=(Window,System,Worker)]
+namespace ChromeUtils {
+  /**
+   * Serialize a snapshot of the heap graph, as seen by |JS::ubi::Node| and
+   * restricted by |boundaries|, and write it to the provided file path.
+   *
+   * @param boundaries        The portion of the heap graph to write.
+   *
+   * @returns                 The path to the file the heap snapshot was written
+   *                          to. This is guaranteed to be within the temp
+   *                          directory and its file name will match the regexp
+   *                          `\d+(\-\d+)?\.fxsnapshot`.
+   */
+  [Throws]
+  DOMString saveHeapSnapshot(optional HeapSnapshotBoundaries boundaries);
+
+  /**
+   * This is the same as saveHeapSnapshot, but with a different return value.
+   *
+   * @returns                 The snapshot ID of the file. This is the file name
+   *                          without the temp directory or the trailing
+   *                          `.fxsnapshot`.
+   */
+  [Throws]
+  DOMString saveHeapSnapshotGetId(optional HeapSnapshotBoundaries boundaries);
+
+  /**
+   * Deserialize a core dump into a HeapSnapshot.
+   *
+   * @param filePath          The file path to read the heap snapshot from.
+   */
+  [Throws, NewObject]
+  HeapSnapshot readHeapSnapshot(DOMString filePath);
+
+  /**
+   * Return the keys in a weak map.  This operation is
+   * non-deterministic because it is affected by the scheduling of the
+   * garbage collector and the cycle collector.
+   *
+   * @param aMap weak map or other JavaScript value
+   * @returns If aMap is a weak map object, return the keys of the weak
+   *          map as an array.  Otherwise, return undefined.
+   */
+  [Throws, NewObject]
+  any nondeterministicGetWeakMapKeys(any map);
+
+  /**
+   * Return the keys in a weak set.  This operation is
+   * non-deterministic because it is affected by the scheduling of the
+   * garbage collector and the cycle collector.
+   *
+   * @param aSet weak set or other JavaScript value
+   * @returns If aSet is a weak set object, return the keys of the weak
+   *          set as an array.  Otherwise, return undefined.
+   */
+  [Throws, NewObject]
+  any nondeterministicGetWeakSetKeys(any aSet);
+
+  /**
+   * Converts a buffer to a Base64 URL-encoded string per RFC 4648.
+   *
+   * @param source The buffer to encode.
+   * @param options Additional encoding options.
+   * @returns The encoded string.
+   */
+  [Throws]
+  ByteString base64URLEncode(BufferSource source,
+                             Base64URLEncodeOptions options);
+
+  /**
+   * Decodes a Base64 URL-encoded string per RFC 4648.
+   *
+   * @param string The string to decode.
+   * @param options Additional decoding options.
+   * @returns The decoded buffer.
+   */
+  [Throws, NewObject]
+  ArrayBuffer base64URLDecode(ByteString string,
+                              Base64URLDecodeOptions options);
+
+  /**
+   * IF YOU ADD NEW METHODS HERE, MAKE SURE THEY ARE THREAD-SAFE.
+   */
+};
+
+/**
+ * Additional ChromeUtils methods that are _not_ thread-safe, and hence not
+ * exposed in workers.
+ */
+[Exposed=(Window,System)]
+partial namespace ChromeUtils {
   /**
    * A helper that converts OriginAttributesDictionary to a opaque suffix string.
    *
    * @param originAttrs       The originAttributes from the caller.
    */
-  static ByteString
+  ByteString
   originAttributesToSuffix(optional OriginAttributesDictionary originAttrs);
 
   /**
    * Returns true if the members of |originAttrs| match the provided members
    * of |pattern|.
    *
    * @param originAttrs       The originAttributes under consideration.
    * @param pattern           The pattern to use for matching.
    */
-  static boolean
+  boolean
   originAttributesMatchPattern(optional OriginAttributesDictionary originAttrs,
                                optional OriginAttributesPatternDictionary pattern);
 
   /**
    * Returns an OriginAttributesDictionary with values from the |origin| suffix
    * and unspecified attributes added and assigned default values.
    *
    * @param origin            The origin URI to create from.
    * @returns                 An OriginAttributesDictionary with values from
    *                          the origin suffix and unspecified attributes
    *                          added and assigned default values.
    */
   [Throws]
-  static OriginAttributesDictionary
+  OriginAttributesDictionary
   createOriginAttributesFromOrigin(DOMString origin);
 
   /**
    * Returns an OriginAttributesDictionary that is a copy of |originAttrs| with
    * unspecified attributes added and assigned default values.
    *
    * @param originAttrs       The origin attributes to copy.
    * @returns                 An OriginAttributesDictionary copy of |originAttrs|
    *                          with unspecified attributes added and assigned
    *                          default values.
    */
-  static OriginAttributesDictionary
+  OriginAttributesDictionary
   fillNonDefaultOriginAttributes(optional OriginAttributesDictionary originAttrs);
 
   /**
    * Returns true if the 2 OriginAttributes are equal.
    */
-  static boolean
+  boolean
   isOriginAttributesEqual(optional OriginAttributesDictionary aA,
                           optional OriginAttributesDictionary aB);
 
   /**
    * Loads and compiles the script at the given URL and returns an object
    * which may be used to execute it repeatedly, in different globals, without
    * re-parsing.
    */
-  [NewObject, Throws]
-  static Promise<PrecompiledScript>
+  [NewObject]
+  Promise<PrecompiledScript>
   compileScript(DOMString url, optional CompileScriptOptionsDictionary options);
 
   /**
    * Waive Xray on a given value. Identity op for primitives.
    */
   [Throws]
-  static any waiveXrays(any val);
+  any waiveXrays(any val);
 
   /**
    * Strip off Xray waivers on a given value. Identity op for primitives.
    */
   [Throws]
-  static any unwaiveXrays(any val);
+  any unwaiveXrays(any val);
 
   /**
    * Gets the name of the JSClass of the object.
    *
    * if |unwrap| is true, all wrappers are unwrapped first. Unless you're
    * specifically trying to detect whether the object is a proxy, this is
    * probably what you want.
    */
-  static DOMString getClassName(object obj, optional boolean unwrap = true);
+  DOMString getClassName(object obj, optional boolean unwrap = true);
 
   /**
    * Clones the properties of the given object into a new object in the given
    * target compartment (or the caller compartment if no target is provided).
    * Property values themeselves are not cloned.
    *
    * Ignores non-enumerable properties, properties on prototypes, and properties
    * with getters or setters.
    */
   [Throws]
-  static object shallowClone(object obj, optional object? target = null);
+  object shallowClone(object obj, optional object? target = null);
 
   /**
    * Dispatches the given callback to the main thread when it would be
    * otherwise idle. Similar to Window.requestIdleCallback, but not bound to a
    * particular DOM windw.
    */
   [Throws]
-  static void idleDispatch(IdleRequestCallback callback,
-                           optional IdleRequestOptions options);
+  void idleDispatch(IdleRequestCallback callback,
+                    optional IdleRequestOptions options);
 };
 
 /**
  * Used by principals and the script security manager to represent origin
  * attributes. The first dictionary is designed to contain the full set of
  * OriginAttributes, the second is used for pattern-matching (i.e. does this
  * OriginAttributesDictionary match the non-empty attributes in this pattern).
  *
@@ -154,8 +244,68 @@ dictionary CompileScriptOptionsDictionar
   /**
    * If true, the script will be compiled so that its last expression will be
    * returned as the value of its execution. This makes certain types of
    * optimization impossible, and disables the JIT in many circumstances, so
    * should not be used when not absolutely necessary.
    */
   boolean hasReturnValue = false;
 };
+
+/**
+ * A JS object whose properties specify what portion of the heap graph to
+ * write. The recognized properties are:
+ *
+ * * globals: [ global, ... ]
+ *   Dump only nodes that either:
+ *   - belong in the compartment of one of the given globals;
+ *   - belong to no compartment, but do belong to a Zone that contains one of
+ *     the given globals;
+ *   - are referred to directly by one of the last two kinds of nodes; or
+ *   - is the fictional root node, described below.
+ *
+ * * debugger: Debugger object
+ *   Like "globals", but use the Debugger's debuggees as the globals.
+ *
+ * * runtime: true
+ *   Dump the entire heap graph, starting with the JSRuntime's roots.
+ *
+ * One, and only one, of these properties must exist on the boundaries object.
+ *
+ * The root of the dumped graph is a fictional node whose ubi::Node type name is
+ * "CoreDumpRoot". If we are dumping the entire ubi::Node graph, this root node
+ * has an edge for each of the JSRuntime's roots. If we are dumping a selected
+ * set of globals, the root has an edge to each global, and an edge for each
+ * incoming JS reference to the selected Zones.
+ */
+dictionary HeapSnapshotBoundaries {
+  sequence<object> globals;
+  object           debugger;
+  boolean          runtime;
+};
+
+dictionary Base64URLEncodeOptions {
+  /** Specifies whether the output should be padded with "=" characters. */
+  required boolean pad;
+};
+
+enum Base64URLDecodePadding {
+  /**
+   * Fails decoding if the input is unpadded. RFC 4648, section 3.2 requires
+   * padding, unless the referring specification prohibits it.
+   */
+  "require",
+
+  /** Tolerates padded and unpadded input. */
+  "ignore",
+
+  /**
+   * Fails decoding if the input is padded. This follows the strict base64url
+   * variant used in JWS (RFC 7515, Appendix C) and HTTP Encrypted
+   * Content-Encoding (draft-ietf-httpbis-encryption-encoding-01).
+   */
+  "reject"
+};
+
+dictionary Base64URLDecodeOptions {
+  /** Specifies the padding mode for decoding the input. */
+  required Base64URLDecodePadding padding;
+};
--- a/dom/webidl/External.webidl
+++ b/dom/webidl/External.webidl
@@ -1,15 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-[JSImplementation="@mozilla.org/sidebar;1"]
+[NoInterfaceObject, JSImplementation="@mozilla.org/sidebar;1"]
 interface External
 {
   [UnsafeInPrerendering] void AddSearchProvider(DOMString aDescriptionURL);
   unsigned long IsSearchProviderInstalled(DOMString aSearchURL);
 };
 
 // Mozilla extension
 partial interface External {
deleted file mode 100644
--- a/dom/webidl/ThreadSafeChromeUtils.webidl
+++ /dev/null
@@ -1,150 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-/**
- * A collection of **thread-safe** static utility methods that are only exposed
- * to Chrome. This interface is exposed in workers, while ChromeUtils is not.
- */
-[ChromeOnly, Exposed=(Window,System,Worker)]
-interface ThreadSafeChromeUtils {
-  /**
-   * Serialize a snapshot of the heap graph, as seen by |JS::ubi::Node| and
-   * restricted by |boundaries|, and write it to the provided file path.
-   *
-   * @param boundaries        The portion of the heap graph to write.
-   *
-   * @returns                 The path to the file the heap snapshot was written
-   *                          to. This is guaranteed to be within the temp
-   *                          directory and its file name will match the regexp
-   *                          `\d+(\-\d+)?\.fxsnapshot`.
-   */
-  [Throws]
-  static DOMString saveHeapSnapshot(optional HeapSnapshotBoundaries boundaries);
-
-  /**
-   * This is the same as saveHeapSnapshot, but with a different return value.
-   *
-   * @returns                 The snapshot ID of the file. This is the file name
-   *                          without the temp directory or the trailing
-   *                          `.fxsnapshot`.
-   */
-  [Throws]
-  static DOMString saveHeapSnapshotGetId(optional HeapSnapshotBoundaries boundaries);
-
-  /**
-   * Deserialize a core dump into a HeapSnapshot.
-   *
-   * @param filePath          The file path to read the heap snapshot from.
-   */
-  [Throws, NewObject]
-  static HeapSnapshot readHeapSnapshot(DOMString filePath);
-
-  /**
-   * Return the keys in a weak map.  This operation is
-   * non-deterministic because it is affected by the scheduling of the
-   * garbage collector and the cycle collector.
-   *
-   * @param aMap weak map or other JavaScript value
-   * @returns If aMap is a weak map object, return the keys of the weak
-   *          map as an array.  Otherwise, return undefined.
-   */
-  [Throws, NewObject]
-  static any nondeterministicGetWeakMapKeys(any map);
-
-  /**
-   * Return the keys in a weak set.  This operation is
-   * non-deterministic because it is affected by the scheduling of the
-   * garbage collector and the cycle collector.
-   *
-   * @param aSet weak set or other JavaScript value
-   * @returns If aSet is a weak set object, return the keys of the weak
-   *          set as an array.  Otherwise, return undefined.
-   */
-  [Throws, NewObject]
-  static any nondeterministicGetWeakSetKeys(any aSet);
-
-  /**
-   * Converts a buffer to a Base64 URL-encoded string per RFC 4648.
-   *
-   * @param source The buffer to encode.
-   * @param options Additional encoding options.
-   * @returns The encoded string.
-   */
-  [Throws]
-  static ByteString base64URLEncode(BufferSource source,
-                                    Base64URLEncodeOptions options);
-
-  /**
-   * Decodes a Base64 URL-encoded string per RFC 4648.
-   *
-   * @param string The string to decode.
-   * @param options Additional decoding options.
-   * @returns The decoded buffer.
-   */
-  [Throws, NewObject]
-  static ArrayBuffer base64URLDecode(ByteString string,
-                                     Base64URLDecodeOptions options);
-};
-
-/**
- * A JS object whose properties specify what portion of the heap graph to
- * write. The recognized properties are:
- *
- * * globals: [ global, ... ]
- *   Dump only nodes that either:
- *   - belong in the compartment of one of the given globals;
- *   - belong to no compartment, but do belong to a Zone that contains one of
- *     the given globals;
- *   - are referred to directly by one of the last two kinds of nodes; or
- *   - is the fictional root node, described below.
- *
- * * debugger: Debugger object
- *   Like "globals", but use the Debugger's debuggees as the globals.
- *
- * * runtime: true
- *   Dump the entire heap graph, starting with the JSRuntime's roots.
- *
- * One, and only one, of these properties must exist on the boundaries object.
- *
- * The root of the dumped graph is a fictional node whose ubi::Node type name is
- * "CoreDumpRoot". If we are dumping the entire ubi::Node graph, this root node
- * has an edge for each of the JSRuntime's roots. If we are dumping a selected
- * set of globals, the root has an edge to each global, and an edge for each
- * incoming JS reference to the selected Zones.
- */
-dictionary HeapSnapshotBoundaries {
-  sequence<object> globals;
-  object           debugger;
-  boolean          runtime;
-};
-
-dictionary Base64URLEncodeOptions {
-  /** Specifies whether the output should be padded with "=" characters. */
-  required boolean pad;
-};
-
-enum Base64URLDecodePadding {
-  /**
-   * Fails decoding if the input is unpadded. RFC 4648, section 3.2 requires
-   * padding, unless the referring specification prohibits it.
-   */
-  "require",
-
-  /** Tolerates padded and unpadded input. */
-  "ignore",
-
-  /**
-   * Fails decoding if the input is padded. This follows the strict base64url
-   * variant used in JWS (RFC 7515, Appendix C) and HTTP Encrypted
-   * Content-Encoding (draft-ietf-httpbis-encryption-encoding-01).
-   */
-  "reject"
-};
-
-dictionary Base64URLDecodeOptions {
-  /** Specifies the padding mode for decoding the input. */
-  required Base64URLDecodePadding padding;
-};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -308,19 +308,16 @@ with Files("SubtleCrypto.webidl"):
     BUG_COMPONENT = ("Core", "DOM: Security")
 
 with Files("TCP*"):
     BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
 
 with Files("TextTrack*"):
     BUG_COMPONENT = ("Core", "Audio/Video")
 
-with Files("ThreadSafeChromeUtils.webidl"):
-    BUG_COMPONENT = ("Firefox", "Developer Tools: Memory")
-
 with Files("TrackEvent.webidl"):
     BUG_COMPONENT = ("Core", "Audio/Video")
 
 with Files("U2F.webidl"):
     BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
 
 with Files("UDP*"):
     BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
@@ -923,17 +920,16 @@ WEBIDL_FILES = [
     'Text.webidl',
     'TextClause.webidl',
     'TextDecoder.webidl',
     'TextEncoder.webidl',
     'TextTrack.webidl',
     'TextTrackCue.webidl',
     'TextTrackCueList.webidl',
     'TextTrackList.webidl',
-    'ThreadSafeChromeUtils.webidl',
     'TimeEvent.webidl',
     'TimeRanges.webidl',
     'Touch.webidl',
     'TouchEvent.webidl',
     'TouchList.webidl',
     'TransitionEvent.webidl',
     'TreeBoxObject.webidl',
     'TreeColumn.webidl',
--- a/dom/worklet/Worklet.cpp
+++ b/dom/worklet/Worklet.cpp
@@ -196,19 +196,21 @@ public:
 
     AutoEntryScript aes(globalScope, "Worklet");
     JSContext* cx = aes.cx();
 
     JS::Rooted<JSObject*> globalObj(cx, globalScope->GetGlobalJSObject());
 
     (void) new XPCWrappedNativeScope(cx, globalObj);
 
+    NS_ConvertUTF16toUTF8 url(mURL);
+
     JS::CompileOptions compileOptions(cx);
     compileOptions.setIntroductionType("Worklet");
-    compileOptions.setFileAndLine(NS_ConvertUTF16toUTF8(mURL).get(), 0);
+    compileOptions.setFileAndLine(url.get(), 0);
     compileOptions.setVersion(JSVERSION_DEFAULT);
     compileOptions.setIsRunOnce(true);
     compileOptions.setNoScriptRval(true);
 
     JSAutoCompartment comp(cx, globalObj);
 
     JS::Rooted<JS::Value> unused(cx);
     if (!JS::Evaluate(cx, compileOptions, buffer, &unused)) {
--- a/editor/libeditor/InsertNodeTransaction.cpp
+++ b/editor/libeditor/InsertNodeTransaction.cpp
@@ -61,16 +61,17 @@ InsertNodeTransaction::DoTransaction()
 
   // Note, it's ok for ref to be null. That means append.
   nsCOMPtr<nsIContent> ref = mParent->GetChildAt(mOffset);
 
   mEditorBase->MarkNodeDirty(GetAsDOMNode(mNode));
 
   ErrorResult rv;
   mParent->InsertBefore(*mNode, ref, rv);
+  rv.WouldReportJSException();
   NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
 
   // Only set selection to insertion point if editor gives permission
   if (mEditorBase->GetShouldTxnSetSelection()) {
     RefPtr<Selection> selection = mEditorBase->GetSelection();
     NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
     // Place the selection just after the inserted element
     selection->Collapse(mParent, mOffset + 1);
--- a/gfx/2d/ScaledFontFontconfig.cpp
+++ b/gfx/2d/ScaledFontFontconfig.cpp
@@ -394,18 +394,19 @@ ScaledFontFontconfig::CreateFromInstance
                                              Float aSize,
                                              NativeFontResource* aNativeFontResource)
 {
   FcPattern* pattern = FcPatternCreate();
   if (!pattern) {
     gfxWarning() << "Failing initializing Fontconfig pattern for scaled font";
     return nullptr;
   }
-  if (aUnscaledFont->GetFace()) {
-    FcPatternAddFTFace(pattern, FC_FT_FACE, aUnscaledFont->GetFace());
+  FT_Face face = aUnscaledFont->GetFace();
+  if (face) {
+    FcPatternAddFTFace(pattern, FC_FT_FACE, face);
   } else {
     FcPatternAddString(pattern, FC_FILE, reinterpret_cast<const FcChar8*>(aUnscaledFont->GetFile()));
     FcPatternAddInteger(pattern, FC_INDEX, aUnscaledFont->GetIndex());
   }
   FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aSize);
   aInstanceData.SetupPattern(pattern);
 
   cairo_font_face_t* font = cairo_ft_font_face_create_for_pattern(pattern);
@@ -415,20 +416,27 @@ ScaledFontFontconfig::CreateFromInstance
     return nullptr;
   }
 
   if (aNativeFontResource) {
     // Bug 1362117 - Cairo may keep the font face alive after the owning NativeFontResource
     // was freed. To prevent this, we must bind the NativeFontResource to the font face so that
     // it stays alive at least as long as the font face.
     aNativeFontResource->AddRef();
-    if (cairo_font_face_set_user_data(font,
-                                      &sNativeFontResourceKey,
-                                      aNativeFontResource,
-                                      ReleaseNativeFontResource) != CAIRO_STATUS_SUCCESS) {
+    // Bug 1412545 - Setting Cairo font user data is not thread-safe. If Fontconfig patterns match,
+    // cairo_ft_font_face_create_for_pattern may share Cairo faces. We need to lock setting user data
+    // to prevent races if multiple threads are thus sharing the same Cairo face.
+    FT_Library library = face ? face->glyph->library : Factory::GetFTLibrary();
+    Factory::LockFTLibrary(library);
+    cairo_status_t err = cairo_font_face_set_user_data(font,
+                                                       &sNativeFontResourceKey,
+                                                       aNativeFontResource,
+                                                       ReleaseNativeFontResource);
+    Factory::UnlockFTLibrary(library);
+    if (err != CAIRO_STATUS_SUCCESS) {
       gfxWarning() << "Failed binding NativeFontResource to Cairo font face";
       aNativeFontResource->Release();
       cairo_font_face_destroy(font);
       FcPatternDestroy(pattern);
       return nullptr;
     }
   }
 
--- a/gfx/gl/GLBlitHelper.h
+++ b/gfx/gl/GLBlitHelper.h
@@ -31,16 +31,17 @@ class MacIOSurfaceImage;
 class SurfaceDescriptorD3D10;
 class SurfaceDescriptorDXGIYCbCr;
 } // namespace layers
 
 namespace gl {
 
 class BindAnglePlanes;
 class GLContext;
+class GLBlitHelper;
 
 bool
 GuessDivisors(const gfx::IntSize& ySize, const gfx::IntSize& uvSize,
               gfx::IntSize* const out_divisors);
 
 template<uint8_t N>
 struct Mat
 {
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -319,21 +319,18 @@ private:
   DECL_GFX_PREF(Live, "apz.max_velocity_inches_per_ms",        APZMaxVelocity, float, -1.0f);
   DECL_GFX_PREF(Once, "apz.max_velocity_queue_size",           APZMaxVelocityQueueSize, uint32_t, 5);
   DECL_GFX_PREF(Live, "apz.min_skate_speed",                   APZMinSkateSpeed, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.minimap.enabled",                   APZMinimap, bool, false);
   DECL_GFX_PREF(Live, "apz.minimap.visibility.enabled",        APZMinimapVisibilityEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.one_touch_pinch.enabled",           APZOneTouchPinchEnabled, bool, true);
   DECL_GFX_PREF(Live, "apz.overscroll.enabled",                APZOverscrollEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.overscroll.min_pan_distance_ratio", APZMinPanDistanceRatio, float, 1.0f);
-  DECL_GFX_PREF(Live, "apz.overscroll.spring_friction",        APZOverscrollSpringFriction, float, 0.015f);
   DECL_GFX_PREF(Live, "apz.overscroll.spring_stiffness",       APZOverscrollSpringStiffness, float, 0.001f);
   DECL_GFX_PREF(Live, "apz.overscroll.stop_distance_threshold", APZOverscrollStopDistanceThreshold, float, 5.0f);
-  DECL_GFX_PREF(Live, "apz.overscroll.stop_velocity_threshold", APZOverscrollStopVelocityThreshold, float, 0.01f);
-  DECL_GFX_PREF(Live, "apz.overscroll.stretch_factor",         APZOverscrollStretchFactor, float, 0.5f);
   DECL_GFX_PREF(Live, "apz.paint_skipping.enabled",            APZPaintSkipping, bool, true);
   DECL_GFX_PREF(Live, "apz.peek_messages.enabled",             APZPeekMessages, bool, true);
   DECL_GFX_PREF(Live, "apz.popups.enabled",                    APZPopupsEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.printtree",                         APZPrintTree, bool, false);
   DECL_GFX_PREF(Live, "apz.record_checkerboarding",            APZRecordCheckerboarding, bool, false);
   DECL_GFX_PREF(Live, "apz.second_tap_tolerance",              APZSecondTapTolerance, float, 0.5f);
   DECL_GFX_PREF(Live, "apz.test.fails_with_native_injection",  APZTestFailsWithNativeInjection, bool, false);
   DECL_GFX_PREF(Live, "apz.test.logging_enabled",              APZTestLoggingEnabled, bool, false);
@@ -366,24 +363,22 @@ private:
   DECL_GFX_PREF(Once, "dom.vr.openvr.enabled",                 VROpenVREnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.osvr.enabled",                   VROSVREnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.poseprediction.enabled",         VRPosePredictionEnabled, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.require-gesture",                VRRequireGesture, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.puppet.enabled",                 VRPuppetEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.puppet.submitframe",             VRPuppetSubmitFrame, uint32_t, 0);
   DECL_GFX_PREF(Live, "dom.vr.display.rafMaxDuration",         VRDisplayRafMaxDuration, uint32_t, 50);
   DECL_GFX_PREF(Live, "dom.w3c_pointer_events.enabled",        PointerEventsEnabled, bool, false);
-  DECL_GFX_PREF(Live, "dom.w3c_touch_events.enabled",          TouchEventsEnabled, int32_t, 0);
 
   DECL_GFX_PREF(Live, "general.smoothScroll",                  SmoothScrollEnabled, bool, true);
   DECL_GFX_PREF(Live, "general.smoothScroll.currentVelocityWeighting",
                 SmoothScrollCurrentVelocityWeighting, float, 0.25);
   DECL_GFX_PREF(Live, "general.smoothScroll.durationToIntervalRatio",
                 SmoothScrollDurationToIntervalRatio, int32_t, 200);
-  DECL_GFX_PREF(Live, "general.smoothScroll.lines",            LineSmoothScrollEnabled, bool, true);
   DECL_GFX_PREF(Live, "general.smoothScroll.lines.durationMaxMS",
                 LineSmoothScrollMaxDurationMs, int32_t, 150);
   DECL_GFX_PREF(Live, "general.smoothScroll.lines.durationMinMS",
                 LineSmoothScrollMinDurationMs, int32_t, 150);
   DECL_GFX_PREF(Live, "general.smoothScroll.mouseWheel",       WheelSmoothScrollEnabled, bool, true);
   DECL_GFX_PREF(Live, "general.smoothScroll.mouseWheel.durationMaxMS",
                 WheelSmoothScrollMaxDurationMs, int32_t, 400);
   DECL_GFX_PREF(Live, "general.smoothScroll.mouseWheel.durationMinMS",
@@ -392,17 +387,16 @@ private:
                 OtherSmoothScrollMaxDurationMs, int32_t, 150);
   DECL_GFX_PREF(Live, "general.smoothScroll.other.durationMinMS",
                 OtherSmoothScrollMinDurationMs, int32_t, 150);
   DECL_GFX_PREF(Live, "general.smoothScroll.pages",            PageSmoothScrollEnabled, bool, true);
   DECL_GFX_PREF(Live, "general.smoothScroll.pages.durationMaxMS",
                 PageSmoothScrollMaxDurationMs, int32_t, 150);
   DECL_GFX_PREF(Live, "general.smoothScroll.pages.durationMinMS",
                 PageSmoothScrollMinDurationMs, int32_t, 150);
-  DECL_GFX_PREF(Live, "general.smoothScroll.pixels",           PixelSmoothScrollEnabled, bool, true);
   DECL_GFX_PREF(Live, "general.smoothScroll.pixels.durationMaxMS",
                 PixelSmoothScrollMaxDurationMs, int32_t, 150);
   DECL_GFX_PREF(Live, "general.smoothScroll.pixels.durationMinMS",
                 PixelSmoothScrollMinDurationMs, int32_t, 150);
   DECL_GFX_PREF(Live, "general.smoothScroll.stopDecelerationWeighting",
                 SmoothScrollStopDecelerationWeighting, float, 0.4f);
 
   DECL_GFX_PREF(Live, "general.smoothScroll.msdPhysics.enabled",
@@ -475,44 +469,36 @@ private:
   DECL_GFX_PREF(Once, "gfx.logging.texture-usage.enabled",     GfxLoggingTextureUsageEnabled, bool, false);
   DECL_GFX_PREF(Once, "gfx.logging.peak-texture-usage.enabled",GfxLoggingPeakTextureUsageEnabled, bool, false);
   // Use gfxPlatform::MaxAllocSize instead of the pref directly
   DECL_GFX_PREF(Once, "gfx.max-alloc-size",                    MaxAllocSizeDoNotUseDirectly, int32_t, (int32_t)500000000);
   // Use gfxPlatform::MaxTextureSize instead of the pref directly
   DECL_GFX_PREF(Once, "gfx.max-texture-size",                  MaxTextureSizeDoNotUseDirectly, int32_t, (int32_t)32767);
   DECL_GFX_PREF(Live, "gfx.partialpresent.force",              PartialPresent, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled",             PerfWarnings, bool, false);
-  DECL_GFX_PREF(Live, "gfx.SurfaceTexture.detach.enabled",     SurfaceTextureDetachEnabled, bool, true);
   DECL_GFX_PREF(Live, "gfx.testing.device-reset",              DeviceResetForTesting, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.testing.device-fail",               DeviceFailForTesting, bool, false);
   DECL_GFX_PREF(Once, "gfx.text.disable-aa",                   DisableAllTextAA, bool, false);
   DECL_GFX_PREF(Live, "gfx.ycbcr.accurate-conversion",         YCbCrAccurateConversion, bool, false);
 
   // Disable surface sharing due to issues with compatible FBConfigs on
   // NVIDIA drivers as described in bug 1193015.
   DECL_GFX_PREF(Live, "gfx.use-glx-texture-from-pixmap",       UseGLXTextureFromPixmap, bool, false);
   DECL_GFX_PREF(Once, "gfx.use-iosurface-textures",            UseIOSurfaceTextures, bool, false);
   DECL_GFX_PREF(Once, "gfx.use-mutex-on-present",              UseMutexOnPresent, bool, false);
   DECL_GFX_PREF(Once, "gfx.use-surfacetexture-textures",       UseSurfaceTextureTextures, bool, false);
-  // These times should be in milliseconds
-  DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold",    TouchResampleVsyncDelayThreshold, int32_t, 20);
-  DECL_GFX_PREF(Once, "gfx.touch.resample.max-predict",        TouchResampleMaxPredict, int32_t, 8);
-  DECL_GFX_PREF(Once, "gfx.touch.resample.min-delta",          TouchResampleMinDelta, int32_t, 2);
-  DECL_GFX_PREF(Once, "gfx.touch.resample.old-touch-threshold",TouchResampleOldTouchThreshold, int32_t, 17);
-  DECL_GFX_PREF(Once, "gfx.touch.resample.vsync-adjust",       TouchVsyncSampleAdjust, int32_t, 5);
 
   DECL_GFX_PREF(Live, "gfx.vsync.collect-scroll-transforms",   CollectScrollTransforms, bool, false);
   DECL_GFX_PREF(Once, "gfx.vsync.compositor.unobserve-count",  CompositorUnobserveCount, int32_t, 10);
 
   DECL_GFX_PREF(Live, "gfx.webrender.blob-images",             WebRenderBlobImages, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.highlight-painted-layers",WebRenderHighlightPaintedLayers, bool, false);
 
   // Use vsync events generated by hardware
   DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs",           WorkAroundDriverBugs, bool, true);
-  DECL_GFX_PREF(Once, "gfx.screen-mirroring.enabled",          ScreenMirroringEnabled, bool, false);
 
   DECL_GFX_PREF(Live, "gl.ignore-dx-interop2-blacklist",       IgnoreDXInterop2Blacklist, bool, false);
   DECL_GFX_PREF(Live, "gl.msaa-level",                         MSAALevel, uint32_t, 2);
 #if defined(XP_MACOSX)
   DECL_GFX_PREF(Live, "gl.multithreaded",                      GLMultithreaded, bool, false);
 #endif
   DECL_GFX_PREF(Live, "gl.require-hardware",                   RequireHardwareGL, bool, false);
   DECL_GFX_PREF(Live, "gl.use-tls-is-current",                 UseTLSIsCurrent, int32_t, 0);
@@ -566,17 +552,16 @@ private:
   // If MOZ_GFX_OPTIMIZE_MOBILE is defined, we force component alpha off
   // and ignore the preference.
   DECL_GFX_PREF(Skip, "layers.componentalpha.enabled",         ComponentAlphaEnabled, bool, false);
 #else
   // If MOZ_GFX_OPTIMIZE_MOBILE is not defined, we actually take the
   // preference value, defaulting to true.
   DECL_GFX_PREF(Once, "layers.componentalpha.enabled",         ComponentAlphaEnabled, bool, true);
 #endif
-  DECL_GFX_PREF(Live, "layers.composer2d.enabled",             Composer2DCompositionEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.d3d11.force-warp",               LayersD3D11ForceWARP, bool, false);
   DECL_GFX_PREF(Live, "layers.deaa.enabled",                   LayersDEAAEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.draw-bigimage-borders",          DrawBigImageBorders, bool, false);
   DECL_GFX_PREF(Live, "layers.draw-borders",                   DrawLayerBorders, bool, false);
   DECL_GFX_PREF(Live, "layers.draw-tile-borders",              DrawTileBorders, bool, false);
   DECL_GFX_PREF(Live, "layers.draw-layer-info",                DrawLayerInfo, bool, false);
   DECL_GFX_PREF(Live, "layers.dump",                           LayersDump, bool, false);
   DECL_GFX_PREF(Live, "layers.dump-texture",                   LayersDumpTexture, bool, false);
@@ -619,17 +604,16 @@ private:
   DECL_GFX_PREF(Live, "layers.offmainthreadcomposition.frame-rate", LayersCompositionFrameRate, int32_t,-1);
   DECL_GFX_PREF(Live, "layers.omtp.force-sync",                LayersOMTPForceSync, bool, false);
   DECL_GFX_PREF(Live, "layers.omtp.release-capture-on-main-thread", LayersOMTPReleaseCaptureOnMainThread, bool, false);
   DECL_GFX_PREF(Live, "layers.orientation.sync.timeout",       OrientationSyncMillis, uint32_t, (uint32_t)0);
   DECL_GFX_PREF(Once, "layers.prefer-opengl",                  LayersPreferOpenGL, bool, false);
   DECL_GFX_PREF(Live, "layers.progressive-paint",              ProgressivePaint, bool, false);
   DECL_GFX_PREF(Live, "layers.shared-buffer-provider.enabled", PersistentBufferProviderSharedEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.single-tile.enabled",            LayersSingleTileEnabled, bool, true);
-  DECL_GFX_PREF(Once, "layers.stereo-video.enabled",           StereoVideoEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.force-synchronous-resize",       LayersForceSynchronousResize, bool, true);
 
   // We allow for configurable and rectangular tile size to avoid wasting memory on devices whose
   // screen size does not align nicely to the default tile size. Although layers can be any size,
   // they are often the same size as the screen, especially for width.
   DECL_GFX_PREF(Once, "layers.tile-width",                     LayersTileWidth, int32_t, 256);
   DECL_GFX_PREF(Once, "layers.tile-height",                    LayersTileHeight, int32_t, 256);
   DECL_GFX_PREF(Once, "layers.tile-initial-pool-size",         LayersTileInitialPoolSize, uint32_t, (uint32_t)50);
--- a/gfx/vr/VRDisplayClient.cpp
+++ b/gfx/vr/VRDisplayClient.cpp
@@ -28,16 +28,17 @@ using namespace mozilla;
 using namespace mozilla::gfx;
 
 VRDisplayClient::VRDisplayClient(const VRDisplayInfo& aDisplayInfo)
   : mDisplayInfo(aDisplayInfo)
   , bLastEventWasMounted(false)
   , bLastEventWasPresenting(false)
   , mPresentationCount(0)
   , mLastEventFrameId(0)
+  , mLastPresentingGeneration(0)
 {
   MOZ_COUNT_CTOR(VRDisplayClient);
 }
 
 VRDisplayClient::~VRDisplayClient() {
   MOZ_COUNT_DTOR(VRDisplayClient);
 }
 
@@ -72,16 +73,32 @@ VRDisplayClient::ZeroSensor()
 
 void
 VRDisplayClient::SetGroupMask(uint32_t aGroupMask)
 {
   VRManagerChild *vm = VRManagerChild::Get();
   vm->SendSetGroupMask(mDisplayInfo.mDisplayID, aGroupMask);
 }
 
+bool
+VRDisplayClient::IsPresentationGenerationCurrent() const
+{
+  if (mLastPresentingGeneration != mDisplayInfo.mPresentingGeneration) {
+    return false;
+  }
+
+  return true;
+}
+
+void
+VRDisplayClient::MakePresentationGenerationCurrent()
+{
+  mLastPresentingGeneration = mDisplayInfo.mPresentingGeneration;
+}
+
 void
 VRDisplayClient::FireEvents()
 {
   VRManagerChild *vm = VRManagerChild::Get();
   // Only fire these events for non-chrome VR sessions
   bool isPresenting = (mDisplayInfo.mPresentingGroups & kVRGroupContent) != 0;
 
   // Check if we need to trigger onVRDisplayPresentChange event
--- a/gfx/vr/VRDisplayClient.h
+++ b/gfx/vr/VRDisplayClient.h
@@ -39,28 +39,32 @@ public:
                                                             uint32_t aGroup);
   void PresentationDestroyed();
 
   bool GetIsConnected() const;
 
   void NotifyDisconnected();
   void SetGroupMask(uint32_t aGroupMask);
 
+  bool IsPresentationGenerationCurrent() const;
+  void MakePresentationGenerationCurrent();
+
 protected:
   virtual ~VRDisplayClient();
 
   void FireEvents();
 
   VRDisplayInfo mDisplayInfo;
 
   bool bLastEventWasMounted;
   bool bLastEventWasPresenting;
 
   int mPresentationCount;
   uint64_t mLastEventFrameId;
+  uint32_t mLastPresentingGeneration;
 private:
   VRSubmitFrameResultInfo mSubmitFrameResult;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* GFX_VR_DISPLAY_CLIENT_H */
--- a/gfx/vr/VRDisplayHost.cpp
+++ b/gfx/vr/VRDisplayHost.cpp
@@ -67,16 +67,17 @@ VRDisplayHost::VRDisplayHost(VRDeviceTyp
  , mFrameStarted(false)
 {
   MOZ_COUNT_CTOR(VRDisplayHost);
   mDisplayInfo.mType = aType;
   mDisplayInfo.mDisplayID = VRSystemManager::AllocateDisplayID();
   mDisplayInfo.mPresentingGroups = 0;
   mDisplayInfo.mGroupMask = kVRGroupContent;
   mDisplayInfo.mFrameId = 0;
+  mDisplayInfo.mPresentingGeneration = 0;
 }
 
 VRDisplayHost::~VRDisplayHost()
 {
   MOZ_COUNT_DTOR(VRDisplayHost);
 }
 
 #if defined(XP_WIN)
@@ -328,24 +329,33 @@ VRDisplayHost::SubmitFrame(VRLayerParent
       }
       IntSize texSize = gfx::IntSize(surf->GetDevicePixelWidth(),
                                      surf->GetDevicePixelHeight());
       if (!SubmitFrame(surf, texSize, aLeftEyeRect, aRightEyeRect)) {
         return;
       }
       break;
     }
+#elif defined(MOZ_ANDROID_GOOGLE_VR)
+    case SurfaceDescriptor::TEGLImageDescriptor: {
+       const EGLImageDescriptor& desc = aTexture.get_EGLImageDescriptor();
+       if (!SubmitFrame(&desc, aLeftEyeRect, aRightEyeRect)) {
+         return;
+       }
+       break;
+    }
 #endif
     default: {
       NS_WARNING("Unsupported SurfaceDescriptor type for VR layer texture");
       return;
     }
   }
 
-#if defined(XP_WIN) || defined(XP_MACOSX)
+#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_ANDROID_GOOGLE_VR)
+
   /**
    * Trigger the next VSync immediately after we are successfully
    * submitting frames.  As SubmitFrame is responsible for throttling
    * the render loop, if we don't successfully call it, we shouldn't trigger
    * NotifyVRVsync immediately, as it will run unbounded.
    * If NotifyVRVsync is not called here due to SubmitFrame failing, the
    * fallback "watchdog" code in VRDisplayHost::NotifyVSync() will cause
    * frames to continue at a lower refresh rate until frame submission
--- a/gfx/vr/VRDisplayHost.h
+++ b/gfx/vr/VRDisplayHost.h
@@ -80,16 +80,20 @@ protected:
                            const IntSize& aSize,
                            const gfx::Rect& aLeftEyeRect,
                            const gfx::Rect& aRightEyeRect) = 0;
 #elif defined(XP_MACOSX)
   virtual bool SubmitFrame(MacIOSurface* aMacIOSurface,
                            const IntSize& aSize,
                            const gfx::Rect& aLeftEyeRect,
                            const gfx::Rect& aRightEyeRect) = 0;
+#elif defined(MOZ_ANDROID_GOOGLE_VR)
+  virtual bool SubmitFrame(const mozilla::layers::EGLImageDescriptor* aDescriptor,
+                           const gfx::Rect& aLeftEyeRect,
+                           const gfx::Rect& aRightEyeRect) = 0;
 #endif
 
   VRDisplayInfo mDisplayInfo;
 
   nsTArray<RefPtr<VRLayerParent>> mLayers;
   // Weak reference to mLayers entries are cleared in
   // VRLayerParent destructor
 
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -20,16 +20,20 @@
 #include "gfxVR.h"
 #if defined(XP_WIN)
 #include "gfxVROculus.h"
 #endif
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
 #include "gfxVROpenVR.h"
 #include "gfxVROSVR.h"
 #endif
+#if defined(MOZ_ANDROID_GOOGLE_VR)
+#include "gfxVRGVR.h"
+#endif // MOZ_ANDROID_GOOGLE_VR
+
 #include "gfxVRPuppet.h"
 #include "ipc/VRLayerParent.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::gl;
 
@@ -88,16 +92,24 @@ VRManager::VRManager()
   }
 
   // OSVR is cross platform compatible
   mgr = VRSystemManagerOSVR::Create();
   if (mgr) {
       mManagers.AppendElement(mgr);
   }
 #endif
+
+#if defined(MOZ_ANDROID_GOOGLE_VR)
+   mgr = VRSystemManagerGVR::Create();
+   if (mgr) {
+     mManagers.AppendElement(mgr);
+   }
+#endif // defined(MOZ_ANDROID_GOOGLE_VR)
+
   // Enable gamepad extensions while VR is enabled.
   // Preference only can be set at the Parent process.
   if (XRE_IsParentProcess() && gfxPrefs::VREnabled()) {
     Preferences::SetBool("dom.gamepad.extensions.enabled", true);
   }
 }
 
 VRManager::~VRManager()
--- a/gfx/vr/gfxVR.h
+++ b/gfx/vr/gfxVR.h
@@ -30,16 +30,17 @@ namespace gfx {
 class VRLayerParent;
 class VRDisplayHost;
 class VRControllerHost;
 
 enum class VRDeviceType : uint16_t {
   Oculus,
   OpenVR,
   OSVR,
+  GVR,
   Puppet,
   NumVRDeviceTypes
 };
 
 enum class VRDisplayCapabilityFlags : uint16_t {
   Cap_None = 0,
   /**
    * Cap_Position is set if the VRDisplay is capable of tracking its position.
@@ -216,16 +217,17 @@ struct VRDisplayInfo
   IntSize mEyeResolution;
   bool mIsConnected;
   bool mIsMounted;
   uint32_t mPresentingGroups;
   uint32_t mGroupMask;
   Size mStageSize;
   Matrix4x4 mSittingToStandingTransform;
   uint64_t mFrameId;
+  uint32_t mPresentingGeneration;
   VRHMDSensorState mLastSensorState[kVRMaxLatencyFrames];
 
   bool operator==(const VRDisplayInfo& other) const {
     for (size_t i = 0; i < kVRMaxLatencyFrames; i++) {
       if (mLastSensorState[i] != other.mLastSensorState[i]) {
         return false;
       }
     }
@@ -239,17 +241,18 @@ struct VRDisplayInfo
            mPresentingGroups == other.mPresentingGroups &&
            mGroupMask == other.mGroupMask &&
            mEyeFOV[0] == other.mEyeFOV[0] &&
            mEyeFOV[1] == other.mEyeFOV[1] &&
            mEyeTranslation[0] == other.mEyeTranslation[0] &&
            mEyeTranslation[1] == other.mEyeTranslation[1] &&
            mStageSize == other.mStageSize &&
            mSittingToStandingTransform == other.mSittingToStandingTransform &&
-           mFrameId == other.mFrameId;
+           mFrameId == other.mFrameId &&
+           mPresentingGeneration == other.mPresentingGeneration;
   }
 
   bool operator!=(const VRDisplayInfo& other) const {
     return !(*this == other);
   }
 
   const VRHMDSensorState& GetSensorState() const
   {
new file mode 100644
--- /dev/null
+++ b/gfx/vr/gfxVRGVR.cpp
@@ -0,0 +1,799 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <math.h>
+
+#include "GLBlitHelper.h"
+#include "GLContextEGL.h"
+#include "GLContextProvider.h"
+#include "GLContextTypes.h"
+#include "GLImages.h"
+#include "GLLibraryEGL.h"
+
+#include "gfxPrefs.h"
+#include "gfxVRGVRAPI.h"
+#include "gfxVRGVR.h"
+
+#include "mozilla/dom/GamepadEventTypes.h"
+#include "mozilla/dom/GamepadBinding.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/gfx/Quaternion.h"
+#include "mozilla/jni/Utils.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/TextureHostOGL.h"
+#include "mozilla/Preferences.h"
+
+#include "GeckoVRManager.h"
+#include "nsString.h"
+
+#include "SurfaceTypes.h"
+
+#include "VRManager.h"
+
+#define MOZ_CHECK_GVR_ERRORS
+
+#if defined(MOZ_CHECK_GVR_ERRORS)
+#define GVR_LOGTAG "GeckoWebVR"
+#include <android/log.h>
+#define GVR_CHECK(X) X; \
+{ \
+  gvr_context* context = (mPresentingContext ? mPresentingContext : GetNonPresentingContext()); \
+  if (context && (gvr_get_error(context) != GVR_ERROR_NONE)) { \
+     __android_log_print(ANDROID_LOG_ERROR, GVR_LOGTAG, \
+                         "GVR ERROR: %s at%s:%s:%d", \
+                         gvr_get_error_string(gvr_get_error(context)), \
+                         __FILE__, __FUNCTION__, __LINE__); \
+    gvr_clear_error(context); \
+  } else if (!context) { \
+    __android_log_print(ANDROID_LOG_ERROR, GVR_LOGTAG, \
+                        "UNABLE TO CHECK GVR ERROR: NO CONTEXT"); \
+  } \
+}
+#define GVR_LOG(format, ...) __android_log_print(ANDROID_LOG_INFO, GVR_LOGTAG, format, ##__VA_ARGS__);
+#else
+#define GVR_CHECK(X) X
+#define GVR_LOG(...)
+#endif
+
+using namespace mozilla;
+using namespace mozilla::gl;
+using namespace mozilla::gfx;
+using namespace mozilla::gfx::impl;
+using namespace mozilla::layers;
+using namespace mozilla::dom;
+
+namespace {
+static VRDisplayGVR* sContextObserver;
+static RefPtr<GLContextEGL> sGLContextEGL;
+static gvr_context* sNonPresentingContext;
+
+gvr_context*
+GetNonPresentingContext() {
+  if (!sNonPresentingContext) {
+    // Try and restore if it has been lost
+    sNonPresentingContext = (gvr_context*)GeckoVRManager::CreateGVRNonPresentingContext();
+  }
+  return sNonPresentingContext;
+}
+
+class SynchronousRunnable : public nsIRunnable {
+public:
+  enum class Type {
+    PresentingContext,
+    NonPresentingContext,
+    Pause,
+    Resume
+  };
+  SynchronousRunnable(const Type aType, void* aContext)
+  : mType(aType)
+  , mContext(aContext)
+  , mUpdateMonitor(new Monitor("SynchronousRunnable_for_Android"))
+  , mUpdated(false)
+  {}
+  NS_DECL_THREADSAFE_ISUPPORTS
+  nsresult Run() override
+  {
+    MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+    MonitorAutoLock lock(*mUpdateMonitor);
+    if (mType == Type::PresentingContext) {
+      SetGVRPresentingContext(mContext);
+    } else if (mType == Type::NonPresentingContext) {
+      CleanupGVRNonPresentingContext();
+    } else if (mType == Type::Pause) {
+      SetGVRPaused(true);
+    } else if (mType == Type::Resume) {
+      SetGVRPaused(false);
+    } else {
+      GVR_LOG("UNKNOWN SynchronousRunnable::Type!");
+    }
+    mUpdated = true;
+    lock.NotifyAll();
+    return NS_OK;
+  }
+  void Wait()
+  {
+    MonitorAutoLock lock(*mUpdateMonitor);
+    while(!mUpdated) {
+      lock.Wait();
+    }
+  }
+
+  static bool Dispatch(const Type aType, void* aContext)
+  {
+    if (!CompositorThreadHolder::IsInCompositorThread()) {
+      RefPtr<SynchronousRunnable> runnable = new SynchronousRunnable(aType, aContext);
+      CompositorThreadHolder::Loop()->PostTask(do_AddRef(runnable));
+      runnable->Wait();
+      return true;
+    }
+
+    return false;
+  }
+
+protected:
+  virtual ~SynchronousRunnable()
+  {
+    delete mUpdateMonitor;
+  }
+
+  Type mType;
+  void* mContext;
+  Monitor* mUpdateMonitor;
+  bool mUpdated;
+};
+
+}
+
+NS_IMPL_ISUPPORTS(SynchronousRunnable, nsIRunnable)
+
+void
+mozilla::gfx::SetGVRPresentingContext(void* aGVRPresentingContext)
+{
+  if (SynchronousRunnable::Dispatch(SynchronousRunnable::Type::PresentingContext, aGVRPresentingContext)) {
+    GVR_LOG("Done waiting for compositor thread to set presenting context.");
+    return;
+  }
+
+  MOZ_ASSERT(sContextObserver);
+  if (!sGLContextEGL && aGVRPresentingContext) {
+    CreateContextFlags flags = CreateContextFlags::NONE;
+    SurfaceCaps caps = SurfaceCaps::ForRGBA();
+    nsCString str;
+    sGLContextEGL = GLContextEGL::CreateEGLPBufferOffscreenContext(flags, IntSize(4, 4), caps, &str);
+    if (!sGLContextEGL->MakeCurrent()) {
+      GVR_LOG("Failed to make GL context current");
+    }
+  }
+  sContextObserver->SetPresentingContext(aGVRPresentingContext);
+}
+
+void
+mozilla::gfx::CleanupGVRNonPresentingContext()
+{
+  if (SynchronousRunnable::Dispatch(SynchronousRunnable::Type::NonPresentingContext, nullptr)) {
+    GVR_LOG("Done waiting for compositor thread to set non presenting context.");
+    return;
+  }
+
+  if (sNonPresentingContext) {
+    sNonPresentingContext = nullptr;
+    GeckoVRManager::DestroyGVRNonPresentingContext();
+  }
+}
+
+void
+mozilla::gfx::SetGVRPaused(const bool aPaused)
+{
+  if (SynchronousRunnable::Dispatch((aPaused ? SynchronousRunnable::Type::Pause : SynchronousRunnable::Type::Resume), nullptr)) {
+    GVR_LOG("Done waiting for GVR in compositor to: %s",(aPaused ? "Pause" : "Resume"));
+    return;
+  }
+  MOZ_ASSERT(sContextObserver);
+  sContextObserver->SetPaused(aPaused);
+}
+
+VRDisplayGVR::VRDisplayGVR()
+  : VRDisplayHost(VRDeviceType::GVR)
+  , mIsPresenting(false)
+  , mControllerAdded(false)
+  , mPresentingContext(nullptr)
+  , mControllerContext(nullptr)
+  , mControllerState(nullptr)
+  , mViewportList(nullptr)
+  , mLeftViewport(nullptr)
+  , mRightViewport(nullptr)
+  , mSwapChain(nullptr)
+  , mFrameBufferSize{0, 0}
+{
+  MOZ_COUNT_CTOR_INHERITED(VRDisplayGVR, VRDisplayHost);
+  MOZ_ASSERT(GetNonPresentingContext());
+  MOZ_ASSERT(!sContextObserver); // There can be only one GVR display at a time.
+  sContextObserver = this;
+
+  mDisplayInfo.mDisplayName.AssignLiteral("GVR HMD");
+  mDisplayInfo.mIsConnected = true;
+  mDisplayInfo.mIsMounted = true;
+  mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
+                                  VRDisplayCapabilityFlags::Cap_Orientation |
+                                  VRDisplayCapabilityFlags::Cap_Position | // Not yet...
+                                  VRDisplayCapabilityFlags::Cap_Present;
+
+  GVR_CHECK(gvr_refresh_viewer_profile(GetNonPresentingContext()));
+  mViewportList = GVR_CHECK(gvr_buffer_viewport_list_create(GetNonPresentingContext()));
+  mLeftViewport = GVR_CHECK(gvr_buffer_viewport_create(GetNonPresentingContext()));
+  mRightViewport = GVR_CHECK(gvr_buffer_viewport_create(GetNonPresentingContext()));
+  UpdateViewport();
+
+  dom::GamepadHand hand = dom::GamepadHand::Right;
+  const gvr_user_prefs* prefs = GVR_CHECK(gvr_get_user_prefs(GetNonPresentingContext()));
+  if (prefs) {
+    hand = ((gvr_user_prefs_get_controller_handedness(prefs) == GVR_CONTROLLER_RIGHT_HANDED) ?
+             dom::GamepadHand::Right : dom::GamepadHand::Left);
+  }
+  mController = new VRControllerGVR(hand, mDisplayInfo.mDisplayID);
+}
+
+VRDisplayGVR::~VRDisplayGVR()
+{
+  MOZ_COUNT_DTOR_INHERITED(VRDisplayGVR, VRDisplayHost);
+}
+
+void
+VRDisplayGVR::ZeroSensor()
+{
+}
+
+void
+VRDisplayGVR::StartPresentation()
+{
+  if (mIsPresenting) {
+    return;
+  }
+
+  mIsPresenting = true;
+  GeckoVRManager::EnableVRMode();
+}
+
+void
+VRDisplayGVR::StopPresentation()
+{
+  if (!mIsPresenting) {
+    return;
+  }
+
+  mIsPresenting = false;
+  GeckoVRManager::DisableVRMode();
+}
+
+bool
+VRDisplayGVR::SubmitFrame(const mozilla::layers::EGLImageDescriptor* aDescriptor,
+                          const gfx::Rect& aLeftEyeRect,
+                          const gfx::Rect& aRightEyeRect)
+{
+  if (!mPresentingContext) {
+    GVR_LOG("Unable to submit frame. No presenting context")
+    return false;
+  }
+  if (!sGLContextEGL) {
+    GVR_LOG("Unable to submit frame. No GL Context");
+    return false;
+  }
+
+  if (!sGLContextEGL->MakeCurrent()) {
+    GVR_LOG("Failed to make GL context current");
+    return false;
+  }
+
+  EGLImage image = (EGLImage)aDescriptor->image();
+  EGLSync sync = (EGLSync)aDescriptor->fence();
+  gfx::IntSize size = aDescriptor->size();
+  MOZ_ASSERT(mSwapChain);
+  GVR_CHECK(gvr_get_recommended_buffer_viewports(mPresentingContext, mViewportList));
+  if ((size.width != mFrameBufferSize.width) || (size.height != mFrameBufferSize.height)) {
+    mFrameBufferSize.width = size.width;
+    mFrameBufferSize.height = size.height;
+    GVR_CHECK(gvr_swap_chain_resize_buffer(mSwapChain, 0, mFrameBufferSize));
+    GVR_LOG("Resize Swap Chain %d,%d", mFrameBufferSize.width, mFrameBufferSize.height);
+  }
+  gvr_frame* frame = GVR_CHECK(gvr_swap_chain_acquire_frame(mSwapChain));
+  if (!frame) {
+    // Sometimes the swap chain seems to not initialized correctly so that
+    // frames can not be acquired. Recreating the swap chain seems to fix the
+    // issue.
+    GVR_LOG("Unable to acquire GVR frame. Recreating swap chain.");
+    RecreateSwapChain();
+    return false;
+  }
+  GVR_CHECK(gvr_frame_bind_buffer(frame, 0));
+
+  EGLint status = LOCAL_EGL_CONDITION_SATISFIED;
+
+  if (sync) {
+    MOZ_ASSERT(sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync));
+    status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), sync, 0, LOCAL_EGL_FOREVER);
+  }
+
+  if (status != LOCAL_EGL_CONDITION_SATISFIED) {
+    MOZ_ASSERT(status != 0,
+               "ClientWaitSync generated an error. Has sync already been destroyed?");
+    return false;
+  }
+
+  if (image) {
+    GLuint tex = 0;
+    sGLContextEGL->fGenTextures(1, &tex);
+
+    const ScopedSaveMultiTex saveTex(sGLContextEGL, 1, LOCAL_GL_TEXTURE_2D);
+    sGLContextEGL->fBindTexture(LOCAL_GL_TEXTURE_2D, tex);
+    sGLContextEGL->TexParams_SetClampNoMips();
+    sGLContextEGL->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, image);
+    sGLContextEGL->BlitHelper()->DrawBlitTextureToFramebuffer(tex, gfx::IntSize(size.width, size.height), gfx::IntSize(mFrameBufferSize.width,  mFrameBufferSize.height));
+    sGLContextEGL->fDeleteTextures(1, &tex);
+  } else {
+    GVR_LOG("Unable to submit frame. Unable to extract EGLImage");
+    return false;
+  }
+  GVR_CHECK(gvr_frame_unbind(frame));
+  GVR_CHECK(gvr_frame_submit(&frame, mViewportList, mHeadMatrix));
+  return true;
+}
+
+void
+VRDisplayGVR::NotifyVSync()
+{
+  VRDisplayHost::NotifyVSync();
+}
+
+static void
+FillMatrix(gfx::Matrix4x4 &target, const gvr_mat4f& source)
+{
+  target._11 = source.m[0][0];
+  target._12 = source.m[0][1];
+  target._13 = source.m[0][2];
+  target._14 = source.m[0][3];
+  target._21 = source.m[1][0];
+  target._22 = source.m[1][1];
+  target._23 = source.m[1][2];
+  target._24 = source.m[1][3];
+  target._31 = source.m[2][0];
+  target._32 = source.m[2][1];
+  target._33 = source.m[2][2];
+  target._34 = source.m[2][3];
+  target._41 = source.m[3][0];
+  target._42 = source.m[3][1];
+  target._43 = source.m[3][2];
+  target._44 = source.m[3][3];
+}
+
+VRHMDSensorState
+VRDisplayGVR::GetSensorState()
+{
+  VRHMDSensorState result{};
+
+  gvr_context* context = (mPresentingContext ? mPresentingContext : GetNonPresentingContext());
+
+  if (!context) {
+    GVR_LOG("Unable to get sensor state. Context is null");
+    return result;
+  }
+
+  gvr_clock_time_point when = GVR_CHECK(gvr_get_time_point_now());
+  if (mIsPresenting) {
+    // 50ms into the future is what GVR docs recommends using for head rotation
+    // prediction.
+    when.monotonic_system_time_nanos += 50000000;
+  }
+  mHeadMatrix = GVR_CHECK(gvr_get_head_space_from_start_space_rotation(context, when));
+  gvr_mat4f neck = GVR_CHECK(gvr_apply_neck_model(context, mHeadMatrix, 1.0));;
+
+  gfx::Matrix4x4 m;
+
+  FillMatrix(m, neck);
+  m.Invert();
+  gfx::Quaternion rot;
+  rot.SetFromRotationMatrix(m);
+
+  result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
+  result.orientation[0] = rot.x;
+  result.orientation[1] = rot.y;
+  result.orientation[2] = rot.z;
+  result.orientation[3] = rot.w;
+  result.angularVelocity[0] = 0.0f;
+  result.angularVelocity[1] = 0.0f;
+  result.angularVelocity[2] = 0.0f;
+
+  result.flags |= VRDisplayCapabilityFlags::Cap_Position;
+  result.position[0] = m._14;
+  result.position[1] = m._24;
+  result.position[2] = m._34;
+  result.linearVelocity[0] = 0.0f;
+  result.linearVelocity[1] = 0.0f;
+  result.linearVelocity[2] = 0.0f;
+
+  UpdateHeadToEye(context, &rot);
+  result.CalcViewMatrices(mHeadToEyes);
+
+  return result;
+}
+
+void
+VRDisplayGVR::SetPaused(const bool aPaused)
+{
+  if (aPaused) {
+    if (mPresentingContext) {
+      GVR_CHECK(gvr_pause_tracking(mPresentingContext));
+    } else if (sNonPresentingContext) {
+      GVR_CHECK(gvr_pause_tracking(sNonPresentingContext));
+    }
+
+    if (mControllerContext) {
+      GVR_CHECK(gvr_controller_pause(mControllerContext));
+    }
+  } else {
+    if (mPresentingContext) {
+      GVR_CHECK(gvr_refresh_viewer_profile(mPresentingContext));
+      GVR_CHECK(gvr_resume_tracking(mPresentingContext));
+    } else if (sNonPresentingContext) {
+      GVR_CHECK(gvr_resume_tracking(sNonPresentingContext));
+    }
+
+    if (mControllerContext) {
+      GVR_CHECK(gvr_controller_resume(mControllerContext));
+    }
+  }
+}
+
+void
+VRDisplayGVR::SetPresentingContext(void* aGVRPresentingContext)
+{
+  MOZ_ASSERT(sGLContextEGL);
+  sGLContextEGL->MakeCurrent();
+  mPresentingContext = (gvr_context*)aGVRPresentingContext;
+  if (mPresentingContext) {
+    GVR_CHECK(gvr_initialize_gl(mPresentingContext));
+    RecreateSwapChain();
+  } else {
+
+    if (mSwapChain) {
+      // gvr_swap_chain_destroy will set the pointer to null
+      GVR_CHECK(gvr_swap_chain_destroy(&mSwapChain));
+      MOZ_ASSERT(!mSwapChain);
+    }
+
+    // The presentation context has been destroy, probably by the user so increment the presenting
+    // generation if we are presenting so that the DOM knows to end the current presentation.
+    if (mIsPresenting) {
+      mDisplayInfo.mPresentingGeneration++;
+    }
+  }
+}
+
+void
+VRDisplayGVR::UpdateHeadToEye(gvr_context* aContext, gfx::Quaternion* aRot)
+{
+  if (!aContext) {
+    return;
+  }
+
+  for (uint32_t eyeIndex = 0; eyeIndex < 2; eyeIndex++) {
+    gvr_mat4f eye = GVR_CHECK(gvr_get_eye_from_head_matrix(aContext, eyeIndex));
+    mDisplayInfo.mEyeTranslation[eyeIndex].x = -eye.m[0][3];
+    mDisplayInfo.mEyeTranslation[eyeIndex].y = -eye.m[1][3];
+    mDisplayInfo.mEyeTranslation[eyeIndex].z = -eye.m[2][3];
+    if (aRot) {
+      mHeadToEyes[eyeIndex].SetRotationFromQuaternion(*aRot);
+    } else {
+      mHeadToEyes[eyeIndex] = gfx::Matrix4x4();
+    }
+    mHeadToEyes[eyeIndex].PreTranslate(eye.m[0][3], eye.m[1][3], eye.m[2][3]);
+  }
+}
+
+void
+VRDisplayGVR::UpdateViewport()
+{
+  gvr_context* context = (mPresentingContext ? mPresentingContext : GetNonPresentingContext());
+
+  if (!context) {
+    return;
+  }
+
+  GVR_CHECK(gvr_get_recommended_buffer_viewports(context, mViewportList));
+  GVR_CHECK(gvr_buffer_viewport_list_get_item(mViewportList, 0, mLeftViewport));
+  GVR_CHECK(gvr_buffer_viewport_list_get_item(mViewportList, 1, mRightViewport));
+
+  gvr_rectf fov = GVR_CHECK(gvr_buffer_viewport_get_source_fov(mLeftViewport));
+  mDisplayInfo.mEyeFOV[VRDisplayInfo::Eye_Left] = VRFieldOfView(fov.top, fov.right, fov.bottom, fov.left);
+  GVR_LOG("FOV:L top:%f right:%f bottom:%f left:%f",(float)fov.top, (float)fov.left, (float)fov.bottom, (float)fov.right);
+
+  fov = GVR_CHECK(gvr_buffer_viewport_get_source_fov(mRightViewport));
+  mDisplayInfo.mEyeFOV[VRDisplayInfo::Eye_Right] = VRFieldOfView(fov.top, fov.right, fov.bottom, fov.left);
+  GVR_LOG("FOV:R top:%f right:%f bottom:%f left:%f",(float)fov.top, (float)fov.left, (float)fov.bottom, (float)fov.right);
+
+  gvr_sizei size = GVR_CHECK(gvr_get_maximum_effective_render_target_size(context));
+  mDisplayInfo.mEyeResolution = IntSize(size.width / 2, size.height);
+  GVR_LOG("Eye Resolution: %dx%d",mDisplayInfo.mEyeResolution.width,mDisplayInfo.mEyeResolution.height);
+
+  UpdateHeadToEye(context);
+}
+
+void
+VRDisplayGVR::RecreateSwapChain()
+{
+  MOZ_ASSERT(sGLContextEGL);
+  sGLContextEGL->MakeCurrent();
+  if (mSwapChain) {
+    // gvr_swap_chain_destroy will set the pointer to null
+    GVR_CHECK(gvr_swap_chain_destroy(&mSwapChain));
+    MOZ_ASSERT(!mSwapChain);
+  }
+  gvr_buffer_spec* spec = GVR_CHECK(gvr_buffer_spec_create(mPresentingContext));
+  mFrameBufferSize = GVR_CHECK(gvr_get_maximum_effective_render_target_size(mPresentingContext));
+  GVR_CHECK(gvr_buffer_spec_set_size(spec, mFrameBufferSize));
+  GVR_CHECK(gvr_buffer_spec_set_samples(spec, 0));
+  GVR_CHECK(gvr_buffer_spec_set_color_format(spec, GVR_COLOR_FORMAT_RGBA_8888));
+  GVR_CHECK(gvr_buffer_spec_set_depth_stencil_format(spec, GVR_DEPTH_STENCIL_FORMAT_NONE));
+  mSwapChain = GVR_CHECK(gvr_swap_chain_create(mPresentingContext, (const gvr_buffer_spec**)&spec, 1));
+  GVR_CHECK(gvr_buffer_spec_destroy(&spec));
+}
+
+void
+VRDisplayGVR::EnableControllers(const bool aEnable, VRSystemManager* aManager)
+{
+  if (aEnable && !mControllerAdded) {
+    // Sometimes the gamepad doesn't get removed cleanly so just try to remove it before adding it.
+    aManager->RemoveGamepad(mController->GetControllerInfo().mControllerID);
+    aManager->AddGamepad(mController->GetControllerInfo());
+    mControllerAdded = true;
+  } else if (!aEnable && mControllerAdded) {
+    mControllerAdded = false;
+    aManager->RemoveGamepad(mController->GetControllerInfo().mControllerID);
+  }
+
+  gvr_context* context = mPresentingContext;
+
+  if (!context) {
+    if (mControllerContext) {
+      GVR_CHECK(gvr_controller_destroy(&mControllerContext));
+    }
+    return;
+  }
+
+  if ((aEnable && mControllerContext) || (!aEnable && !mControllerContext)) {
+    return;
+  }
+
+  if (aEnable) {
+    if (!mControllerContext) {
+      int32_t options = GVR_CHECK(gvr_controller_get_default_options());
+      options |= GVR_CONTROLLER_ENABLE_GYRO | GVR_CONTROLLER_ENABLE_ACCEL | GVR_CONTROLLER_ENABLE_ARM_MODEL;
+      mControllerContext = GVR_CHECK(gvr_controller_create_and_init(options, context));
+      GVR_CHECK(gvr_controller_resume(mControllerContext));
+    }
+    if (!mControllerState) {
+      mControllerState = GVR_CHECK(gvr_controller_state_create());
+    }
+  } else {
+    GVR_CHECK(gvr_controller_pause(mControllerContext));
+    GVR_CHECK(gvr_controller_destroy(&mControllerContext));
+  }
+}
+
+void
+VRDisplayGVR::UpdateControllers(VRSystemManager* aManager)
+{
+  if (!mControllerContext) {
+    return;
+  }
+
+  GVR_CHECK(gvr_controller_apply_arm_model(mControllerContext, GVR_CONTROLLER_RIGHT_HANDED,GVR_ARM_MODEL_FOLLOW_GAZE, mHeadMatrix));
+  GVR_CHECK(gvr_controller_state_update(mControllerContext, 0, mControllerState));
+  mController->Update(mControllerState, aManager);
+}
+
+
+void
+VRDisplayGVR::GetControllers(nsTArray<RefPtr<VRControllerHost> >& aControllerResult)
+{
+  aControllerResult.AppendElement(mController.get());
+}
+
+VRControllerGVR::VRControllerGVR(dom::GamepadHand aHand, uint32_t aDisplayID)
+  : VRControllerHost(VRDeviceType::GVR, aHand, aDisplayID)
+{
+  MOZ_COUNT_CTOR_INHERITED(VRControllerGVR, VRControllerHost);
+  mControllerInfo.mControllerName.AssignLiteral("Daydream Controller");
+  // The gvr_controller_button enum starts with GVR_CONTROLLER_BUTTON_NONE at index zero
+  // so the GVR controller has one less button than GVR_CONTROLLER_BUTTON_COUNT specifies.
+  mControllerInfo.mNumButtons = GVR_CONTROLLER_BUTTON_COUNT - 1; // Skip dummy none button
+  mControllerInfo.mNumAxes = 2;
+  mControllerInfo.mNumHaptics = 0;
+}
+
+VRControllerGVR::~VRControllerGVR()
+{
+  MOZ_COUNT_DTOR_INHERITED(VRControllerGVR, VRControllerHost);
+}
+
+void
+VRControllerGVR::Update(gvr_controller_state* aState, VRSystemManager* aManager)
+{
+  mPose.Clear();
+
+  if (gvr_controller_state_get_connection_state(aState) != GVR_CONTROLLER_CONNECTED) {
+    return;
+  }
+  const uint64_t previousPressMask = GetButtonPressed();
+  const uint64_t previousTouchMask = GetButtonTouched();
+  uint64_t currentPressMask = 0;
+  uint64_t currentTouchMask = 0;
+  // Index 0 is the dummy button so skip it.
+  for (int ix = 1; ix < GVR_CONTROLLER_BUTTON_COUNT; ix++) {
+    const uint64_t buttonMask = 0x01 << (ix - 1);
+    bool pressed = gvr_controller_state_get_button_state(aState, ix);
+    bool touched = pressed;
+    if (ix == GVR_CONTROLLER_BUTTON_CLICK) {
+       touched = gvr_controller_state_is_touching(aState);
+       double xAxis = 0.0;
+       double yAxis = 0.0;
+       if (touched) {
+         gvr_vec2f axes = gvr_controller_state_get_touch_pos(aState);
+         xAxis = (axes.x * 2.0) - 1.0;
+         yAxis = (axes.y * 2.0) - 1.0;
+       }
+       aManager->NewAxisMove(0, 0, xAxis);
+       aManager->NewAxisMove(0, 1, yAxis);
+    }
+    if (pressed) {
+      currentPressMask |= buttonMask;
+    }
+    if (touched) {
+      currentTouchMask |= buttonMask;
+    }
+    if (((currentPressMask & buttonMask) ^ (previousPressMask & buttonMask)) ||
+        ((currentTouchMask & buttonMask) ^ (previousTouchMask & buttonMask))) {
+      aManager->NewButtonEvent(0, ix - 1, pressed, touched, pressed ? 1.0 : 0.0);
+    }
+  }
+  SetButtonPressed(currentPressMask);
+  SetButtonTouched(currentTouchMask);
+
+  mPose.flags = dom::GamepadCapabilityFlags::Cap_Orientation | dom::GamepadCapabilityFlags::Cap_Position | dom::GamepadCapabilityFlags::Cap_LinearAcceleration;
+
+  gvr_quatf ori = gvr_controller_state_get_orientation(aState);
+  mPose.orientation[0] = ori.qx;
+  mPose.orientation[1] = ori.qy;
+  mPose.orientation[2] = ori.qz;
+  mPose.orientation[3] = ori.qw;
+  mPose.isOrientationValid = true;
+
+  gvr_vec3f acc = gvr_controller_state_get_accel(aState);
+  mPose.linearAcceleration[0] = acc.x;
+  mPose.linearAcceleration[1] = acc.y;
+  mPose.linearAcceleration[2] = acc.z;
+
+  gvr_vec3f vel = gvr_controller_state_get_gyro(aState);
+  mPose.angularVelocity[0] = vel.x;
+  mPose.angularVelocity[1] = vel.y;
+  mPose.angularVelocity[2] = vel.z;
+
+  gvr_vec3f pos =  gvr_controller_state_get_position(aState);
+  mPose.position[0] = pos.x;
+  mPose.position[1] = pos.y;
+  mPose.position[2] = pos.z;
+
+  aManager->NewPoseState(0, mPose);
+}
+
+/*static*/ already_AddRefed<VRSystemManagerGVR>
+VRSystemManagerGVR::Create()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!gfxPrefs::VREnabled()) {
+    return nullptr;
+  }
+
+  RefPtr<VRSystemManagerGVR> manager = new VRSystemManagerGVR();
+  return manager.forget();
+}
+
+void
+VRSystemManagerGVR::Destroy()
+{
+
+}
+
+void
+VRSystemManagerGVR::Shutdown()
+{
+
+}
+
+bool
+VRSystemManagerGVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost> >& aHMDResult)
+{
+  if (!GeckoVRManager::IsGVRPresent()) {
+    return false;
+  }
+
+  if (!mGVRHMD) {
+    mGVRHMD = new VRDisplayGVR();
+  }
+
+  aHMDResult.AppendElement(mGVRHMD);
+  return true;
+}
+
+bool
+VRSystemManagerGVR::GetIsPresenting()
+{
+  if (!mGVRHMD) {
+    return false;
+  }
+
+  VRDisplayInfo displayInfo(mGVRHMD->GetDisplayInfo());
+  return displayInfo.GetPresentingGroups() != kVRGroupNone;
+}
+
+void
+VRSystemManagerGVR::HandleInput()
+{
+  if (mGVRHMD) {
+    mGVRHMD->UpdateControllers(this);
+  }
+}
+
+void
+VRSystemManagerGVR::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
+{
+  if (mGVRHMD) {
+    mGVRHMD->GetControllers(aControllerResult);
+  }
+}
+
+void
+VRSystemManagerGVR::ScanForControllers()
+{
+  if (mGVRHMD) {
+    mGVRHMD->EnableControllers(true, this);
+  }
+}
+
+void
+VRSystemManagerGVR::RemoveControllers()
+{
+  if (mGVRHMD) {
+    mGVRHMD->EnableControllers(false, this);
+  }
+}
+
+void
+VRSystemManagerGVR::VibrateHaptic(uint32_t aControllerIdx,
+                                  uint32_t aHapticIndex,
+                                  double aIntensity,
+                                  double aDuration,
+                                  uint32_t aPromiseID)
+{
+
+}
+
+void
+VRSystemManagerGVR::StopVibrateHaptic(uint32_t aControllerIdx)
+{
+
+}
+
+VRSystemManagerGVR::VRSystemManagerGVR()
+  : mGVRHMD(nullptr)
+{
+
+}
+
+VRSystemManagerGVR::~VRSystemManagerGVR()
+{
+
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/vr/gfxVRGVR.h
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_VR_GVR_H
+#define GFX_VR_GVR_H
+
+#include "gfxVR.h"
+
+#include <memory>
+
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+
+#include "nsTArray.h"
+#include "nsIRunnable.h"
+#include "nsIScreen.h"
+#include "nsCOMPtr.h"
+
+#include "VRDisplayHost.h"
+
+#pragma GCC system_header
+#pragma GCC visibility push(default)
+#include "vr/gvr/capi/include/gvr.h"
+#include "vr/gvr/capi/include/gvr_controller.h"
+#pragma GCC visibility pop
+
+
+namespace mozilla {
+namespace gl {
+class GLContextEGL;
+} // namespace gl
+namespace layers {
+class EGLImageDescriptor;
+} // namespace layers
+namespace gfx {
+namespace impl {
+
+class VRControllerGVR : public VRControllerHost
+{
+public:
+  explicit VRControllerGVR(dom::GamepadHand aHand, uint32_t aDisplayID);
+  virtual ~VRControllerGVR();
+  void Update(gvr_controller_state* aState, VRSystemManager* aManager);
+};
+
+class VRDisplayGVR : public VRDisplayHost
+{
+public:
+  VRDisplayGVR();
+
+  // BEGIN VRDisplayHost interface
+  void ZeroSensor() override;
+  void StartPresentation() override;
+  void StopPresentation() override;
+  bool SubmitFrame(const mozilla::layers::EGLImageDescriptor* aDescriptor,
+                   const gfx::Rect& aLeftEyeRect,
+                   const gfx::Rect& aRightEyeRect) override;
+  void NotifyVSync() override;
+protected:
+  virtual VRHMDSensorState GetSensorState() override;
+  // END VRDisplayHost interface
+
+public:
+  void SetPaused(const bool aPaused);
+  void SetPresentingContext(void* aGVRPresentingContext);
+  void EnableControllers(const bool aEnable, VRSystemManager* aManager);
+  void UpdateControllers(VRSystemManager* aManager);
+  void GetControllers(nsTArray<RefPtr<VRControllerHost> >& aControllerResult);
+
+protected:
+  virtual ~VRDisplayGVR();
+  void UpdateHeadToEye(gvr_context* aContext, gfx::Quaternion* aRot = nullptr);
+  void UpdateViewport();
+  void RecreateSwapChain();
+
+  bool mIsPresenting;
+  bool mControllerAdded;
+
+  gfx::Matrix4x4 mHeadToEyes[2];
+  gvr_context* mPresentingContext;
+  gvr_controller_context* mControllerContext;
+  gvr_controller_state* mControllerState;
+  gvr_buffer_viewport_list* mViewportList;
+  gvr_buffer_viewport* mLeftViewport;
+  gvr_buffer_viewport* mRightViewport;
+  gvr_mat4f mHeadMatrix;
+  gvr_swap_chain* mSwapChain;
+  gvr_sizei mFrameBufferSize;
+
+  RefPtr<VRControllerGVR> mController;
+};
+
+
+} // namespace impl
+
+class VRSystemManagerGVR : public VRSystemManager
+{
+public:
+  static already_AddRefed<VRSystemManagerGVR> Create();
+
+  void Destroy() override;
+  void Shutdown() override;
+  bool GetHMDs(nsTArray<RefPtr<VRDisplayHost> >& aHMDResult) override;
+  bool GetIsPresenting() override;
+  void HandleInput() override;
+  void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
+                      aControllerResult) override;
+  void ScanForControllers() override;
+  void RemoveControllers() override;
+  void VibrateHaptic(uint32_t aControllerIdx,
+                     uint32_t aHapticIndex,
+                     double aIntensity,
+                     double aDuration,
+                     uint32_t aPromiseID) override;
+  void StopVibrateHaptic(uint32_t aControllerIdx) override;
+
+protected:
+  VRSystemManagerGVR();
+  virtual ~VRSystemManagerGVR();
+
+private:
+  RefPtr<impl::VRDisplayGVR> mGVRHMD;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+
+#endif /* GFX_VR_GVR_H */
new file mode 100644
--- /dev/null
+++ b/gfx/vr/gfxVRGVRAPI.h
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_VR_GVR_API_H
+#define GFX_VR_GVR_API_H
+namespace mozilla {
+namespace gfx {
+
+void SetGVRPresentingContext(void* aGVRPresentingContext);
+void CleanupGVRNonPresentingContext();
+void SetGVRPaused(const bool aPaused);
+
+} // namespace gfx
+} // namespace mozilla
+#endif // GFX_VR_GVR_API_H
--- a/gfx/vr/gfxVROSVR.cpp
+++ b/gfx/vr/gfxVROSVR.cpp
@@ -352,16 +352,27 @@ VRDisplayOSVR::SubmitFrame(MacIOSurface*
                            const IntSize& aSize,
                            const gfx::Rect& aLeftEyeRect,
                            const gfx::Rect& aRightEyeRect)
 {
   // XXX Add code to submit frame
   return false;
 }
 
+#elif defined(MOZ_ANDROID_GOOGLE_VR)
+
+bool
+VRDisplayOSVR::SubmitFrame(const mozilla::layers::EGLImageDescriptor*,
+                           const gfx::Rect& aLeftEyeRect,
+                           const gfx::Rect& aRightEyeRect)
+{
+  // XXX Add code to submit frame
+  return false;
+}
+
 #endif
 
 void
 VRDisplayOSVR::StartPresentation()
 {
   // XXX Add code to start VR Presentation
 }
 
--- a/gfx/vr/gfxVROSVR.h
+++ b/gfx/vr/gfxVROSVR.h
@@ -41,16 +41,20 @@ protected:
                            const IntSize& aSize,
                            const gfx::Rect& aLeftEyeRect,
                            const gfx::Rect& aRightEyeRect) override;
 #elif defined(XP_MACOSX)
   virtual bool SubmitFrame(MacIOSurface* aMacIOSurface,
                            const IntSize& aSize,
                            const gfx::Rect& aLeftEyeRect,
                            const gfx::Rect& aRightEyeRect) override;
+#elif defined(MOZ_ANDROID_GOOGLE_VR)
+  virtual bool SubmitFrame(const mozilla::layers::EGLImageDescriptor*,
+                           const gfx::Rect& aLeftEyeRect,
+                           const gfx::Rect& aRightEyeRect) override;
 #endif
 
 public:
   explicit VRDisplayOSVR(OSVR_ClientContext* context,
                          OSVR_ClientInterface* iface,
                          OSVR_DisplayConfig* display);
 
 protected:
@@ -119,9 +123,9 @@ private:
   void InitializeClientContext();
   void InitializeDisplay();
   void InitializeInterface();
 };
 
 } // namespace gfx
 } // namespace mozilla
 
-#endif /* GFX_VR_OSVR_H */
\ No newline at end of file
+#endif /* GFX_VR_OSVR_H */
--- a/gfx/vr/gfxVRPuppet.cpp
+++ b/gfx/vr/gfxVRPuppet.cpp
@@ -545,16 +545,26 @@ VRDisplayPuppet::SubmitFrame(MacIOSurfac
       MOZ_ASSERT(false, "No support for showing VR frames on MacOSX yet.");
       break;
     }
   }
 
   return false;
 }
 
+#elif defined(MOZ_ANDROID_GOOGLE_VR)
+
+bool
+VRDisplayPuppet::SubmitFrame(const mozilla::layers::EGLImageDescriptor* aDescriptor,
+                           const gfx::Rect& aLeftEyeRect,
+                           const gfx::Rect& aRightEyeRect) {
+
+  return false;
+}
+
 #endif
 
 void
 VRDisplayPuppet::NotifyVSync()
 {
   // We update mIsConneced once per frame.
   mDisplayInfo.mIsConnected = true;
 
--- a/gfx/vr/gfxVRPuppet.h
+++ b/gfx/vr/gfxVRPuppet.h
@@ -37,16 +37,20 @@ protected:
                            const IntSize& aSize,
                            const gfx::Rect& aLeftEyeRect,
                            const gfx::Rect& aRightEyeRect) override;
 #elif defined(XP_MACOSX)
   virtual bool SubmitFrame(MacIOSurface* aMacIOSurface,
                            const IntSize& aSize,
                            const gfx::Rect& aLeftEyeRect,
                            const gfx::Rect& aRightEyeRect) override;
+#elif defined(MOZ_ANDROID_GOOGLE_VR)
+  virtual bool SubmitFrame(const mozilla::layers::EGLImageDescriptor* aDescriptor,
+                           const gfx::Rect& aLeftEyeRect,
+                           const gfx::Rect& aRightEyeRect) override;
 #endif
 
 public:
   explicit VRDisplayPuppet();
 
 protected:
   virtual ~VRDisplayPuppet();
   void Destroy();
@@ -138,9 +142,9 @@ private:
   // there can only be one
   RefPtr<impl::VRDisplayPuppet> mPuppetHMD;
   nsTArray<RefPtr<impl::VRControllerPuppet>> mPuppetController;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
-#endif  /* GFX_VR_PUPPET_H*/
\ No newline at end of file
+#endif  /* GFX_VR_PUPPET_H*/
--- a/gfx/vr/ipc/VRMessageUtils.h
+++ b/gfx/vr/ipc/VRMessageUtils.h
@@ -40,16 +40,17 @@ struct ParamTraits<mozilla::gfx::VRDispl
     WriteParam(aMsg, aParam.mEyeResolution);
     WriteParam(aMsg, aParam.mIsConnected);
     WriteParam(aMsg, aParam.mIsMounted);
     WriteParam(aMsg, aParam.mPresentingGroups);
     WriteParam(aMsg, aParam.mGroupMask);
     WriteParam(aMsg, aParam.mStageSize);
     WriteParam(aMsg, aParam.mSittingToStandingTransform);
     WriteParam(aMsg, aParam.mFrameId);
+    WriteParam(aMsg, aParam.mPresentingGeneration);
     for (int i = 0; i < mozilla::gfx::VRDisplayInfo::NumEyes; i++) {
       WriteParam(aMsg, aParam.mEyeFOV[i]);
       WriteParam(aMsg, aParam.mEyeTranslation[i]);
     }
     for (int i = 0; i < mozilla::gfx::kVRMaxLatencyFrames; i++) {
       WriteParam(aMsg, aParam.mLastSensorState[i]);
     }
   }
@@ -62,17 +63,18 @@ struct ParamTraits<mozilla::gfx::VRDispl
         !ReadParam(aMsg, aIter, &(aResult->mCapabilityFlags)) ||
         !ReadParam(aMsg, aIter, &(aResult->mEyeResolution)) ||
         !ReadParam(aMsg, aIter, &(aResult->mIsConnected)) ||
         !ReadParam(aMsg, aIter, &(aResult->mIsMounted)) ||
         !ReadParam(aMsg, aIter, &(aResult->mPresentingGroups)) ||
         !ReadParam(aMsg, aIter, &(aResult->mGroupMask)) ||
         !ReadParam(aMsg, aIter, &(aResult->mStageSize)) ||
         !ReadParam(aMsg, aIter, &(aResult->mSittingToStandingTransform)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mFrameId))) {
+        !ReadParam(aMsg, aIter, &(aResult->mFrameId)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mPresentingGeneration))) {
       return false;
     }
     for (int i = 0; i < mozilla::gfx::VRDisplayInfo::NumEyes; i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->mEyeFOV[i])) ||
           !ReadParam(aMsg, aIter, &(aResult->mEyeTranslation[i]))) {
         return false;
       }
     }
new file mode 100644
--- /dev/null
+++ b/gfx/vr/jni/gfxGVRJNI.cpp
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <jni.h>
+
+extern "C" __attribute__((visibility("default"))) jlong
+Java_com_google_vr_cardboard_DisplaySynchronizer_nativeCreate(
+    JNIEnv* env,
+    jobject jcaller,
+    jclass classLoader,
+    jobject appContext);
+
+// Step 2: method stubs.
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_cardboard_DisplaySynchronizer_nativeDestroy(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeDisplaySynchronizer);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_cardboard_DisplaySynchronizer_nativeReset(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeDisplaySynchronizer,
+    jlong expectedInterval,
+    jlong vsyncOffset);
+
+extern "C" __attribute__((visibility("default"))) void
+Java_com_google_vr_cardboard_DisplaySynchronizer_nativeUpdate(
+    JNIEnv* env,
+    jobject jcaller,
+    jlong nativeDisplaySynchronizer,
+    jlong syncTime,
+    jint currentRotation);
+
+namespace {
+
+bool
+check(JNIEnv* env) {
+  if (env->ExceptionCheck()) {
+    env->ExceptionDescribe();
+    env->ExceptionClear();
+    return false;
+  }
+  return true;
+}
+
+const char kDisplaySynchronizerClassPath[] = "com/google/vr/cardboard/DisplaySynchronizer";
+
+static const JNINativeMethod kMethodsDisplaySynchronizer[] = {
+    {"nativeCreate",
+     "("
+     "Ljava/lang/ClassLoader;"
+     "Landroid/content/Context;"
+     ")"
+     "J",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_cardboard_DisplaySynchronizer_nativeCreate)},
+    {"nativeDestroy",
+     "("
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_cardboard_DisplaySynchronizer_nativeDestroy)},
+    {"nativeReset",
+     "("
+     "J"
+     "J"
+     "J"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_cardboard_DisplaySynchronizer_nativeReset)},
+    {"nativeUpdate",
+     "("
+     "J"
+     "J"
+     "I"
+     ")"
+     "V",
+     reinterpret_cast<void*>(
+         Java_com_google_vr_cardboard_DisplaySynchronizer_nativeUpdate)},
+};
+}
+
+bool
+SetupGVRJNI(JNIEnv* env)
+{
+  jclass displaySynchronizerClazz = env->FindClass(kDisplaySynchronizerClassPath);
+  if (!check(env)) { return false; }
+  if (displaySynchronizerClazz == nullptr) {
+    return false;
+  }
+  env->RegisterNatives(displaySynchronizerClazz, kMethodsDisplaySynchronizer, sizeof(kMethodsDisplaySynchronizer) / sizeof(kMethodsDisplaySynchronizer[0]));
+  if (!check(env)) { return false; }
+  env->DeleteLocalRef(displaySynchronizerClazz);
+  if (!check(env)) { return false; }
+
+  return true;
+}
+
--- a/gfx/vr/moz.build
+++ b/gfx/vr/moz.build
@@ -52,16 +52,24 @@ if CONFIG['OS_TARGET'] in ('WINNT', 'Lin
         'gfxVROpenVR.cpp',
     ]
 
 if CONFIG['OS_TARGET'] == 'WINNT':
     SOURCES += [
         'gfxVROculus.cpp',
     ]
 
+if CONFIG['MOZ_ANDROID_GOOGLE_VR']:
+    SOURCES += [
+        'gfxVRGVR.cpp',
+        'jni/gfxGVRJNI.cpp',
+    ]
+    CXXFLAGS += ['-I%s' % CONFIG['MOZ_ANDROID_GOOGLE_VR_INCLUDE']]
+    LOCAL_INCLUDES += ['/widget/android']
+
 IPDL_SOURCES = [
     'ipc/PVRLayer.ipdl',
     'ipc/PVRManager.ipdl',
 ]
 
 # For building with the real SDK instead of our local hack
 #SOURCES += [
 #    'OVR_CAPI_Util.cpp',
--- a/gfx/webrender_bindings/Moz2DImageRenderer.cpp
+++ b/gfx/webrender_bindings/Moz2DImageRenderer.cpp
@@ -14,18 +14,17 @@
 
 #include <iostream>
 #include <unordered_map>
 
 #ifdef XP_MACOSX
 #include "mozilla/gfx/UnscaledFontMac.h"
 #elif defined(XP_WIN)
 #include "mozilla/gfx/UnscaledFontDWrite.h"
-#elif defined(MOZ_ENABLE_FREETYPE)
-#include "mozilla/ThreadLocal.h"
+#else
 #include "mozilla/gfx/UnscaledFontFreeType.h"
 #endif
 
 namespace std {
   template <>
     struct hash<mozilla::wr::FontKey>{
       public :
         size_t operator()(const mozilla::wr::FontKey &key ) const
@@ -38,29 +37,24 @@ namespace std {
 
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace wr {
 
-#ifdef MOZ_ENABLE_FREETYPE
-static MOZ_THREAD_LOCAL(FT_Library) sFTLibrary;
-#endif
-
 struct FontTemplate {
   const uint8_t *mData;
   size_t mSize;
   uint32_t mIndex;
   const VecU8 *mVec;
   RefPtr<UnscaledFont> mUnscaledFont;
 };
 
-// we need to do special things for linux so that we have fonts per backend
 std::unordered_map<FontKey, FontTemplate> sFontDataTable;
 
 extern "C" {
 void
 AddFontData(WrFontKey aKey, const uint8_t *aData, size_t aSize, uint32_t aIndex, const ArcVecU8 *aVec) {
   auto i = sFontDataTable.find(aKey);
   if (i == sFontDataTable.end()) {
     FontTemplate font;
@@ -152,31 +146,16 @@ static bool Moz2DRenderCallback(const Ra
   }
 
   auto stride = aSize.width * gfx::BytesPerPixel(aFormat);
 
   if (aOutput.length() < static_cast<size_t>(aSize.height * stride)) {
     return false;
   }
 
-  void* fontContext = nullptr;
-#ifdef MOZ_ENABLE_FREETYPE
-  if (!sFTLibrary.init()) {
-    return false;
-  }
-  if (!sFTLibrary.get()) {
-    FT_Library library = gfx::Factory::NewFTLibrary();
-    if (!library) {
-      return false;
-    }
-    sFTLibrary.set(library);
-  }
-  fontContext = sFTLibrary.get();
-#endif
-
   // In bindings.rs we allocate a buffer filled with opaque white.
   bool uninitialized = false;
 
   RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(
     gfx::BackendType::SKIA,
     aOutput.begin().get(),
     aSize,
     stride,
@@ -220,17 +199,17 @@ static bool Moz2DRenderCallback(const Ra
   Reader reader(aBlob.begin().get()+indexOffset, aBlob.length()-sizeof(size_t)-indexOffset);
 
   bool ret;
   size_t offset = 0;
   while (reader.pos < reader.len) {
     size_t end = reader.ReadSize();
     size_t extra_end = reader.ReadSize();
 
-    gfx::InlineTranslator translator(dt, fontContext);
+    gfx::InlineTranslator translator(dt);
 
     size_t count = *(size_t*)(aBlob.begin().get() + end);
     for (size_t i = 0; i < count; i++) {
       wr::FontKey key = *(wr::FontKey*)(aBlob.begin() + end + sizeof(count) + sizeof(wr::FontKey)*i).get();
       RefPtr<UnscaledFont> font = GetUnscaledFont(&translator, key);
       translator.AddUnscaledFont(0, font);
     }
     Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);
--- a/gfx/webrender_bindings/moz.build
+++ b/gfx/webrender_bindings/moz.build
@@ -44,15 +44,14 @@ if CONFIG['MOZ_ENABLE_D3D10_LAYER']:
     EXPORTS.mozilla.webrender += [
         'RenderD3D11TextureHostOGL.h',
     ]
     UNIFIED_SOURCES += [
         'RenderD3D11TextureHostOGL.cpp',
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3'):
-    DEFINES['MOZ_ENABLE_FREETYPE'] = True
     CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
     CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -145,17 +145,17 @@ static const size_t gMaxStackSize = 2 * 
 #else
 static const size_t gMaxStackSize = 128 * sizeof(size_t) * 1024;
 #endif
 
 /*
  * Limit the timeout to 30 minutes to prevent an overflow on platfoms
  * that represent the time internally in microseconds using 32-bit int.
  */
-static const TimeDuration MAX_TIMEOUT_INTERVAL = TimeDuration::FromSeconds(1800.0);
+static const double MAX_TIMEOUT_SECONDS = 1800.0;
 
 // SharedArrayBuffer and Atomics are enabled by default (tracking Firefox).
 #define SHARED_MEMORY_DEFAULT 1
 
 // Code to support GCOV code coverage measurements on standalone shell
 #ifdef MOZ_CODE_COVERAGE
 #if defined(__GNUC__) && !defined(__clang__)
 extern "C" void __gcov_dump();
@@ -3847,16 +3847,17 @@ Sleep_fn(JSContext* cx, unsigned argc, V
         if (!ToNumber(cx, args[0], &t_secs))
             return false;
         if (mozilla::IsNaN(t_secs)) {
             JS_ReportErrorASCII(cx, "sleep interval is not a number");
             return false;
         }
 
         duration = TimeDuration::FromSeconds(Max(0.0, t_secs));
+        const TimeDuration MAX_TIMEOUT_INTERVAL = TimeDuration::FromSeconds(MAX_TIMEOUT_SECONDS);
         if (duration > MAX_TIMEOUT_INTERVAL) {
             JS_ReportErrorASCII(cx, "Excessive sleep interval");
             return false;
         }
     }
     {
         LockGuard<Mutex> guard(sc->watchdogLock);
         TimeStamp toWakeup = TimeStamp::Now() + duration;
@@ -4014,16 +4015,17 @@ CancelExecution(JSContext* cx)
 
 static bool
 SetTimeoutValue(JSContext* cx, double t)
 {
     if (mozilla::IsNaN(t)) {
         JS_ReportErrorASCII(cx, "timeout is not a number");
         return false;
     }
+    const TimeDuration MAX_TIMEOUT_INTERVAL = TimeDuration::FromSeconds(MAX_TIMEOUT_SECONDS);
     if (TimeDuration::FromSeconds(t) > MAX_TIMEOUT_INTERVAL) {
         JS_ReportErrorASCII(cx, "Excessive timeout value");
         return false;
     }
     GetShellContext(cx)->timeoutInterval = t;
     if (!ScheduleWatchdog(cx, t)) {
         JS_ReportErrorASCII(cx, "Failed to create the watchdog");
         return false;
--- a/js/xpconnect/tests/chrome/test_getweakmapkeys.xul
+++ b/js/xpconnect/tests/chrome/test_getweakmapkeys.xul
@@ -17,26 +17,26 @@ https://bugzilla.mozilla.org/show_bug.cg
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
   /** Test for Bug 688277 **/
 
   let Cu = Components.utils;
 
   /* Fail gracefully if junk is passed in. */
-  is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(11), undefined,
+  is(ChromeUtils.nondeterministicGetWeakMapKeys(11), undefined,
     "nondeterministicGetWeakMapKeys should return undefined for non-objects");
-  is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys({}), undefined,
+  is(ChromeUtils.nondeterministicGetWeakMapKeys({}), undefined,
     "nondeterministicGetWeakMapKeys should return undefined for non-weakmap objects");
-  is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(null), undefined,
+  is(ChromeUtils.nondeterministicGetWeakMapKeys(null), undefined,
     "nondeterministicGetWeakMapKeys should return undefined for null");
 
   /* return an empty array for an empty WeakMap */
   let mempty = new WeakMap();
-  is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(mempty).length, 0,
+  is(ChromeUtils.nondeterministicGetWeakMapKeys(mempty).length, 0,
     "nondeterministicGetWeakMapKeys should return empty array for empty weakmap");
 
   /* Test freeing/nonfreeing. */
   let m = new WeakMap();
   let liveKeys = new Array();
 
   let add_elements = function () {
     let k1 = {};
@@ -48,17 +48,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     let k = {};
     m.set(k, k); /* simple cycle */
   };
 
   add_elements();
 
   Cu.schedulePreciseGC(function () {
-    let keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(m);
+    let keys = ChromeUtils.nondeterministicGetWeakMapKeys(m);
     is(liveKeys.length, 1, "Wrong number of live keys.");
     is(keys.length, 1, "Should have one weak map key.");
     is(m.get(keys[0]), "live1", "live1 should be live");
     SimpleTest.finish();
   });
 
   SimpleTest.waitForExplicitFinish();
 
--- a/js/xpconnect/tests/chrome/test_paris_weakmap_keys.xul
+++ b/js/xpconnect/tests/chrome/test_paris_weakmap_keys.xul
@@ -62,17 +62,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   SimpleTest.waitForExplicitFinish();
 
   Cu.schedulePreciseGC(function () {
     SpecialPowers.DOMWindowUtils.cycleCollect();
     SpecialPowers.DOMWindowUtils.garbageCollect();
     SpecialPowers.DOMWindowUtils.garbageCollect();
 
-    is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(live_map).length, 1,
+    is(ChromeUtils.nondeterministicGetWeakMapKeys(live_map).length, 1,
        "Live nsISupports new DOM bindings wrappercached native weak map key should not be removed.");
 
     is(live_map.get(get_div_style()), 12345, "Live map should have live style with right value after GC.");
 
     SimpleTest.finish();
   });
 
   ]]>
--- a/js/xpconnect/tests/chrome/test_weakmap_keys_preserved2.xul
+++ b/js/xpconnect/tests/chrome/test_weakmap_keys_preserved2.xul
@@ -49,30 +49,30 @@ https://bugzilla.mozilla.org/show_bug.cg
     sandbox.wrapper = wrappee;
     sandbox.value = dead_dom;
     let map = Cu.evalInSandbox("wm = new WeakMap(); wm.set(wrapper, value); wm", sandbox);
     sandbox.wrapper = null;
     sandbox.value = null;
 
     live_dom.xyzabc = {wrappee: wrappee, m: map, sb: sandbox};
 
-    let key = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(map)[0];
+    let key = ChromeUtils.nondeterministicGetWeakMapKeys(map)[0];
     let value = map.get(key);
     is(value.children.length, 1, "children have wrong length");
   }
 
   wrappers_as_keys_test();
 
   let check_wrappers_as_keys = function () {
     let live_dom = window.eeeevent.data.dummy;
     let live_map = live_dom.xyzabc.m;
     let sandbox = live_dom.xyzabc.sb;
-    is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(live_map).length, 1,
+    is(ChromeUtils.nondeterministicGetWeakMapKeys(live_map).length, 1,
       "Map should not be empty.");
-    let key = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(live_map)[0];
+    let key = ChromeUtils.nondeterministicGetWeakMapKeys(live_map)[0];
     let value = live_map.get(key);
     is(value.children.length, 1, "children have wrong length");
   }
 
   Cu.schedulePreciseGC(function () {
     SpecialPowers.DOMWindowUtils.cycleCollect();
     SpecialPowers.DOMWindowUtils.garbageCollect();
 
--- a/js/xpconnect/tests/chrome/test_weakmaps.xul
+++ b/js/xpconnect/tests/chrome/test_weakmaps.xul
@@ -26,17 +26,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   let make_weak_ref = function (obj) {
     let m = new WeakMap;
     m.set(obj, {});
     return m;
   };
 
   /* Check to see if a weak reference is dead. */
   let weak_ref_dead = function (r) {
-    return ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(r).length == 0;
+    return ChromeUtils.nondeterministicGetWeakMapKeys(r).length == 0;
   }
 
   /* Deterministically grab an arbitrary DOM element. */
   let get_live_dom = function () {
     let elems = document.getElementsByTagName("a");
     return elems[0];
   };
 
@@ -113,25 +113,25 @@ https://bugzilla.mozilla.org/show_bug.cg
     ok(weak_ref_dead(basic_weak_ref), "Dead value was kept alive.");
     ok(weak_ref_dead(basic_map_weak_ref), "Dead map was kept alive.");
 
     // check the live gray map
     is(live_map.get(live_key).my_key, 'live_live',
       "Live key should have the same value in live map.");
     is(live_map.get(black_key).my_key, 'live_black',
       "Black key should have the same value in live map.");
-    is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(live_map).length, 2,
+    is(ChromeUtils.nondeterministicGetWeakMapKeys(live_map).length, 2,
       "Live map should have two entries.");
 
     // check the live black map
     is(black_map.get(live_key).my_key, 'black_live',
       "Live key should have the same value in black map.");
     is(black_map.get(black_key).my_key, 'black_black',
       "Black key should have the same value in black map.");
-    is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(black_map).length, 2,
+    is(ChromeUtils.nondeterministicGetWeakMapKeys(black_map).length, 2,
       "Black map should have two entries.");    
 
   };
 
 
   /* live gray chained weak map entries, involving the cycle collector. */
   let chainm = new WeakMap;
   let num_chains = 5;
@@ -225,26 +225,26 @@ https://bugzilla.mozilla.org/show_bug.cg
     SpecialPowers.DOMWindowUtils.cycleCollect();
     SpecialPowers.DOMWindowUtils.garbageCollect();
     SpecialPowers.DOMWindowUtils.garbageCollect();
 
     ok(weak_ref_dead(weakref), "Garbage gray cycle should be collected.");
 
     check_nested_cc_maps();
 
-    is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(garbage_map).length, 0, "Chained garbage weak map entries should not leak.");
+    is(ChromeUtils.nondeterministicGetWeakMapKeys(garbage_map).length, 0, "Chained garbage weak map entries should not leak.");
 
     check_basic_unit();
 
     // fixed by Bug 680937
-    is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(wn_garbage_map).length, 0,
+    is(ChromeUtils.nondeterministicGetWeakMapKeys(wn_garbage_map).length, 0,
        "Chained garbage WN weak map entries should not leak.");
 
     // fixed by Bug 680937
-    is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(wn_live_map).length, 1,
+    is(ChromeUtils.nondeterministicGetWeakMapKeys(wn_live_map).length, 1,
        "Live weak map wrapped native key should not be removed.");
 
     ok(wn_live_map.has(get_live_dom()), "Live map should have live dom.");
 
     SimpleTest.finish();
   });
 
   ]]>
--- a/js/xpconnect/tests/mochitest/test_bug862380.html
+++ b/js/xpconnect/tests/mochitest/test_bug862380.html
@@ -8,26 +8,37 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 862380</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 862380 **/
   SimpleTest.waitForExplicitFinish();
   function go() {
-    checkNotEnumerable($('ifr').contentWindow);
-    checkNotEnumerable($('ifr').contentWindow.location);
+    checkNotEnumerable($('ifr').contentWindow, true);
+    checkNotEnumerable($('ifr').contentWindow.location, false);
     SimpleTest.finish();
   }
 
-  function checkNotEnumerable(obj) {
+function checkNotEnumerable(obj, isWindow) {
     try {
-      is(Object.keys(obj).length, 0, "Object.keys gives empty array");
+      const expectedWindow = ["0"];
+      const expectedLocation = [];
+      const expected = isWindow ? expectedWindow : expectedLocation;
+      is(Object.keys(obj).length, expected.length,
+         "Object.keys gives right array length");
+      var actual = [];
       for (var i in obj)
-        ok(false, "Enumerated something: " + i);
+        actual.push(i);
+      is(actual.length, expected.length,
+         "Enumeration sees the right number of props");
+      actual.sort();
+      expected.sort();
+      for (var i = 0; i < actual.length; ++i)
+        is(actual[i], expected[i], "Arrays should be the same " + i);
     } catch (e) {
       ok(false, "threw: " + e);
     }
   }
 
   </script>
 </head>
 <body>
--- a/js/xpconnect/tests/unit/test_weak_keys.js
+++ b/js/xpconnect/tests/unit/test_weak_keys.js
@@ -9,37 +9,37 @@ function run_test()
   var bunnies = new String("bunnies");
   var lizards = new String("lizards");
 
   var weakset = new WeakSet([bunnies, lizards]);
   var weakmap = new WeakMap();
   weakmap.set(bunnies, 23);
   weakmap.set(lizards, "oh no");
 
-  var keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(bunnies);
+  var keys = ChromeUtils.nondeterministicGetWeakMapKeys(bunnies);
   equal(keys, undefined, "test nondeterministicGetWeakMapKeys on non-WeakMap");
 
-  keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(weakmap);
+  keys = ChromeUtils.nondeterministicGetWeakMapKeys(weakmap);
   equal(keys.length, 2, "length of nondeterministicGetWeakMapKeys");
   equal(weakmap.get(bunnies), 23, "check bunnies in weakmap");
   equal(weakmap.get(lizards), "oh no", "check lizards in weakmap");
 
-  keys = ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(bunnies);
+  keys = ChromeUtils.nondeterministicGetWeakSetKeys(bunnies);
   equal(keys, undefined, "test nondeterministicGetWeakSetKeys on non-WeakMap");
 
-  keys = ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(weakset);
+  keys = ChromeUtils.nondeterministicGetWeakSetKeys(weakset);
   equal(keys.length, 2, "length of nondeterministicGetWeakSetKeys");
   ok(weakset.has(bunnies), "check bunnies in weakset");
   ok(weakset.has(lizards), "check lizards in weakset");
 
   bunnies = null;
   keys = null;
 
   Components.utils.forceGC();
 
-  keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(weakmap);
+  keys = ChromeUtils.nondeterministicGetWeakMapKeys(weakmap);
   equal(keys.length, 1, "length of nondeterministicGetWeakMapKeys after GC");
   equal(weakmap.get(lizards), "oh no", "check lizards still in weakmap");
 
-  keys = ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(weakset);
+  keys = ChromeUtils.nondeterministicGetWeakSetKeys(weakset);
   equal(keys.length, 1, "length of nondeterministicGetWeakSetKeys after GC");
   ok(weakset.has(lizards), "check lizards still in weakset");
 }
--- a/js/xpconnect/wrappers/FilteringWrapper.cpp
+++ b/js/xpconnect/wrappers/FilteringWrapper.cpp
@@ -218,19 +218,22 @@ CrossOriginXrayWrapper::getPropertyDescr
         // from the ones we add ourselves here.
         MOZ_ASSERT(!JSID_IS_SYMBOL(id),
                    "What's this symbol-named property that appeared on a "
                    "Window or Location instance?");
 
         // All properties on cross-origin DOM objects are |own|.
         desc.object().set(wrapper);
 
-        // All properties on cross-origin DOM objects are non-enumerable and
-        // "configurable". Any value attributes are read-only.
-        desc.attributesRef() &= ~JSPROP_ENUMERATE;
+        // All properties on cross-origin DOM objects are "configurable". Any
+        // value attributes are read-only.  Indexed properties are enumerable,
+        // but nothing else is.
+        if (!JSID_IS_INT(id)) {
+            desc.attributesRef() &= ~JSPROP_ENUMERATE;
+        }
         desc.attributesRef() &= ~JSPROP_PERMANENT;
         if (!desc.getter() && !desc.setter())
             desc.attributesRef() |= JSPROP_READONLY;
     } else if (IsCrossOriginWhitelistedSymbol(cx, id)) {
         // Spec says to return PropertyDescriptor {
         //   [[Value]]: undefined, [[Writable]]: false, [[Enumerable]]: false,
         //   [[Configurable]]: true
         // }.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -7320,36 +7320,16 @@ nsLayoutUtils::GetDisplayRootFrame(nsIFr
     nsIFrame* parent = GetCrossDocParentFrame(f);
     if (!parent)
       return f;
     f = parent;
   }
 }
 
 /* static */ nsIFrame*
-nsLayoutUtils::GetViewportFrame(nsIFrame* aFrame)
-{
-  nsIFrame* f = aFrame;
-
-  for (;;) {
-    MOZ_ASSERT(f);
-    if (f->Type() == LayoutFrameType::Viewport) {
-      return f;
-    }
-
-    nsIFrame* parent = GetCrossDocParentFrame(f);
-    if (!parent) {
-      return f;
-    }
-
-    f = parent;
-  }
-}
-
-/* static */ nsIFrame*
 nsLayoutUtils::GetReferenceFrame(nsIFrame* aFrame)
 {
   nsIFrame *f = aFrame;
   for (;;) {
     const nsStyleDisplay* disp = f->StyleDisplay();
     if (f->IsTransformed(disp) || f->IsPreserve3DLeaf(disp) || IsPopup(f)) {
       return f;
     }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2014,21 +2014,16 @@ public:
 
   /**
    * Find the nearest "display root". This is the nearest enclosing
    * popup frame or the root prescontext's root frame.
    */
   static nsIFrame* GetDisplayRootFrame(nsIFrame* aFrame);
 
   /**
-   * Find the nearest viewport frame that is an ancestor of the given frame.
-   */
-  static nsIFrame* GetViewportFrame(nsIFrame* aFrame);
-
-  /**
    * Get the reference frame that would be used when constructing a
    * display item for this frame.  Rather than using their own frame
    * as a reference frame.)
    *
    * This duplicates some of the logic of GetDisplayRootFrame above and
    * of nsDisplayListBuilder::FindReferenceFrameFor.
    *
    * If you have an nsDisplayListBuilder, you should get the reference
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -964,16 +964,32 @@ nsIFrame::RemoveDisplayItemDataForDeleti
       if (i->GetDependentFrame() == this &&
           !i->HasDeletedFrame()) {
         i->Frame()->MarkNeedsDisplayItemRebuild();
       }
       i->RemoveFrame(this);
     }
     delete items;
   }
+
+  if (IsFrameModified()) {
+    nsIFrame* rootFrame = PresContext()->PresShell()->GetRootFrame();
+    MOZ_ASSERT(rootFrame);
+
+    nsTArray<nsIFrame*>* modifiedFrames =
+      rootFrame->GetProperty(nsIFrame::ModifiedFrameList());
+    MOZ_ASSERT(modifiedFrames);
+
+    for (auto& frame : *modifiedFrames) {
+      if (frame == this) {
+        frame = nullptr;
+        break;
+      }
+    }
+  }
 }
 
 void
 nsIFrame::MarkNeedsDisplayItemRebuild()
 {
   if (!gfxPrefs::LayoutRetainDisplayList() || !XRE_IsContentProcess() ||
       IsFrameModified()) {
     // Skip non-content processes and frames that are already marked modified.
@@ -985,35 +1001,56 @@ nsIFrame::MarkNeedsDisplayItemRebuild()
 
   RetainedDisplayListBuilder* retainedBuilder =
     displayRoot->GetProperty(RetainedDisplayListBuilder::Cached());
 
   if (!retainedBuilder) {
     return;
   }
 
-  nsIFrame* viewport = nsLayoutUtils::GetViewportFrame(this);
-  MOZ_ASSERT(viewport);
-
-  std::vector<WeakFrame>* modifiedFrames =
-    viewport->GetProperty(nsIFrame::ModifiedFrameList());
+  nsIFrame* rootFrame = PresContext()->PresShell()->GetRootFrame();
+  MOZ_ASSERT(rootFrame);
+
+  if (rootFrame->IsFrameModified()) {
+    return;
+  }
+
+  nsTArray<nsIFrame*>* modifiedFrames =
+    rootFrame->GetProperty(nsIFrame::ModifiedFrameList());
 
   if (!modifiedFrames) {
-    modifiedFrames = new std::vector<WeakFrame>();
-    viewport->SetProperty(nsIFrame::ModifiedFrameList(), modifiedFrames);
-  }
-
-  modifiedFrames->emplace_back(this);
+    modifiedFrames = new nsTArray<nsIFrame*>();
+    rootFrame->SetProperty(nsIFrame::ModifiedFrameList(), modifiedFrames);
+  }
+
+  if (this == rootFrame) {
+    // If this is the root frame, then marking us as needing a display
+    // item rebuild implies the same for all our descendents. Clear them
+    // all out to reduce the number of modified frames we keep around.
+    for (nsIFrame* f : *modifiedFrames) {
+      if (f) {
+        f->SetFrameIsModified(false);
+      }
+    }
+    modifiedFrames->Clear();
+  } else if (modifiedFrames->Length() > gfxPrefs::LayoutRebuildFrameLimit()) {
+    // If the list starts getting too big, then just mark the root frame
+    // as needing a rebuild.
+    rootFrame->MarkNeedsDisplayItemRebuild();
+    return;
+  }
+
+  modifiedFrames->AppendElement(this);
 
   // TODO: this is a bit of a hack. We are using ModifiedFrameList property to
   // decide whether we are trying to reuse the display list.
-  if (displayRoot != viewport &&
+  if (displayRoot != rootFrame &&
       !displayRoot->HasProperty(nsIFrame::ModifiedFrameList())) {
     displayRoot->SetProperty(nsIFrame::ModifiedFrameList(),
-                             new std::vector<WeakFrame>());
+                             new nsTArray<nsIFrame*>());
   }
 
   MOZ_ASSERT(PresContext()->LayoutPhaseCount(eLayoutPhase_DisplayListBuilding) == 0);
   SetFrameIsModified(true);
 
   // Hopefully this is cheap, but we could use a frame state bit to note
   // the presence of dependencies to speed it up.
   DisplayItemArray* items = GetProperty(DisplayItems());
@@ -2768,17 +2805,17 @@ nsIFrame::BuildDisplayListForStackingCon
       hasOverrideDirtyRect = true;
     }
   }
   // Always build the entire display list if we previously had a blend
   // container since a partial build might make us think we no longer
   // need the container even though the merged result will.
   if (aBuilder->IsRetainingDisplayList() && BuiltBlendContainer()) {
     dirtyRect = visibleRect;
-    aBuilder->MarkFrameModifiedDuringBuilding(this);
+    aBuilder->MarkCurrentFrameModifiedDuringBuilding();
   }
 
   bool usingFilter = StyleEffects()->HasFilters();
   bool usingMask = nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this);
   bool usingSVGEffects = usingFilter || usingMask;
 
   nsRect visibleRectOutsideSVGEffects = visibleRect;
   nsDisplayList hoistedScrollInfoItemsStorage(aBuilder);
@@ -2931,17 +2968,17 @@ 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->MarkFrameModifiedDuringBuilding(this);
+      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());
         set.DeleteAll(aBuilder);
 
         if (eventRegions) {
@@ -3602,17 +3639,19 @@ nsIFrame::BuildDisplayListForChild(nsDis
       aBuilder->SetContainsBlendMode(true);
     }
     // True stacking context.
     // For stacking contexts, BuildDisplayListForStackingContext handles
     // clipping and MarkAbsoluteFramesForDisplayList.
     nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
     child->BuildDisplayListForStackingContext(aBuilder, &list, &canSkipWrapList);
     wrapListASR = contASRTracker.GetContainerASR();
-    aBuilder->DisplayCaret(child, &list);
+    if (aBuilder->DisplayCaret(child, &list)) {
+      canSkipWrapList = false;
+    }
   } else {
     Maybe<nsRect> clipPropClip =
       child->GetClipPropClipRect(disp, effects, child->GetSize());
     if (clipPropClip) {
       aBuilder->IntersectVisibleRect(*clipPropClip);
       aBuilder->IntersectDirtyRect(*clipPropClip);
       clipState.ClipContentDescendants(
         *clipPropClip + aBuilder->ToReferenceFrame(child));
@@ -3668,17 +3707,19 @@ nsIFrame::BuildDisplayListForChild(nsDis
     // A pseudo-stacking context (e.g., a positioned element with z-index auto).
     // We allow positioned descendants of the child to escape to our parent
     // stacking context's positioned descendant list, because they might be
     // z-index:non-auto
     nsDisplayListCollection pseudoStack(aBuilder);
     aBuilder->AdjustWindowDraggingRegion(child);
     nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
     child->BuildDisplayList(aBuilder, pseudoStack);
-    aBuilder->DisplayCaret(child, pseudoStack.Content());
+    if (aBuilder->DisplayCaret(child, pseudoStack.Content())) {
+      canSkipWrapList = false;
+    }
     wrapListASR = contASRTracker.GetContainerASR();
 
     list.AppendToTop(pseudoStack.BorderBackground());
     list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
     list.AppendToTop(pseudoStack.Floats());
     list.AppendToTop(pseudoStack.Content());
     list.AppendToTop(pseudoStack.Outlines());
     extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1252,17 +1252,17 @@ public:
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FragStretchBSizeProperty, nscoord)
 
   // The block-axis margin-box size associated with eBClampMarginBoxMinSize.
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BClampMarginBoxMinSizeProperty, nscoord)
 
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(IBaselinePadProperty, nscoord)
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BBaselinePadProperty, nscoord)
 
-  NS_DECLARE_FRAME_PROPERTY_DELETABLE(ModifiedFrameList, std::vector<WeakFrame>)
+  NS_DECLARE_FRAME_PROPERTY_DELETABLE(ModifiedFrameList, nsTArray<nsIFrame*>)
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(DisplayItems, DisplayItemArray)
 
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BidiDataProperty, mozilla::FrameBidiData)
 
   NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(PlaceholderFrameProperty, nsPlaceholderFrame)
   NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(WebRenderUserDataProperty, WebRenderUserDataTable, DestroyWebRenderUserDataTable)
 
   static void DestroyWebRenderUserDataTable(WebRenderUserDataTable* aTable);
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -457,28 +457,28 @@ nsSubDocumentFrame::BuildDisplayList(nsD
     // The value of needsOwnLayer can change between builds without
     // an invalidation recorded for this frame (like if the root
     // scrollframe becomes active). If this happens,
     // then we need to notify the builder so that merging can
     // happen correctly.
     if (!mPreviouslyNeededLayer ||
         mPreviouslyNeededLayer.value() != needsOwnLayer) {
       dirty = visible;
-      aBuilder->MarkFrameModifiedDuringBuilding(this);
+      aBuilder->MarkCurrentFrameModifiedDuringBuilding();
     }
     mPreviouslyNeededLayer = Some(needsOwnLayer);
 
     // Caret frame changed, rebuild the entire subdoc.
     // We could just invalidate the old and new frame
     // areas and save some work here. RetainedDisplayListBuilder
     // does this, so we could teach it to find and check all
     // subdocs in advance.
     if (mPreviousCaret != aBuilder->GetCaretFrame()) {
       dirty = visible;
-      aBuilder->MarkFrameModifiedDuringBuilding(this);
+      aBuilder->MarkCurrentFrameModifiedDuringBuilding();
     }
     mPreviousCaret = aBuilder->GetCaretFrame();
   }
 
   nsDisplayList childItems(aBuilder);
 
   {
     DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -467,32 +467,30 @@ RetainedDisplayListBuilder::MergeDisplay
 }
 
 static void
 TakeAndAddModifiedFramesFromRootFrame(nsTArray<nsIFrame*>& aFrames,
                                       nsIFrame* aRootFrame)
 {
   MOZ_ASSERT(aRootFrame);
 
-  std::vector<WeakFrame>* frames =
+  nsTArray<nsIFrame*>* frames =
     aRootFrame->GetProperty(nsIFrame::ModifiedFrameList());
 
   if (!frames) {
     return;
   }
 
-  for (WeakFrame& frame : *frames) {
-    nsIFrame* f = frame.GetFrame();
-
+  for (nsIFrame* f : *frames) {
     if (f) {
       aFrames.AppendElement(f);
     }
   }
 
-  frames->clear();
+  frames->Clear();
 }
 
 static bool
 SubDocEnumCb(nsIDocument* aDocument, void* aData)
 {
   MOZ_ASSERT(aDocument);
   MOZ_ASSERT(aData);
 
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -715,21 +715,24 @@ public:
     bool temp = mIsCompositingCheap;
     mIsCompositingCheap = aCompositingCheap;
     return temp;
   }
   bool IsCompositingCheap() const { return mIsCompositingCheap; }
   /**
    * Display the caret if needed.
    */
-  void DisplayCaret(nsIFrame* aFrame, nsDisplayList* aList) {
+  bool DisplayCaret(nsIFrame* aFrame, nsDisplayList* aList)
+  {
     nsIFrame* frame = GetCaretFrame();
     if (aFrame == frame) {
       frame->DisplayCaret(this, aList);
+      return true;
     }
+    return false;
   }
   /**
    * Get the frame that the caret is supposed to draw in.
    * If the caret is currently invisible, this will be null.
    */
   nsIFrame* GetCaretFrame() {
     return CurrentPresShellState()->mCaretFrame;
   }
@@ -1599,16 +1602,26 @@ public:
     mBuildingInvisibleItems = aBuildingInvisibleItems;
   }
 
   bool MarkFrameModifiedDuringBuilding(nsIFrame* aFrame)
   {
     if (!aFrame->IsFrameModified()) {
       mModifiedFramesDuringBuilding.AppendElement(aFrame);
       aFrame->SetFrameIsModified(true);
+      mInInvalidSubtree = true;
+      return true;
+    }
+    return false;
+  }
+
+  bool MarkCurrentFrameModifiedDuringBuilding()
+  {
+    if (MarkFrameModifiedDuringBuilding(const_cast<nsIFrame*>(mCurrentFrame))) {
+      mInInvalidSubtree = true;
       return true;
     }
     return false;
   }
 
   /**
    * This is a convenience function to ease the transition until AGRs and ASRs
    * are unified.
--- a/media/audioipc/server/src/lib.rs
+++ b/media/audioipc/server/src/lib.rs
@@ -611,17 +611,17 @@ impl Server {
         }
     }
 
     fn accept(&mut self, poll: &mut mio::Poll) -> Result<()> {
         debug!("Server accepting connection");
 
         let client_socket = match self.socket.accept() {
             Err(e) => {
-                error!("server accept error: {}", e);
+                warn!("server accept error: {}", e);
                 return Err(e.into());
             },
             Ok(None) => panic!("accept returned EAGAIN unexpectedly"),
             Ok(Some((socket, _))) => socket,
         };
 
         if !self.conns.has_available() {
             trace!(
@@ -668,24 +668,24 @@ impl Server {
         Ok(())
     }
 
     pub fn poll(&mut self, poll: &mut mio::Poll) -> Result<()> {
         let mut events = mio::Events::with_capacity(16);
 
         match poll.poll(&mut events, None) {
             Ok(_) => {},
-            Err(e) => error!("server poll error: {}", e),
+            Err(e) => warn!("server poll error: {}", e),
         }
 
         for event in events.iter() {
             match event.token() {
                 SERVER => {
                     if let Err(e) = self.accept(poll) {
-                        error!("server accept error: {}", e);
+                        warn!("server accept error: {}", e);
                     };
                 },
                 QUIT => {
                     info!("Quitting Audio Server loop");
                     bail!("quit");
                 },
                 token => {
                     trace!("token {:?} ready", token);
--- a/media/ffvpx/config_android32.h
+++ b/media/ffvpx/config_android32.h
@@ -554,17 +554,17 @@
 #define CONFIG_LSP 0
 #define CONFIG_LZO 0
 #define CONFIG_MDCT 0
 #define CONFIG_PIXELUTILS 0
 #define CONFIG_NETWORK 0
 #define CONFIG_RDFT 0
 #define CONFIG_AUTODETECT 0
 #define CONFIG_FONTCONFIG 0
-#define CONFIG_LINUX_PERF 1
+#define CONFIG_LINUX_PERF 0
 #define CONFIG_MEMORY_POISONING 0
 #define CONFIG_NEON_CLOBBER_TEST 0
 #define CONFIG_OSSFUZZ 0
 #define CONFIG_PIC 1
 #define CONFIG_THUMB 0
 #define CONFIG_VALGRIND_BACKTRACE 0
 #define CONFIG_XMM_CLOBBER_TEST 0
 #define CONFIG_BSFS 1
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -325,19 +325,16 @@ pref("gfx.displayport.strategy_vb.danger
 pref("gfx.displayport.strategy_vb.danger_y_incr", -1); // additional danger zone on y-axis when multiplied by viewport height and velocity
 
 // prediction bias strategy options
 pref("gfx.displayport.strategy_pb.threshold", -1); // velocity threshold in inches/frame
 
 // Allow 24-bit colour when the hardware supports it
 pref("gfx.android.rgb16.force", false);
 
-// Allow GLContexts to be attached/detached from SurfaceTextures
-pref("gfx.SurfaceTexture.detach.enabled", true);
-
 // Use SurfaceTextures as preferred backend for TextureClient/Host
 pref("gfx.use-surfacetexture-textures", false);
 
 // don't allow JS to move and resize existing windows
 pref("dom.disable_window_move_resize", true);
 
 // prevent click image resizing for nsImageDocument
 pref("browser.enable_click_image_resizing", false);
--- a/mobile/android/app/moz.build
+++ b/mobile/android/app/moz.build
@@ -50,8 +50,13 @@ if CONFIG['MOZ_PKG_SPECIAL']:
 
 JS_PREFERENCE_PP_FILES += [
      'mobile.js',
 ]
 
 FINAL_TARGET_PP_FILES += [
   'ua-update.json.in',
 ]
+
+if CONFIG['MOZ_ANDROID_GOOGLE_VR']:
+    FINAL_TARGET_FILES += [
+        '/' + CONFIG['MOZ_ANDROID_GOOGLE_VR_LIBS'] + 'libgvr.so',
+    ]
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -385,16 +385,17 @@ gvjar.sources += [geckoview_source_dir +
     'GeckoProfileDirectories.java',
     'GeckoScreenOrientation.java',
     'GeckoSession.java',
     'GeckoSessionHandler.java',
     'GeckoSessionSettings.java',
     'GeckoSharedPrefs.java',
     'GeckoThread.java',
     'GeckoView.java',
+    'GeckoVRManager.java',
     'gfx/BitmapUtils.java',
     'gfx/BufferedImage.java',
     'gfx/BufferedImageGLInfo.java',
     'gfx/DynamicToolbarAnimator.java',
     'gfx/FloatSize.java',
     'gfx/FullScreenState.java',
     'gfx/GeckoLayerClient.java',
     'gfx/GeckoSurface.java',
--- a/mobile/android/chrome/content/about.js
+++ b/mobile/android/chrome/content/about.js
@@ -25,17 +25,17 @@ function init() {
   try {
     let distroId = Services.prefs.getCharPref("distribution.id");
     if (distroId) {
       let distroVersion = Services.prefs.getCharPref("distribution.version");
       let distroIdField = document.getElementById("distributionID");
       distroIdField.textContent = distroId + " - " + distroVersion;
       distroIdField.hidden = false;
 
-      let distroAbout = Services.prefs.getComplexValue("distribution.about", Ci.nsISupportsString);
+      let distroAbout = Services.prefs.getStringPref("distribution.about");
       let distroField = document.getElementById("distributionAbout");
       distroField.textContent = distroAbout;
       distroField.hidden = false;
     }
   } catch (e) {
     // Pref is unset
   }
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoVRManager.java
@@ -0,0 +1,125 @@
+/* -*- 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.gecko;
+
+import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.util.ThreadUtils;
+
+public class GeckoVRManager {
+    /**
+     * GeckoView applications implement this interface to provide GVR support for WebVR.
+     */
+    public interface GVRDelegate {
+        /**
+         * Creates non-presenting context. Will be invoked in the compositor thread.
+         */
+        long createNonPresentingContext();
+        /**
+         * Destroys non-presenting context. Will be invoked in the compositor thread.
+         */
+        void destroyNonPresentingContext();
+        /**
+         * Called when WebVR needs a presenting context. Will be invoked in the UI thread.
+         */
+        boolean enableVRMode();
+        /**
+         * Called when WebVR has finished presenting. Will be invoked in the UI thread.
+         */
+        void disableVRMode();
+    }
+
+    private static GVRDelegate mGVRDelegate;
+
+    /**
+     * Set the GVR Delegate for GeckoView.
+     * @param delegate GVRDelegate instance or null to unset.
+     */
+    public static void setGVRDelegate(GVRDelegate delegate) {
+        mGVRDelegate = delegate;
+    }
+
+    /**
+     * Set the GVR paused state.
+     * @param aPaused True if the application is being paused, False if the
+     * application is resuming.
+     */
+    @WrapForJNI(calledFrom = "ui")
+    public static native void setGVRPaused(final boolean aPaused);
+
+    /**
+     * Set the GVR presenting context.
+     * @param aContext GVR context to use when WebVR starts to present. Pass in
+     * zero to stop presenting.
+     */
+    @WrapForJNI(calledFrom = "ui")
+    public static native void setGVRPresentingContext(final long aContext);
+
+    /**
+     * Inform WebVR that the non-presenting context needs to be destroyed.
+     */
+    @WrapForJNI(calledFrom = "ui")
+    public static native void cleanupGVRNonPresentingContext();
+
+    @WrapForJNI
+    /* package */ static boolean isGVRPresent() {
+        return mGVRDelegate != null;
+    }
+
+    @WrapForJNI
+    /* package */ static long createGVRNonPresentingContext() {
+        if (mGVRDelegate == null) {
+            return 0;
+        }
+        return mGVRDelegate.createNonPresentingContext();
+    }
+
+    @WrapForJNI
+    /* package */ static void destroyGVRNonPresentingContext() {
+        if (mGVRDelegate == null) {
+            return;
+        }
+        mGVRDelegate.destroyNonPresentingContext();
+    }
+
+    @WrapForJNI
+    /* package */ static void enableVRMode() {
+        if (!ThreadUtils.isOnUiThread()) {
+            ThreadUtils.postToUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    enableVRMode();
+                }
+            });
+            return;
+        }
+
+        if (mGVRDelegate == null) {
+            return;
+        }
+
+        mGVRDelegate.enableVRMode();
+    }
+
+    @WrapForJNI
+    /* package */ static void disableVRMode() {
+        if (!ThreadUtils.isOnUiThread()) {
+            ThreadUtils.postToUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    disableVRMode();
+                }
+            });
+            return;
+        }
+
+        if (mGVRDelegate == null) {
+            return;
+        }
+
+        mGVRDelegate.disableVRMode();
+    }
+}
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -71,16 +71,20 @@
 #endif
 
 [lib destdir="lib/@ANDROID_CPU_ARCH@"]
 @BINPATH@/@DLL_PREFIX@mozglue@DLL_SUFFIX@
 # This should be MOZ_CHILD_PROCESS_NAME, but that has a "lib/" prefix.
 @BINPATH@/@MOZ_CHILD_PROCESS_NAME@
 @BINPATH@/@MOZ_CHILD_PROCESS_NAME_PIE@
 
+#ifdef MOZ_ANDROID_GOOGLE_VR
+@BINPATH@/@DLL_PREFIX@gvr@DLL_SUFFIX@
+#endif
+
 [xpcom]
 @BINPATH@/package-name.txt
 @BINPATH@/classes.dex
 
 [browser]
 ; [Base Browser Files]
 @BINPATH@/application.ini
 @BINPATH@/platform.ini
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -293,16 +293,17 @@ struct CallbackNode
 {
   const char* mDomain;
 
   // If someone attempts to remove the node from the callback list while
   // pref_DoCallback is running, |func| is set to nullptr. Such nodes will
   // be removed at the end of pref_DoCallback.
   PrefChangedFunc mFunc;
   void* mData;
+  Preferences::MatchKind mMatchKind;
   CallbackNode* mNext;
 };
 
 static PLDHashTable* gHashTable;
 
 static ArenaAllocator<8192, 4> gPrefNameArena;
 
 // The callback list contains all the priority callbacks followed by the
@@ -1070,25 +1071,27 @@ PREF_PrefIsLocked(const char* aPrefName)
 }
 
 // Adds a node to the callback list; the position depends on aIsPriority. The
 // callback function will be called if anything below that node is modified.
 static void
 PREF_RegisterCallback(const char* aPrefNode,
                       PrefChangedFunc aCallback,
                       void* aData,
+                      Preferences::MatchKind aMatchKind,
                       bool aIsPriority)
 {
   NS_PRECONDITION(aPrefNode, "aPrefNode must not be nullptr");
   NS_PRECONDITION(aCallback, "aCallback must not be nullptr");
 
   auto node = (CallbackNode*)moz_xmalloc(sizeof(CallbackNode));
   node->mDomain = moz_xstrdup(aPrefNode);
   node->mFunc = aCallback;
   node->mData = aData;
+  node->mMatchKind = aMatchKind;
 
   if (aIsPriority) {
     // Add to the start of the list.
     node->mNext = gFirstCallback;
     gFirstCallback = node;
     if (!gLastPriorityNode) {
       gLastPriorityNode = node;
     }
@@ -1129,24 +1132,26 @@ pref_RemoveCallbackNode(CallbackNode* aN
   return next_node;
 }
 
 // Deletes a node from the callback list or marks it for deletion. Succeeds if
 // a callback was found that matched all the parameters.
 static nsresult
 PREF_UnregisterCallback(const char* aPrefNode,
                         PrefChangedFunc aCallback,
-                        void* aData)
+                        void* aData,
+                        Preferences::MatchKind aMatchKind)
 {
   nsresult rv = NS_ERROR_FAILURE;
   CallbackNode* node = gFirstCallback;
   CallbackNode* prev_node = nullptr;
 
   while (node != nullptr) {
     if (node->mFunc == aCallback && node->mData == aData &&
+        node->mMatchKind == aMatchKind &&
         strcmp(node->mDomain, aPrefNode) == 0) {
       if (gCallbacksInProgress) {
         // postpone the node removal until after
         // callbacks enumeration is finished.
         node->mFunc = nullptr;
         gShouldCleanupDeadNodes = true;
         prev_node = node;
         node = node->mNext;
@@ -1171,20 +1176,25 @@ pref_DoCallback(const char* aChangedPref
   bool reentered = gCallbacksInProgress;
 
   // Nodes must not be deleted while gCallbacksInProgress is true.
   // Nodes that need to be deleted are marked for deletion by nulling
   // out the |func| pointer. We release them at the end of this function
   // if we haven't reentered.
   gCallbacksInProgress = true;
 
-  for (node = gFirstCallback; node != nullptr; node = node->mNext) {
-    if (node->mFunc &&
-        PL_strncmp(aChangedPref, node->mDomain, strlen(node->mDomain)) == 0) {
-      (*node->mFunc)(aChangedPref, node->mData);
+  for (node = gFirstCallback; node; node = node->mNext) {
+    if (node->mFunc) {
+      bool matches =
+        node->mMatchKind == Preferences::ExactMatch
+          ? strcmp(node->mDomain, aChangedPref) == 0
+          : strncmp(node->mDomain, aChangedPref, strlen(node->mDomain)) == 0;
+      if (matches) {
+        (node->mFunc)(aChangedPref, node->mData);
+      }
     }
   }
 
   gCallbacksInProgress = reentered;
 
   if (gShouldCleanupDeadNodes && !gCallbacksInProgress) {
     CallbackNode* prev_node = nullptr;
     node = gFirstCallback;
@@ -2526,30 +2536,16 @@ nsPrefBranch::GetComplexValue(const char
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     relativePref.forget(reinterpret_cast<nsIRelativeFilePref**>(aRetVal));
     return NS_OK;
   }
 
-  if (aType.Equals(NS_GET_IID(nsISupportsString))) {
-    nsCOMPtr<nsISupportsString> theString(
-      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv));
-
-    if (NS_SUCCEEDED(rv)) {
-      // Debugging to see why we end up with very long strings here with
-      // some addons, see bug 836263.
-      NS_ConvertUTF8toUTF16 wdata(utf8String);
-      theString->SetData(wdata);
-      theString.forget(reinterpret_cast<nsISupportsString**>(aRetVal));
-    }
-    return rv;
-  }
-
   NS_WARNING("nsPrefBranch::GetComplexValue - Unsupported interface type");
   return NS_NOINTERFACE;
 }
 
 nsresult
 nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName,
                                         const nsAString& aValue)
 {
@@ -2671,18 +2667,17 @@ nsPrefBranch::SetComplexValue(const char
     nsAutoCString descriptorString;
     descriptorString.Append('[');
     descriptorString.Append(relativeToKey);
     descriptorString.Append(']');
     descriptorString.Append(relDescriptor);
     return SetCharPrefInternal(aPrefName, descriptorString);
   }
 
-  if (aType.Equals(NS_GET_IID(nsISupportsString)) ||
-      aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
+  if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
     nsCOMPtr<nsISupportsString> theString = do_QueryInterface(aValue);
 
     if (theString) {
       nsString wideString;
 
       rv = theString->GetData(wideString);
       if (NS_SUCCEEDED(rv)) {
         // Check sanity of string length before any lengthy conversion
@@ -2893,18 +2888,22 @@ nsPrefBranch::AddObserver(const char* aD
   }
 
   p.OrInsert([&pCallback]() { return pCallback; });
 
   // We must pass a fully qualified preference name to the callback
   // aDomain == nullptr is the only possible failure, and we trapped it with
   // NS_ENSURE_ARG above.
   const PrefName& pref = GetPrefName(aDomain);
-  PREF_RegisterCallback(
-    pref.get(), NotifyObserver, pCallback, /* isPriority */ false);
+  PREF_RegisterCallback(pref.get(),
+                        NotifyObserver,
+                        pCallback,
+                        Preferences::PrefixMatch,
+                        /* isPriority */ false);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrefBranch::RemoveObserver(const char* aDomain, nsIObserver* aObserver)
 {
   NS_ENSURE_ARG(aDomain);
   NS_ENSURE_ARG(aObserver);
@@ -2926,17 +2925,18 @@ nsPrefBranch::RemoveObserver(const char*
   // to it. Unregister the callback first, and then let the owning pointer go
   // out of scope and destroy the callback.
   PrefCallback key(aDomain, aObserver, this);
   nsAutoPtr<PrefCallback> pCallback;
   mObservers.Remove(&key, &pCallback);
   if (pCallback) {
     // aDomain == nullptr is the only possible failure, trapped above.
     const PrefName& pref = GetPrefName(aDomain);
-    rv = PREF_UnregisterCallback(pref.get(), NotifyObserver, pCallback);
+    rv = PREF_UnregisterCallback(
+      pref.get(), NotifyObserver, pCallback, Preferences::PrefixMatch);
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsPrefBranch::Observe(nsISupports* aSubject,
                       const char* aTopic,
@@ -2988,17 +2988,20 @@ nsPrefBranch::FreeObserverList()
   // over it. In particular, some clients will call RemoveObserver() when
   // they're removed and destructed via the iterator; we set
   // mFreeingObserverList to keep those calls from touching mObservers.
   mFreeingObserverList = true;
   for (auto iter = mObservers.Iter(); !iter.Done(); iter.Next()) {
     nsAutoPtr<PrefCallback>& callback = iter.Data();
     nsPrefBranch* prefBranch = callback->GetPrefBranch();
     const PrefName& pref = prefBranch->GetPrefName(callback->GetDomain().get());
-    PREF_UnregisterCallback(pref.get(), nsPrefBranch::NotifyObserver, callback);
+    PREF_UnregisterCallback(pref.get(),
+                            nsPrefBranch::NotifyObserver,
+                            callback,
+                            Preferences::PrefixMatch);
     iter.Remove();
   }
   mFreeingObserverList = false;
 }
 
 void
 nsPrefBranch::RemoveExpiredCallback(PrefCallback* aCallback)
 {
@@ -3199,120 +3202,16 @@ static const char kPrefFileHeader[] =
 // clang-format on
 
 StaticRefPtr<Preferences> Preferences::sPreferences;
 bool Preferences::sShutdown = false;
 
 // This globally enables or disables OMT pref writing, both sync and async.
 static int32_t sAllowOMTPrefWrite = -1;
 
-class ValueObserverHashKey : public PLDHashEntryHdr
-{
-public:
-  typedef ValueObserverHashKey* KeyType;
-  typedef const ValueObserverHashKey* KeyTypePointer;
-
-  static const ValueObserverHashKey* KeyToPointer(ValueObserverHashKey* aKey)
-  {
-    return aKey;
-  }
-
-  static PLDHashNumber HashKey(const ValueObserverHashKey* aKey)
-  {
-    PLDHashNumber hash = HashString(aKey->mPrefName);
-    hash = AddToHash(hash, aKey->mMatchKind);
-    return AddToHash(hash, aKey->mCallback);
-  }
-
-  ValueObserverHashKey(const char* aPref,
-                       PrefChangedFunc aCallback,
-                       Preferences::MatchKind aMatchKind)
-    : mPrefName(aPref)
-    , mCallback(aCallback)
-    , mMatchKind(aMatchKind)
-  {
-  }
-
-  explicit ValueObserverHashKey(const ValueObserverHashKey* aOther)
-    : mPrefName(aOther->mPrefName)
-    , mCallback(aOther->mCallback)
-    , mMatchKind(aOther->mMatchKind)
-  {
-  }
-
-  bool KeyEquals(const ValueObserverHashKey* aOther) const
-  {
-    return mCallback == aOther->mCallback && mPrefName == aOther->mPrefName &&
-           mMatchKind == aOther->mMatchKind;
-  }
-
-  ValueObserverHashKey* GetKey() const
-  {
-    return const_cast<ValueObserverHashKey*>(this);
-  }
-
-  enum
-  {
-    ALLOW_MEMMOVE = true
-  };
-
-  nsCString mPrefName;
-  PrefChangedFunc mCallback;
-  Preferences::MatchKind mMatchKind;
-};
-
-class ValueObserver final
-  : public nsIObserver
-  , public ValueObserverHashKey
-{
-  ~ValueObserver() = default;
-
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
-
-  ValueObserver(const char* aPref,
-                PrefChangedFunc aCallback,
-                Preferences::MatchKind aMatchKind)
-    : ValueObserverHashKey(aPref, aCallback, aMatchKind)
-  {
-  }
-
-  void AppendClosure(void* aClosure) { mClosures.AppendElement(aClosure); }
-
-  void RemoveClosure(void* aClosure) { mClosures.RemoveElement(aClosure); }
-
-  bool HasNoClosures() { return mClosures.Length() == 0; }
-
-  nsTArray<void*> mClosures;
-};
-
-NS_IMPL_ISUPPORTS(ValueObserver, nsIObserver)
-
-NS_IMETHODIMP
-ValueObserver::Observe(nsISupports* aSubject,
-                       const char* aTopic,
-                       const char16_t* aData)
-{
-  NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
-               "invalid topic");
-
-  NS_ConvertUTF16toUTF8 data(aData);
-  if (mMatchKind == Preferences::ExactMatch &&
-      !mPrefName.EqualsASCII(data.get())) {
-    return NS_OK;
-  }
-
-  for (uint32_t i = 0; i < mClosures.Length(); i++) {
-    mCallback(data.get(), mClosures.ElementAt(i));
-  }
-
-  return NS_OK;
-}
-
 // Write the preference data to a file.
 class PreferencesWriter final
 {
 public:
   PreferencesWriter() = default;
 
   static nsresult Write(nsIFile* aFile, PrefSaveData& aPrefs)
   {
@@ -3451,18 +3350,16 @@ struct CacheData
     float mDefaultValueFloat;
   };
 };
 
 // gCacheDataDesc holds information about prefs startup. It's being used for
 // diagnosing prefs startup problems in bug 1276488.
 static const char* gCacheDataDesc = "untouched";
 static nsTArray<nsAutoPtr<CacheData>>* gCacheData = nullptr;
-static nsRefPtrHashtable<ValueObserverHashKey, ValueObserver>* gObserverTable =
-  nullptr;
 
 #ifdef DEBUG
 static bool
 HaveExistingCacheFor(void* aPtr)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (gCacheData) {
     for (size_t i = 0, count = gCacheData->Length(); i < count; ++i) {
@@ -3518,24 +3415,16 @@ Preferences::SizeOfIncludingThisAndOther
 
   if (gCacheData) {
     n += gCacheData->ShallowSizeOfIncludingThis(aMallocSizeOf);
     for (uint32_t i = 0, count = gCacheData->Length(); i < count; ++i) {
       n += aMallocSizeOf((*gCacheData)[i]);
     }
   }
 
-  if (gObserverTable) {
-    n += gObserverTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
-    for (auto iter = gObserverTable->Iter(); !iter.Done(); iter.Next()) {
-      n += iter.Key()->mPrefName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
-      n += iter.Data()->mClosures.ShallowSizeOfExcludingThis(aMallocSizeOf);
-    }
-  }
-
   if (sPreferences->mRootBranch) {
     n += static_cast<nsPrefBranch*>(sPreferences->mRootBranch.get())
            ->SizeOfIncludingThis(aMallocSizeOf);
   }
 
   if (sPreferences->mDefaultRootBranch) {
     n += static_cast<nsPrefBranch*>(sPreferences->mDefaultRootBranch.get())
            ->SizeOfIncludingThis(aMallocSizeOf);
@@ -3700,18 +3589,16 @@ Preferences::GetInstanceForService()
     sPreferences = nullptr;
     gCacheDataDesc = res.unwrapErr();
     return nullptr;
   }
 
   gCacheData = new nsTArray<nsAutoPtr<CacheData>>();
   gCacheDataDesc = "set by GetInstanceForService()";
 
-  gObserverTable = new nsRefPtrHashtable<ValueObserverHashKey, ValueObserver>();
-
   // Preferences::GetInstanceForService() can be called from GetService(), and
   // RegisterStrongMemoryReporter calls GetService(nsIMemoryReporter).  To
   // avoid a potential recursive GetService() call, we can't register the
   // memory reporter here; instead, do it off a runnable.
   RefPtr<AddPreferencesMemoryReporterRunnable> runnable =
     new AddPreferencesMemoryReporterRunnable();
   NS_DispatchToMainThread(runnable);
 
@@ -3758,19 +3645,16 @@ Preferences::Preferences()
   , mDefaultRootBranch(new nsPrefBranch("", true))
 {
 }
 
 Preferences::~Preferences()
 {
   MOZ_ASSERT(!sPreferences);
 
-  delete gObserverTable;
-  gObserverTable = nullptr;
-
   delete gCacheData;
   gCacheData = nullptr;
 
   PREF_Cleanup();
 }
 
 //
 // nsISupports Implementation
@@ -4935,72 +4819,42 @@ Preferences::RemoveObservers(nsIObserver
 
   for (uint32_t i = 0; aPrefs[i]; i++) {
     nsresult rv = RemoveObserver(aObserver, aPrefs[i]);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
-static void
-NotifyObserver(const char* aPref, void* aClosure)
-{
-  nsCOMPtr<nsIObserver> observer = static_cast<nsIObserver*>(aClosure);
-  observer->Observe(nullptr,
-                    NS_PREFBRANCH_PREFCHANGE_TOPIC_ID,
-                    NS_ConvertASCIItoUTF16(aPref).get());
-}
-
-static void
-RegisterCallbackHelper(PrefChangedFunc aCallback,
-                       const char* aPref,
-                       void* aClosure,
-                       Preferences::MatchKind aMatchKind,
-                       bool aIsPriority)
-{
-  ValueObserverHashKey hashKey(aPref, aCallback, aMatchKind);
-  RefPtr<ValueObserver> observer;
-  gObserverTable->Get(&hashKey, getter_AddRefs(observer));
-  if (observer) {
-    observer->AppendClosure(aClosure);
-    return;
-  }
-
-  observer = new ValueObserver(aPref, aCallback, aMatchKind);
-  observer->AppendClosure(aClosure);
-  PREF_RegisterCallback(
-    aPref, NotifyObserver, static_cast<nsIObserver*>(observer), aIsPriority);
-  gObserverTable->Put(observer, observer);
-}
-
 // RegisterVarCacheCallback uses high priority callbacks to ensure that cache
 // observers are called prior to ordinary pref observers. Doing this ensures
 // that ordinary observers will never get stale values from cache variables.
 static void
 RegisterVarCacheCallback(PrefChangedFunc aCallback,
                          const char* aPref,
                          void* aClosure)
 {
   MOZ_ASSERT(Preferences::IsServiceAvailable());
 
-  RegisterCallbackHelper(
-    aCallback, aPref, aClosure, Preferences::ExactMatch, /* isPriority */ true);
+  PREF_RegisterCallback(
+    aPref, aCallback, aClosure, Preferences::ExactMatch, /* isPriority */ true);
 }
 
 /* static */ nsresult
 Preferences::RegisterCallback(PrefChangedFunc aCallback,
                               const char* aPref,
                               void* aClosure,
                               MatchKind aMatchKind)
 {
   MOZ_ASSERT(aCallback);
   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
 
-  RegisterCallbackHelper(
-    aCallback, aPref, aClosure, aMatchKind, /* isPriority */ false);
+  PREF_RegisterCallback(
+    aPref, aCallback, aClosure, aMatchKind, /* isPriority */ false);
+
   return NS_OK;
 }
 
 /* static */ nsresult
 Preferences::RegisterCallbackAndCall(PrefChangedFunc aCallback,
                                      const char* aPref,
                                      void* aClosure,
                                      MatchKind aMatchKind)
@@ -5021,32 +4875,17 @@ Preferences::UnregisterCallback(PrefChan
                                 MatchKind aMatchKind)
 {
   MOZ_ASSERT(aCallback);
   if (!sPreferences && sShutdown) {
     return NS_OK; // Observers have been released automatically.
   }
   NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
 
-  ValueObserverHashKey hashKey(aPref, aCallback, aMatchKind);
-  RefPtr<ValueObserver> observer;
-  gObserverTable->Get(&hashKey, getter_AddRefs(observer));
-  if (!observer) {
-    return NS_OK;
-  }
-
-  observer->RemoveClosure(aClosure);
-  if (observer->HasNoClosures()) {
-    // Delete the callback since its list of closures is empty.
-    MOZ_ALWAYS_SUCCEEDS(
-      PREF_UnregisterCallback(aPref, NotifyObserver, observer));
-
-    gObserverTable->Remove(observer);
-  }
-  return NS_OK;
+  return PREF_UnregisterCallback(aPref, aCallback, aClosure, aMatchKind);
 }
 
 static void
 BoolVarChanged(const char* aPref, void* aClosure)
 {
   CacheData* cache = static_cast<CacheData*>(aClosure);
   *static_cast<bool*>(cache->mCacheLocation) =
     Preferences::GetBool(aPref, cache->mDefaultValueBool);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3208,43 +3208,32 @@ pref("dom.max_script_run_time", 10);
 pref("dom.max_ext_content_script_run_time", 5);
 
 // Stop all scripts in a compartment when the "stop script" dialog is used.
 pref("dom.global_stop_script", true);
 
 // Time (milliseconds) between throttled idle callbacks.
 pref("dom.idle_period.throttled_length", 10000);
 
-// The amount of idle time (milliseconds) reserved for a long idle period
-pref("idle_queue.long_period", 50);
-
 // Support the input event queue on the main thread of content process
 pref("input_event_queue.supported", true);
 
 // The maximum and minimum time (milliseconds) we reserve for handling input
 // events in each frame.
 pref("input_event_queue.duration.max", 8);
 pref("input_event_queue.duration.min", 1);
 
 // The default amount of time (milliseconds) required for handling a input
 // event.
 pref("input_event_queue.default_duration_per_event", 1);
 
 // The number of processed input events we use to predict the amount of time
 // required to process the following input events.
 pref("input_event_queue.count_for_prediction", 9);
 
-// The minimum amount of time (milliseconds) required for an idle
-// period to be scheduled on the main thread. N.B. that
-// layout.idle_period.time_limit adds padding at the end of the idle
-// period, which makes the point in time that we expect to become busy
-// again be:
-// now + idle_queue.min_period + layout.idle_period.time_limit
-pref("idle_queue.min_period", 3);
-
 // Hang monitor timeout after which we kill the browser, in seconds
 // (0 is disabled)
 // Disabled on all platforms per bug 705748 until the found issues are
 // resolved.
 pref("hangmonitor.timeout", 0);
 
 pref("plugins.load_appdir_plugins", false);
 // If true, plugins will be click to play
@@ -5205,44 +5194,21 @@ pref("layout.css.expensive-style-struct-
 
 #if defined(MOZ_WIDGET_ANDROID)
 // Network Information API
 pref("dom.netinfo.enabled", true);
 #else
 pref("dom.netinfo.enabled", false);
 #endif
 
-#ifdef XP_WIN
-// On 32-bit Windows, fire a low-memory notification if we have less than this
-// many mb of virtual address space available.
-pref("memory.low_virtual_memory_threshold_mb", 128);
-
-// On Windows 32-bit, fire a low-memory notification if we have less
-// than this many mb of commit space (physical memory plus page file) left.
-pref("memory.low_commit_space_threshold_mb", 128);
-
-// On Windows 32-bit, fire a low-memory notification if we have less
-// than this many mb of physical memory available on the whole machine.
-pref("memory.low_physical_memory_threshold_mb", 0);
-
-// On Windows 32-bit, don't fire a low-memory notification because of
-// low available physical memory or low commit space more than once every
-// low_memory_notification_interval_ms.
-pref("memory.low_memory_notification_interval_ms", 10000);
-#endif
-
 // How long must we wait before declaring that a window is a "ghost" (i.e., a
 // likely leak)?  This should be longer than it usually takes for an eligible
 // window to be collected via the GC/CC.
 pref("memory.ghost_window_timeout_seconds", 60);
 
-// On memory pressure, release dirty but unused pages held by jemalloc
-// back to the system.
-pref("memory.free_dirty_pages", true);
-
 // Don't dump memory reports on OOM, by default.
 pref("memory.dump_reports_on_oom", false);
 
 // Number of stack frames to capture in createObjectURL for about:memory.
 pref("memory.blob_report.stack_frames", 0);
 
 // Disable idle observer fuzz, because only privileged content can access idle
 // observers (bug 780507).
--- a/modules/libpref/nsIPrefBranch.idl
+++ b/modules/libpref/nsIPrefBranch.idl
@@ -186,18 +186,16 @@ interface nsIPrefBranch : nsISupports
    * Called to get the state of an individual complex preference. A complex
    * preference is a preference which represents an XPCOM object that can not
    * be easily represented using a standard boolean, integer or string value.
    *
    * @param aPrefName The complex preference to get the value of.
    * @param aType     The XPCOM interface that this complex preference
    *                  represents. Interfaces currently supported are:
    *                    - nsIFile
-   *                    - nsISupportsString (UniChar)
-   *                      (deprecated; see getStringPref)
    *                    - nsIPrefLocalizedString (Localized UniChar)
    * @param aValue    The XPCOM object into which to the complex preference 
    *                  value should be retrieved.
    *
    * @throws Error The value does not exist or is the wrong type.
    *
    * @see setComplexValue
    */
--- a/modules/libpref/test/unit/test_defaultValues.js
+++ b/modules/libpref/test/unit/test_defaultValues.js
@@ -31,23 +31,16 @@ function run_test() {
   prefName = "test.default.values.string";
   do_check_throws(function() { ps.getCharPref(prefName); },
                   Cr.NS_ERROR_UNEXPECTED);
   strictEqual(ps.getStringPref(prefName, ""), "");
   strictEqual(ps.getStringPref(prefName, "éèçàê€"), "éèçàê€");
   ps.setStringPref(prefName, "éèçàê€");
   strictEqual(ps.getStringPref(prefName), "éèçàê€");
   strictEqual(ps.getStringPref(prefName, "string"), "éèçàê€");
-  strictEqual(ps.getStringPref(prefName),
-              ps.getComplexValue(prefName, Ci.nsISupportsString).data);
-  let str = Cc["@mozilla.org/supports-string;1"].
-              createInstance(Ci.nsISupportsString);
-  str.data = "ù€ÚîœïŒëøÇ“";
-  ps.setComplexValue(prefName, Ci.nsISupportsString, str);
-  strictEqual(ps.getStringPref(prefName), "ù€ÚîœïŒëøÇ“");
 
   prefName = "test.default.values.float";
   do_check_throws(function() { ps.getFloatPref(prefName); },
                   Cr.NS_ERROR_UNEXPECTED);
   strictEqual(ps.getFloatPref(prefName, 3.5), 3.5);
   strictEqual(ps.getFloatPref(prefName, 0), 0);
   ps.setCharPref(prefName, 1.75);
   strictEqual(ps.getFloatPref(prefName), 1.75);
--- a/modules/libpref/test/unit/test_libPrefs.js
+++ b/modules/libpref/test/unit/test_libPrefs.js
@@ -43,19 +43,19 @@ function run_test() {
     pb.getIntPref(null); },  Cr.NS_ERROR_INVALID_ARG);
   do_check_throws(function() {
     pb.setIntPref(null, 0); },  Cr.NS_ERROR_INVALID_ARG);
   do_check_throws(function() {
     pb.getCharPref(null); },  Cr.NS_ERROR_INVALID_ARG);
   do_check_throws(function() {
     pb.setCharPref(null, null); },  Cr.NS_ERROR_INVALID_ARG);
   do_check_throws(function() {
-    pb.getComplexValue(null, Components.interfaces.nsISupportsString); },  Cr.NS_ERROR_INVALID_ARG);
+    pb.getStringPref(null); },  Cr.NS_ERROR_INVALID_ARG);
   do_check_throws(function() {
-    pb.setComplexValue(null, Components.interfaces.nsISupportsString, pb); },  Cr.NS_ERROR_INVALID_ARG);
+    pb.setStringPref(null, null); },  Cr.NS_ERROR_INVALID_ARG);
   do_check_throws(function() {
     pb.clearUserPref(null); },  Cr.NS_ERROR_INVALID_ARG);
   do_check_throws(function() {
     pb.prefHasUserValue(null); },  Cr.NS_ERROR_INVALID_ARG);
   do_check_throws(function() {
     pb.lockPref(null); },  Cr.NS_ERROR_INVALID_ARG);
   do_check_throws(function() {
     pb.prefIsLocked(null); },  Cr.NS_ERROR_INVALID_ARG);
--- a/netwerk/dns/nsIDNService.cpp
+++ b/netwerk/dns/nsIDNService.cpp
@@ -97,24 +97,24 @@ NS_IMETHODIMP nsIDNService::Observe(nsIS
 }
 
 void nsIDNService::prefsChanged(nsIPrefBranch *prefBranch, const char16_t *pref)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mLock.AssertCurrentThreadOwns();
 
   if (!pref || NS_LITERAL_STRING(NS_NET_PREF_IDNBLACKLIST).Equals(pref)) {
-    nsCOMPtr<nsISupportsString> blacklist;
-    nsresult rv = prefBranch->GetComplexValue(NS_NET_PREF_IDNBLACKLIST,
-                                              NS_GET_IID(nsISupportsString),
-                                              getter_AddRefs(blacklist));
-    if (NS_SUCCEEDED(rv))
-      blacklist->ToString(getter_Copies(mIDNBlacklist));
-    else
+    nsAutoCString blacklist;
+    nsresult rv =
+      prefBranch->GetStringPref(NS_NET_PREF_IDNBLACKLIST, EmptyCString(), 0, blacklist);
+    if (NS_SUCCEEDED(rv)) {
+      CopyUTF8toUTF16(blacklist, mIDNBlacklist);
+    } else {
       mIDNBlacklist.Truncate();
+    }
   }
   if (!pref || NS_LITERAL_STRING(NS_NET_PREF_SHOWPUNYCODE).Equals(pref)) {
     bool val;
     if (NS_SUCCEEDED(prefBranch->GetBoolPref(NS_NET_PREF_SHOWPUNYCODE, &val)))
       mShowPunycode = val;
   }
   if (!pref || NS_LITERAL_STRING(NS_NET_PREF_IDNUSEWHITELIST).Equals(pref)) {
     bool val;
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
@@ -30,26 +30,30 @@
 #include "WinUtils.h"
 
 // This list of DLLs have been found to cause instability in sandboxed child
 // processes and so they will be unloaded if they attempt to load.
 const std::vector<std::wstring> kDllsToUnload = {
   // Symantec Corporation (bug 1400637)
   L"ffm64.dll",
   L"ffm.dll",
+  L"prntm64.dll",
 
   // HitmanPro - SurfRight now part of Sophos (bug 1400637)
   L"hmpalert.dll",
 
   // Avast Antivirus (bug 1400637)
   L"snxhk64.dll",
   L"snxhk.dll",
 
   // Webroot SecureAnywhere (bug 1400637)
   L"wrusr.dll",
+
+  // Comodo Internet Security (bug 1400637)
+  L"guard32.dll",
 };
 
 namespace mozilla
 {
 
 sandbox::BrokerServices *SandboxBroker::sBrokerService = nullptr;
 
 // This is set to true in Initialize when our exe file name has a drive type of
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -863,26 +863,26 @@ class Marionette(object):
         :param pref: Name of the preference.
         """
         with self.using_context(self.CONTEXT_CHROME):
             self.execute_script("""
                Components.utils.import("resource://gre/modules/Preferences.jsm");
                Preferences.reset(arguments[0]);
                """, script_args=(pref,))
 
-    def get_pref(self, pref, default_branch=False, value_type="nsISupportsString"):
+    def get_pref(self, pref, default_branch=False, value_type="unspecified"):
         """Get the value of the specified preference.
 
         :param pref: Name of the preference.
         :param default_branch: Optional, if `True` the preference value will be read
                                from the default branch. Otherwise the user-defined
                                value if set is returned. Defaults to `False`.
         :param value_type: Optional, XPCOM interface of the pref's complex value.
-                           Defaults to `nsISupportsString`. Other possible values are:
-                           `nsIFile`, and `nsIPrefLocalizedString`.
+                           Possible values are: `nsIFile` and
+                           `nsIPrefLocalizedString`.
 
         Usage example::
 
             marionette.get_pref("browser.tabs.warnOnClose")
 
         """
         with self.using_context(self.CONTEXT_CHROME):
             pref_value = self.execute_script("""
--- a/testing/talos/talos/tests/devtools/addon/content/damp.js
+++ b/testing/talos/talos/tests/devtools/addon/content/damp.js
@@ -14,18 +14,18 @@ XPCOMUtils.defineLazyGetter(this, "gDevT
 XPCOMUtils.defineLazyGetter(this, "EVENTS", function() {
   let { EVENTS } = require("devtools/client/netmonitor/src/constants");
   return EVENTS;
 });
 XPCOMUtils.defineLazyGetter(this, "TargetFactory", function() {
   let { TargetFactory } = require("devtools/client/framework/target");
   return TargetFactory;
 });
-XPCOMUtils.defineLazyGetter(this, "ThreadSafeChromeUtils", function() {
-  return require("ThreadSafeChromeUtils");
+XPCOMUtils.defineLazyGetter(this, "ChromeUtils", function() {
+  return require("ChromeUtils");
 });
 
 const webserver = Services.prefs.getCharPref("addon.test.damp.webserver");
 
 const SIMPLE_URL = webserver + "/tests/devtools/addon/content/pages/simple.html";
 const COMPLICATED_URL = webserver + "/tests/tp5n/bild.de/www.bild.de/index.html";
 
 function getMostRecentBrowserWindow() {
@@ -137,17 +137,17 @@ Damp.prototype = {
         name: label + ".saveHeapSnapshot",
         value: end - start
       });
     });
   },
 
   readHeapSnapshot(label) {
     let start = performance.now();
-    this._snapshot = ThreadSafeChromeUtils.readHeapSnapshot(this._heapSnapshotFilePath);
+    this._snapshot = ChromeUtils.readHeapSnapshot(this._heapSnapshotFilePath);
     let end = performance.now();
     this._results.push({
       name: label + ".readHeapSnapshot",
       value: end - start
     });
     return Promise.resolve();
   },
 
deleted file mode 100644
--- a/testing/web-platform/meta/html/browsers/origin/cross-origin-objects/cross-origin-objects.html.ini
+++ /dev/null
@@ -1,10 +0,0 @@
-[cross-origin-objects.html]
-  type: testharness
-  [[[GetOwnProperty\]\] - Property descriptors for cross-origin properties should be set up correctly]
-    expected: FAIL
-
-  [Can only enumerate safelisted properties]
-    expected: FAIL
-
-  [[[OwnPropertyKeys\]\] should return all properties from cross-origin objects]
-    expected: FAIL
--- a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
@@ -179,27 +179,30 @@ addTest(function() {
 addTest(function() {
   assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'close')), "C.close is |own|");
   assert_true(isObject(Object.getOwnPropertyDescriptor(C, 'top')), "C.top is |own|");
   assert_true(isObject(Object.getOwnPropertyDescriptor(C.location, 'href')), "C.location.href is |own|");
   assert_true(isObject(Object.getOwnPropertyDescriptor(C.location, 'replace')), "C.location.replace is |own|");
 }, "[[GetOwnProperty]] - Properties on cross-origin objects should be reported |own|");
 
 function checkPropertyDescriptor(desc, propName, expectWritable) {
-  var isSymbol = (typeof(propName) == "symbol");
+  const isSymbol = typeof(propName) === "symbol";
+  const isArrayIndexPropertyName = !isSymbol && !isNaN(parseInt(propName, 10));
   propName = String(propName);
   assert_true(isObject(desc), "property descriptor for " + propName + " should exist");
   assert_equals(desc.configurable, true, "property descriptor for " + propName + " should be configurable");
-  if (isSymbol) {
-    assert_equals(desc.enumerable, false, "symbol-property descriptor for " + propName + " should not be enumerable");
-    assert_true("value" in desc,
-                "property descriptor for " + propName + " should be a value descriptor");
-    assert_equals(desc.value, undefined,
+  if (!isArrayIndexPropertyName) {
+    assert_equals(desc.enumerable, false, "property descriptor for " + propName + " should not be enumerable");
+    if(isSymbol) {
+      assert_true("value" in desc,
+                  "property descriptor for " + propName + " should be a value descriptor");
+      assert_equals(desc.value, undefined,
                   "symbol-named cross-origin visible prop " + propName +
                   " should come back as undefined");
+    }
   } else {
     assert_equals(desc.enumerable, true, "property descriptor for " + propName + " should be enumerable");
   }
   if ('value' in desc)
     assert_equals(desc.writable, expectWritable, "property descriptor for " + propName + " should have writable: " + expectWritable);
   else
     assert_equals(typeof desc.set != 'undefined', expectWritable,
                   "property descriptor for " + propName + " should " + (expectWritable ? "" : "not ") + "have setter");
@@ -260,44 +263,42 @@ addTest(function() {
 /*
  * EnumerateObjectProperties (backed by [[OwnPropertyKeys]])
  */
 
 addTest(function() {
   let i = 0;
   for (var prop in C) {
     i++;
-    assert_true(whitelistedWindowPropNames.includes(prop), prop + " is not safelisted for a cross-origin Window");
+    assert_true(whitelistedWindowIndices.includes(prop), prop + " is not safelisted for a cross-origin Window");
   }
-  assert_equals(i, whitelistedWindowPropNames.length, "Enumerate all safelisted cross-origin Window properties");
+  assert_equals(i, whitelistedWindowIndices.length, "Enumerate all enumerable safelisted cross-origin Window properties");
   i = 0;
   for (var prop in C.location) {
     i++;
-    assert_true(whitelistedLocationPropNames.includes(prop), prop + " is not safelisted for a cross-origin Location");
   }
-  assert_equals(i, whitelistedLocationPropNames.length, "Enumerate all safelisted cross-origin Location properties");
-}, "Can only enumerate safelisted properties");
+  assert_equals(i, 0, "There's nothing to enumerate for cross-origin Location properties");
+}, "Can only enumerate safelisted enumerable properties");
 
 /*
  * [[OwnPropertyKeys]]
  */
 
 addTest(function() {
   assert_array_equals(Object.getOwnPropertyNames(C).sort(),
                       whitelistedWindowPropNames,
                       "Object.getOwnPropertyNames() gives the right answer for cross-origin Window");
   assert_array_equals(Object.keys(C).sort(),
-                      whitelistedWindowPropNames,
+                      whitelistedWindowIndices,
                       "Object.keys() gives the right answer for cross-origin Window");
   assert_array_equals(Object.getOwnPropertyNames(C.location).sort(),
                       whitelistedLocationPropNames,
                       "Object.getOwnPropertyNames() gives the right answer for cross-origin Location");
-  assert_array_equals(Object.keys(C.location).sort(),
-                      whitelistedLocationPropNames,
-                      "Object.keys() gives the right answer for cross-origin Location");
+  assert_equals(Object.keys(C.location).length, 0,
+                "Object.keys() gives the right answer for cross-origin Location");
 }, "[[OwnPropertyKeys]] should return all properties from cross-origin objects");
 
 addTest(function() {
   assert_array_equals(Object.getOwnPropertySymbols(C), whitelistedSymbols,
     "Object.getOwnPropertySymbols() should return the three symbol-named properties that are exposed on a cross-origin Window");
   assert_array_equals(Object.getOwnPropertySymbols(C.location),
                       whitelistedSymbols,
     "Object.getOwnPropertySymbols() should return the three symbol-named properties that are exposed on a cross-origin Location");
--- a/toolkit/components/ctypes/tests/unit/head.js
+++ b/toolkit/components/ctypes/tests/unit/head.js
@@ -1,9 +1,9 @@
-/* global ThreadSafeChromeUtils */
+/* global ChromeUtils */
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 try {
   // We might be running without privileges, in which case it's up to the
   // harness to give us the 'ctypes' object.
   Components.utils.import("resource://gre/modules/ctypes.jsm");
 } catch (e) {
@@ -21,17 +21,17 @@ function ResourceCleaner() {
   this._map = new WeakMap();
 }
 ResourceCleaner.prototype = {
   add: function ResourceCleaner_add(v) {
     this._map.set(v);
     return v;
   },
   cleanup: function ResourceCleaner_cleanup() {
-    let keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(this._map);
+    let keys = ChromeUtils.nondeterministicGetWeakMapKeys(this._map);
     keys.forEach(k => {
       try {
         k.dispose();
       } catch (x) {
         // This can fail if |forget|/|dispose| has been called manually
         // during the test. This is normal.
       }
       this._map.delete(k);
--- a/toolkit/components/telemetry/TelemetryEnvironment.jsm
+++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm
@@ -225,17 +225,16 @@ const DEFAULT_ENVIRONMENT_PREFS = new Ma
   ["extensions.strictCompatibility", {what: RECORD_PREF_VALUE}],
   ["extensions.update.enabled", {what: RECORD_PREF_VALUE}],
   ["extensions.update.url", {what: RECORD_PREF_VALUE}],
   ["extensions.update.background.url", {what: RECORD_PREF_VALUE}],
   ["extensions.screenshots.disabled", {what: RECORD_PREF_VALUE}],
   ["general.smoothScroll", {what: RECORD_PREF_VALUE}],
   ["gfx.direct2d.disabled", {what: RECORD_PREF_VALUE}],
   ["gfx.direct2d.force-enabled", {what: RECORD_PREF_VALUE}],
-  ["gfx.direct2d.use1_1", {what: RECORD_PREF_VALUE}],
   ["layers.acceleration.disabled", {what: RECORD_PREF_VALUE}],
   ["layers.acceleration.force-enabled", {what: RECORD_PREF_VALUE}],
   ["layers.async-pan-zoom.enabled", {what: RECORD_PREF_VALUE}],
   ["layers.async-video-oop.enabled", {what: RECORD_PREF_VALUE}],
   ["layers.async-video.enabled", {what: RECORD_PREF_VALUE}],
   ["layers.componentalpha.enabled", {what: RECORD_PREF_VALUE}],
   ["layers.d3d11.disable-warp", {what: RECORD_PREF_VALUE}],
   ["layers.d3d11.force-warp", {what: RECORD_PREF_VALUE}],
--- a/toolkit/content/tests/chrome/test_preferences.xul
+++ b/toolkit/content/tests/chrome/test_preferences.xul
@@ -17,50 +17,44 @@
                             .getService(Components.interfaces.nsIPrefBranch);
 
     // preference values, set 1
     const kPrefValueSet1 =
     {
         int:          23,
         bool:         true,
         string:       "rheeet!",
+        unichar:      "äöüßÄÖÜ",
         wstring_data: "日本語",
-        unichar_data: "äöüßÄÖÜ",
         file_data:    "/",
 
         wstring: Components.classes["@mozilla.org/pref-localizedstring;1"]
                            .createInstance(Components.interfaces.nsIPrefLocalizedString),
-        unichar: Components.classes["@mozilla.org/supports-string;1"]
-                           .createInstance(Components.interfaces.nsISupportsString),
         file:    Components.classes["@mozilla.org/file/local;1"]
                            .createInstance(Components.interfaces.nsIFile)
     };
     kPrefValueSet1.wstring.data = kPrefValueSet1.wstring_data;
-    kPrefValueSet1.unichar.data = kPrefValueSet1.unichar_data;
     SafeFileInit(kPrefValueSet1.file, kPrefValueSet1.file_data);
 
     // preference values, set 2
     const kPrefValueSet2 =
     {
         int:          42,
         bool:         false,
         string:       "Mozilla",
+        unichar:      "áôùšŽ",
         wstring_data: "헤드라인A",
-        unichar_data: "áôùšŽ",
         file_data:    "/home",
 
         wstring: Components.classes["@mozilla.org/pref-localizedstring;1"]
                            .createInstance(Components.interfaces.nsIPrefLocalizedString),
-        unichar: Components.classes["@mozilla.org/supports-string;1"]
-                           .createInstance(Components.interfaces.nsISupportsString),
         file:    Components.classes["@mozilla.org/file/local;1"]
                            .createInstance(Components.interfaces.nsIFile)
     };
     kPrefValueSet2.wstring.data = kPrefValueSet2.wstring_data;
-    kPrefValueSet2.unichar.data = kPrefValueSet2.unichar_data;
     SafeFileInit(kPrefValueSet2.file, kPrefValueSet2.file_data);
 
 
     function SafeFileInit(aFile, aPath)
     {
       // set file path without dying for exceptions
       try
       {
@@ -71,62 +65,57 @@
 
     function CreateEmptyPrefValueSet()
     {
       var result =
       {
         int:          undefined,
         bool:         undefined,
         string:       undefined,
+        unichar:      undefined,
         wstring_data: undefined,
-        unichar_data: undefined,
         file_data:    undefined,
         wstring:      undefined,
-        unichar:      undefined,
         file:         undefined
       };
       return result;
     }
 
     function WritePrefsToSystem(aPrefValueSet)
     {
       // write preference data via XPCOM
       kPref.setIntPref ("tests.static_preference_int",    aPrefValueSet.int);
       kPref.setBoolPref("tests.static_preference_bool",   aPrefValueSet.bool);
       kPref.setCharPref("tests.static_preference_string", aPrefValueSet.string);
+      kPref.setStringPref("tests.static_preference_unichar", aPrefValueSet.unichar);
       kPref.setComplexValue("tests.static_preference_wstring",
                             Components.interfaces.nsIPrefLocalizedString,
                             aPrefValueSet.wstring);
-      kPref.setComplexValue("tests.static_preference_unichar",
-                            Components.interfaces.nsISupportsString,
-                            aPrefValueSet.unichar);
       kPref.setComplexValue("tests.static_preference_file",
                             Components.interfaces.nsIFile,
                             aPrefValueSet.file);
     }
 
     function ReadPrefsFromSystem()
     {
       // read preference data via XPCOM
       var result = CreateEmptyPrefValueSet();
       try {result.int    = kPref.getIntPref ("tests.static_preference_int")   } catch (ignored) {};
       try {result.bool   = kPref.getBoolPref("tests.static_preference_bool")  } catch (ignored) {};
       try {result.string = kPref.getCharPref("tests.static_preference_string")} catch (ignored) {};
       try
       {
-        result.wstring = kPref.getComplexValue("tests.static_preference_wstring",
-                                               Components.interfaces.nsIPrefLocalizedString);
-        result.wstring_data = result.wstring.data;
+        result.unichar = kPref.getStringPref("tests.static_preference_unichar");
       }
       catch (ignored) {};
       try
       {
-        result.unichar = kPref.getComplexValue("tests.static_preference_unichar",
-                                               Components.interfaces.nsISupportsString);
-        result.unichar_data = result.unichar.data;
+        result.wstring = kPref.getComplexValue("tests.static_preference_wstring",
+                                               Components.interfaces.nsIPrefLocalizedString);
+        result.wstring_data = result.wstring.data;
       }
       catch (ignored) {};
       try
       {
         result.file = kPref.getComplexValue("tests.static_preference_file",
                                             Components.interfaces.nsIFile);
         result.file_data    = result.file.data;
       }
@@ -140,177 +129,170 @@
     }
 
     function WritePrefsToPreferences(aPrefWindow, aPrefValueSet)
     {
       // write preference data into <preference>s
       GetXULElement(aPrefWindow, "tests.static_preference_int"    ).value = aPrefValueSet.int;
       GetXULElement(aPrefWindow, "tests.static_preference_bool"   ).value = aPrefValueSet.bool;
       GetXULElement(aPrefWindow, "tests.static_preference_string" ).value = aPrefValueSet.string;
+      GetXULElement(aPrefWindow, "tests.static_preference_unichar").value = aPrefValueSet.unichar;
       GetXULElement(aPrefWindow, "tests.static_preference_wstring").value = aPrefValueSet.wstring_data;
-      GetXULElement(aPrefWindow, "tests.static_preference_unichar").value = aPrefValueSet.unichar_data;
       GetXULElement(aPrefWindow, "tests.static_preference_file"   ).value = aPrefValueSet.file_data;
     }
 
     function ReadPrefsFromPreferences(aPrefWindow)
     {
       // read preference data from <preference>s
       var result =
       {
         int:          GetXULElement(aPrefWindow, "tests.static_preference_int"    ).value,
         bool:         GetXULElement(aPrefWindow, "tests.static_preference_bool"   ).value,
         string:       GetXULElement(aPrefWindow, "tests.static_preference_string" ).value,
+        unichar:      GetXULElement(aPrefWindow, "tests.static_preference_unichar").value,
         wstring_data: GetXULElement(aPrefWindow, "tests.static_preference_wstring").value,
-        unichar_data: GetXULElement(aPrefWindow, "tests.static_preference_unichar").value,
         file_data:    GetXULElement(aPrefWindow, "tests.static_preference_file"   ).value,
         wstring: Components.classes["@mozilla.org/pref-localizedstring;1"]
                            .createInstance(Components.interfaces.nsIPrefLocalizedString),
-        unichar: Components.classes["@mozilla.org/supports-string;1"]
-                           .createInstance(Components.interfaces.nsISupportsString),
         file:    Components.classes["@mozilla.org/file/local;1"]
                            .createInstance(Components.interfaces.nsIFile)
       }
       result.wstring.data = result.wstring_data;
-      result.unichar.data = result.unichar_data;
       SafeFileInit(result.file, result.file_data);
       return result;
     }
 
     function WritePrefsToUI(aPrefWindow, aPrefValueSet)
     {
       // write preference data into UI elements
       GetXULElement(aPrefWindow, "static_element_int"    ).value   = aPrefValueSet.int;
       GetXULElement(aPrefWindow, "static_element_bool"   ).checked = aPrefValueSet.bool;
       GetXULElement(aPrefWindow, "static_element_string" ).value   = aPrefValueSet.string;
+      GetXULElement(aPrefWindow, "static_element_unichar").value   = aPrefValueSet.unichar;
       GetXULElement(aPrefWindow, "static_element_wstring").value   = aPrefValueSet.wstring_data;
-      GetXULElement(aPrefWindow, "static_element_unichar").value   = aPrefValueSet.unichar_data;
       GetXULElement(aPrefWindow, "static_element_file"   ).value   = aPrefValueSet.file_data;
     }
 
     function ReadPrefsFromUI(aPrefWindow)
     {
       // read preference data from <preference>s
       var result =
       {
         int:          GetXULElement(aPrefWindow, "static_element_int"    ).value,
         bool:         GetXULElement(aPrefWindow, "static_element_bool"   ).checked,
         string:       GetXULElement(aPrefWindow, "static_element_string" ).value,
+        unichar:      GetXULElement(aPrefWindow, "static_element_unichar").value,
         wstring_data: GetXULElement(aPrefWindow, "static_element_wstring").value,
-        unichar_data: GetXULElement(aPrefWindow, "static_element_unichar").value,
         file_data:    GetXULElement(aPrefWindow, "static_element_file"   ).value,
         wstring: Components.classes["@mozilla.org/pref-localizedstring;1"]
                            .createInstance(Components.interfaces.nsIPrefLocalizedString),
-        unichar: Components.classes["@mozilla.org/supports-string;1"]
-                           .createInstance(Components.interfaces.nsISupportsString),
         file:    Components.classes["@mozilla.org/file/local;1"]
                            .createInstance(Components.interfaces.nsIFile)
       }
       result.wstring.data = result.wstring_data;
-      result.unichar.data = result.unichar_data;
       SafeFileInit(result.file, result.file_data);
       return result;
     }
 
 
     function RunInstantPrefTest(aPrefWindow)
     {
       // remark: there's currently no UI element binding for files
 
       // were all <preferences> correctly initialized?
       var expected = kPrefValueSet1;
       var found    = ReadPrefsFromPreferences(aPrefWindow);
       ok(found.int          === expected.int,          "instant pref init int"    );
       ok(found.bool         === expected.bool,         "instant pref init bool"   );
       ok(found.string       === expected.string,       "instant pref init string" );
+      ok(found.unichar      === expected.unichar,      "instant pref init unichar");
       ok(found.wstring_data === expected.wstring_data, "instant pref init wstring");
-      ok(found.unichar_data === expected.unichar_data, "instant pref init unichar");
       todo(found.file_data  === expected.file_data,    "instant pref init file"   );
 
       // were all elements correctly initialized? (loose check)
       found = ReadPrefsFromUI(aPrefWindow);
       ok(found.int          == expected.int,          "instant element init int"    );
       ok(found.bool         == expected.bool,         "instant element init bool"   );
       ok(found.string       == expected.string,       "instant element init string" );
+      ok(found.unichar      == expected.unichar,      "instant element init unichar");
       ok(found.wstring_data == expected.wstring_data, "instant element init wstring");
-      ok(found.unichar_data == expected.unichar_data, "instant element init unichar");
       todo(found.file_data  == expected.file_data,    "instant element init file"   );
 
       // do some changes in the UI
       expected = kPrefValueSet2;
       WritePrefsToUI(aPrefWindow, expected);
 
       // UI changes should get passed to the <preference>s,
       // but currently they aren't if the changes are made programmatically
       // (the handlers preference.change/prefpane.input and prefpane.change
       //  are called for manual changes, though).
       found = ReadPrefsFromPreferences(aPrefWindow);
       todo(found.int          === expected.int,          "instant change pref int"    );
       todo(found.bool         === expected.bool,         "instant change pref bool"   );
       todo(found.string       === expected.string,       "instant change pref string" );
+      todo(found.unichar      === expected.unichar,      "instant change pref unichar");
       todo(found.wstring_data === expected.wstring_data, "instant change pref wstring");
-      todo(found.unichar_data === expected.unichar_data, "instant change pref unichar");
       todo(found.file_data    === expected.file_data,    "instant change pref file"   );
 
       // and these changes should get passed to the system instantly
       // (which obviously can't pass with the above failing)
       found = ReadPrefsFromSystem();
       todo(found.int          === expected.int,          "instant change element int"    );
       todo(found.bool         === expected.bool,         "instant change element bool"   );
       todo(found.string       === expected.string,       "instant change element string" );
+      todo(found.unichar      === expected.unichar,      "instant change element unichar");
       todo(found.wstring_data === expected.wstring_data, "instant change element wstring");
-      todo(found.unichar_data === expected.unichar_data, "instant change element unichar");
       todo(found.file_data    === expected.file_data,    "instant change element file"   );
 
       // try resetting the prefs to default values (which should be empty here)
       GetXULElement(aPrefWindow, "tests.static_preference_int"    ).reset();
       GetXULElement(aPrefWindow, "tests.static_preference_bool"   ).reset();
       GetXULElement(aPrefWindow, "tests.static_preference_string" ).reset();
+      GetXULElement(aPrefWindow, "tests.static_preference_unichar").reset();
       GetXULElement(aPrefWindow, "tests.static_preference_wstring").reset();
-      GetXULElement(aPrefWindow, "tests.static_preference_unichar").reset();
       GetXULElement(aPrefWindow, "tests.static_preference_file"   ).reset();
 
       // check system
       expected = CreateEmptyPrefValueSet();
       found    = ReadPrefsFromSystem();
       ok(found.int          === expected.int,          "instant reset system int"    );
       ok(found.bool         === expected.bool,         "instant reset system bool"   );
       ok(found.string       === expected.string,       "instant reset system string" );
+      ok(found.unichar      === expected.unichar,      "instant reset system unichar");
       ok(found.wstring_data === expected.wstring_data, "instant reset system wstring");
-      ok(found.unichar_data === expected.unichar_data, "instant reset system unichar");
       ok(found.file_data    === expected.file_data,    "instant reset system file"   );
 
       // check UI
       expected =
       {
         // alas, we don't have XUL elements with typeof(value) == int :(
         // int:         0,
         int:          "",
         bool:         false,
         string:       "",
+        unichar:      "",
         wstring_data: "",
-        unichar_data: "",
         file_data:    "",
         wstring:      {},
-        unichar:      {},
         file:         {}
       };
       found = ReadPrefsFromUI(aPrefWindow);
       ok(found.int          === expected.int,          "instant reset element int"    );
       ok(found.bool         === expected.bool,         "instant reset element bool"   );
       ok(found.string       === expected.string,       "instant reset element string" );
+      ok(found.unichar      === expected.unichar,      "instant reset element unichar");
       ok(found.wstring_data === expected.wstring_data, "instant reset element wstring");
-      ok(found.unichar_data === expected.unichar_data, "instant reset element unichar");
 //      ok(found.file_data    === expected.file_data,    "instant reset element file"   );
 
       // check hasUserValue
       ok(GetXULElement(aPrefWindow, "tests.static_preference_int"    ).hasUserValue === false, "instant reset hasUserValue int"    );
       ok(GetXULElement(aPrefWindow, "tests.static_preference_bool"   ).hasUserValue === false, "instant reset hasUserValue bool"   );
       ok(GetXULElement(aPrefWindow, "tests.static_preference_string" ).hasUserValue === false, "instant reset hasUserValue string" );
+      ok(GetXULElement(aPrefWindow, "tests.static_preference_unichar").hasUserValue === false, "instant reset hasUserValue unichar");
       ok(GetXULElement(aPrefWindow, "tests.static_preference_wstring").hasUserValue === false, "instant reset hasUserValue wstring");
-      ok(GetXULElement(aPrefWindow, "tests.static_preference_unichar").hasUserValue === false, "instant reset hasUserValue unichar");
       ok(GetXULElement(aPrefWindow, "tests.static_preference_file"   ).hasUserValue === false, "instant reset hasUserValue file"   );
 
       // done with instant apply checks
     }
 
     function RunNonInstantPrefTestGeneral(aPrefWindow)
     {
       // Non-instant apply tests are harder: not only do we need to check that
@@ -320,103 +302,102 @@
       // remark: there's currently no UI element binding for files
 
       // were all <preferences> correctly initialized?
       var expected = kPrefValueSet1;
       var found    = ReadPrefsFromPreferences(aPrefWindow);
       ok(found.int          === expected.int,          "non-instant pref init int"    );
       ok(found.bool         === expected.bool,         "non-instant pref init bool"   );
       ok(found.string       === expected.string,       "non-instant pref init string" );
+      ok(found.unichar      === expected.unichar,      "non-instant pref init unichar");
       ok(found.wstring_data === expected.wstring_data, "non-instant pref init wstring");
-      ok(found.unichar_data === expected.unichar_data, "non-instant pref init unichar");
       todo(found.file_data  === expected.file_data,    "non-instant pref init file"   );
 
       // were all elements correctly initialized? (loose check)
       found = ReadPrefsFromUI(aPrefWindow);
       ok(found.int          == expected.int,          "non-instant element init int"    );
       ok(found.bool         == expected.bool,         "non-instant element init bool"   );
       ok(found.string       == expected.string,       "non-instant element init string" );
+      ok(found.unichar      == expected.unichar,      "non-instant element init unichar");
       ok(found.wstring_data == expected.wstring_data, "non-instant element init wstring");
-      ok(found.unichar_data == expected.unichar_data, "non-instant element init unichar");
       todo(found.file_data  == expected.file_data,    "non-instant element init file"   );
 
       // do some changes in the UI
       expected = kPrefValueSet2;
       WritePrefsToUI(aPrefWindow, expected);
 
       // UI changes should get passed to the <preference>s,
       // but currently they aren't if the changes are made programmatically
       // (the handlers preference.change/prefpane.input and prefpane.change
       //  are called for manual changes, though).
       found = ReadPrefsFromPreferences(aPrefWindow);
       todo(found.int          === expected.int,          "non-instant change pref int"    );
       todo(found.bool         === expected.bool,         "non-instant change pref bool"   );
       todo(found.string       === expected.string,       "non-instant change pref string" );
+      todo(found.unichar      === expected.unichar,      "non-instant change pref unichar");
       todo(found.wstring_data === expected.wstring_data, "non-instant change pref wstring");
-      todo(found.unichar_data === expected.unichar_data, "non-instant change pref unichar");
       todo(found.file_data    === expected.file_data,    "non-instant change pref file"   );
 
       // and these changes should *NOT* get passed to the system
       // (which obviously always passes with the above failing)
       expected = kPrefValueSet1;
       found    = ReadPrefsFromSystem();
       ok(found.int          === expected.int,          "non-instant change element int"    );
       ok(found.bool         === expected.bool,         "non-instant change element bool"   );
       ok(found.string       === expected.string,       "non-instant change element string" );
+      ok(found.unichar      === expected.unichar,      "non-instant change element unichar");
       ok(found.wstring_data === expected.wstring_data, "non-instant change element wstring");
-      ok(found.unichar_data === expected.unichar_data, "non-instant change element unichar");
       todo(found.file_data  === expected.file_data,    "non-instant change element file"   );
 
       // try resetting the prefs to default values (which should be empty here)
       GetXULElement(aPrefWindow, "tests.static_preference_int"    ).reset();
       GetXULElement(aPrefWindow, "tests.static_preference_bool"   ).reset();
       GetXULElement(aPrefWindow, "tests.static_preference_string" ).reset();
+      GetXULElement(aPrefWindow, "tests.static_preference_unichar").reset();
       GetXULElement(aPrefWindow, "tests.static_preference_wstring").reset();
-      GetXULElement(aPrefWindow, "tests.static_preference_unichar").reset();
       GetXULElement(aPrefWindow, "tests.static_preference_file"   ).reset();
 
       // check system: the current values *MUST NOT* change
       expected = kPrefValueSet1;
       found    = ReadPrefsFromSystem();
       ok(found.int          === expected.int,          "non-instant reset system int"    );
       ok(found.bool         === expected.bool,         "non-instant reset system bool"   );
       ok(found.string       === expected.string,       "non-instant reset system string" );
+      ok(found.unichar      === expected.unichar,      "non-instant reset system unichar");
       ok(found.wstring_data === expected.wstring_data, "non-instant reset system wstring");
-      ok(found.unichar_data === expected.unichar_data, "non-instant reset system unichar");
       todo(found.file_data  === expected.file_data,    "non-instant reset system file"   );
 
       // check UI: these values should be reset
       expected =
       {
         // alas, we don't have XUL elements with typeof(value) == int :(
         // int:         0,
         int:          "",
         bool:         false,
         string:       "",
+        unichar:      "",
         wstring_data: "",
-        unichar_data: "",
         file_data:    "",
         wstring:      {},
-        unichar:      {},
         file:         {}
       };
       found = ReadPrefsFromUI(aPrefWindow);
       ok(found.int          === expected.int,          "non-instant reset element int"    );
       ok(found.bool         === expected.bool,         "non-instant reset element bool"   );
       ok(found.string       === expected.string,       "non-instant reset element string" );
+      ok(found.unichar      === expected.unichar,      "non-instant reset element unichar");
       ok(found.wstring_data === expected.wstring_data, "non-instant reset element wstring");
-      ok(found.unichar_data === expected.unichar_data, "non-instant reset element unichar");
 //      ok(found.file_data    === expected.file_data,    "non-instant reset element file"   );
 
       // check hasUserValue
       ok(GetXULElement(aPrefWindow, "tests.static_preference_int"    ).hasUserValue === false, "non-instant reset hasUserValue int"    );
       ok(GetXULElement(aPrefWindow, "tests.static_preference_bool"   ).hasUserValue === false, "non-instant reset hasUserValue bool"   );
       ok(GetXULElement(aPrefWindow, "tests.static_preference_string" ).hasUserValue === false, "non-instant reset hasUserValue string" );
+      ok(GetXULElement(aPrefWindow, "tests.static_preference_unichar").hasUserValue === false, "non-instant reset hasUserValue unichar");
       ok(GetXULElement(aPrefWindow, "tests.static_preference_wstring").hasUserValue === false, "non-instant reset hasUserValue wstring");
-      ok(GetXULElement(aPrefWindow, "tests.static_preference_unichar").hasUserValue === false, "non-instant reset hasUserValue unichar");
       ok(GetXULElement(aPrefWindow, "tests.static_preference_file"   ).hasUserValue === false, "non-instant reset hasUserValue file"   );
     }
 
     function RunNonInstantPrefTestClose(aPrefWindow)
     {
       WritePrefsToPreferences(aPrefWindow, kPrefValueSet2);
     }
 
@@ -429,18 +410,18 @@
     }
 
     function RunResetPrefTest(aPrefWindow)
     {
       // try resetting the prefs to default values
       GetXULElement(aPrefWindow, "tests.static_preference_int"    ).reset();
       GetXULElement(aPrefWindow, "tests.static_preference_bool"   ).reset();
       GetXULElement(aPrefWindow, "tests.static_preference_string" ).reset();
+      GetXULElement(aPrefWindow, "tests.static_preference_unichar").reset();
       GetXULElement(aPrefWindow, "tests.static_preference_wstring").reset();
-      GetXULElement(aPrefWindow, "tests.static_preference_unichar").reset();
       GetXULElement(aPrefWindow, "tests.static_preference_file"   ).reset();
     }
 
     function InitTestPrefs(aInstantApply)
     {
       // set instant apply mode and init prefs to set 1
       kPref.setBoolPref("browser.preferences.instantApply", aInstantApply);
       WritePrefsToSystem(kPrefValueSet1);
@@ -455,18 +436,18 @@
       // - test deferred reset in child window
       InitTestPrefs(true);
       openDialog("window_preferences2.xul", "", "modal", RunResetPrefTest, false);
       expected = kPrefValueSet1;
       found    = ReadPrefsFromSystem();
       ok(found.int          === expected.int,          "instant reset deferred int"    );
       ok(found.bool         === expected.bool,         "instant reset deferred bool"   );
       ok(found.string       === expected.string,       "instant reset deferred string" );
+      ok(found.unichar      === expected.unichar,      "instant reset deferred unichar");
       ok(found.wstring_data === expected.wstring_data, "instant reset deferred wstring");
-      ok(found.unichar_data === expected.unichar_data, "instant reset deferred unichar");
       todo(found.file_data  === expected.file_data,    "instant reset deferred file"   );
     }
 
     function RunTestNonInstant()
     {
       // test without instantApply
       // - general tests, similar to instant apply
       InitTestPrefs(false);
@@ -475,42 +456,42 @@
       // - test Cancel
       InitTestPrefs(false);
       openDialog("window_preferences.xul", "", "modal", RunNonInstantPrefTestClose, false);
       var expected = kPrefValueSet1;
       var found    = ReadPrefsFromSystem();
       ok(found.int          === expected.int,          "non-instant cancel system int"    );
       ok(found.bool         === expected.bool,         "non-instant cancel system bool"   );
       ok(found.string       === expected.string,       "non-instant cancel system string" );
+      ok(found.unichar      === expected.unichar,      "non-instant cancel system unichar");
       ok(found.wstring_data === expected.wstring_data, "non-instant cancel system wstring");
-      ok(found.unichar_data === expected.unichar_data, "non-instant cancel system unichar");
       todo(found.file_data  === expected.file_data,    "non-instant cancel system file"   );
       
       // - test Accept
       InitTestPrefs(false);
       openDialog("window_preferences.xul", "", "modal", RunNonInstantPrefTestClose, true);
       expected = kPrefValueSet2;
       found    = ReadPrefsFromSystem();
       ok(found.int          === expected.int,          "non-instant accept system int"    );
       ok(found.bool         === expected.bool,         "non-instant accept system bool"   );
       ok(found.string       === expected.string,       "non-instant accept system string" );
+      ok(found.unichar      === expected.unichar,      "non-instant accept system unichar");
       ok(found.wstring_data === expected.wstring_data, "non-instant accept system wstring");
-      ok(found.unichar_data === expected.unichar_data, "non-instant accept system unichar");
       todo(found.file_data  === expected.file_data,    "non-instant accept system file"   );
 
       // - test deferred reset in child window
       InitTestPrefs(false);
       openDialog("window_preferences2.xul", "", "modal", RunResetPrefTest, true);
       expected = CreateEmptyPrefValueSet();
       found    = ReadPrefsFromSystem();
       ok(found.int          === expected.int,          "non-instant reset deferred int"    );
       ok(found.bool         === expected.bool,         "non-instant reset deferred bool"   );
       ok(found.string       === expected.string,       "non-instant reset deferred string" );
+      ok(found.unichar      === expected.unichar,      "non-instant reset deferred unichar");
       ok(found.wstring_data === expected.wstring_data, "non-instant reset deferred wstring");
-      ok(found.unichar_data === expected.unichar_data, "non-instant reset deferred unichar");
       ok(found.file_data    === expected.file_data,    "non-instant reset deferred file"   );
     }
 
     function RunTestCommandRedirect()
     {
       openDialog("window_preferences_commandretarget.xul", "", "modal", RunCheckCommandRedirect, true);
     }
 
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -201,36 +201,24 @@ static XP_CHAR* eventsDirectory;
 static char* eventsEnv = nullptr;
 
 // The current telemetry session ID to write to the event file
 static char* currentSessionId = nullptr;
 
 // If this is false, we don't launch the crash reporter
 static bool doReport = true;
 
-// If this is true, we don't have a crash reporter
-static bool headlessClient = false;
-
 // if this is true, we pass the exception on to the OS crash reporter
 static bool showOSCrashReporter = false;
 
 // The time of the last recorded crash, as a time_t value.
 static time_t lastCrashTime = 0;
 // The pathname of a file to store the crash time in
 static XP_CHAR lastCrashTimeFilename[XP_PATH_MAX] = {0};
 
-// A marker file to hold the path to the last dump written, which
-// will be checked on startup.
-static XP_CHAR crashMarkerFilename[XP_PATH_MAX] = {0};
-
-// Whether we've already looked for the marker file.
-static bool lastRunCrashID_checked = false;
-// The minidump ID contained in the marker file.
-static nsString* lastRunCrashID = nullptr;
-
 #if defined(MOZ_WIDGET_ANDROID)
 // on Android 4.2 and above there is a user serial number associated
 // with the current process that gets lost when we fork so we need to
 // explicitly pass it to am
 static char* androidUserSerial = nullptr;
 #endif
 
 // this holds additional data sent via the API
@@ -981,27 +969,16 @@ MinidumpCallback(
   if (memoryReportPath) {
 #ifdef XP_WIN
     CopyFile(memoryReportPath, memoryReportLocalPath, false);
 #else
     copy_file(memoryReportPath, memoryReportLocalPath);
 #endif
   }
 
-  if (headlessClient) {
-    // Leave a marker indicating that there was a crash.
-    PlatformWriter markerFile(crashMarkerFilename);
-#if defined(XP_WIN)
-    markerFile.WriteBuffer(reinterpret_cast<const char*>(minidumpPath),
-                           2*wcslen(minidumpPath));
-#elif defined(XP_UNIX)
-    markerFile.WriteBuffer(minidumpPath, my_strlen(minidumpPath));
-#endif
-  }
-
   char oomAllocationSizeBuffer[32] = "";
   if (gOOMAllocationSize) {
     XP_STOA(gOOMAllocationSize, oomAllocationSizeBuffer, 10);
   }
 
   char texturesSizeBuffer[32] = "";
   if (gTexturesSize) {
     XP_STOA(gTexturesSize, texturesSizeBuffer, 10);
@@ -1588,65 +1565,63 @@ nsresult SetExceptionHandler(nsIFile* aX
 
   crashReporterAPIData_Hash =
     new nsDataHashtable<nsCStringHashKey,nsCString>();
   NS_ENSURE_TRUE(crashReporterAPIData_Hash, NS_ERROR_OUT_OF_MEMORY);
 
   notesField = new nsCString();
   NS_ENSURE_TRUE(notesField, NS_ERROR_OUT_OF_MEMORY);
 
-  if (!headlessClient) {
 #if !defined(MOZ_WIDGET_ANDROID)
-    // Locate the crash reporter executable
-    nsAutoString crashReporterPath_temp;
-    nsresult rv = LocateExecutable(aXREDirectory,
-                                   NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME),
-                                   crashReporterPath_temp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
+  // Locate the crash reporter executable
+  nsAutoString crashReporterPath_temp;
+  nsresult rv = LocateExecutable(aXREDirectory,
+                                 NS_LITERAL_CSTRING(CRASH_REPORTER_FILENAME),
+                                 crashReporterPath_temp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
 #ifdef XP_MACOSX
-    nsCOMPtr<nsIFile> libPath;
-    rv = aXREDirectory->Clone(getter_AddRefs(libPath));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-    }
-
-    nsAutoString libraryPath_temp;
-    rv = libPath->GetPath(libraryPath_temp);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-    }
+  nsCOMPtr<nsIFile> libPath;
+  rv = aXREDirectory->Clone(getter_AddRefs(libPath));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+  }
+
+  nsAutoString libraryPath_temp;
+  rv = libPath->GetPath(libraryPath_temp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+  }
 #endif // XP_MACOSX
 
 #ifdef XP_WIN32
   crashReporterPath =
     reinterpret_cast<wchar_t*>(ToNewUnicode(crashReporterPath_temp));
 #else
   crashReporterPath = ToNewCString(crashReporterPath_temp);
 #ifdef XP_MACOSX
   libraryPath = ToNewCString(libraryPath_temp);
 #endif
 #endif // XP_WIN32
 #else
-    // On Android, we launch using the application package name instead of a
-    // filename, so use the dynamically set MOZ_ANDROID_PACKAGE_NAME, or fall
-    // back to the static ANDROID_PACKAGE_NAME.
-    const char* androidPackageName = PR_GetEnv("MOZ_ANDROID_PACKAGE_NAME");
-    if (androidPackageName != nullptr) {
-      nsCString package(androidPackageName);
-      package.AppendLiteral("/org.mozilla.gecko.CrashReporter");
-      crashReporterPath = ToNewCString(package);
-    } else {
-      nsCString package(ANDROID_PACKAGE_NAME "/org.mozilla.gecko.CrashReporter");
-      crashReporterPath = ToNewCString(package);
-    }
+  // On Android, we launch using the application package name instead of a
+  // filename, so use the dynamically set MOZ_ANDROID_PACKAGE_NAME, or fall
+  // back to the static ANDROID_PACKAGE_NAME.
+  const char* androidPackageName = PR_GetEnv("MOZ_ANDROID_PACKAGE_NAME");
+  if (androidPackageName != nullptr) {
+    nsCString package(androidPackageName);
+    package.AppendLiteral("/org.mozilla.gecko.CrashReporter");
+    crashReporterPath = ToNewCString(package);
+  } else {
+    nsCString package(ANDROID_PACKAGE_NAME "/org.mozilla.gecko.CrashReporter");
+    crashReporterPath = ToNewCString(package);
+  }
 #endif // !defined(MOZ_WIDGET_ANDROID)
-  }
 
   // get temp path to use for minidump path
 #if defined(XP_WIN32)
   nsString tempPath;
 #else
   nsCString tempPath;
 #endif
   if (!BuildTempPath(tempPath)) {
@@ -2036,44 +2011,16 @@ nsresult SetupExtraData(nsIFile* aAppDat
   nsAutoCString filename;
   rv = lastCrashFile->GetNativePath(filename);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (filename.Length() < XP_PATH_MAX)
     strncpy(lastCrashTimeFilename, filename.get(), filename.Length());
 #endif
 
-  if (headlessClient) {
-    nsCOMPtr<nsIFile> markerFile;
-    rv = dataDirectory->Clone(getter_AddRefs(markerFile));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = markerFile->AppendNative(NS_LITERAL_CSTRING("LastCrashFilename"));
-    NS_ENSURE_SUCCESS(rv, rv);
-    memset(crashMarkerFilename, 0, sizeof(crashMarkerFilename));
-
-#if defined(XP_WIN32)
-    nsAutoString markerFilename;
-    rv = markerFile->GetPath(markerFilename);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (markerFilename.Length() < XP_PATH_MAX)
-      wcsncpy(crashMarkerFilename, markerFilename.get(),
-              markerFilename.Length());
-#else
-    nsAutoCString markerFilename;
-    rv = markerFile->GetNativePath(markerFilename);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (markerFilename.Length() < XP_PATH_MAX)
-      strncpy(crashMarkerFilename, markerFilename.get(),
-              markerFilename.Length());
-#endif
-  }
-
   return NS_OK;
 }
 
 static void OOPDeinit();
 
 nsresult UnsetExceptionHandler()
 {
   if (isSafeToDump) {
@@ -2103,19 +2050,16 @@ nsresult UnsetExceptionHandler()
   crashReporterAPIData = nullptr;
 
   delete crashEventAPIData;
   crashEventAPIData = nullptr;
 
   delete notesField;
   notesField = nullptr;
 
-  delete lastRunCrashID;
-  lastRunCrashID = nullptr;
-
   if (pendingDirectory) {
     free(pendingDirectory);
     pendingDirectory = nullptr;
   }
 
   if (crashReporterPath) {
     free(crashReporterPath);
     crashReporterPath = nullptr;
@@ -3617,93 +3561,16 @@ UnregisterInjectorCallback(DWORD process
     return;
 
   MutexAutoLock lock(*dumpMapLock);
   pidToMinidump->RemoveEntry(processID);
 }
 
 #endif // MOZ_CRASHREPORTER_INJECTOR
 
-static bool
-CheckForLastRunCrash()
-{
-  if (lastRunCrashID)
-    return true;
-
-  // The exception handler callback leaves the filename of the
-  // last minidump in a known file.
-  nsCOMPtr<nsIFile> lastCrashFile;
-  CreateFileFromPath(crashMarkerFilename,
-                     getter_AddRefs(lastCrashFile));
-
-  bool exists;
-  if (NS_FAILED(lastCrashFile->Exists(&exists)) || !exists) {
-    return false;
-  }
-
-  nsAutoCString lastMinidump_contents;
-  if (NS_FAILED(GetFileContents(lastCrashFile, lastMinidump_contents))) {
-    return false;
-  }
-  lastCrashFile->Remove(false);
-
-#ifdef XP_WIN
-  // Ugly but effective.
-  nsDependentString lastMinidump(
-      reinterpret_cast<const char16_t*>(lastMinidump_contents.get()));
-#else
-  nsAutoCString lastMinidump = lastMinidump_contents;
-#endif
-  nsCOMPtr<nsIFile> lastMinidumpFile;
-  CreateFileFromPath(lastMinidump.get(),
-                     getter_AddRefs(lastMinidumpFile));
-
-  if (!lastMinidumpFile || NS_FAILED(lastMinidumpFile->Exists(&exists)) || !exists) {
-    return false;
-  }
-
-  nsCOMPtr<nsIFile> lastExtraFile;
-  if (!GetExtraFileForMinidump(lastMinidumpFile,
-                               getter_AddRefs(lastExtraFile))) {
-    return false;
-  }
-
-  nsCOMPtr<nsIFile> memoryReportFile;
-  nsresult rv = GetDefaultMemoryReportFile(getter_AddRefs(memoryReportFile));
-  if (NS_FAILED(rv) || NS_FAILED(memoryReportFile->Exists(&exists)) || !exists) {
-    memoryReportFile = nullptr;
-  }
-
-  FindPendingDir();
-
-  // Move {dump,extra,memory} to pending folder
-  if (!MoveToPending(lastMinidumpFile, lastExtraFile, memoryReportFile)) {
-    return false;
-  }
-
-  lastRunCrashID = new nsString();
-  return GetIDFromMinidump(lastMinidumpFile, *lastRunCrashID);
-}
-
-bool
-GetLastRunCrashID(nsAString& id)
-{
-  if (!lastRunCrashID_checked) {
-    CheckForLastRunCrash();
-    lastRunCrashID_checked = true;
-  }
-
-  if (!lastRunCrashID) {
-    return false;
-  }
-
-  id = *lastRunCrashID;
-  return true;
-}
-
 #if defined(XP_WIN) || defined(XP_MACOSX)
 void
 InitChildProcessTmpDir(nsIFile* aDirOverride)
 {
   MOZ_ASSERT(!XRE_IsParentProcess());
   if (aDirOverride) {
     childProcessTmpDir = CreatePathFromFile(aDirOverride);
     return;
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -80,18 +80,16 @@ void AnnotateOOMAllocationSize(size_t si
 void AnnotateTexturesSize(size_t size);
 nsresult SetGarbageCollecting(bool collecting);
 void SetEventloopNestingLevel(uint32_t level);
 void SetMinidumpAnalysisAllThreads();
 
 nsresult SetRestartArgs(int argc, char** argv);
 nsresult SetupExtraData(nsIFile* aAppDataDirectory,
                         const nsACString& aBuildID);
-bool GetLastRunCrashID(nsAString& id);
-
 // Registers an additional memory region to be included in the minidump
 nsresult RegisterAppMemory(void* ptr, size_t length);
 nsresult UnregisterAppMemory(void* ptr);
 
 // Include heap regions of the crash context.
 void SetIncludeContextHeap(bool aValue);
 
 // Functions for working with minidumps and .extras
--- a/toolkit/library/moz.build
+++ b/toolkit/library/moz.build
@@ -205,16 +205,22 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         'winspool',
     ]
 
 if CONFIG['OS_ARCH'] == 'Linux' and CONFIG['OS_TARGET'] != 'Android':
     OS_LIBS += [
         'rt',
     ]
 
+if CONFIG['MOZ_ANDROID_GOOGLE_VR']:
+    OS_LIBS += [
+        '-L%s' % CONFIG['MOZ_ANDROID_GOOGLE_VR_LIBS'],
+        '-lgvr',
+    ]
+
 OS_LIBS += CONFIG['MOZ_CAIRO_OSLIBS']
 OS_LIBS += CONFIG['MOZ_WEBRTC_X11_LIBS']
 
 if CONFIG['SERVO_TARGET_DIR']:
     if CONFIG['_MSC_VER']:
         OS_LIBS += ['%s/geckoservo' % CONFIG['SERVO_TARGET_DIR']]
     else:
         OS_LIBS += ['-L%s' % CONFIG['SERVO_TARGET_DIR'], '-lgeckoservo']
--- a/toolkit/modules/Preferences.jsm
+++ b/toolkit/modules/Preferences.jsm
@@ -42,27 +42,33 @@ this.Preferences =
  * @param   defaultValue
  *          the default value, if any, for prefs that don't have one
  *
  * @param   valueType
  *          the XPCOM interface of the pref's complex value type, if any
  *
  * @returns the value of the pref, if any; otherwise the default value
  */
-Preferences.get = function(prefName, defaultValue, valueType = Ci.nsISupportsString) {
+Preferences.get = function(prefName, defaultValue, valueType = null) {
   if (Array.isArray(prefName))
     return prefName.map(v => this.get(v, defaultValue));
 
   return this._get(prefName, defaultValue, valueType);
 };
 
 Preferences._get = function(prefName, defaultValue, valueType) {
   switch (this._prefBranch.getPrefType(prefName)) {
     case Ci.nsIPrefBranch.PREF_STRING:
-      return this._prefBranch.getComplexValue(prefName, valueType).data;
+      if (valueType) {
+        let ifaces = ["nsIFile", "nsIPrefLocalizedString"];
+        if (ifaces.includes(valueType.name)) {
+          return this._prefBranch.getComplexValue(prefName, valueType).data;
+        }
+      }
+      return this._prefBranch.getStringPref(prefName);
 
     case Ci.nsIPrefBranch.PREF_INT:
       return this._prefBranch.getIntPref(prefName);
 
     case Ci.nsIPrefBranch.PREF_BOOL:
       return this._prefBranch.getBoolPref(prefName);
 
     case Ci.nsIPrefBranch.PREF_INVALID:
--- a/toolkit/modules/Troubleshoot.jsm
+++ b/toolkit/modules/Troubleshoot.jsm
@@ -99,17 +99,16 @@ const PREFS_WHITELIST = [
 // The blacklist, unlike the whitelist, is a list of regular expressions.
 const PREFS_BLACKLIST = [
   /^network[.]proxy[.]/,
   /[.]print_to_filename$/,
   /^print[.]macosx[.]pagesetup/,
 ];
 
 // Table of getters for various preference types.
-// It's important to use getComplexValue for strings: it returns Unicode (wchars), getCharPref returns UTF-8 encoded chars.
 const PREFS_GETTERS = {};
 
 PREFS_GETTERS[Ci.nsIPrefBranch.PREF_STRING] = (prefs, name) => prefs.getStringPref(name);
 PREFS_GETTERS[Ci.nsIPrefBranch.PREF_INT] = (prefs, name) => prefs.getIntPref(name);
 PREFS_GETTERS[Ci.nsIPrefBranch.PREF_BOOL] = (prefs, name) => prefs.getBoolPref(name);
 
 // Return the preferences filtered by PREFS_BLACKLIST and PREFS_WHITELIST lists
 // and also by the custom 'filter'-ing function.
--- a/toolkit/modules/tests/xpcshell/test_Preferences.js
+++ b/toolkit/modules/tests/xpcshell/test_Preferences.js
@@ -107,19 +107,17 @@ add_test(function test_set_unsupported_p
     Preferences.set("test_set_unsupported_pref", []);
     // We expect this to throw, so the test is designed to fail if it doesn't.
     do_check_true(false);
   } catch (ex) {}
 
   run_next_test();
 });
 
-// Make sure that we can get a string pref that we didn't set ourselves
-// (i.e. that the way we get a string pref using getComplexValue doesn't
-// hork us getting a string pref that wasn't set using setComplexValue).
+// Make sure that we can get a string pref that we didn't set ourselves.
 add_test(function test_get_string_pref() {
   let svc = Cc["@mozilla.org/preferences-service;1"].
             getService(Ci.nsIPrefService).
             getBranch("");
   svc.setCharPref("test_get_string_pref", "a normal string");
   do_check_eq(Preferences.get("test_get_string_pref"), "a normal string");
 
   // Clean up.
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1105,27 +1105,16 @@ nsXULAppInfo::GetReplacedLockTime(PRTime
 {
   if (!gProfileLock)
     return NS_ERROR_NOT_AVAILABLE;
   gProfileLock->GetReplacedLockTime(aReplacedLockTime);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsXULAppInfo::GetLastRunCrashID(nsAString &aLastRunCrashID)
-{
-#ifdef MOZ_CRASHREPORTER
-  CrashReporter::GetLastRunCrashID(aLastRunCrashID);
-  return NS_OK;
-#else
-  return NS_ERROR_NOT_IMPLEMENTED;
-#endif
-}
-
-NS_IMETHODIMP
 nsXULAppInfo::GetIsReleaseOrBeta(bool* aResult)
 {
 #ifdef RELEASE_OR_BETA
   *aResult = true;
 #else
   *aResult = false;
 #endif
   return NS_OK;
--- a/tools/lint/docs/linters/eslint-plugin-mozilla.rst
+++ b/tools/lint/docs/linters/eslint-plugin-mozilla.rst
@@ -54,21 +54,16 @@ Often timing relative to the page load i
 be necessary.
 
 avoid-removeChild
 -----------------
 
 Rejects using element.parentNode.removeChild(element) when element.remove()
 can be used instead.
 
-avoid-nsISupportsString-preferences
------------------------------------
-
-Rejects using getComplexValue and setComplexValue with nsISupportsString.
-
 balanced-listeners
 ------------------
 
 Checks that for every occurence of 'addEventListener' or 'on' there is an
 occurence of 'removeEventListener' or 'off' with the same event name.
 
 import-browser-window-globals
 -----------------------------
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
@@ -132,17 +132,16 @@ module.exports = {
 
     // Don't enforce the maximum depth that blocks can be nested. The complexity
     // rule is a better rule to check this.
     "max-depth": "off",
 
     // Maximum depth callbacks can be nested.
     "max-nested-callbacks": ["error", 10],
 
-    "mozilla/avoid-nsISupportsString-preferences": "error",
     "mozilla/avoid-removeChild": "error",
     "mozilla/import-browser-window-globals": "error",
     "mozilla/import-globals": "error",
     "mozilla/no-import-into-var-and-global": "error",
     "mozilla/no-useless-parameters": "error",
     "mozilla/no-useless-removeEventListener": "error",
     "mozilla/use-default-preference-values": "error",
     "mozilla/use-ownerGlobal": "error",
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
@@ -27,18 +27,16 @@ module.exports = {
     "simpletest": require("../lib/environments/simpletest.js")
   },
   processors: {
     ".xml": require("../lib/processors/xbl-bindings")
   },
   rules: {
     "avoid-Date-timing": require("../lib/rules/avoid-Date-timing"),
     "avoid-removeChild": require("../lib/rules/avoid-removeChild"),
-    "avoid-nsISupportsString-preferences":
-      require("../lib/rules/avoid-nsISupportsString-preferences"),
     "balanced-listeners": require("../lib/rules/balanced-listeners"),
     "import-browser-window-globals":
       require("../lib/rules/import-browser-window-globals"),
     "import-content-task-globals":
       require("../lib/rules/import-content-task-globals"),
     "import-globals": require("../lib/rules/import-globals"),
     "import-headjs-globals": require("../lib/rules/import-headjs-globals"),
     "mark-test-function-used": require("../lib/rules/mark-test-function-used"),
@@ -61,17 +59,16 @@ module.exports = {
       require("../lib/rules/use-default-preference-values"),
     "use-ownerGlobal": require("../lib/rules/use-ownerGlobal"),
     "use-services": require("../lib/rules/use-services"),
     "var-only-at-top-level": require("../lib/rules/var-only-at-top-level")
   },
   rulesConfig: {
     "avoid-Date-timing": "off",
     "avoid-removeChild": "off",
-    "avoid-nsISupportsString-preferences": "off",
     "balanced-listeners": "off",
     "import-browser-window-globals": "off",
     "import-content-task-globals": "off",
     "import-globals": "off",
     "import-headjs-globals": "off",
     "mark-test-function-used": "off",
     "no-aArgs": "off",
     "no-arbitrary-setTimeout": "off",
deleted file mode 100644
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/avoid-nsISupportsString-preferences.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * @fileoverview Rejects using getComplexValue and setComplexValue with
- *               nsISupportsString.
- *
- * 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";
-
-// -----------------------------------------------------------------------------
-// Rule Definition
-// -----------------------------------------------------------------------------
-
-function isNsISupportsString(arg) {
-  let isNsISupportsStringIdentifier = obj =>
-    obj.type == "Identifier" && obj.name == "nsISupportsString";
-  return isNsISupportsStringIdentifier(arg) ||
-         (arg.type == "MemberExpression" &&
-          isNsISupportsStringIdentifier(arg.property));
-}
-
-module.exports = function(context) {
-
-  // ---------------------------------------------------------------------------
-  // Public
-  //  --------------------------------------------------------------------------
-
-  return {
-    "CallExpression": function(node) {
-      let callee = node.callee;
-      if (callee.type !== "MemberExpression" ||
-          callee.property.type !== "Identifier" ||
-          node.arguments.length < 2 ||
-          !isNsISupportsString(node.arguments[1])) {
-        return;
-      }
-
-      if (callee.property.name == "getComplexValue") {
-        context.report(node, "use getStringPref instead of " +
-                             "getComplexValue with nsISupportsString");
-      } else if (callee.property.name == "setComplexValue") {
-        context.report(node, "use setStringPref instead of " +
-                             "setComplexValue with nsISupportsString");
-      }
-    }
-  };
-};
--- a/tools/lint/eslint/eslint-plugin-mozilla/package.json
+++ b/tools/lint/eslint/eslint-plugin-mozilla/package.json
@@ -1,11 +1,11 @@
 {
   "name": "eslint-plugin-mozilla",
-  "version": "0.4.6",
+  "version": "0.4.7",
   "description": "A collection of rules that help enforce JavaScript coding standard in the Mozilla project.",
   "keywords": [
     "eslint",
     "eslintplugin",
     "eslint-plugin",
     "mozilla",
     "firefox"
   ],
deleted file mode 100644
--- a/tools/lint/eslint/eslint-plugin-mozilla/tests/avoid-nsISupportsString-preferences.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// ------------------------------------------------------------------------------
-// Requirements
-// ------------------------------------------------------------------------------
-
-var rule = require("../lib/rules/avoid-nsISupportsString-preferences");
-var RuleTester = require("eslint/lib/testers/rule-tester");
-
-const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
-
-// ------------------------------------------------------------------------------
-// Tests
-// ------------------------------------------------------------------------------
-
-function invalidCode(code, accessType = "get") {
-  let message = "use " + accessType + "StringPref instead of " +
-                accessType + "ComplexValue with nsISupportsString";
-  return {code, errors: [{message, type: "CallExpression"}]};
-}
-
-ruleTester.run("avoid-nsISupportsString-preferences", rule, {
-  valid: [
-    "branch.getStringPref('name');",
-    "branch.getComplexValue('name', Ci.nsIPrefLocalizedString);",
-    "branch.setStringPref('name', 'blah');",
-    "branch.setComplexValue('name', Ci.nsIPrefLocalizedString, pref);"
-  ],
-  invalid: [
-    invalidCode("branch.getComplexValue('name', Ci.nsISupportsString);"),
-    invalidCode("branch.getComplexValue('name', nsISupportsString);"),
-    invalidCode("branch.getComplexValue('name', Ci.nsISupportsString).data;"),
-    invalidCode("branch.setComplexValue('name', Ci.nsISupportsString, str);",
-                "set"),
-    invalidCode("branch.setComplexValue('name', nsISupportsString, str);",
-                "set")
-  ]
-});
new file mode 100644
--- /dev/null
+++ b/widget/android/GeckoVRManager.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_GeckoVRManager_h_
+#define mozilla_GeckoVRManager_h_
+
+#include "GeneratedJNINatives.h"
+#include "mozilla/jni/Utils.h"
+
+#if defined(MOZ_ANDROID_GOOGLE_VR)
+#include "gfxVRGVRAPI.h"
+extern bool SetupGVRJNI(JNIEnv* env);
+#endif // defined(MOZ_ANDROID_GOOGLE_VR)
+
+namespace mozilla {
+
+class GeckoVRManager
+    : public mozilla::java::GeckoVRManager::Natives<mozilla::GeckoVRManager>
+{
+  typedef mozilla::java::GeckoVRManager::Natives<mozilla::GeckoVRManager> Super;
+public:
+  static void Init()
+  {
+    Super::Init();
+#if defined(MOZ_ANDROID_GOOGLE_VR)
+    SetupGVRJNI(jni::GetGeckoThreadEnv());
+#endif // defined(MOZ_ANDROID_GOOGLE_VR)
+  }
+
+  static void SetGVRPresentingContext(const int64_t aContext)
+  {
+#if defined(MOZ_ANDROID_GOOGLE_VR)
+    mozilla::gfx::SetGVRPresentingContext((void*)aContext);
+#endif // defined(MOZ_ANDROID_GOOGLE_VR)
+  }
+
+  static void CleanupGVRNonPresentingContext()
+  {
+#if defined(MOZ_ANDROID_GOOGLE_VR)
+    mozilla::gfx::CleanupGVRNonPresentingContext();
+#endif // defined(MOZ_ANDROID_GOOGLE_VR)
+  }
+
+  static void SetGVRPaused(const bool aPaused)
+  {
+#if defined(MOZ_ANDROID_GOOGLE_VR)
+    mozilla::gfx::SetGVRPaused(aPaused);
+#endif // defined(MOZ_ANDROID_GOOGLE_VR)
+  }
+
+  static void* CreateGVRNonPresentingContext()
+  {
+    return (void*)mozilla::java::GeckoVRManager::CreateGVRNonPresentingContext();
+  }
+
+  static void DestroyGVRNonPresentingContext()
+  {
+    mozilla::java::GeckoVRManager::DestroyGVRNonPresentingContext();
+  }
+
+  static void EnableVRMode()
+  {
+    mozilla::java::GeckoVRManager::EnableVRMode();
+  }
+
+  static void DisableVRMode()
+  {
+    mozilla::java::GeckoVRManager::DisableVRMode();
+  }
+
+  static bool IsGVRPresent()
+  {
+    return mozilla::java::GeckoVRManager::IsGVRPresent();
+  }
+
+};
+
+} // namespace mozilla
+
+#endif // mozilla_GeckoVRManager_h_
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -284,16 +284,39 @@ const JNINativeMethod GeckoThread::Nativ
             ::template Wrap<&Impl::SpeculativeConnect>),
 
     mozilla::jni::MakeNativeMethod<GeckoThread::WaitOnGecko_t>(
             mozilla::jni::NativeStub<GeckoThread::WaitOnGecko_t, Impl>
             ::template Wrap<&Impl::WaitOnGecko>)
 };
 
 template<class Impl>
+class GeckoVRManager::Natives : public mozilla::jni::NativeImpl<GeckoVRManager, Impl>
+{
+public:
+    static const JNINativeMethod methods[3];
+};
+
+template<class Impl>
+const JNINativeMethod GeckoVRManager::Natives<Impl>::methods[] = {
+
+    mozilla::jni::MakeNativeMethod<GeckoVRManager::CleanupGVRNonPresentingContext_t>(
+            mozilla::jni::NativeStub<GeckoVRManager::CleanupGVRNonPresentingContext_t, Impl>
+            ::template Wrap<&Impl::CleanupGVRNonPresentingContext>),
+
+    mozilla::jni::MakeNativeMethod<GeckoVRManager::SetGVRPaused_t>(
+            mozilla::jni::NativeStub<GeckoVRManager::SetGVRPaused_t, Impl>
+            ::template Wrap<&Impl::SetGVRPaused>),
+
+    mozilla::jni::MakeNativeMethod<GeckoVRManager::SetGVRPresentingContext_t>(
+            mozilla::jni::NativeStub<GeckoVRManager::SetGVRPresentingContext_t, Impl>
+            ::template Wrap<&Impl::SetGVRPresentingContext>)
+};
+
+template<class Impl>
 class PrefsHelper::Natives : public mozilla::jni::NativeImpl<PrefsHelper, Impl>
 {
 public:
     static const JNINativeMethod methods[4];
 };
 
 template<class Impl>
 const JNINativeMethod PrefsHelper::Natives<Impl>::methods[] = {
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -982,16 +982,68 @@ auto GeckoThread::State::RESTARTING() ->
 constexpr char GeckoThread::State::RUNNING_t::name[];
 constexpr char GeckoThread::State::RUNNING_t::signature[];
 
 auto GeckoThread::State::RUNNING() -> State::LocalRef
 {
     return mozilla::jni::Field<RUNNING_t>::Get(State::Context(), nullptr);
 }
 
+const char GeckoVRManager::name[] =
+        "org/mozilla/gecko/GeckoVRManager";
+
+constexpr char GeckoVRManager::CleanupGVRNonPresentingContext_t::name[];
+constexpr char GeckoVRManager::CleanupGVRNonPresentingContext_t::signature[];
+
+constexpr char GeckoVRManager::CreateGVRNonPresentingContext_t::name[];
+constexpr char GeckoVRManager::CreateGVRNonPresentingContext_t::signature[];
+
+auto GeckoVRManager::CreateGVRNonPresentingContext() -> int64_t
+{
+    return mozilla::jni::Method<CreateGVRNonPresentingContext_t>::Call(GeckoVRManager::Context(), nullptr);
+}
+
+constexpr char GeckoVRManager::DestroyGVRNonPresentingContext_t::name[];
+constexpr char GeckoVRManager::DestroyGVRNonPresentingContext_t::signature[];
+
+auto GeckoVRManager::DestroyGVRNonPresentingContext() -> void
+{
+    return mozilla::jni::Method<DestroyGVRNonPresentingContext_t>::Call(GeckoVRManager::Context(), nullptr);
+}
+
+constexpr char GeckoVRManager::DisableVRMode_t::name[];
+constexpr char GeckoVRManager::DisableVRMode_t::signature[];
+
+auto GeckoVRManager::DisableVRMode() -> void
+{
+    return mozilla::jni::Method<DisableVRMode_t>::Call(GeckoVRManager::Context(), nullptr);
+}
+
+constexpr char GeckoVRManager::EnableVRMode_t::name[];
+constexpr char GeckoVRManager::EnableVRMode_t::signature[];
+
+auto GeckoVRManager::EnableVRMode() -> void
+{
+    return mozilla::jni::Method<EnableVRMode_t>::Call(GeckoVRManager::Context(), nullptr);
+}
+
+constexpr char GeckoVRManager::IsGVRPresent_t::name[];
+constexpr char GeckoVRManager::IsGVRPresent_t::signature[];
+
+auto GeckoVRManager::IsGVRPresent() -> bool
+{
+    return mozilla::jni::Method<IsGVRPresent_t>::Call(GeckoVRManager::Context(), nullptr);
+}
+
+constexpr char GeckoVRManager::SetGVRPaused_t::name[];
+constexpr char GeckoVRManager::SetGVRPaused_t::signature[];
+
+constexpr char GeckoVRManager::SetGVRPresentingContext_t::name[];
+constexpr char GeckoVRManager::SetGVRPresentingContext_t::signature[];
+
 const char PrefsHelper::name[] =
         "org/mozilla/gecko/PrefsHelper";
 
 constexpr char PrefsHelper::CallPrefHandler_t::name[];
 constexpr char PrefsHelper::CallPrefHandler_t::signature[];
 
 auto PrefsHelper::CallPrefHandler(mozilla::jni::Object::Param a0, int32_t a1, mozilla::jni::String::Param a2, bool a3, int32_t a4, mozilla::jni::String::Param a5) -> void
 {
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -2983,16 +2983,177 @@ public:
 
     static auto RUNNING() -> State::LocalRef;
 
     static const mozilla::jni::CallingThread callingThread =
             mozilla::jni::CallingThread::ANY;
 
 };
 
+class GeckoVRManager : public mozilla::jni::ObjectBase<GeckoVRManager>
+{
+public:
+    static const char name[];
+
+    explicit GeckoVRManager(const Context& ctx) : ObjectBase<GeckoVRManager>(ctx) {}
+
+    struct CleanupGVRNonPresentingContext_t {
+        typedef GeckoVRManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "cleanupGVRNonPresentingContext";
+        static constexpr char signature[] =
+                "()V";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::UI;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    struct CreateGVRNonPresentingContext_t {
+        typedef GeckoVRManager Owner;
+        typedef int64_t ReturnType;
+        typedef int64_t SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "createGVRNonPresentingContext";
+        static constexpr char signature[] =
+                "()J";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto CreateGVRNonPresentingContext() -> int64_t;
+
+    struct DestroyGVRNonPresentingContext_t {
+        typedef GeckoVRManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "destroyGVRNonPresentingContext";
+        static constexpr char signature[] =
+                "()V";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto DestroyGVRNonPresentingContext() -> void;
+
+    struct DisableVRMode_t {
+        typedef GeckoVRManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "disableVRMode";
+        static constexpr char signature[] =
+                "()V";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto DisableVRMode() -> void;
+
+    struct EnableVRMode_t {
+        typedef GeckoVRManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "enableVRMode";
+        static constexpr char signature[] =
+                "()V";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto EnableVRMode() -> void;
+
+    struct IsGVRPresent_t {
+        typedef GeckoVRManager Owner;
+        typedef bool ReturnType;
+        typedef bool SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "isGVRPresent";
+        static constexpr char signature[] =
+                "()Z";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::ANY;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static auto IsGVRPresent() -> bool;
+
+    struct SetGVRPaused_t {
+        typedef GeckoVRManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                bool> Args;
+        static constexpr char name[] = "setGVRPaused";
+        static constexpr char signature[] =
+                "(Z)V";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::UI;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    struct SetGVRPresentingContext_t {
+        typedef GeckoVRManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int64_t> Args;
+        static constexpr char name[] = "setGVRPresentingContext";
+        static constexpr char signature[] =
+                "(J)V";
+        static const bool isStatic = true;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+        static const mozilla::jni::CallingThread callingThread =
+                mozilla::jni::CallingThread::UI;
+        static const mozilla::jni::DispatchTarget dispatchTarget =
+                mozilla::jni::DispatchTarget::CURRENT;
+    };
+
+    static const mozilla::jni::CallingThread callingThread =
+            mozilla::jni::CallingThread::ANY;
+
+    template<class Impl> class Natives;
+};
+
 class PrefsHelper : public mozilla::jni::ObjectBase<PrefsHelper>
 {
 public:
     static const char name[];
 
     explicit PrefsHelper(const Context& ctx) : ObjectBase<PrefsHelper>(ctx) {}
 
     struct CallPrefHandler_t {
--- a/widget/android/moz.build
+++ b/widget/android/moz.build
@@ -61,16 +61,17 @@ include('/ipc/chromium/chromium-config.m
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/docshell/base',
     '/dom/base',
     '/dom/system/android',
     '/gfx/2d',
+    '/gfx/vr',
     '/layout/painting',
     '/netwerk/base',
     '/netwerk/cache',
     '/widget',
     '/xpcom/threads',
 ]
 
 CXXFLAGS += ['-Wno-error=shadow']
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -64,16 +64,17 @@
 
 #include "AndroidAlerts.h"
 #include "AndroidUiThread.h"
 #include "ANRReporter.h"
 #include "GeckoBatteryManager.h"
 #include "GeckoNetworkManager.h"
 #include "GeckoProcessManager.h"
 #include "GeckoScreenOrientation.h"
+#include "GeckoVRManager.h"
 #include "PrefsHelper.h"
 #include "fennec/MemoryMonitor.h"
 #include "fennec/Telemetry.h"
 #include "fennec/ThumbnailHelper.h"
 
 #ifdef DEBUG_ANDROID_EVENTS
 #define EVLOG(args...)  ALOG(args)
 #else
@@ -402,16 +403,17 @@ nsAppShell::nsAppShell()
         AndroidBridge::ConstructBridge();
         GeckoAppShellSupport::Init();
         GeckoThreadSupport::Init();
         mozilla::GeckoBatteryManager::Init();
         mozilla::GeckoNetworkManager::Init();
         mozilla::GeckoProcessManager::Init();
         mozilla::GeckoScreenOrientation::Init();
         mozilla::PrefsHelper::Init();
+        mozilla::GeckoVRManager::Init();
         nsWindow::InitNatives();
 
         if (jni::IsFennec()) {
             mozilla::ANRReporter::Init();
             mozilla::MemoryMonitor::Init();
             mozilla::widget::Telemetry::Init();
             mozilla::ThumbnailHelper::Init();
         }
--- a/xpcom/base/AvailableMemoryTracker.cpp
+++ b/xpcom/base/AvailableMemoryTracker.cpp
@@ -32,21 +32,31 @@
 #endif  // MOZ_MEMORY
 
 using namespace mozilla;
 
 namespace {
 
 #if defined(_M_IX86) && defined(XP_WIN)
 
+// Fire a low-memory notification if we have less than this many MiB of virtual
+// address space available.
+static const uint32_t kLowVirtualMemoryThresholdMiB = 256;
 
-uint32_t sLowVirtualMemoryThreshold = 0;
-uint32_t sLowCommitSpaceThreshold = 0;
-uint32_t sLowPhysicalMemoryThreshold = 0;
-uint32_t sLowMemoryNotificationIntervalMS = 0;
+// Fire a low-memory notification if we have less than this many MiB of commit
+// space (physical memory plus page file) left.
+static const uint32_t kLowCommitSpaceThresholdMiB = 256;
+
+// Fire a low-memory notification if we have less than this many MiB of
+// physical memory available on the whole machine.
+static const uint32_t kLowPhysicalMemoryThresholdMiB = 0;
+
+// Don't fire a low-memory notification because of low available physical
+// memory or low commit space more often than this interval.
+static const uint32_t kLowMemoryNotificationIntervalMS = 10000;
 
 Atomic<uint32_t> sNumLowVirtualMemEvents;
 Atomic<uint32_t> sNumLowCommitSpaceEvents;
 Atomic<uint32_t> sNumLowPhysicalMemEvents;
 
 WindowsDllInterceptor sKernel32Intercept;
 WindowsDllInterceptor sGdi32Intercept;
 
@@ -80,17 +90,17 @@ HBITMAP(WINAPI* sCreateDIBSectionOrig)(H
  */
 bool
 MaybeScheduleMemoryPressureEvent()
 {
   // If this interval rolls over, we may fire an extra memory pressure
   // event, but that's not a big deal.
   PRIntervalTime interval = PR_IntervalNow() - sLastLowMemoryNotificationTime;
   if (sHasScheduledOneLowMemoryNotification &&
-      PR_IntervalToMilliseconds(interval) < sLowMemoryNotificationIntervalMS) {
+      PR_IntervalToMilliseconds(interval) < kLowMemoryNotificationIntervalMS) {
 
     return false;
   }
 
   // There's a bit of a race condition here, since an interval may be a
   // 64-bit number, and 64-bit writes aren't atomic on x86-32.  But let's
   // not worry about it -- the races only happen when we're already
   // experiencing memory pressure and firing notifications, so the worst
@@ -110,28 +120,28 @@ CheckMemAvailable()
     return;
   }
 
   MEMORYSTATUSEX stat;
   stat.dwLength = sizeof(stat);
   bool success = GlobalMemoryStatusEx(&stat);
 
   if (success) {
-    // sLowVirtualMemoryThreshold is in MB, but ullAvailVirtual is in bytes.
-    if (stat.ullAvailVirtual < sLowVirtualMemoryThreshold * 1024 * 1024) {
+    // kLowVirtualMemoryThresholdMiB is in MiB, but ullAvailVirtual is in bytes.
+    if (stat.ullAvailVirtual < kLowVirtualMemoryThresholdMiB * 1024 * 1024) {
       // If we're running low on virtual memory, unconditionally schedule the
       // notification.  We'll probably crash if we run out of virtual memory,
       // so don't worry about firing this notification too often.
       ++sNumLowVirtualMemEvents;
       NS_DispatchEventualMemoryPressure(MemPressure_New);
-    } else if (stat.ullAvailPageFile < sLowCommitSpaceThreshold * 1024 * 1024) {
+    } else if (stat.ullAvailPageFile < kLowCommitSpaceThresholdMiB * 1024 * 1024) {
       if (MaybeScheduleMemoryPressureEvent()) {
         ++sNumLowCommitSpaceEvents;
       }
-    } else if (stat.ullAvailPhys < sLowPhysicalMemoryThreshold * 1024 * 1024) {
+    } else if (stat.ullAvailPhys < kLowPhysicalMemoryThresholdMiB * 1024 * 1024) {
       if (MaybeScheduleMemoryPressureEvent()) {
         ++sNumLowPhysicalMemEvents;
       }
     }
   }
 }
 
 LPVOID WINAPI
@@ -151,18 +161,18 @@ VirtualAllocHook(LPVOID aAddress, SIZE_T
   // afterwards how much free virtual address space we have.  If we're running
   // low, we schedule a low-memory notification to run as soon as possible.
 
   LPVOID result = sVirtualAllocOrig(aAddress, aSize, aAllocationType, aProtect);
 
   // Don't call CheckMemAvailable for MEM_RESERVE if we're not tracking low
   // virtual memory.  Similarly, don't call CheckMemAvailable for MEM_COMMIT if
   // we're not tracking low physical memory.
-  if ((sLowVirtualMemoryThreshold != 0 && aAllocationType & MEM_RESERVE) ||
-      (sLowPhysicalMemoryThreshold != 0 && aAllocationType & MEM_COMMIT)) {
+  if ((kLowVirtualMemoryThresholdMiB != 0 && aAllocationType & MEM_RESERVE) ||
+      (kLowPhysicalMemoryThresholdMiB != 0 && aAllocationType & MEM_COMMIT)) {
     CheckMemAvailable();
   }
 
   return result;
 }
 
 LPVOID WINAPI
 MapViewOfFileHook(HANDLE aFileMappingObject,
@@ -321,84 +331,64 @@ class nsMemoryPressureWatcher final : pu
 {
   ~nsMemoryPressureWatcher() {}
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   void Init();
-
-private:
-  static bool sFreeDirtyPages;
 };
 
 NS_IMPL_ISUPPORTS(nsMemoryPressureWatcher, nsIObserver)
 
-bool nsMemoryPressureWatcher::sFreeDirtyPages = true;
-
 /**
  * Initialize and subscribe to the memory-pressure events. We subscribe to the
  * observer service in this method and not in the constructor because we need
  * to hold a strong reference to 'this' before calling the observer service.
  */
 void
 nsMemoryPressureWatcher::Init()
 {
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
 
   if (os) {
     os->AddObserver(this, "memory-pressure", /* ownsWeak */ false);
   }
-
-  Preferences::AddBoolVarCache(&sFreeDirtyPages, "memory.free_dirty_pages",
-                               true);
 }
 
 /**
  * Reacts to all types of memory-pressure events, launches a runnable to
  * free dirty pages held by jemalloc.
  */
 NS_IMETHODIMP
 nsMemoryPressureWatcher::Observe(nsISupports* aSubject, const char* aTopic,
                                  const char16_t* aData)
 {
   MOZ_ASSERT(!strcmp(aTopic, "memory-pressure"), "Unknown topic");
 
-  if (sFreeDirtyPages) {
-    nsCOMPtr<nsIRunnable> runnable = new nsJemallocFreeDirtyPagesRunnable();
+  nsCOMPtr<nsIRunnable> runnable = new nsJemallocFreeDirtyPagesRunnable();
 
-    NS_DispatchToMainThread(runnable);
-  }
+  NS_DispatchToMainThread(runnable);
 
   return NS_OK;
 }
 
 } // namespace
 
 namespace mozilla {
 namespace AvailableMemoryTracker {
 
 void
 Activate()
 {
 #if defined(_M_IX86) && defined(XP_WIN)
   MOZ_ASSERT(sInitialized);
   MOZ_ASSERT(!sHooksActive);
 
-  Preferences::AddUintVarCache(&sLowVirtualMemoryThreshold,
-                               "memory.low_virtual_mem_threshold_mb", 256);
-  Preferences::AddUintVarCache(&sLowPhysicalMemoryThreshold,
-                               "memory.low_physical_memory_threshold_mb", 0);
-  Preferences::AddUintVarCache(&sLowCommitSpaceThreshold,
-                               "memory.low_commit_space_threshold_mb", 256);
-  Preferences::AddUintVarCache(&sLowMemoryNotificationIntervalMS,
-                               "memory.low_memory_notification_interval_ms",
-                               10000);
-
   RegisterStrongMemoryReporter(new LowEventsReporter());
   RegisterLowMemoryEventsVirtualDistinguishedAmount(
     LowMemoryEventsVirtualDistinguishedAmount);
   RegisterLowMemoryEventsPhysicalDistinguishedAmount(
     LowMemoryEventsPhysicalDistinguishedAmount);
   sHooksActive = true;
 #endif
 
--- a/xpcom/base/nsConsoleService.cpp
+++ b/xpcom/base/nsConsoleService.cpp
@@ -47,20 +47,20 @@ using namespace mozilla;
 NS_IMPL_ADDREF(nsConsoleService)
 NS_IMPL_RELEASE(nsConsoleService)
 NS_IMPL_CLASSINFO(nsConsoleService, nullptr,
                   nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON,
                   NS_CONSOLESERVICE_CID)
 NS_IMPL_QUERY_INTERFACE_CI(nsConsoleService, nsIConsoleService, nsIObserver)
 NS_IMPL_CI_INTERFACE_GETTER(nsConsoleService, nsIConsoleService, nsIObserver)
 
-static bool sLoggingEnabled = true;
-static bool sLoggingBuffered = true;
+static const bool gLoggingEnabled = true;
+static const bool gLoggingBuffered = true;
 #if defined(ANDROID)
-static bool sLoggingLogcat = true;
+static bool gLoggingLogcat = false;
 #endif // defined(ANDROID)
 
 nsConsoleService::MessageElement::~MessageElement()
 {
 }
 
 nsConsoleService::nsConsoleService()
   : mCurrentSize(0)
@@ -130,28 +130,32 @@ public:
   explicit AddConsolePrefWatchers(nsConsoleService* aConsole)
     : mozilla::Runnable("AddConsolePrefWatchers")
     , mConsole(aConsole)
   {
   }
 
   NS_IMETHOD Run() override
   {
-    Preferences::AddBoolVarCache(&sLoggingEnabled, "consoleservice.enabled", true);
-    Preferences::AddBoolVarCache(&sLoggingBuffered, "consoleservice.buffered", true);
 #if defined(ANDROID)
-    Preferences::AddBoolVarCache(&sLoggingLogcat, "consoleservice.logcat", true);
+    Preferences::AddBoolVarCache(&gLoggingLogcat, "consoleservice.logcat",
+    #ifdef RELEASE_OR_BETA
+      false
+    #else
+      true
+    #endif
+    );
 #endif // defined(ANDROID)
 
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     MOZ_ASSERT(obs);
     obs->AddObserver(mConsole, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
     obs->AddObserver(mConsole, "inner-window-destroyed", false);
 
-    if (!sLoggingBuffered) {
+    if (!gLoggingBuffered) {
       mConsole->Reset();
     }
     return NS_OK;
   }
 
 private:
   RefPtr<nsConsoleService> mConsole;
 };
@@ -216,17 +220,17 @@ nsConsoleService::LogMessage(nsIConsoleM
 nsresult
 nsConsoleService::LogMessageWithMode(nsIConsoleMessage* aMessage,
                                      nsConsoleService::OutputMode aOutputMode)
 {
   if (!aMessage) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  if (!sLoggingEnabled) {
+  if (!gLoggingEnabled) {
     return NS_OK;
   }
 
   if (NS_IsMainThread() && mDeliveringMessage) {
     nsCString msg;
     aMessage->ToString(msg);
     NS_WARNING(nsPrintfCString("Reentrancy error: some client attempted "
       "to display a message to the console while in a console listener. "
@@ -240,17 +244,17 @@ nsConsoleService::LogMessageWithMode(nsI
   /*
    * Lock while updating buffer, and while taking snapshot of
    * listeners array.
    */
   {
     MutexAutoLock lock(mLock);
 
 #if defined(ANDROID)
-    if (sLoggingLogcat && aOutputMode == OutputToLog) {
+    if (gLoggingLogcat && aOutputMode == OutputToLog) {
       nsCString msg;
       aMessage->ToString(msg);
 
       /** Attempt to use the process name as the log tag. */
       mozilla::dom::ContentChild* child =
           mozilla::dom::ContentChild::GetSingleton();
       nsCString appName;
       if (child) {
@@ -296,17 +300,17 @@ nsConsoleService::LogMessageWithMode(nsI
       int prefixPos = msg.Find(GetJSLabelPrefix());
       if (prefixPos >= 0) {
         nsDependentCSubstring submsg(msg, prefixPos);
         AddLabel("%s", submsg.BeginReading());
       }
     }
 #endif
 
-    if (sLoggingBuffered) {
+    if (gLoggingBuffered) {
       MessageElement* e = new MessageElement(aMessage);
       mMessages.insertBack(e);
       if (mCurrentSize != mMaximumSize) {
         mCurrentSize++;
       } else {
         MessageElement* p = mMessages.popFirst();
         MOZ_ASSERT(p);
         p->swapMessage(retiredMessage);
@@ -347,17 +351,17 @@ nsConsoleService::CollectCurrentListener
     nsIConsoleListener* value = iter.UserData();
     aListeners.AppendObject(value);
   }
 }
 
 NS_IMETHODIMP
 nsConsoleService::LogStringMessage(const char16_t* aMessage)
 {
-  if (!sLoggingEnabled) {
+  if (!gLoggingEnabled) {
     return NS_OK;
   }
 
   RefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage));
   return this->LogMessage(msg);
 }
 
 NS_IMETHODIMP
--- a/xpcom/string/nsStringBuffer.h
+++ b/xpcom/string/nsStringBuffer.h
@@ -7,32 +7,16 @@
 #ifndef nsStringBuffer_h__
 #define nsStringBuffer_h__
 
 #include <atomic>
 #include "mozilla/MemoryReporting.h"
 
 template<class T> struct already_AddRefed;
 
-/*
- * Add a canary field to protect against double-frees of nsStringBuffer and
- * other potential heap corruptions.  We intend to back this out before 58 hits
- * beta.
- */
-#if (defined(DEBUG) || defined(NIGHTLY_BUILD)) && !defined(MOZ_ASAN)
-# define STRING_BUFFER_CANARY 1
-#endif
-
-#ifdef STRING_BUFFER_CANARY
-enum nsStringBufferCanary : uint32_t {
-  CANARY_OK = 0xaf57c8fa,
-  CANARY_POISON = 0x534dc0f5
-};
-#endif
-
 /**
  * This structure precedes the string buffers "we" allocate.  It may be the
  * case that nsTAString::mData does not point to one of these special
  * buffers.  The mDataFlags member variable distinguishes the buffer type.
  *
  * When this header is in use, it enables reference counting, and capacity
  * tracking.  NOTE: A string buffer can be modified only if its reference
  * count is 1.
@@ -40,20 +24,16 @@ enum nsStringBufferCanary : uint32_t {
 class nsStringBuffer
 {
 private:
   friend class CheckStaticAtomSizes;
 
   std::atomic<uint32_t> mRefCount;
   uint32_t mStorageSize;
 
-#ifdef STRING_BUFFER_CANARY
-  uint32_t mCanary;
-#endif
-
 public:
 
   /**
    * Allocates a new string buffer, with given size in bytes and a
    * reference count of one.  When the string buffer is no longer needed,
    * it should be released via Release.
    *
    * It is up to the caller to set the bytes corresponding to the string
@@ -90,22 +70,21 @@ public:
    * buffer will be destroyed when its reference count reaches zero.
    */
   void NS_FASTCALL Release();
 
   /**
    * This method returns the string buffer corresponding to the given data
    * pointer.  The data pointer must have been returned previously by a
    * call to the nsStringBuffer::Data method.
-   *
-   * This method is normally defined here in the header file for inlining into
-   * callers.  It has been outlined temporarily to make the canary checking code
-   * simpler WRT header include order.
    */
-  static nsStringBuffer* FromData(void* aData);
+  static nsStringBuffer* FromData(void* aData)
+  {
+    return reinterpret_cast<nsStringBuffer*>(aData) - 1;
+  }
 
   /**
    * This method returns the data pointer for this string buffer.
    */
   void* Data() const
   {
     return const_cast<char*>(reinterpret_cast<const char*>(this + 1));
   }
--- a/xpcom/string/nsSubstring.cpp
+++ b/xpcom/string/nsSubstring.cpp
@@ -15,46 +15,32 @@
 #include <stdio.h>
 #endif
 
 #include <stdlib.h>
 #include "nsSubstring.h"
 #include "nsString.h"
 #include "nsStringBuffer.h"
 #include "nsDependentString.h"
-#include "nsPrintfCString.h"
 #include "nsMemory.h"
 #include "prprf.h"
 #include "nsStaticAtom.h"
 #include "nsCOMPtr.h"
 
 #include "mozilla/IntegerPrintfMacros.h"
 #ifdef XP_WIN
 #include <windows.h>
 #include <process.h>
 #define getpid() _getpid()
 #define pthread_self() GetCurrentThreadId()
 #else
 #include <pthread.h>
 #include <unistd.h>
 #endif
 
-#ifdef STRING_BUFFER_CANARY
-#define CHECK_STRING_BUFFER_CANARY(c)                                     \
-  do {                                                                    \
-    if ((c) != CANARY_OK) {                                               \
-      MOZ_CRASH_UNSAFE_PRINTF("Bad canary value %d", c);                  \
-    }                                                                     \
-  } while(0)
-#else
-#define CHECK_STRING_BUFFER_CANARY(c)                                     \
-  do {                                                                    \
-  } while(0)
-#endif
-
 using mozilla::Atomic;
 
 // ---------------------------------------------------------------------------
 
 static const char16_t gNullChar = 0;
 
 char* const nsCharTraits<char>::sEmptyBuffer =
   (char*)const_cast<char16_t*>(&gNullChar);
@@ -214,33 +200,28 @@ nsStringBuffer::AddRef()
     ;
   STRING_STAT_INCREMENT(Share);
   NS_LOG_ADDREF(this, count, "nsStringBuffer", sizeof(*this));
 }
 
 void
 nsStringBuffer::Release()
 {
-  CHECK_STRING_BUFFER_CANARY(mCanary);
-
   // Since this may be the last release on this thread, we need
   // release semantics so that prior writes on this thread are visible
   // to the thread that destroys the object when it reads mValue with
   // acquire semantics.
   uint32_t count = mRefCount.fetch_sub(1, std::memory_order_release) - 1;
   NS_LOG_RELEASE(this, count, "nsStringBuffer");
   if (count == 0) {
     // We're going to destroy the object on this thread, so we need
     // acquire semantics to synchronize with the memory released by
     // the last release on other threads, that is, to ensure that
     // writes prior to that release are now visible on this thread.
     count = mRefCount.load(std::memory_order_acquire);
-#ifdef STRING_BUFFER_CANARY
-    mCanary = CANARY_POISON;
-#endif
 
     STRING_STAT_INCREMENT(Free);
     free(this); // we were allocated with |malloc|
   }
 }
 
 /**
  * Alloc returns a pointer to a new string header with set capacity.
@@ -255,30 +236,26 @@ nsStringBuffer::Alloc(size_t aSize)
 
   nsStringBuffer* hdr =
     (nsStringBuffer*)malloc(sizeof(nsStringBuffer) + aSize);
   if (hdr) {
     STRING_STAT_INCREMENT(Alloc);
 
     hdr->mRefCount = 1;
     hdr->mStorageSize = aSize;
-#ifdef STRING_BUFFER_CANARY
-    hdr->mCanary = CANARY_OK;
-#endif
     NS_LOG_ADDREF(hdr, 1, "nsStringBuffer", sizeof(*hdr));
   }
   return dont_AddRef(hdr);
 }
 
 nsStringBuffer*
 nsStringBuffer::Realloc(nsStringBuffer* aHdr, size_t aSize)
 {
   STRING_STAT_INCREMENT(Realloc);
 
-  CHECK_STRING_BUFFER_CANARY(aHdr->mCanary);
   NS_ASSERTION(aSize != 0, "zero capacity allocation not allowed");
   NS_ASSERTION(sizeof(nsStringBuffer) + aSize <= size_t(uint32_t(-1)) &&
                sizeof(nsStringBuffer) + aSize > aSize,
                "mStorageSize will truncate");
 
   // no point in trying to save ourselves if we hit this assertion
   NS_ASSERTION(!aHdr->IsReadonly(), "|Realloc| attempted on readonly string");
 
@@ -292,24 +269,16 @@ nsStringBuffer::Realloc(nsStringBuffer* 
     NS_LOG_ADDREF(aHdr, 1, "nsStringBuffer", sizeof(*aHdr));
     aHdr->mStorageSize = aSize;
   }
 
   return aHdr;
 }
 
 nsStringBuffer*
-nsStringBuffer::FromData(void* aData)
-{
-  nsStringBuffer* str = reinterpret_cast<nsStringBuffer*>(aData) - 1;
-  CHECK_STRING_BUFFER_CANARY(str->mCanary);
-  return str;
-}
-
-nsStringBuffer*
 nsStringBuffer::FromString(const nsAString& aStr)
 {
   const nsAStringAccessor* accessor =
     static_cast<const nsAStringAccessor*>(&aStr);
 
   if (!(accessor->flags() & nsAString::DataFlags::SHARED)) {
     return nullptr;
   }
--- a/xpcom/system/nsIXULRuntime.idl
+++ b/xpcom/system/nsIXULRuntime.idl
@@ -164,22 +164,16 @@ interface nsIXULRuntime : nsISupports
   /**
    * Modification time of the profile lock before the profile was locked on
    * this startup. Used to know the last time the profile was used and not
    * closed cleanly. This is set to 0 if there was no existing profile lock.
    */
   readonly attribute PRTime replacedLockTime;
 
   /**
-   * Local ID of the minidump generated when the process crashed
-   * on the previous run. Can be passed directly to CrashSubmit.submit.
-   */
-  readonly attribute DOMString lastRunCrashID;
-
-  /**
    * True if this is RELEASE_OR_BETA.
    */
   readonly attribute boolean isReleaseOrBeta;
 
   /**
    * True if this build uses official branding (MOZ_OFFICIAL_BRANDING).
    */
   readonly attribute boolean isOfficialBranding;
--- a/xpcom/threads/MainThreadIdlePeriod.cpp
+++ b/xpcom/threads/MainThreadIdlePeriod.cpp
@@ -6,96 +6,58 @@
 
 #include "MainThreadIdlePeriod.h"
 
 #include "mozilla/Maybe.h"
 #include "mozilla/Preferences.h"
 #include "nsRefreshDriver.h"
 #include "nsThreadUtils.h"
 
-#define DEFAULT_LONG_IDLE_PERIOD 50.0f
-#define DEFAULT_MIN_IDLE_PERIOD 3.0f
-#define DEFAULT_MAX_TIMER_THREAD_BOUND 5
+// The amount of idle time (milliseconds) reserved for a long idle period.
+static const double kLongIdlePeriodMS = 50.0;
 
-const uint32_t kMaxTimerThreadBoundClamp = 15;
+// The minimum amount of time (milliseconds) required for an idle period to be
+// scheduled on the main thread. N.B. layout.idle_period.time_limit adds
+// padding at the end of the idle period, which makes the point in time that we
+// expect to become busy again be:
+//   now + kMinIdlePeriodMS + layout.idle_period.time_limit
+static const double kMinIdlePeriodMS = 3.0;
+
+static const uint32_t kMaxTimerThreadBound = 5;       // milliseconds
+static const uint32_t kMaxTimerThreadBoundClamp = 15; // milliseconds
 
 namespace mozilla {
 
 NS_IMETHODIMP
 MainThreadIdlePeriod::GetIdlePeriodHint(TimeStamp* aIdleDeadline)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aIdleDeadline);
 
   TimeStamp now = TimeStamp::Now();
   TimeStamp currentGuess =
-    now + TimeDuration::FromMilliseconds(GetLongIdlePeriod());
+    now + TimeDuration::FromMilliseconds(kLongIdlePeriodMS);
 
   currentGuess = nsRefreshDriver::GetIdleDeadlineHint(currentGuess);
-  currentGuess = NS_GetTimerDeadlineHintOnCurrentThread(currentGuess, GetMaxTimerThreadBound());
+  currentGuess = NS_GetTimerDeadlineHintOnCurrentThread(currentGuess, kMaxTimerThreadBound);
 
   // If the idle period is too small, then just return a null time
   // to indicate we are busy. Otherwise return the actual deadline.
   TimeDuration minIdlePeriod =
-    TimeDuration::FromMilliseconds(GetMinIdlePeriod());
+    TimeDuration::FromMilliseconds(kMinIdlePeriodMS);
   bool busySoon = currentGuess.IsNull() ||
                   (now >= (currentGuess - minIdlePeriod)) ||
                   currentGuess < mLastIdleDeadline;
 
   if (!busySoon) {
     *aIdleDeadline = mLastIdleDeadline = currentGuess;
   }
 
   return NS_OK;
 }
 
 /* static */ float
 MainThreadIdlePeriod::GetLongIdlePeriod()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  static float sLongIdlePeriod = DEFAULT_LONG_IDLE_PERIOD;
-  static bool sInitialized = false;
-
-  if (!sInitialized && Preferences::IsServiceAvailable()) {
-    sInitialized = true;
-    Preferences::AddFloatVarCache(&sLongIdlePeriod, "idle_queue.long_period",
-                                  DEFAULT_LONG_IDLE_PERIOD);
-  }
-
-  return sLongIdlePeriod;
-}
-
-/* static */ float
-MainThreadIdlePeriod::GetMinIdlePeriod()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  static float sMinIdlePeriod = DEFAULT_MIN_IDLE_PERIOD;
-  static bool sInitialized = false;
-
-  if (!sInitialized && Preferences::IsServiceAvailable()) {
-    sInitialized = true;
-    Preferences::AddFloatVarCache(&sMinIdlePeriod, "idle_queue.min_period",
-                                  DEFAULT_MIN_IDLE_PERIOD);
-  }
-
-  return sMinIdlePeriod;
-}
-
-/* static */ uint32_t
-MainThreadIdlePeriod::GetMaxTimerThreadBound()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  static uint32_t sMaxTimerThreadBound = DEFAULT_MAX_TIMER_THREAD_BOUND;
-  static bool sInitialized = false;
-
-  if (!sInitialized && Preferences::IsServiceAvailable()) {
-    sInitialized = true;
-    Preferences::AddUintVarCache(&sMaxTimerThreadBound, "idle_queue.max_timer_thread_bound",
-                                 DEFAULT_MAX_TIMER_THREAD_BOUND);
-  }
-
-  return std::max(sMaxTimerThreadBound, kMaxTimerThreadBoundClamp);
+  return kLongIdlePeriodMS;
 }
 
 } // namespace mozilla
--- a/xpcom/threads/MainThreadIdlePeriod.h
+++ b/xpcom/threads/MainThreadIdlePeriod.h
@@ -18,18 +18,17 @@ public:
   NS_DECL_NSIIDLEPERIOD
 
   MainThreadIdlePeriod()
     : mLastIdleDeadline(TimeStamp::Now())
   {
   }
 
   static float GetLongIdlePeriod();
-  static float GetMinIdlePeriod();
-  static uint32_t GetMaxTimerThreadBound();
+
 private:
   virtual ~MainThreadIdlePeriod() {}
 
   TimeStamp mLastIdleDeadline;
 };
 
 } // namespace mozilla