merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 13 Oct 2015 12:00:54 +0200
changeset 267410 607a236c229994df99766c005f9ec729532d7747
parent 267355 afa71e48a9f1ec9f96c5bc107560659d3b15c96e (current diff)
parent 267409 09f71d26b0e31fd69fcb6558bb25252d7ed73a2f (diff)
child 267411 2387ada864282880d3a498d643abe3d8b887ee59
child 267418 b2f57376ca0aa750c43e137b6923953b52563f6b
child 267480 36a317c63ba4697bf845cc81f64f603484329718
child 267524 3dff8d76c46e087cfa5a7c8be005773fb9a71378
push id29517
push usercbook@mozilla.com
push dateTue, 13 Oct 2015 10:01:04 +0000
treeherdermozilla-central@607a236c2299 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone44.0a1
first release with
nightly linux32
607a236c2299 / 44.0a1 / 20151013030225 / files
nightly linux64
607a236c2299 / 44.0a1 / 20151013030225 / files
nightly mac
607a236c2299 / 44.0a1 / 20151013030225 / files
nightly win32
607a236c2299 / 44.0a1 / 20151013030225 / files
nightly win64
607a236c2299 / 44.0a1 / 20151013030225 / 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 mozilla-inbound to mozilla-central a=merge
dom/imptests/failures/html/dom/nodes/test_Node-isEqualNode.xhtml.json
dom/imptests/failures/html/html/dom/documents/dta/test_document.title-03.html.json
dom/imptests/failures/html/html/dom/documents/dta/test_document.title-04.xhtml.json
dom/imptests/failures/html/html/dom/documents/dta/test_document.title-07.html.json
dom/imptests/failures/html/html/semantics/forms/the-option-element/mochitest.ini
dom/imptests/failures/html/html/semantics/forms/the-option-element/test_option-text-spaces.html.json
testing/web-platform/meta/dom/nodes/Node-isEqualNode.xhtml.ini
testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-03.html.ini
testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-04.xhtml.ini
testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-07.html.ini
testing/web-platform/meta/html/semantics/forms/the-option-element/option-text-spaces.html.ini
toolkit/components/extensions/test/extensions/background/background.js
toolkit/components/extensions/test/extensions/background/manifest.json
toolkit/components/extensions/test/extensions/background/moz.build
toolkit/components/extensions/test/extensions/content_script/background.js
toolkit/components/extensions/test/extensions/content_script/content_script.js
toolkit/components/extensions/test/extensions/content_script/content_script_end.js
toolkit/components/extensions/test/extensions/content_script/content_script_idle.js
toolkit/components/extensions/test/extensions/content_script/content_script_start.js
toolkit/components/extensions/test/extensions/content_script/manifest.json
toolkit/components/extensions/test/extensions/content_script/moz.build
toolkit/components/extensions/test/extensions/moz.build
toolkit/components/extensions/test/extensions/notifications/background.js
toolkit/components/extensions/test/extensions/notifications/manifest.json
toolkit/components/extensions/test/extensions/notifications/moz.build
toolkit/components/extensions/test/extensions/simple/manifest.json
toolkit/components/extensions/test/extensions/simple/moz.build
toolkit/components/extensions/test/extensions/webrequest/background.js
toolkit/components/extensions/test/extensions/webrequest/manifest.json
toolkit/components/extensions/test/extensions/webrequest/moz.build
--- a/browser/components/extensions/test/browser/browser_ext_simple.js
+++ b/browser/components/extensions/test/browser/browser_ext_simple.js
@@ -1,19 +1,51 @@
 add_task(function* test_simple() {
-  let extension = ExtensionTestUtils.loadExtension("simple");
+  let extensionData = {
+    manifest: {
+      "name": "Simple extension test",
+      "version": "1.0",
+      "manifest_version": 2,
+      "description": ""
+    }
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
   info("load complete");
   yield extension.startup();
   info("startup complete");
   yield extension.unload();
   info("extension unloaded successfully");
 });
 
 add_task(function* test_background() {
-  let extension = ExtensionTestUtils.loadExtension("background");
+  function backgroundScript() {
+    browser.test.log("running background script");
+
+    browser.test.onMessage.addListener((x, y) => {
+      browser.test.assertEq(x, 10, "x is 10");
+      browser.test.assertEq(y, 20, "y is 20");
+
+      browser.test.notifyPass("background test passed");
+    });
+
+    browser.test.sendMessage("running", 1);
+  }
+
+  let extensionData = {
+    background: "(" + backgroundScript.toString() + ")()",
+    manifest: {
+      "name": "Simple extension test",
+      "version": "1.0",
+      "manifest_version": 2,
+      "description": ""
+    }
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
   info("load complete");
   let [, x] = yield Promise.all([extension.startup(), extension.awaitMessage("running")]);
   is(x, 1, "got correct value from extension");
   info("startup complete");
   extension.sendMessage(10, 20);
   yield extension.awaitFinish();
   info("test complete");
   yield extension.unload();
--- a/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
+++ b/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
@@ -104,18 +104,18 @@ function getFilePath(aName, aAllowMissin
 
   if (aUsePlatformPathSeparator && path.match(/^\w:/)) {
     path = path.replace(/\//g, "\\");
   }
 
   return path;
 }
 
-function saveNewHeapSnapshot() {
-  const filePath = ChromeUtils.saveHeapSnapshot({ runtime: true });
+function saveNewHeapSnapshot(opts = { runtime: true }) {
+  const filePath = ChromeUtils.saveHeapSnapshot(opts);
   ok(filePath, "Should get a file path to save the core dump to.");
   ok(true, "Saved a heap snapshot to " + filePath);
   return filePath;
 }
 
 /**
  * Save a heap snapshot to the file with the given name in the current
  * directory, read it back as a HeapSnapshot instance, and then take a census of
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_04.js
@@ -0,0 +1,118 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the HeapAnalyses{Client,Worker} can send SavedFrame stacks from
+// by-allocation-stack reports from the worker.
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function* test() {
+  const client = new HeapAnalysesClient();
+
+  // Track some allocation stacks.
+
+  const g = newGlobal();
+  const dbg = new Debugger(g);
+  g.eval(`                                                   // 1
+         this.log = [];                                      // 2
+         function f() { this.log.push(allocationMarker()); } // 3
+         function g() { this.log.push(allocationMarker()); } // 4
+         function h() { this.log.push(allocationMarker()); } // 5
+         `);                                                 // 6
+
+  // Create one allocationMarker with tracking turned off,
+  // so it will have no associated stack.
+  g.f();
+
+  dbg.memory.allocationSamplingProbability = 1;
+
+  for (let [func, n] of [ [g.f, 20],
+                          [g.g, 10],
+                          [g.h,  5] ]) {
+    for (let i = 0; i < n; i++) {
+      dbg.memory.trackingAllocationSites = true;
+      // All allocations of allocationMarker occur with this line as the oldest
+      // stack frame.
+      func();
+      dbg.memory.trackingAllocationSites = false;
+    }
+  }
+
+  // Take a heap snapshot.
+
+  const snapshotFilePath = saveNewHeapSnapshot({ debugger: dbg });
+  yield client.readHeapSnapshot(snapshotFilePath);
+  ok(true, "Should have read the heap snapshot");
+
+  // Run a census broken down by class name -> allocation stack so we can grab
+  // only the AllocationMarker objects we have complete control over.
+
+  const report = yield client.takeCensus(snapshotFilePath, {
+    breakdown: { by: 'objectClass',
+                 then: { by: 'allocationStack',
+                         then: { by: 'count',
+                                 bytes: true,
+                                 count: true
+                               },
+                         noStack: { by: 'count',
+                                    bytes: true,
+                                    count: true
+                                  }
+                       }
+               }
+  });
+
+  // Test the generated report.
+
+  ok(report, "Should get a report");
+
+  const map = report.AllocationMarker;
+  ok(map, "Should get AllocationMarkers in the report.");
+  // From a module with a different global, and therefore a different Map
+  // constructor, so we can't use instanceof.
+  equal(map.__proto__.constructor.name, "Map");
+
+  equal(map.size, 4, "Should have 4 allocation stacks (including the lack of a stack)")
+
+  // Gather the stacks we are expecting to appear as keys, and
+  // check that there are no unexpected keys.
+  let stacks = {};
+
+  map.forEach((v, k) => {
+    if (k === 'noStack') {
+      // No need to save this key.
+    } else if (k.functionDisplayName === 'f' &&
+               k.parent.functionDisplayName === 'test') {
+      stacks.f = k;
+    } else if (k.functionDisplayName === 'g' &&
+               k.parent.functionDisplayName === 'test') {
+      stacks.g = k;
+    } else if (k.functionDisplayName === 'h' &&
+               k.parent.functionDisplayName === 'test') {
+      stacks.h = k;
+    } else {
+      dumpn("Unexpected allocation stack:")
+      k.toString().split(/\n/g).forEach(s => dumpn(s));
+      ok(false);
+    }
+  });
+
+  ok(map.get('noStack'));
+  equal(map.get('noStack').count, 1);
+
+  ok(stacks.f);
+  ok(map.get(stacks.f));
+  equal(map.get(stacks.f).count, 20);
+
+  ok(stacks.g);
+  ok(map.get(stacks.g));
+  equal(map.get(stacks.g).count, 10);
+
+  ok(stacks.h);
+  ok(map.get(stacks.h));
+  equal(map.get(stacks.h).count, 5);
+
+  client.destroy();
+});
--- a/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
+++ b/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
@@ -11,16 +11,17 @@ support-files =
 
 [test_census-tree-node-01.js]
 [test_census-tree-node-02.js]
 [test_census-tree-node-03.js]
 [test_HeapAnalyses_readHeapSnapshot_01.js]
 [test_HeapAnalyses_takeCensus_01.js]
 [test_HeapAnalyses_takeCensus_02.js]
 [test_HeapAnalyses_takeCensus_03.js]
+[test_HeapAnalyses_takeCensus_04.js]
 [test_HeapSnapshot_takeCensus_01.js]
 [test_HeapSnapshot_takeCensus_02.js]
 [test_HeapSnapshot_takeCensus_03.js]
 [test_HeapSnapshot_takeCensus_04.js]
 [test_HeapSnapshot_takeCensus_05.js]
 [test_HeapSnapshot_takeCensus_06.js]
 [test_HeapSnapshot_takeCensus_07.js]
 [test_HeapSnapshot_takeCensus_08.js]
--- a/docshell/test/chrome/chrome.ini
+++ b/docshell/test/chrome/chrome.ini
@@ -75,17 +75,16 @@ skip-if = toolkit == "gtk2"
 [test_bug565388.xul]
 skip-if = os == 'linux' || os == 'mac' # Bug 1026815
 [test_bug582176.xul]
 [test_bug608669.xul]
 [test_bug662200.xul]
 [test_bug690056.xul]
 [test_bug789773.xul]
 [test_bug846906.xul]
-skip-if = (os == 'linux' && asan) || debug # Bug 1207161
 [test_bug89419.xul]
 [test_bug909218.html]
 [test_bug92598.xul]
 [test_mozFrameType.xul]
 [test_principalInherit.xul]
 [test_private_hidden_window.html]
 [test_viewsource_forbidden_in_iframe.xul]
 skip-if = true # bug 1019315
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -2705,16 +2705,19 @@ nsContentUtils::GenerateStateKey(nsICont
 // static
 nsIPrincipal*
 nsContentUtils::SubjectPrincipal()
 {
   MOZ_ASSERT(IsInitialized());
   MOZ_ASSERT(NS_IsMainThread());
   JSContext* cx = GetCurrentJSContext();
   if (!cx) {
+#ifndef RELEASE_BUILD
+    MOZ_CRASH("Accessing the Subject Principal without an AutoJSAPI on the stack is forbidden");
+#endif
     Telemetry::Accumulate(Telemetry::SUBJECT_PRINCIPAL_ACCESSED_WITHOUT_SCRIPT_ON_STACK, true);
     return GetSystemPrincipal();
   }
 
   JSCompartment *compartment = js::GetContextCompartment(cx);
 
   // When an AutoJSAPI is instantiated, we are in a null compartment until the
   // first JSAutoCompartment, which is kind of a purgatory as far as permissions
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1977,16 +1977,17 @@ public:
     , mIsRefocus(aIsRefocus)
   {
   }
 
   NS_IMETHOD Run()
   {
     InternalFocusEvent event(true, mEventMessage);
     event.mFlags.mBubbles = false;
+    event.mFlags.mCancelable = false;
     event.fromRaise = mWindowRaised;
     event.isRefocus = mIsRefocus;
     return EventDispatcher::Dispatch(mTarget, mContext, &event);
   }
 
   nsCOMPtr<nsISupports>   mTarget;
   nsRefPtr<nsPresContext> mContext;
   EventMessage            mEventMessage;
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -1069,23 +1069,16 @@ nsINode::IsEqualNode(nsINode* aOther)
         }
     
         // System ID
         docType1->GetSystemId(string1);
         docType2->GetSystemId(string2);
         if (!string1.Equals(string2)) {
           return false;
         }
-    
-        // Internal subset
-        docType1->GetInternalSubset(string1);
-        docType2->GetInternalSubset(string2);
-        if (!string1.Equals(string2)) {
-          return false;
-        }
 
         break;
       }
       default:
         MOZ_ASSERT(false, "Unknown node type");
     }
 
     nsINode* nextNode = node1->GetFirstChild();
--- a/dom/base/test/test_createHTMLDocument.html
+++ b/dom/base/test/test_createHTMLDocument.html
@@ -33,18 +33,17 @@ function checkDoc(title, expectedtitle, 
     // Doesn't always work out in WebKit.
     ok(doc.documentElement.firstChild.firstChild.firstChild, "Need a text node.");
     is(doc.documentElement.firstChild.firstChild.firstChild.data, expectedtitle);
   } else {
     is(doc.documentElement.firstChild.childNodes.length, 0);
   }
   isElement(doc.documentElement.lastChild, "body");
   is(doc.documentElement.lastChild.childNodes.length, 0);
-  ((!title || title.indexOf("\f") === -1) ? is : todo_is)
-    (doc.title, normalizedtitle);
+  is(doc.title, normalizedtitle);
   doc.body.innerHTML = "foo";
   is(doc.body.innerHTML, "foo", "innerHTML should work in HTML data documents!");
 }
 checkDoc("", "", "");
 checkDoc(null, "null", "null");
 checkDoc(undefined, "", "");
 checkDoc("foo  bar baz", "foo  bar baz", "foo bar baz");
 checkDoc("foo\t\tbar baz", "foo\t\tbar baz", "foo bar baz");
--- a/dom/imptests/failures/html/dom/nodes/mochitest.ini
+++ b/dom/imptests/failures/html/dom/nodes/mochitest.ini
@@ -1,14 +1,13 @@
 # THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
 [DEFAULT]
 support-files =
 
 
 [test_Document-createElement-namespace.html.json]
 [test_Document-createElementNS.html.json]
 [test_Document-getElementsByTagName.html.json]
-[test_Node-isEqualNode.xhtml.json]
 [test_Node-properties.html.json]
 [test_attributes.html.json]
 [test_case.html.json]
 [test_getElementsByClassName-10.xml.json]
 [test_getElementsByClassName-11.xml.json]
deleted file mode 100644
--- a/dom/imptests/failures/html/dom/nodes/test_Node-isEqualNode.xhtml.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "isEqualNode should return true when only the internal subsets of DocumentTypes differ.": true
-}
--- a/dom/imptests/failures/html/html/dom/documents/dta/mochitest.ini
+++ b/dom/imptests/failures/html/html/dom/documents/dta/mochitest.ini
@@ -1,14 +1,11 @@
 # THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
 [DEFAULT]
 support-files =
 
 
-[test_document.title-03.html.json]
-[test_document.title-04.xhtml.json]
 [test_document.title-06.html.json]
-[test_document.title-07.html.json]
 [test_nameditem-02.html.json]
 [test_nameditem-03.html.json]
 [test_nameditem-04.html.json]
 [test_nameditem-05.html.json]
 [test_nameditem-06.html.json]
deleted file mode 100644
--- a/dom/imptests/failures/html/html/dom/documents/dta/test_document.title-03.html.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  " document.title  and space normalization  ": true
-}
deleted file mode 100644
--- a/dom/imptests/failures/html/html/dom/documents/dta/test_document.title-04.xhtml.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  " document.title  and space normalization  ": true
-}
deleted file mode 100644
--- a/dom/imptests/failures/html/html/dom/documents/dta/test_document.title-07.html.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "Document.title and DOMImplementation.createHTMLDocument 6": true
-}
deleted file mode 100644
--- a/dom/imptests/failures/html/html/semantics/forms/the-option-element/mochitest.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-# THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
-[DEFAULT]
-support-files =
-
-
-[test_option-text-spaces.html.json]
deleted file mode 100644
--- a/dom/imptests/failures/html/html/semantics/forms/the-option-element/test_option-text-spaces.html.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "option.text should strip leading space characters (\"\\f\")": true,
-  "option.text should strip trailing space characters (\"\\f\")": true,
-  "option.text should strip leading and trailing space characters (\"\\f\")": true,
-  "option.text should replace single internal space characters (\"\\f\")": true,
-  "option.text should replace multiple internal space characters (\" \", \"\\f\")": true,
-  "option.text should replace multiple internal space characters (\"\\t\", \"\\f\")": true,
-  "option.text should replace multiple internal space characters (\"\\n\", \"\\f\")": true,
-  "option.text should replace multiple internal space characters (\"\\f\", \" \")": true,
-  "option.text should replace multiple internal space characters (\"\\f\", \"\\t\")": true,
-  "option.text should replace multiple internal space characters (\"\\f\", \"\\n\")": true,
-  "option.text should replace multiple internal space characters (\"\\f\", \"\\f\")": true,
-  "option.text should replace multiple internal space characters (\"\\f\", \"\\r\")": true,
-  "option.text should replace multiple internal space characters (\"\\r\", \"\\f\")": true
-}
--- a/dom/imptests/moz.build
+++ b/dom/imptests/moz.build
@@ -19,17 +19,16 @@ MOCHITEST_MANIFESTS += [
     'failures/html/dom/mochitest.ini',
     'failures/html/dom/nodes/mochitest.ini',
     'failures/html/dom/ranges/mochitest.ini',
     'failures/html/html/browsers/the-window-object/mochitest.ini',
     'failures/html/html/browsers/the-window-object/named-access-on-the-window-object/mochitest.ini',
     'failures/html/html/dom/documents/dta/doc.gEBN/mochitest.ini',
     'failures/html/html/dom/documents/dta/mochitest.ini',
     'failures/html/html/obsolete/implreq/oeaaa/mochitest.ini',
-    'failures/html/html/semantics/forms/the-option-element/mochitest.ini',
     'failures/html/html/semantics/forms/the-select-element/mochitest.ini',
     'failures/html/html/semantics/scripting-1/the-script-element/mochitest.ini',
     'failures/html/html/semantics/tabular-data/the-table-element/mochitest.ini',
     'failures/html/html/webappapis/atob/mochitest.ini',
     'failures/html/js/builtins/mochitest.ini',
     'failures/html/microdata/microdata-dom-api/mochitest.ini',
     'failures/html/typedarrays/mochitest.ini',
     'failures/webapps/WebStorage/tests/submissions/Infraware/mochitest.ini',
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -141,21 +141,21 @@ class TransactionDatabaseOperationBase;
 class VersionChangeTransaction;
 
 /*******************************************************************************
  * Constants
  ******************************************************************************/
 
 // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
 // schema version.
-static_assert(JS_STRUCTURED_CLONE_VERSION == 5,
+static_assert(JS_STRUCTURED_CLONE_VERSION == 6,
               "Need to update the major schema version.");
 
 // Major schema version. Bump for almost everything.
-const uint32_t kMajorSchemaVersion = 21;
+const uint32_t kMajorSchemaVersion = 22;
 
 // Minor schema version. Should almost always be 0 (maybe bump on release
 // branches if we have to).
 const uint32_t kMinorSchemaVersion = 0;
 
 // The schema version we store in the SQLite database is a (signed) 32-bit
 // integer. The major version is left-shifted 4 bits so the max value is
 // 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
@@ -4062,16 +4062,29 @@ UpgradeSchemaFrom20_0To21_0(mozIStorageC
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
+UpgradeSchemaFrom21_0To22_0(mozIStorageConnection* aConnection)
+{
+  // The only change between 21 and 22 was a different structured clone format,
+  // but it's backwards-compatible.
+  nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(22, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
 GetDatabaseFileURL(nsIFile* aDatabaseFile,
                    PersistenceType aPersistenceType,
                    const nsACString& aGroup,
                    const nsACString& aOrigin,
                    uint32_t aTelemetryId,
                    nsIFileURL** aResult)
 {
   MOZ_ASSERT(aDatabaseFile);
@@ -4553,17 +4566,17 @@ CreateStorageConnection(nsIFile* aDBFile
       }
 
       rv = stmt->Execute();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else  {
       // This logic needs to change next time we change the schema!
-      static_assert(kSQLiteSchemaVersion == int32_t((21 << 4) + 0),
+      static_assert(kSQLiteSchemaVersion == int32_t((22 << 4) + 0),
                     "Upgrade function needed due to schema version increase.");
 
       while (schemaVersion != kSQLiteSchemaVersion) {
         if (schemaVersion == 4) {
           rv = UpgradeSchemaFrom4To5(connection);
         } else if (schemaVersion == 5) {
           rv = UpgradeSchemaFrom5To6(connection);
         } else if (schemaVersion == 6) {
@@ -4593,16 +4606,18 @@ CreateStorageConnection(nsIFile* aDBFile
           rv = UpgradeSchemaFrom17_0To18_0(connection, aOrigin);
           vacuumNeeded = true;
         } else if (schemaVersion == MakeSchemaVersion(18, 0)) {
           rv = UpgradeSchemaFrom18_0To19_0(connection);
         } else if (schemaVersion == MakeSchemaVersion(19, 0)) {
           rv = UpgradeSchemaFrom19_0To20_0(aFMDirectory, connection);
         } else if (schemaVersion == MakeSchemaVersion(20, 0)) {
           rv = UpgradeSchemaFrom20_0To21_0(connection);
+        } else if (schemaVersion == MakeSchemaVersion(21, 0)) {
+          rv = UpgradeSchemaFrom21_0To22_0(connection);
         } else {
           IDB_WARNING("Unable to open IndexedDB database, no upgrade path is "
                       "available!");
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
 
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -252,16 +252,20 @@ HangMonitorChild::HangMonitorChild(Proce
    mShutdownDone(false),
    mIPCOpen(true)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 }
 
 HangMonitorChild::~HangMonitorChild()
 {
+  // For some reason IPDL doesn't automatically delete the channel for a
+  // bridged protocol (bug 1090570). So we have to do it ourselves.
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(GetTransport()));
+
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(sInstance == this);
   sInstance = nullptr;
 }
 
 void
 HangMonitorChild::Shutdown()
 {
@@ -464,17 +468,17 @@ DeleteMinidump(const uint32_t& aPluginId
     CrashReporter::DeleteMinidumpFilesForID(aCrashId);
   }
 #endif
   return PL_DHASH_NEXT;
 }
 
 HangMonitorParent::~HangMonitorParent()
 {
-  // For some reason IPDL doesn't autmatically delete the channel for a
+  // For some reason IPDL doesn't automatically delete the channel for a
   // bridged protocol (bug 1090570). So we have to do it ourselves.
   XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(GetTransport()));
 
   MutexAutoLock lock(mBrowserCrashDumpHashLock);
   mBrowserCrashDumpIds.EnumerateRead(DeleteMinidump, nullptr);
 }
 
 void
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -156,17 +156,17 @@ PluginModuleChild::PluginModuleChild(boo
       mac_plugin_interposing::child::SetUpCocoaInterposing();
     }
 #endif
 }
 
 PluginModuleChild::~PluginModuleChild()
 {
     if (mTransport) {
-        // For some reason IPDL doesn't autmatically delete the channel for a
+        // For some reason IPDL doesn't automatically delete the channel for a
         // bridged protocol (bug 1090570). So we have to do it ourselves. This
         // code is only invoked for PluginModuleChild instances created via
         // bridging; otherwise mTransport is null.
         XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(mTransport));
     }
 
     if (mIsChrome) {
         MOZ_ASSERT(gChromeInstance == this);
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -685,16 +685,19 @@ PluginModuleParent::~PluginModuleParent(
 PluginModuleContentParent::PluginModuleContentParent(bool aAllowAsyncInit)
     : PluginModuleParent(false, aAllowAsyncInit)
 {
     Preferences::RegisterCallback(TimeoutChanged, kContentTimeoutPref, this);
 }
 
 PluginModuleContentParent::~PluginModuleContentParent()
 {
+    XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                     new DeleteTask<Transport>(GetTransport()));
+
     Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref, this);
 }
 
 bool PluginModuleChromeParent::sInstantiated = false;
 
 PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath,
                                                    uint32_t aPluginId,
                                                    int32_t aSandboxLevel,
--- a/dom/xml/nsXMLContentSink.cpp
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -91,26 +91,22 @@ NS_NewXMLContentSink(nsIXMLContentSink**
   nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel);
   NS_ENSURE_SUCCESS(rv, rv);
 
   it.forget(aResult);
   return NS_OK;
 }
 
 nsXMLContentSink::nsXMLContentSink()
-  : mConstrainSize(true),
-    mPrettyPrintXML(true)
+  : mPrettyPrintXML(true)
 {
 }
 
 nsXMLContentSink::~nsXMLContentSink()
 {
-  if (mText) {
-    PR_Free(mText);  //  Doesn't null out, unlike PR_FREEIF
-  }
 }
 
 nsresult
 nsXMLContentSink::Init(nsIDocument* aDoc,
                        nsIURI* aURI,
                        nsISupports* aContainer,
                        nsIChannel* aChannel)
 {
@@ -470,17 +466,16 @@ nsXMLContentSink::CreateElement(const ch
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML)
       || aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG)
     ) {
     nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(content);
     sele->SetScriptLineNumber(aLineNumber);
     sele->SetCreatorParser(GetParser());
-    mConstrainSize = false;
   }
 
   // XHTML needs some special attention
   if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
     mPrettyPrintHasFactoredElements = true;
   }
   else {
     // If we care, find out if we just used a special factory.
@@ -547,17 +542,16 @@ nsXMLContentSink::CloseElement(nsIConten
   if (!nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) &&
       !nodeInfo->NamespaceEquals(kNameSpaceID_SVG)) {
     return NS_OK;
   }
 
   if (nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML)
       || nodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_SVG)
     ) {
-    mConstrainSize = true;
     nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(aContent);
 
     if (mPreventScriptExecution) {
       sele->PreventExecution();
       return NS_OK;
     }
 
     // Always check the clock in nsContentSink right after a script
@@ -773,53 +767,44 @@ nsXMLContentSink::IsScriptExecuting()
 
 nsresult
 nsXMLContentSink::FlushText(bool aReleaseTextNode)
 {
   nsresult rv = NS_OK;
 
   if (mTextLength != 0) {
     if (mLastTextNode) {
-      if ((mLastTextNodeSize + mTextLength) > mTextSize && !mXSLTProcessor) {
-        mLastTextNodeSize = 0;
-        mLastTextNode = nullptr;
-        FlushText(aReleaseTextNode);
-      } else {
-        bool notify = HaveNotifiedForCurrentContent();
-        // We could probably always increase mInNotification here since
-        // if AppendText doesn't notify it shouldn't trigger evil code.
-        // But just in case it does, we don't want to mask any notifications.
-        if (notify) {
-          ++mInNotification;
-        }
-        rv = mLastTextNode->AppendText(mText, mTextLength, notify);
-        if (notify) {
-          --mInNotification;
-        }
+      bool notify = HaveNotifiedForCurrentContent();
+      // We could probably always increase mInNotification here since
+      // if AppendText doesn't notify it shouldn't trigger evil code.
+      // But just in case it does, we don't want to mask any notifications.
+      if (notify) {
+        ++mInNotification;
+      }
+      rv = mLastTextNode->AppendText(mText, mTextLength, notify);
+      if (notify) {
+        --mInNotification;
+      }
 
-        mLastTextNodeSize += mTextLength;
-        mTextLength = 0;
-      }
+      mTextLength = 0;
     } else {
       nsRefPtr<nsTextNode> textContent = new nsTextNode(mNodeInfoManager);
 
       mLastTextNode = textContent;
 
       // Set the text in the text node
       textContent->SetText(mText, mTextLength, false);
-      mLastTextNodeSize += mTextLength;
       mTextLength = 0;
 
       // Add text to its parent
       rv = AddContentAsLeaf(textContent);
     }
   }
 
   if (aReleaseTextNode) {
-    mLastTextNodeSize = 0;
     mLastTextNode = nullptr;
   }
 
   return rv;
 }
 
 nsIContent*
 nsXMLContentSink::GetCurrentContent()
@@ -1432,51 +1417,29 @@ nsXMLContentSink::AddAttributes(const ch
 }
 
 #define NS_ACCUMULATION_BUFFER_SIZE 4096
 
 nsresult
 nsXMLContentSink::AddText(const char16_t* aText,
                           int32_t aLength)
 {
-  // Create buffer when we first need it
-  if (0 == mTextSize) {
-    mText = (char16_t *) PR_MALLOC(sizeof(char16_t) * NS_ACCUMULATION_BUFFER_SIZE);
-    if (nullptr == mText) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    mTextSize = NS_ACCUMULATION_BUFFER_SIZE;
-  }
-
-  // Copy data from string into our buffer; flush buffer when it fills up
+  // Copy data from string into our buffer; flush buffer when it fills up.
   int32_t offset = 0;
   while (0 != aLength) {
-    int32_t amount = mTextSize - mTextLength;
+    int32_t amount = NS_ACCUMULATION_BUFFER_SIZE - mTextLength;
     if (0 == amount) {
-      // XSLT wants adjacent textnodes merged.
-      if (mConstrainSize && !mXSLTProcessor) {
-        nsresult rv = FlushText();
-        if (NS_OK != rv) {
-          return rv;
-        }
-
-        amount = mTextSize - mTextLength;
+      nsresult rv = FlushText(false);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
       }
-      else {
-        mTextSize += aLength;
-        mText = (char16_t *) PR_REALLOC(mText, sizeof(char16_t) * mTextSize);
-        if (nullptr == mText) {
-          mTextSize = 0;
+      MOZ_ASSERT(mTextLength == 0);
+      amount = NS_ACCUMULATION_BUFFER_SIZE;
+    }
 
-          return NS_ERROR_OUT_OF_MEMORY;
-        }
-
-        amount = aLength;
-      }
-    }
     if (amount > aLength) {
       amount = aLength;
     }
     memcpy(&mText[mTextLength], &aText[offset], sizeof(char16_t) * amount);
     mTextLength += amount;
     offset += amount;
     aLength -= amount;
   }
--- a/dom/xml/nsXMLContentSink.h
+++ b/dom/xml/nsXMLContentSink.h
@@ -166,34 +166,35 @@ protected:
                               uint32_t aAttsCount, uint32_t aLineNumber,
                               bool aInterruptable);
   nsresult HandleEndElement(const char16_t *aName, bool aInterruptable);
   nsresult HandleCharacterData(const char16_t *aData, uint32_t aLength,
                                bool aInterruptable);
 
   nsCOMPtr<nsIContent> mDocElement;
   nsCOMPtr<nsIContent> mCurrentHead;  // When set, we're in an XHTML <haed>
-  char16_t*       mText;
 
   XMLContentSinkState mState;
 
+  // The length of the valid data in mText.
   int32_t mTextLength;
-  int32_t mTextSize;
   
   int32_t mNotifyLevel;
   nsCOMPtr<nsIContent> mLastTextNode;
-  int32_t mLastTextNodeSize;
 
-  uint8_t mConstrainSize : 1;
   uint8_t mPrettyPrintXML : 1;
   uint8_t mPrettyPrintHasSpecialRoot : 1;
   uint8_t mPrettyPrintHasFactoredElements : 1;
   uint8_t mPrettyPrinting : 1;  // True if we called PrettyPrint() and it
                                 // decided we should in fact prettyprint.
   // True to call prevent script execution in the fragment mode.
   uint8_t mPreventScriptExecution : 1;
   
   nsTArray<StackNode>              mContentStack;
 
   nsCOMPtr<nsIDocumentTransformer> mXSLTProcessor;
+
+  static const int NS_ACCUMULATION_BUFFER_SIZE = 4096;
+  // Our currently accumulated text that we have not flushed to a textnode yet.
+  char16_t mText[NS_ACCUMULATION_BUFFER_SIZE];
 };
 
 #endif // nsXMLContentSink_h__
--- a/gfx/2d/DrawingJob.h
+++ b/gfx/2d/DrawingJob.h
@@ -8,16 +8,17 @@
 
 #include <stdint.h>
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/gfx/Matrix.h"
 #include "mozilla/gfx/JobScheduler.h"
 #include "mozilla/gfx/IterableArena.h"
+#include "mozilla/RefCounted.h"
 #include "DrawCommand.h"
 
 namespace mozilla {
 namespace gfx {
 
 class DrawingCommand;
 class PrintCommand;
 class SignalCommand;
--- a/gfx/2d/JobScheduler.h
+++ b/gfx/2d/JobScheduler.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_TASKSCHEDULER_H_
 #define MOZILLA_GFX_TASKSCHEDULER_H_
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/gfx/Types.h"
+#include "mozilla/RefCounted.h"
 
 #ifdef WIN32
 #include "mozilla/gfx/JobScheduler_win32.h"
 #else
 #include "mozilla/gfx/JobScheduler_posix.h"
 #endif
 
 #include <vector>
@@ -242,9 +243,9 @@ protected:
   virtual void SetName(const char* aName) {}
 
   MultiThreadedJobQueue* mQueue;
 };
 
 } // namespace
 } // namespace
 
-#endif
\ No newline at end of file
+#endif
--- a/gfx/2d/JobScheduler_posix.h
+++ b/gfx/2d/JobScheduler_posix.h
@@ -12,16 +12,17 @@
 #include <list>
 #include <pthread.h>
 #include <stdint.h>
 #include <stdio.h>
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/gfx/CriticalSection.h"
+#include "mozilla/RefCounted.h"
 
 namespace mozilla {
 namespace gfx {
 
 class Job;
 class PosixCondVar;
 class WorkerThread;
 
--- a/gfx/2d/JobScheduler_win32.h
+++ b/gfx/2d/JobScheduler_win32.h
@@ -7,16 +7,17 @@
 #ifndef MOZILLA_GFX_TASKSCHEDULER_WIN32_H_
 #define MOZILLA_GFX_TASKSCHEDULER_WIN32_H_
 
 #include <windows.h>
 #include <list>
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/gfx/CriticalSection.h"
+#include "mozilla/RefCounted.h"
 
 namespace mozilla {
 namespace gfx {
 
 class WorkerThread;
 class Job;
 
 // The public interface of this class must remain identical to its equivalent
--- a/gfx/2d/MacIOSurface.h
+++ b/gfx/2d/MacIOSurface.h
@@ -40,16 +40,17 @@ typedef IOSurfacePtr (*IOSurfaceContextG
 typedef IOSurfacePtr (*CVPixelBufferGetIOSurfaceFunc)(
   CVPixelBufferRef pixelBuffer);
 
 typedef OSType (*IOSurfacePixelFormatFunc)(IOSurfacePtr io_surface);
 
 #import <OpenGL/OpenGL.h>
 #include "2D.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/RefCounted.h"
 
 struct _CGLContextObject;
 
 typedef _CGLContextObject* CGLContextObj;
 typedef struct CGContext* CGContextRef;
 typedef struct CGImage* CGImageRef;
 typedef uint32_t IOSurfaceID;
 
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -96,21 +96,23 @@ DCFromDrawTarget::DCFromDrawTarget(DrawT
         mNeedsRelease = false;
         SaveDC(mDC);
         cairo_t* ctx = (cairo_t*)
             aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT);
         cairo_scaled_font_t* scaled = cairo_get_scaled_font(ctx);
         cairo_win32_scaled_font_select_font(scaled, mDC);
       }
     }
-    if (!mDC) {
-      mDC = GetDC(nullptr);
-      SetGraphicsMode(mDC, GM_ADVANCED);
-      mNeedsRelease = true;
-    }
+  }
+
+  if (!mDC) {
+    // Get the whole screen DC:
+    mDC = GetDC(nullptr);
+    SetGraphicsMode(mDC, GM_ADVANCED);
+    mNeedsRelease = true;
   }
 }
 
 #ifdef CAIRO_HAS_D2D_SURFACE
 
 static const char *kFeatureLevelPref =
   "gfx.direct3d.last_used_feature_level_idx";
 static const int kSupportedFeatureLevels[] =
@@ -679,20 +681,23 @@ gfxWindowsPlatform::VerifyD2DDevice(bool
     }
 #endif
 }
 
 gfxPlatformFontList*
 gfxWindowsPlatform::CreatePlatformFontList()
 {
     gfxPlatformFontList *pfl;
+
 #ifdef CAIRO_HAS_DWRITE_FONT
     // bug 630201 - older pre-RTM versions of Direct2D/DirectWrite cause odd
     // crashers so blacklist them altogether
-    if (IsNotWin7PreRTM() && GetDWriteFactory()) {
+    if (IsNotWin7PreRTM() && GetDWriteFactory() &&
+        // Skia doesn't support DirectWrite fonts yet.
+       (gfxPlatform::GetDefaultContentBackend() != BackendType::SKIA)) {
         pfl = new gfxDWriteFontList();
         if (NS_SUCCEEDED(pfl->InitFontList())) {
             return pfl;
         }
         // DWrite font initialization failed! Don't know why this would happen,
         // but apparently it can - see bug 594865.
         // So we're going to fall back to GDI fonts & rendering.
         gfxPlatformFontList::Shutdown();
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -1216,19 +1216,16 @@ VectorImage::OnSVGDocumentLoaded()
   EvaluateAnimation();
 }
 
 void
 VectorImage::OnSVGDocumentError()
 {
   CancelAllListeners();
 
-  // XXXdholbert Need to do something more for the parsing failed case -- right
-  // now, this just makes us draw the "object" icon, rather than the (jagged)
-  // "broken image" icon.  See bug 594505.
   mError = true;
 
   if (mProgressTracker) {
     // Notify observers about the error and unblock page load.
     Progress progress = FLAG_DECODE_COMPLETE |
                         FLAG_ONLOAD_UNBLOCKED |
                         FLAG_HAS_ERROR;
 
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.h
@@ -116,17 +116,17 @@ typedef bool (*TransferStructuredCloneOp
 // assert in a debug build if it does.)
 typedef void (*FreeTransferStructuredCloneOp)(uint32_t tag, JS::TransferableOwnership ownership,
                                               void* content, uint64_t extraData, void* closure);
 
 // The maximum supported structured-clone serialization format version.
 // Increment this when anything at all changes in the serialization format.
 // (Note that this does not need to be bumped for Transferable-only changes,
 // since they are never saved to persistent storage.)
-#define JS_STRUCTURED_CLONE_VERSION 5
+#define JS_STRUCTURED_CLONE_VERSION 6
 
 struct JSStructuredCloneCallbacks {
     ReadStructuredCloneOp read;
     WriteStructuredCloneOp write;
     StructuredCloneErrorOp reportError;
     ReadTransferStructuredCloneOp readTransfer;
     TransferStructuredCloneOp writeTransfer;
     FreeTransferStructuredCloneOp freeTransfer;
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -2211,16 +2211,25 @@ IonCache::disable()
 void
 GetPropertyIC::maybeDisable(bool emitted)
 {
     if (emitted) {
         failedUpdates_ = 0;
         return;
     }
 
+    if (!canAttachStub() && id().constant()) {
+        // Don't disable the cache (and discard stubs) if we have a GETPROP and
+        // attached the maximum number of stubs. This can happen when JS code
+        // uses an AST-like data structure and accesses a field of a "base
+        // class", like node.nodeType. This should be temporary until we handle
+        // this case better, see bug 1107515.
+        return;
+    }
+
     if (!canAttachStub() || (stubCount_ == 0 && failedUpdates_ > MAX_FAILED_UPDATES)) {
         JitSpew(JitSpew_IonIC, "Disable inline cache");
         disable();
     }
 }
 
 void
 IonCache::reset(ReprotectCode reprotect)
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -2334,32 +2334,32 @@ MacroAssemblerARMCompat::store32(Imm32 s
     AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
     move32(src, scratch2);
     storePtr(scratch2, address);
 }
 
 void
 MacroAssemblerARMCompat::store32(Imm32 imm, const BaseIndex& dest)
 {
-    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
-    ma_mov(imm, scratch2);
-    store32(scratch2, dest);
+    ScratchRegisterScope scratch(asMasm());
+    ma_mov(imm, scratch);
+    store32(scratch, dest);
 }
 
 void
 MacroAssemblerARMCompat::store32(Register src, const BaseIndex& dest)
 {
     Register base = dest.base;
     uint32_t scale = Imm32::ShiftOf(dest.scale).value;
 
-    ScratchRegisterScope scratch(asMasm());
+    AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
 
     if (dest.offset != 0) {
-        ma_add(base, Imm32(dest.offset), scratch);
-        base = scratch;
+        ma_add(base, Imm32(dest.offset), scratch2);
+        base = scratch2;
     }
     ma_str(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
 }
 
 void
 MacroAssemblerARMCompat::store32_NoSecondScratch(Imm32 src, const Address& address)
 {
     // move32() needs to use the ScratchRegister internally, but there is no additional
--- a/js/src/jit/mips-shared/Assembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.cpp
@@ -476,46 +476,89 @@ AssemblerMIPSShared::as_addu(Register rd
 BufferOffset
 AssemblerMIPSShared::as_addiu(Register rd, Register rs, int32_t j)
 {
     MOZ_ASSERT(Imm16::IsInSignedRange(j));
     return writeInst(InstImm(op_addiu, rs, rd, Imm16(j)).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_daddu(Register rd, Register rs, Register rt)
+{
+    return writeInst(InstReg(op_special, rs, rt, rd, ff_daddu).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_daddiu(Register rd, Register rs, int32_t j)
+{
+    MOZ_ASSERT(Imm16::IsInSignedRange(j));
+    return writeInst(InstImm(op_daddiu, rs, rd, Imm16(j)).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_subu(Register rd, Register rs, Register rt)
 {
     return writeInst(InstReg(op_special, rs, rt, rd, ff_subu).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_dsubu(Register rd, Register rs, Register rt)
+{
+    return writeInst(InstReg(op_special, rs, rt, rd, ff_dsubu).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_mult(Register rs, Register rt)
 {
     return writeInst(InstReg(op_special, rs, rt, ff_mult).encode());
 }
 
 BufferOffset
 AssemblerMIPSShared::as_multu(Register rs, Register rt)
 {
     return writeInst(InstReg(op_special, rs, rt, ff_multu).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_dmult(Register rs, Register rt)
+{
+    return writeInst(InstReg(op_special, rs, rt, ff_dmult).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_dmultu(Register rs, Register rt)
+{
+    return writeInst(InstReg(op_special, rs, rt, ff_dmultu).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_div(Register rs, Register rt)
 {
     return writeInst(InstReg(op_special, rs, rt, ff_div).encode());
 }
 
 BufferOffset
 AssemblerMIPSShared::as_divu(Register rs, Register rt)
 {
     return writeInst(InstReg(op_special, rs, rt, ff_divu).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_ddiv(Register rs, Register rt)
+{
+    return writeInst(InstReg(op_special, rs, rt, ff_ddiv).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_ddivu(Register rs, Register rt)
+{
+    return writeInst(InstReg(op_special, rs, rt, ff_ddivu).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_mul(Register rd, Register rs, Register rt)
 {
     return writeInst(InstReg(op_special2, rs, rt, rd, ff_mul).encode());
 }
 
 BufferOffset
 AssemblerMIPSShared::as_lui(Register rd, int32_t j)
 {
@@ -527,60 +570,140 @@ AssemblerMIPSShared::as_lui(Register rd,
 BufferOffset
 AssemblerMIPSShared::as_sll(Register rd, Register rt, uint16_t sa)
 {
     MOZ_ASSERT(sa < 32);
     return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_sll).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_dsll(Register rd, Register rt, uint16_t sa)
+{
+    MOZ_ASSERT(sa < 32);
+    return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_dsll).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_dsll32(Register rd, Register rt, uint16_t sa)
+{
+    MOZ_ASSERT(31 < sa && sa < 64);
+    return writeInst(InstReg(op_special, rs_zero, rt, rd, sa - 32, ff_dsll32).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_sllv(Register rd, Register rt, Register rs)
 {
     return writeInst(InstReg(op_special, rs, rt, rd, ff_sllv).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_dsllv(Register rd, Register rt, Register rs)
+{
+    return writeInst(InstReg(op_special, rs, rt, rd, ff_dsllv).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_srl(Register rd, Register rt, uint16_t sa)
 {
     MOZ_ASSERT(sa < 32);
     return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_srl).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_dsrl(Register rd, Register rt, uint16_t sa)
+{
+    MOZ_ASSERT(sa < 32);
+    return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_dsrl).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_dsrl32(Register rd, Register rt, uint16_t sa)
+{
+    MOZ_ASSERT(31 < sa && sa < 64);
+    return writeInst(InstReg(op_special, rs_zero, rt, rd, sa - 32, ff_dsrl32).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_srlv(Register rd, Register rt, Register rs)
 {
     return writeInst(InstReg(op_special, rs, rt, rd, ff_srlv).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_dsrlv(Register rd, Register rt, Register rs)
+{
+    return writeInst(InstReg(op_special, rs, rt, rd, ff_dsrlv).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_sra(Register rd, Register rt, uint16_t sa)
 {
     MOZ_ASSERT(sa < 32);
     return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_sra).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_dsra(Register rd, Register rt, uint16_t sa)
+{
+    MOZ_ASSERT(sa < 32);
+    return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_dsra).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_dsra32(Register rd, Register rt, uint16_t sa)
+{
+    MOZ_ASSERT(31 < sa && sa < 64);
+    return writeInst(InstReg(op_special, rs_zero, rt, rd, sa - 32, ff_dsra32).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_srav(Register rd, Register rt, Register rs)
 {
     return writeInst(InstReg(op_special, rs, rt, rd, ff_srav).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_dsrav(Register rd, Register rt, Register rs)
+{
+    return writeInst(InstReg(op_special, rs, rt, rd, ff_dsrav).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_rotr(Register rd, Register rt, uint16_t sa)
 {
     MOZ_ASSERT(sa < 32);
     return writeInst(InstReg(op_special, rs_one, rt, rd, sa, ff_srl).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_drotr(Register rd, Register rt, uint16_t sa)
+{
+    MOZ_ASSERT(sa < 32);
+    return writeInst(InstReg(op_special, rs_one, rt, rd, sa, ff_dsrl).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_drotr32(Register rd, Register rt, uint16_t sa)
+{
+    MOZ_ASSERT(31 < sa && sa < 64);
+    return writeInst(InstReg(op_special, rs_one, rt, rd, sa - 32, ff_dsrl32).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_rotrv(Register rd, Register rt, Register rs)
 {
     return writeInst(InstReg(op_special, rs, rt, rd, 1, ff_srlv).encode());
 }
 
+BufferOffset
+AssemblerMIPSShared::as_drotrv(Register rd, Register rt, Register rs)
+{
+    return writeInst(InstReg(op_special, rs, rt, rd, 1, ff_dsrlv).encode());
+}
+
 // Load and store instructions
 BufferOffset
 AssemblerMIPSShared::as_lb(Register rd, Register rs, int16_t off)
 {
     return writeInst(InstImm(op_lb, rs, rd, Imm16(off)).encode());
 }
 
 BufferOffset
@@ -603,28 +726,58 @@ AssemblerMIPSShared::as_lhu(Register rd,
 
 BufferOffset
 AssemblerMIPSShared::as_lw(Register rd, Register rs, int16_t off)
 {
     return writeInst(InstImm(op_lw, rs, rd, Imm16(off)).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_lwu(Register rd, Register rs, int16_t off)
+{
+    return writeInst(InstImm(op_lwu, rs, rd, Imm16(off)).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_lwl(Register rd, Register rs, int16_t off)
 {
     return writeInst(InstImm(op_lwl, rs, rd, Imm16(off)).encode());
 }
 
 BufferOffset
 AssemblerMIPSShared::as_lwr(Register rd, Register rs, int16_t off)
 {
     return writeInst(InstImm(op_lwr, rs, rd, Imm16(off)).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_ll(Register rd, Register rs, int16_t off)
+{
+    return writeInst(InstImm(op_ll, rs, rd, Imm16(off)).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_ld(Register rd, Register rs, int16_t off)
+{
+    return writeInst(InstImm(op_ld, rs, rd, Imm16(off)).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_ldl(Register rd, Register rs, int16_t off)
+{
+    return writeInst(InstImm(op_ldl, rs, rd, Imm16(off)).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_ldr(Register rd, Register rs, int16_t off)
+{
+    return writeInst(InstImm(op_ldr, rs, rd, Imm16(off)).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_sb(Register rd, Register rs, int16_t off)
 {
     return writeInst(InstImm(op_sb, rs, rd, Imm16(off)).encode());
 }
 
 BufferOffset
 AssemblerMIPSShared::as_sh(Register rd, Register rs, int16_t off)
 {
@@ -644,16 +797,40 @@ AssemblerMIPSShared::as_swl(Register rd,
 }
 
 BufferOffset
 AssemblerMIPSShared::as_swr(Register rd, Register rs, int16_t off)
 {
     return writeInst(InstImm(op_swr, rs, rd, Imm16(off)).encode());
 }
 
+BufferOffset
+AssemblerMIPSShared::as_sc(Register rd, Register rs, int16_t off)
+{
+    return writeInst(InstImm(op_sc, rs, rd, Imm16(off)).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_sd(Register rd, Register rs, int16_t off)
+{
+    return writeInst(InstImm(op_sd, rs, rd, Imm16(off)).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_sdl(Register rd, Register rs, int16_t off)
+{
+    return writeInst(InstImm(op_sdl, rs, rd, Imm16(off)).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_sdr(Register rd, Register rs, int16_t off)
+{
+    return writeInst(InstImm(op_sdr, rs, rd, Imm16(off)).encode());
+}
+
 // Move from HI/LO register.
 BufferOffset
 AssemblerMIPSShared::as_mfhi(Register rd)
 {
     return writeInst(InstReg(op_special, rd, ff_mfhi).encode());
 }
 
 BufferOffset
@@ -721,33 +898,106 @@ AssemblerMIPSShared::as_movf(Register rd
 // Bit twiddling.
 BufferOffset
 AssemblerMIPSShared::as_clz(Register rd, Register rs)
 {
     return writeInst(InstReg(op_special2, rs, rd, rd, ff_clz).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_dclz(Register rd, Register rs)
+{
+    return writeInst(InstReg(op_special2, rs, rd, rd, ff_dclz).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_ins(Register rt, Register rs, uint16_t pos, uint16_t size)
 {
     MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32);
     Register rd;
     rd = Register::FromCode(pos + size - 1);
     return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ins).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_dins(Register rt, Register rs, uint16_t pos, uint16_t size)
+{
+    MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32);
+    Register rd;
+    rd = Register::FromCode(pos + size - 1);
+    return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_dins).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_dinsm(Register rt, Register rs, uint16_t pos, uint16_t size)
+{
+    MOZ_ASSERT(pos < 32 && size >= 2 && size <= 64 && pos + size > 32 && pos + size <= 64);
+    Register rd;
+    rd = Register::FromCode(pos + size - 1 - 32);
+    return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_dinsm).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_dinsu(Register rt, Register rs, uint16_t pos, uint16_t size)
+{
+    MOZ_ASSERT(pos >= 32 && pos < 64 && size >= 1 && size <= 32 && pos + size > 32 && pos + size <= 64);
+    Register rd;
+    rd = Register::FromCode(pos + size - 1 - 32);
+    return writeInst(InstReg(op_special3, rs, rt, rd, pos - 32, ff_dinsu).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_ext(Register rt, Register rs, uint16_t pos, uint16_t size)
 {
     MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32);
     Register rd;
     rd = Register::FromCode(size - 1);
     return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ext).encode());
 }
 
+// Sign extend
+BufferOffset
+AssemblerMIPSShared::as_seb(Register rd, Register rt)
+{
+    return writeInst(InstReg(op_special3, zero, rt, rd, 16, ff_bshfl).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_seh(Register rd, Register rt)
+{
+    return writeInst(InstReg(op_special3, zero, rt, rd, 24, ff_bshfl).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_dext(Register rt, Register rs, uint16_t pos, uint16_t size)
+{
+    MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 63);
+    Register rd;
+    rd = Register::FromCode(size - 1);
+    return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_dext).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_dextm(Register rt, Register rs, uint16_t pos, uint16_t size)
+{
+    MOZ_ASSERT(pos < 32 && size > 32 && size <= 64 && pos + size > 32 && pos + size <= 64);
+    Register rd;
+    rd = Register::FromCode(size - 1 - 32);
+   return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_dextm).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_dextu(Register rt, Register rs, uint16_t pos, uint16_t size)
+{
+    MOZ_ASSERT(pos >= 32 && pos < 64 && size != 0 && size <= 32 && pos + size > 32 && pos + size <= 64);
+    Register rd;
+    rd = Register::FromCode(size - 1);
+    return writeInst(InstReg(op_special3, rs, rt, rd, pos - 32, ff_dextu).encode());
+}
+
 // FP instructions
 BufferOffset
 AssemblerMIPSShared::as_ld(FloatRegister fd, Register base, int32_t off)
 {
     MOZ_ASSERT(Imm16::IsInSignedRange(off));
     return writeInst(InstImm(op_ldc1, base, fd, Imm16(off)).encode());
 }
 
@@ -791,16 +1041,40 @@ AssemblerMIPSShared::as_mtc1(Register rt
 }
 
 BufferOffset
 AssemblerMIPSShared::as_mfc1(Register rt, FloatRegister fs)
 {
     return writeInst(InstReg(op_cop1, rs_mfc1, rt, fs).encode());
 }
 
+BufferOffset
+AssemblerMIPSShared::as_mthc1(Register rt, FloatRegister fs)
+{
+    return writeInst(InstReg(op_cop1, rs_mthc1, rt, fs).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_mfhc1(Register rt, FloatRegister fs)
+{
+    return writeInst(InstReg(op_cop1, rs_mfhc1, rt, fs).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_dmtc1(Register rt, FloatRegister fs)
+{
+    return writeInst(InstReg(op_cop1, rs_dmtc1, rt, fs).encode());
+}
+
+BufferOffset
+AssemblerMIPSShared::as_dmfc1(Register rt, FloatRegister fs)
+{
+    return writeInst(InstReg(op_cop1, rs_dmfc1, rt, fs).encode());
+}
+
 // FP convert instructions
 BufferOffset
 AssemblerMIPSShared::as_ceilws(FloatRegister fd, FloatRegister fs)
 {
     return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_ceil_w_fmt).encode());
 }
 
 BufferOffset
@@ -841,16 +1115,22 @@ AssemblerMIPSShared::as_roundwd(FloatReg
 
 BufferOffset
 AssemblerMIPSShared::as_truncwd(FloatRegister fd, FloatRegister fs)
 {
     return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_trunc_w_fmt).encode());
 }
 
 BufferOffset
+AssemblerMIPSShared::as_cvtdl(FloatRegister fd, FloatRegister fs)
+{
+    return writeInst(InstReg(op_cop1, rs_l, zero, fs, fd, ff_cvt_d_fmt).encode());
+}
+
+BufferOffset
 AssemblerMIPSShared::as_cvtds(FloatRegister fd, FloatRegister fs)
 {
     return writeInst(InstReg(op_cop1, rs_s, zero, fs, fd, ff_cvt_d_fmt).encode());
 }
 
 BufferOffset
 AssemblerMIPSShared::as_cvtdw(FloatRegister fd, FloatRegister fs)
 {
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -25,24 +25,28 @@ namespace jit {
 static MOZ_CONSTEXPR_VAR Register zero = { Registers::zero };
 static MOZ_CONSTEXPR_VAR Register at = { Registers::at };
 static MOZ_CONSTEXPR_VAR Register v0 = { Registers::v0 };
 static MOZ_CONSTEXPR_VAR Register v1 = { Registers::v1 };
 static MOZ_CONSTEXPR_VAR Register a0 = { Registers::a0 };
 static MOZ_CONSTEXPR_VAR Register a1 = { Registers::a1 };
 static MOZ_CONSTEXPR_VAR Register a2 = { Registers::a2 };
 static MOZ_CONSTEXPR_VAR Register a3 = { Registers::a3 };
+static MOZ_CONSTEXPR_VAR Register a4 = { Registers::ta0 };
+static MOZ_CONSTEXPR_VAR Register a5 = { Registers::ta1 };
+static MOZ_CONSTEXPR_VAR Register a6 = { Registers::ta2 };
+static MOZ_CONSTEXPR_VAR Register a7 = { Registers::ta3 };
 static MOZ_CONSTEXPR_VAR Register t0 = { Registers::t0 };
 static MOZ_CONSTEXPR_VAR Register t1 = { Registers::t1 };
 static MOZ_CONSTEXPR_VAR Register t2 = { Registers::t2 };
 static MOZ_CONSTEXPR_VAR Register t3 = { Registers::t3 };
-static MOZ_CONSTEXPR_VAR Register t4 = { Registers::t4 };
-static MOZ_CONSTEXPR_VAR Register t5 = { Registers::t5 };
-static MOZ_CONSTEXPR_VAR Register t6 = { Registers::t6 };
-static MOZ_CONSTEXPR_VAR Register t7 = { Registers::t7 };
+static MOZ_CONSTEXPR_VAR Register t4 = { Registers::ta0 };
+static MOZ_CONSTEXPR_VAR Register t5 = { Registers::ta1 };
+static MOZ_CONSTEXPR_VAR Register t6 = { Registers::ta2 };
+static MOZ_CONSTEXPR_VAR Register t7 = { Registers::ta3 };
 static MOZ_CONSTEXPR_VAR Register s0 = { Registers::s0 };
 static MOZ_CONSTEXPR_VAR Register s1 = { Registers::s1 };
 static MOZ_CONSTEXPR_VAR Register s2 = { Registers::s2 };
 static MOZ_CONSTEXPR_VAR Register s3 = { Registers::s3 };
 static MOZ_CONSTEXPR_VAR Register s4 = { Registers::s4 };
 static MOZ_CONSTEXPR_VAR Register s5 = { Registers::s5 };
 static MOZ_CONSTEXPR_VAR Register s6 = { Registers::s6 };
 static MOZ_CONSTEXPR_VAR Register s7 = { Registers::s7 };
@@ -80,16 +84,20 @@ static MOZ_CONSTEXPR_VAR Register CallTe
 static MOZ_CONSTEXPR_VAR Register CallTempReg1 = t1;
 static MOZ_CONSTEXPR_VAR Register CallTempReg2 = t2;
 static MOZ_CONSTEXPR_VAR Register CallTempReg3 = t3;
 
 static MOZ_CONSTEXPR_VAR Register IntArgReg0 = a0;
 static MOZ_CONSTEXPR_VAR Register IntArgReg1 = a1;
 static MOZ_CONSTEXPR_VAR Register IntArgReg2 = a2;
 static MOZ_CONSTEXPR_VAR Register IntArgReg3 = a3;
+static MOZ_CONSTEXPR_VAR Register IntArgReg4 = a4;
+static MOZ_CONSTEXPR_VAR Register IntArgReg5 = a5;
+static MOZ_CONSTEXPR_VAR Register IntArgReg6 = a6;
+static MOZ_CONSTEXPR_VAR Register IntArgReg7 = a7;
 static MOZ_CONSTEXPR_VAR Register GlobalReg = s6; // used by Odin
 static MOZ_CONSTEXPR_VAR Register HeapReg = s7; // used by Odin
 static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = CALL_TEMP_NON_ARG_REGS;
 static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs);
 
 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = a1;
 
 static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg };
@@ -241,47 +249,62 @@ enum Opcode {
     op_cop1     = 17 << OpcodeShift,
     op_cop1x    = 19 << OpcodeShift,
 
     op_beql     = 20 << OpcodeShift,
     op_bnel     = 21 << OpcodeShift,
     op_blezl    = 22 << OpcodeShift,
     op_bgtzl    = 23 << OpcodeShift,
 
+    op_daddi    = 24 << OpcodeShift,
+    op_daddiu   = 25 << OpcodeShift,
+
+    op_ldl      = 26 << OpcodeShift,
+    op_ldr      = 27 << OpcodeShift,
+
     op_special2 = 28 << OpcodeShift,
     op_special3 = 31 << OpcodeShift,
 
     op_lb       = 32 << OpcodeShift,
     op_lh       = 33 << OpcodeShift,
     op_lwl      = 34 << OpcodeShift,
     op_lw       = 35 << OpcodeShift,
     op_lbu      = 36 << OpcodeShift,
     op_lhu      = 37 << OpcodeShift,
     op_lwr      = 38 << OpcodeShift,
+    op_lwu      = 39 << OpcodeShift,
     op_sb       = 40 << OpcodeShift,
     op_sh       = 41 << OpcodeShift,
     op_swl      = 42 << OpcodeShift,
     op_sw       = 43 << OpcodeShift,
+    op_sdl      = 44 << OpcodeShift,
+    op_sdr      = 45 << OpcodeShift,
     op_swr      = 46 << OpcodeShift,
 
+    op_ll       = 48 << OpcodeShift,
     op_lwc1     = 49 << OpcodeShift,
     op_ldc1     = 53 << OpcodeShift,
+    op_ld       = 55 << OpcodeShift,
 
+    op_sc       = 56 << OpcodeShift,
     op_swc1     = 57 << OpcodeShift,
-    op_sdc1     = 61 << OpcodeShift
+    op_sdc1     = 61 << OpcodeShift,
+    op_sd       = 63 << OpcodeShift,
 };
 
 enum RSField {
     rs_zero  = 0 << RSShift,
     // cop1 encoding of RS field.
     rs_mfc1  = 0 << RSShift,
     rs_one   = 1 << RSShift,
+    rs_dmfc1 = 1 << RSShift,
     rs_cfc1  = 2 << RSShift,
     rs_mfhc1 = 3 << RSShift,
     rs_mtc1  = 4 << RSShift,
+    rs_dmtc1 = 5 << RSShift,
     rs_ctc1  = 6 << RSShift,
     rs_mthc1 = 7 << RSShift,
     rs_bc1   = 8 << RSShift,
     rs_s     = 16 << RSShift,
     rs_d     = 17 << RSShift,
     rs_w     = 20 << RSShift,
     rs_l     = 21 << RSShift,
     rs_ps    = 22 << RSShift
@@ -311,48 +334,74 @@ enum FunctionField {
     ff_movz        = 10,
     ff_movn        = 11,
     ff_break       = 13,
     ff_sync        = 15,
 
     ff_mfhi        = 16,
     ff_mflo        = 18,
 
+    ff_dsllv       = 20,
+    ff_dsrlv       = 22,
+    ff_dsrav       = 23,
+
     ff_mult        = 24,
     ff_multu       = 25,
     ff_div         = 26,
     ff_divu        = 27,
+    ff_dmult       = 28,
+    ff_dmultu      = 29,
+    ff_ddiv        = 30,
+    ff_ddivu       = 31,
 
     ff_add         = 32,
     ff_addu        = 33,
     ff_sub         = 34,
     ff_subu        = 35,
     ff_and         = 36,
     ff_or          = 37,
     ff_xor         = 38,
     ff_nor         = 39,
 
     ff_slt         = 42,
     ff_sltu        = 43,
+    ff_dadd        = 44,
+    ff_daddu       = 45,
+    ff_dsub        = 46,
+    ff_dsubu       = 47,
 
     ff_tge         = 48,
     ff_tgeu        = 49,
     ff_tlt         = 50,
     ff_tltu        = 51,
     ff_teq         = 52,
     ff_tne         = 54,
+    ff_dsll        = 56,
+    ff_dsrl        = 58,
+    ff_dsra        = 59,
+    ff_dsll32      = 60,
+    ff_dsrl32      = 62,
+    ff_dsra32      = 63,
 
     // special2 encoding of function field.
     ff_mul         = 2,
     ff_clz         = 32,
     ff_clo         = 33,
+    ff_dclz        = 36,
 
     // special3 encoding of function field.
     ff_ext         = 0,
+    ff_dextm       = 1,
+    ff_dextu       = 2,
+    ff_dext        = 3,
     ff_ins         = 4,
+    ff_dinsm       = 5,
+    ff_dinsu       = 6,
+    ff_dins        = 7,
+    ff_bshfl       = 32,
 
     // cop1 encoding of function field.
     ff_add_fmt     = 0,
     ff_sub_fmt     = 1,
     ff_mul_fmt     = 2,
     ff_div_fmt     = 3,
     ff_sqrt_fmt    = 4,
     ff_abs_fmt     = 5,
@@ -807,58 +856,86 @@ class AssemblerMIPSShared : public Assem
     BufferOffset as_jal(JOffImm26 off);
 
     BufferOffset as_jr(Register rs);
     BufferOffset as_jalr(Register rs);
 
     // Arithmetic instructions
     BufferOffset as_addu(Register rd, Register rs, Register rt);
     BufferOffset as_addiu(Register rd, Register rs, int32_t j);
+    BufferOffset as_daddu(Register rd, Register rs, Register rt);
+    BufferOffset as_daddiu(Register rd, Register rs, int32_t j);
     BufferOffset as_subu(Register rd, Register rs, Register rt);
+    BufferOffset as_dsubu(Register rd, Register rs, Register rt);
     BufferOffset as_mult(Register rs, Register rt);
     BufferOffset as_multu(Register rs, Register rt);
+    BufferOffset as_dmult(Register rs, Register rt);
+    BufferOffset as_dmultu(Register rs, Register rt);
     BufferOffset as_div(Register rs, Register rt);
     BufferOffset as_divu(Register rs, Register rt);
     BufferOffset as_mul(Register rd, Register rs, Register rt);
+    BufferOffset as_ddiv(Register rs, Register rt);
+    BufferOffset as_ddivu(Register rs, Register rt);
 
     // Logical instructions
     BufferOffset as_and(Register rd, Register rs, Register rt);
     BufferOffset as_or(Register rd, Register rs, Register rt);
     BufferOffset as_xor(Register rd, Register rs, Register rt);
     BufferOffset as_nor(Register rd, Register rs, Register rt);
 
     BufferOffset as_andi(Register rd, Register rs, int32_t j);
     BufferOffset as_ori(Register rd, Register rs, int32_t j);
     BufferOffset as_xori(Register rd, Register rs, int32_t j);
     BufferOffset as_lui(Register rd, int32_t j);
 
     // Shift instructions
     // as_sll(zero, zero, x) instructions are reserved as nop
     BufferOffset as_sll(Register rd, Register rt, uint16_t sa);
+    BufferOffset as_dsll(Register rd, Register rt, uint16_t sa);
+    BufferOffset as_dsll32(Register rd, Register rt, uint16_t sa);
     BufferOffset as_sllv(Register rd, Register rt, Register rs);
+    BufferOffset as_dsllv(Register rd, Register rt, Register rs);
     BufferOffset as_srl(Register rd, Register rt, uint16_t sa);
+    BufferOffset as_dsrl(Register rd, Register rt, uint16_t sa);
+    BufferOffset as_dsrl32(Register rd, Register rt, uint16_t sa);
     BufferOffset as_srlv(Register rd, Register rt, Register rs);
+    BufferOffset as_dsrlv(Register rd, Register rt, Register rs);
     BufferOffset as_sra(Register rd, Register rt, uint16_t sa);
+    BufferOffset as_dsra(Register rd, Register rt, uint16_t sa);
+    BufferOffset as_dsra32(Register rd, Register rt, uint16_t sa);
     BufferOffset as_srav(Register rd, Register rt, Register rs);
     BufferOffset as_rotr(Register rd, Register rt, uint16_t sa);
     BufferOffset as_rotrv(Register rd, Register rt, Register rs);
+    BufferOffset as_dsrav(Register rd, Register rt, Register rs);
+    BufferOffset as_drotr(Register rd, Register rt, uint16_t sa);
+    BufferOffset as_drotr32(Register rd, Register rt, uint16_t sa);
+    BufferOffset as_drotrv(Register rd, Register rt, Register rs);
 
     // Load and store instructions
     BufferOffset as_lb(Register rd, Register rs, int16_t off);
     BufferOffset as_lbu(Register rd, Register rs, int16_t off);
     BufferOffset as_lh(Register rd, Register rs, int16_t off);
     BufferOffset as_lhu(Register rd, Register rs, int16_t off);
     BufferOffset as_lw(Register rd, Register rs, int16_t off);
+    BufferOffset as_lwu(Register rd, Register rs, int16_t off);
     BufferOffset as_lwl(Register rd, Register rs, int16_t off);
     BufferOffset as_lwr(Register rd, Register rs, int16_t off);
+    BufferOffset as_ll(Register rd, Register rs, int16_t off);
+    BufferOffset as_ld(Register rd, Register rs, int16_t off);
+    BufferOffset as_ldl(Register rd, Register rs, int16_t off);
+    BufferOffset as_ldr(Register rd, Register rs, int16_t off);
     BufferOffset as_sb(Register rd, Register rs, int16_t off);
     BufferOffset as_sh(Register rd, Register rs, int16_t off);
     BufferOffset as_sw(Register rd, Register rs, int16_t off);
     BufferOffset as_swl(Register rd, Register rs, int16_t off);
     BufferOffset as_swr(Register rd, Register rs, int16_t off);
+    BufferOffset as_sc(Register rd, Register rs, int16_t off);
+    BufferOffset as_sd(Register rd, Register rs, int16_t off);
+    BufferOffset as_sdl(Register rd, Register rs, int16_t off);
+    BufferOffset as_sdr(Register rd, Register rs, int16_t off);
 
     // Move from HI/LO register.
     BufferOffset as_mfhi(Register rd);
     BufferOffset as_mflo(Register rd);
 
     // Set on less than.
     BufferOffset as_slt(Register rd, Register rs, Register rt);
     BufferOffset as_sltu(Register rd, Register rs, Register rt);
@@ -868,18 +945,29 @@ class AssemblerMIPSShared : public Assem
     // Conditional move.
     BufferOffset as_movz(Register rd, Register rs, Register rt);
     BufferOffset as_movn(Register rd, Register rs, Register rt);
     BufferOffset as_movt(Register rd, Register rs, uint16_t cc = 0);
     BufferOffset as_movf(Register rd, Register rs, uint16_t cc = 0);
 
     // Bit twiddling.
     BufferOffset as_clz(Register rd, Register rs);
+    BufferOffset as_dclz(Register rd, Register rs);
     BufferOffset as_ins(Register rt, Register rs, uint16_t pos, uint16_t size);
+    BufferOffset as_dins(Register rt, Register rs, uint16_t pos, uint16_t size);
+    BufferOffset as_dinsm(Register rt, Register rs, uint16_t pos, uint16_t size);
+    BufferOffset as_dinsu(Register rt, Register rs, uint16_t pos, uint16_t size);
     BufferOffset as_ext(Register rt, Register rs, uint16_t pos, uint16_t size);
+    BufferOffset as_dext(Register rt, Register rs, uint16_t pos, uint16_t size);
+    BufferOffset as_dextm(Register rt, Register rs, uint16_t pos, uint16_t size);
+    BufferOffset as_dextu(Register rt, Register rs, uint16_t pos, uint16_t size);
+
+    // Sign extend
+    BufferOffset as_seb(Register rd, Register rt);
+    BufferOffset as_seh(Register rd, Register rt);
 
     // FP instructions
 
     // Use these two functions only when you are sure address is aligned.
     // Otherwise, use ma_ld and ma_sd.
     BufferOffset as_ld(FloatRegister fd, Register base, int32_t off);
     BufferOffset as_sd(FloatRegister fd, Register base, int32_t off);
 
@@ -887,16 +975,20 @@ class AssemblerMIPSShared : public Assem
     BufferOffset as_ss(FloatRegister fd, Register base, int32_t off);
 
     BufferOffset as_movs(FloatRegister fd, FloatRegister fs);
     BufferOffset as_movd(FloatRegister fd, FloatRegister fs);
 
     BufferOffset as_mtc1(Register rt, FloatRegister fs);
     BufferOffset as_mfc1(Register rt, FloatRegister fs);
 
+    BufferOffset as_mthc1(Register rt, FloatRegister fs);
+    BufferOffset as_mfhc1(Register rt, FloatRegister fs);
+    BufferOffset as_dmtc1(Register rt, FloatRegister fs);
+    BufferOffset as_dmfc1(Register rt, FloatRegister fs);
 
   public:
     // FP convert instructions
     BufferOffset as_ceilws(FloatRegister fd, FloatRegister fs);
     BufferOffset as_floorws(FloatRegister fd, FloatRegister fs);
     BufferOffset as_roundws(FloatRegister fd, FloatRegister fs);
     BufferOffset as_truncws(FloatRegister fd, FloatRegister fs);
 
@@ -967,17 +1059,18 @@ class AssemblerMIPSShared : public Assem
     void call(Label* label);
     void call(void* target);
 
     void as_break(uint32_t code);
     void as_sync(uint32_t stype = 0);
 
   public:
     static bool SupportsFloatingPoint() {
-#if (defined(__mips_hard_float) && !defined(__mips_single_float)) || defined(JS_SIMULATOR_MIPS32)
+#if (defined(__mips_hard_float) && !defined(__mips_single_float)) || \
+    defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
         return true;
 #else
         return false;
 #endif
     }
     static bool SupportsSimd() {
         return js::jit::SupportsSimd;
     }
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -205,16 +205,17 @@ JitRuntime::generateEnterJIT(JSContext* 
 
     CodeLabel returnLabel;
     if (type == EnterJitBaseline) {
         // Handle OSR.
         AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
         regs.take(OsrFrameReg);
         regs.take(BaselineFrameReg);
         regs.take(reg_code);
+        regs.take(ReturnReg);
 
         const Address slotNumStackValues(BaselineFrameReg, sizeof(EnterJITRegs) +
                                          offsetof(EnterJITArgs, numStackValues));
         const Address slotScopeChain(BaselineFrameReg, sizeof(EnterJITRegs) +
                                      offsetof(EnterJITArgs, scopeChain));
 
         Label notOsr;
         masm.ma_b(OsrFrameReg, OsrFrameReg, &notOsr, Assembler::Zero, ShortJump);
@@ -260,20 +261,17 @@ JitRuntime::generateEnterJIT(JSContext* 
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(BaselineFrameReg); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr));
 
         regs.add(OsrFrameReg);
-        regs.add(scratch);
-        regs.add(numStackValues);
         regs.take(JSReturnOperand);
-        regs.take(ReturnReg);
         Register jitcode = regs.takeAny();
         masm.loadPtr(Address(StackPointer, 0), jitcode);
         masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), framePtr);
         masm.freeStack(2 * sizeof(uintptr_t));
 
         Label error;
         masm.freeStack(ExitFrameLayout::SizeWithFooter());
         masm.addPtr(Imm32(BaselineFrame::Size()), framePtr);
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/Assembler-mips64.cpp
@@ -0,0 +1,493 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "jit/mips64/Assembler-mips64.h"
+
+#include "mozilla/DebugOnly.h"
+
+using mozilla::DebugOnly;
+
+using namespace js;
+using namespace js::jit;
+
+ABIArgGenerator::ABIArgGenerator()
+  : usedArgSlots_(0),
+    firstArgFloat(false),
+    current_()
+{}
+
+ABIArg
+ABIArgGenerator::next(MIRType type)
+{
+    switch (type) {
+      case MIRType_Int32:
+      case MIRType_Pointer: {
+        Register destReg;
+        if (GetIntArgReg(usedArgSlots_, &destReg))
+            current_ = ABIArg(destReg);
+        else
+            current_ = ABIArg(GetArgStackDisp(usedArgSlots_));
+        usedArgSlots_++;
+        break;
+      }
+      case MIRType_Float32:
+      case MIRType_Double: {
+        FloatRegister destFReg;
+        FloatRegister::ContentType contentType;
+        if (!usedArgSlots_)
+            firstArgFloat = true;
+        contentType = (type == MIRType_Double) ?
+            FloatRegisters::Double : FloatRegisters::Single;
+        if (GetFloatArgReg(usedArgSlots_, &destFReg))
+            current_ = ABIArg(FloatRegister(destFReg.id(), contentType));
+        else
+            current_ = ABIArg(GetArgStackDisp(usedArgSlots_));
+        usedArgSlots_++;
+        break;
+      }
+      default:
+        MOZ_CRASH("Unexpected argument type");
+    }
+    return current_;
+}
+
+const Register ABIArgGenerator::NonArgReturnReg0 = t0;
+const Register ABIArgGenerator::NonArgReturnReg1 = t1;
+const Register ABIArgGenerator::NonArg_VolatileReg = v0;
+const Register ABIArgGenerator::NonReturn_VolatileReg0 = a0;
+const Register ABIArgGenerator::NonReturn_VolatileReg1 = a1;
+
+uint32_t
+js::jit::RT(FloatRegister r)
+{
+    MOZ_ASSERT(r.id() < FloatRegisters::TotalPhys);
+    return r.id() << RTShift;
+}
+
+uint32_t
+js::jit::RD(FloatRegister r)
+{
+    MOZ_ASSERT(r.id() < FloatRegisters::TotalPhys);
+    return r.id() << RDShift;
+}
+
+uint32_t
+js::jit::SA(FloatRegister r)
+{
+    MOZ_ASSERT(r.id() < FloatRegisters::TotalPhys);
+    return r.id() << SAShift;
+}
+
+// Used to patch jumps created by MacroAssemblerMIPS64Compat::jumpWithPatch.
+void
+jit::PatchJump(CodeLocationJump& jump_, CodeLocationLabel label, ReprotectCode reprotect)
+{
+    Instruction* inst = (Instruction*)jump_.raw();
+
+    // Six instructions used in load 64-bit imm.
+    MaybeAutoWritableJitCode awjc(inst, 6 * sizeof(uint32_t), reprotect);
+    Assembler::UpdateLoad64Value(inst, (uint64_t)label.raw());
+
+    AutoFlushICache::flush(uintptr_t(inst), 6 * sizeof(uint32_t));
+}
+
+// For more infromation about backedges look at comment in
+// MacroAssemblerMIPS64Compat::backedgeJump()
+void
+jit::PatchBackedge(CodeLocationJump& jump, CodeLocationLabel label,
+                   JitRuntime::BackedgeTarget target)
+{
+    uintptr_t sourceAddr = (uintptr_t)jump.raw();
+    uintptr_t targetAddr = (uintptr_t)label.raw();
+    InstImm* branch = (InstImm*)jump.raw();
+
+    MOZ_ASSERT(branch->extractOpcode() == (uint32_t(op_beq) >> OpcodeShift));
+
+    if (BOffImm16::IsInRange(targetAddr - sourceAddr)) {
+        branch->setBOffImm16(BOffImm16(targetAddr - sourceAddr));
+    } else {
+        if (target == JitRuntime::BackedgeLoopHeader) {
+            Instruction* inst = &branch[1];
+            Assembler::UpdateLoad64Value(inst, targetAddr);
+            // Jump to first ori. The lui will be executed in delay slot.
+            branch->setBOffImm16(BOffImm16(2 * sizeof(uint32_t)));
+        } else {
+            Instruction* inst = &branch[6];
+            Assembler::UpdateLoad64Value(inst, targetAddr);
+            // Jump to first ori of interrupt loop.
+            branch->setBOffImm16(BOffImm16(6 * sizeof(uint32_t)));
+        }
+    }
+}
+
+void
+Assembler::executableCopy(uint8_t* buffer)
+{
+    MOZ_ASSERT(isFinished);
+    m_buffer.executableCopy(buffer);
+
+    // Patch all long jumps during code copy.
+    for (size_t i = 0; i < longJumps_.length(); i++) {
+        Instruction* inst = (Instruction*) ((uintptr_t)buffer + longJumps_[i]);
+
+        uint64_t value = Assembler::ExtractLoad64Value(inst);
+        Assembler::UpdateLoad64Value(inst, (uint64_t)buffer + value);
+    }
+
+    AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size());
+}
+
+uintptr_t
+Assembler::GetPointer(uint8_t* instPtr)
+{
+    Instruction* inst = (Instruction*)instPtr;
+    return Assembler::ExtractLoad64Value(inst);
+}
+
+static JitCode *
+CodeFromJump(Instruction* jump)
+{
+    uint8_t* target = (uint8_t*)Assembler::ExtractLoad64Value(jump);
+    return JitCode::FromExecutable(target);
+}
+
+void
+Assembler::TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader)
+{
+    while (reader.more()) {
+        JitCode* child = CodeFromJump((Instruction*)(code->raw() + reader.readUnsigned()));
+        TraceManuallyBarrieredEdge(trc, &child, "rel32");
+    }
+}
+
+static void
+TraceOneDataRelocation(JSTracer* trc, Instruction* inst)
+{
+    void* ptr = (void*)Assembler::ExtractLoad64Value(inst);
+    void* prior = ptr;
+
+    // All pointers on MIPS64 will have the top bits cleared. If those bits
+    // are not cleared, this must be a Value.
+    uintptr_t word = reinterpret_cast<uintptr_t>(ptr);
+    if (word >> JSVAL_TAG_SHIFT) {
+        jsval_layout layout;
+        layout.asBits = word;
+        Value v = IMPL_TO_JSVAL(layout);
+        TraceManuallyBarrieredEdge(trc, &v, "ion-masm-value");
+        ptr = (void*)JSVAL_TO_IMPL(v).asBits;
+    } else {
+        // No barrier needed since these are constants.
+        TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast<gc::Cell**>(&ptr),
+                                                     "ion-masm-ptr");
+    }
+
+    if (ptr != prior) {
+        Assembler::UpdateLoad64Value(inst, uint64_t(ptr));
+        AutoFlushICache::flush(uintptr_t(inst), 6 * sizeof(uint32_t));
+    }
+}
+
+static void
+TraceDataRelocations(JSTracer* trc, uint8_t* buffer, CompactBufferReader& reader)
+{
+    while (reader.more()) {
+        size_t offset = reader.readUnsigned();
+        Instruction* inst = (Instruction*)(buffer + offset);
+        TraceOneDataRelocation(trc, inst);
+    }
+}
+
+static void
+TraceDataRelocations(JSTracer* trc, MIPSBuffer* buffer, CompactBufferReader& reader)
+{
+    while (reader.more()) {
+        BufferOffset bo (reader.readUnsigned());
+        MIPSBuffer::AssemblerBufferInstIterator iter(bo, buffer);
+        TraceOneDataRelocation(trc, iter.cur());
+    }
+}
+
+void
+Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader)
+{
+    ::TraceDataRelocations(trc, code->raw(), reader);
+}
+
+void
+Assembler::trace(JSTracer* trc)
+{
+    for (size_t i = 0; i < jumps_.length(); i++) {
+        RelativePatch& rp = jumps_[i];
+        if (rp.kind == Relocation::JITCODE) {
+            JitCode* code = JitCode::FromExecutable((uint8_t*)rp.target);
+            TraceManuallyBarrieredEdge(trc, &code, "masmrel32");
+            MOZ_ASSERT(code == JitCode::FromExecutable((uint8_t*)rp.target));
+        }
+    }
+    if (dataRelocations_.length()) {
+        CompactBufferReader reader(dataRelocations_);
+        ::TraceDataRelocations(trc, &m_buffer, reader);
+    }
+}
+
+int64_t
+Assembler::ExtractCodeLabelOffset(uint8_t* code)
+{
+    Instruction* inst = (Instruction*)code;
+    return Assembler::ExtractLoad64Value(inst);
+}
+
+void
+Assembler::Bind(uint8_t* rawCode, AbsoluteLabel* label, const void* address)
+{
+    if (label->used()) {
+        int64_t src = label->offset();
+        do {
+            Instruction* inst = (Instruction*) (rawCode + src);
+            uint64_t next = Assembler::ExtractLoad64Value(inst);
+            Assembler::UpdateLoad64Value(inst, (uint64_t)address);
+            src = next;
+        } while (src != AbsoluteLabel::INVALID_OFFSET);
+    }
+    label->bind();
+}
+
+void
+Assembler::bind(InstImm* inst, uint64_t branch, uint64_t target)
+{
+    int64_t offset = target - branch;
+    InstImm inst_bgezal = InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0));
+    InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0));
+
+    // If encoded offset is 4, then the jump must be short
+    if (BOffImm16(inst[0]).decode() == 4) {
+        MOZ_ASSERT(BOffImm16::IsInRange(offset));
+        inst[0].setBOffImm16(BOffImm16(offset));
+        inst[1].makeNop();
+        return;
+    }
+
+    // Generate the long jump for calls because return address has to be the
+    // address after the reserved block.
+    if (inst[0].encode() == inst_bgezal.encode()) {
+        addLongJump(BufferOffset(branch));
+        Assembler::WriteLoad64Instructions(inst, ScratchRegister, target);
+        inst[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr).encode();
+        // There is 1 nop after this.
+        return;
+    }
+
+    if (BOffImm16::IsInRange(offset)) {
+        bool conditional = (inst[0].encode() != inst_bgezal.encode() &&
+                            inst[0].encode() != inst_beq.encode());
+
+        inst[0].setBOffImm16(BOffImm16(offset));
+        inst[1].makeNop();
+
+        // Skip the trailing nops in conditional branches.
+        // FIXME: On Loongson3 platform, the branch degrade performance.
+        if (0 && conditional) {
+            inst[2] = InstImm(op_regimm, zero, rt_bgez, BOffImm16(5 * sizeof(uint32_t))).encode();
+            // There are 4 nops after this
+        }
+        return;
+    }
+
+    if (inst[0].encode() == inst_beq.encode()) {
+        // Handle long unconditional jump.
+        addLongJump(BufferOffset(branch));
+        Assembler::WriteLoad64Instructions(inst, ScratchRegister, target);
+        inst[4] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode();
+        // There is 1 nop after this.
+    } else {
+        // Handle long conditional jump.
+        inst[0] = invertBranch(inst[0], BOffImm16(7 * sizeof(uint32_t)));
+        // No need for a "nop" here because we can clobber scratch.
+        addLongJump(BufferOffset(branch + sizeof(uint32_t)));
+        Assembler::WriteLoad64Instructions(&inst[1], ScratchRegister, target);
+        inst[5] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode();
+        // There is 1 nop after this.
+    }
+}
+
+void
+Assembler::bind(RepatchLabel* label)
+{
+    BufferOffset dest = nextOffset();
+    if (label->used()) {
+        // If the label has a use, then change this use to refer to
+        // the bound label;
+        BufferOffset b(label->offset());
+        InstImm* inst1 = (InstImm*)editSrc(b);
+
+        // If first instruction is branch, then this is a loop backedge.
+        if (inst1->extractOpcode() == ((uint32_t)op_beq >> OpcodeShift)) {
+            // Backedges are short jumps when bound, but can become long
+            // when patched.
+            uint64_t offset = dest.getOffset() - label->offset();
+            MOZ_ASSERT(BOffImm16::IsInRange(offset));
+            inst1->setBOffImm16(BOffImm16(offset));
+        } else {
+            Assembler::UpdateLoad64Value(inst1, dest.getOffset());
+        }
+
+    }
+    label->bind(dest.getOffset());
+}
+
+uint32_t
+Assembler::PatchWrite_NearCallSize()
+{
+    // Load an address needs 4 instructions, and a jump with a delay slot.
+    return (4 + 2) * sizeof(uint32_t);
+}
+
+void
+Assembler::PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall)
+{
+    Instruction* inst = (Instruction*) start.raw();
+    uint8_t* dest = toCall.raw();
+
+    // Overwrite whatever instruction used to be here with a call.
+    // Always use long jump for two reasons:
+    // - Jump has to be the same size because of PatchWrite_NearCallSize.
+    // - Return address has to be at the end of replaced block.
+    // Short jump wouldn't be more efficient.
+    Assembler::WriteLoad64Instructions(inst, ScratchRegister, (uint64_t)dest);
+    inst[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
+    inst[5] = InstNOP();
+
+    // Ensure everyone sees the code that was just written into memory.
+    AutoFlushICache::flush(uintptr_t(inst), PatchWrite_NearCallSize());
+}
+
+uint64_t
+Assembler::ExtractLoad64Value(Instruction* inst0)
+{
+    InstImm* i0 = (InstImm*) inst0;
+    InstImm* i1 = (InstImm*) i0->next();
+    InstReg* i2 = (InstReg*) i1->next();
+    InstImm* i3 = (InstImm*) i2->next();
+    InstImm* i5 = (InstImm*) i3->next()->next();
+
+    MOZ_ASSERT(i0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift));
+    MOZ_ASSERT(i1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift));
+    MOZ_ASSERT(i3->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift));
+
+    if ((i2->extractOpcode() == ((uint32_t)op_special >> OpcodeShift)) &&
+        (i2->extractFunctionField() == ff_dsrl32))
+    {
+        uint64_t value = (uint64_t(i0->extractImm16Value()) << 32) |
+                         (uint64_t(i1->extractImm16Value()) << 16) |
+                         uint64_t(i3->extractImm16Value());
+        return uint64_t((int64_t(value) <<16) >> 16);
+    }
+
+    MOZ_ASSERT(i5->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift));
+    uint64_t value = (uint64_t(i0->extractImm16Value()) << 48) |
+                     (uint64_t(i1->extractImm16Value()) << 32) |
+                     (uint64_t(i3->extractImm16Value()) << 16) |
+                     uint64_t(i5->extractImm16Value());
+    return value;
+}
+
+void
+Assembler::UpdateLoad64Value(Instruction* inst0, uint64_t value)
+{
+    InstImm* i0 = (InstImm*) inst0;
+    InstImm* i1 = (InstImm*) i0->next();
+    InstReg* i2 = (InstReg*) i1->next();
+    InstImm* i3 = (InstImm*) i2->next();
+    InstImm* i5 = (InstImm*) i3->next()->next();
+
+    MOZ_ASSERT(i0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift));
+    MOZ_ASSERT(i1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift));
+    MOZ_ASSERT(i3->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift));
+
+    if ((i2->extractOpcode() == ((uint32_t)op_special >> OpcodeShift)) &&
+        (i2->extractFunctionField() == ff_dsrl32))
+    {
+        i0->setImm16(Imm16::Lower(Imm32(value >> 32)));
+        i1->setImm16(Imm16::Upper(Imm32(value)));
+        i3->setImm16(Imm16::Lower(Imm32(value)));
+        return;
+    }
+
+    MOZ_ASSERT(i5->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift));
+
+    i0->setImm16(Imm16::Upper(Imm32(value >> 32)));
+    i1->setImm16(Imm16::Lower(Imm32(value >> 32)));
+    i3->setImm16(Imm16::Upper(Imm32(value)));
+    i5->setImm16(Imm16::Lower(Imm32(value)));
+}
+
+void
+Assembler::WriteLoad64Instructions(Instruction* inst0, Register reg, uint64_t value)
+{
+    Instruction* inst1 = inst0->next();
+    Instruction* inst2 = inst1->next();
+    Instruction* inst3 = inst2->next();
+
+    *inst0 = InstImm(op_lui, zero, reg, Imm16::Lower(Imm32(value >> 32)));
+    *inst1 = InstImm(op_ori, reg, reg, Imm16::Upper(Imm32(value)));
+    *inst2 = InstReg(op_special, rs_one, reg, reg, 48 - 32, ff_dsrl32);
+    *inst3 = InstImm(op_ori, reg, reg, Imm16::Lower(Imm32(value)));
+}
+
+void
+Assembler::PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
+                                   PatchedImmPtr expectedValue)
+{
+    Instruction* inst = (Instruction*) label.raw();
+
+    // Extract old Value
+    DebugOnly<uint64_t> value = Assembler::ExtractLoad64Value(inst);
+    MOZ_ASSERT(value == uint64_t(expectedValue.value));
+
+    // Replace with new value
+    Assembler::UpdateLoad64Value(inst, uint64_t(newValue.value));
+
+    AutoFlushICache::flush(uintptr_t(inst), 6 * sizeof(uint32_t));
+}
+
+void
+Assembler::PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm)
+{
+    InstImm* inst = (InstImm*)code;
+    Assembler::UpdateLoad64Value(inst, (uint64_t)imm.value);
+}
+
+void
+Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled)
+{
+    Instruction* inst = (Instruction*)inst_.raw();
+    InstImm* i0 = (InstImm*) inst;
+    InstImm* i1 = (InstImm*) i0->next();
+    InstImm* i3 = (InstImm*) i1->next()->next();
+    Instruction* i4 = (Instruction*) i3->next();
+
+    MOZ_ASSERT(i0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift));
+    MOZ_ASSERT(i1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift));
+    MOZ_ASSERT(i3->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift));
+
+    if (enabled) {
+        MOZ_ASSERT(i4->extractOpcode() != ((uint32_t)op_lui >> OpcodeShift));
+        InstReg jalr = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
+        *i4 = jalr;
+    } else {
+        InstNOP nop;
+        *i4 = nop;
+    }
+
+    AutoFlushICache::flush(uintptr_t(i4), sizeof(uint32_t));
+}
+
+void
+Assembler::UpdateBoundsCheck(uint64_t heapSize, Instruction* inst)
+{
+    // Replace with new value
+    Assembler::UpdateLoad64Value(inst, heapSize);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/Assembler-mips64.h
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 jit_mips64_Assembler_mips64_h
+#define jit_mips64_Assembler_mips64_h
+
+// NOTE: Don't use these macros directly
+// CallTempNonArgRegs
+#define CALL_TEMP_NON_ARG_REGS  \
+    { t0, t1, t2, t3 };
+// NumIntArgRegs
+#define NUM_INT_ARG_REGS        8;
+
+#include "jit/mips-shared/Assembler-mips-shared.h"
+
+#include "jit/mips64/Architecture-mips64.h"
+
+namespace js {
+namespace jit {
+
+static MOZ_CONSTEXPR_VAR Register CallTempReg4 = a4;
+static MOZ_CONSTEXPR_VAR Register CallTempReg5 = a5;
+
+class ABIArgGenerator
+{
+    unsigned usedArgSlots_;
+    bool firstArgFloat;
+    ABIArg current_;
+
+  public:
+    ABIArgGenerator();
+    ABIArg next(MIRType argType);
+    ABIArg& current() { return current_; }
+
+    uint32_t stackBytesConsumedSoFar() const {
+        if (usedArgSlots_ <= 8)
+            return 0;
+
+        return (usedArgSlots_ - 8) * sizeof(int64_t);
+    }
+
+    static const Register NonArgReturnReg0;
+    static const Register NonArgReturnReg1;
+    static const Register NonArg_VolatileReg;
+    static const Register NonReturn_VolatileReg0;
+    static const Register NonReturn_VolatileReg1;
+};
+
+static MOZ_CONSTEXPR_VAR Register JSReturnReg = v1;
+static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = JSReturnReg;
+static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = JSReturnReg;
+static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32Reg = { FloatRegisters::f0, FloatRegisters::Single };
+static MOZ_CONSTEXPR_VAR FloatRegister ReturnDoubleReg = { FloatRegisters::f0, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloat32Reg = { FloatRegisters::f23, FloatRegisters::Single };
+static MOZ_CONSTEXPR_VAR FloatRegister ScratchDoubleReg = { FloatRegisters::f23, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister SecondScratchFloat32Reg = { FloatRegisters::f21, FloatRegisters::Single };
+static MOZ_CONSTEXPR_VAR FloatRegister SecondScratchDoubleReg = { FloatRegisters::f21, FloatRegisters::Double };
+
+// Registers used in the GenerateFFIIonExit Disable Activation block.
+// None of these may be the second scratch register (t8).
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = JSReturnReg_Data;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = JSReturnReg_Type;
+
+static MOZ_CONSTEXPR_VAR FloatRegister f0  = { FloatRegisters::f0, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f1  = { FloatRegisters::f1, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f2  = { FloatRegisters::f2, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f3  = { FloatRegisters::f3, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f4  = { FloatRegisters::f4, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f5  = { FloatRegisters::f5, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f6  = { FloatRegisters::f6, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f7  = { FloatRegisters::f7, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f8  = { FloatRegisters::f8, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f9  = { FloatRegisters::f9, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f10 = { FloatRegisters::f10, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f11 = { FloatRegisters::f11, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f12 = { FloatRegisters::f12, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f13 = { FloatRegisters::f13, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f14 = { FloatRegisters::f14, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f15 = { FloatRegisters::f15, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f16 = { FloatRegisters::f16, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f17 = { FloatRegisters::f17, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f18 = { FloatRegisters::f18, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f19 = { FloatRegisters::f19, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f20 = { FloatRegisters::f20, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f21 = { FloatRegisters::f21, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f22 = { FloatRegisters::f22, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f23 = { FloatRegisters::f23, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f24 = { FloatRegisters::f24, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f25 = { FloatRegisters::f25, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f26 = { FloatRegisters::f26, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f27 = { FloatRegisters::f27, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f28 = { FloatRegisters::f28, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f29 = { FloatRegisters::f29, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f30 = { FloatRegisters::f30, FloatRegisters::Double };
+static MOZ_CONSTEXPR_VAR FloatRegister f31 = { FloatRegisters::f31, FloatRegisters::Double };
+
+// MIPS64 CPUs can only load multibyte data that is "naturally"
+// eight-byte-aligned, sp register should be sixteen-byte-aligned.
+static MOZ_CONSTEXPR_VAR uint32_t ABIStackAlignment = 16;
+static MOZ_CONSTEXPR_VAR uint32_t JitStackAlignment = 16;
+
+static MOZ_CONSTEXPR_VAR uint32_t JitStackValueAlignment = JitStackAlignment / sizeof(Value);
+static_assert(JitStackAlignment % sizeof(Value) == 0 && JitStackValueAlignment >= 1,
+  "Stack alignment should be a non-zero multiple of sizeof(Value)");
+
+// TODO this is just a filler to prevent a build failure. The MIPS SIMD
+// alignment requirements still need to be explored.
+// TODO Copy the static_asserts from x64/x86 assembler files.
+static MOZ_CONSTEXPR_VAR uint32_t SimdMemoryAlignment = 16;
+
+static MOZ_CONSTEXPR_VAR uint32_t AsmJSStackAlignment = SimdMemoryAlignment;
+
+static MOZ_CONSTEXPR_VAR Scale ScalePointer = TimesEight;
+
+class Assembler : public AssemblerMIPSShared
+{
+  public:
+    Assembler()
+      : AssemblerMIPSShared()
+    { }
+
+    // MacroAssemblers hold onto gcthings, so they are traced by the GC.
+    void trace(JSTracer* trc);
+
+    static uintptr_t GetPointer(uint8_t*);
+
+    using AssemblerMIPSShared::bind;
+    using AssemblerMIPSShared::PatchDataWithValueCheck;
+
+    void bind(RepatchLabel* label);
+    void Bind(uint8_t* rawCode, AbsoluteLabel* label, const void* address);
+
+    static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
+    static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
+
+    void bind(InstImm* inst, uint64_t branch, uint64_t target);
+
+    // Copy the assembly code to the given buffer, and perform any pending
+    // relocations relying on the target address.
+    void executableCopy(uint8_t* buffer);
+
+    static uint32_t PatchWrite_NearCallSize();
+
+    static uint64_t ExtractLoad64Value(Instruction* inst0);
+    static void UpdateLoad64Value(Instruction* inst0, uint64_t value);
+    static void WriteLoad64Instructions(Instruction* inst0, Register reg, uint64_t value);
+
+
+    static void PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall);
+    static void PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
+                                        PatchedImmPtr expectedValue);
+
+    static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm);
+
+    static void ToggleCall(CodeLocationLabel inst_, bool enabled);
+
+    static void UpdateBoundsCheck(uint64_t logHeapSize, Instruction* inst);
+    static int64_t ExtractCodeLabelOffset(uint8_t* code);
+}; // Assembler
+
+static const uint32_t NumFloatArgRegs = NumIntArgRegs;
+
+static inline bool
+GetFloatArgReg(uint32_t usedArgSlots, FloatRegister* out)
+{
+    if (usedArgSlots < NumFloatArgRegs) {
+        *out = FloatRegister::FromCode(f12.code() + usedArgSlots);
+        return true;
+    }
+    return false;
+}
+
+static inline uint32_t
+GetArgStackDisp(uint32_t usedArgSlots)
+{
+    MOZ_ASSERT(usedArgSlots >= NumIntArgRegs);
+    return (usedArgSlots - NumIntArgRegs) * sizeof(int64_t);
+}
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_mips64_Assembler_mips64_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/Bailouts-mips64.cpp
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "jscntxt.h"
+#include "jscompartment.h"
+
+#include "jit/mips-shared/Bailouts-mips-shared.h"
+
+using namespace js;
+using namespace js::jit;
+
+BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& activations,
+                                   BailoutStack* bailout)
+  : machine_(bailout->machine())
+{
+    uint8_t* sp = bailout->parentStackPointer();
+    framePointer_ = sp + bailout->frameSize();
+    topFrameSize_ = framePointer_ - sp;
+
+    JSScript* script = ScriptFromCalleeToken(((JitFrameLayout*) framePointer_)->calleeToken());
+    topIonScript_ = script->ionScript();
+
+    attachOnJitActivation(activations);
+    snapshotOffset_ = bailout->snapshotOffset();
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/BaselineCompiler-mips64.cpp
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "jit/mips64/BaselineCompiler-mips64.h"
+
+using namespace js;
+using namespace js::jit;
+
+BaselineCompilerMIPS64::BaselineCompilerMIPS64(JSContext* cx, TempAllocator& alloc,
+                                               JSScript* script)
+  : BaselineCompilerMIPSShared(cx, alloc, script)
+{
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/BaselineCompiler-mips64.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 jit_mips64_BaselineCompiler_mips64_h
+#define jit_mips64_BaselineCompiler_mips64_h
+
+#include "jit/mips-shared/BaselineCompiler-mips-shared.h"
+
+namespace js {
+namespace jit {
+
+class BaselineCompilerMIPS64 : public BaselineCompilerMIPSShared
+{
+  protected:
+    BaselineCompilerMIPS64(JSContext* cx, TempAllocator& alloc, JSScript* script);
+};
+
+typedef BaselineCompilerMIPS64 BaselineCompilerSpecific;
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_mips64_BaselineCompiler_mips64_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit/mips64/BaselineIC-mips64.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "jit/BaselineCompiler.h"
+#include "jit/BaselineIC.h"
+#include "jit/BaselineJIT.h"
+#include "jit/Linker.h"
+#include "jit/SharedICHelpers.h"
+
+using namespace js;
+using namespace js::jit;
+
+namespace js {
+namespace jit {
+
+// ICCompare_Int32
+
+bool
+ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
+{
+    // Guard that R0 is an integer and R1 is an integer.
+    Label failure;
+    Label conditionTrue;
+    masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
+    masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
+
+    // Compare payload regs of R0 and R1.
+    masm.unboxInt32(R0, ExtractTemp0);
+    masm.unboxInt32(R1, ExtractTemp1);
+    Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
+    masm.ma_cmp_set(R0.valueReg(), ExtractTemp0, ExtractTemp1, cond);
+
+    masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.valueReg(), R0);
+    EmitReturnFromIC(masm);
+
+    // Failure case - jump to next stub
+    masm.bind(&failure);
+    EmitStubGuardFailure(masm);
+
+    return true;
+}
+
+} // namespace jit
+} // namespace js
--- a/js/src/jsapi-tests/testStructuredClone.cpp
+++ b/js/src/jsapi-tests/testStructuredClone.cpp
@@ -73,8 +73,156 @@ BEGIN_TEST(testStructuredClone_string)
         JS::RootedValue expected(cx, JS::StringValue(
             JS_NewStringCopyZ(cx, "Hello World!")));
         CHECK_SAME(v2, expected);
     }
 
     return true;
 }
 END_TEST(testStructuredClone_string)
+
+struct StructuredCloneTestPrincipals final : public JSPrincipals {
+    uint32_t rank;
+
+    explicit StructuredCloneTestPrincipals(uint32_t rank, int32_t rc = 1) : rank(rank) {
+        this->refcount = rc;
+    }
+
+    bool write(JSContext* cx, JSStructuredCloneWriter* writer) override {
+        return JS_WriteUint32Pair(writer, rank, 0);
+    }
+
+    static bool read(JSContext* cx, JSStructuredCloneReader *reader, JSPrincipals** outPrincipals) {
+        uint32_t rank;
+        uint32_t unused;
+        if (!JS_ReadUint32Pair(reader, &rank, &unused))
+            return false;
+
+        *outPrincipals = new StructuredCloneTestPrincipals(rank);
+        return !!*outPrincipals;
+    }
+
+    static void destroy(JSPrincipals* p) {
+        auto p1 = static_cast<StructuredCloneTestPrincipals*>(p);
+        delete p1;
+    }
+
+    static uint32_t getRank(JSPrincipals* p) {
+        if (!p)
+            return 0;
+        return static_cast<StructuredCloneTestPrincipals*>(p)->rank;
+    }
+
+    static bool subsumes(JSPrincipals* a, JSPrincipals* b) {
+        return getRank(a) > getRank(b);
+    }
+
+    static JSSecurityCallbacks securityCallbacks;
+
+    static StructuredCloneTestPrincipals testPrincipals;
+};
+
+JSSecurityCallbacks StructuredCloneTestPrincipals::securityCallbacks = {
+    nullptr, // contentSecurityPolicyAllows
+    subsumes
+};
+
+BEGIN_TEST(testStructuredClone_SavedFrame)
+{
+    JS_SetSecurityCallbacks(rt, &StructuredCloneTestPrincipals::securityCallbacks);
+    JS_InitDestroyPrincipalsCallback(rt, StructuredCloneTestPrincipals::destroy);
+    JS_InitReadPrincipalsCallback(rt, StructuredCloneTestPrincipals::read);
+
+    auto testPrincipals = new StructuredCloneTestPrincipals(42, 0);
+    CHECK(testPrincipals);
+
+    auto DONE = (JSPrincipals*) 0xDEADBEEF;
+
+    struct {
+        const char* name;
+        JSPrincipals* principals;
+    } principalsToTest[] = {
+        { "IsSystem", &js::ReconstructedSavedFramePrincipals::IsSystem },
+        { "IsNotSystem", &js::ReconstructedSavedFramePrincipals::IsNotSystem },
+        { "testPrincipals", testPrincipals },
+        { "nullptr principals", nullptr },
+        { "DONE", DONE }
+    };
+
+    const char* FILENAME = "filename.js";
+
+    for (auto* pp = principalsToTest; pp->principals != DONE; pp++) {
+        fprintf(stderr, "Testing with principals '%s'\n", pp->name);
+
+        JS::RootedObject g(cx, JS_NewGlobalObject(cx, getGlobalClass(), pp->principals,
+                                                  JS::FireOnNewGlobalHook));
+        CHECK(g);
+        JSAutoCompartment ac(cx, g);
+
+        CHECK(js::DefineTestingFunctions(cx, g, false, false));
+
+        JS::RootedValue srcVal(cx);
+        CHECK(evaluate("(function one() {                      \n"  // 1
+                       "  return (function two() {             \n"  // 2
+                       "    return (function three() {         \n"  // 3
+                       "      return saveStack();              \n"  // 4
+                       "    }());                              \n"  // 5
+                       "  }());                                \n"  // 6
+                       "}());                                  \n", // 7
+                       FILENAME,
+                       1,
+                       &srcVal));
+
+        CHECK(srcVal.isObject());
+        JS::RootedObject srcObj(cx, &srcVal.toObject());
+
+        CHECK(srcObj->is<js::SavedFrame>());
+        js::RootedSavedFrame srcFrame(cx, &srcObj->as<js::SavedFrame>());
+
+        CHECK(srcFrame->getPrincipals() == pp->principals);
+
+        JS::RootedValue destVal(cx);
+        CHECK(JS_StructuredClone(cx, srcVal, &destVal, nullptr, nullptr));
+
+        CHECK(destVal.isObject());
+        JS::RootedObject destObj(cx, &destVal.toObject());
+
+        CHECK(destObj->is<js::SavedFrame>());
+        auto destFrame = &destObj->as<js::SavedFrame>();
+
+        size_t framesCopied = 0;
+        for (auto& f : *destFrame) {
+            framesCopied++;
+
+            CHECK(&f != srcFrame);
+
+            if (pp->principals == testPrincipals) {
+                // We shouldn't get a pointer to the same
+                // StructuredCloneTestPrincipals instance since we should have
+                // serialized and then deserialized it into a new instance.
+                CHECK(f.getPrincipals() != pp->principals);
+
+                // But it should certainly have the same rank.
+                CHECK(StructuredCloneTestPrincipals::getRank(f.getPrincipals()) ==
+                      StructuredCloneTestPrincipals::getRank(pp->principals));
+            } else {
+                // For our singleton principals, we should always get the same
+                // pointer back.
+                CHECK(js::ReconstructedSavedFramePrincipals::is(pp->principals) ||
+                      pp->principals == nullptr);
+                CHECK(f.getPrincipals() == pp->principals);
+            }
+
+            CHECK(EqualStrings(f.getSource(), srcFrame->getSource()));
+            CHECK(f.getLine() == srcFrame->getLine());
+            CHECK(f.getColumn() == srcFrame->getColumn());
+            CHECK(EqualStrings(f.getFunctionDisplayName(), srcFrame->getFunctionDisplayName()));
+
+            srcFrame = srcFrame->getParent();
+        }
+
+        // Four function frames + one global frame.
+        CHECK(framesCopied == 4);
+    }
+
+    return true;
+}
+END_TEST(testStructuredClone_SavedFrame)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1959,44 +1959,16 @@ JS_SetPrivate(JSObject* obj, void* data)
 JS_PUBLIC_API(void*)
 JS_GetInstancePrivate(JSContext* cx, HandleObject obj, const JSClass* clasp, CallArgs* args)
 {
     if (!JS_InstanceOf(cx, obj, clasp, args))
         return nullptr;
     return obj->as<NativeObject>().getPrivate();
 }
 
-JS_PUBLIC_API(bool)
-JS_GetPrototype(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JSObject*> protop)
-{
-    return GetPrototype(cx, obj, protop);
-}
-
-JS_PUBLIC_API(bool)
-JS_SetPrototype(JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<JSObject*> proto)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, proto);
-
-    return SetPrototype(cx, obj, proto);
-}
-
-JS_PUBLIC_API(bool)
-JS_IsExtensible(JSContext* cx, HandleObject obj, bool* extensible)
-{
-    return IsExtensible(cx, obj, extensible);
-}
-
-JS_PUBLIC_API(bool)
-JS_PreventExtensions(JSContext* cx, JS::HandleObject obj, ObjectOpResult& result)
-{
-    return PreventExtensions(cx, obj, result);
-}
-
 JS_PUBLIC_API(JSObject*)
 JS_GetConstructor(JSContext* cx, HandleObject proto)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, proto);
 
     RootedValue cval(cx);
@@ -2161,159 +2133,128 @@ JS_IsNative(JSObject* obj)
 }
 
 JS_PUBLIC_API(JSRuntime*)
 JS_GetObjectRuntime(JSObject* obj)
 {
     return obj->compartment()->runtimeFromMainThread();
 }
 
-JS_PUBLIC_API(bool)
-JS_FreezeObject(JSContext* cx, HandleObject obj)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj);
-    return FreezeObject(cx, obj);
-}
-
-JS_PUBLIC_API(bool)
-JS_DeepFreezeObject(JSContext* cx, HandleObject obj)
+
+/*** Standard internal methods *******************************************************************/
+
+JS_PUBLIC_API(bool)
+JS_GetPrototype(JSContext* cx, HandleObject obj, MutableHandleObject result)
+{
+    return GetPrototype(cx, obj, result);
+}
+
+JS_PUBLIC_API(bool)
+JS_SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj);
-
-    /* Assume that non-extensible objects are already deep-frozen, to avoid divergence. */
-    bool extensible;
-    if (!IsExtensible(cx, obj, &extensible))
-        return false;
-    if (!extensible)
-        return true;
-
-    if (!FreezeObject(cx, obj))
-        return false;
-
-    /* Walk slots in obj and if any value is a non-null object, seal it. */
-    if (obj->isNative()) {
-        for (uint32_t i = 0, n = obj->as<NativeObject>().slotSpan(); i < n; ++i) {
-            const Value& v = obj->as<NativeObject>().getSlot(i);
-            if (v.isPrimitive())
-                continue;
-            RootedObject obj(cx, &v.toObject());
-            if (!JS_DeepFreezeObject(cx, obj))
-                return false;
-        }
-    }
-
-    return true;
-}
-
-JS_PUBLIC_API(bool)
-JS_HasPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
+    assertSameCompartment(cx, obj, proto);
+
+    return SetPrototype(cx, obj, proto);
+}
+
+JS_PUBLIC_API(bool)
+JS_IsExtensible(JSContext* cx, HandleObject obj, bool* extensible)
+{
+    return IsExtensible(cx, obj, extensible);
+}
+
+JS_PUBLIC_API(bool)
+JS_PreventExtensions(JSContext* cx, JS::HandleObject obj, ObjectOpResult& result)
+{
+    return PreventExtensions(cx, obj, result);
+}
+
+JS_PUBLIC_API(bool)
+JS_SetImmutablePrototype(JSContext *cx, JS::HandleObject obj, bool *succeeded)
+{
+    return SetImmutablePrototype(cx, obj, succeeded);
+}
+
+JS_PUBLIC_API(bool)
+JS_GetOwnPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id,
+                                MutableHandle<JSPropertyDescriptor> desc)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
-    return HasProperty(cx, obj, id, foundp);
-}
-
-JS_PUBLIC_API(bool)
-JS_HasElement(JSContext* cx, HandleObject obj, uint32_t index, bool* foundp)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    RootedId id(cx);
-    if (!IndexToId(cx, index, &id))
-        return false;
-    return JS_HasPropertyById(cx, obj, id, foundp);
-}
-
-JS_PUBLIC_API(bool)
-JS_HasProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp)
+    return GetOwnPropertyDescriptor(cx, obj, id, desc);
+}
+
+JS_PUBLIC_API(bool)
+JS_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name,
+                            MutableHandle<JSPropertyDescriptor> desc)
 {
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
-    return JS_HasPropertyById(cx, obj, id, foundp);
-}
-
-#define AUTO_NAMELEN(s,n)   (((n) == (size_t)-1) ? js_strlen(s) : (n))
-
-JS_PUBLIC_API(bool)
-JS_HasUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, bool* foundp)
-{
-    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    return JS_GetOwnPropertyDescriptorById(cx, obj, id, desc);
+}
+
+JS_PUBLIC_API(bool)
+JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name,
+                              MutableHandle<JSPropertyDescriptor> desc)
+{
+    JSAtom* atom = AtomizeChars(cx, name, js_strlen(name));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
-    return JS_HasPropertyById(cx, obj, id, foundp);
-}
-
-JS_PUBLIC_API(bool)
-JS_AlreadyHasOwnPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, id);
-
-    if (!obj->isNative())
-        return js::HasOwnProperty(cx, obj, id, foundp);
-
-    RootedNativeObject nativeObj(cx, &obj->as<NativeObject>());
-    RootedShape prop(cx);
-    NativeLookupOwnPropertyNoResolve(cx, nativeObj, id, &prop);
-    *foundp = !!prop;
-    return true;
-}
-
-JS_PUBLIC_API(bool)
-JS_AlreadyHasOwnElement(JSContext* cx, HandleObject obj, uint32_t index, bool* foundp)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    RootedId id(cx);
-    if (!IndexToId(cx, index, &id))
-        return false;
-    return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
-}
-
-JS_PUBLIC_API(bool)
-JS_AlreadyHasOwnProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp)
+    return JS_GetOwnPropertyDescriptorById(cx, obj, id, desc);
+}
+
+JS_PUBLIC_API(bool)
+JS_GetPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id,
+                             MutableHandle<JSPropertyDescriptor> desc)
+{
+    return GetPropertyDescriptor(cx, obj, id, desc);
+}
+
+JS_PUBLIC_API(bool)
+JS_GetPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name,
+                         MutableHandle<JSPropertyDescriptor> desc)
 {
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
-    return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
-}
-
-JS_PUBLIC_API(bool)
-JS_AlreadyHasOwnUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                           bool* foundp)
-{
-    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
-}
-
-/*
- * Wrapper functions to create wrappers with no corresponding JSJitInfo from API
- * function arguments.
- */
-static JSNativeWrapper
-NativeOpWrapper(Native native)
-{
-    JSNativeWrapper ret;
-    ret.op = native;
-    ret.info = nullptr;
-    return ret;
+    return atom && JS_GetPropertyDescriptorById(cx, obj, id, desc);
+}
+
+static bool
+DefinePropertyByDescriptor(JSContext* cx, HandleObject obj, HandleId id,
+                           Handle<JSPropertyDescriptor> desc, ObjectOpResult& result)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, id, desc);
+    return DefineProperty(cx, obj, id, desc.value(), desc.getter(), desc.setter(),
+                          desc.attributes(), result);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id,
+                      Handle<JSPropertyDescriptor> desc, ObjectOpResult& result)
+{
+    return DefinePropertyByDescriptor(cx, obj, id, desc, result);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id,
+                      Handle<JSPropertyDescriptor> desc)
+{
+    ObjectOpResult result;
+    return DefinePropertyByDescriptor(cx, obj, id, desc, result) &&
+           result.checkStrict(cx, obj, id);
 }
 
 static bool
 DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue value,
                    const JSNativeWrapper& get, const JSNativeWrapper& set,
                    unsigned attrs, unsigned flags)
 {
     JSGetterOp getter = JS_CAST_NATIVE_TO(get.op, JSGetterOp);
@@ -2401,16 +2342,29 @@ DefinePropertyById(JSContext* cx, Handle
     }
     if (getter == JS_PropertyStub)
         getter = nullptr;
     if (setter == JS_StrictPropertyStub)
         setter = nullptr;
     return DefineProperty(cx, obj, id, value, getter, setter, attrs);
 }
 
+/*
+ * Wrapper functions to create wrappers with no corresponding JSJitInfo from API
+ * function arguments.
+ */
+static JSNativeWrapper
+NativeOpWrapper(Native native)
+{
+    JSNativeWrapper ret;
+    ret.op = native;
+    ret.info = nullptr;
+    return ret;
+}
+
 JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue value,
                       unsigned attrs, Native getter, Native setter)
 {
     return DefinePropertyById(cx, obj, id, value,
                               NativeOpWrapper(getter), NativeOpWrapper(setter),
                               attrs, 0);
 }
@@ -2458,43 +2412,183 @@ JS_DefinePropertyById(JSContext* cx, Han
                       unsigned attrs, Native getter, Native setter)
 {
     Value value = NumberValue(valueArg);
     return DefinePropertyById(cx, obj, id, HandleValue::fromMarkedLocation(&value),
                               NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
 }
 
 static bool
-DefinePropertyByDescriptor(JSContext* cx, HandleObject obj, HandleId id,
-                           Handle<JSPropertyDescriptor> desc, ObjectOpResult& result)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, id, desc);
-    return DefineProperty(cx, obj, id, desc.value(), desc.getter(), desc.setter(),
-                          desc.attributes(), result);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id,
-                      Handle<JSPropertyDescriptor> desc, ObjectOpResult& result)
-{
+DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleValue value,
+               const JSNativeWrapper& getter, const JSNativeWrapper& setter,
+               unsigned attrs, unsigned flags)
+{
+    AutoRooterGetterSetter gsRoot(cx, attrs, const_cast<JSNative*>(&getter.op),
+                                  const_cast<JSNative*>(&setter.op));
+
+    JSAtom* atom = Atomize(cx, name, strlen(name));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+
+    return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleValue value,
+                  unsigned attrs,
+                  Native getter /* = nullptr */, Native setter /* = nullptr */)
+{
+    return DefineProperty(cx, obj, name, value, NativeOpWrapper(getter), NativeOpWrapper(setter),
+                          attrs, 0);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleObject valueArg,
+                  unsigned attrs,
+                  Native getter /* = nullptr */, Native setter /* = nullptr */)
+{
+    RootedValue value(cx, ObjectValue(*valueArg));
+    return DefineProperty(cx, obj, name, value, NativeOpWrapper(getter), NativeOpWrapper(setter),
+                          attrs, 0);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleString valueArg,
+                  unsigned attrs,
+                  Native getter /* = nullptr */, Native setter /* = nullptr */)
+{
+    RootedValue value(cx, StringValue(valueArg));
+    return DefineProperty(cx, obj, name, value, NativeOpWrapper(getter), NativeOpWrapper(setter),
+                          attrs, 0);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, int32_t valueArg,
+                  unsigned attrs,
+                  Native getter /* = nullptr */, Native setter /* = nullptr */)
+{
+    Value value = Int32Value(valueArg);
+    return DefineProperty(cx, obj, name, HandleValue::fromMarkedLocation(&value),
+                          NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, uint32_t valueArg,
+                  unsigned attrs,
+                  Native getter /* = nullptr */, Native setter /* = nullptr */)
+{
+    Value value = NumberValue(valueArg);
+    return DefineProperty(cx, obj, name, HandleValue::fromMarkedLocation(&value),
+                          NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, double valueArg,
+                  unsigned attrs,
+                  Native getter /* = nullptr */, Native setter /* = nullptr */)
+{
+    Value value = NumberValue(valueArg);
+    return DefineProperty(cx, obj, name, HandleValue::fromMarkedLocation(&value),
+                          NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
+}
+
+#define AUTO_NAMELEN(s,n)   (((n) == (size_t)-1) ? js_strlen(s) : (n))
+
+JS_PUBLIC_API(bool)
+JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                    Handle<JSPropertyDescriptor> desc,
+                    ObjectOpResult& result)
+{
+    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
     return DefinePropertyByDescriptor(cx, obj, id, desc, result);
 }
 
 JS_PUBLIC_API(bool)
-JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id,
-                      Handle<JSPropertyDescriptor> desc)
-{
+JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                    Handle<JSPropertyDescriptor> desc)
+{
+    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
     ObjectOpResult result;
     return DefinePropertyByDescriptor(cx, obj, id, desc, result) &&
            result.checkStrict(cx, obj, id);
 }
 
 static bool
+DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                 const Value& value_, Native getter, Native setter, unsigned attrs,
+                 unsigned flags)
+{
+    RootedValue value(cx, value_);
+    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
+    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return DefinePropertyById(cx, obj, id, value, NativeOpWrapper(getter), NativeOpWrapper(setter),
+                              attrs, flags);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                    HandleValue value, unsigned attrs, Native getter, Native setter)
+{
+    return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                    HandleObject valueArg, unsigned attrs, Native getter, Native setter)
+{
+    RootedValue value(cx, ObjectValue(*valueArg));
+    return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                    HandleString valueArg, unsigned attrs, Native getter, Native setter)
+{
+    RootedValue value(cx, StringValue(valueArg));
+    return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                    int32_t valueArg, unsigned attrs, Native getter, Native setter)
+{
+    Value value = Int32Value(valueArg);
+    return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value),
+                            getter, setter, attrs, 0);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                    uint32_t valueArg, unsigned attrs, Native getter, Native setter)
+{
+    Value value = NumberValue(valueArg);
+    return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value),
+                            getter, setter, attrs, 0);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                    double valueArg, unsigned attrs, Native getter, Native setter)
+{
+    Value value = NumberValue(valueArg);
+    return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value),
+                            getter, setter, attrs, 0);
+}
+
+static bool
 DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
               unsigned attrs, Native getter, Native setter)
 {
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
@@ -2549,30 +2643,546 @@ JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, double valueArg,
                  unsigned attrs, Native getter, Native setter)
 {
     Value value = NumberValue(valueArg);
     return DefineElement(cx, obj, index, HandleValue::fromMarkedLocation(&value),
                          attrs, getter, setter);
 }
 
+JS_PUBLIC_API(bool)
+JS_HasPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+
+    return HasProperty(cx, obj, id, foundp);
+}
+
+JS_PUBLIC_API(bool)
+JS_HasProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp)
+{
+    JSAtom* atom = Atomize(cx, name, strlen(name));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return JS_HasPropertyById(cx, obj, id, foundp);
+}
+
+JS_PUBLIC_API(bool)
+JS_HasUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen, bool* foundp)
+{
+    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return JS_HasPropertyById(cx, obj, id, foundp);
+}
+
+JS_PUBLIC_API(bool)
+JS_HasElement(JSContext* cx, HandleObject obj, uint32_t index, bool* foundp)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    RootedId id(cx);
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return JS_HasPropertyById(cx, obj, id, foundp);
+}
+
+JS_PUBLIC_API(bool)
+JS_HasOwnPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, id);
+
+    return HasOwnProperty(cx, obj, id, foundp);
+}
+
+JS_PUBLIC_API(bool)
+JS_HasOwnProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp)
+{
+    JSAtom* atom = Atomize(cx, name, strlen(name));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return JS_HasOwnPropertyById(cx, obj, id, foundp);
+}
+
+JS_PUBLIC_API(bool)
+JS_ForwardGetPropertyTo(JSContext* cx, HandleObject obj, HandleId id, HandleValue receiver,
+                        MutableHandleValue vp)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, id, receiver);
+
+    return GetProperty(cx, obj, receiver, id, vp);
+}
+
+JS_PUBLIC_API(bool)
+JS_ForwardGetElementTo(JSContext* cx, HandleObject obj, uint32_t index, HandleObject receiver,
+                       MutableHandleValue vp)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj);
+
+    return GetElement(cx, obj, receiver, index, vp);
+}
+
+JS_PUBLIC_API(bool)
+JS_GetPropertyById(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
+{
+    RootedValue receiver(cx, ObjectValue(*obj));
+    return JS_ForwardGetPropertyTo(cx, obj, id, receiver, vp);
+}
+
+JS_PUBLIC_API(bool)
+JS_GetProperty(JSContext* cx, HandleObject obj, const char* name, MutableHandleValue vp)
+{
+    JSAtom* atom = Atomize(cx, name, strlen(name));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return JS_GetPropertyById(cx, obj, id, vp);
+}
+
+JS_PUBLIC_API(bool)
+JS_GetUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                 MutableHandleValue vp)
+{
+    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return JS_GetPropertyById(cx, obj, id, vp);
+}
+
+JS_PUBLIC_API(bool)
+JS_GetElement(JSContext* cx, HandleObject objArg, uint32_t index, MutableHandleValue vp)
+{
+    return JS_ForwardGetElementTo(cx, objArg, index, objArg, vp);
+}
+
+JS_PUBLIC_API(bool)
+JS_ForwardSetPropertyTo(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
+                        HandleValue receiver, ObjectOpResult& result)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, id, receiver);
+
+    return SetProperty(cx, obj, id, v, receiver, result);
+}
+
+JS_PUBLIC_API(bool)
+JS_SetPropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, id);
+
+    RootedValue receiver(cx, ObjectValue(*obj));
+    ObjectOpResult ignored;
+    return SetProperty(cx, obj, id, v, receiver, ignored);
+}
+
+JS_PUBLIC_API(bool)
+JS_SetProperty(JSContext* cx, HandleObject obj, const char* name, HandleValue v)
+{
+    JSAtom* atom = Atomize(cx, name, strlen(name));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return JS_SetPropertyById(cx, obj, id, v);
+}
+
+JS_PUBLIC_API(bool)
+JS_SetUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                 HandleValue v)
+{
+    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return JS_SetPropertyById(cx, obj, id, v);
+}
+
 static bool
-DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleValue value,
-               const JSNativeWrapper& getter, const JSNativeWrapper& setter,
-               unsigned attrs, unsigned flags)
-{
-    AutoRooterGetterSetter gsRoot(cx, attrs, const_cast<JSNative*>(&getter.op),
-                                  const_cast<JSNative*>(&setter.op));
+SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, v);
+
+    RootedValue receiver(cx, ObjectValue(*obj));
+    ObjectOpResult ignored;
+    return SetElement(cx, obj, index, v, receiver, ignored);
+}
+
+JS_PUBLIC_API(bool)
+JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v)
+{
+    return SetElement(cx, obj, index, v);
+}
+
+JS_PUBLIC_API(bool)
+JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleObject v)
+{
+    RootedValue value(cx, ObjectOrNullValue(v));
+    return SetElement(cx, obj, index, value);
+}
+
+JS_PUBLIC_API(bool)
+JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleString v)
+{
+    RootedValue value(cx, StringValue(v));
+    return SetElement(cx, obj, index, value);
+}
+
+JS_PUBLIC_API(bool)
+JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, int32_t v)
+{
+    RootedValue value(cx, NumberValue(v));
+    return SetElement(cx, obj, index, value);
+}
+
+JS_PUBLIC_API(bool)
+JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, uint32_t v)
+{
+    RootedValue value(cx, NumberValue(v));
+    return SetElement(cx, obj, index, value);
+}
+
+JS_PUBLIC_API(bool)
+JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, double v)
+{
+    RootedValue value(cx, NumberValue(v));
+    return SetElement(cx, obj, index, value);
+}
+
+JS_PUBLIC_API(bool)
+JS_DeletePropertyById(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, id);
+
+    return DeleteProperty(cx, obj, id, result);
+}
+
+JS_PUBLIC_API(bool)
+JS_DeleteProperty(JSContext* cx, HandleObject obj, const char* name, ObjectOpResult& result)
+{
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj);
 
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
-
-    return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags);
+    return DeleteProperty(cx, obj, id, result);
+}
+
+JS_PUBLIC_API(bool)
+JS_DeleteUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                    ObjectOpResult& result)
+{
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj);
+
+    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return DeleteProperty(cx, obj, id, result);
+}
+
+JS_PUBLIC_API(bool)
+JS_DeleteElement(JSContext* cx, HandleObject obj, uint32_t index, ObjectOpResult& result)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj);
+
+    return DeleteElement(cx, obj, index, result);
+}
+
+JS_PUBLIC_API(bool)
+JS_DeletePropertyById(JSContext* cx, HandleObject obj, HandleId id)
+{
+    ObjectOpResult ignored;
+    return JS_DeletePropertyById(cx, obj, id, ignored);
+}
+
+JS_PUBLIC_API(bool)
+JS_DeleteProperty(JSContext* cx, HandleObject obj, const char* name)
+{
+    ObjectOpResult ignored;
+    return JS_DeleteProperty(cx, obj, name, ignored);
+}
+
+JS_PUBLIC_API(bool)
+JS_DeleteElement(JSContext* cx, HandleObject obj, uint32_t index)
+{
+    ObjectOpResult ignored;
+    return JS_DeleteElement(cx, obj, index, ignored);
+}
+
+JS_PUBLIC_API(bool)
+JS_Enumerate(JSContext* cx, HandleObject obj, JS::MutableHandle<IdVector> props)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj);
+    MOZ_ASSERT(props.empty());
+
+    AutoIdVector ids(cx);
+    if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &ids))
+        return false;
+
+    return props.append(ids.begin(), ids.end());
+}
+
+JS_PUBLIC_API(bool)
+JS::IsCallable(JSObject* obj)
+{
+    return obj->isCallable();
+}
+
+JS_PUBLIC_API(bool)
+JS::IsConstructor(JSObject* obj)
+{
+    return obj->isConstructor();
+}
+
+struct AutoLastFrameCheck
+{
+    explicit AutoLastFrameCheck(JSContext* cx
+                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : cx(cx)
+    {
+        MOZ_ASSERT(cx);
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    ~AutoLastFrameCheck() {
+        if (cx->isExceptionPending() &&
+            !JS_IsRunning(cx) &&
+            (!cx->options().dontReportUncaught() && !cx->options().autoJSAPIOwnsErrorReporting())) {
+            ReportUncaughtException(cx);
+        }
+    }
+
+  private:
+    JSContext* cx;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+JS_PUBLIC_API(bool)
+JS_CallFunctionValue(JSContext* cx, HandleObject obj, HandleValue fval, const HandleValueArray& args,
+                     MutableHandleValue rval)
+{
+    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, fval, args);
+    AutoLastFrameCheck lfc(cx);
+
+    return Invoke(cx, ObjectOrNullValue(obj), fval, args.length(), args.begin(), rval);
+}
+
+JS_PUBLIC_API(bool)
+JS_CallFunction(JSContext* cx, HandleObject obj, HandleFunction fun, const HandleValueArray& args,
+                MutableHandleValue rval)
+{
+    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, fun, args);
+    AutoLastFrameCheck lfc(cx);
+
+    return Invoke(cx, ObjectOrNullValue(obj), ObjectValue(*fun), args.length(), args.begin(), rval);
+}
+
+JS_PUBLIC_API(bool)
+JS_CallFunctionName(JSContext* cx, HandleObject obj, const char* name, const HandleValueArray& args,
+                    MutableHandleValue rval)
+{
+    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, args);
+    AutoLastFrameCheck lfc(cx);
+
+    JSAtom* atom = Atomize(cx, name, strlen(name));
+    if (!atom)
+        return false;
+
+    RootedValue v(cx);
+    RootedId id(cx, AtomToId(atom));
+    if (!GetProperty(cx, obj, obj, id, &v))
+        return false;
+
+    return Invoke(cx, ObjectOrNullValue(obj), v, args.length(), args.begin(), rval);
+}
+
+JS_PUBLIC_API(bool)
+JS::Call(JSContext* cx, HandleValue thisv, HandleValue fval, const JS::HandleValueArray& args,
+         MutableHandleValue rval)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, thisv, fval, args);
+    AutoLastFrameCheck lfc(cx);
+
+    return Invoke(cx, thisv, fval, args.length(), args.begin(), rval);
+}
+
+JS_PUBLIC_API(bool)
+JS::Construct(JSContext* cx, HandleValue fval, HandleObject newTarget, const JS::HandleValueArray& args,
+              MutableHandleValue rval)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, fval, newTarget, args);
+    AutoLastFrameCheck lfc(cx);
+
+    if (!IsConstructor(fval)) {
+        ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr);
+        return false;
+    }
+
+    RootedValue newTargetVal(cx, ObjectValue(*newTarget));
+    if (!IsConstructor(newTargetVal)) {
+        ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, newTargetVal, nullptr);
+        return false;
+    }
+
+    ConstructArgs cargs(cx);
+    if (!FillArgumentsFromArraylike(cx, cargs, args))
+        return false;
+
+    return js::Construct(cx, fval, cargs, newTargetVal, rval);
+}
+
+JS_PUBLIC_API(bool)
+JS::Construct(JSContext* cx, HandleValue fval, const JS::HandleValueArray& args,
+              MutableHandleValue rval)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, fval, args);
+    AutoLastFrameCheck lfc(cx);
+
+    if (!IsConstructor(fval)) {
+        ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr);
+        return false;
+    }
+
+    ConstructArgs cargs(cx);
+    if (!FillArgumentsFromArraylike(cx, cargs, args))
+        return false;
+
+    return js::Construct(cx, fval, cargs, fval, rval);
+}
+
+
+/* * */
+
+JS_PUBLIC_API(bool)
+JS_AlreadyHasOwnPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, id);
+
+    if (!obj->isNative())
+        return js::HasOwnProperty(cx, obj, id, foundp);
+
+    RootedNativeObject nativeObj(cx, &obj->as<NativeObject>());
+    RootedShape prop(cx);
+    NativeLookupOwnPropertyNoResolve(cx, nativeObj, id, &prop);
+    *foundp = !!prop;
+    return true;
+}
+
+JS_PUBLIC_API(bool)
+JS_AlreadyHasOwnProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp)
+{
+    JSAtom* atom = Atomize(cx, name, strlen(name));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
+}
+
+JS_PUBLIC_API(bool)
+JS_AlreadyHasOwnUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
+                           bool* foundp)
+{
+    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
+}
+
+JS_PUBLIC_API(bool)
+JS_AlreadyHasOwnElement(JSContext* cx, HandleObject obj, uint32_t index, bool* foundp)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    RootedId id(cx);
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
+}
+
+JS_PUBLIC_API(bool)
+JS_FreezeObject(JSContext* cx, HandleObject obj)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj);
+    return FreezeObject(cx, obj);
+}
+
+JS_PUBLIC_API(bool)
+JS_DeepFreezeObject(JSContext* cx, HandleObject obj)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj);
+
+    /* Assume that non-extensible objects are already deep-frozen, to avoid divergence. */
+    bool extensible;
+    if (!IsExtensible(cx, obj, &extensible))
+        return false;
+    if (!extensible)
+        return true;
+
+    if (!FreezeObject(cx, obj))
+        return false;
+
+    /* Walk slots in obj and if any value is a non-null object, seal it. */
+    if (obj->isNative()) {
+        for (uint32_t i = 0, n = obj->as<NativeObject>().slotSpan(); i < n; ++i) {
+            const Value& v = obj->as<NativeObject>().getSlot(i);
+            if (v.isPrimitive())
+                continue;
+            RootedObject obj(cx, &v.toObject());
+            if (!JS_DeepFreezeObject(cx, obj))
+                return false;
+        }
+    }
+
+    return true;
 }
 
 static bool
 DefineSelfHostedProperty(JSContext* cx, HandleObject obj, HandleId id,
                          const char* getterName, const char* setterName,
                          unsigned attrs, unsigned flags)
 {
     JSAtom* getterNameAtom = Atomize(cx, getterName, strlen(getterName));
@@ -2612,171 +3222,16 @@ DefineSelfHostedProperty(JSContext* cx, 
     }
     JSNative setterOp = JS_DATA_TO_FUNC_PTR(JSNative, setterFunc.get());
 
     return DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue,
                               NativeOpWrapper(getterOp), NativeOpWrapper(setterOp),
                               attrs, flags);
 }
 
-JS_PUBLIC_API(bool)
-JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleValue value,
-                  unsigned attrs,
-                  Native getter /* = nullptr */, Native setter /* = nullptr */)
-{
-    return DefineProperty(cx, obj, name, value, NativeOpWrapper(getter), NativeOpWrapper(setter),
-                          attrs, 0);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleObject valueArg,
-                  unsigned attrs,
-                  Native getter /* = nullptr */, Native setter /* = nullptr */)
-{
-    RootedValue value(cx, ObjectValue(*valueArg));
-    return DefineProperty(cx, obj, name, value, NativeOpWrapper(getter), NativeOpWrapper(setter),
-                          attrs, 0);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleString valueArg,
-                  unsigned attrs,
-                  Native getter /* = nullptr */, Native setter /* = nullptr */)
-{
-    RootedValue value(cx, StringValue(valueArg));
-    return DefineProperty(cx, obj, name, value, NativeOpWrapper(getter), NativeOpWrapper(setter),
-                          attrs, 0);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, int32_t valueArg,
-                  unsigned attrs,
-                  Native getter /* = nullptr */, Native setter /* = nullptr */)
-{
-    Value value = Int32Value(valueArg);
-    return DefineProperty(cx, obj, name, HandleValue::fromMarkedLocation(&value),
-                          NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, uint32_t valueArg,
-                  unsigned attrs,
-                  Native getter /* = nullptr */, Native setter /* = nullptr */)
-{
-    Value value = NumberValue(valueArg);
-    return DefineProperty(cx, obj, name, HandleValue::fromMarkedLocation(&value),
-                          NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, double valueArg,
-                  unsigned attrs,
-                  Native getter /* = nullptr */, Native setter /* = nullptr */)
-{
-    Value value = NumberValue(valueArg);
-    return DefineProperty(cx, obj, name, HandleValue::fromMarkedLocation(&value),
-                          NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0);
-}
-
-static bool
-DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                 const Value& value_, Native getter, Native setter, unsigned attrs,
-                 unsigned flags)
-{
-    RootedValue value(cx, value_);
-    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return DefinePropertyById(cx, obj, id, value, NativeOpWrapper(getter), NativeOpWrapper(setter),
-                              attrs, flags);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                    HandleValue value, unsigned attrs,
-                    Native getter, Native setter)
-{
-    return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                    HandleObject valueArg, unsigned attrs,
-                    Native getter, Native setter)
-{
-    RootedValue value(cx, ObjectValue(*valueArg));
-    return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                    HandleString valueArg, unsigned attrs,
-                    Native getter, Native setter)
-{
-    RootedValue value(cx, StringValue(valueArg));
-    return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs, 0);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                    int32_t valueArg, unsigned attrs,
-                    Native getter, Native setter)
-{
-    Value value = Int32Value(valueArg);
-    return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value),
-                            getter, setter, attrs, 0);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                    uint32_t valueArg, unsigned attrs,
-                    Native getter, Native setter)
-{
-    Value value = NumberValue(valueArg);
-    return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value),
-                            getter, setter, attrs, 0);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                    double valueArg, unsigned attrs,
-                    Native getter, Native setter)
-{
-    Value value = NumberValue(valueArg);
-    return DefineUCProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value),
-                            getter, setter, attrs, 0);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                    Handle<JSPropertyDescriptor> desc,
-                    ObjectOpResult& result)
-{
-    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return DefinePropertyByDescriptor(cx, obj, id, desc, result);
-}
-
-JS_PUBLIC_API(bool)
-JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                    Handle<JSPropertyDescriptor> desc)
-{
-    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    ObjectOpResult result;
-    return DefinePropertyByDescriptor(cx, obj, id, desc, result) &&
-           result.checkStrict(cx, obj, id);
-}
-
 JS_PUBLIC_API(JSObject*)
 JS_DefineObject(JSContext* cx, HandleObject obj, const char* name, const JSClass* jsclasp,
                 unsigned attrs)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
@@ -2907,314 +3362,16 @@ JS::ObjectToCompletePropertyDescriptor(J
 {
     if (!ToPropertyDescriptor(cx, descObj, true, desc))
         return false;
     CompletePropertyDescriptor(desc);
     desc.object().set(obj);
     return true;
 }
 
-JS_PUBLIC_API(bool)
-JS_GetOwnPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id,
-                                MutableHandle<JSPropertyDescriptor> desc)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-
-    return GetOwnPropertyDescriptor(cx, obj, id, desc);
-}
-
-JS_PUBLIC_API(bool)
-JS_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name,
-                            MutableHandle<JSPropertyDescriptor> desc)
-{
-    JSAtom* atom = Atomize(cx, name, strlen(name));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return JS_GetOwnPropertyDescriptorById(cx, obj, id, desc);
-}
-
-JS_PUBLIC_API(bool)
-JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* name,
-                              MutableHandle<JSPropertyDescriptor> desc)
-{
-    JSAtom* atom = AtomizeChars(cx, name, js_strlen(name));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return JS_GetOwnPropertyDescriptorById(cx, obj, id, desc);
-}
-
-JS_PUBLIC_API(bool)
-JS_HasOwnPropertyById(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, id);
-
-    return HasOwnProperty(cx, obj, id, foundp);
-}
-
-JS_PUBLIC_API(bool)
-JS_HasOwnProperty(JSContext* cx, HandleObject obj, const char* name, bool* foundp)
-{
-    JSAtom* atom = Atomize(cx, name, strlen(name));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return JS_HasOwnPropertyById(cx, obj, id, foundp);
-}
-
-JS_PUBLIC_API(bool)
-JS_GetPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id,
-                             MutableHandle<JSPropertyDescriptor> desc)
-{
-    return GetPropertyDescriptor(cx, obj, id, desc);
-}
-
-JS_PUBLIC_API(bool)
-JS_GetPropertyDescriptor(JSContext* cx, HandleObject obj, const char* name,
-                         MutableHandle<JSPropertyDescriptor> desc)
-{
-    JSAtom* atom = Atomize(cx, name, strlen(name));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return atom && JS_GetPropertyDescriptorById(cx, obj, id, desc);
-}
-
-JS_PUBLIC_API(bool)
-JS_GetPropertyById(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, id);
-
-    return GetProperty(cx, obj, obj, id, vp);
-}
-
-JS_PUBLIC_API(bool)
-JS_ForwardGetPropertyTo(JSContext* cx, HandleObject obj, HandleId id, HandleValue onBehalfOf,
-                        MutableHandleValue vp)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, id, onBehalfOf);
-
-    return GetProperty(cx, obj, onBehalfOf, id, vp);
-}
-
-JS_PUBLIC_API(bool)
-JS_GetElement(JSContext* cx, HandleObject obj, uint32_t index, MutableHandleValue vp)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj);
-
-    return GetElement(cx, obj, obj, index, vp);
-}
-
-JS_PUBLIC_API(bool)
-JS_ForwardGetElementTo(JSContext* cx, HandleObject obj, uint32_t index, HandleObject onBehalfOf,
-                       MutableHandleValue vp)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj);
-
-    return GetElement(cx, obj, onBehalfOf, index, vp);
-}
-
-JS_PUBLIC_API(bool)
-JS_GetProperty(JSContext* cx, HandleObject obj, const char* name, MutableHandleValue vp)
-{
-    JSAtom* atom = Atomize(cx, name, strlen(name));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return JS_GetPropertyById(cx, obj, id, vp);
-}
-
-JS_PUBLIC_API(bool)
-JS_GetUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                 MutableHandleValue vp)
-{
-    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return JS_GetPropertyById(cx, obj, id, vp);
-}
-
-JS_PUBLIC_API(bool)
-JS_SetPropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, id);
-
-    RootedValue receiver(cx, ObjectValue(*obj));
-    ObjectOpResult ignored;
-    return SetProperty(cx, obj, id, v, receiver, ignored);
-}
-
-JS_PUBLIC_API(bool)
-JS_ForwardSetPropertyTo(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
-                        HandleValue receiver, ObjectOpResult& result)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, id, receiver);
-
-    return SetProperty(cx, obj, id, v, receiver, result);
-}
-
-static bool
-SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, v);
-
-    RootedValue receiver(cx, ObjectValue(*obj));
-    ObjectOpResult ignored;
-    return SetElement(cx, obj, index, v, receiver, ignored);
-}
-
-JS_PUBLIC_API(bool)
-JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v)
-{
-    return SetElement(cx, obj, index, v);
-}
-
-JS_PUBLIC_API(bool)
-JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleObject v)
-{
-    RootedValue value(cx, ObjectOrNullValue(v));
-    return SetElement(cx, obj, index, value);
-}
-
-JS_PUBLIC_API(bool)
-JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleString v)
-{
-    RootedValue value(cx, StringValue(v));
-    return SetElement(cx, obj, index, value);
-}
-
-JS_PUBLIC_API(bool)
-JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, int32_t v)
-{
-    RootedValue value(cx, NumberValue(v));
-    return SetElement(cx, obj, index, value);
-}
-
-JS_PUBLIC_API(bool)
-JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, uint32_t v)
-{
-    RootedValue value(cx, NumberValue(v));
-    return SetElement(cx, obj, index, value);
-}
-
-JS_PUBLIC_API(bool)
-JS_SetElement(JSContext* cx, HandleObject obj, uint32_t index, double v)
-{
-    RootedValue value(cx, NumberValue(v));
-    return SetElement(cx, obj, index, value);
-}
-
-JS_PUBLIC_API(bool)
-JS_SetProperty(JSContext* cx, HandleObject obj, const char* name, HandleValue v)
-{
-    JSAtom* atom = Atomize(cx, name, strlen(name));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return JS_SetPropertyById(cx, obj, id, v);
-}
-
-JS_PUBLIC_API(bool)
-JS_SetUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                 HandleValue v)
-{
-    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return JS_SetPropertyById(cx, obj, id, v);
-}
-
-JS_PUBLIC_API(bool)
-JS_DeletePropertyById(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, id);
-
-    return DeleteProperty(cx, obj, id, result);
-}
-
-JS_PUBLIC_API(bool)
-JS_DeleteElement(JSContext* cx, HandleObject obj, uint32_t index, ObjectOpResult& result)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj);
-
-    return DeleteElement(cx, obj, index, result);
-}
-
-JS_PUBLIC_API(bool)
-JS_DeleteProperty(JSContext* cx, HandleObject obj, const char* name, ObjectOpResult& result)
-{
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj);
-
-    JSAtom* atom = Atomize(cx, name, strlen(name));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return DeleteProperty(cx, obj, id, result);
-}
-
-JS_PUBLIC_API(bool)
-JS_DeleteUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                    ObjectOpResult& result)
-{
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj);
-
-    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return DeleteProperty(cx, obj, id, result);
-}
-
-JS_PUBLIC_API(bool)
-JS_DeletePropertyById(JSContext* cx, HandleObject obj, HandleId id)
-{
-    ObjectOpResult ignored;
-    return JS_DeletePropertyById(cx, obj, id, ignored);
-}
-
-JS_PUBLIC_API(bool)
-JS_DeleteElement(JSContext* cx, HandleObject obj, uint32_t index)
-{
-    ObjectOpResult ignored;
-    return JS_DeleteElement(cx, obj, index, ignored);
-}
-
-JS_PUBLIC_API(bool)
-JS_DeleteProperty(JSContext* cx, HandleObject obj, const char* name)
-{
-    ObjectOpResult ignored;
-    return JS_DeleteProperty(cx, obj, name, ignored);
-}
-
 JS_PUBLIC_API(void)
 JS_SetAllNonReservedSlotsToUndefined(JSContext* cx, JSObject* objArg)
 {
     RootedObject obj(cx, objArg);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
@@ -3223,31 +3380,16 @@ JS_SetAllNonReservedSlotsToUndefined(JSC
 
     const Class* clasp = obj->getClass();
     unsigned numReserved = JSCLASS_RESERVED_SLOTS(clasp);
     unsigned numSlots = obj->as<NativeObject>().slotSpan();
     for (unsigned i = numReserved; i < numSlots; i++)
         obj->as<NativeObject>().setSlot(i, UndefinedValue());
 }
 
-JS_PUBLIC_API(bool)
-JS_Enumerate(JSContext* cx, HandleObject obj, JS::MutableHandle<IdVector> props)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj);
-    MOZ_ASSERT(props.empty());
-
-    AutoIdVector ids(cx);
-    if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &ids))
-        return false;
-
-    return props.append(ids.begin(), ids.end());
-}
-
 JS_PUBLIC_API(Value)
 JS_GetReservedSlot(JSObject* obj, uint32_t index)
 {
     return obj->as<NativeObject>().getReservedSlot(index);
 }
 
 JS_PUBLIC_API(void)
 JS_SetReservedSlot(JSObject* obj, uint32_t index, Value value)
@@ -3639,32 +3781,16 @@ JS_GetFunctionDisplayId(JSFunction* fun)
 }
 
 JS_PUBLIC_API(uint16_t)
 JS_GetFunctionArity(JSFunction* fun)
 {
     return fun->nargs();
 }
 
-namespace JS {
-
-JS_PUBLIC_API(bool)
-IsCallable(JSObject* obj)
-{
-    return obj->isCallable();
-}
-
-JS_PUBLIC_API(bool)
-IsConstructor(JSObject* obj)
-{
-    return obj->isConstructor();
-}
-
-} /* namespace JS */
-
 JS_PUBLIC_API(bool)
 JS_ObjectIsFunction(JSContext* cx, JSObject* obj)
 {
     return obj->is<JSFunction>();
 }
 
 JS_PUBLIC_API(bool)
 JS_IsNativeFunction(JSObject* funobj, JSNative call)
@@ -3829,39 +3955,16 @@ JS_DefineFunctionById(JSContext* cx, Han
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     return DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
-struct AutoLastFrameCheck
-{
-    explicit AutoLastFrameCheck(JSContext* cx
-                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : cx(cx)
-    {
-        MOZ_ASSERT(cx);
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    ~AutoLastFrameCheck() {
-        if (cx->isExceptionPending() &&
-            !JS_IsRunning(cx) &&
-            (!cx->options().dontReportUncaught() && !cx->options().autoJSAPIOwnsErrorReporting())) {
-            ReportUncaughtException(cx);
-        }
-    }
-
-  private:
-    JSContext* cx;
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 /* Use the fastest available getc. */
 #if defined(HAVE_GETC_UNLOCKED)
 # define fast_getc getc_unlocked
 #elif defined(HAVE__GETC_NOLOCK)
 # define fast_getc _getc_nolock
 #else
 # define fast_getc getc
 #endif
@@ -4704,124 +4807,16 @@ JS::Evaluate(JSContext* cx, AutoObjectVe
 
 JS_PUBLIC_API(bool)
 JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
              const char* filename, MutableHandleValue rval)
 {
     return ::Evaluate(cx, optionsArg, filename, rval);
 }
 
-JS_PUBLIC_API(bool)
-JS_CallFunction(JSContext* cx, HandleObject obj, HandleFunction fun, const HandleValueArray& args,
-                MutableHandleValue rval)
-{
-    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, fun, args);
-    AutoLastFrameCheck lfc(cx);
-
-    return Invoke(cx, ObjectOrNullValue(obj), ObjectValue(*fun), args.length(), args.begin(), rval);
-}
-
-JS_PUBLIC_API(bool)
-JS_CallFunctionName(JSContext* cx, HandleObject obj, const char* name, const HandleValueArray& args,
-                    MutableHandleValue rval)
-{
-    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, args);
-    AutoLastFrameCheck lfc(cx);
-
-    JSAtom* atom = Atomize(cx, name, strlen(name));
-    if (!atom)
-        return false;
-
-    RootedValue v(cx);
-    RootedId id(cx, AtomToId(atom));
-    if (!GetProperty(cx, obj, obj, id, &v))
-        return false;
-
-    return Invoke(cx, ObjectOrNullValue(obj), v, args.length(), args.begin(), rval);
-}
-
-JS_PUBLIC_API(bool)
-JS_CallFunctionValue(JSContext* cx, HandleObject obj, HandleValue fval, const HandleValueArray& args,
-                     MutableHandleValue rval)
-{
-    MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, fval, args);
-    AutoLastFrameCheck lfc(cx);
-
-    return Invoke(cx, ObjectOrNullValue(obj), fval, args.length(), args.begin(), rval);
-}
-
-JS_PUBLIC_API(bool)
-JS::Call(JSContext* cx, HandleValue thisv, HandleValue fval, const JS::HandleValueArray& args,
-         MutableHandleValue rval)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, thisv, fval, args);
-    AutoLastFrameCheck lfc(cx);
-
-    return Invoke(cx, thisv, fval, args.length(), args.begin(), rval);
-}
-
-JS_PUBLIC_API(bool)
-JS::Construct(JSContext* cx, HandleValue fval, const JS::HandleValueArray& args,
-              MutableHandleValue rval)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, fval, args);
-    AutoLastFrameCheck lfc(cx);
-
-    if (!IsConstructor(fval)) {
-        ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr);
-        return false;
-    }
-
-    ConstructArgs cargs(cx);
-    if (!FillArgumentsFromArraylike(cx, cargs, args))
-        return false;
-
-    return js::Construct(cx, fval, cargs, fval, rval);
-}
-
-JS_PUBLIC_API(bool)
-JS::Construct(JSContext* cx, HandleValue fval, HandleObject newTarget, const JS::HandleValueArray& args,
-              MutableHandleValue rval)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, fval, newTarget, args);
-    AutoLastFrameCheck lfc(cx);
-
-    if (!IsConstructor(fval)) {
-        ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr);
-        return false;
-    }
-
-    RootedValue newTargetVal(cx, ObjectValue(*newTarget));
-    if (!IsConstructor(newTargetVal)) {
-        ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, newTargetVal, nullptr);
-        return false;
-    }
-
-    ConstructArgs cargs(cx);
-    if (!FillArgumentsFromArraylike(cx, cargs, args))
-        return false;
-
-    return js::Construct(cx, fval, cargs, newTargetVal, rval);
-}
-
 static JSObject*
 JS_NewHelper(JSContext* cx, HandleObject ctor, const JS::HandleValueArray& inputArgs)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, ctor, inputArgs);
 
     RootedValue ctorVal(cx, ObjectValue(*ctor));
@@ -6385,22 +6380,16 @@ JS_DecodeInterpretedFunction(JSContext* 
 {
     XDRDecoder decoder(cx, data, length);
     RootedFunction funobj(cx);
     if (!decoder.codeFunction(&funobj))
         return nullptr;
     return funobj;
 }
 
-JS_PUBLIC_API(bool)
-JS_SetImmutablePrototype(JSContext *cx, JS::HandleObject obj, bool *succeeded)
-{
-    return SetImmutablePrototype(cx, obj, succeeded);
-}
-
 JS_PUBLIC_API(void)
 JS::SetAsmJSCacheOps(JSRuntime* rt, const JS::AsmJSCacheOps* ops)
 {
     rt->asmJSCacheOps = *ops;
 }
 
 char*
 JSAutoByteString::encodeLatin1(ExclusiveContext* cx, JSString* str)
@@ -6444,9 +6433,8 @@ JS::GetObjectZone(JSObject* obj)
 
 JS_PUBLIC_API(void)
 JS::ResetTimeZone()
 {
 #if ENABLE_INTL_API && defined(ICU_TZ_HAS_RECREATE_DEFAULT)
     icu::TimeZone::recreateDefault();
 #endif
 }
-
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2181,22 +2181,16 @@ JS_GetPrivate(JSObject* obj);
 
 extern JS_PUBLIC_API(void)
 JS_SetPrivate(JSObject* obj, void* data);
 
 extern JS_PUBLIC_API(void*)
 JS_GetInstancePrivate(JSContext* cx, JS::Handle<JSObject*> obj, const JSClass* clasp,
                       JS::CallArgs* args);
 
-extern JS_PUBLIC_API(bool)
-JS_GetPrototype(JSContext* cx, JS::HandleObject obj, JS::MutableHandleObject protop);
-
-extern JS_PUBLIC_API(bool)
-JS_SetPrototype(JSContext* cx, JS::HandleObject obj, JS::HandleObject proto);
-
 extern JS_PUBLIC_API(JSObject*)
 JS_GetConstructor(JSContext* cx, JS::Handle<JSObject*> proto);
 
 namespace JS {
 
 enum ZoneSpecifier {
     FreshZone = 0,
     SystemZone = 1
@@ -2412,20 +2406,16 @@ extern JS_PUBLIC_API(void)
 JS_GlobalObjectTraceHook(JSTracer* trc, JSObject* global);
 
 extern JS_PUBLIC_API(void)
 JS_FireOnNewGlobalObject(JSContext* cx, JS::HandleObject global);
 
 extern JS_PUBLIC_API(JSObject*)
 JS_NewObject(JSContext* cx, const JSClass* clasp);
 
-/* Queries the [[Extensible]] property of the object. */
-extern JS_PUBLIC_API(bool)
-JS_IsExtensible(JSContext* cx, JS::HandleObject obj, bool* extensible);
-
 extern JS_PUBLIC_API(bool)
 JS_IsNative(JSObject* obj);
 
 extern JS_PUBLIC_API(JSRuntime*)
 JS_GetObjectRuntime(JSObject* obj);
 
 /*
  * Unlike JS_NewObject, JS_NewObjectWithGivenProto does not compute a default
@@ -2447,39 +2437,16 @@ extern JS_PUBLIC_API(bool)
 JS_DeepFreezeObject(JSContext* cx, JS::Handle<JSObject*> obj);
 
 /*
  * Freezes an object; see ES5's Object.freeze(obj) method.
  */
 extern JS_PUBLIC_API(bool)
 JS_FreezeObject(JSContext* cx, JS::Handle<JSObject*> obj);
 
-/*
- * Attempt to make |obj| non-extensible.  If an error occurs while making the
- * attempt, return false (with a pending exception set, depending upon the
- * nature of the error).  If no error occurs, return true with |result| set
- * to indicate whether the attempt successfully set the [[Extensible]] property
- * to false.
- */
-extern JS_PUBLIC_API(bool)
-JS_PreventExtensions(JSContext* cx, JS::HandleObject obj, JS::ObjectOpResult& result);
-
-/*
- * Attempt to make the [[Prototype]] of |obj| immutable, such that any attempt
- * to modify it will fail.  If an error occurs during the attempt, return false
- * (with a pending exception set, depending upon the nature of the error).  If
- * no error occurs, return true with |*succeeded| set to indicate whether the
- * attempt successfully made the [[Prototype]] immutable.
- */
-extern JS_PUBLIC_API(bool)
-JS_SetImmutablePrototype(JSContext* cx, JS::HandleObject obj, bool* succeeded);
-
-extern JS_PUBLIC_API(JSObject*)
-JS_New(JSContext* cx, JS::HandleObject ctor, const JS::HandleValueArray& args);
-
 
 /*** Property descriptors ************************************************************************/
 
 struct JSPropertyDescriptor : public JS::Traceable {
     JSObject* obj;
     unsigned attrs;
     JSGetterOp getter;
     JSSetterOp setter;
@@ -2763,183 +2730,203 @@ extern JS_PUBLIC_API(bool)
 ObjectToCompletePropertyDescriptor(JSContext* cx,
                                    JS::HandleObject obj,
                                    JS::HandleValue descriptor,
                                    JS::MutableHandle<JSPropertyDescriptor> desc);
 
 } // namespace JS
 
 
-/*** [[DefineOwnProperty]] and variations ********************************************************/
-
-extern JS_PUBLIC_API(bool)
-JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleValue value,
-                  unsigned attrs,
-                  JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleObject value,
-                  unsigned attrs,
-                  JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleString value,
-                  unsigned attrs,
-                  JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, int32_t value,
-                  unsigned attrs,
-                  JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, uint32_t value,
-                  unsigned attrs,
-                  JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, double value,
-                  unsigned attrs,
-                  JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value,
-                      unsigned attrs,
-                      JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject value,
-                      unsigned attrs,
-                      JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleString value,
-                      unsigned attrs,
-                      JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, int32_t value,
-                      unsigned attrs,
-                      JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, uint32_t value,
-                      unsigned attrs,
-                      JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, double value,
-                      unsigned attrs,
-                      JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
-                      JS::Handle<JSPropertyDescriptor> desc,
-                      JS::ObjectOpResult& result);
-
-extern JS_PUBLIC_API(bool)
-JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
-                      JS::Handle<JSPropertyDescriptor> desc);
-
-extern JS_PUBLIC_API(JSObject*)
-JS_DefineObject(JSContext* cx, JS::HandleObject obj, const char* name,
-                const JSClass* clasp = nullptr, unsigned attrs = 0);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineConstDoubles(JSContext* cx, JS::HandleObject obj, const JSConstDoubleSpec* cds);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineConstIntegers(JSContext* cx, JS::HandleObject obj, const JSConstIntegerSpec* cis);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineProperties(JSContext* cx, JS::HandleObject obj, const JSPropertySpec* ps);
-
-
-/* * */
-
-extern JS_PUBLIC_API(bool)
-JS_AlreadyHasOwnProperty(JSContext* cx, JS::HandleObject obj, const char* name,
-                         bool* foundp);
-
-extern JS_PUBLIC_API(bool)
-JS_AlreadyHasOwnPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
-                             bool* foundp);
-
-extern JS_PUBLIC_API(bool)
-JS_HasProperty(JSContext* cx, JS::HandleObject obj, const char* name, bool* foundp);
-
-extern JS_PUBLIC_API(bool)
-JS_HasPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp);
-
-
+/*** Standard internal methods ********************************************************************
+ *
+ * The functions below are the fundamental operations on objects.
+ *
+ * ES6 specifies 14 internal methods that define how objects behave.  The
+ * standard is actually quite good on this topic, though you may have to read
+ * it a few times. See ES6 sections 6.1.7.2 and 6.1.7.3.
+ *
+ * When 'obj' is an ordinary object, these functions have boring standard
+ * behavior as specified by ES6 section 9.1; see the section about internal
+ * methods in js/src/vm/NativeObject.h.
+ *
+ * Proxies override the behavior of internal methods. So when 'obj' is a proxy,
+ * any one of the functions below could do just about anything. See
+ * js/public/Proxy.h.
+ */
+
+/**
+ * Get the prototype of obj, storing it in result.
+ *
+ * Implements: ES6 [[GetPrototypeOf]] internal method.
+ */
+extern JS_PUBLIC_API(bool)
+JS_GetPrototype(JSContext* cx, JS::HandleObject obj, JS::MutableHandleObject result);
+
+/**
+ * Change the prototype of obj.
+ *
+ * Implements: ES6 [[SetPrototypeOf]] internal method.
+ *
+ * In cases where ES6 [[SetPrototypeOf]] returns false without an exception,
+ * JS_SetPrototype throws a TypeError and returns false.
+ *
+ * Performance warning: JS_SetPrototype is very bad for performance. It may
+ * cause compiled jit-code to be invalidated. It also causes not only obj but
+ * all other objects in the same "group" as obj to be permanently deoptimized.
+ * It's better to create the object with the right prototype from the start.
+ */
+extern JS_PUBLIC_API(bool)
+JS_SetPrototype(JSContext* cx, JS::HandleObject obj, JS::HandleObject proto);
+
+/**
+ * Determine whether obj is extensible. Extensible objects can have new
+ * properties defined on them. Inextensible objects can't, and their
+ * [[Prototype]] slot is fixed as well.
+ *
+ * Implements: ES6 [[IsExtensible]] internal method.
+ */
+extern JS_PUBLIC_API(bool)
+JS_IsExtensible(JSContext* cx, JS::HandleObject obj, bool* extensible);
+
+/**
+ * Attempt to make |obj| non-extensible.
+ *
+ * Not all failures are treated as errors. See the comment on
+ * JS::ObjectOpResult in js/public/Class.h.
+ *
+ * Implements: ES6 [[PreventExtensions]] internal method.
+ */
+extern JS_PUBLIC_API(bool)
+JS_PreventExtensions(JSContext* cx, JS::HandleObject obj, JS::ObjectOpResult& result);
+
+/**
+ * Attempt to make the [[Prototype]] of |obj| immutable, such that any attempt
+ * to modify it will fail.  If an error occurs during the attempt, return false
+ * (with a pending exception set, depending upon the nature of the error).  If
+ * no error occurs, return true with |*succeeded| set to indicate whether the
+ * attempt successfully made the [[Prototype]] immutable.
+ *
+ * This is a nonstandard internal method.
+ */
+extern JS_PUBLIC_API(bool)
+JS_SetImmutablePrototype(JSContext* cx, JS::HandleObject obj, bool* succeeded);
+
+/**
+ * Get a description of one of obj's own properties. If no such property exists
+ * on obj, return true with desc.object() set to null.
+ *
+ * Implements: ES6 [[GetOwnProperty]] internal method.
+ */
 extern JS_PUBLIC_API(bool)
 JS_GetOwnPropertyDescriptorById(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                                 JS::MutableHandle<JSPropertyDescriptor> desc);
 
 extern JS_PUBLIC_API(bool)
 JS_GetOwnPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char* name,
                             JS::MutableHandle<JSPropertyDescriptor> desc);
 
 extern JS_PUBLIC_API(bool)
 JS_GetOwnUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name,
                               JS::MutableHandle<JSPropertyDescriptor> desc);
 
-extern JS_PUBLIC_API(bool)
-JS_HasOwnPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp);
-
-extern JS_PUBLIC_API(bool)
-JS_HasOwnProperty(JSContext* cx, JS::HandleObject obj, const char* name, bool* foundp);
-
-/*
- * Like JS_GetOwnPropertyDescriptorById but will return a property on
- * an object on the prototype chain (returned in desc->obj). If desc->obj is null,
- * then this property was not found on the prototype chain.
+/**
+ * Like JS_GetOwnPropertyDescriptorById, but also searches the prototype chain
+ * if no own property is found directly on obj. The object on which the
+ * property is found is returned in desc.object(). If the property is not found
+ * on the prototype chain, this returns true with desc.object() set to null.
  */
 extern JS_PUBLIC_API(bool)
 JS_GetPropertyDescriptorById(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                              JS::MutableHandle<JSPropertyDescriptor> desc);
 
 extern JS_PUBLIC_API(bool)
 JS_GetPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char* name,
                          JS::MutableHandle<JSPropertyDescriptor> desc);
 
-extern JS_PUBLIC_API(bool)
-JS_GetProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::MutableHandleValue vp);
-
-extern JS_PUBLIC_API(bool)
-JS_GetPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp);
-
-extern JS_PUBLIC_API(bool)
-JS_ForwardGetPropertyTo(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue onBehalfOf,
-                        JS::MutableHandleValue vp);
-
-extern JS_PUBLIC_API(bool)
-JS_SetProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleValue v);
-
-extern JS_PUBLIC_API(bool)
-JS_SetPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v);
-
-extern JS_PUBLIC_API(bool)
-JS_ForwardSetPropertyTo(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v,
-                        JS::HandleValue receiver, JS::ObjectOpResult& result);
-
-extern JS_PUBLIC_API(bool)
-JS_DeleteProperty(JSContext* cx, JS::HandleObject obj, const char* name);
-
-extern JS_PUBLIC_API(bool)
-JS_DeleteProperty(JSContext* cx, JS::HandleObject obj, const char* name,
-                  JS::ObjectOpResult& result);
-
-extern JS_PUBLIC_API(bool)
-JS_DeletePropertyById(JSContext* cx, JS::HandleObject obj, jsid id);
-
-extern JS_PUBLIC_API(bool)
-JS_DeletePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+/**
+ * Define a property on obj.
+ *
+ * This function uses JS::ObjectOpResult to indicate conditions that ES6
+ * specifies as non-error failures. This is inconvenient at best, so use this
+ * function only if you are implementing a proxy handler's defineProperty()
+ * method. For all other purposes, use one of the many DefineProperty functions
+ * below that throw an exception in all failure cases.
+ *
+ * Implements: ES6 [[DefineOwnProperty]] internal method.
+ */
+extern JS_PUBLIC_API(bool)
+JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                      JS::Handle<JSPropertyDescriptor> desc,
                       JS::ObjectOpResult& result);
 
+/**
+ * Define a property on obj, throwing a TypeError if the attempt fails.
+ * This is the C++ equivalent of `Object.defineProperty(obj, id, desc)`.
+ */
+extern JS_PUBLIC_API(bool)
+JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                      JS::Handle<JSPropertyDescriptor> desc);
+
+extern JS_PUBLIC_API(bool)
+JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value,
+                      unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject value,
+                      unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleString value,
+                      unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, int32_t value,
+                      unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, uint32_t value,
+                      unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, double value,
+                      unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleValue value,
+                  unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleObject value,
+                  unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleString value,
+                  unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, int32_t value,
+                  unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, uint32_t value,
+                  unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, double value,
+                  unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
+                    JS::Handle<JSPropertyDescriptor> desc,
+                    JS::ObjectOpResult& result);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
+                    JS::Handle<JSPropertyDescriptor> desc);
+
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
                     JS::HandleValue value, unsigned attrs,
                     JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
                     JS::HandleObject value, unsigned attrs,
@@ -2961,46 +2948,380 @@ JS_DefineUCProperty(JSContext* cx, JS::H
                     JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
                     double value, unsigned attrs,
                     JSNative getter = nullptr, JSNative setter = nullptr);
 
 extern JS_PUBLIC_API(bool)
-JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
-                    JS::Handle<JSPropertyDescriptor> desc,
+JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleValue value,
+                 unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleObject value,
+                 unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleString value,
+                 unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, int32_t value,
+                 unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, uint32_t value,
+                 unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, double value,
+                 unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr);
+
+/**
+ * Compute the expression `id in obj`.
+ *
+ * If obj has an own or inherited property obj[id], set *foundp = true and
+ * return true. If not, set *foundp = false and return true. On error, return
+ * false with an exception pending.
+ *
+ * Implements: ES6 [[Has]] internal method.
+ */
+extern JS_PUBLIC_API(bool)
+JS_HasPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp);
+
+extern JS_PUBLIC_API(bool)
+JS_HasProperty(JSContext* cx, JS::HandleObject obj, const char* name, bool* foundp);
+
+extern JS_PUBLIC_API(bool)
+JS_HasUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
+                 bool* vp);
+
+extern JS_PUBLIC_API(bool)
+JS_HasElement(JSContext* cx, JS::HandleObject obj, uint32_t index, bool* foundp);
+
+/**
+ * Determine whether obj has an own property with the key `id`.
+ *
+ * Implements: ES6 7.3.11 HasOwnProperty(O, P).
+ */
+extern JS_PUBLIC_API(bool)
+JS_HasOwnPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp);
+
+extern JS_PUBLIC_API(bool)
+JS_HasOwnProperty(JSContext* cx, JS::HandleObject obj, const char* name, bool* foundp);
+
+/**
+ * Get the value of the property `obj[id]`, or undefined if no such property
+ * exists. This is the C++ equivalent of `vp = Reflect.get(obj, id, receiver)`.
+ *
+ * Most callers don't need the `receiver` argument. Consider using
+ * JS_GetProperty instead. (But if you're implementing a proxy handler's set()
+ * method, it's often correct to call this function and pass the receiver
+ * through.)
+ *
+ * Implements: ES6 [[Get]] internal method.
+ */
+extern JS_PUBLIC_API(bool)
+JS_ForwardGetPropertyTo(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                        JS::HandleValue receiver, JS::MutableHandleValue vp);
+
+extern JS_PUBLIC_API(bool)
+JS_ForwardGetElementTo(JSContext* cx, JS::HandleObject obj, uint32_t index,
+                       JS::HandleObject receiver, JS::MutableHandleValue vp);
+
+/**
+ * Get the value of the property `obj[id]`, or undefined if no such property
+ * exists. The result is stored in vp.
+ *
+ * Implements: ES6 7.3.1 Get(O, P).
+ */
+extern JS_PUBLIC_API(bool)
+JS_GetPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                   JS::MutableHandleValue vp);
+
+extern JS_PUBLIC_API(bool)
+JS_GetProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::MutableHandleValue vp);
+
+extern JS_PUBLIC_API(bool)
+JS_GetUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
+                 JS::MutableHandleValue vp);
+
+extern JS_PUBLIC_API(bool)
+JS_GetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp);
+
+/**
+ * Perform the same property assignment as `Reflect.set(obj, id, v, receiver)`.
+ *
+ * This function has a `receiver` argument that most callers don't need.
+ * Consider using JS_SetProperty instead.
+ *
+ * Implements: ES6 [[Set]] internal method.
+ */
+extern JS_PUBLIC_API(bool)
+JS_ForwardSetPropertyTo(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v,
+                        JS::HandleValue receiver, JS::ObjectOpResult& result);
+
+/**
+ * Perform the assignment `obj[id] = v`.
+ *
+ * This function performs non-strict assignment, so if the property is
+ * read-only, nothing happens and no error is thrown.
+ */
+extern JS_PUBLIC_API(bool)
+JS_SetPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v);
+
+extern JS_PUBLIC_API(bool)
+JS_SetProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleValue v);
+
+extern JS_PUBLIC_API(bool)
+JS_SetUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
+                 JS::HandleValue v);
+
+extern JS_PUBLIC_API(bool)
+JS_SetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleValue v);
+
+extern JS_PUBLIC_API(bool)
+JS_SetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleObject v);
+
+extern JS_PUBLIC_API(bool)
+JS_SetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleString v);
+
+extern JS_PUBLIC_API(bool)
+JS_SetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, int32_t v);
+
+extern JS_PUBLIC_API(bool)
+JS_SetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, uint32_t v);
+
+extern JS_PUBLIC_API(bool)
+JS_SetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, double v);
+
+/**
+ * Delete a property. This is the C++ equivalent of
+ * `result = Reflect.deleteProperty(obj, id)`.
+ *
+ * This function has a `result` out parameter that most callers don't need.
+ * Unless you can pass through an ObjectOpResult provided by your caller, it's
+ * probably best to use the JS_DeletePropertyById signature with just 3
+ * arguments.
+ *
+ * Implements: ES6 [[Delete]] internal method.
+ */
+extern JS_PUBLIC_API(bool)
+JS_DeletePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                      JS::ObjectOpResult& result);
+
+extern JS_PUBLIC_API(bool)
+JS_DeleteProperty(JSContext* cx, JS::HandleObject obj, const char* name,
+                  JS::ObjectOpResult& result);
+
+extern JS_PUBLIC_API(bool)
+JS_DeleteUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
                     JS::ObjectOpResult& result);
 
 extern JS_PUBLIC_API(bool)
-JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
-                    JS::Handle<JSPropertyDescriptor> desc);
+JS_DeleteElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::ObjectOpResult& result);
+
+/**
+ * Delete a property, ignoring strict failures. This is the C++ equivalent of
+ * the JS `delete obj[id]` in non-strict mode code.
+ */
+extern JS_PUBLIC_API(bool)
+JS_DeletePropertyById(JSContext* cx, JS::HandleObject obj, jsid id);
+
+extern JS_PUBLIC_API(bool)
+JS_DeleteProperty(JSContext* cx, JS::HandleObject obj, const char* name);
+
+extern JS_PUBLIC_API(bool)
+JS_DeleteElement(JSContext* cx, JS::HandleObject obj, uint32_t index);
+
+/**
+ * Get an array of the non-symbol enumerable properties of obj.
+ * This function is roughly equivalent to:
+ *
+ *     var result = [];
+ *     for (key in obj)
+ *         result.push(key);
+ *     return result;
+ *
+ * This is the closest thing we currently have to the ES6 [[Enumerate]]
+ * internal method.
+ *
+ * The JSIdArray returned by JS_Enumerate must be rooted to protect its
+ * contents from garbage collection. Use JS::AutoIdArray.
+ */
+extern JS_PUBLIC_API(bool)
+JS_Enumerate(JSContext* cx, JS::HandleObject obj, JS::MutableHandle<JS::IdVector> props);
+
+/*
+ * API for determining callability and constructability. [[Call]] and
+ * [[Construct]] are internal methods that aren't present on all objects, so it
+ * is useful to ask if they are there or not. The standard itself asks these
+ * questions routinely.
+ */
+namespace JS {
+
+/**
+ * Return true if the given object is callable. In ES6 terms, an object is
+ * callable if it has a [[Call]] internal method.
+ *
+ * Implements: ES6 7.2.3 IsCallable(argument).
+ *
+ * Functions are callable. A scripted proxy or wrapper is callable if its
+ * target is callable. Most other objects aren't callable.
+ */
+extern JS_PUBLIC_API(bool)
+IsCallable(JSObject* obj);
+
+/**
+ * Return true if the given object is a constructor. In ES6 terms, an object is
+ * a constructor if it has a [[Construct]] internal method. The expression
+ * `new obj()` throws a TypeError if obj is not a constructor.
+ *
+ * Implements: ES6 7.2.4 IsConstructor(argument).
+ *
+ * JS functions and classes are constructors. Arrow functions and most builtin
+ * functions are not. A scripted proxy or wrapper is a constructor if its
+ * target is a constructor.
+ */
+extern JS_PUBLIC_API(bool)
+IsConstructor(JSObject* obj);
+
+} /* namespace JS */
+
+/**
+ * Call a function, passing a this-value and arguments. This is the C++
+ * equivalent of `rval = Reflect.apply(fun, obj, args)`.
+ *
+ * Implements: ES6 7.3.12 Call(F, V, [argumentsList]).
+ * Use this function to invoke the [[Call]] internal method.
+ */
+extern JS_PUBLIC_API(bool)
+JS_CallFunctionValue(JSContext* cx, JS::HandleObject obj, JS::HandleValue fval,
+                     const JS::HandleValueArray& args, JS::MutableHandleValue rval);
+
+extern JS_PUBLIC_API(bool)
+JS_CallFunction(JSContext* cx, JS::HandleObject obj, JS::HandleFunction fun,
+                const JS::HandleValueArray& args, JS::MutableHandleValue rval);
+
+/**
+ * Perform the method call `rval = obj[name](args)`.
+ */
+extern JS_PUBLIC_API(bool)
+JS_CallFunctionName(JSContext* cx, JS::HandleObject obj, const char* name,
+                    const JS::HandleValueArray& args, JS::MutableHandleValue rval);
+
+namespace JS {
+
+static inline bool
+Call(JSContext* cx, JS::HandleObject thisObj, JS::HandleFunction fun,
+     const JS::HandleValueArray& args, MutableHandleValue rval)
+{
+    return !!JS_CallFunction(cx, thisObj, fun, args, rval);
+}
+
+static inline bool
+Call(JSContext* cx, JS::HandleObject thisObj, JS::HandleValue fun, const JS::HandleValueArray& args,
+     MutableHandleValue rval)
+{
+    return !!JS_CallFunctionValue(cx, thisObj, fun, args, rval);
+}
+
+static inline bool
+Call(JSContext* cx, JS::HandleObject thisObj, const char* name, const JS::HandleValueArray& args,
+     MutableHandleValue rval)
+{
+    return !!JS_CallFunctionName(cx, thisObj, name, args, rval);
+}
+
+extern JS_PUBLIC_API(bool)
+Call(JSContext* cx, JS::HandleValue thisv, JS::HandleValue fun, const JS::HandleValueArray& args,
+     MutableHandleValue rval);
+
+static inline bool
+Call(JSContext* cx, JS::HandleValue thisv, JS::HandleObject funObj, const JS::HandleValueArray& args,
+     MutableHandleValue rval)
+{
+    MOZ_ASSERT(funObj);
+    JS::RootedValue fun(cx, JS::ObjectValue(*funObj));
+    return Call(cx, thisv, fun, args, rval);
+}
+
+/**
+ * Invoke a constructor. This is the C++ equivalent of
+ * `rval = Reflect.construct(fun, args, newTarget)`.
+ *
+ * JS::Construct() takes a `newTarget` argument that most callers don't need.
+ * Consider using the four-argument Construct signature instead. (But if you're
+ * implementing a subclass or a proxy handler's construct() method, this is the
+ * right function to call.)
+ *
+ * Implements: ES6 7.3.13 Construct(F, [argumentsList], [newTarget]).
+ * Use this function to invoke the [[Construct]] internal method.
+ */
+extern JS_PUBLIC_API(bool)
+Construct(JSContext* cx, JS::HandleValue fun, HandleObject newTarget,
+          const JS::HandleValueArray &args, MutableHandleValue rval);
+
+/**
+ * Invoke a constructor. This is the C++ equivalent of
+ * `rval = new fun(...args)`.
+ *
+ * The value left in rval on success is always an object in practice,
+ * though at the moment this is not enforced by the C++ type system.
+ *
+ * Implements: ES6 7.3.13 Construct(F, [argumentsList], [newTarget]), when
+ * newTarget is omitted.
+ */
+extern JS_PUBLIC_API(bool)
+Construct(JSContext* cx, JS::HandleValue fun, const JS::HandleValueArray& args,
+          MutableHandleValue rval);
+
+} /* namespace JS */
+
+/**
+ * Invoke a constructor, like the JS expression `new ctor(...args)`. Returns
+ * the new object, or null on error.
+ */
+extern JS_PUBLIC_API(JSObject*)
+JS_New(JSContext* cx, JS::HandleObject ctor, const JS::HandleValueArray& args);
+
+
+/*** Other property-defining functions ***********************************************************/
+
+extern JS_PUBLIC_API(JSObject*)
+JS_DefineObject(JSContext* cx, JS::HandleObject obj, const char* name,
+                const JSClass* clasp = nullptr, unsigned attrs = 0);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineConstDoubles(JSContext* cx, JS::HandleObject obj, const JSConstDoubleSpec* cds);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineConstIntegers(JSContext* cx, JS::HandleObject obj, const JSConstIntegerSpec* cis);
+
+extern JS_PUBLIC_API(bool)
+JS_DefineProperties(JSContext* cx, JS::HandleObject obj, const JSPropertySpec* ps);
+
+
+/* * */
+
+extern JS_PUBLIC_API(bool)
+JS_AlreadyHasOwnPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+                             bool* foundp);
+
+extern JS_PUBLIC_API(bool)
+JS_AlreadyHasOwnProperty(JSContext* cx, JS::HandleObject obj, const char* name,
+                         bool* foundp);
 
 extern JS_PUBLIC_API(bool)
 JS_AlreadyHasOwnUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name,
                            size_t namelen, bool* foundp);
 
 extern JS_PUBLIC_API(bool)
-JS_HasUCProperty(JSContext* cx, JS::HandleObject obj,
-                 const char16_t* name, size_t namelen,
-                 bool* vp);
-
-extern JS_PUBLIC_API(bool)
-JS_GetUCProperty(JSContext* cx, JS::HandleObject obj,
-                 const char16_t* name, size_t namelen,
-                 JS::MutableHandleValue vp);
-
-extern JS_PUBLIC_API(bool)
-JS_SetUCProperty(JSContext* cx, JS::HandleObject obj,
-                 const char16_t* name, size_t namelen,
-                 JS::HandleValue v);
-
-extern JS_PUBLIC_API(bool)
-JS_DeleteUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
-                    JS::ObjectOpResult& result);
+JS_AlreadyHasOwnElement(JSContext* cx, JS::HandleObject obj, uint32_t index, bool* foundp);
 
 extern JS_PUBLIC_API(JSObject*)
 JS_NewArrayObject(JSContext* cx, const JS::HandleValueArray& contents);
 
 extern JS_PUBLIC_API(JSObject*)
 JS_NewArrayObject(JSContext* cx, size_t length);
 
 // Returns true and sets |*isArray| indicating whether |value| is an Array
@@ -3020,83 +3341,16 @@ extern JS_PUBLIC_API(bool)
 JS_IsArrayObject(JSContext* cx, JS::HandleObject obj, bool* isArray);
 
 extern JS_PUBLIC_API(bool)
 JS_GetArrayLength(JSContext* cx, JS::Handle<JSObject*> obj, uint32_t* lengthp);
 
 extern JS_PUBLIC_API(bool)
 JS_SetArrayLength(JSContext* cx, JS::Handle<JSObject*> obj, uint32_t length);
 
-extern JS_PUBLIC_API(bool)
-JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleValue value,
-                 unsigned attrs,
-                 JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleObject value,
-                 unsigned attrs,
-                 JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleString value,
-                 unsigned attrs,
-                 JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, int32_t value,
-                 unsigned attrs,
-                 JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, uint32_t value,
-                 unsigned attrs,
-                 JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, double value,
-                 unsigned attrs,
-                 JSNative getter = nullptr, JSNative setter = nullptr);
-
-extern JS_PUBLIC_API(bool)
-JS_AlreadyHasOwnElement(JSContext* cx, JS::HandleObject obj, uint32_t index, bool* foundp);
-
-extern JS_PUBLIC_API(bool)
-JS_HasElement(JSContext* cx, JS::HandleObject obj, uint32_t index, bool* foundp);
-
-extern JS_PUBLIC_API(bool)
-JS_GetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp);
-
-extern JS_PUBLIC_API(bool)
-JS_ForwardGetElementTo(JSContext* cx, JS::HandleObject obj, uint32_t index,
-                       JS::HandleObject onBehalfOf, JS::MutableHandleValue vp);
-
-extern JS_PUBLIC_API(bool)
-JS_SetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleValue v);
-
-extern JS_PUBLIC_API(bool)
-JS_SetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleObject v);
-
-extern JS_PUBLIC_API(bool)
-JS_SetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleString v);
-
-extern JS_PUBLIC_API(bool)
-JS_SetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, int32_t v);
-
-extern JS_PUBLIC_API(bool)
-JS_SetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, uint32_t v);
-
-extern JS_PUBLIC_API(bool)
-JS_SetElement(JSContext* cx, JS::HandleObject obj, uint32_t index, double v);
-
-extern JS_PUBLIC_API(bool)
-JS_DeleteElement(JSContext* cx, JS::HandleObject obj, uint32_t index);
-
-extern JS_PUBLIC_API(bool)
-JS_DeleteElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::ObjectOpResult& result);
-
 /*
  * Assign 'undefined' to all of the object's non-reserved slots. Note: this is
  * done for all slots, regardless of the associated property descriptor.
  */
 JS_PUBLIC_API(void)
 JS_SetAllNonReservedSlotsToUndefined(JSContext* cx, JSObject* objArg);
 
 /*
@@ -3136,25 +3390,23 @@ JS_CreateMappedArrayBufferContents(int f
  * object is created.
  * If a new object has been created by JS_NewMappedArrayBufferWithContents()
  * with this content, then JS_NeuterArrayBuffer() should be used instead to
  * release the resource used by the object.
  */
 extern JS_PUBLIC_API(void)
 JS_ReleaseMappedArrayBufferContents(void* contents, size_t length);
 
-extern JS_PUBLIC_API(bool)
-JS_Enumerate(JSContext* cx, JS::HandleObject obj, JS::MutableHandle<JS::IdVector> props);
-
 extern JS_PUBLIC_API(JS::Value)
 JS_GetReservedSlot(JSObject* obj, uint32_t index);
 
 extern JS_PUBLIC_API(void)
 JS_SetReservedSlot(JSObject* obj, uint32_t index, JS::Value v);
 
+
 /************************************************************************/
 
 /*
  * Functions and scripts.
  */
 extern JS_PUBLIC_API(JSFunction*)
 JS_NewFunction(JSContext* cx, JSNative call, unsigned nargs, unsigned flags,
                const char* name);
@@ -3202,30 +3454,16 @@ JS_GetFunctionDisplayId(JSFunction* fun)
 
 /*
  * Return the arity (length) of fun.
  */
 extern JS_PUBLIC_API(uint16_t)
 JS_GetFunctionArity(JSFunction* fun);
 
 /*
- * API for determining callability and constructability. This does the right
- * thing for proxies.
- */
-namespace JS {
-
-extern JS_PUBLIC_API(bool)
-IsCallable(JSObject* obj);
-
-extern JS_PUBLIC_API(bool)
-IsConstructor(JSObject* obj);
-
-} /* namespace JS */
-
-/*
  * Infallible predicate to test whether obj is a function object (faster than
  * comparing obj's class name to "Function", but equivalent unless someone has
  * overwritten the "Function" identifier with a different constructor and then
  * created instances using that constructor that might be passed in as obj).
  */
 extern JS_PUBLIC_API(bool)
 JS_ObjectIsFunction(JSContext* cx, JSObject* obj);
 
@@ -3914,76 +4152,16 @@ Evaluate(JSContext* cx, const ReadOnlyCo
  */
 extern JS_PUBLIC_API(bool)
 Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options,
          const char* filename, JS::MutableHandleValue rval);
 
 } /* namespace JS */
 
 extern JS_PUBLIC_API(bool)
-JS_CallFunction(JSContext* cx, JS::HandleObject obj, JS::HandleFunction fun,
-                const JS::HandleValueArray& args, JS::MutableHandleValue rval);
-
-extern JS_PUBLIC_API(bool)
-JS_CallFunctionName(JSContext* cx, JS::HandleObject obj, const char* name,
-                    const JS::HandleValueArray& args, JS::MutableHandleValue rval);
-
-extern JS_PUBLIC_API(bool)
-JS_CallFunctionValue(JSContext* cx, JS::HandleObject obj, JS::HandleValue fval,
-                     const JS::HandleValueArray& args, JS::MutableHandleValue rval);
-
-namespace JS {
-
-static inline bool
-Call(JSContext* cx, JS::HandleObject thisObj, JS::HandleFunction fun,
-     const JS::HandleValueArray& args, MutableHandleValue rval)
-{
-    return !!JS_CallFunction(cx, thisObj, fun, args, rval);
-}
-
-static inline bool
-Call(JSContext* cx, JS::HandleObject thisObj, const char* name, const JS::HandleValueArray& args,
-     MutableHandleValue rval)
-{
-    return !!JS_CallFunctionName(cx, thisObj, name, args, rval);
-}
-
-static inline bool
-Call(JSContext* cx, JS::HandleObject thisObj, JS::HandleValue fun, const JS::HandleValueArray& args,
-     MutableHandleValue rval)
-{
-    return !!JS_CallFunctionValue(cx, thisObj, fun, args, rval);
-}
-
-extern JS_PUBLIC_API(bool)
-Call(JSContext* cx, JS::HandleValue thisv, JS::HandleValue fun, const JS::HandleValueArray& args,
-     MutableHandleValue rval);
-
-static inline bool
-Call(JSContext* cx, JS::HandleValue thisv, JS::HandleObject funObj, const JS::HandleValueArray& args,
-     MutableHandleValue rval)
-{
-    MOZ_ASSERT(funObj);
-    JS::RootedValue fun(cx, JS::ObjectValue(*funObj));
-    return Call(cx, thisv, fun, args, rval);
-}
-
-extern JS_PUBLIC_API(bool)
-Construct(JSContext* cx, JS::HandleValue fun,
-          const JS::HandleValueArray& args,
-          MutableHandleValue rval);
-
-extern JS_PUBLIC_API(bool)
-Construct(JSContext* cx, JS::HandleValue fun,
-          HandleObject newTarget, const JS::HandleValueArray &args,
-          MutableHandleValue rval);
-
-} /* namespace JS */
-
-extern JS_PUBLIC_API(bool)
 JS_CheckForInterrupt(JSContext* cx);
 
 /*
  * These functions allow setting an interrupt callback that will be called
  * from the JS thread some time after any thread triggered the callback using
  * JS_RequestInterruptCallback(rt).
  *
  * To schedule the GC and for other activities the engine internally triggers
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -504,16 +504,20 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONF
         ]
         if CONFIG['JS_SIMULATOR_MIPS32']:
             UNIFIED_SOURCES += [
                 'jit/mips32/Simulator-mips32.cpp'
             ]
     elif CONFIG['JS_CODEGEN_MIPS64']:
         UNIFIED_SOURCES += [
             'jit/mips64/Architecture-mips64.cpp',
+            'jit/mips64/Assembler-mips64.cpp',
+            'jit/mips64/Bailouts-mips64.cpp',
+            'jit/mips64/BaselineCompiler-mips64.cpp',
+            'jit/mips64/BaselineIC-mips64.cpp',
         ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     SOURCES += [
         'jit/ExecutableAllocatorWin.cpp',
     ]
     # _CRT_RAND_S must be #defined before #including stdlib.h to get rand_s()
     DEFINES['_CRT_RAND_S'] = True
--- a/js/src/vm/SavedFrame.h
+++ b/js/src/vm/SavedFrame.h
@@ -2,22 +2,25 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 vm_SavedFrame_h
 #define vm_SavedFrame_h
 
+#include "jswrapper.h"
+
 #include "js/UbiNode.h"
 
 namespace js {
 
 class SavedFrame : public NativeObject {
     friend class SavedStacks;
+    friend struct ::JSStructuredCloneReader;
 
   public:
     static const Class          class_;
     static const JSPropertySpec protoAccessors[];
     static const JSFunctionSpec protoFunctions[];
     static const JSFunctionSpec staticFunctions[];
 
     // Prototype methods and properties to be exposed to JS.
@@ -115,16 +118,23 @@ class SavedFrame : public NativeObject {
         RootedIterator end() { return RootedIterator(); }
     };
 
     static bool isSavedFrameAndNotProto(JSObject& obj) {
         return obj.is<SavedFrame>() &&
                !obj.as<SavedFrame>().getReservedSlot(JSSLOT_SOURCE).isNull();
     }
 
+    static bool isSavedFrameOrWrapperAndNotProto(JSObject& obj) {
+        auto unwrapped = CheckedUnwrap(&obj);
+        if (!unwrapped)
+            return false;
+        return isSavedFrameAndNotProto(*unwrapped);
+    }
+
     struct Lookup;
     struct HashPolicy;
 
     typedef HashSet<js::ReadBarriered<SavedFrame*>,
                     HashPolicy,
                     SystemAllocPolicy> Set;
 
     class AutoLookupVector;
@@ -137,18 +147,27 @@ class SavedFrame : public NativeObject {
         explicit HandleLookup(Lookup& lookup) : lookup(lookup) { }
 
       public:
         inline Lookup& get() { return lookup; }
         inline Lookup* operator->() { return &lookup; }
     };
 
   private:
+    static SavedFrame* create(JSContext* cx);
     static bool finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject proto);
     void initFromLookup(HandleLookup lookup);
+    void initSource(JSAtom* source);
+    void initLine(uint32_t line);
+    void initColumn(uint32_t column);
+    void initFunctionDisplayName(JSAtom* maybeName);
+    void initAsyncCause(JSAtom* maybeCause);
+    void initParent(SavedFrame* maybeParent);
+    void initPrincipalsAlreadyHeld(JSPrincipals* principals);
+    void initPrincipals(JSPrincipals* principals);
 
     enum {
         // The reserved slots in the SavedFrame class.
         JSSLOT_SOURCE,
         JSSLOT_LINE,
         JSSLOT_COLUMN,
         JSSLOT_FUNCTIONDISPLAYNAME,
         JSSLOT_ASYNCCAUSE,
--- a/js/src/vm/SavedStacks-inl.h
+++ b/js/src/vm/SavedStacks-inl.h
@@ -17,18 +17,12 @@
 // SavedFrame objects and the SavedFrame accessors themselves handle wrappers
 // and use the original caller's compartment's principals to determine what
 // level of data to present. Unwrapping and entering the referent's compartment
 // would mess that up. See the module level documentation in
 // `js/src/vm/SavedStacks.h` as well as the comments in `js/src/jsapi.h`.
 inline void
 js::AssertObjectIsSavedFrameOrWrapper(JSContext* cx, HandleObject stack)
 {
-#ifdef DEBUG
-    if (stack) {
-        RootedObject savedFrameObj(cx, CheckedUnwrap(stack));
-        MOZ_ASSERT(savedFrameObj);
-        MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
-    }
-#endif
+    MOZ_ASSERT_IF(stack, js::SavedFrame::isSavedFrameOrWrapperAndNotProto(*stack));
 }
 
 #endif // vm_SavedStacksInl_h
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -402,38 +402,100 @@ SavedFrame::getPrincipals()
 {
     const Value& v = getReservedSlot(JSSLOT_PRINCIPALS);
     if (v.isUndefined())
         return nullptr;
     return static_cast<JSPrincipals*>(v.toPrivate());
 }
 
 void
+SavedFrame::initSource(JSAtom* source)
+{
+    MOZ_ASSERT(source);
+    initReservedSlot(JSSLOT_SOURCE, StringValue(source));
+}
+
+void
+SavedFrame::initLine(uint32_t line)
+{
+    initReservedSlot(JSSLOT_LINE, PrivateUint32Value(line));
+}
+
+void
+SavedFrame::initColumn(uint32_t column)
+{
+    initReservedSlot(JSSLOT_COLUMN, PrivateUint32Value(column));
+}
+
+void
+SavedFrame::initPrincipals(JSPrincipals* principals)
+{
+    if (principals)
+        JS_HoldPrincipals(principals);
+    initPrincipalsAlreadyHeld(principals);
+}
+
+void
+SavedFrame::initPrincipalsAlreadyHeld(JSPrincipals* principals)
+{
+    MOZ_ASSERT_IF(principals, principals->refcount > 0);
+    initReservedSlot(JSSLOT_PRINCIPALS, PrivateValue(principals));
+}
+
+void
+SavedFrame::initFunctionDisplayName(JSAtom* maybeName)
+{
+    initReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME, maybeName ? StringValue(maybeName) : NullValue());
+}
+
+void
+SavedFrame::initAsyncCause(JSAtom* maybeCause)
+{
+    initReservedSlot(JSSLOT_ASYNCCAUSE, maybeCause ? StringValue(maybeCause) : NullValue());
+}
+
+void
+SavedFrame::initParent(SavedFrame* maybeParent)
+{
+    initReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(maybeParent));
+}
+
+void
 SavedFrame::initFromLookup(SavedFrame::HandleLookup lookup)
 {
-    MOZ_ASSERT(lookup->source);
-    MOZ_ASSERT(getReservedSlot(JSSLOT_SOURCE).isUndefined());
-    setReservedSlot(JSSLOT_SOURCE, StringValue(lookup->source));
+    initSource(lookup->source);
+    initLine(lookup->line);
+    initColumn(lookup->column);
+    initFunctionDisplayName(lookup->functionDisplayName);
+    initAsyncCause(lookup->asyncCause);
+    initParent(lookup->parent);
+    initPrincipals(lookup->principals);
+}
+
+/* static */ SavedFrame*
+SavedFrame::create(JSContext* cx)
+{
+    RootedGlobalObject global(cx, cx->global());
+    assertSameCompartment(cx, global);
 
-    setReservedSlot(JSSLOT_LINE, PrivateUint32Value(lookup->line));
-    setReservedSlot(JSSLOT_COLUMN, PrivateUint32Value(lookup->column));
-    setReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME,
-                    lookup->functionDisplayName
-                        ? StringValue(lookup->functionDisplayName)
-                        : NullValue());
-    setReservedSlot(JSSLOT_ASYNCCAUSE,
-                    lookup->asyncCause
-                        ? StringValue(lookup->asyncCause)
-                        : NullValue());
-    setReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(lookup->parent));
+    // Ensure that we don't try to capture the stack again in the
+    // `SavedStacksMetadataCallback` for this new SavedFrame object, and
+    // accidentally cause O(n^2) behavior.
+    SavedStacks::AutoReentrancyGuard guard(cx->compartment()->savedStacks());
 
-    MOZ_ASSERT(getReservedSlot(JSSLOT_PRINCIPALS).isUndefined());
-    if (lookup->principals)
-        JS_HoldPrincipals(lookup->principals);
-    setReservedSlot(JSSLOT_PRINCIPALS, PrivateValue(lookup->principals));
+    RootedNativeObject proto(cx, GlobalObject::getOrCreateSavedFramePrototype(cx, global));
+    if (!proto)
+        return nullptr;
+    assertSameCompartment(cx, proto);
+
+    RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto));
+    if (!frameObj)
+        return nullptr;
+
+    return &frameObj->as<SavedFrame>();
 }
 
 bool
 SavedFrame::isSelfHosted()
 {
     JSAtom* source = getSource();
     return StringEqualsAscii(source, "self-hosted");
 }
@@ -1217,40 +1279,25 @@ SavedStacks::getOrCreateSavedFrame(JSCon
         return nullptr;
 
     return frame;
 }
 
 SavedFrame*
 SavedStacks::createFrameFromLookup(JSContext* cx, SavedFrame::HandleLookup lookup)
 {
-    RootedGlobalObject global(cx, cx->global());
-    assertSameCompartment(cx, global);
-
-    // Ensure that we don't try to capture the stack again in the
-    // `SavedStacksMetadataCallback` for this new SavedFrame object, and
-    // accidentally cause O(n^2) behavior.
-    SavedStacks::AutoReentrancyGuard guard(*this);
+    RootedSavedFrame frame(cx, SavedFrame::create(cx));
+    if (!frame)
+        return nullptr;
+    frame->initFromLookup(lookup);
 
-    RootedNativeObject proto(cx, GlobalObject::getOrCreateSavedFramePrototype(cx, global));
-    if (!proto)
-        return nullptr;
-    assertSameCompartment(cx, proto);
-
-    RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto));
-    if (!frameObj)
+    if (!FreezeObject(cx, frame))
         return nullptr;
 
-    RootedSavedFrame f(cx, &frameObj->as<SavedFrame>());
-    f->initFromLookup(lookup);
-
-    if (!FreezeObject(cx, frameObj))
-        return nullptr;
-
-    return f.get();
+    return frame;
 }
 
 /*
  * Remove entries from the table whose JSScript is being collected.
  */
 void
 SavedStacks::sweepPCLocationMap()
 {
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -143,16 +143,17 @@ namespace js {
 //
 // In the case of z, the `SavedFrame` accessors are called with the `SavedFrame`
 // object in the `this` value, and the content compartment as the cx's current
 // compartment. Similar to the case of y, only the B and C frames are exposed
 // because the cx's current compartment's principals do not subsume A's captured
 // principals.
 
 class SavedStacks {
+    friend class SavedFrame;
     friend JSObject* SavedStacksMetadataCallback(JSContext* cx, JSObject* target);
     friend bool JS::ubi::ConstructSavedFrameStackSlow(JSContext* cx,
                                                       JS::ubi::StackFrame& ubiFrame,
                                                       MutableHandleObject outSavedFrameStack);
 
   public:
     SavedStacks()
       : frames(),
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -37,16 +37,17 @@
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jswrapper.h"
 
 #include "builtin/MapObject.h"
 #include "js/Date.h"
 #include "js/TraceableHashTable.h"
+#include "vm/SavedFrame.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
@@ -87,16 +88,22 @@ enum StructuredDataType : uint32_t {
     SCTAG_DO_NOT_USE_1, // Required for backwards compatibility
     SCTAG_DO_NOT_USE_2, // Required for backwards compatibility
     SCTAG_TYPED_ARRAY_OBJECT,
     SCTAG_MAP_OBJECT,
     SCTAG_SET_OBJECT,
     SCTAG_END_OF_KEYS,
     SCTAG_SHARED_TYPED_ARRAY_OBJECT,
     SCTAG_DATA_VIEW_OBJECT,
+    SCTAG_SAVED_FRAME_OBJECT,
+
+    SCTAG_JSPRINCIPALS,
+    SCTAG_NULL_JSPRINCIPALS,
+    SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM,
+    SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM,
 
     SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
     SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int8,
     SCTAG_TYPED_ARRAY_V1_UINT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint8,
     SCTAG_TYPED_ARRAY_V1_INT16 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int16,
     SCTAG_TYPED_ARRAY_V1_UINT16 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint16,
     SCTAG_TYPED_ARRAY_V1_INT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int32,
     SCTAG_TYPED_ARRAY_V1_UINT32 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Uint32,
@@ -238,16 +245,17 @@ struct JSStructuredCloneReader {
 
     bool checkDouble(double d);
     bool readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
                         bool v1Read = false);
     bool readDataView(uint32_t byteLength, MutableHandleValue vp);
     bool readSharedTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
     bool readArrayBuffer(uint32_t nbytes, MutableHandleValue vp);
     bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
+    JSObject* readSavedFrame(uint32_t principalsTag);
     bool startRead(MutableHandleValue vp);
 
     SCInput& in;
 
     // Stack of objects with properties remaining to be read.
     AutoValueVector objs;
 
     // Stack of all objects read during this deserialization
@@ -300,16 +308,17 @@ struct JSStructuredCloneWriter {
     bool writeDataView(HandleObject obj);
     bool writeSharedArrayBuffer(HandleObject obj);
     bool writeSharedTypedArray(HandleObject obj);
     bool startObject(HandleObject obj, bool* backref);
     bool startWrite(HandleValue v);
     bool traverseObject(HandleObject obj);
     bool traverseMap(HandleObject obj);
     bool traverseSet(HandleObject obj);
+    bool traverseSavedFrame(HandleObject obj);
 
     bool parseTransferable();
     bool reportErrorTransferable(uint32_t errorId);
     bool transferOwnership();
 
     inline void checkStack();
 
     SCOutput out;
@@ -319,19 +328,20 @@ struct JSStructuredCloneWriter {
     // NB: These can span multiple compartments, so the compartment must be
     // entered before any manipulation is performed.
     AutoValueVector objs;
 
     // counts[i] is the number of entries of objs[i] remaining to be written.
     // counts.length() == objs.length() and sum(counts) == entries.length().
     Vector<size_t> counts;
 
-    // For JSObject: Propery IDs as value
-    // For Map: Key followed by value.
+    // For JSObject: Property IDs as value
+    // For Map: Key followed by value
     // For Set: Key
+    // For SavedFrame: parent SavedFrame
     AutoValueVector entries;
 
     // The "memory" list described in the HTML5 internal structured cloning algorithm.
     // memory is a superset of objs; items are never removed from Memory
     // until a serialization operation is finished
     using CloneMemory = TraceableHashMap<JSObject*, uint32_t>;
     Rooted<CloneMemory> memory;
 
@@ -1032,16 +1042,113 @@ JSStructuredCloneWriter::traverseSet(Han
         return false;
 
     checkStack();
 
     /* Write the header for obj. */
     return out.writePair(SCTAG_SET_OBJECT, 0);
 }
 
+// Objects are written as a "preorder" traversal of the object graph: object
+// "headers" (the class tag and any data needed for initial construction) are
+// visited first, then the children are recursed through (where children are
+// properties, Set or Map entries, etc.). So for example
+//
+//     m = new Map();
+//     m.set(key1 = {}, value1 = {})
+//
+// would be stored as
+//
+//     <Map tag>
+//     <key1 class tag>
+//     <value1 class tag>
+//     <end-of-children marker for key1>
+//     <end-of-children marker for value1>
+//     <end-of-children marker for Map>
+//
+// Notice how the end-of-children marker for key1 is sandwiched between the
+// value1 beginning and end.
+bool
+JSStructuredCloneWriter::traverseSavedFrame(HandleObject obj)
+{
+    RootedObject unwrapped(context(), js::CheckedUnwrap(obj));
+    MOZ_ASSERT(unwrapped && unwrapped->is<SavedFrame>());
+
+    RootedSavedFrame savedFrame(context(), &unwrapped->as<SavedFrame>());
+
+    RootedObject parent(context(), savedFrame->getParent());
+    if (!context()->compartment()->wrap(context(), &parent))
+        return false;
+
+    if (!objs.append(ObjectValue(*obj)) ||
+        !entries.append(parent ? ObjectValue(*parent) : NullValue()) ||
+        !counts.append(1))
+    {
+        return false;
+    }
+
+    checkStack();
+
+    // Write the SavedFrame tag and the SavedFrame's principals.
+
+    if (savedFrame->getPrincipals() == &ReconstructedSavedFramePrincipals::IsSystem) {
+        if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT,
+                           SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM))
+        {
+            return false;
+        };
+    } else if (savedFrame->getPrincipals() == &ReconstructedSavedFramePrincipals::IsNotSystem) {
+        if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT,
+                           SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM))
+        {
+            return false;
+        }
+    } else {
+        if (auto principals = savedFrame->getPrincipals()) {
+            if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, SCTAG_JSPRINCIPALS) ||
+                !principals->write(context(), this))
+            {
+                return false;
+            }
+        } else {
+            if (!out.writePair(SCTAG_SAVED_FRAME_OBJECT, SCTAG_NULL_JSPRINCIPALS))
+                return false;
+        }
+    }
+
+    // Write the SavedFrame's reserved slots, except for the parent, which is
+    // queued on objs for further traversal.
+
+    RootedValue val(context());
+
+    val = StringValue(savedFrame->getSource());
+    if (!startWrite(val))
+        return false;
+
+    val = NumberValue(savedFrame->getLine());
+    if (!startWrite(val))
+        return false;
+
+    val = NumberValue(savedFrame->getColumn());
+    if (!startWrite(val))
+        return false;
+
+    auto name = savedFrame->getFunctionDisplayName();
+    val = name ? StringValue(name) : NullValue();
+    if (!startWrite(val))
+        return false;
+
+    auto cause = savedFrame->getAsyncCause();
+    val = cause ? StringValue(cause) : NullValue();
+    if (!startWrite(val))
+        return false;
+
+    return true;
+}
+
 bool
 JSStructuredCloneWriter::startWrite(HandleValue v)
 {
     assertSameCompartment(context(), v);
 
     if (v.isString()) {
         return writeString(SCTAG_STRING, v.toString());
     } else if (v.isInt32()) {
@@ -1106,16 +1213,18 @@ JSStructuredCloneWriter::startWrite(Hand
             RootedValue unboxed(context());
             if (!Unbox(context(), obj, &unboxed))
                 return false;
             return writeString(SCTAG_STRING_OBJECT, unboxed.toString());
         } else if (cls == ESClass_Map) {
             return traverseMap(obj);
         } else if (cls == ESClass_Set) {
             return traverseSet(obj);
+        } else if (SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) {
+            return traverseSavedFrame(obj);
         }
 
         if (callbacks && callbacks->write)
             return callbacks->write(context(), this, obj, closure);
         /* else fall through */
     }
 
     JS_ReportErrorNumber(context(), GetErrorMessage, nullptr, JSMSG_SC_UNSUPPORTED_TYPE);
@@ -1263,17 +1372,17 @@ JSStructuredCloneWriter::write(HandleVal
             if (cls == ESClass_Map) {
                 counts.back()--;
                 RootedValue val(context(), entries.back());
                 entries.popBack();
                 checkStack();
 
                 if (!startWrite(key) || !startWrite(val))
                     return false;
-            } else if (cls == ESClass_Set) {
+            } else if (cls == ESClass_Set || SavedFrame::isSavedFrameOrWrapperAndNotProto(*obj)) {
                 if (!startWrite(key))
                     return false;
             } else {
                 RootedId id(context());
                 if (!ValueToId<CanGC>(context(), key, &id))
                   return false;
                 MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
 
@@ -1772,16 +1881,24 @@ JSStructuredCloneReader::startRead(Mutab
       case SCTAG_SET_OBJECT: {
         JSObject* obj = SetObject::create(context());
         if (!obj || !objs.append(ObjectValue(*obj)))
             return false;
         vp.setObject(*obj);
         break;
       }
 
+      case SCTAG_SAVED_FRAME_OBJECT: {
+        auto obj = readSavedFrame(data);
+        if (!obj || !objs.append(ObjectValue(*obj)))
+            return false;
+        vp.setObject(*obj);
+        break;
+      }
+
       default: {
         if (tag <= SCTAG_FLOAT_MAX) {
             double d = ReinterpretPairAsDouble(tag, data);
             if (!checkDouble(d))
                 return false;
             vp.setNumber(d);
             break;
         }
@@ -1889,63 +2006,186 @@ JSStructuredCloneReader::readTransferMap
     MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_HEADER);
     MOZ_ASSERT(TransferableMapHeader(data) != SCTAG_TM_TRANSFERRED);
 #endif
     *headerPos = PairToUInt64(SCTAG_TRANSFER_MAP_HEADER, SCTAG_TM_TRANSFERRED);
 
     return true;
 }
 
+JSObject*
+JSStructuredCloneReader::readSavedFrame(uint32_t principalsTag)
+{
+    RootedSavedFrame savedFrame(context(), SavedFrame::create(context()));
+    if (!savedFrame)
+        return nullptr;
+
+    JSPrincipals* principals;
+    if (principalsTag == SCTAG_JSPRINCIPALS) {
+        if (!context()->runtime()->readPrincipals) {
+            JS_ReportErrorNumber(context(), GetErrorMessage, nullptr,
+                                 JSMSG_SC_UNSUPPORTED_TYPE);
+            return nullptr;
+        }
+
+        if (!context()->runtime()->readPrincipals(context(), this, &principals))
+            return nullptr;
+    } else if (principalsTag == SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_SYSTEM) {
+        principals = &ReconstructedSavedFramePrincipals::IsSystem;
+        principals->refcount++;
+    } else if (principalsTag == SCTAG_RECONSTRUCTED_SAVED_FRAME_PRINCIPALS_IS_NOT_SYSTEM) {
+        principals = &ReconstructedSavedFramePrincipals::IsNotSystem;
+        principals->refcount++;
+    } else if (principalsTag == SCTAG_NULL_JSPRINCIPALS) {
+        principals = nullptr;
+    } else {
+        JS_ReportErrorNumber(context(), GetErrorMessage, nullptr,
+                             JSMSG_SC_BAD_SERIALIZED_DATA, "bad SavedFrame principals");
+        return nullptr;
+    }
+    savedFrame->initPrincipalsAlreadyHeld(principals);
+
+    RootedValue source(context());
+    if (!startRead(&source) || !source.isString())
+        return nullptr;
+    auto atomSource = AtomizeString(context(), source.toString());
+    if (!atomSource)
+        return nullptr;
+    savedFrame->initSource(atomSource);
+
+    RootedValue lineVal(context());
+    uint32_t line;
+    if (!startRead(&lineVal) || !lineVal.isNumber() || !ToUint32(context(), lineVal, &line))
+        return nullptr;
+    savedFrame->initLine(line);
+
+    RootedValue columnVal(context());
+    uint32_t column;
+    if (!startRead(&columnVal) || !columnVal.isNumber() || !ToUint32(context(), columnVal, &column))
+        return nullptr;
+    savedFrame->initColumn(column);
+
+    RootedValue name(context());
+    if (!startRead(&name) || !(name.isString() || name.isNull()))
+        return nullptr;
+    JSAtom* atomName = nullptr;
+    if (name.isString()) {
+        atomName = AtomizeString(context(), name.toString());
+        if (!atomName)
+            return nullptr;
+    }
+    savedFrame->initFunctionDisplayName(atomName);
+
+    RootedValue cause(context());
+    if (!startRead(&cause) || !(cause.isString() || cause.isNull()))
+        return nullptr;
+    JSAtom* atomCause = nullptr;
+    if (cause.isString()) {
+        atomCause = AtomizeString(context(), cause.toString());
+        if (!atomCause)
+            return nullptr;
+    }
+    savedFrame->initAsyncCause(atomCause);
+
+    return savedFrame;
+}
+
+// Perform the whole recursive reading procedure.
 bool
 JSStructuredCloneReader::read(MutableHandleValue vp)
 {
     if (!readTransferMap())
         return false;
 
+    // Start out by reading in the main object and pushing it onto the 'objs'
+    // stack. The data related to this object and its descendants extends from
+    // here to the SCTAG_END_OF_KEYS at the end of the stream.
     if (!startRead(vp))
         return false;
 
+    // Stop when the stack shows that all objects have been read.
     while (objs.length() != 0) {
+        // What happens depends on the top obj on the objs stack.
         RootedObject obj(context(), &objs.back().toObject());
 
         uint32_t tag, data;
         if (!in.getPair(&tag, &data))
             return false;
 
         if (tag == SCTAG_END_OF_KEYS) {
+            // Pop the current obj off the stack, since we are done with it and
+            // its children.
             MOZ_ALWAYS_TRUE(in.readPair(&tag, &data));
             objs.popBack();
             continue;
         }
 
+        // The input stream contains a sequence of "child" values, whose
+        // interpretation depends on the type of obj. These values can be
+        // anything, and startRead() will push onto 'objs' for any non-leaf
+        // value (i.e., anything that may contain children).
+        //
+        // startRead() will allocate the (empty) object, but note that when
+        // startRead() returns, 'key' is not yet initialized with any of its
+        // properties. Those will be filled in by returning to the head of this
+        // loop, processing the first child obj, and continuing until all
+        // children have been fully created.
+        //
+        // Note that this means the ordering in the stream is a little funky
+        // for things like Map. See the comment above startWrite() for an
+        // example.
         RootedValue key(context());
         if (!startRead(&key))
             return false;
 
-        if (key.isNull() && !(obj->is<MapObject>() || obj->is<SetObject>())) {
-            // Backwards compatibility: Null used to indicate
-            // the end of object properties.
+        if (key.isNull() &&
+            !(obj->is<MapObject>() || obj->is<SetObject>() || obj->is<SavedFrame>()))
+        {
+            // Backwards compatibility: Null formerly indicated the end of
+            // object properties.
             objs.popBack();
             continue;
         }
 
+        // Set object: the values between obj header (from startRead()) and
+        // SCTAG_END_OF_KEYS are all interpreted as values to add to the set.
         if (obj->is<SetObject>()) {
             if (!SetObject::add(context(), obj, key))
                 return false;
             continue;
         }
 
+        // SavedFrame object: there is one following value, the parent
+        // SavedFrame, which is either null or another SavedFrame object.
+        if (obj->is<SavedFrame>()) {
+            SavedFrame* parentFrame;
+            if (key.isNull())
+                parentFrame = nullptr;
+            else if (key.isObject() && key.toObject().is<SavedFrame>())
+                parentFrame = &key.toObject().as<SavedFrame>();
+            else
+                return false;
+
+            obj->as<SavedFrame>().initParent(parentFrame);
+            continue;
+        }
+
+        // Everything else uses a series of key,value,key,value,... Value
+        // objects.
         RootedValue val(context());
         if (!startRead(&val))
             return false;
 
         if (obj->is<MapObject>()) {
+            // For a Map, store those <key,value> pairs in the contained map
+            // data structure.
             if (!MapObject::set(context(), obj, key, val))
                 return false;
         } else {
+            // For any other Object, interpret them as plain properties.
             RootedId id(context());
             if (!ValueToId<CanGC>(context(), key, &id))
                 return false;
 
             if (!DefineProperty(context(), obj, id, val))
                 return false;
          }
     }
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -284,17 +284,17 @@ public:
    * for the purposes of clipping, and its scrollbars will be hidden. We use
    * this to allow RenderOffscreen to render a whole document without beign
    * clipped by the viewport or drawing the viewport scrollbars.
    */
   void SetIgnoreScrollFrame(nsIFrame* aFrame) { mIgnoreScrollFrame = aFrame; }
   /**
    * Get the scrollframe to ignore, if any.
    */
-  nsIFrame* GetIgnoreScrollFrame() const { return mIgnoreScrollFrame; }
+  nsIFrame* GetIgnoreScrollFrame() { return mIgnoreScrollFrame; }
   /**
    * Get the ViewID of the nearest scrolling ancestor frame.
    */
   ViewID GetCurrentScrollParentId() const { return mCurrentScrollParentId; }
   /**
    * Get and set the flag that indicates if scroll parents should have layers
    * forcibly created. This flag is set when a deeply nested scrollframe has
    * a displayport, and for scroll handoff to work properly the ancestor
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -3033,35 +3033,16 @@ nsLayoutUtils::CalculateAndSetDisplayPor
   ScreenMargin displayportMargins = APZCTreeManager::CalculatePendingDisplayPort(
       metrics, ParentLayerPoint(0.0f, 0.0f), 0.0);
   nsIPresShell* presShell = frame->PresContext()->GetPresShell();
   return nsLayoutUtils::SetDisplayPortMargins(
       content, presShell, displayportMargins, 0, aRepaintMode);
 }
 
 bool
-nsLayoutUtils::WantDisplayPort(const nsDisplayListBuilder* aBuilder,
-                               nsIFrame* aScrollFrame)
-{
-  nsIScrollableFrame* scrollableFrame = do_QueryFrame(aScrollFrame);
-  if (!scrollableFrame) {
-    return false;
-  }
-
-  // We perform an optimization where we ensure that at least one
-  // async-scrollable frame (i.e. one that WantAsyncScroll()) has a displayport.
-  // If that's not the case yet, and we are async-scrollable, we will get a
-  // displayport.
-  return aBuilder->IsPaintingToWindow() &&
-         nsLayoutUtils::AsyncPanZoomEnabled(aScrollFrame) &&
-         !aBuilder->HaveScrollableDisplayPort() &&
-         scrollableFrame->WantAsyncScroll();
-}
-
-bool
 nsLayoutUtils::GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder,
                                            nsIFrame* aScrollFrame,
                                            nsRect aDisplayPortBase,
                                            nsRect* aOutDisplayport) {
   nsIContent* content = aScrollFrame->GetContent();
   nsIScrollableFrame* scrollableFrame = do_QueryFrame(aScrollFrame);
   if (!content || !scrollableFrame) {
     return false;
@@ -3069,17 +3050,27 @@ nsLayoutUtils::GetOrMaybeCreateDisplayPo
 
   // Set the base rect. Note that this will not influence 'haveDisplayPort',
   // which is based on either the whole rect or margins being set, but it
   // will affect what is returned in 'aOutDisplayPort' if margins are set.
   SetDisplayPortBase(content, aDisplayPortBase);
 
   bool haveDisplayPort = GetDisplayPort(content, aOutDisplayport);
 
-  if (WantDisplayPort(&aBuilder, aScrollFrame)) {
+  // We perform an optimization where we ensure that at least one
+  // async-scrollable frame (i.e. one that WantsAsyncScroll()) has a displayport.
+  // If that's not the case yet, and we are async-scrollable, we will get a
+  // displayport.
+  // Note: we only do this in processes where we do subframe scrolling to
+  //       begin with (i.e., not in the parent process on B2G).
+  if (aBuilder.IsPaintingToWindow() &&
+      nsLayoutUtils::AsyncPanZoomEnabled(aScrollFrame) &&
+      !aBuilder.HaveScrollableDisplayPort() &&
+      scrollableFrame->WantAsyncScroll()) {
+
     // If we don't already have a displayport, calculate and set one.
     if (!haveDisplayPort) {
       CalculateAndSetDisplayPortMargins(scrollableFrame, nsLayoutUtils::RepaintMode::DoNotRepaint);
       haveDisplayPort = GetDisplayPort(content, aOutDisplayport);
       NS_ASSERTION(haveDisplayPort, "should have a displayport after having just set it");
     }
 
     // Record that the we now have a scrollable display port.
@@ -3137,24 +3128,26 @@ nsLayoutUtils::PaintFrame(nsRenderingCon
   }
   if (aFlags & PAINT_IGNORE_SUPPRESSION) {
     builder.IgnorePaintSuppression();
   }
 
   nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
   bool usingDisplayPort = false;
   nsRect displayport;
-  if (rootScrollFrame && !aFrame->GetParent() &&
-      builder.IsPaintingToWindow() &&
-      gfxPrefs::LayoutUseContainersForRootFrames()) {
-    nsRect displayportBase(
-        nsPoint(0,0),
-        nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame));
-    usingDisplayPort = nsLayoutUtils::GetOrMaybeCreateDisplayPort(
-        builder, rootScrollFrame, displayportBase, &displayport);
+  if (rootScrollFrame && !aFrame->GetParent()) {
+    nsIScrollableFrame* rootScrollableFrame = presShell->GetRootScrollFrameAsScrollable();
+    MOZ_ASSERT(rootScrollableFrame);
+    displayport = aFrame->GetVisualOverflowRectRelativeToSelf();
+    usingDisplayPort = rootScrollableFrame->DecideScrollableLayer(&builder,
+                         &displayport, /* aAllowCreateDisplayPort = */ true);
+
+    if (!gfxPrefs::LayoutUseContainersForRootFrames()) {
+      usingDisplayPort = false;
+    }
   }
 
   nsDisplayList hoistedScrollItemStorage;
   if (builder.IsPaintingToWindow()) {
     builder.SetCommittedScrollInfoItemList(&hoistedScrollItemStorage);
   }
 
   nsRegion visibleRegion;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2647,22 +2647,16 @@ public:
    * mode provided is passed through to the call to SetDisplayPortMargins.
    * The |aScrollFrame| parameter must be non-null and queryable to an nsIFrame.
    * @return true iff the call to SetDisplayPortMargins returned true.
    */
   static bool CalculateAndSetDisplayPortMargins(nsIScrollableFrame* aScrollFrame,
                                                 RepaintMode aRepaintMode);
 
   /**
-   * Return true if GetOrMaybeCreateDisplayPort would create a displayport.
-   */
-  static bool WantDisplayPort(const nsDisplayListBuilder* aBuilder,
-                              nsIFrame* aScrollFrame);
-
-  /**
    * Get the display port for |aScrollFrame|'s content. If |aScrollFrame|
    * WantsAsyncScroll() and we don't have a scrollable displayport yet (as
    * tracked by |aBuilder|), calculate and set a display port. Returns true if
    * there is (now) a displayport, and if so the displayport is returned in
    * |aOutDisplayport|.
    *
    * Note that a displayport can either be stored as a rect, or as a base
    * rect + margins. If it is stored as a base rect + margins, the base rect
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5846,51 +5846,38 @@ PresShell::UpdateImageVisibility()
   list.DeleteAll();
 #endif
 }
 
 bool
 PresShell::AssumeAllImagesVisible()
 {
   static bool sImageVisibilityEnabled = true;
-  static bool sImageVisibilityEnabledForBrowserElementsOnly = false;
   static bool sImageVisibilityPrefCached = false;
 
   if (!sImageVisibilityPrefCached) {
     Preferences::AddBoolVarCache(&sImageVisibilityEnabled,
       "layout.imagevisibility.enabled", true);
-    Preferences::AddBoolVarCache(&sImageVisibilityEnabledForBrowserElementsOnly,
-      "layout.imagevisibility.enabled_for_browser_elements_only", false);
     sImageVisibilityPrefCached = true;
   }
 
-  if ((!sImageVisibilityEnabled &&
-       !sImageVisibilityEnabledForBrowserElementsOnly) ||
-      !mPresContext || !mDocument) {
+  if (!sImageVisibilityEnabled || !mPresContext || !mDocument) {
     return true;
   }
 
   // We assume all images are visible in print, print preview, chrome, xul, and
   // resource docs and don't keep track of them.
   if (mPresContext->Type() == nsPresContext::eContext_PrintPreview ||
       mPresContext->Type() == nsPresContext::eContext_Print ||
       mPresContext->IsChrome() ||
       mDocument->IsResourceDoc() ||
       mDocument->IsXULDocument()) {
     return true;
   }
 
-  if (!sImageVisibilityEnabled &&
-      sImageVisibilityEnabledForBrowserElementsOnly) {
-    nsCOMPtr<nsIDocShell> docshell(mPresContext->GetDocShell());
-    if (!docshell || !docshell->GetIsInBrowserElement()) {
-      return true;
-    }
-  }
-
   return false;
 }
 
 void
 PresShell::ScheduleImageVisibilityUpdate()
 {
   if (AssumeAllImagesVisible())
     return;
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -56,16 +56,17 @@
 #include "nsIFrameInlines.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "AsyncScrollBase.h"
 #include "UnitTransforms.h"
 #include "nsPluginFrame.h"
 #include <mozilla/layers/AxisPhysicsModel.h>
 #include <mozilla/layers/AxisPhysicsMSDModel.h>
+#include "mozilla/unused.h"
 #include <algorithm>
 #include <cstdlib> // for std::abs(int/long)
 #include <cmath> // for std::abs(float/double)
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layout;
 
@@ -1840,17 +1841,17 @@ ScrollFrameHelper::ScrollFrameHelper(nsC
   , mHadNonInitialReflow(false)
   , mHorizontalOverflow(false)
   , mVerticalOverflow(false)
   , mPostedReflowCallback(false)
   , mMayHaveDirtyFixedChildren(false)
   , mUpdateScrollbarAttributes(false)
   , mHasBeenScrolledRecently(false)
   , mCollapsedResizer(false)
-  , mShouldBuildScrollableLayer(false)
+  , mWillBuildScrollableLayer(false)
   , mIsScrollableLayerInRootContainer(false)
   , mHasBeenScrolled(false)
   , mIsResolutionSet(false)
   , mIgnoreMomentumScroll(false)
   , mScaleToResolution(false)
   , mTransformingByAPZ(false)
   , mZoomableByAPZ(false)
   , mVelocityQueue(aOuter->PresContext())
@@ -2819,63 +2820,16 @@ ClipListsExceptCaret(nsDisplayListCollec
   ClipItemsExceptCaret(aLists->BorderBackground(), aBuilder, aClipFrame, clip, aUsingDisplayPort);
   ClipItemsExceptCaret(aLists->BlockBorderBackgrounds(), aBuilder, aClipFrame, clip, aUsingDisplayPort);
   ClipItemsExceptCaret(aLists->Floats(), aBuilder, aClipFrame, clip, aUsingDisplayPort);
   ClipItemsExceptCaret(aLists->PositionedDescendants(), aBuilder, aClipFrame, clip, aUsingDisplayPort);
   ClipItemsExceptCaret(aLists->Outlines(), aBuilder, aClipFrame, clip, aUsingDisplayPort);
   ClipItemsExceptCaret(aLists->Content(), aBuilder, aClipFrame, clip, aUsingDisplayPort);
 }
 
-bool
-ScrollFrameHelper::IsUsingDisplayPort(const nsDisplayListBuilder* aBuilder) const
-{
-  return aBuilder->IsPaintingToWindow() &&
-    nsLayoutUtils::GetDisplayPort(mOuter->GetContent());
-}
-
-bool
-ScrollFrameHelper::WillUseDisplayPort(const nsDisplayListBuilder* aBuilder) const
-{
-  bool wantsDisplayPort = nsLayoutUtils::WantDisplayPort(aBuilder, mOuter);
-
-  if (mIsRoot && gfxPrefs::LayoutUseContainersForRootFrames()) {
-    // This condition mirrors the calls to GetOrMaybeCreateDisplayPort in
-    // nsSubDocumentFrame::BuildDisplayList and nsLayoutUtils::PaintFrame.
-    if (wantsDisplayPort) {
-      return true;
-    }
-  }
-
-  // The following conditions mirror the checks in BuildDisplayList
-
-  if (IsUsingDisplayPort(aBuilder)) {
-    return true;
-  }
-
-  if (aBuilder->GetIgnoreScrollFrame() == mOuter || IsIgnoringViewportClipping()) {
-    return false;
-  }
-
-  return wantsDisplayPort;
-}
-
-bool
-ScrollFrameHelper::WillBuildScrollableLayer(const nsDisplayListBuilder* aBuilder) const
-{
-  if (WillUseDisplayPort(aBuilder)) {
-    return true;
-  }
-
-  if (aBuilder->GetIgnoreScrollFrame() == mOuter || IsIgnoringViewportClipping()) {
-    return false;
-  }
-
-  return nsContentUtils::HasScrollgrab(mOuter->GetContent());
-}
-
 void
 ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                     const nsRect&           aDirtyRect,
                                     const nsDisplayListSet& aLists)
 {
   if (aBuilder->IsForImageVisibility()) {
     mLastUpdateImagesPos = GetScrollPosition();
   }
@@ -2906,31 +2860,24 @@ ScrollFrameHelper::BuildDisplayList(nsDi
   // they're not scrolled with the rest of the document. But when both
   // scrollbars are visible, the layer's visible rectangle would be the size
   // of the viewport, so most layer implementations would create a layer buffer
   // that's much larger than necessary. Creating independent layers for each
   // scrollbar works around the problem.
   bool createLayersForScrollbars = mIsRoot &&
     mOuter->PresContext()->IsRootContentDocument();
 
-  bool usingDisplayPort = IsUsingDisplayPort(aBuilder);
-  mShouldBuildScrollableLayer = WillBuildScrollableLayer(aBuilder);
-
   if (aBuilder->GetIgnoreScrollFrame() == mOuter || IsIgnoringViewportClipping()) {
+    bool usingDisplayPort = aBuilder->IsPaintingToWindow() &&
+      nsLayoutUtils::GetDisplayPort(mOuter->GetContent());
+
     // Root scrollframes have FrameMetrics and clipping on their container
     // layers, so don't apply clipping again.
     mAddClipRectToLayer = false;
 
-    if (usingDisplayPort) {
-      // There is a display port for this frame, so we want to appear as having
-      // active scrolling, so that animated geometry roots are assigned correctly.
-      MOZ_ASSERT(mShouldBuildScrollableLayer);
-      mIsScrollableLayerInRootContainer = true;
-    }
-
     // If we are a root scroll frame that has a display port we want to add
     // scrollbars, they will be children of the scrollable layer, but they get
     // adjusted by the APZC automatically.
     bool addScrollBars = mIsRoot && usingDisplayPort && !aBuilder->IsForEventDelivery();
 
     if (addScrollBars) {
       // Add classic scrollbars.
       AppendScrollPartsTo(aBuilder, aDirtyRect, aLists, usingDisplayPort,
@@ -2962,63 +2909,32 @@ ScrollFrameHelper::BuildDisplayList(nsDi
   // non-moving frames.
   // Not all our descendants will be clipped by overflow clipping, but all
   // the ones that aren't clipped will be out of flow frames that have already
   // had dirty rects saved for them by their parent frames calling
   // MarkOutOfFlowChildrenForDisplayList, so it's safe to restrict our
   // dirty rect here.
   nsRect dirtyRect = aDirtyRect.Intersect(mScrollPort);
 
-  nsRect displayPort;
-  if (aBuilder->IsPaintingToWindow()) {
-    bool wasUsingDisplayPort = usingDisplayPort;
-
-    if (mIsRoot && gfxPrefs::LayoutUseContainersForRootFrames()) {
-      // For a root frame in a container, just get the value of the existing
-      // display port if any.
-      usingDisplayPort = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayPort);
-    } else {
-      // Override the value of the display port base rect, and possibly create a
-      // display port if there isn't one already.
-      nsRect displayportBase = dirtyRect;
-      if (mIsRoot && mOuter->PresContext()->IsRootContentDocument()) {
-        displayportBase =
-          nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter));
-      }
-      usingDisplayPort = nsLayoutUtils::GetOrMaybeCreateDisplayPort(
-            *aBuilder, mOuter, displayportBase, &displayPort);
-    }
-
-    // Override the dirty rectangle if the displayport has been set.
-    if (usingDisplayPort) {
-      dirtyRect = displayPort;
-
-      // The cached animated geometry root for the display builder is out of
-      // date if we just introduced a new animated geometry root.
-      if (!wasUsingDisplayPort) {
-        aBuilder->RecomputeCurrentAnimatedGeometryRoot();
-      }
-    }
-  }
-
-  // Since making new layers is expensive, only use nsDisplayScrollLayer
-  // if the area is scrollable and we're the content process (unless we're on
-  // B2G, where we support async scrolling for scrollable elements in the
-  // parent process as well).
-  // When a displayport is being used, force building of a layer so that
-  // CompositorParent can always find the scrollable layer for the root content
-  // document.
-  // If the element is marked 'scrollgrab', also force building of a layer
-  // so that APZ can implement scroll grabbing.
-  MOZ_ASSERT(mShouldBuildScrollableLayer == (usingDisplayPort || nsContentUtils::HasScrollgrab(mOuter->GetContent())));
-  bool shouldBuildLayer = false;
-  if (mShouldBuildScrollableLayer) {
-    shouldBuildLayer = true;
+  unused << DecideScrollableLayer(aBuilder, &dirtyRect,
+              /* aAllowCreateDisplayPort = */ !mIsRoot);
+
+  bool usingDisplayPort = aBuilder->IsPaintingToWindow() &&
+    nsLayoutUtils::GetDisplayPort(mOuter->GetContent());
+
+  // Whether we might want to build a scrollable layer for this scroll frame
+  // at some point in the future. This controls whether we add the information
+  // to the layer tree (a scroll info layer if necessary, and add the right
+  // area to the dispatch to content layer event regions) necessary to activate
+  // a scroll frame so it creates a scrollable layer.
+  bool couldBuildLayer = false;
+  if (mWillBuildScrollableLayer) {
+    couldBuildLayer = true;
   } else {
-    shouldBuildLayer =
+    couldBuildLayer =
       nsLayoutUtils::AsyncPanZoomEnabled(mOuter) &&
       WantAsyncScroll() &&
       // If we are using containers for root frames, and we are the root
       // scroll frame for the display root, then we don't need a scroll
       // info layer. nsDisplayList::PaintForFrame already calls
       // ComputeFrameMetrics for us.
       (!(gfxPrefs::LayoutUseContainersForRootFrames() && mIsRoot) ||
        (aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext()));
@@ -3078,17 +2994,17 @@ ScrollFrameHelper::BuildDisplayList(nsDi
   {
     // Note that setting the current scroll parent id here means that positioned children
     // of this scroll info layer will pick up the scroll info layer as their scroll handoff
     // parent. This is intentional because that is what happens for positioned children
     // of scroll layers, and we want to maintain consistent behaviour between scroll layers
     // and scroll info layers.
     nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
         aBuilder,
-        shouldBuildLayer && mScrolledFrame->GetContent()
+        couldBuildLayer && mScrolledFrame->GetContent()
             ? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent())
             : aBuilder->GetCurrentScrollParentId());
 
     // We need to apply at least one clip, potentially more, and each needs to be applied
     // through a separate DisplayListClipState::AutoSaveRestore object.
     // clipStatePtr will always point to the innermost used one.
     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
     DisplayListClipState::AutoSaveRestore* clipStatePtr = &clipState;
@@ -3157,37 +3073,36 @@ ScrollFrameHelper::BuildDisplayList(nsDi
     if (idSetter.ShouldForceLayerForScrollParent() &&
         !gfxPrefs::LayoutUseContainersForRootFrames())
     {
       // Note that forcing layerization of scroll parents follows the scroll
       // handoff chain which is subject to the out-of-flow-frames caveat noted
       // above (where the idSetter variable is created).
       //
       // This is not compatible when using containes for root scrollframes.
-      MOZ_ASSERT(shouldBuildLayer && mScrolledFrame->GetContent());
-      mShouldBuildScrollableLayer = true;
+      MOZ_ASSERT(couldBuildLayer && mScrolledFrame->GetContent());
+      mWillBuildScrollableLayer = true;
     }
   }
 
-  if (mShouldBuildScrollableLayer && !gfxPrefs::LayoutUseContainersForRootFrames()) {
+  if (mWillBuildScrollableLayer && !gfxPrefs::LayoutUseContainersForRootFrames()) {
     aBuilder->ForceLayerForScrollParent();
   }
 
   if (contentBoxClipForNonCaretContent) {
     ClipListsExceptCaret(&scrolledContent, aBuilder, mScrolledFrame,
                          *contentBoxClipForNonCaretContent, usingDisplayPort);
   }
 
-  if (shouldBuildLayer) {
+  if (couldBuildLayer) {
     // Make sure that APZ will dispatch events back to content so we can create
     // a displayport for this frame. We'll add the item later on.
     nsDisplayLayerEventRegions* inactiveRegionItem = nullptr;
     if (aBuilder->IsPaintingToWindow() &&
-        !mShouldBuildScrollableLayer &&
-        shouldBuildLayer &&
+        !mWillBuildScrollableLayer &&
         aBuilder->IsBuildingLayerEventRegions())
     {
       inactiveRegionItem = new (aBuilder) nsDisplayLayerEventRegions(aBuilder, mScrolledFrame);
       inactiveRegionItem->AddInactiveScrollPort(mScrollPort + aBuilder->ToReferenceFrame(mOuter));
     }
 
     if (inactiveRegionItem) {
       nsDisplayList* positionedDescendants = scrolledContent.PositionedDescendants();
@@ -3209,34 +3124,92 @@ ScrollFrameHelper::BuildDisplayList(nsDi
     }
   }
   // Now display overlay scrollbars and the resizer, if we have one.
   AppendScrollPartsTo(aBuilder, aDirtyRect, scrolledContent, usingDisplayPort,
                       createLayersForScrollbars, true);
   scrolledContent.MoveTo(aLists);
 }
 
+bool
+ScrollFrameHelper::DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
+                                         nsRect* aDirtyRect,
+                                         bool aAllowCreateDisplayPort)
+{
+  bool usingDisplayPort = false;
+  nsIContent* content = mOuter->GetContent();
+  if (aBuilder->IsPaintingToWindow()) {
+    bool wasUsingDisplayPort = nsLayoutUtils::GetDisplayPort(content);
+
+    nsRect displayportBase = *aDirtyRect;
+    nsPresContext* pc = mOuter->PresContext();
+    if (mIsRoot && (pc->IsRootContentDocument() || !pc->GetParentPresContext())) {
+      displayportBase =
+        nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter));
+    }
+
+    nsRect displayPort;
+    if (aAllowCreateDisplayPort) {
+      // Provide the value of the display port base rect, and possibly create a
+      // display port if there isn't one already.
+      usingDisplayPort = nsLayoutUtils::GetOrMaybeCreateDisplayPort(
+            *aBuilder, mOuter, displayportBase, &displayPort);
+    } else {
+      // We should have already been called with aAllowCreateDisplayPort == true
+      // which should have set a displayport base.
+      MOZ_ASSERT(content->GetProperty(nsGkAtoms::DisplayPortBase));
+      usingDisplayPort = nsLayoutUtils::GetDisplayPort(content, &displayPort);
+    }
+
+    // Override the dirty rectangle if the displayport has been set.
+    if (usingDisplayPort) {
+      *aDirtyRect = displayPort;
+
+      // The cached animated geometry root for the display builder is out of
+      // date if we just introduced a new animated geometry root.
+      if (!wasUsingDisplayPort) {
+        aBuilder->RecomputeCurrentAnimatedGeometryRoot();
+      }
+    }
+  }
+
+  // Since making new layers is expensive, only create a scrollable layer
+  // for some scroll frames.
+  // When a displayport is being used, force building of a layer so that
+  // the compositor can find the scrollable layer for async scrolling.
+  // If the element is marked 'scrollgrab', also force building of a layer
+  // so that APZ can implement scroll grabbing.
+  mWillBuildScrollableLayer = usingDisplayPort || nsContentUtils::HasScrollgrab(content);
+
+  if (gfxPrefs::LayoutUseContainersForRootFrames() && mWillBuildScrollableLayer && mIsRoot) {
+    mIsScrollableLayerInRootContainer = true;
+  }
+
+  return mWillBuildScrollableLayer;
+}
+
+
 Maybe<DisplayItemClip>
 ScrollFrameHelper::ComputeScrollClip(bool aIsForCaret) const
 {
   const Maybe<DisplayItemClip>& ancestorClip = aIsForCaret ? mAncestorClipForCaret : mAncestorClip;
-  if (!mShouldBuildScrollableLayer || mIsScrollableLayerInRootContainer) {
+  if (!mWillBuildScrollableLayer || mIsScrollableLayerInRootContainer) {
     return Nothing();
   }
 
   return ancestorClip;
 }
 
 Maybe<FrameMetricsAndClip>
 ScrollFrameHelper::ComputeFrameMetrics(Layer* aLayer,
                                        nsIFrame* aContainerReferenceFrame,
                                        const ContainerLayerParameters& aParameters,
                                        bool aIsForCaret) const
 {
-  if (!mShouldBuildScrollableLayer || mIsScrollableLayerInRootContainer) {
+  if (!mWillBuildScrollableLayer || mIsScrollableLayerInRootContainer) {
     return Nothing();
   }
 
   bool needsParentLayerClip = true;
   if (gfxPrefs::LayoutUseContainersForRootFrames() && !mAddClipRectToLayer) {
     // For containerful frames, the clip is on the container frame.
     needsParentLayerClip = false;
   }
@@ -4493,32 +4466,31 @@ ScrollFrameHelper::IsMaybeScrollingActiv
 {
   const nsStyleDisplay* disp = mOuter->StyleDisplay();
   if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL)) {
     return true;
   }
 
   return mHasBeenScrolledRecently ||
       IsAlwaysActive() ||
-      mShouldBuildScrollableLayer;
+      mWillBuildScrollableLayer;
 }
 
 bool
 ScrollFrameHelper::IsScrollingActive(nsDisplayListBuilder* aBuilder) const
 {
   const nsStyleDisplay* disp = mOuter->StyleDisplay();
   if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL) &&
     aBuilder->IsInWillChangeBudget(mOuter, GetScrollPositionClampingScrollPortSize())) {
     return true;
   }
 
   return mHasBeenScrolledRecently ||
         IsAlwaysActive() ||
-        mShouldBuildScrollableLayer ||
-        WillBuildScrollableLayer(aBuilder);
+        mWillBuildScrollableLayer;
 }
 
 /**
  * Reflow the scroll area if it needs it and return its size. Also determine if the reflow will
  * cause any of the scrollbars to need to be reflowed.
  */
 nsresult
 nsXULScrollFrame::Layout(nsBoxLayoutState& aState)
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -76,20 +76,16 @@ public:
 
   void AppendScrollPartsTo(nsDisplayListBuilder*   aBuilder,
                            const nsRect&           aDirtyRect,
                            const nsDisplayListSet& aLists,
                            bool                    aUsingDisplayPort,
                            bool                    aCreateLayer,
                            bool                    aPositioned);
 
-  bool IsUsingDisplayPort(const nsDisplayListBuilder* aBuilder) const;
-  bool WillUseDisplayPort(const nsDisplayListBuilder* aBuilder) const;
-  bool WillBuildScrollableLayer(const nsDisplayListBuilder* aBuilder) const;
-
   bool GetBorderRadii(const nsSize& aFrameSize, const nsSize& aBorderArea,
                       Sides aSkipSides, nscoord aRadii[8]) const;
 
   // nsIReflowCallback
   virtual bool ReflowFinished() override;
   virtual void ReflowCallbackCanceled() override;
 
   /**
@@ -365,16 +361,20 @@ public:
   }
   bool IsTransformingByAPZ() const {
     return mTransformingByAPZ;
   }
   void SetZoomableByAPZ(bool aZoomable);
 
   bool UsesContainerScrolling() const;
 
+  bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
+                             nsRect* aDirtyRect,
+                             bool aAllowCreateDisplayPort);
+
   void ScheduleSyntheticMouseMove();
   static void ScrollActivityCallback(nsITimer *aTimer, void* anInstance);
 
   void HandleScrollbarStyleSwitching();
 
   nsIAtom* LastScrollOrigin() const { return mLastScrollOrigin; }
   nsIAtom* LastSmoothScrollOrigin() const { return mLastSmoothScrollOrigin; }
   uint32_t CurrentScrollGeneration() const { return mScrollGeneration; }
@@ -385,17 +385,17 @@ public:
       mLastSmoothScrollOrigin = nullptr;
     }
   }
   bool WantAsyncScroll() const;
   Maybe<FrameMetricsAndClip> ComputeFrameMetrics(
     Layer* aLayer, nsIFrame* aContainerReferenceFrame,
     const ContainerLayerParameters& aParameters,
     bool aIsForCaret) const;
-  virtual mozilla::Maybe<mozilla::DisplayItemClip> ComputeScrollClip(bool aIsForCaret) const;
+  mozilla::Maybe<mozilla::DisplayItemClip> ComputeScrollClip(bool aIsForCaret) const;
 
   // nsIScrollbarMediator
   void ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
                     nsIScrollbarMediator::ScrollSnapMode aSnap
                       = nsIScrollbarMediator::DISABLE_SNAP);
   void ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
                      nsIScrollbarMediator::ScrollSnapMode aSnap
                        = nsIScrollbarMediator::DISABLE_SNAP);
@@ -505,19 +505,19 @@ public:
   // reflow callback.
   bool mUpdateScrollbarAttributes:1;
   // If true, we should be prepared to scroll using this scrollframe
   // by placing descendant content into its own layer(s)
   bool mHasBeenScrolledRecently:1;
   // If true, the resizer is collapsed and not displayed
   bool mCollapsedResizer:1;
 
-  // If true, the layer should always be active because we always build a
-  // scrollable layer. Used for asynchronous scrolling.
-  bool mShouldBuildScrollableLayer:1;
+  // If true, the scroll frame should always be active because we always build
+  // a scrollable layer. Used for asynchronous scrolling.
+  bool mWillBuildScrollableLayer:1;
 
   // Whether we are the root scroll frame that is used for containerful
   // scrolling with a display port. If true, the scrollable frame
   // shouldn't attach frame metrics to its layers because the container
   // will already have the necessary frame metrics.
   bool mIsScrollableLayerInRootContainer:1;
 
   // If true, add clipping in ScrollFrameHelper::ComputeFrameMetrics.
@@ -872,16 +872,21 @@ public:
     return mHelper.IsIgnoringViewportClipping();
   }
   virtual void MarkScrollbarsDirtyForReflow() const override {
     mHelper.MarkScrollbarsDirtyForReflow();
   }
   virtual bool UsesContainerScrolling() const override {
     return mHelper.UsesContainerScrolling();
   }
+  virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
+                                     nsRect* aDirtyRect,
+                                     bool aAllowCreateDisplayPort) override {
+    return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
+  }
 
   // nsIStatefulFrame
   NS_IMETHOD SaveState(nsPresState** aState) override {
     NS_ENSURE_ARG_POINTER(aState);
     *aState = mHelper.SaveState();
     return NS_OK;
   }
   NS_IMETHOD RestoreState(nsPresState* aState) override {
@@ -1350,16 +1355,22 @@ public:
     return mHelper.UsesContainerScrolling();
   }
   bool IsTransformingByAPZ() const override {
     return mHelper.IsTransformingByAPZ();
   }
   void SetZoomableByAPZ(bool aZoomable) override {
     mHelper.SetZoomableByAPZ(aZoomable);
   }
+  virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
+                                     nsRect* aDirtyRect,
+                                     bool aAllowCreateDisplayPort) override {
+    return mHelper.DecideScrollableLayer(aBuilder, aDirtyRect, aAllowCreateDisplayPort);
+  }
+
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
 protected:
   nsXULScrollFrame(nsStyleContext* aContext, bool aIsRoot,
                    bool aClipAllDescendants);
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -450,11 +450,24 @@ public:
   virtual void SetZoomableByAPZ(bool aZoomable) = 0;
 
   /**
    * Whether or not this frame uses containerful scrolling.
    */
   virtual bool UsesContainerScrolling() const = 0;
 
   virtual mozilla::Maybe<mozilla::DisplayItemClip> ComputeScrollClip(bool aIsForCaret) const = 0;
+
+  /**
+   * Determine if we should build a scrollable layer for this scroll frame and
+   * return the result. It will also record this result on the scroll frame.
+   * Pass the dirty rect in aDirtyRect. On return it will be set to the
+   * displayport if there is one (ie the dirty rect that should be used).
+   * This function may create a display port where one did not exist before if
+   * aAllowCreateDisplayPort is true. It is only allowed to be false if there
+   * has been a call with it set to true before on the same paint.
+   */
+  virtual bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
+                                     nsRect* aDirtyRect,
+                                     bool aAllowCreateDisplayPort) = 0;
 };
 
 #endif
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -411,28 +411,23 @@ nsSubDocumentFrame::BuildDisplayList(nsD
   nsIFrame* savedIgnoreScrollFrame = nullptr;
   if (subdocRootFrame) {
     // get the dirty rect relative to the root frame of the subdoc
     dirty = aDirtyRect + GetOffsetToCrossDoc(subdocRootFrame);
     // and convert into the appunits of the subdoc
     dirty = dirty.ScaleToOtherAppUnitsRoundOut(parentAPD, subdocAPD);
 
     if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
-      if (gfxPrefs::LayoutUseContainersForRootFrames()) {
-        // for root content documents we want the base to be the composition bounds
-        nsRect displayportBase = presContext->IsRootContentDocument() ?
-            nsRect(nsPoint(0,0), nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame)) :
-            dirty.Intersect(nsRect(nsPoint(0,0), subdocRootFrame->GetSize()));
-        nsRect displayPort;
-        if (aBuilder->IsPaintingToWindow() &&
-            nsLayoutUtils::GetOrMaybeCreateDisplayPort(
-              *aBuilder, rootScrollFrame, displayportBase, &displayPort)) {
-          haveDisplayPort = true;
-          dirty = displayPort;
-        }
+      nsIScrollableFrame* rootScrollableFrame = presShell->GetRootScrollFrameAsScrollable();
+      MOZ_ASSERT(rootScrollableFrame);
+      haveDisplayPort = rootScrollableFrame->DecideScrollableLayer(aBuilder,
+                          &dirty, /* aAllowCreateDisplayPort = */ true);
+
+      if (!gfxPrefs::LayoutUseContainersForRootFrames()) {
+        haveDisplayPort = false;
       }
 
       ignoreViewportScrolling = presShell->IgnoringViewportScrolling();
       if (ignoreViewportScrolling) {
         savedIgnoreScrollFrame = aBuilder->GetIgnoreScrollFrame();
         aBuilder->SetIgnoreScrollFrame(rootScrollFrame);
 
         if (aBuilder->IsForImageVisibility()) {
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -13,16 +13,27 @@
 #include "mozilla/css/Loader.h"
 #include "nsIFile.h"
 #include "nsNetUtil.h"
 #include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIXULRuntime.h"
 #include "nsPrintfCString.h"
 
+// Includes for the crash report annotation in ErrorLoadingBuiltinSheet.
+#ifdef MOZ_CRASHREPORTER
+#include "mozilla/Omnijar.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsExceptionHandler.h"
+#include "nsIChromeRegistry.h"
+#include "nsISimpleEnumerator.h"
+#include "nsISubstitutingProtocolHandler.h"
+#endif
+
 using namespace mozilla;
 
 static bool sNumberControlEnabled;
 
 #define NUMBER_CONTROL_PREF "dom.forms.number"
 
 NS_IMPL_ISUPPORTS(
   nsLayoutStylesheetCache, nsIObserver, nsIMemoryReporter)
@@ -449,19 +460,259 @@ nsLayoutStylesheetCache::LoadSheetFile(n
   if (!exists) return;
 
   nsCOMPtr<nsIURI> uri;
   NS_NewFileURI(getter_AddRefs(uri), aFile);
 
   LoadSheet(uri, aSheet, false);
 }
 
+#ifdef MOZ_CRASHREPORTER
+static void
+ListInterestingFiles(nsString& aAnnotation, nsIFile* aFile,
+                     const nsTArray<nsString>& aInterestingFilenames)
+{
+  nsString filename;
+  aFile->GetLeafName(filename);
+  for (const nsString& interestingFilename : aInterestingFilenames) {
+    if (interestingFilename == filename) {
+      nsString path;
+      aFile->GetPath(path);
+      aAnnotation.AppendLiteral("  ");
+      aAnnotation.Append(path);
+      aAnnotation.AppendLiteral(" (");
+      int64_t size;
+      if (NS_SUCCEEDED(aFile->GetFileSize(&size))) {
+        aAnnotation.AppendPrintf("%ld", size);
+      } else {
+        aAnnotation.AppendLiteral("???");
+      }
+      aAnnotation.AppendLiteral(" bytes)\n");
+      return;
+    }
+  }
+
+  bool isDir = false;
+  aFile->IsDirectory(&isDir);
+
+  if (!isDir) {
+    return;
+  }
+
+  nsCOMPtr<nsISimpleEnumerator> entries;
+  if (NS_FAILED(aFile->GetDirectoryEntries(getter_AddRefs(entries)))) {
+    aAnnotation.AppendLiteral("  (failed to enumerated directory)\n");
+    return;
+  }
+
+  for (;;) {
+    bool hasMore = false;
+    if (NS_FAILED(entries->HasMoreElements(&hasMore))) {
+      aAnnotation.AppendLiteral("  (failed during directory enumeration)\n");
+      return;
+    }
+    if (!hasMore) {
+      break;
+    }
+
+    nsCOMPtr<nsISupports> entry;
+    if (NS_FAILED(entries->GetNext(getter_AddRefs(entry)))) {
+      aAnnotation.AppendLiteral("  (failed during directory enumeration)\n");
+      return;
+    }
+
+    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+    if (file) {
+      ListInterestingFiles(aAnnotation, file, aInterestingFilenames);
+    }
+  }
+}
+
+// Generate a crash report annotation to help debug issues with style
+// sheets failing to load (bug 1194856).
+static void
+AnnotateCrashReport(nsIURI* aURI)
+{
+  nsAutoCString spec;
+  nsAutoCString scheme;
+  nsDependentCSubstring filename;
+  if (aURI) {
+    aURI->GetSpec(spec);
+    aURI->GetScheme(scheme);
+    int32_t i = spec.RFindChar('/');
+    if (i != -1) {
+      filename.Rebind(spec, i + 1);
+    }
+  }
+
+  nsString annotation;
+
+  // The URL of the sheet that failed to load.
+  annotation.AppendLiteral("Error loading sheet: ");
+  annotation.Append(NS_ConvertUTF8toUTF16(spec).get());
+  annotation.Append('\n');
+
+  // The jar: or file: URL that the sheet's resource: or chrome: URL
+  // resolves to.
+  if (scheme.EqualsLiteral("resource")) {
+    annotation.AppendLiteral("Real location: ");
+    nsCOMPtr<nsISubstitutingProtocolHandler> handler;
+    nsCOMPtr<nsIIOService> io(do_GetIOService());
+    if (io) {
+      nsCOMPtr<nsIProtocolHandler> ph;
+      io->GetProtocolHandler(scheme.get(), getter_AddRefs(ph));
+      if (ph) {
+        handler = do_QueryInterface(ph);
+      }
+    }
+    if (!handler) {
+      annotation.AppendLiteral("(ResolveURI failed)\n");
+    } else {
+      nsAutoCString resolvedSpec;
+      handler->ResolveURI(aURI, resolvedSpec);
+      annotation.Append(NS_ConvertUTF8toUTF16(resolvedSpec));
+      annotation.Append('\n');
+    }
+  } else if (scheme.EqualsLiteral("chrome")) {
+    annotation.AppendLiteral("Real location: ");
+    nsCOMPtr<nsIChromeRegistry> reg =
+      mozilla::services::GetChromeRegistryService();
+    if (!reg) {
+      annotation.AppendLiteral("(no chrome registry)\n");
+    } else {
+      nsCOMPtr<nsIURI> resolvedURI;
+      reg->ConvertChromeURL(aURI, getter_AddRefs(resolvedURI));
+      if (!resolvedURI) {
+        annotation.AppendLiteral("(ConvertChromeURL failed)\n");
+      } else {
+        nsAutoCString resolvedSpec;
+        resolvedURI->GetSpec(resolvedSpec);
+        annotation.Append(NS_ConvertUTF8toUTF16(resolvedSpec));
+        annotation.Append('\n');
+      }
+    }
+  }
+
+  nsTArray<nsString> interestingFiles;
+  interestingFiles.AppendElement(NS_LITERAL_STRING("chrome.manifest"));
+  interestingFiles.AppendElement(NS_LITERAL_STRING("omni.ja"));
+  interestingFiles.AppendElement(NS_ConvertUTF8toUTF16(filename));
+
+  annotation.AppendLiteral("GRE directory: ");
+  nsCOMPtr<nsIFile> file;
+  nsDirectoryService::gService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile),
+                                    getter_AddRefs(file));
+  if (file) {
+    // The Firefox installation directory.
+    nsString path;
+    file->GetPath(path);
+    annotation.Append(path);
+    annotation.Append('\n');
+
+    // List interesting files -- any chrome.manifest or omni.ja file or any file
+    // whose name is the sheet's filename -- under the Firefox installation
+    // directory.
+    annotation.AppendLiteral("Interesting files in the GRE directory:\n");
+    ListInterestingFiles(annotation, file, interestingFiles);
+
+    // If the Firefox installation directory has a chrome.manifest file, let's
+    // see what's in it.
+    file->Append(NS_LITERAL_STRING("chrome.manifest"));
+    bool exists = false;
+    file->Exists(&exists);
+    if (exists) {
+      annotation.AppendLiteral("Contents of chrome.manifest:\n[[[\n");
+      PRFileDesc* fd;
+      if (NS_SUCCEEDED(file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd))) {
+        nsCString contents;
+        char buf[512];
+        int32_t n;
+        while ((n = PR_Read(fd, buf, sizeof(buf))) > 0) {
+          contents.Append(buf, n);
+        }
+        if (n < 0) {
+          annotation.AppendLiteral("  (error while reading)\n");
+        } else {
+          annotation.Append(NS_ConvertUTF8toUTF16(contents));
+        }
+        PR_Close(fd);
+      }
+      annotation.AppendLiteral("]]]\n");
+    }
+  } else {
+    annotation.AppendLiteral("(none)\n");
+  }
+
+  // The jar: or file: URL prefix that chrome: and resource: URLs get translated
+  // to.
+  annotation.AppendLiteral("GRE omnijar URI string: ");
+  nsCString uri;
+  nsresult rv = Omnijar::GetURIString(Omnijar::GRE, uri);
+  if (NS_FAILED(rv)) {
+    annotation.AppendLiteral("(failed)\n");
+  } else {
+    annotation.Append(NS_ConvertUTF8toUTF16(uri));
+    annotation.Append('\n');
+  }
+
+  nsRefPtr<nsZipArchive> zip = Omnijar::GetReader(Omnijar::GRE);
+  if (zip) {
+    // List interesting files in the GRE omnijar.
+    annotation.AppendLiteral("Interesting files in the GRE omnijar:\n");
+    nsZipFind* find;
+    rv = zip->FindInit(nullptr, &find);
+    if (NS_FAILED(rv)) {
+      annotation.AppendPrintf("  (FindInit failed with 0x%08x)\n", rv);
+    } else if (!find) {
+      annotation.AppendLiteral("  (FindInit returned null)\n");
+    } else {
+      const char* result;
+      uint16_t len;
+      while (NS_SUCCEEDED(find->FindNext(&result, &len))) {
+        nsCString itemPathname;
+        nsString itemFilename;
+        itemPathname.Append(result, len);
+        int32_t i = itemPathname.RFindChar('/');
+        if (i != -1) {
+          itemFilename = NS_ConvertUTF8toUTF16(Substring(itemPathname, i + 1));
+        }
+        for (const nsString& interestingFile : interestingFiles) {
+          if (interestingFile == itemFilename) {
+            annotation.AppendLiteral("  ");
+            annotation.Append(NS_ConvertUTF8toUTF16(itemPathname));
+            nsZipItem* item = zip->GetItem(itemPathname.get());
+            if (!item) {
+              annotation.AppendLiteral(" (GetItem failed)\n");
+            } else {
+              annotation.AppendPrintf(" (%d bytes, crc32 = 0x%08x)\n",
+                                      item->RealSize(),
+                                      item->CRC32());
+            }
+            break;
+          }
+        }
+      }
+      delete find;
+    }
+  } else {
+    annotation.AppendLiteral("No GRE omnijar\n");
+  }
+
+  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("SheetLoadFailure"),
+                                     NS_ConvertUTF16toUTF8(annotation));
+}
+#endif
+
 static void
 ErrorLoadingBuiltinSheet(nsIURI* aURI, const char* aMsg)
 {
+#ifdef MOZ_CRASHREPORTER
+  AnnotateCrashReport(aURI);
+#endif
+
   nsAutoCString spec;
   if (aURI) {
     aURI->GetSpec(spec);
   }
   NS_RUNTIMEABORT(nsPrintfCString("%s loading built-in stylesheet '%s'",
                                   aMsg, spec.get()).get());
 }
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -927,20 +927,16 @@ pref("nglayout.debug.paint_flashing_chro
 pref("nglayout.debug.widget_update_flashing", false);
 
 // Enable/disable display list invalidation logging --- useful for debugging.
 pref("nglayout.debug.invalidation", false);
 
 // Whether image visibility is enabled globally (ie we will try to unlock images
 // that are not visible).
 pref("layout.imagevisibility.enabled", true);
-// Whether image visibility is enabled in documents that are within a browser
-// element as defined by nsDocShell::FrameType and GetInheritedFrameType. This
-// pref only has an effect if layout.imagevisibility.enabled is false.
-pref("layout.imagevisibility.enabled_for_browser_elements_only", false);
 pref("layout.imagevisibility.numscrollportwidths", 0);
 pref("layout.imagevisibility.numscrollportheights", 1);
 
 // scrollbar snapping region
 // 0 - off
 // 1 and higher - slider thickness multiple
 pref("slider.snapMultiplier", 0);
 
--- a/mozglue/linker/ElfLoader.h
+++ b/mozglue/linker/ElfLoader.h
@@ -3,16 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef ElfLoader_h
 #define ElfLoader_h
 
 #include <vector>
 #include <dlfcn.h>
 #include <signal.h>
+#include "mozilla/RefCounted.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "Zip.h"
 #include "Elfxx.h"
 #include "Mappable.h"
 
 /**
  * dlfcn.h replacement functions
--- a/mozglue/linker/Zip.h
+++ b/mozglue/linker/Zip.h
@@ -6,16 +6,17 @@
 #define Zip_h
 
 #include <cstring>
 #include <stdint.h>
 #include <vector>
 #include <zlib.h>
 #include "Utils.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/RefCounted.h"
 #include "mozilla/RefPtr.h"
 
 /**
  * Helper class wrapping z_stream to avoid malloc() calls during
  * inflate. Do not use for deflate.
  * inflateInit allocates two buffers:
  * - one for its internal state, which is "approximately 10K bytes" according
  *   to inflate.h from zlib.
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -2581,19 +2581,16 @@ def run_test_harness(options):
         if key.startswith('log') or key == 'valgrind' }
     runner = Mochitest(logger_options)
 
     options.runByDir = False
 
     if runner.getTestFlavor(options) == 'mochitest':
         options.runByDir = True
 
-    if mozinfo.info['asan'] and options.e10s:
-        options.runByDir = False
-
     if runner.getTestFlavor(options) == 'browser-chrome':
         options.runByDir = True
 
     if runner.getTestFlavor(options) == 'chrome':
         options.runByDir = True
 
     if mozinfo.info.get('buildapp') == 'mulet':
         options.runByDir = False
deleted file mode 100644
--- a/testing/web-platform/meta/dom/nodes/Node-isEqualNode.xhtml.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[Node-isEqualNode.xhtml]
-  type: testharness
-  [isEqualNode should return true when only the internal subsets of DocumentTypes differ.]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-03.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[document.title-03.html]
-  type: testharness
-  [ document.title  and space normalization  ]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-04.xhtml.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[document.title-04.xhtml]
-  type: testharness
-  [ document.title  and space normalization  ]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/document.title-07.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[document.title-07.html]
-  type: testharness
-  [createHTMLDocument test 6: "foo\\f\\fbar baz","foo\\f\\fbar baz","foo bar baz"]
-    expected: FAIL
-
--- a/testing/web-platform/meta/html/editing/focus/focus-management/focus-events.html.ini
+++ b/testing/web-platform/meta/html/editing/focus/focus-management/focus-events.html.ini
@@ -5,16 +5,14 @@
     if e10s and (os == "linux"): https://bugzilla.mozilla.org/show_bug.cgi?id=1203917
   expected:
     if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): TIMEOUT
     if not debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): TIMEOUT
   [focusing on a focusable element fires a focus event at the element]
     expected:
       if not debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): NOTRUN
       if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): NOTRUN
-      FAIL
 
   [focusing on a focusable element fires a blur event at the previous focussed element]
     expected:
       if not debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): NOTRUN
       if debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): NOTRUN
-      FAIL
 
deleted file mode 100644
--- a/testing/web-platform/meta/html/semantics/forms/the-option-element/option-text-spaces.html.ini
+++ /dev/null
@@ -1,41 +0,0 @@
-[option-text-spaces.html]
-  type: testharness
-  [option.text should strip leading space characters ("\\f")]
-    expected: FAIL
-
-  [option.text should strip trailing space characters ("\\f")]
-    expected: FAIL
-
-  [option.text should strip leading and trailing space characters ("\\f")]
-    expected: FAIL
-
-  [option.text should replace single internal space characters ("\\f")]
-    expected: FAIL
-
-  [option.text should replace multiple internal space characters (" ", "\\f")]
-    expected: FAIL
-
-  [option.text should replace multiple internal space characters ("\\t", "\\f")]
-    expected: FAIL
-
-  [option.text should replace multiple internal space characters ("\\n", "\\f")]
-    expected: FAIL
-
-  [option.text should replace multiple internal space characters ("\\f", " ")]
-    expected: FAIL
-
-  [option.text should replace multiple internal space characters ("\\f", "\\t")]
-    expected: FAIL
-
-  [option.text should replace multiple internal space characters ("\\f", "\\n")]
-    expected: FAIL
-
-  [option.text should replace multiple internal space characters ("\\f", "\\f")]
-    expected: FAIL
-
-  [option.text should replace multiple internal space characters ("\\f", "\\r")]
-    expected: FAIL
-
-  [option.text should replace multiple internal space characters ("\\r", "\\f")]
-    expected: FAIL
-
--- a/toolkit/components/extensions/ext-webRequest.js
+++ b/toolkit/components/extensions/ext-webRequest.js
@@ -89,16 +89,39 @@ function WebRequestEventManager(context,
   return SingletonEventManager.call(this, context, name, register);
 }
 
 WebRequestEventManager.prototype = Object.create(SingletonEventManager.prototype);
 
 extensions.registerPrivilegedAPI("webRequest", (extension, context) => {
   return {
     webRequest: {
+      ResourceType: {
+        MAIN_FRAME: "main_frame",
+        SUB_FRAME: "sub_frame",
+        STYLESHEET: "stylesheet",
+        SCRIPT: "script",
+        IMAGE: "image",
+        OBJECT: "object",
+        OBJECT_SUBREQUEST: "object_subrequest",
+        XMLHTTPREQUEST: "xmlhttprequest",
+        XBL: "xbl",
+        XSLT: "xslt",
+        PING: "ping",
+        BEACON: "beacon",
+        XML_DTD: "xml_dtd",
+        FONT: "font",
+        MEDIA: "media",
+        WEBSOCKET: "websocket",
+        CSP_REPORT: "csp_report",
+        IMAGESET: "imageset",
+        WEB_MANIFEST: "web_manifest",
+        OTHER: "other",
+      },
+
       onBeforeRequest: new WebRequestEventManager(context, "onBeforeRequest").api(),
       onBeforeSendHeaders: new WebRequestEventManager(context, "onBeforeSendHeaders").api(),
       onSendHeaders: new WebRequestEventManager(context, "onSendHeaders").api(),
       onHeadersReceived: new WebRequestEventManager(context, "onHeadersReceived").api(),
       onResponseStarted: new WebRequestEventManager(context, "onResponseStarted").api(),
       onCompleted: new WebRequestEventManager(context, "onCompleted").api(),
       handlerBehaviorChanged: function() {
         // TODO: Flush all caches.
--- a/toolkit/components/extensions/moz.build
+++ b/toolkit/components/extensions/moz.build
@@ -10,10 +10,8 @@ EXTRA_JS_MODULES += [
     'ExtensionManagement.jsm',
     'ExtensionStorage.jsm',
     'ExtensionUtils.jsm',
 ]
 
 JAR_MANIFESTS += ['jar.mn']
 
 MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
-
-DIRS += ['test/extensions']
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/background/background.js
+++ /dev/null
@@ -1,10 +0,0 @@
-browser.test.log("running background script");
-
-browser.test.onMessage.addListener((x, y) => {
-  browser.test.assertEq(x, 10, "x is 10");
-  browser.test.assertEq(y, 20, "y is 20");
-
-  browser.test.notifyPass("background test passed");
-});
-
-browser.test.sendMessage("running", 1);
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/background/manifest.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "name": "Simple extension test",
-  "version": "1.0",
-  "manifest_version": 2,
-  "description": "",
-  "background": {
-    "scripts": ["background.js"]
-  }
-}
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/background/moz.build
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-TESTING_JS_MODULES.extensions.background += [
-    'background.js',
-    'manifest.json',
-]
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/content_script/background.js
+++ /dev/null
@@ -1,10 +0,0 @@
-browser.runtime.onMessage.addListener(([msg, expectedState, readyState], sender) => {
-  if (msg == "chrome-namespace-ok") {
-    browser.test.sendMessage(msg);
-    return;
-  }
-
-  browser.test.assertEq(msg, "script-run", "message type is correct");
-  browser.test.assertEq(readyState, expectedState, "readyState is correct");
-  browser.test.sendMessage("script-run-" + expectedState);
-});
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/content_script/content_script.js
+++ /dev/null
@@ -1,1 +0,0 @@
-chrome.runtime.sendMessage(["chrome-namespace-ok"]);
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/content_script/content_script_end.js
+++ /dev/null
@@ -1,1 +0,0 @@
-browser.runtime.sendMessage(["script-run", "interactive", document.readyState]);
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/content_script/content_script_idle.js
+++ /dev/null
@@ -1,1 +0,0 @@
-browser.runtime.sendMessage(["script-run", "complete", document.readyState]);
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/content_script/content_script_start.js
+++ /dev/null
@@ -1,1 +0,0 @@
-browser.runtime.sendMessage(["script-run", "loading", document.readyState]);
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/content_script/manifest.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
-  "name": "Content script extension test",
-  "version": "1.0",
-  "manifest_version": 2,
-  "description": "",
-
-  "content_scripts": [
-    {
-      "matches": ["http://mochi.test/*/file_sample.html"],
-      "js": ["content_script_start.js"],
-      "run_at": "document_start"
-    },
-    {
-      "matches": ["http://mochi.test/*/file_sample.html"],
-      "js": ["content_script_end.js"],
-      "run_at": "document_end"
-    },
-    {
-      "matches": ["http://mochi.test/*/file_sample.html"],
-      "js": ["content_script_idle.js"],
-      "run_at": "document_idle"
-    },
-    {
-      "matches": ["http://mochi.test/*/file_sample.html"],
-      "js": ["content_script.js"],
-      "run_at": "document_idle"
-    }
-  ],
-
-  "background": {
-    "scripts": ["background.js"]
-  }
-}
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/content_script/moz.build
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-TESTING_JS_MODULES.extensions.content_script += [
-    'background.js',
-    'content_script.js',
-    'content_script_end.js',
-    'content_script_idle.js',
-    'content_script_start.js',
-    'manifest.json',
-]
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/moz.build
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-DIRS += [
-    'background',
-    'content_script',
-    'simple',
-    'notifications',
-    'webrequest',
-]
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/notifications/background.js
+++ /dev/null
@@ -1,9 +0,0 @@
-browser.test.log("running background script");
-
-var opts = {title: "Testing Notification", message: "Carry on"};
-
-browser.notifications.create("5", opts, function(id){
-  browser.test.sendMessage("running", id);
-  browser.test.notifyPass("background test passed");
-});
-
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/notifications/manifest.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "name": "Notifications Test",
-  "version": "1.0",
-  "manifest_version": 2,
-  "description": "",
-  "background": {
-    "scripts": ["background.js"]
-  },
-  "permissions": [
-    "notifications"
-  ]
-}
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/notifications/moz.build
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-TESTING_JS_MODULES.extensions.notifications += [
-    'background.js',
-    'manifest.json',
-]
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/simple/manifest.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "name": "Simple extension test",
-  "version": "1.0",
-  "manifest_version": 2,
-  "description": ""
-}
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/simple/moz.build
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-TESTING_JS_MODULES.extensions.simple += [
-    'manifest.json',
-]
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/webrequest/background.js
+++ /dev/null
@@ -1,97 +0,0 @@
-const BASE = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest";
-
-var savedTabId = -1;
-
-function checkType(details)
-{
-  var expected_type = "???";
-  if (details.url.indexOf("style") != -1) {
-    expected_type = "stylesheet";
-  } else if (details.url.indexOf("image") != -1) {
-    expected_type = "image";
-  } else if (details.url.indexOf("script") != -1) {
-    expected_type = "script";
-  } else if (details.url.indexOf("page1") != -1) {
-    expected_type = "main_frame";
-  } else if (details.url.indexOf("page2") != -1) {
-    expected_type = "sub_frame";
-  } else if (details.url.indexOf("xhr") != -1) {
-    expected_type = "xmlhttprequest";
-  }
-  browser.test.assertEq(details.type, expected_type, "resource type is correct");
-}
-
-var frameIDs = new Map();
-
-var requested = [];
-
-function onBeforeRequest(details)
-{
-  browser.test.log(`onBeforeRequest ${details.url}`);
-  if (details.url.startsWith(BASE)) {
-    requested.push(details.url);
-
-    if (savedTabId == -1) {
-      browser.test.assertTrue(details.tabId !== undefined, "tab ID defined");
-      savedTabId = details.tabId;
-    }
-
-    browser.test.assertEq(details.tabId, savedTabId, "correct tab ID");
-    checkType(details);
-
-    frameIDs.set(details.url, details.frameId);
-    if (details.url.indexOf("page1") != -1) {
-      browser.test.assertEq(details.frameId, 0, "frame ID correct");
-      browser.test.assertEq(details.parentFrameId, -1, "parent frame ID correct");
-    }
-    if (details.url.indexOf("page2") != -1) {
-      browser.test.assertTrue(details.frameId != 0, "sub-frame gets its own frame ID");
-      browser.test.assertTrue(details.frameId !== undefined, "sub-frame ID defined");
-      browser.test.assertEq(details.parentFrameId, 0, "parent frame id is correct");
-    }
-  }
-  if (details.url.indexOf("_bad.") != -1) {
-    return {cancel: true};
-  }
-}
-
-var sendHeaders = [];
-
-function onBeforeSendHeaders(details)
-{
-  browser.test.log(`onBeforeSendHeaders ${details.url}`);
-  if (details.url.startsWith(BASE)) {
-    sendHeaders.push(details.url);
-
-    browser.test.assertEq(details.tabId, savedTabId, "correct tab ID");
-    checkType(details);
-
-    var id = frameIDs.get(details.url);
-    browser.test.assertEq(id, details.frameId, "frame ID same in onBeforeSendHeaders as onBeforeRequest");
-  }
-  if (details.url.indexOf("_redirect.") != -1) {
-    return {redirectUrl: details.url.replace("_redirect.", "_good.")};
-  }
-}
-
-var headersReceived = [];
-
-function onResponseStarted(details)
-{
-  if (details.url.startsWith(BASE)) {
-    headersReceived.push(details.url);
-  }
-}
-
-browser.webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: "<all_urls>"}, ["blocking"]);
-browser.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: "<all_urls>"}, ["blocking"]);
-browser.webRequest.onResponseStarted.addListener(onResponseStarted, {urls: "<all_urls>"});
-
-function onTestMessage()
-{
-  browser.test.sendMessage("results", [requested, sendHeaders, headersReceived]);
-}
-
-browser.test.onMessage.addListener(onTestMessage);
-
-browser.test.sendMessage("ready");
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/webrequest/manifest.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "name": "webRequest extension test",
-  "version": "1.0",
-  "manifest_version": 2,
-  "description": "",
-  "background": {
-    "scripts": ["background.js"]
-  },
-  "permissions": [
-    "webRequest",
-    "webRequestBlocking"
-  ]
-}
deleted file mode 100644
--- a/toolkit/components/extensions/test/extensions/webrequest/moz.build
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-TESTING_JS_MODULES.extensions.webrequest += [
-    'background.js',
-    'manifest.json',
-]
--- a/toolkit/components/extensions/test/mochitest/test_ext_contentscript.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript.html
@@ -10,17 +10,79 @@
 </head>
 <body>
 
 <script type="application/javascript;version=1.8">
 "use strict";
 
 add_task(function* test_contentscript()
 {
-  let extension = ExtensionTestUtils.loadExtension("content_script");
+  function backgroundScript() {
+    browser.runtime.onMessage.addListener(([msg, expectedState, readyState], sender) => {
+      if (msg == "chrome-namespace-ok") {
+        browser.test.sendMessage(msg);
+        return;
+      }
+
+      browser.test.assertEq(msg, "script-run", "message type is correct");
+      browser.test.assertEq(readyState, expectedState, "readyState is correct");
+      browser.test.sendMessage("script-run-" + expectedState);
+    });
+  }
+
+  function contentScriptStart() {
+    browser.runtime.sendMessage(["script-run", "loading", document.readyState]);
+  }
+  function contentScriptEnd() {
+    browser.runtime.sendMessage(["script-run", "interactive", document.readyState]);
+  }
+  function contentScriptIdle() {
+    browser.runtime.sendMessage(["script-run", "complete", document.readyState]);
+  }
+
+  function contentScript() {
+    chrome.runtime.sendMessage(["chrome-namespace-ok"]);
+  }
+
+  let extensionData = {
+    manifest: {
+      content_scripts: [
+        {
+          "matches": ["http://mochi.test/*/file_sample.html"],
+          "js": ["content_script_start.js"],
+          "run_at": "document_start"
+        },
+        {
+          "matches": ["http://mochi.test/*/file_sample.html"],
+          "js": ["content_script_end.js"],
+          "run_at": "document_end"
+        },
+        {
+          "matches": ["http://mochi.test/*/file_sample.html"],
+          "js": ["content_script_idle.js"],
+          "run_at": "document_idle"
+        },
+        {
+          "matches": ["http://mochi.test/*/file_sample.html"],
+          "js": ["content_script.js"],
+          "run_at": "document_idle"
+        }
+      ],
+    },
+    background: "(" + backgroundScript.toString() + ")()",
+
+    files: {
+      "content_script_start.js": "(" + contentScriptStart.toString() + ")()",
+      "content_script_end.js": "(" + contentScriptEnd.toString() + ")()",
+      "content_script_idle.js": "(" + contentScriptIdle.toString() + ")()",
+      "content_script.js": "(" + contentScript.toString() + ")()",
+    },
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
 
   let loadingCount = 0;
   let interactiveCount = 0;
   let completeCount = 0;
   extension.onMessage("script-run-loading", () => { loadingCount++; });
   extension.onMessage("script-run-interactive", () => { interactiveCount++; });
 
   let completePromise = new Promise(resolve => {
--- a/toolkit/components/extensions/test/mochitest/test_ext_notifications.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_notifications.html
@@ -7,17 +7,35 @@
   <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 
 <script type="application/javascript;version=1.8">
 
 add_task(function* test_notifications() {
-  let extension = ExtensionTestUtils.loadExtension("notifications");
+  function backgroundScript() {
+    browser.test.log("running background script");
+
+    var opts = {title: "Testing Notification", message: "Carry on"};
+
+    browser.notifications.create("5", opts, function(id) {
+      browser.test.sendMessage("running", id);
+      browser.test.notifyPass("background test passed");
+    });
+  }
+
+  let extensionData = {
+    manifest: {
+      permissions: ["notifications"]
+    },
+    background: "(" + backgroundScript.toString() + ")()"
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
   info("load complete");
   yield extension.startup();
   info("startup complete");
   let x = yield extension.awaitMessage("running");
   is(x, "5", "got correct value from extension");
   yield extension.awaitFinish();
   info("test complete");
   yield extension.unload();
--- a/toolkit/components/extensions/test/mochitest/test_ext_simple.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_simple.html
@@ -8,26 +8,58 @@
   <script type="text/javascript" src="head.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 
 <script type="application/javascript;version=1.8">
 
 add_task(function* test_simple() {
-  let extension = ExtensionTestUtils.loadExtension("simple");
+  let extensionData = {
+    manifest: {
+      "name": "Simple extension test",
+      "version": "1.0",
+      "manifest_version": 2,
+      "description": ""
+    }
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
   info("load complete");
   yield extension.startup();
   info("startup complete");
   yield extension.unload();
   info("extension unloaded successfully");
 });
 
 add_task(function* test_background() {
-  let extension = ExtensionTestUtils.loadExtension("background");
+  function backgroundScript() {
+    browser.test.log("running background script");
+
+    browser.test.onMessage.addListener((x, y) => {
+      browser.test.assertEq(x, 10, "x is 10");
+      browser.test.assertEq(y, 20, "y is 20");
+
+      browser.test.notifyPass("background test passed");
+    });
+
+    browser.test.sendMessage("running", 1);
+  }
+
+  let extensionData = {
+    background: "(" + backgroundScript.toString() + ")()",
+    manifest: {
+      "name": "Simple extension test",
+      "version": "1.0",
+      "manifest_version": 2,
+      "description": ""
+    }
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
   info("load complete");
   let [, x] = yield Promise.all([extension.startup(), extension.awaitMessage("running")]);
   is(x, 1, "got correct value from extension");
   info("startup complete");
   extension.sendMessage(10, 20);
   yield extension.awaitFinish();
   info("test complete");
   yield extension.unload();
--- a/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html
@@ -37,24 +37,24 @@ const expected_sendHeaders = [BASE + "/f
                               BASE + "/file_image_redirect.png",
                               BASE + "/file_script_good.js",
                               BASE + "/file_script_redirect.js",
                               BASE + "/file_script_xhr.js",
                               BASE + "/file_WebRequest_page2.html",
                               BASE + "/nonexistent_script_url.js",
                               BASE + "/xhr_resource"];
 
-const expected_headersReceived = [BASE + "/file_WebRequest_page1.html",
-                                  BASE + "/file_style_good.css",
-                                  BASE + "/file_image_good.png",
-                                  BASE + "/file_script_good.js",
-                                  BASE + "/file_script_xhr.js",
-                                  BASE + "/file_WebRequest_page2.html",
-                                  BASE + "/nonexistent_script_url.js",
-                                  BASE + "/xhr_resource"];
+const expected_complete = [BASE + "/file_WebRequest_page1.html",
+                           BASE + "/file_style_good.css",
+                           BASE + "/file_image_good.png",
+                           BASE + "/file_script_good.js",
+                           BASE + "/file_script_xhr.js",
+                           BASE + "/file_WebRequest_page2.html",
+                           BASE + "/nonexistent_script_url.js",
+                           BASE + "/xhr_resource"];
 
 function removeDupes(list)
 {
   let j = 0;
   for (let i = 1; i < list.length; i++) {
     if (list[i] != list[j]) {
       j++;
       if (i != j) {
@@ -69,22 +69,151 @@ function compareLists(list1, list2, kind
 {
   list1.sort();
   removeDupes(list1);
   list2.sort();
   removeDupes(list2);
   is(String(list1), String(list2), `${kind} URLs correct`);
 }
 
+function backgroundScript()
+{
+  const BASE = "http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest";
+
+  var savedTabId = -1;
+
+  function checkType(details)
+  {
+    var expected_type = "???";
+    if (details.url.indexOf("style") != -1) {
+      expected_type = "stylesheet";
+    } else if (details.url.indexOf("image") != -1) {
+      expected_type = "image";
+    } else if (details.url.indexOf("script") != -1) {
+      expected_type = "script";
+    } else if (details.url.indexOf("page1") != -1) {
+      expected_type = "main_frame";
+    } else if (details.url.indexOf("page2") != -1) {
+      expected_type = "sub_frame";
+    } else if (details.url.indexOf("xhr") != -1) {
+      expected_type = "xmlhttprequest";
+    }
+    browser.test.assertEq(details.type, expected_type, "resource type is correct");
+  }
+
+  var frameIDs = new Map();
+
+  var recorded = {requested: [],
+                  beforeSendHeaders: [],
+                  sendHeaders: [],
+                  responseStarted: [],
+                  completed: []};
+
+  function checkResourceType(type)
+  {
+    var key = type.toUpperCase();
+    browser.test.assertTrue(key in browser.webRequest.ResourceType);
+  }
+
+  function onBeforeRequest(details)
+  {
+    browser.test.log(`onBeforeRequest ${details.url}`);
+    checkResourceType(details.type);
+    if (details.url.startsWith(BASE)) {
+      recorded.requested.push(details.url);
+
+      if (savedTabId == -1) {
+        browser.test.assertTrue(details.tabId !== undefined, "tab ID defined");
+        savedTabId = details.tabId;
+      }
+
+      browser.test.assertEq(details.tabId, savedTabId, "correct tab ID");
+      checkType(details);
+
+      frameIDs.set(details.url, details.frameId);
+      if (details.url.indexOf("page1") != -1) {
+        browser.test.assertEq(details.frameId, 0, "frame ID correct");
+        browser.test.assertEq(details.parentFrameId, -1, "parent frame ID correct");
+      }
+      if (details.url.indexOf("page2") != -1) {
+        browser.test.assertTrue(details.frameId != 0, "sub-frame gets its own frame ID");
+        browser.test.assertTrue(details.frameId !== undefined, "sub-frame ID defined");
+        browser.test.assertEq(details.parentFrameId, 0, "parent frame id is correct");
+      }
+    }
+    if (details.url.indexOf("_bad.") != -1) {
+      return {cancel: true};
+    }
+    return {};
+  }
+
+  function onBeforeSendHeaders(details)
+  {
+    browser.test.log(`onBeforeSendHeaders ${details.url}`);
+    checkResourceType(details.type);
+    if (details.url.startsWith(BASE)) {
+      recorded.beforeSendHeaders.push(details.url);
+
+      browser.test.assertEq(details.tabId, savedTabId, "correct tab ID");
+      checkType(details);
+
+      var id = frameIDs.get(details.url);
+      browser.test.assertEq(id, details.frameId, "frame ID same in onBeforeSendHeaders as onBeforeRequest");
+    }
+    if (details.url.indexOf("_redirect.") != -1) {
+      return {redirectUrl: details.url.replace("_redirect.", "_good.")};
+    }
+    return {};
+  }
+
+  function onRecord(kind, details)
+  {
+    checkResourceType(details.type);
+    if (details.url.startsWith(BASE)) {
+      recorded[kind].push(details.url);
+    }
+  }
+
+  browser.webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: "<all_urls>"}, ["blocking"]);
+  browser.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: "<all_urls>"}, ["blocking"]);
+  browser.webRequest.onSendHeaders.addListener(onRecord.bind(null, "sendHeaders"), {urls: "<all_urls>"});
+  browser.webRequest.onResponseStarted.addListener(onRecord.bind(null, "responseStarted"), {urls: "<all_urls>"});
+  browser.webRequest.onCompleted.addListener(onRecord.bind(null, "completed"), {urls: "<all_urls>"});
+
+  function onTestMessage()
+  {
+    browser.test.sendMessage("results", recorded);
+  }
+
+  browser.test.onMessage.addListener(onTestMessage);
+
+  browser.test.sendMessage("ready", browser.webRequest.ResourceType);
+}
+
 function* test_once()
 {
-  let extension = ExtensionTestUtils.loadExtension("webrequest");
-  yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
+  let extensionData = {
+    manifest: {
+      permissions: [
+        "webRequest",
+        "webRequestBlocking"
+      ],
+    },
+    background: "(" + backgroundScript.toString() + ")()",
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
+  let [, resourceTypes] = yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
   info("webrequest extension loaded");
 
+  for (var key in resourceTypes) {
+    var value = resourceTypes[key];
+    is(key, value.toUpperCase());
+  }
+
   yield new Promise(resolve => { setTimeout(resolve, 0); });
 
   let win = window.open();
 
   // Clear the image cache, since it gets in the way otherwise.
   var imgTools = SpecialPowers.Cc["@mozilla.org/image/tools;1"].getService(SpecialPowers.Ci.imgITools);
   var cache = imgTools.getImgCacheForDocument(win.document);
   cache.clearCache(false);
@@ -101,21 +230,23 @@ function* test_once()
   is(win.failure, undefined, "Failure script didn't run");
 
   let style = win.getComputedStyle(win.document.getElementById("test"), null);
   is(style.getPropertyValue("color"), "rgb(255, 0, 0)", "Good CSS loaded");
 
   win.close();
 
   extension.sendMessage("getResults");
-  let [requested, sendHeaders, headersReceived] = yield extension.awaitMessage("results");
+  let recorded = yield extension.awaitMessage("results");
 
-  compareLists(requested, expected_requested, "requested");
-  compareLists(sendHeaders, expected_sendHeaders, "sendHeaders");
-  compareLists(headersReceived, expected_headersReceived, "headersReceived");
+  compareLists(recorded.requested, expected_requested, "requested");
+  compareLists(recorded.beforeSendHeaders, expected_sendHeaders, "beforeSendHeaders");
+  compareLists(recorded.sendHeaders, expected_complete, "sendHeaders");
+  compareLists(recorded.responseStarted, expected_complete, "responseStarted");
+  compareLists(recorded.completed, expected_complete, "completed");
 
   yield extension.unload();
   info("webrequest extension unloaded");
 }
 
 // Run the test twice to make sure it works with caching.
 add_task(test_once);
 add_task(test_once);
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -117,35 +117,36 @@ var ContentPolicyManager = {
 
     this.policyData.delete(id);
     this.idMap.delete(callback);
     this.policies.delete(id);
   },
 };
 ContentPolicyManager.init();
 
-function StartStopListener(manager)
+function StartStopListener(manager, loadContext)
 {
   this.manager = manager;
+  this.loadContext = loadContext;
   this.orig = null;
 }
 
 StartStopListener.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver,
                                          Ci.nsIStreamListener,
                                          Ci.nsISupports]),
 
   onStartRequest: function(request, context) {
-    this.manager.onStartRequest(request);
+    this.manager.onStartRequest(request, this.loadContext);
     return this.orig.onStartRequest(request, context);
   },
 
   onStopRequest(request, context, statusCode) {
     let result = this.orig.onStopRequest(request, context, statusCode);
-    this.manager.onStopRequest(request);
+    this.manager.onStopRequest(request, this.loadContext);
     return result;
   },
 
   onDataAvailable(...args) {
     return this.orig.onDataAvailable(...args);
   }
 };
 
@@ -224,34 +225,34 @@ var HttpObserverManager = {
                                              Ci.nsISupports]),
     };
 
     channel[method](visitor);
     return headers;
   },
 
   observe(subject, topic, data) {
+    let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+
     if (topic == "http-on-modify-request") {
-      this.modify(subject, topic, data);
+      this.modify(channel, topic, data);
     } else if (topic == "http-on-examine-response" ||
                topic == "http-on-examine-cached-response" ||
                topic == "http-on-examine-merged-response") {
-      this.examine(subject, topic, data);
+      this.examine(channel, topic, data);
     }
   },
 
   shouldRunListener(policyType, uri, filter) {
     return WebRequestCommon.typeMatches(policyType, filter.types) &&
            WebRequestCommon.urlMatches(uri, filter.urls);
   },
 
-  runChannelListener(request, kind) {
+  runChannelListener(channel, loadContext, kind) {
     let listeners = this.listeners[kind];
-    let channel = request.QueryInterface(Ci.nsIHttpChannel);
-    let loadContext = this.getLoadContext(channel);
     let browser = loadContext ? loadContext.topFrameElement : null;
     let loadInfo = channel.loadInfo;
     let policyType = loadInfo.contentPolicyType;
 
     let requestHeaders;
     let responseHeaders;
 
     let includeStatus = kind == "headersReceived" || kind == "onStart" || kind == "onStop";
@@ -319,43 +320,48 @@ var HttpObserverManager = {
           channel.setResponseHeader(name, "", false);
         }
 
         for (let {name, value} of result.responseHeaders) {
           channel.setResponseHeader(name, value, false);
         }
       }
     }
+
+    return true;
   },
 
-  modify(subject, topic, data) {
-    if (this.runChannelListener(subject, "modify")) {
-      this.runChannelListener(subject, "afterModify");
+  modify(channel, topic, data) {
+    let loadContext = this.getLoadContext(channel);
+
+    if (this.runChannelListener(channel, loadContext, "modify")) {
+      this.runChannelListener(channel, loadContext, "afterModify");
     }
   },
 
-  examine(subject, topic, data) {
-    let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+  examine(channel, topic, data) {
+    let loadContext = this.getLoadContext(channel);
+
     if (this.listeners.onStart.size || this.listeners.onStop.size) {
       if (channel instanceof Components.interfaces.nsITraceableChannel) {
-        let listener = new StartStopListener(this);
-        let orig = subject.setNewListener(listener);
+        let listener = new StartStopListener(this, loadContext);
+        let orig = channel.setNewListener(listener);
         listener.orig = orig;
       }
     }
 
-    this.runChannelListener(subject, "headersReceived");
+    this.runChannelListener(channel, loadContext, "headersReceived");
   },
 
-  onStartRequest(request) {
-    this.runChannelListener(request, "onStart");
+  onStartRequest(channel, loadContext) {
+    this.runChannelListener(channel, loadContext, "onStart");
   },
 
-  onStopRequest(request) {
-    this.runChannelListener(request, "onStop");
+  onStopRequest(channel, loadContext) {
+    this.runChannelListener(channel, loadContext, "onStop");
   },
 };
 
 var onBeforeRequest = {
   addListener(callback, filter = null, opt_extraInfoSpec = null) {
     // FIXME: Add requestBody support.
     let opts = parseExtra(opt_extraInfoSpec, ["blocking"]);
     opts.filter = parseFilter(filter);
--- a/toolkit/modules/addons/WebRequestCommon.jsm
+++ b/toolkit/modules/addons/WebRequestCommon.jsm
@@ -13,18 +13,29 @@ var WebRequestCommon = {
   typeForPolicyType(type) {
     switch (type) {
       case Ci.nsIContentPolicy.TYPE_DOCUMENT: return "main_frame";
       case Ci.nsIContentPolicy.TYPE_SUBDOCUMENT: return "sub_frame";
       case Ci.nsIContentPolicy.TYPE_STYLESHEET: return "stylesheet";
       case Ci.nsIContentPolicy.TYPE_SCRIPT: return "script";
       case Ci.nsIContentPolicy.TYPE_IMAGE: return "image";
       case Ci.nsIContentPolicy.TYPE_OBJECT: return "object";
-      case Ci.nsIContentPolicy.TYPE_OBJECT_SUBREQUEST: return "object";
+      case Ci.nsIContentPolicy.TYPE_OBJECT_SUBREQUEST: return "object_subrequest";
       case Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST: return "xmlhttprequest";
+      case Ci.nsIContentPolicy.TYPE_XBL: return "xbl";
+      case Ci.nsIContentPolicy.TYPE_XSLT: return "xslt";
+      case Ci.nsIContentPolicy.TYPE_PING: return "ping";
+      case Ci.nsIContentPolicy.TYPE_BEACON: return "beacon";
+      case Ci.nsIContentPolicy.TYPE_DTD: return "xml_dtd";
+      case Ci.nsIContentPolicy.TYPE_FONT: return "font";
+      case Ci.nsIContentPolicy.TYPE_MEDIA: return "media";
+      case Ci.nsIContentPolicy.TYPE_WEBSOCKET: return "websocket";
+      case Ci.nsIContentPolicy.TYPE_CSP_REPORT: return "csp_report";
+      case Ci.nsIContentPolicy.TYPE_IMAGESET: return "imageset";
+      case Ci.nsIContentPolicy.TYPE_WEB_MANIFEST: return "web_manifest";
       default: return "other";
     }
   },
 
   typeMatches(policyType, filterTypes) {
     if (filterTypes === null) {
       return true;
     }
--- a/view/nsView.cpp
+++ b/view/nsView.cpp
@@ -131,17 +131,22 @@ void nsView::DestroyWidget()
       mWindow->SetAttachedWidgetListener(nullptr);
     }
     else {
       mWindow->SetWidgetListener(nullptr);
 
       nsCOMPtr<nsIRunnable> widgetDestroyer =
         new DestroyWidgetRunnable(mWindow);
 
-      NS_DispatchToMainThread(widgetDestroyer);
+      // Don't leak if we happen to arrive here after the main thread
+      // has disappeared.
+      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+      if (mainThread) {
+        mainThread->Dispatch(widgetDestroyer.forget(), NS_DISPATCH_NORMAL);
+      }
     }
 
     mWindow = nullptr;
   }
 }
 
 nsView* nsView::GetViewFor(nsIWidget* aWidget)
 {
--- a/xpcom/string/nsStringObsolete.cpp
+++ b/xpcom/string/nsStringObsolete.cpp
@@ -535,17 +535,17 @@ StripChars2(char16_t* aString,uint32_t a
     }
     *to = 0;
   }
   return to - (char16_t*)aString;
 }
 
 /* ***** END RICKG BLOCK ***** */
 
-static const char* kWhitespace="\b\t\r\n ";
+static const char* kWhitespace="\f\t\r\n ";
 
 // This function is used to implement FindCharInSet and friends
 template <class CharT>
 #ifndef __SUNPRO_CC
 static
 #endif /* !__SUNPRO_CC */
 CharT
 GetFindInSetFilter( const CharT* set)
--- a/xpcom/tests/external/TestMinStringAPI.cpp
+++ b/xpcom/tests/external/TestMinStringAPI.cpp
@@ -260,17 +260,17 @@ static bool test_replace()
                              "crazy",
                              "oh");
     if (!rv)
       return rv;
 
     return true;
   }
 
-static const char* kWhitespace="\b\t\r\n ";
+static const char* kWhitespace="\f\t\r\n ";
 
 static void
 CompressWhitespace(nsACString &str)
   {
     const char *p;
     int32_t i, len = (int32_t) NS_CStringGetData(str, &p);
 
     // trim leading whitespace