Merge m-c to b-i
authorPhil Ringnalda <philringnalda@gmail.com>
Thu, 03 Oct 2013 21:07:29 -0700
changeset 163752 8211a7ddd2f01aef69e02811cc762e0e3cbf8b58
parent 163751 1bb4b547414fafa63f761fd5ba6300d2cd4d14c5 (current diff)
parent 163688 8f08240128c8ad49cefc464d68c5a91d4d83813c (diff)
child 163753 f8cdd9fa43e98c8e40b022b3d9b55ccd88067e45
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b-i
js/src/jit-test/tests/for-of/generators-6.js
--- a/addon-sdk/source/lib/sdk/deprecated/list.js
+++ b/addon-sdk/source/lib/sdk/deprecated/list.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Trait } = require('../deprecated/traits');
+const { iteratorSymbol } = require('../util/iteration');
 
 /**
  * @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/list
  */
 const Iterable = Trait.compose({
   /**
    * Hash map of key-values to iterate over.
    * Note: That this property can be a getter if you need dynamic behavior.
@@ -33,17 +34,17 @@ exports.Iterable = Iterable;
 
 /**
  * An ordered collection (also known as a sequence) disallowing duplicate
  * elements. List is composed out of `Iterable` there for it provides custom
  * enumeration behavior that is similar to array (enumerates only on the
  * elements of the list). List is a base trait and is meant to be a part of
  * composition, since all of it's API is private except length property.
  */
-const List = Trait.resolve({ toString: null }).compose({
+const listOptions = {
   _keyValueMap: null,
   /**
    * List constructor can take any number of element to populate itself.
    * @params {Object|String|Number} element
    * @example
    *    List(1,2,3).length == 3 // true
    */
   constructor: function List() {
@@ -109,17 +110,17 @@ const List = Trait.resolve({ toString: n
    * @param {Boolean} onKeys
    */
   __iterator__: function __iterator__(onKeys, onKeyValue) {
     let array = this._keyValueMap.slice(0),
         i = -1;
     for (let element of array)
       yield onKeyValue ? [++i, element] : onKeys ? ++i : element;
   },
-  iterator: function iterator() {
-    let array = this._keyValueMap.slice(0);
+};
+listOptions[iteratorSymbol] = function* iterator() {
+  let array = this._keyValueMap.slice(0);
 
-    for (let element of array)
-      yield element;
-  }
-
-});
+  for (let element of array)
+    yield element;
+}
+const List = Trait.resolve({ toString: null }).compose(listOptions);
 exports.List = List;
--- a/addon-sdk/source/lib/sdk/selection.js
+++ b/addon-sdk/source/lib/sdk/selection.js
@@ -17,17 +17,18 @@ const { Ci, Cc } = require("chrome"),
     { Class, obscure } = require("./core/heritage"),
     { EventTarget } = require("./event/target"),
     { ns } = require("./core/namespace"),
     { when: unload } = require("./system/unload"),
     { ignoreWindow } = require('./private-browsing/utils'),
     { getTabs, getTabContentWindow, getTabForContentWindow,
       getAllTabContentWindows } = require('./tabs/utils'),
     winUtils = require("./window/utils"),
-    events = require("./system/events");
+    events = require("./system/events"),
+    { iteratorSymbol, forInIterator } = require("./util/iteration");
 
 // The selection types
 const HTML = 0x01,
       TEXT = 0x02,
       DOM  = 0x03; // internal use only
 
 // A more developer-friendly message than the caught exception when is not
 // possible change a selection.
@@ -94,35 +95,36 @@ const selectionListener = {
 /**
  * Defines iterators so that discontiguous selections can be iterated.
  * Empty selections are skipped - see `safeGetRange` for further details.
  *
  * If discontiguous selections are in a text field, only the first one
  * is returned because the text field selection APIs doesn't support
  * multiple selections.
  */
-function iterator() {
-    let selection = getSelection(DOM);
-    let count = 0;
+function* forOfIterator() {
+  let selection = getSelection(DOM);
+  let count = 0;
 
-    if (selection)
-      count = selection.rangeCount || (getElementWithSelection() ? 1 : 0);
+  if (selection)
+    count = selection.rangeCount || (getElementWithSelection() ? 1 : 0);
 
-    for (let i = 0; i < count; i++) {
-      let sel = Selection(i);
+  for (let i = 0; i < count; i++) {
+    let sel = Selection(i);
 
-      if (sel.text)
-        yield Selection(i);
-    }
+    if (sel.text)
+      yield Selection(i);
+  }
 }
 
-const selectionIterator = obscure({
-  __iterator__: iterator, // for...in; for each...in
-  iterator: iterator // for....of
-});
+const selectionIteratorOptions = {
+  __iterator__: forInIterator
+}
+selectionIteratorOptions[iteratorSymbol] = forOfIterator;
+const selectionIterator = obscure(selectionIteratorOptions);
 
 /**
  * Returns the most recent focused window.
  * if private browsing window is most recent and not supported,
  * then ignore it and return `null`, because the focused window
  * can't be targeted.
  */
 function getFocusedWindow() {
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/util/iteration.js
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+'use strict';
+
+module.metadata = {
+  "stability": "experimental"
+};
+
+// This is known as @@iterator in the ES6 spec.  Until it is bound to
+// some well-known name, find the @@iterator object by expecting it as
+// the first property accessed on a for-of iterable.
+const iteratorSymbol = (function() {
+  try {
+    for (var _ of Proxy.create({get: function(_, name) { throw name; } }))
+      break;
+  } catch (name) {
+    return name;
+  }
+  throw new TypeError;
+})();
+
+exports.iteratorSymbol = iteratorSymbol;
+
+// An adaptor that, given an object that is iterable with for-of, is
+// suitable for being bound to __iterator__ in order to make the object
+// iterable in the same way via for-in.
+function forInIterator() {
+    for (let item of this)
+        yield item;
+}
+
+exports.forInIterator = forInIterator;
--- a/addon-sdk/source/lib/sdk/util/list.js
+++ b/addon-sdk/source/lib/sdk/util/list.js
@@ -4,18 +4,19 @@
 'use strict';
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Class } = require('../core/heritage');
 const listNS = require('../core/namespace').ns();
+const { iteratorSymbol } = require('../util/iteration');
 
-const List = Class({
+const listOptions = {
   /**
    * List constructor can take any number of element to populate itself.
    * @params {Object|String|Number} element
    * @example
    *    List(1,2,3).length == 3 // true
    */
   initialize: function List() {
     listNS(this).keyValueMap = [];
@@ -41,24 +42,21 @@ const List = Class({
    * @param {Boolean} onKeys
    */
   __iterator__: function __iterator__(onKeys, onKeyValue) {
     let array = listNS(this).keyValueMap.slice(0),
                 i = -1;
     for each(let element in array)
       yield onKeyValue ? [++i, element] : onKeys ? ++i : element;
   },
-  iterator: function iterator() {
-    let array = listNS(this).keyValueMap.slice(0),
-                i = -1;
-
-    for (let element of array)
-      yield element;
-  }
-});
+};
+listOptions[iteratorSymbol] = function iterator() {
+    return listNS(this).keyValueMap.slice(0)[iteratorSymbol]();
+};
+const List = Class(listOptions);
 exports.List = List;
 
 function addListItem(that, value) {
   let list = listNS(that).keyValueMap,
       index = list.indexOf(value);
 
   if (-1 === index) {
     try {
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -729,16 +729,17 @@ ResultsPanelContainer.prototype = Herita
     // If the anchor node is not null, create a panel to attach to the anchor
     // when showing the popup.
     if (aNode) {
       if (!this._panel) {
         this._panel = document.createElement("panel");
         this._panel.className = "results-panel";
         this._panel.setAttribute("level", "top");
         this._panel.setAttribute("noautofocus", "true");
+        this._panel.setAttribute("consumeoutsideclicks", "false");
         document.documentElement.appendChild(this._panel);
       }
       if (!this.widget) {
         this.widget = new ListWidget(this._panel);
         this.widget.itemType = "vbox";
         this.widget.itemFactory = this._createItemView;
       }
     }
--- a/browser/devtools/debugger/debugger.xul
+++ b/browser/devtools/debugger/debugger.xul
@@ -353,18 +353,19 @@
         </vbox>
       </hbox>
     </vbox>
   </vbox>
 
   <panel id="searchbox-help-panel"
          level="top"
          type="arrow"
+         position="before_start"
          noautofocus="true"
-         position="before_start">
+         consumeoutsideclicks="false">
     <vbox>
       <hbox>
         <label id="filter-label"/>
       </hbox>
       <label id="searchbox-panel-operators"
              value="&debuggerUI.searchPanelOperators;"/>
       <hbox align="center">
         <button id="global-operator-button"
@@ -400,30 +401,31 @@
                 command="variableSearchCommand"/>
         <label id="variable-operator-label"
                class="plain searchbox-panel-operator-label"/>
       </hbox>
     </vbox>
   </panel>
 
   <panel id="conditional-breakpoint-panel"
-         hidden="true"
          level="top"
          type="arrow"
-         noautofocus="true">
+         noautofocus="true"
+         consumeoutsideclicks="false">
     <vbox>
       <label id="conditional-breakpoint-panel-description"
              value="&debuggerUI.condBreakPanelTitle;"/>
       <textbox id="conditional-breakpoint-panel-textbox"/>
     </vbox>
   </panel>
 
   <panel id="resumption-order-panel"
          type="arrow"
+         position="before_start"
          noautofocus="true"
-         position="before_start">
+         consumeoutsideclicks="false">
     <hbox align="start">
       <image class="alert-icon"/>
       <label id="resumption-panel-desc" class="description"/>
     </hbox>
   </panel>
 
 </window>
--- a/browser/devtools/webconsole/hudservice.js
+++ b/browser/devtools/webconsole/hudservice.js
@@ -478,17 +478,17 @@ WebConsole.prototype = {
     let toolbox = gDevTools.getToolbox(this.target);
     if (!toolbox) {
       this.viewSource(aSourceURL, aSourceLine);
       return;
     }
 
     let showSource = ({ DebuggerView }) => {
       if (DebuggerView.Sources.containsValue(aSourceURL)) {
-        DebuggerView.setEditorLocation(aSourceURL, aSourceLine);
+        DebuggerView.setEditorLocation(aSourceURL, aSourceLine, { noDebug: true });
         return;
       }
       toolbox.selectTool("webconsole");
       this.viewSource(aSourceURL, aSourceLine);
     }
 
     // If the Debugger was already open, switch to it and try to show the
     // source immediately. Otherwise, initialize it and wait for the sources
--- a/browser/devtools/webconsole/test/browser_console_addonsdk_loader_exception.js
+++ b/browser/devtools/webconsole/test/browser_console_addonsdk_loader_exception.js
@@ -48,17 +48,17 @@ function test()
       expectUncaughtException();
       toolbox.getToolPanels();
     });
 
     waitForMessages({
       webconsole: hud,
       messages: [
         {
-          text: "TypeError: this._toolPanels is null",
+          text: "TypeError: can't convert null to object",
           category: CATEGORY_JS,
           severity: SEVERITY_ERROR,
         },
       ],
     }).then((results) => {
       fixToolbox();
       onMessageFound(results);
     });
--- a/content/base/src/CSPUtils.jsm
+++ b/content/base/src/CSPUtils.jsm
@@ -147,19 +147,23 @@ CSPPolicyURIListener.prototype = {
   function(request, context, status) {
     if (Components.isSuccessCode(status)) {
       // send the policy we received back to the parent document's CSP
       // for parsing
       this._csp.appendPolicy(this._policy, this._docURI,
                              this._reportOnly, this._csp._specCompliant);
     }
     else {
-      // problem fetching policy so fail closed
-      this._csp.refinePolicy("default-src 'none'", this._docURI,
-                             this._csp._specCompliant);
+      // problem fetching policy so fail closed by appending a "block it all"
+      // policy.  Also toss an error into the console so developers can see why
+      // this policy is used.
+      this._csp.log(WARN_FLAG, CSPLocalizer.getFormatStr("errorFetchingPolicy",
+                                                         [status]));
+      this._csp.appendPolicy("default-src 'none'", this._docURI,
+                             this._reportOnly, this._csp._specCompliant);
     }
     // resume the parent document request
     this._docRequest.resume();
   }
 };
 
 //:::::::::::::::::::::::: CLASSES :::::::::::::::::::::::::://
 
--- a/content/html/content/test/test_formelements.html
+++ b/content/html/content/test/test_formelements.html
@@ -47,14 +47,14 @@ is(names[3], "3", "Entry 4")
 is(names[4], "4", "Entry 5")
 is(names[5], "w", "Entry 6")
 is(names[6], "x", "Entry 7")
 is(names[7], "y", "Entry 8")
 is(names[8], "z", "Entry 9")
 is(names[9], "something", "Entry 10")
 is(names[10], "namedItem", "Entry 11")
 is(names[11], "item", "Entry 12")
-is(names[12], "iterator", "Entry 13")
+is(names[12], "@@iterator", "Entry 13")
 is(names[13], "length", "Entry 14")
 </script>
 </pre>
 </body>
 </html>
--- a/content/html/content/test/test_htmlcollection.html
+++ b/content/html/content/test/test_htmlcollection.html
@@ -35,14 +35,14 @@ is(names[2], "2", "Entry 3")
 is(names[3], "3", "Entry 4")
 is(names[4], "x", "Entry 5")
 is(names[5], "y", "Entry 6")
 is(names[6], "z", "Entry 7")
 is(names[7], "w", "Entry 8")
 is(names[8], "something", "Entry 9")
 is(names[9], "item", "Entry 10")
 is(names[10], "namedItem", "Entry 11")
-is(names[11], "iterator", "Entry 12")
+is(names[11], "@@iterator", "Entry 12")
 is(names[12], "length", "Entry 13")
 </script>
 </pre>
 </body>
 </html>
--- a/content/html/content/test/test_rowscollection.html
+++ b/content/html/content/test/test_rowscollection.html
@@ -47,14 +47,14 @@ is(names[4], "4", "Entry 5")
 is(names[5], "5", "Entry 6")
 is(names[6], "x", "Entry 7")
 is(names[7], "y", "Entry 8")
 is(names[8], "z", "Entry 9")
 is(names[9], "w", "Entry 10")
 is(names[10], "something", "Entry 11")
 is(names[11], "item", "Entry 12")
 is(names[12], "namedItem", "Entry 13")
-is(names[13], "iterator", "Entry 14")
+is(names[13], "@@iterator", "Entry 14")
 is(names[14], "length", "Entry 15")
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/apple/AppleMP3Reader.cpp
+++ b/content/media/apple/AppleMP3Reader.cpp
@@ -432,17 +432,17 @@ AppleMP3Reader::AudioMetadataCallback(Au
 
 void
 AppleMP3Reader::SetupDecoder()
 {
   // Get input format description from demuxer
   AudioStreamBasicDescription inputFormat, outputFormat;
   GetProperty(mAudioFileStream, kAudioFileStreamProperty_DataFormat, &inputFormat);
 
-  outputFormat = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+  memset(&outputFormat, 0, sizeof(outputFormat));
 
   // Set output format
 #if defined(MOZ_SAMPLE_TYPE_FLOAT32)
   outputFormat.mBitsPerChannel = 32;
   outputFormat.mFormatFlags =
     kLinearPCMFormatFlagIsFloat |
     0;
 #else
--- a/content/xml/document/resources/XMLPrettyPrint.xml
+++ b/content/xml/document/resources/XMLPrettyPrint.xml
@@ -7,28 +7,16 @@
           xmlns:html="http://www.w3.org/1999/xhtml">
 
   <binding id="prettyprint">
 
     <content><html:div id='top'/>
       <html:span style="display: none;"><children/></html:span>
     </content>
 
-    <implementation implements="nsIObserver">
-      <method name="observe">
-        <parameter name="aSubject"/>
-        <parameter name="aTopic"/>
-        <parameter name="aData"/>
-        <body>
-          if (aTopic == "prettyprint-dom-created")
-            document.getAnonymousNodes(this).item(0).appendChild(aSubject);
-        </body>
-      </method>
-    </implementation>
-
     <handlers>
       <handler event="click" button="0">
       <![CDATA[
         try {
           var par = event.originalTarget;
           if (par.nodeName == 'div' && par.className == 'expander') {
             if (par.parentNode.className == 'expander-closed') {
               par.parentNode.className = 'expander-open';
@@ -38,13 +26,18 @@
               par.parentNode.className = 'expander-closed';
               event.originalTarget.firstChild.data = '+';
             }
           }
         } catch (e) {
         }
       ]]>
       </handler>
+      <handler event="prettyprint-dom-created" allowuntrusted="false">
+        <![CDATA[
+          document.getAnonymousNodes(this).item(0).appendChild(event.detail);
+        ]]>
+      </handler>
     </handlers>
 
   </binding>
 
 </bindings>
--- a/content/xml/document/src/nsXMLPrettyPrinter.cpp
+++ b/content/xml/document/src/nsXMLPrettyPrinter.cpp
@@ -17,16 +17,19 @@
 #include "nsNetUtil.h"
 #include "mozilla/dom/Element.h"
 #include "nsIDOMDocumentFragment.h"
 #include "nsBindingManager.h"
 #include "nsXBLService.h"
 #include "nsIScriptSecurityManager.h"
 #include "mozilla/Preferences.h"
 #include "nsIDocument.h"
+#include "nsVariant.h"
+#include "nsIDOMCustomEvent.h"
+#include "GeneratedEvents.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS2(nsXMLPrettyPrinter,
                    nsIDocumentObserver,
                    nsIMutationObserver)
 
@@ -147,26 +150,33 @@ nsXMLPrettyPrinter::PrettyPrint(nsIDocum
 
     // Load the bindings.
     nsRefPtr<nsXBLBinding> unused;
     bool ignored;
     rv = xblService->LoadBindings(rootCont, bindingUri, sysPrincipal,
                                   getter_AddRefs(unused), &ignored);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    // Hand the result document to the binding
-    nsCOMPtr<nsIObserver> binding;
-    aDocument->BindingManager()->GetBindingImplementation(rootCont,
-                                              NS_GET_IID(nsIObserver),
-                                              (void**)getter_AddRefs(binding));
-    NS_ASSERTION(binding, "Prettyprint binding doesn't implement nsIObserver");
-    NS_ENSURE_TRUE(binding, NS_ERROR_UNEXPECTED);
-    
-    rv = binding->Observe(resultFragment, "prettyprint-dom-created",
-                          EmptyString().get());
+    // Fire an event at the bound element to pass it |resultFragment|.
+    nsCOMPtr<nsIDOMEvent> domEvent;
+    rv = NS_NewDOMCustomEvent(getter_AddRefs(domEvent), rootCont,
+                              nullptr, nullptr);
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsIDOMCustomEvent> customEvent = do_QueryInterface(domEvent);
+    MOZ_ASSERT(customEvent);
+    nsCOMPtr<nsIWritableVariant> resultFragmentVariant = new nsVariant();
+    rv = resultFragmentVariant->SetAsISupports(resultFragment);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    rv = customEvent->InitCustomEvent(NS_LITERAL_STRING("prettyprint-dom-created"),
+                                      /* bubbles = */ false, /* cancelable = */ false,
+                                      /* detail = */ resultFragmentVariant);
+    NS_ENSURE_SUCCESS(rv, rv);
+    customEvent->SetTrusted(true);
+    bool dummy;
+    rv = rootCont->DispatchEvent(domEvent, &dummy);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Observe the document so we know when to switch to "normal" view
     aDocument->AddObserver(this);
     mDocument = aDocument;
 
     NS_ADDREF_THIS();
 
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -392,33 +392,37 @@ AudioChannelService::SetDefaultVolumeCon
   if (!aHidden && mDefChannelChildID != aChildID) {
     return;
   }
 
   mDefChannelChildID = aChildID;
   nsString channelName;
   channelName.AssignASCII(ChannelName(aType));
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  obs->NotifyObservers(nullptr, "default-volume-channel-changed",
-                       channelName.get());
+  if (obs) {
+    obs->NotifyObservers(nullptr, "default-volume-channel-changed",
+                         channelName.get());
+  }
 }
 
 void
 AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID)
 {
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     return;
   }
 
   nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
   props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), aChildID);
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  obs->NotifyObservers(static_cast<nsIWritablePropertyBag*>(props),
-                       "audio-channel-process-changed", nullptr);
+  if (obs) {
+    obs->NotifyObservers(static_cast<nsIWritablePropertyBag*>(props),
+                         "audio-channel-process-changed", nullptr);
+  }
 
   // Calculating the most important active channel.
   AudioChannelType higher = AUDIO_CHANNEL_LAST;
 
   // Top-Down in the hierarchy for visible elements
   if (!mChannelCounters[AUDIO_CHANNEL_INT_PUBLICNOTIFICATION].IsEmpty()) {
     higher = AUDIO_CHANNEL_PUBLICNOTIFICATION;
   }
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -12,16 +12,17 @@
 #include "JavaScriptParent.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Assertions.h"
 
 #include "AccessCheck.h"
 #include "jsfriendapi.h"
+#include "js/OldDebugAPI.h"
 #include "nsIDOMGlobalPropertyInitializer.h"
 #include "nsIXPConnect.h"
 #include "WrapperFactory.h"
 #include "xpcprivate.h"
 #include "XPCQuickStubs.h"
 #include "XrayWrapper.h"
 #include "nsPrintfCString.h"
 #include "prprf.h"
@@ -211,16 +212,20 @@ ErrorResult::ReportJSExceptionFromJSImpl
   nsString message;
   domError->GetMessage(message);
 
   JSErrorReport errorReport;
   memset(&errorReport, 0, sizeof(JSErrorReport));
   errorReport.errorNumber = JSMSG_USER_DEFINED_ERROR;
   errorReport.ucmessage = message.get();
   errorReport.exnType = JSEXN_ERR;
+  JS::Rooted<JSScript*> script(aCx);
+  if (JS_DescribeScriptedCaller(aCx, &script, &errorReport.lineno)) {
+    errorReport.filename = JS_GetScriptFilename(aCx, script);
+  }
   JS_ThrowReportedError(aCx, nullptr, &errorReport);
   JS_RemoveValueRoot(aCx, &mJSException);
   
   // We no longer have a useful exception but we do want to signal that an error
   // occured.
   mResult = NS_ERROR_FAILURE;
 }
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1509,19 +1509,19 @@ class MethodDefiner(PropertyDefiner):
                        "condition": PropertyDefiner.getControllingCondition(m) }
             if isChromeOnly(m):
                 self.chrome.append(method)
             else:
                 self.regular.append(method)
 
         # FIXME Check for an existing iterator on the interface first.
         if any(m.isGetter() and m.isIndexed() for m in methods):
-            self.regular.append({"name": 'iterator',
+            self.regular.append({"name": "@@iterator",
                                  "methodInfo": False,
-                                 "nativeName": "JS_ArrayIterator",
+                                 "selfHostedName": "ArrayIterator",
                                  "length": 0,
                                  "flags": "JSPROP_ENUMERATE",
                                  "condition": MemberCondition(None, None) })
 
         if not static:
             stringifier = descriptor.operations['Stringifier']
             if stringifier:
                 toStringDesc = { "name": "toString",
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -315,17 +315,17 @@ class IDLUnresolvedIdentifier(IDLObject)
 
         assert len(name) > 0
 
         if name[:2] == "__" and not allowDoubleUnderscore:
             raise WebIDLError("Identifiers beginning with __ are reserved",
                               [location])
         if name[0] == '_' and not allowDoubleUnderscore:
             name = name[1:]
-        if (name in ["constructor", "iterator", "toString", "toJSON"] and
+        if (name in ["constructor", "toString", "toJSON"] and
             not allowForbidden):
             raise WebIDLError("Cannot use reserved identifier '%s'" % (name),
                               [location])
 
         self.name = name
 
     def __str__(self):
         return self.QName()
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -18,14 +18,15 @@ support-files =
 [test_bug862092.html]
 [test_defineProperty.html]
 [test_enums.html]
 [test_exceptionThrowing.html]
 [test_exception_messages.html]
 [test_forOf.html]
 [test_integers.html]
 [test_interfaceToString.html]
+[test_exceptions_from_jsimplemented.html]
 [test_lenientThis.html]
 [test_lookupGetter.html]
 [test_namedNoIndexed.html]
 [test_queryInterface.html]
 [test_sequence_wrapping.html]
 [test_traceProtos.html]
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_exceptions_from_jsimplemented.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=923010
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 923010</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+  /** Test for Bug 923010 **/
+  try {
+    var conn = new mozRTCPeerConnection();
+    try {
+      conn.createAnswer(function() {}, null, { "mandatory": { "BOGUS": 5 } } )
+      ok(false, "That call to createAnswer should have thrown");
+    } catch (e) {
+      is(e.lineNumber, 16, "Exception should have been on line 15");
+      is(e.message,
+         "createAnswer passed invalid constraints - unknown mandatory constraint: BOGUS",
+         "Should have the exception we expect");
+    }
+  } catch (e) {
+    // b2g has no WebRTC, apparently
+    todo(false, "No WebRTC on b2g yet");
+  }
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=923010">Mozilla Bug 923010</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/bindings/test/test_forOf.html
+++ b/dom/bindings/test/test_forOf.html
@@ -45,30 +45,22 @@ function runTestsForDocument(document, m
         if (x.nodeType != x.TEXT_NODE) {
             log += x.id + ";";
             if (x.id == "egg1")
                 basket.appendChild(egg3);
         }
     }
     is(log, "egg0;egg1;egg2;egg3;", "'for (x of div.childNodes)' should see elements added during iteration");
 
-    var iter1 = basket.childNodes.iterator();
-    var iter2 = basket.childNodes.iterator();
-    isnot(iter1, iter2, "nodelist.iterator() returns a new iterator each time");
-
     log = '';
     basket.appendChild(document.createTextNode("some text"));
     for (var x of basket.children)
         log += x.id + ";";
     is(log, "egg0;egg1;egg2;egg3;", "'for (x of div.children)' should iterate over child elements");
 
-    var iter1 = basket.children.iterator();
-    var iter2 = basket.children.iterator();
-    isnot(iter1, iter2, ".iterator() returns a new iterator each time");
-
     var count = 0;
     for (var x of document.getElementsByClassName("hazardous-materials"))
         count++;
     is(count, 0, "'for (x of emptyNodeList)' loop should run zero times");
 
     var log = '';
     for (var x of document.querySelectorAll("span"))
         log += x.id + ";";
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -454,17 +454,18 @@ GLContext::InitWithPrefix(const char *pr
         unsigned int version = 0;
 
         bool parseSuccess = ParseGLVersion(this, &version);
         printf_stderr("OpenGL version detected: %u\n", version);
 
         if (version >= mVersion) {
             mVersion = version;
         } else if (parseSuccess) {
-            MOZ_ASSERT(false, "Parsed version less than expected.");
+            NS_WARNING("Parsed version less than expected.");
+            mInitialized = false;
         }
     }
 
     // Load OpenGL ES 2.0 symbols, or desktop if we aren't using ES 2.
     if (mInitialized) {
         if (IsGLES2()) {
             SymLoadStruct symbols_ES2[] = {
                 { (PRFuncPtr*) &mSymbols.fGetShaderPrecisionFormat, { "GetShaderPrecisionFormat", nullptr } },
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -643,16 +643,17 @@ export:: selfhosting
 selfhosting:: selfhosted.out.h
 
 selfhosting_srcs := \
   $(srcdir)/builtin/Utilities.js \
   $(srcdir)/builtin/Array.js \
   $(srcdir)/builtin/Date.js \
   $(srcdir)/builtin/Intl.js \
   $(srcdir)/builtin/IntlData.js \
+  $(srcdir)/builtin/Iterator.js \
   $(srcdir)/builtin/Number.js \
   $(srcdir)/builtin/ParallelArray.js \
   $(srcdir)/builtin/String.js \
   $(srcdir)/builtin/Set.js \
   $(srcdir)/builtin/Map.js \
   $(NULL)
 
 selfhosted_out_h_deps := \
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/Iterator.js
@@ -0,0 +1,97 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function IteratorIdentity() {
+    return this;
+}
+
+var LegacyIteratorWrapperMap = new std_WeakMap();
+
+function IteratorResult(value, done) {
+    var result = std_Object_create(null);
+    result.value = value;
+    result.done = done;
+    return result;
+}
+
+function LegacyIteratorNext(arg) {
+    var iter = callFunction(std_WeakMap_get, LegacyIteratorWrapperMap, this);
+    try {
+        return IteratorResult(iter.next(arg), false);
+    } catch (e) {
+        if (e instanceof std_StopIteration)
+            return IteratorResult(undefined, true);
+        throw e;
+    }
+}
+
+function LegacyIteratorThrow(exn) {
+    var iter = callFunction(std_WeakMap_get, LegacyIteratorWrapperMap, this);
+    try {
+        return IteratorResult(iter.throw(exn), false);
+    } catch (e) {
+        if (e instanceof std_StopIteration)
+            return IteratorResult(undefined, true);
+        throw e;
+    }
+}
+
+function LegacyIterator(iter) {
+    callFunction(std_WeakMap_set, LegacyIteratorWrapperMap, this, iter);
+}
+
+function LegacyGeneratorIterator(iter) {
+    callFunction(std_WeakMap_set, LegacyIteratorWrapperMap, this, iter);
+}
+
+var LegacyIteratorsInitialized = std_Object_create(null);
+
+function InitLegacyIterators() {
+    var props = std_Object_create(null);
+
+    props.next = std_Object_create(null);
+    props.next.value = LegacyIteratorNext;
+    props.next.enumerable = false;
+    props.next.configurable = true;
+    props.next.writable = true;
+
+    props[std_iterator] = std_Object_create(null);
+    props[std_iterator].value = IteratorIdentity;
+    props[std_iterator].enumerable = false;
+    props[std_iterator].configurable = true;
+    props[std_iterator].writable = true;
+
+    var LegacyIteratorProto = std_Object_create(GetIteratorPrototype(), props);
+    MakeConstructible(LegacyIterator, LegacyIteratorProto);
+
+    props.throw = std_Object_create(null);
+    props.throw.value = LegacyIteratorThrow;
+    props.throw.enumerable = false;
+    props.throw.configurable = true;
+    props.throw.writable = true;
+
+    var LegacyGeneratorIteratorProto = std_Object_create(GetIteratorPrototype(), props);
+    MakeConstructible(LegacyGeneratorIterator, LegacyGeneratorIteratorProto);
+
+    LegacyIteratorsInitialized.initialized = true;
+}
+
+function NewLegacyIterator(iter, wrapper) {
+    if (!LegacyIteratorsInitialized.initialized)
+        InitLegacyIterators();
+
+    return new wrapper(iter);
+}
+
+function LegacyIteratorShim() {
+    return NewLegacyIterator(ToObject(this), LegacyIterator);
+}
+
+function LegacyGeneratorIteratorShim() {
+    return NewLegacyIterator(ToObject(this), LegacyGeneratorIterator);
+}
+
+function ArrayIterator() {
+    return NewLegacyIterator(callFunction(std_Array_iterator, this), LegacyIterator);
+}
--- a/js/src/builtin/Map.js
+++ b/js/src/builtin/Map.js
@@ -19,18 +19,15 @@ function MapForEach(callbackfn, thisArg 
 
     /* Step 5. */
     if (!IsCallable(callbackfn))
         ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     /* Step 6-8. */
     var entries = std_Map_iterator.call(M);
     while (true) {
-        try {
-            var entry = std_Map_iterator_next.call(entries);
-        } catch (err) {
-            if (err instanceof StopIteration)
-                break;
-            throw err;
-        }
+        var result = std_Map_iterator_next.call(entries);
+        if (result.done)
+            break;
+        var entry = result.value;
         callFunction(callbackfn, thisArg, entry[1], entry[0], M);
     }
 }
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -20,16 +20,17 @@
 #include "jsobjinlines.h"
 
 using namespace js;
 
 using mozilla::DoubleIsInt32;
 using mozilla::IsNaN;
 using mozilla::OldMove;
 using mozilla::MoveRef;
+using mozilla::ArrayLength;
 using JS::DoubleNaNValue;
 
 
 /*** OrderedHashTable ****************************************************************************/
 
 /*
  * Define two collection templates, js::OrderedHashMap and js::OrderedHashSet.
  * They are like js::HashMap and js::HashSet except that:
@@ -884,16 +885,17 @@ const Class MapIteratorObject::class_ = 
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     MapIteratorObject::finalize
 };
 
 const JSFunctionSpec MapIteratorObject::methods[] = {
+    JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
     JS_FN("next", next, 0, 0),
     JS_FS_END
 };
 
 inline ValueMap::Range *
 MapIteratorObject::range()
 {
     return static_cast<ValueMap::Range *>(getSlot(RangeSlot).toPrivate());
@@ -960,45 +962,54 @@ MapIteratorObject::is(HandleValue v)
     return v.isObject() && v.toObject().hasClass(&class_);
 }
 
 bool
 MapIteratorObject::next_impl(JSContext *cx, CallArgs args)
 {
     MapIteratorObject &thisobj = args.thisv().toObject().as<MapIteratorObject>();
     ValueMap::Range *range = thisobj.range();
-    if (!range)
-        return js_ThrowStopIteration(cx);
-    if (range->empty()) {
+    RootedValue value(cx);
+    bool done;
+
+    if (!range || range->empty()) {
         js_delete(range);
         thisobj.setReservedSlot(RangeSlot, PrivateValue(nullptr));
-        return js_ThrowStopIteration(cx);
+        value.setUndefined();
+        done = true;
+    } else {
+        switch (thisobj.kind()) {
+          case MapObject::Keys:
+            value = range->front().key.get();
+            break;
+
+          case MapObject::Values:
+            value = range->front().value;
+            break;
+
+          case MapObject::Entries: {
+            Value pair[2] = { range->front().key.get(), range->front().value };
+            AutoValueArray root(cx, pair, ArrayLength(pair));
+
+            JSObject *pairobj = NewDenseCopiedArray(cx, ArrayLength(pair), pair);
+            if (!pairobj)
+                return false;
+            value.setObject(*pairobj);
+            break;
+          }
+        }
+        range->popFront();
+        done = false;
     }
 
-    switch (thisobj.kind()) {
-      case MapObject::Keys:
-        args.rval().set(range->front().key.get());
-        break;
-
-      case MapObject::Values:
-        args.rval().set(range->front().value);
-        break;
+    RootedObject result(cx, CreateItrResultObject(cx, value, done));
+    if (!result)
+        return false;
+    args.rval().setObject(*result);
 
-      case MapObject::Entries: {
-        Value pair[2] = { range->front().key.get(), range->front().value };
-        AutoValueArray root(cx, pair, 2);
-
-        JSObject *pairobj = NewDenseCopiedArray(cx, 2, pair);
-        if (!pairobj)
-            return false;
-        args.rval().setObject(*pairobj);
-        break;
-      }
-    }
-    range->popFront();
     return true;
 }
 
 bool
 MapIteratorObject::next(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod(cx, is, next_impl, args);
@@ -1072,17 +1083,17 @@ MapObject::initClass(JSContext *cx, JSOb
     if (proto) {
         // Define the "entries" method.
         JSFunction *fun = JS_DefineFunction(cx, proto, "entries", entries, 0, 0);
         if (!fun)
             return nullptr;
 
         // Define its alias.
         RootedValue funval(cx, ObjectValue(*fun));
-        if (!JS_DefineProperty(cx, proto, "iterator", funval, nullptr, nullptr, 0))
+        if (!JS_DefineProperty(cx, proto, js_std_iterator_str, funval, nullptr, nullptr, 0))
             return nullptr;
     }
     return proto;
 }
 
 template <class Range>
 static void
 MarkKey(Range &r, const HashableValue &key, JSTracer *trc)
@@ -1168,43 +1179,52 @@ MapObject::construct(JSContext *cx, unsi
     if (!map->init()) {
         js_ReportOutOfMemory(cx);
         return false;
     }
     obj->setPrivate(map);
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.hasDefined(0)) {
-        ForOfIterator iter(cx, args[0]);
-        while (iter.next()) {
-            RootedObject pairobj(cx, js_ValueToNonNullObject(cx, iter.value()));
-            if (!pairobj)
+        ForOfIterator iter(cx);
+        if (!iter.init(args[0]))
+            return false;
+        RootedValue pairVal(cx);
+        RootedObject pairObj(cx);
+        while (true) {
+            bool done;
+            if (!iter.next(&pairVal, &done))
+                return false;
+            if (done)
+                break;
+            // FIXME: We're supposed to throw if pairVal isn't an object.  Bug
+            // 918341.
+            pairObj = ToObject(cx, pairVal);
+            if (!pairObj)
                 return false;
 
             RootedValue key(cx);
-            if (!JSObject::getElement(cx, pairobj, pairobj, 0, &key))
+            if (!JSObject::getElement(cx, pairObj, pairObj, 0, &key))
                 return false;
 
             AutoHashableValueRooter hkey(cx);
             if (!hkey.setValue(cx, key))
                 return false;
 
             RootedValue val(cx);
-            if (!JSObject::getElement(cx, pairobj, pairobj, 1, &val))
+            if (!JSObject::getElement(cx, pairObj, pairObj, 1, &val))
                 return false;
 
             RelocatableValue rval(val);
             if (!map->put(hkey, rval)) {
                 js_ReportOutOfMemory(cx);
                 return false;
             }
             WriteBarrierPost(cx->runtime(), map, hkey);
         }
-        if (!iter.close())
-            return false;
     }
 
     args.rval().setObject(*obj);
     return true;
 }
 
 bool
 MapObject::is(HandleValue v)
@@ -1451,16 +1471,17 @@ const Class SetIteratorObject::class_ = 
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     SetIteratorObject::finalize
 };
 
 const JSFunctionSpec SetIteratorObject::methods[] = {
+    JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
     JS_FN("next", next, 0, 0),
     JS_FS_END
 };
 
 inline ValueSet::Range *
 SetIteratorObject::range()
 {
     return static_cast<ValueSet::Range *>(getSlot(RangeSlot).toPrivate());
@@ -1526,42 +1547,50 @@ SetIteratorObject::is(HandleValue v)
     return v.isObject() && v.toObject().is<SetIteratorObject>();
 }
 
 bool
 SetIteratorObject::next_impl(JSContext *cx, CallArgs args)
 {
     SetIteratorObject &thisobj = args.thisv().toObject().as<SetIteratorObject>();
     ValueSet::Range *range = thisobj.range();
-    if (!range)
-        return js_ThrowStopIteration(cx);
-    if (range->empty()) {
+    RootedValue value(cx);
+    bool done;
+
+    if (!range || range->empty()) {
         js_delete(range);
         thisobj.setReservedSlot(RangeSlot, PrivateValue(nullptr));
-        return js_ThrowStopIteration(cx);
+        value.setUndefined();
+        done = true;
+    } else {
+        switch (thisobj.kind()) {
+          case SetObject::Values:
+            value = range->front().get();
+            break;
+
+          case SetObject::Entries: {
+            Value pair[2] = { range->front().get(), range->front().get() };
+            AutoValueArray root(cx, pair, 2);
+
+            JSObject *pairObj = NewDenseCopiedArray(cx, 2, pair);
+            if (!pairObj)
+              return false;
+            value.setObject(*pairObj);
+            break;
+          }
+        }
+        range->popFront();
+        done = false;
     }
 
-    switch (thisobj.kind()) {
-      case SetObject::Values:
-        args.rval().set(range->front().get());
-        break;
-
-      case SetObject::Entries: {
-        Value pair[2] = { range->front().get(), range->front().get() };
-        AutoValueArray root(cx, pair, 2);
+    RootedObject result(cx, CreateItrResultObject(cx, value, done));
+    if (!result)
+        return false;
+    args.rval().setObject(*result);
 
-        JSObject *pairobj = NewDenseCopiedArray(cx, 2, pair);
-        if (!pairobj)
-          return false;
-        args.rval().setObject(*pairobj);
-        break;
-      }
-    }
-
-    range->popFront();
     return true;
 }
 
 bool
 SetIteratorObject::next(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod(cx, is, next_impl, args);
@@ -1615,17 +1644,17 @@ SetObject::initClass(JSContext *cx, JSOb
         JSFunction *fun = JS_DefineFunction(cx, proto, "values", values, 0, 0);
         if (!fun)
             return nullptr;
 
         // Define its aliases.
         RootedValue funval(cx, ObjectValue(*fun));
         if (!JS_DefineProperty(cx, proto, "keys", funval, nullptr, nullptr, 0))
             return nullptr;
-        if (!JS_DefineProperty(cx, proto, "iterator", funval, nullptr, nullptr, 0))
+        if (!JS_DefineProperty(cx, proto, js_std_iterator_str, funval, nullptr, nullptr, 0))
             return nullptr;
     }
     return proto;
 }
 
 void
 SetObject::mark(JSTracer *trc, JSObject *obj)
 {
@@ -1657,29 +1686,35 @@ SetObject::construct(JSContext *cx, unsi
     if (!set->init()) {
         js_ReportOutOfMemory(cx);
         return false;
     }
     obj->setPrivate(set);
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.hasDefined(0)) {
-        ForOfIterator iter(cx, args[0]);
-        while (iter.next()) {
-            AutoHashableValueRooter key(cx);
-            if (!key.setValue(cx, iter.value()))
+        RootedValue keyVal(cx);
+        ForOfIterator iter(cx);
+        if (!iter.init(args[0]))
+            return false;
+        AutoHashableValueRooter key(cx);
+        while (true) {
+            bool done;
+            if (!iter.next(&keyVal, &done))
+                return false;
+            if (done)
+                break;
+            if (!key.setValue(cx, keyVal))
                 return false;
             if (!set->put(key)) {
                 js_ReportOutOfMemory(cx);
                 return false;
             }
             WriteBarrierPost(cx->runtime(), set, key);
         }
-        if (!iter.close())
-            return false;
     }
 
     args.rval().setObject(*obj);
     return true;
 }
 
 bool
 SetObject::is(HandleValue v)
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -25,24 +25,22 @@ using mozilla::ArrayLength;
 
 
 bool
 js::obj_construct(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject obj(cx, nullptr);
-    if (args.length() > 0) {
-        /* If argv[0] is null or undefined, obj comes back null. */
-        if (!js_ValueToObjectOrNull(cx, args[0], &obj))
+    if (args.length() > 0 && !args[0].isNullOrUndefined()) {
+        obj = ToObject(cx, args[0]);
+        if (!obj)
             return false;
-    }
-    if (!obj) {
+    } else {
         /* Make an object whether this was called with 'new' or not. */
-        JS_ASSERT(!args.length() || args[0].isNullOrUndefined());
         if (!NewObjectScriptedCall(cx, &obj))
             return false;
     }
 
     args.rval().setObject(*obj);
     return true;
 }
 
--- a/js/src/builtin/Set.js
+++ b/js/src/builtin/Set.js
@@ -19,18 +19,15 @@ function SetForEach(callbackfn, thisArg 
 
     /* Step 5-6. */
     if (!IsCallable(callbackfn))
         ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     /* Step 7-8. */
     var values = std_Set_iterator.call(S);
     while (true) {
-        try {
-            var entry = std_Set_iterator_next.call(values);
-        } catch (err) {
-            if (err instanceof StopIteration)
-                break;
-            throw err;
-        }
-        callFunction(callbackfn, thisArg, entry, entry, S);
+        var result = std_Set_iterator_next.call(values);
+        if (result.done)
+            break;
+        var value = result.value;
+        callFunction(callbackfn, thisArg, value, value, S);
     }
 }
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -34,16 +34,17 @@
 #else
 #define assert(b, info)
 #endif
 
 /* cache built-in functions before applications can change them */
 var std_isFinite = isFinite;
 var std_isNaN = isNaN;
 var std_Array_indexOf = ArrayIndexOf;
+var std_Array_iterator = Array.prototype.iterator;
 var std_Array_join = Array.prototype.join;
 var std_Array_push = Array.prototype.push;
 var std_Array_shift = Array.prototype.shift;
 var std_Array_slice = Array.prototype.slice;
 var std_Array_sort = Array.prototype.sort;
 var std_Array_unshift = Array.prototype.unshift;
 var std_Boolean_toString = Boolean.prototype.toString;
 var Std_Date = Date;
@@ -66,25 +67,28 @@ var std_String_indexOf = String.prototyp
 var std_String_lastIndexOf = String.prototype.lastIndexOf;
 var std_String_match = String.prototype.match;
 var std_String_replace = String.prototype.replace;
 var std_String_split = String.prototype.split;
 var std_String_startsWith = String.prototype.startsWith;
 var std_String_substring = String.prototype.substring;
 var std_String_toLowerCase = String.prototype.toLowerCase;
 var std_String_toUpperCase = String.prototype.toUpperCase;
+var std_WeakMap = WeakMap;
 var std_WeakMap_get = WeakMap.prototype.get;
 var std_WeakMap_has = WeakMap.prototype.has;
 var std_WeakMap_set = WeakMap.prototype.set;
 var std_Map_has = Map.prototype.has;
 var std_Set_has = Set.prototype.has;
-var std_Map_iterator = Map().iterator;
-var std_Set_iterator = Set().iterator;
-var std_Map_iterator_next = Object.getPrototypeOf(Map().iterator()).next;
-var std_Set_iterator_next = Object.getPrototypeOf(Set().iterator()).next;
+var std_iterator = '@@iterator'; // FIXME: Change to be a symbol.
+var std_StopIteration = StopIteration;
+var std_Map_iterator = Map.prototype[std_iterator];
+var std_Set_iterator = Set.prototype[std_iterator];
+var std_Map_iterator_next = Object.getPrototypeOf(Map()[std_iterator]()).next;
+var std_Set_iterator_next = Object.getPrototypeOf(Set()[std_iterator]()).next;
 
 /********** List specification type **********/
 
 
 /* Spec: ECMAScript Language Specification, 5.1 edition, 8.8 */
 function List() {}
 {
   let ListProto = std_Object_create(null);
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -149,32 +149,48 @@ bufferTooSmall:
     }
     return false;
 }
 
 /*******************************************************************************
 ** JSAPI function prototypes
 *******************************************************************************/
 
+// We use an enclosing struct here out of paranoia about the ability of gcc 4.4
+// (and maybe 4.5) to correctly compile this if it were a template function.
+// See also the comments in dom/workers/Events.cpp (and other adjacent files) by
+// the |struct Property| there.
+template<JS::IsAcceptableThis Test, JS::NativeImpl Impl>
+struct Property
+{
+  static bool
+  Fun(JSContext* cx, unsigned argc, JS::Value* vp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    return JS::CallNonGenericMethod<Test, Impl>(cx, args);
+  }
+};
+
 static bool ConstructAbstract(JSContext* cx, unsigned argc, jsval* vp);
 
 namespace CType {
   static bool ConstructData(JSContext* cx, unsigned argc, jsval* vp);
   static bool ConstructBasic(JSContext* cx, HandleObject obj, const CallArgs& args);
 
   static void Trace(JSTracer* trc, JSObject* obj);
   static void Finalize(JSFreeOp *fop, JSObject* obj);
 
-  static bool PrototypeGetter(JSContext* cx, HandleObject obj, HandleId idval,
-    MutableHandleValue vp);
-  static bool NameGetter(JSContext* cx, HandleObject obj, HandleId idval,
-    MutableHandleValue vp);
-  static bool SizeGetter(JSContext* cx, HandleObject obj, HandleId idval,
-    MutableHandleValue vp);
-  static bool PtrGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp);
+  bool IsCType(HandleValue v);
+  bool IsCTypeOrProto(HandleValue v);
+
+  bool PrototypeGetter(JSContext* cx, JS::CallArgs args);
+  bool NameGetter(JSContext* cx, JS::CallArgs args);
+  bool SizeGetter(JSContext* cx, JS::CallArgs args);
+  bool PtrGetter(JSContext* cx, JS::CallArgs args);
+
   static bool CreateArray(JSContext* cx, unsigned argc, jsval* vp);
   static bool ToString(JSContext* cx, unsigned argc, jsval* vp);
   static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
   static bool HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp);
 
 
   /*
    * Get the global "ctypes" object.
@@ -191,101 +207,103 @@ namespace ABI {
   bool IsABI(JSObject* obj);
   static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
 }
 
 namespace PointerType {
   static bool Create(JSContext* cx, unsigned argc, jsval* vp);
   static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
 
-  static bool TargetTypeGetter(JSContext* cx, HandleObject obj, HandleId idval,
-    MutableHandleValue vp);
-  static bool ContentsGetter(JSContext* cx, HandleObject obj, HandleId idval,
-    MutableHandleValue vp);
-  static bool ContentsSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict,
-    MutableHandleValue vp);
+  bool IsPointerType(HandleValue v);
+  bool IsPointer(HandleValue v);
+
+  bool TargetTypeGetter(JSContext* cx, JS::CallArgs args);
+  bool ContentsGetter(JSContext* cx, JS::CallArgs args);
+  bool ContentsSetter(JSContext* cx, JS::CallArgs args);
+
   static bool IsNull(JSContext* cx, unsigned argc, jsval* vp);
   static bool Increment(JSContext* cx, unsigned argc, jsval* vp);
   static bool Decrement(JSContext* cx, unsigned argc, jsval* vp);
   // The following is not an instance function, since we don't want to expose arbitrary
   // pointer arithmetic at this moment.
   static bool OffsetBy(JSContext* cx, const CallArgs& args, int offset);
 }
 
 namespace ArrayType {
+  bool IsArrayType(HandleValue v);
+  bool IsArrayOrArrayType(HandleValue v);
+
   static bool Create(JSContext* cx, unsigned argc, jsval* vp);
   static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
 
-  static bool ElementTypeGetter(JSContext* cx, HandleObject obj, HandleId idval,
-    MutableHandleValue vp);
-  static bool LengthGetter(JSContext* cx, HandleObject obj, HandleId idval,
-    MutableHandleValue vp);
+  bool ElementTypeGetter(JSContext* cx, JS::CallArgs args);
+  bool LengthGetter(JSContext* cx, JS::CallArgs args);
+
   static bool Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp);
   static bool Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp);
   static bool AddressOfElement(JSContext* cx, unsigned argc, jsval* vp);
 }
 
 namespace StructType {
+  bool IsStruct(HandleValue v);
+
   static bool Create(JSContext* cx, unsigned argc, jsval* vp);
   static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
 
-  static bool FieldsArrayGetter(JSContext* cx, HandleObject obj, HandleId idval,
-    MutableHandleValue vp);
+  bool FieldsArrayGetter(JSContext* cx, JS::CallArgs args);
+
   static bool FieldGetter(JSContext* cx, HandleObject obj, HandleId idval,
     MutableHandleValue vp);
   static bool FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict,
                             MutableHandleValue vp);
   static bool AddressOfField(JSContext* cx, unsigned argc, jsval* vp);
   static bool Define(JSContext* cx, unsigned argc, jsval* vp);
 }
 
 namespace FunctionType {
   static bool Create(JSContext* cx, unsigned argc, jsval* vp);
   static bool ConstructData(JSContext* cx, HandleObject typeObj,
     HandleObject dataObj, HandleObject fnObj, HandleObject thisObj, jsval errVal);
 
   static bool Call(JSContext* cx, unsigned argc, jsval* vp);
 
-  static bool ArgTypesGetter(JSContext* cx, HandleObject obj, HandleId idval,
-    MutableHandleValue vp);
-  static bool ReturnTypeGetter(JSContext* cx, HandleObject obj, HandleId idval,
-    MutableHandleValue vp);
-  static bool ABIGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp);
-  static bool IsVariadicGetter(JSContext* cx, HandleObject obj, HandleId idval,
-    MutableHandleValue vp);
+  bool IsFunctionType(HandleValue v);
+
+  bool ArgTypesGetter(JSContext* cx, JS::CallArgs args);
+  bool ReturnTypeGetter(JSContext* cx, JS::CallArgs args);
+  bool ABIGetter(JSContext* cx, JS::CallArgs args);
+  bool IsVariadicGetter(JSContext* cx, JS::CallArgs args);
 }
 
 namespace CClosure {
   static void Trace(JSTracer* trc, JSObject* obj);
   static void Finalize(JSFreeOp *fop, JSObject* obj);
 
   // libffi callback
   static void ClosureStub(ffi_cif* cif, void* result, void** args,
     void* userData);
 }
 
 namespace CData {
   static void Finalize(JSFreeOp *fop, JSObject* obj);
 
-  static bool ValueGetter(JSContext* cx, HandleObject obj, HandleId idval,
-                          MutableHandleValue vp);
-  static bool ValueSetter(JSContext* cx, HandleObject obj, HandleId idval,
-                          bool strict, MutableHandleValue vp);
+  bool ValueGetter(JSContext* cx, JS::CallArgs args);
+  bool ValueSetter(JSContext* cx, JS::CallArgs args);
+
   static bool Address(JSContext* cx, unsigned argc, jsval* vp);
   static bool ReadString(JSContext* cx, unsigned argc, jsval* vp);
   static bool ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp);
   static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
   static JSString *GetSourceString(JSContext *cx, HandleObject typeObj,
                                    void *data);
-  static bool ErrnoGetter(JSContext* cx, HandleObject obj, HandleId idval,
-                          MutableHandleValue vp);
+
+  bool ErrnoGetter(JSContext* cx, JS::CallArgs args);
 
 #if defined(XP_WIN)
-  static bool LastErrorGetter(JSContext* cx, HandleObject obj, HandleId idval,
-                              MutableHandleValue vp);
+  bool LastErrorGetter(JSContext* cx, JS::CallArgs args);
 #endif // defined(XP_WIN)
 }
 
 namespace CDataFinalizer {
   /*
    * Attach a C function as a finalizer to a JS object.
    *
    * This function is available from JS as |ctypes.withFinalizer|.
@@ -528,33 +546,41 @@ static const JSClass sCDataFinalizerClas
 
 
 #define CTYPESFN_FLAGS \
   (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
 
 #define CTYPESCTOR_FLAGS \
   (CTYPESFN_FLAGS | JSFUN_CONSTRUCTOR)
 
-#define CTYPESPROP_FLAGS \
-  (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
+#define CTYPESACC_FLAGS \
+  (JSPROP_ENUMERATE | JSPROP_PERMANENT)
 
 #define CABIFN_FLAGS \
   (JSPROP_READONLY | JSPROP_PERMANENT)
 
 #define CDATAFN_FLAGS \
   (JSPROP_READONLY | JSPROP_PERMANENT)
 
 #define CDATAFINALIZERFN_FLAGS \
   (JSPROP_READONLY | JSPROP_PERMANENT)
 
 static const JSPropertySpec sCTypeProps[] = {
-  { "name", 0, CTYPESPROP_FLAGS, JSOP_WRAPPER(CType::NameGetter), JSOP_NULLWRAPPER },
-  { "size", 0, CTYPESPROP_FLAGS, JSOP_WRAPPER(CType::SizeGetter), JSOP_NULLWRAPPER },
-  { "ptr", 0, CTYPESPROP_FLAGS, JSOP_WRAPPER(CType::PtrGetter), JSOP_NULLWRAPPER },
-  { "prototype", 0, CTYPESPROP_FLAGS, JSOP_WRAPPER(CType::PrototypeGetter), JSOP_NULLWRAPPER },
+  JS_PSG("name",
+         (Property<CType::IsCType, CType::NameGetter>::Fun),
+         CTYPESACC_FLAGS),
+  JS_PSG("size",
+         (Property<CType::IsCType, CType::SizeGetter>::Fun),
+         CTYPESACC_FLAGS),
+  JS_PSG("ptr",
+         (Property<CType::IsCType, CType::PtrGetter>::Fun),
+         CTYPESACC_FLAGS),
+  JS_PSG("prototype",
+         (Property<CType::IsCTypeOrProto, CType::PrototypeGetter>::Fun),
+         CTYPESACC_FLAGS),
   { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
 };
 
 static const JSFunctionSpec sCTypeFunctions[] = {
   JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS),
   JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS),
   JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS),
   JS_FS_END
@@ -562,19 +588,21 @@ static const JSFunctionSpec sCTypeFuncti
 
 static const JSFunctionSpec sCABIFunctions[] = {
   JS_FN("toSource", ABI::ToSource, 0, CABIFN_FLAGS),
   JS_FN("toString", ABI::ToSource, 0, CABIFN_FLAGS),
   JS_FS_END
 };
 
 static const JSPropertySpec sCDataProps[] = {
-  { "value", 0, JSPROP_SHARED | JSPROP_PERMANENT,
-    JSOP_WRAPPER(CData::ValueGetter), JSOP_WRAPPER(CData::ValueSetter) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("value",
+          (Property<CData::IsCData, CData::ValueGetter>::Fun),
+          (Property<CData::IsCData, CData::ValueSetter>::Fun),
+          JSPROP_PERMANENT),
+  JS_PS_END
 };
 
 static const JSFunctionSpec sCDataFunctions[] = {
   JS_FN("address", CData::Address, 0, CDATAFN_FLAGS),
   JS_FN("readString", CData::ReadString, 0, CDATAFN_FLAGS),
   JS_FN("readStringReplaceMalformed", CData::ReadStringReplaceMalformed, 0, CDATAFN_FLAGS),
   JS_FN("toSource", CData::ToSource, 0, CDATAFN_FLAGS),
   JS_FN("toString", CData::ToSource, 0, CDATAFN_FLAGS),
@@ -593,89 +621,99 @@ static const JSFunctionSpec sCDataFinali
   JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0, CDATAFINALIZERFN_FLAGS),
   JS_FS_END
 };
 
 static const JSFunctionSpec sPointerFunction =
   JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
 
 static const JSPropertySpec sPointerProps[] = {
-  { "targetType", 0, CTYPESPROP_FLAGS,
-    JSOP_WRAPPER(PointerType::TargetTypeGetter), JSOP_NULLWRAPPER },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSG("targetType",
+         (Property<PointerType::IsPointerType, PointerType::TargetTypeGetter>::Fun),
+         CTYPESACC_FLAGS),
+  JS_PS_END
 };
 
 static const JSFunctionSpec sPointerInstanceFunctions[] = {
   JS_FN("isNull", PointerType::IsNull, 0, CTYPESFN_FLAGS),
   JS_FN("increment", PointerType::Increment, 0, CTYPESFN_FLAGS),
   JS_FN("decrement", PointerType::Decrement, 0, CTYPESFN_FLAGS),
   JS_FS_END
 };
 
 static const JSPropertySpec sPointerInstanceProps[] = {
-  { "contents", 0, JSPROP_SHARED | JSPROP_PERMANENT,
-    JSOP_WRAPPER(PointerType::ContentsGetter),
-    JSOP_WRAPPER(PointerType::ContentsSetter) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("contents",
+         (Property<PointerType::IsPointer, PointerType::ContentsGetter>::Fun),
+         (Property<PointerType::IsPointer, PointerType::ContentsSetter>::Fun),
+          JSPROP_PERMANENT),
+  JS_PS_END
 };
 
 static const JSFunctionSpec sArrayFunction =
   JS_FN("ArrayType", ArrayType::Create, 1, CTYPESCTOR_FLAGS);
 
 static const JSPropertySpec sArrayProps[] = {
-  { "elementType", 0, CTYPESPROP_FLAGS,
-    JSOP_WRAPPER(ArrayType::ElementTypeGetter), JSOP_NULLWRAPPER },
-  { "length", 0, CTYPESPROP_FLAGS,
-    JSOP_WRAPPER(ArrayType::LengthGetter), JSOP_NULLWRAPPER },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSG("elementType",
+         (Property<ArrayType::IsArrayType, ArrayType::ElementTypeGetter>::Fun),
+         CTYPESACC_FLAGS),
+  JS_PSG("length",
+         (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
+         CTYPESACC_FLAGS),
+  JS_PS_END
 };
 
 static const JSFunctionSpec sArrayInstanceFunctions[] = {
   JS_FN("addressOfElement", ArrayType::AddressOfElement, 1, CDATAFN_FLAGS),
   JS_FS_END
 };
 
 static const JSPropertySpec sArrayInstanceProps[] = {
-  { "length", 0, JSPROP_SHARED | JSPROP_READONLY | JSPROP_PERMANENT,
-    JSOP_WRAPPER(ArrayType::LengthGetter), JSOP_NULLWRAPPER },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSG("length",
+         (Property<ArrayType::IsArrayOrArrayType, ArrayType::LengthGetter>::Fun),
+         JSPROP_PERMANENT),
+  JS_PS_END
 };
 
 static const JSFunctionSpec sStructFunction =
   JS_FN("StructType", StructType::Create, 2, CTYPESCTOR_FLAGS);
 
 static const JSPropertySpec sStructProps[] = {
-  { "fields", 0, CTYPESPROP_FLAGS,
-    JSOP_WRAPPER(StructType::FieldsArrayGetter), JSOP_NULLWRAPPER },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSG("fields",
+         (Property<StructType::IsStruct, StructType::FieldsArrayGetter>::Fun),
+         CTYPESACC_FLAGS),
+  JS_PS_END
 };
 
 static const JSFunctionSpec sStructFunctions[] = {
   JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS),
   JS_FS_END
 };
 
 static const JSFunctionSpec sStructInstanceFunctions[] = {
   JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
   JS_FS_END
 };
 
 static const JSFunctionSpec sFunctionFunction =
   JS_FN("FunctionType", FunctionType::Create, 2, CTYPESCTOR_FLAGS);
 
 static const JSPropertySpec sFunctionProps[] = {
-  { "argTypes", 0, CTYPESPROP_FLAGS,
-    JSOP_WRAPPER(FunctionType::ArgTypesGetter), JSOP_NULLWRAPPER },
-  { "returnType", 0, CTYPESPROP_FLAGS,
-    JSOP_WRAPPER(FunctionType::ReturnTypeGetter), JSOP_NULLWRAPPER },
-  { "abi", 0, CTYPESPROP_FLAGS,
-    JSOP_WRAPPER(FunctionType::ABIGetter), JSOP_NULLWRAPPER },
-  { "isVariadic", 0, CTYPESPROP_FLAGS,
-    JSOP_WRAPPER(FunctionType::IsVariadicGetter), JSOP_NULLWRAPPER },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSG("argTypes",
+         (Property<FunctionType::IsFunctionType, FunctionType::ArgTypesGetter>::Fun),
+         CTYPESACC_FLAGS),
+  JS_PSG("returnType",
+         (Property<FunctionType::IsFunctionType, FunctionType::ReturnTypeGetter>::Fun),
+         CTYPESACC_FLAGS),
+  JS_PSG("abi",
+         (Property<FunctionType::IsFunctionType, FunctionType::ABIGetter>::Fun),
+         CTYPESACC_FLAGS),
+  JS_PSG("isVariadic",
+         (Property<FunctionType::IsFunctionType, FunctionType::IsVariadicGetter>::Fun),
+         CTYPESACC_FLAGS),
+  JS_PS_END
 };
 
 static const JSFunctionSpec sFunctionInstanceFunctions[] = {
   JS_FN("call", js_fun_call, 1, CDATAFN_FLAGS),
   JS_FN("apply", js_fun_apply, 2, CDATAFN_FLAGS),
   JS_FS_END
 };
 
@@ -731,23 +769,25 @@ static const JSFunctionSpec sInt64Functi
 
 static const JSFunctionSpec sUInt64Functions[] = {
   JS_FN("toString", UInt64::ToString, 0, CTYPESFN_FLAGS),
   JS_FN("toSource", UInt64::ToSource, 0, CTYPESFN_FLAGS),
   JS_FS_END
 };
 
 static const JSPropertySpec sModuleProps[] = {
-  { "errno", 0, JSPROP_SHARED | JSPROP_PERMANENT,
-    JSOP_WRAPPER(CData::ErrnoGetter), JSOP_NULLWRAPPER },
+  JS_PSG("errno",
+         (Property<IsCTypesGlobal, CData::ErrnoGetter>::Fun),
+         JSPROP_PERMANENT),
 #if defined(XP_WIN)
-  { "winLastError", 0, JSPROP_SHARED | JSPROP_PERMANENT,
-    JSOP_WRAPPER(CData::LastErrorGetter), JSOP_NULLWRAPPER },
+  JS_PSG("winLastError",
+         (Property<IsCTypesGlobal, CData::LastErrorGetter>::Fun),
+         JSPROP_PERMANENT),
 #endif // defined(XP_WIN)
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PS_END
 };
 
 static const JSFunctionSpec sModuleFunctions[] = {
   JS_FN("CDataFinalizer", CDataFinalizer::Construct, 2, CTYPESFN_FLAGS),
   JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
   JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
   JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS),
   JS_FN("libraryName", Library::Name, 1, CTYPESFN_FLAGS),
@@ -1230,16 +1270,22 @@ InitTypeClasses(JSContext* cx, HandleObj
 }
 
 bool
 IsCTypesGlobal(JSObject* obj)
 {
   return JS_GetClass(obj) == &sCTypesGlobalClass;
 }
 
+bool
+IsCTypesGlobal(HandleValue v)
+{
+  return v.isObject() && IsCTypesGlobal(&v.toObject());
+}
+
 // Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'.
 JSCTypesCallbacks*
 GetCallbacks(JSObject* obj)
 {
   JS_ASSERT(IsCTypesGlobal(obj));
 
   jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS);
   if (JSVAL_IS_VOID(result))
@@ -3569,72 +3615,71 @@ CType::GetProtoFromType(JSContext* cx, J
 
   // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype.
   jsval result = JS_GetReservedSlot(proto, slot);
   JS_ASSERT(!JSVAL_IS_PRIMITIVE(result));
   return JSVAL_TO_OBJECT(result);
 }
 
 bool
-CType::PrototypeGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
-{
-  if (!(CType::IsCType(obj) || CType::IsCTypeProto(obj))) {
-    JS_ReportError(cx, "not a CType or CTypeProto");
-    return false;
-  }
-
+CType::IsCTypeOrProto(HandleValue v)
+{
+  if (!v.isObject())
+    return false;
+  JSObject* obj = &v.toObject();
+  return CType::IsCType(obj) || CType::IsCTypeProto(obj);
+}
+
+bool
+CType::PrototypeGetter(JSContext* cx, JS::CallArgs args)
+{
+  RootedObject obj(cx, &args.thisv().toObject());
   unsigned slot = CType::IsCTypeProto(obj) ? (unsigned) SLOT_OURDATAPROTO
                                            : (unsigned) SLOT_PROTO;
-  vp.set(JS_GetReservedSlot(obj, slot));
-  JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp) || JSVAL_IS_VOID(vp));
+  args.rval().set(JS_GetReservedSlot(obj, slot));
+  MOZ_ASSERT(args.rval().isObject() || args.rval().isUndefined());
   return true;
 }
 
 bool
-CType::NameGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
-{
-  if (!CType::IsCType(obj)) {
-    JS_ReportError(cx, "not a CType");
-    return false;
-  }
-
+CType::IsCType(HandleValue v)
+{
+  return v.isObject() && CType::IsCType(&v.toObject());
+}
+
+bool
+CType::NameGetter(JSContext* cx, JS::CallArgs args)
+{
+  RootedObject obj(cx, &args.thisv().toObject());
   JSString* name = CType::GetName(cx, obj);
   if (!name)
     return false;
 
-  vp.setString(name);
+  args.rval().setString(name);
   return true;
 }
 
 bool
-CType::SizeGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
-{
-  if (!CType::IsCType(obj)) {
-    JS_ReportError(cx, "not a CType");
-    return false;
-  }
-
-  vp.set(JS_GetReservedSlot(obj, SLOT_SIZE));
-  JS_ASSERT(JSVAL_IS_NUMBER(vp) || JSVAL_IS_VOID(vp));
+CType::SizeGetter(JSContext* cx, JS::CallArgs args)
+{
+  RootedObject obj(cx, &args.thisv().toObject());
+  args.rval().set(JS_GetReservedSlot(obj, SLOT_SIZE));
+  MOZ_ASSERT(args.rval().isNumber() || args.rval().isUndefined());
   return true;
 }
 
 bool
-CType::PtrGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
-{
-  if (!CType::IsCType(obj)) {
-    JS_ReportError(cx, "not a CType");
-    return false;
-  }
-
+CType::PtrGetter(JSContext* cx, JS::CallArgs args)
+{
+  RootedObject obj(cx, &args.thisv().toObject());
   JSObject* pointerType = PointerType::CreateInternal(cx, obj);
   if (!pointerType)
     return false;
 
-  vp.setObject(*pointerType);
+  args.rval().setObject(*pointerType);
   return true;
 }
 
 bool
 CType::CreateArray(JSContext* cx, unsigned argc, jsval* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject baseType(cx, JS_THIS_OBJECT(cx, vp));
@@ -3977,28 +4022,39 @@ PointerType::GetBaseType(JSObject* obj)
   JS_ASSERT(CType::GetTypeCode(obj) == TYPE_pointer);
 
   jsval type = JS_GetReservedSlot(obj, SLOT_TARGET_T);
   JS_ASSERT(!type.isNull());
   return &type.toObject();
 }
 
 bool
-PointerType::TargetTypeGetter(JSContext* cx,
-                              HandleObject obj,
-                              HandleId idval,
-                              MutableHandleValue vp)
-{
-  if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_pointer) {
-    JS_ReportError(cx, "not a PointerType");
-    return false;
-  }
-
-  vp.set(JS_GetReservedSlot(obj, SLOT_TARGET_T));
-  JS_ASSERT(vp.isObject());
+PointerType::IsPointerType(HandleValue v)
+{
+  if (!v.isObject())
+    return false;
+  JSObject* obj = &v.toObject();
+  return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_pointer;
+}
+
+bool
+PointerType::IsPointer(HandleValue v)
+{
+  if (!v.isObject())
+    return false;
+  JSObject* obj = &v.toObject();
+  return CData::IsCData(obj) && CType::GetTypeCode(CData::GetCType(obj)) == TYPE_pointer;
+}
+
+bool
+PointerType::TargetTypeGetter(JSContext* cx, JS::CallArgs args)
+{
+  RootedObject obj(cx, &args.thisv().toObject());
+  args.rval().set(JS_GetReservedSlot(obj, SLOT_TARGET_T));
+  MOZ_ASSERT(args.rval().isObject());
   return true;
 }
 
 bool
 PointerType::IsNull(JSContext* cx, unsigned argc, jsval* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
@@ -4067,85 +4123,57 @@ PointerType::Increment(JSContext* cx, un
 bool
 PointerType::Decrement(JSContext* cx, unsigned argc, jsval* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   return OffsetBy(cx, args, -1);
 }
 
 bool
-PointerType::ContentsGetter(JSContext* cx,
-                            HandleObject obj,
-                            HandleId idval,
-                            MutableHandleValue vp)
-{
-  if (!CData::IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
-  }
-
-  // Get pointer type and base type.
-  JSObject* typeObj = CData::GetCType(obj);
-  if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
-    JS_ReportError(cx, "not a PointerType");
-    return false;
-  }
-
-  RootedObject baseType(cx, GetBaseType(typeObj));
+PointerType::ContentsGetter(JSContext* cx, JS::CallArgs args)
+{
+  RootedObject obj(cx, &args.thisv().toObject());
+  RootedObject baseType(cx, GetBaseType(CData::GetCType(obj)));
   if (!CType::IsSizeDefined(baseType)) {
     JS_ReportError(cx, "cannot get contents of undefined size");
     return false;
   }
 
   void* data = *static_cast<void**>(CData::GetData(obj));
   if (data == nullptr) {
     JS_ReportError(cx, "cannot read contents of null pointer");
     return false;
   }
 
   RootedValue result(cx);
   if (!ConvertToJS(cx, baseType, NullPtr(), data, false, false, result.address()))
     return false;
 
-  vp.set(result);
+  args.rval().set(result);
   return true;
 }
 
 bool
-PointerType::ContentsSetter(JSContext* cx,
-                            HandleObject obj,
-                            HandleId idval,
-                            bool strict,
-                            MutableHandleValue vp)
-{
-  if (!CData::IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
-  }
-
-  // Get pointer type and base type.
-  JSObject* typeObj = CData::GetCType(obj);
-  if (CType::GetTypeCode(typeObj) != TYPE_pointer) {
-    JS_ReportError(cx, "not a PointerType");
-    return false;
-  }
-
-  JSObject* baseType = GetBaseType(typeObj);
+PointerType::ContentsSetter(JSContext* cx, JS::CallArgs args)
+{
+  RootedObject obj(cx, &args.thisv().toObject());
+  RootedObject baseType(cx, GetBaseType(CData::GetCType(obj)));
   if (!CType::IsSizeDefined(baseType)) {
     JS_ReportError(cx, "cannot set contents of undefined size");
     return false;
   }
 
   void* data = *static_cast<void**>(CData::GetData(obj));
   if (data == nullptr) {
     JS_ReportError(cx, "cannot write contents to null pointer");
     return false;
   }
 
-  return ImplicitConvert(cx, vp, baseType, data, false, nullptr);
+  args.rval().setUndefined();
+  return ImplicitConvert(cx, args.get(0), baseType, data, false, nullptr);
 }
 
 /*******************************************************************************
 ** ArrayType implementation
 *******************************************************************************/
 
 bool
 ArrayType::Create(JSContext* cx, unsigned argc, jsval* vp)
@@ -4426,45 +4454,60 @@ ArrayType::BuildFFIType(JSContext* cx, J
   for (size_t i = 0; i < length; ++i)
     ffiType->elements[i] = ffiBaseType;
   ffiType->elements[length] = nullptr;
 
   return ffiType.forget();
 }
 
 bool
-ArrayType::ElementTypeGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
-{
-  if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
-    JS_ReportError(cx, "not an ArrayType");
-    return false;
-  }
-
-  vp.set(JS_GetReservedSlot(obj, SLOT_ELEMENT_T));
-  JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp));
+ArrayType::IsArrayType(HandleValue v)
+{
+  if (!v.isObject())
+    return false;
+  JSObject* obj = &v.toObject();
+  return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array;
+}
+
+bool
+ArrayType::IsArrayOrArrayType(HandleValue v)
+{
+  if (!v.isObject())
+    return false;
+  JSObject* obj = &v.toObject();
+
+   // Allow both CTypes and CDatas of the ArrayType persuasion by extracting the
+   // CType if we're dealing with a CData.
+  if (CData::IsCData(obj)) {
+    obj = CData::GetCType(obj);
+  }
+  return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_array;
+}
+
+bool
+ArrayType::ElementTypeGetter(JSContext* cx, JS::CallArgs args)
+{
+  RootedObject obj(cx, &args.thisv().toObject());
+  args.rval().set(JS_GetReservedSlot(obj, SLOT_ELEMENT_T));
+  MOZ_ASSERT(args.rval().isObject());
   return true;
 }
 
 bool
-ArrayType::LengthGetter(JSContext* cx, HandleObject obj_, HandleId idval, MutableHandleValue vp)
-{
-  JSObject *obj = obj_;
+ArrayType::LengthGetter(JSContext* cx, JS::CallArgs args)
+{
+  JSObject *obj = &args.thisv().toObject();
 
   // This getter exists for both CTypes and CDatas of the ArrayType persuasion.
   // If we're dealing with a CData, get the CType from it.
   if (CData::IsCData(obj))
     obj = CData::GetCType(obj);
 
-  if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_array) {
-    JS_ReportError(cx, "not an ArrayType");
-    return false;
-  }
-
-  vp.set(JS_GetReservedSlot(obj, SLOT_LENGTH));
-  JS_ASSERT(vp.isNumber() || vp.isUndefined());
+  args.rval().set(JS_GetReservedSlot(obj, SLOT_LENGTH));
+  JS_ASSERT(args.rval().isNumber() || args.rval().isUndefined());
   return true;
 }
 
 bool
 ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
 {
   // This should never happen, but we'll check to be safe.
   if (!CData::IsCData(obj)) {
@@ -5106,43 +5149,49 @@ StructType::BuildFieldsArray(JSContext* 
 
   // Seal the fields array.
   if (!JS_FreezeObject(cx, fieldsProp))
     return nullptr;
 
   return fieldsProp;
 }
 
+/* static */ bool
+StructType::IsStruct(HandleValue v)
+{
+  if (!v.isObject())
+    return false;
+  JSObject* obj = &v.toObject();
+  return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_struct;
+}
+
 bool
-StructType::FieldsArrayGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
-{
-  if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_struct) {
-    JS_ReportError(cx, "not a StructType");
-    return false;
-  }
-
-  vp.set(JS_GetReservedSlot(obj, SLOT_FIELDS));
+StructType::FieldsArrayGetter(JSContext* cx, JS::CallArgs args)
+{
+  RootedObject obj(cx, &args.thisv().toObject());
+
+  args.rval().set(JS_GetReservedSlot(obj, SLOT_FIELDS));
 
   if (!CType::IsSizeDefined(obj)) {
-    JS_ASSERT(JSVAL_IS_VOID(vp));
+    MOZ_ASSERT(args.rval().isUndefined());
     return true;
   }
 
-  if (JSVAL_IS_VOID(vp)) {
+  if (args.rval().isUndefined()) {
     // Build the 'fields' array lazily.
     JSObject* fields = BuildFieldsArray(cx, obj);
     if (!fields)
       return false;
     JS_SetReservedSlot(obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields));
 
-    vp.setObject(*fields);
-  }
-
-  JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp) &&
-            JS_IsArrayObject(cx, JSVAL_TO_OBJECT(vp)));
+    args.rval().setObject(*fields);
+  }
+
+  MOZ_ASSERT(args.rval().isObject());
+  MOZ_ASSERT(JS_IsArrayObject(cx, &args.rval().toObject()));
   return true;
 }
 
 bool
 StructType::FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
 {
   if (!CData::IsCData(obj)) {
     JS_ReportError(cx, "not a CData");
@@ -5866,90 +5915,81 @@ FunctionType::GetFunctionInfo(JSObject* 
   JS_ASSERT(CType::GetTypeCode(obj) == TYPE_function);
 
   jsval slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
   JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
 
   return static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
 }
 
-static bool
-CheckFunctionType(JSContext* cx, JSObject* obj)
-{
-  if (!CType::IsCType(obj) || CType::GetTypeCode(obj) != TYPE_function) {
-    JS_ReportError(cx, "not a FunctionType");
-    return false;
-  }
-  return true;
+bool
+FunctionType::IsFunctionType(HandleValue v)
+{
+  if (!v.isObject())
+    return false;
+  JSObject* obj = &v.toObject();
+  return CType::IsCType(obj) && CType::GetTypeCode(obj) == TYPE_function;
 }
 
 bool
-FunctionType::ArgTypesGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
-{
-  if (!CheckFunctionType(cx, obj))
-    return false;
-
-  // Check if we have a cached argTypes array.
-  vp.set(JS_GetReservedSlot(obj, SLOT_ARGS_T));
-  if (!JSVAL_IS_VOID(vp))
+FunctionType::ArgTypesGetter(JSContext* cx, JS::CallArgs args)
+{
+  JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
+
+  args.rval().set(JS_GetReservedSlot(obj, SLOT_ARGS_T));
+  if (!args.rval().isUndefined())
     return true;
 
   FunctionInfo* fninfo = GetFunctionInfo(obj);
   size_t len = fninfo->mArgTypes.length();
 
   // Prepare a new array.
-  JS::AutoValueVector vec(cx);
-  if (!vec.resize(len))
-    return false;
-
-  for (size_t i = 0; i < len; ++i)
-    vec[i] = OBJECT_TO_JSVAL(fninfo->mArgTypes[i]);
-
-  RootedObject argTypes(cx, JS_NewArrayObject(cx, len, vec.begin()));
-  if (!argTypes)
-    return false;
+  JS::Rooted<JSObject*> argTypes(cx);
+  {
+      JS::AutoValueVector vec(cx);
+      if (!vec.resize(len))
+        return false;
+
+      for (size_t i = 0; i < len; ++i)
+        vec[i] = JS::ObjectValue(*fninfo->mArgTypes[i]);
+
+      argTypes = JS_NewArrayObject(cx, len, vec.begin());
+      if (!argTypes)
+        return false;
+  }
 
   // Seal and cache it.
   if (!JS_FreezeObject(cx, argTypes))
     return false;
-  JS_SetReservedSlot(obj, SLOT_ARGS_T, OBJECT_TO_JSVAL(argTypes));
-
-  vp.setObject(*argTypes);
+  JS_SetReservedSlot(obj, SLOT_ARGS_T, JS::ObjectValue(*argTypes));
+
+  args.rval().setObject(*argTypes);
   return true;
 }
 
 bool
-FunctionType::ReturnTypeGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
-{
-  if (!CheckFunctionType(cx, obj))
-    return false;
-
+FunctionType::ReturnTypeGetter(JSContext* cx, JS::CallArgs args)
+{
   // Get the returnType object from the FunctionInfo.
-  vp.setObject(*GetFunctionInfo(obj)->mReturnType);
+  args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mReturnType);
   return true;
 }
 
 bool
-FunctionType::ABIGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
-{
-  if (!CheckFunctionType(cx, obj))
-    return false;
-
+FunctionType::ABIGetter(JSContext* cx, JS::CallArgs args)
+{
   // Get the abi object from the FunctionInfo.
-  vp.setObject(*GetFunctionInfo(obj)->mABI);
+  args.rval().setObject(*GetFunctionInfo(&args.thisv().toObject())->mABI);
   return true;
 }
 
 bool
-FunctionType::IsVariadicGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
-{
-  if (!CheckFunctionType(cx, obj))
-    return false;
-
-  vp.setBoolean(GetFunctionInfo(obj)->mIsVariadic);
+FunctionType::IsVariadicGetter(JSContext* cx, JS::CallArgs args)
+{
+  args.rval().setBoolean(GetFunctionInfo(&args.thisv().toObject())->mIsVariadic);
   return true;
 }
 
 /*******************************************************************************
 ** CClosure implementation
 *******************************************************************************/
 
 JSObject*
@@ -6359,46 +6399,43 @@ CData::GetData(JSObject* dataObj)
 
 bool
 CData::IsCData(JSObject* obj)
 {
   return JS_GetClass(obj) == &sCDataClass;
 }
 
 bool
+CData::IsCData(HandleValue v)
+{
+  return v.isObject() && CData::IsCData(&v.toObject());
+}
+
+bool
 CData::IsCDataProto(JSObject* obj)
 {
   return JS_GetClass(obj) == &sCDataProtoClass;
 }
 
 bool
-CData::ValueGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp)
-{
-  if (!IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
-  }
+CData::ValueGetter(JSContext* cx, JS::CallArgs args)
+{
+  RootedObject obj(cx, &args.thisv().toObject());
 
   // Convert the value to a primitive; do not create a new CData object.
   RootedObject ctype(cx, GetCType(obj));
-  if (!ConvertToJS(cx, ctype, NullPtr(), GetData(obj), true, false, vp.address()))
-    return false;
-
-  return true;
+  return ConvertToJS(cx, ctype, NullPtr(), GetData(obj), true, false, args.rval().address());
 }
 
 bool
-CData::ValueSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp)
-{
-  if (!IsCData(obj)) {
-    JS_ReportError(cx, "not a CData");
-    return false;
-  }
-
-  return ImplicitConvert(cx, vp, GetCType(obj), GetData(obj), false, nullptr);
+CData::ValueSetter(JSContext* cx, JS::CallArgs args)
+{
+  RootedObject obj(cx, &args.thisv().toObject());
+  args.rval().setUndefined();
+  return ImplicitConvert(cx, args.get(0), GetCType(obj), GetData(obj), false, nullptr);
 }
 
 bool
 CData::Address(JSContext* cx, unsigned argc, jsval* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     JS_ReportError(cx, "address takes zero arguments");
@@ -6654,37 +6691,27 @@ CData::ToSource(JSContext* cx, unsigned 
   if (!result)
     return false;
 
   args.rval().setString(result);
   return true;
 }
 
 bool
-CData::ErrnoGetter(JSContext* cx, HandleObject obj, HandleId, MutableHandleValue vp)
-{
-  if (!IsCTypesGlobal(obj)) {
-    JS_ReportError(cx, "this is not not global object ctypes");
-    return false;
-  }
-
-  vp.set(JS_GetReservedSlot(obj, SLOT_ERRNO));
+CData::ErrnoGetter(JSContext* cx, JS::CallArgs args)
+{
+  args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_ERRNO));
   return true;
 }
 
 #if defined(XP_WIN)
 bool
-CData::LastErrorGetter(JSContext* cx, HandleObject obj, HandleId, MutableHandleValue vp)
-{
-  if (!IsCTypesGlobal(obj)) {
-    JS_ReportError(cx, "not global object ctypes");
-    return false;
-  }
-
-  vp.set(JS_GetReservedSlot(obj, SLOT_LASTERROR));
+CData::LastErrorGetter(JSContext* cx, JS::CallArgs args)
+{
+  args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_LASTERROR));
   return true;
 }
 #endif // defined(XP_WIN)
 
 bool
 CDataFinalizer::Methods::ToSource(JSContext *cx, unsigned argc, jsval *vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -300,16 +300,17 @@ struct ClosureInfo
 
   ~ClosureInfo() {
     if (closure)
       ffi_closure_free(closure);
     js_free(errResult);
   }
 };
 
+bool IsCTypesGlobal(HandleValue v);
 bool IsCTypesGlobal(JSObject* obj);
 
 JSCTypesCallbacks* GetCallbacks(JSObject* obj);
 
 /*******************************************************************************
 ** JSClass reserved slot definitions
 *******************************************************************************/
 
@@ -471,16 +472,17 @@ namespace CClosure {
 
 namespace CData {
   JSObject* Create(JSContext* cx, HandleObject typeObj, HandleObject refObj,
     void* data, bool ownResult);
 
   JSObject* GetCType(JSObject* dataObj);
   void* GetData(JSObject* dataObj);
   bool IsCData(JSObject* obj);
+  bool IsCData(HandleValue v);
   bool IsCDataProto(JSObject* obj);
 
   // Attached by JSAPI as the function 'ctypes.cast'
   bool Cast(JSContext* cx, unsigned argc, jsval* vp);
   // Attached by JSAPI as the function 'ctypes.getRuntime'
   bool GetRuntime(JSContext* cx, unsigned argc, jsval* vp);
 }
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -172,16 +172,18 @@ UpdateDepth(ExclusiveContext *cx, Byteco
     int nuses, ndefs;
     if (op == JSOP_ENTERBLOCK) {
         nuses = 0;
         ndefs = CurrentBlock(bce->topStmt).slotCount();
     } else if (op == JSOP_ENTERLET0) {
         nuses = ndefs = CurrentBlock(bce->topStmt).slotCount();
     } else if (op == JSOP_ENTERLET1) {
         nuses = ndefs = CurrentBlock(bce->topStmt).slotCount() + 1;
+    } else if (op == JSOP_ENTERLET2) {
+        nuses = ndefs = CurrentBlock(bce->topStmt).slotCount() + 2;
     } else {
         nuses = StackUses(nullptr, pc);
         ndefs = StackDefs(nullptr, pc);
     }
 
     bce->stackDepth -= nuses;
     JS_ASSERT(bce->stackDepth >= 0);
     bce->stackDepth += ndefs;
@@ -293,16 +295,17 @@ static const char * const statementName[
     js_with_statement_str,   /* WITH */
     "catch block",           /* CATCH */
     "try block",             /* TRY */
     js_finally_block_str,    /* FINALLY */
     js_finally_block_str,    /* SUBROUTINE */
     "do loop",               /* DO_LOOP */
     "for loop",              /* FOR_LOOP */
     "for/in loop",           /* FOR_IN_LOOP */
+    "for/of loop",           /* FOR_OF_LOOP */
     "while loop",            /* WHILE_LOOP */
 };
 
 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(statementName) == STMT_LIMIT);
 
 static const char *
 StatementName(StmtInfoBCE *topStmt)
 {
@@ -550,16 +553,20 @@ EmitNonLocalJumpFixup(ExclusiveContext *
             /* There's a With object on the stack that we need to pop. */
             FLUSH_POPS();
             if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
                 return false;
             if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
                 return false;
             break;
 
+          case STMT_FOR_OF_LOOP:
+            npops += 2;
+            break;
+
           case STMT_FOR_IN_LOOP:
             FLUSH_POPS();
             if (!PopIterator(cx, bce))
                 return false;
             break;
 
           case STMT_SUBROUTINE:
             /*
@@ -572,33 +579,38 @@ EmitNonLocalJumpFixup(ExclusiveContext *
           default:;
         }
 
         if (stmt->isBlockScope) {
             FLUSH_POPS();
             unsigned blockObjCount = stmt->blockObj->slotCount();
             if (stmt->isForLetBlock) {
                 /*
-                 * For a for-let-in statement, pushing/popping the block is
+                 * For a for-let-in/of statement, pushing/popping the block is
                  * interleaved with JSOP_(END)ITER. Just handle both together
                  * here and skip over the enclosing STMT_FOR_IN_LOOP.
                  */
-                JS_ASSERT(stmt->down->type == STMT_FOR_IN_LOOP);
+                unsigned popCount = blockObjCount;
                 stmt = stmt->down;
                 if (stmt == toStmt)
                     break;
                 if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
                     return false;
                 if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
                     return false;
-                if (!PopIterator(cx, bce))
-                    return false;
+                if (stmt->type == STMT_FOR_OF_LOOP) {
+                    popCount += 2;
+                } else {
+                    JS_ASSERT(stmt->type == STMT_FOR_IN_LOOP);
+                    if (!PopIterator(cx, bce))
+                        return false;
+                }
                 if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
                     return false;
-                EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount);
+                EMIT_UINT16_IMM_OP(JSOP_POPN, popCount);
             } else {
                 /* There is a Block object with locals on the stack to pop. */
                 if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
                     return false;
                 EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObjCount);
             }
         }
     }
@@ -1047,18 +1059,22 @@ static bool
 EmitEnterBlock(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
 {
     JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
     if (!EmitObjectOp(cx, pn->pn_objbox, op, bce))
         return false;
 
     Rooted<StaticBlockObject*> blockObj(cx, &pn->pn_objbox->object->as<StaticBlockObject>());
 
-    int depth = bce->stackDepth -
-                (blockObj->slotCount() + ((op == JSOP_ENTERLET1) ? 1 : 0));
+    int extraSlots = (op == JSOP_ENTERLET1)
+                     ? 1
+                     : (op == JSOP_ENTERLET2)
+                     ? 2
+                     : 0;
+    int depth = bce->stackDepth - (blockObj->slotCount() + extraSlots);
     JS_ASSERT(depth >= 0);
 
     blockObj->setStackDepth(depth);
 
     int depthPlusFixed = AdjustBlockSlot(cx, bce, depth);
     if (depthPlusFixed < 0)
         return false;
 
@@ -3513,20 +3529,21 @@ EmitAssignment(ExclusiveContext *cx, Byt
     }
 
     /* Now emit the right operand (it may affect the namespace). */
     if (rhs) {
         if (!EmitTree(cx, bce, rhs))
             return false;
     } else {
         /*
-         * The value to assign is the next enumeration value in a for-in loop.
-         * That value is produced by a JSOP_ITERNEXT op, previously emitted.
-         * If offset == 1, that slot is already at the top of the
-         * stack. Otherwise, rearrange the stack to put that value on top.
+         * The value to assign is the next enumeration value in a for-in or
+         * for-of loop.  That value has already been emitted: by JSOP_ITERNEXT
+         * in the for-in case, or via a GETPROP "value" on the result object in
+         * the for-of case.  If offset == 1, that slot is already at the top of
+         * the stack. Otherwise, rearrange the stack to put that value on top.
          */
         if (offset != 1 && Emit2(cx, bce, JSOP_PICK, offset - 1) < 0)
             return false;
     }
 
     /* If += etc., emit the binary operator with a source note. */
     if (op != JSOP_NOP) {
         /*
@@ -4230,16 +4247,180 @@ EmitWith(ExclusiveContext *cx, BytecodeE
     if (!EmitTree(cx, bce, pn->pn_right))
         return false;
     if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
         return false;
     return PopStatementBCE(cx, bce);
 }
 
 static bool
+EmitForOf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
+{
+    StmtInfoBCE stmtInfo(cx);
+    PushStatementBCE(bce, &stmtInfo, STMT_FOR_OF_LOOP, top);
+
+    ParseNode *forHead = pn->pn_left;
+    ParseNode *forBody = pn->pn_right;
+
+    ParseNode *pn1 = forHead->pn_kid1;
+    bool letDecl = pn1 && pn1->isKind(PNK_LEXICALSCOPE);
+    JS_ASSERT_IF(letDecl, pn1->isLet());
+
+    Rooted<StaticBlockObject*>
+        blockObj(cx, letDecl ? &pn1->pn_objbox->object->as<StaticBlockObject>() : nullptr);
+    uint32_t blockObjCount = blockObj ? blockObj->slotCount() : 0;
+
+    // For-of loops run with two values on the stack: the iterator and the
+    // current result object.  If the loop also has a lexical block, those
+    // lexicals are deeper on the stack than the iterator.
+    for (uint32_t i = 0; i < blockObjCount; ++i) {
+        if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
+            return false;
+    }
+
+    // If the left part is 'var x', emit code to define x if necessary using a
+    // prolog opcode, but do not emit a pop.
+    if (pn1) {
+        ParseNode *decl = letDecl ? pn1->pn_expr : pn1;
+        JS_ASSERT(decl->isKind(PNK_VAR) || decl->isKind(PNK_LET));
+        bce->emittingForInit = true;
+        if (!EmitVariables(cx, bce, decl, DefineVars))
+            return false;
+        bce->emittingForInit = false;
+    }
+
+    // Compile the object expression to the right of 'of'.
+    if (!EmitTree(cx, bce, forHead->pn_kid3))
+        return false;
+
+    // Convert iterable to iterator.
+    if (Emit1(cx, bce, JSOP_DUP) < 0)                          // OBJ OBJ
+        return false;
+    if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // OBJ @@ITERATOR
+        return false;
+    if (Emit1(cx, bce, JSOP_SWAP) < 0)                         // @@ITERATOR OBJ
+        return false;
+    if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
+        return false;
+    if (EmitCall(cx, bce, JSOP_CALL, 0) < 0)                   // ITER
+        return false;
+    CheckTypeSet(cx, bce, JSOP_CALL);
+
+    // Push a dummy result so that we properly enter iteration midstream.
+    if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)                    // ITER RESULT
+        return false;
+
+    // Enter the block before the loop body, after evaluating the obj.
+    StmtInfoBCE letStmt(cx);
+    if (letDecl) {
+        PushBlockScopeBCE(bce, &letStmt, *blockObj, bce->offset());
+        letStmt.isForLetBlock = true;
+        if (!EmitEnterBlock(cx, bce, pn1, JSOP_ENTERLET2))
+            return false;
+    }
+
+    // Jump down to the loop condition to minimize overhead assuming at least
+    // one iteration, as the other loop forms do.  Annotate so IonMonkey can
+    // find the loop-closing jump.
+    int noteIndex = NewSrcNote(cx, bce, SRC_FOR_OF);
+    if (noteIndex < 0)
+        return false;
+    ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
+    if (jmp < 0)
+        return false;
+
+    top = bce->offset();
+    SET_STATEMENT_TOP(&stmtInfo, top);
+    if (EmitLoopHead(cx, bce, nullptr) < 0)
+        return false;
+
+#ifdef DEBUG
+    int loopDepth = bce->stackDepth;
+#endif
+
+    // Emit code to assign result.value to the iteration variable.
+    if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ITER RESULT RESULT
+        return false;
+    if (!EmitAtomOp(cx, cx->names().value, JSOP_GETPROP, bce)) // ITER RESULT VALUE
+        return false;
+    if (!EmitAssignment(cx, bce, forHead->pn_kid2, JSOP_NOP, nullptr)) // ITER RESULT VALUE
+        return false;
+    if (Emit1(cx, bce, JSOP_POP) < 0)                          // ITER RESULT
+        return false;
+
+    // The stack should be balanced around the assignment opcode sequence.
+    JS_ASSERT(bce->stackDepth == loopDepth);
+
+    // Emit code for the loop body.
+    if (!EmitTree(cx, bce, forBody))
+        return false;
+
+    // Set loop and enclosing "update" offsets, for continue.
+    StmtInfoBCE *stmt = &stmtInfo;
+    do {
+        stmt->update = bce->offset();
+    } while ((stmt = stmt->down) != nullptr && stmt->type == STMT_LABEL);
+
+    // COME FROM the beginning of the loop to here.
+    SetJumpOffsetAt(bce, jmp);
+    if (!EmitLoopEntry(cx, bce, nullptr))
+        return false;
+
+    if (Emit1(cx, bce, JSOP_POP) < 0)                          // ITER
+        return false;
+    if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ITER ITER
+        return false;
+    if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ITER ITER ITER
+        return false;
+    if (!EmitAtomOp(cx, cx->names().next, JSOP_CALLPROP, bce)) // ITER ITER NEXT
+        return false;
+    if (Emit1(cx, bce, JSOP_SWAP) < 0)                         // ITER NEXT ITER
+        return false;
+    if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
+        return false;
+    if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)                    // ITER NEXT ITER UNDEFINED
+        return false;
+    if (Emit1(cx, bce, JSOP_NOTEARG) < 0)
+        return false;
+    if (EmitCall(cx, bce, JSOP_CALL, 1) < 0)                   // ITER RESULT
+        return false;
+    CheckTypeSet(cx, bce, JSOP_CALL);
+    if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ITER RESULT RESULT
+        return false;
+    if (!EmitAtomOp(cx, cx->names().done, JSOP_GETPROP, bce))  // ITER RESULT DONE?
+        return false;
+
+    ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, top - bce->offset()); // ITER RESULT
+    if (beq < 0)
+        return false;
+
+    JS_ASSERT(bce->stackDepth == loopDepth);
+
+    // Let Ion know where the closing jump of this loop is.
+    if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, beq - jmp))
+        return false;
+
+    // Fixup breaks and continues.
+    if (!PopStatementBCE(cx, bce))
+        return false;
+
+    if (letDecl) {
+        if (!PopStatementBCE(cx, bce))
+            return false;
+        if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
+            return false;
+    }
+
+    // Pop result, iter, and slots from the lexical block (if any).
+    EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount + 2);
+
+    return true;
+}
+
+static bool
 EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
 {
     StmtInfoBCE stmtInfo(cx);
     PushStatementBCE(bce, &stmtInfo, STMT_FOR_IN_LOOP, top);
 
     ParseNode *forHead = pn->pn_left;
     ParseNode *forBody = pn->pn_right;
 
@@ -4548,20 +4729,24 @@ EmitNormalFor(ExclusiveContext *cx, Byte
 
     /* Now fixup all breaks and continues. */
     return PopStatementBCE(cx, bce);
 }
 
 static inline bool
 EmitFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
 {
-    JS_ASSERT(pn->pn_left->isKind(PNK_FORIN) || pn->pn_left->isKind(PNK_FORHEAD));
-    return pn->pn_left->isKind(PNK_FORIN)
-           ? EmitForIn(cx, bce, pn, top)
-           : EmitNormalFor(cx, bce, pn, top);
+    if (pn->pn_left->isKind(PNK_FORIN)) {
+        // FIXME: Give for-of loops their own PNK.  Bug 922066.
+        if (pn->pn_iflags == JSITER_FOR_OF)
+            return EmitForOf(cx, bce, pn, top);
+        return EmitForIn(cx, bce, pn, top);
+    }
+    JS_ASSERT(pn->pn_left->isKind(PNK_FORHEAD));
+    return EmitNormalFor(cx, bce, pn, top);
 }
 
 static JS_NEVER_INLINE bool
 EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     cx->maybePause();
 
     FunctionBox *funbox = pn->pn_funbox;
@@ -4987,16 +5172,17 @@ EmitYieldStar(ExclusiveContext *cx, Byte
     if (Emit1(cx, bce, JSOP_NOTEARG) < 0)                        // EXCEPTION ITER THROW ITER
         return false;
     if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0)            // ITER THROW ITER EXCEPTION
         return false;
     if (Emit1(cx, bce, JSOP_NOTEARG) < 0)                        // ITER THROW ITER EXCEPTION
         return false;
     if (EmitCall(cx, bce, JSOP_CALL, 1) < 0)                     // ITER RESULT
         return false;
+    CheckTypeSet(cx, bce, JSOP_CALL);
     JS_ASSERT(bce->stackDepth == depth + 1);
     ptrdiff_t checkResult = -1;
     if (EmitBackPatchOp(cx, bce, &checkResult) < 0)              // goto checkResult
         return false;
 
     // Catch epilogue.
     if (!PopStatementBCE(cx, bce))
         return false;
@@ -5027,16 +5213,17 @@ EmitYieldStar(ExclusiveContext *cx, Byte
     if (Emit1(cx, bce, JSOP_NOTEARG) < 0)                        // RECEIVED ITER NEXT ITER
         return false;
     if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0)            // ITER NEXT ITER RECEIVED
         return false;
     if (Emit1(cx, bce, JSOP_NOTEARG) < 0)                        // ITER NEXT ITER RECEIVED
         return false;
     if (EmitCall(cx, bce, JSOP_CALL, 1) < 0)                     // ITER RESULT
         return false;
+    CheckTypeSet(cx, bce, JSOP_CALL);
     JS_ASSERT(bce->stackDepth == depth + 1);
 
     if (!BackPatch(cx, bce, checkResult, bce->code().end(), JSOP_GOTO)) // checkResult:
         return false;
     // if (!result.done) goto tryStart;                          // ITER RESULT
     if (Emit1(cx, bce, JSOP_DUP) < 0)                            // ITER RESULT RESULT
         return false;
     if (!EmitAtomOp(cx, cx->names().done, JSOP_GETPROP, bce))    // ITER RESULT DONE
@@ -6667,51 +6854,53 @@ CGConstList::finish(ConstArray *array)
 
     for (unsigned i = 0; i < length(); i++)
         array->vector[i] = list[i];
 }
 
 /*
  * We should try to get rid of offsetBias (always 0 or 1, where 1 is
  * JSOP_{NOP,POP}_LENGTH), which is used only by SRC_FOR.
+ *
+ * FIXME: Generate this using a higher-order macro.  Bug 922070.
  */
 const JSSrcNoteSpec js_SrcNoteSpec[] = {
 /*  0 */ {"null",           0},
 
 /*  1 */ {"if",             0},
 /*  2 */ {"if-else",        1},
 /*  3 */ {"cond",           1},
 
 /*  4 */ {"for",            3},
 
 /*  5 */ {"while",          1},
 /*  6 */ {"for-in",         1},
-/*  7 */ {"continue",       0},
-/*  8 */ {"break",          0},
-/*  9 */ {"break2label",    0},
-/* 10 */ {"switchbreak",    0},
-
-/* 11 */ {"tableswitch",    1},
-/* 12 */ {"condswitch",     2},
-
-/* 13 */ {"nextcase",       1},
-
-/* 14 */ {"assignop",       0},
-
-/* 15 */ {"hidden",         0},
-
-/* 16 */ {"catch",          0},
-
-/* 17 */ {"try",            1},
-
-/* 18 */ {"colspan",        1},
-/* 19 */ {"newline",        0},
-/* 20 */ {"setline",        1},
-
-/* 21 */ {"unused21",       0},
+/*  7 */ {"for-of",         1},
+/*  8 */ {"continue",       0},
+/*  9 */ {"break",          0},
+/* 10 */ {"break2label",    0},
+/* 11 */ {"switchbreak",    0},
+
+/* 12 */ {"tableswitch",    1},
+/* 13 */ {"condswitch",     2},
+
+/* 14 */ {"nextcase",       1},
+
+/* 15 */ {"assignop",       0},
+
+/* 16 */ {"hidden",         0},
+
+/* 17 */ {"catch",          0},
+
+/* 18 */ {"try",            1},
+
+/* 19 */ {"colspan",        1},
+/* 20 */ {"newline",        0},
+/* 21 */ {"setline",        1},
+
 /* 22 */ {"unused22",       0},
 /* 23 */ {"unused23",       0},
 
 /* 24 */ {"xdelta",         0},
 };
 
 JS_FRIEND_API(unsigned)
 js_SrcNoteLength(jssrcnote *sn)
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3988,17 +3988,17 @@ Parser<FullParseHandler>::forStatement()
         /*
          * Parse the rest of the for/in or for/of head.
          *
          * Here pn1 is everything to the left of 'in' or 'of'. At the end of
          * this block, pn1 is a decl or nullptr, pn2 is the assignment target
          * that receives the enumeration value each iteration, and pn3 is the
          * rhs of 'in'.
          */
-        forStmt.type = STMT_FOR_IN_LOOP;
+        forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
 
         /* Set iflags and rule out invalid combinations. */
         if (isForOf && isForEach) {
             report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
             return null();
         }
         iflags |= (isForOf ? JSITER_FOR_OF : JSITER_ENUMERATE);
 
@@ -4270,17 +4270,17 @@ Parser<SyntaxParseHandler>::forStatement
      * We can be sure that it's a for/in loop if there's still an 'in'
      * keyword here, even if JavaScript recognizes 'in' as an operator,
      * as we've excluded 'in' from being parsed in RelExpr by setting
      * pc->parsingForInit.
      */
     bool isForOf;
     if (lhsNode && matchInOrOf(&isForOf)) {
         /* Parse the rest of the for/in or for/of head. */
-        forStmt.type = STMT_FOR_IN_LOOP;
+        forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
 
         /* Check that the left side of the 'in' or 'of' is valid. */
         if (!isForDecl &&
             lhsNode != SyntaxParseHandler::NodeName &&
             lhsNode != SyntaxParseHandler::NodeGetProp &&
             lhsNode != SyntaxParseHandler::NodeLValue)
         {
             JS_ALWAYS_FALSE(abortIfSyntaxParser());
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -358,16 +358,17 @@ enum StmtType {
     STMT_WITH,                  /* with statement */
     STMT_CATCH,                 /* catch block */
     STMT_TRY,                   /* try block */
     STMT_FINALLY,               /* finally block */
     STMT_SUBROUTINE,            /* gosub-target subroutine body */
     STMT_DO_LOOP,               /* do/while loop statement */
     STMT_FOR_LOOP,              /* for loop statement */
     STMT_FOR_IN_LOOP,           /* for/in loop statement */
+    STMT_FOR_OF_LOOP,           /* for/of loop statement */
     STMT_WHILE_LOOP,            /* while loop statement */
     STMT_LIMIT
 };
 
 /*
  * A comment on the encoding of the js::StmtType enum and StmtInfoBase
  * type-testing methods:
  *
--- a/js/src/frontend/SourceNotes.h
+++ b/js/src/frontend/SourceNotes.h
@@ -31,61 +31,65 @@ namespace js {
  * At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE,
  * SRC_COLSPAN, SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode.
  *
  * NB: the js_SrcNoteSpec array in BytecodeEmitter.cpp is indexed by this
  * enum, so its initializers need to match the order here.
  *
  * Don't forget to update XDR_BYTECODE_VERSION in vm/Xdr.h for all such
  * incompatible source note or other bytecode changes.
+ *
+ * FIXME: Use higher-order macro to force this to be in sync with
+ * js_SrcNoteSpec.  Bug 922070.
  */
 enum SrcNoteType {
     SRC_NULL        = 0,        /* terminates a note vector */
 
     SRC_IF          = 1,        /* JSOP_IFEQ bytecode is from an if-then */
     SRC_IF_ELSE     = 2,        /* JSOP_IFEQ bytecode is from an if-then-else */
     SRC_COND        = 3,        /* JSOP_IFEQ is from conditional ?: operator */
 
     SRC_FOR         = 4,        /* JSOP_NOP or JSOP_POP in for(;;) loop head */
 
     SRC_WHILE       = 5,        /* JSOP_GOTO to for or while loop condition
                                    from before loop, else JSOP_NOP at top of
                                    do-while loop */
     SRC_FOR_IN      = 6,        /* JSOP_GOTO to for-in loop condition from
                                    before loop */
-    SRC_CONTINUE    = 7,        /* JSOP_GOTO is a continue */
-    SRC_BREAK       = 8,        /* JSOP_GOTO is a break */
-    SRC_BREAK2LABEL = 9,        /* JSOP_GOTO for 'break label' */
-    SRC_SWITCHBREAK = 10,       /* JSOP_GOTO is a break in a switch */
+    SRC_FOR_OF      = 7,        /* JSOP_GOTO to for-of loop condition from
+                                   before loop */
+    SRC_CONTINUE    = 8,        /* JSOP_GOTO is a continue */
+    SRC_BREAK       = 9,        /* JSOP_GOTO is a break */
+    SRC_BREAK2LABEL = 10,       /* JSOP_GOTO for 'break label' */
+    SRC_SWITCHBREAK = 11,       /* JSOP_GOTO is a break in a switch */
 
-    SRC_TABLESWITCH = 11,       /* JSOP_TABLESWITCH, offset points to end of
+    SRC_TABLESWITCH = 12,       /* JSOP_TABLESWITCH, offset points to end of
                                    switch */
-    SRC_CONDSWITCH  = 12,       /* JSOP_CONDSWITCH, 1st offset points to end of
+    SRC_CONDSWITCH  = 13,       /* JSOP_CONDSWITCH, 1st offset points to end of
                                    switch, 2nd points to first JSOP_CASE */
 
-    SRC_NEXTCASE    = 13,       /* distance forward from one CASE in a
+    SRC_NEXTCASE    = 14,       /* distance forward from one CASE in a
                                    CONDSWITCH to the next */
 
-    SRC_ASSIGNOP    = 14,       /* += or another assign-op follows */
+    SRC_ASSIGNOP    = 15,       /* += or another assign-op follows */
 
-    SRC_HIDDEN      = 15,       /* opcode shouldn't be decompiled */
+    SRC_HIDDEN      = 16,       /* opcode shouldn't be decompiled */
 
-    SRC_CATCH       = 16,       /* catch block has guard */
+    SRC_CATCH       = 17,       /* catch block has guard */
 
-    SRC_TRY         = 17,       /* JSOP_TRY, offset points to goto at the
+    SRC_TRY         = 18,       /* JSOP_TRY, offset points to goto at the
                                    end of the try block. */
 
     /* All notes below here are "gettable".  See SN_IS_GETTABLE below. */
     SRC_LAST_GETTABLE = SRC_TRY,
 
-    SRC_COLSPAN     = 18,       /* number of columns this opcode spans */
-    SRC_NEWLINE     = 19,       /* bytecode follows a source newline */
-    SRC_SETLINE     = 20,       /* a file-absolute source line number note */
+    SRC_COLSPAN     = 19,       /* number of columns this opcode spans */
+    SRC_NEWLINE     = 20,       /* bytecode follows a source newline */
+    SRC_SETLINE     = 21,       /* a file-absolute source line number note */
 
-    SRC_UNUSED21    = 21,
     SRC_UNUSED22    = 22,
     SRC_UNUSED23    = 23,
 
     SRC_XDELTA      = 24        /* 24-31 are for extended delta notes */
 };
 
 /* A source note array is terminated by an all-zero element. */
 inline void
--- a/js/src/jit-test/lib/iteration.js
+++ b/js/src/jit-test/lib/iteration.js
@@ -1,14 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 load(libdir + "asserts.js");
 
+// FIXME: Import from std::iteration.
+const std_iterator = '@@iterator';
+
 if (typeof assertIteratorResult === 'undefined') {
     var assertIteratorResult = function assertIteratorResult(result, value, done) {
         assertEq(typeof result, "object");
+        var expectedProps = ['done', 'value'];
+        var actualProps = Object.getOwnPropertyNames(result);
+        actualProps.sort(), expectedProps.sort();
+        assertDeepEq(actualProps, expectedProps);
         assertDeepEq(result.value, value);
         assertDeepEq(result.done, done);
     }
 }
--- a/js/src/jit-test/tests/basic/bug770952.js
+++ b/js/src/jit-test/tests/basic/bug770952.js
@@ -1,5 +1,7 @@
 // |jit-test| error: TypeError
+load(libdir + "iteration.js");
+
 eval("var x; typeof x")
-Array.prototype.iterator = function () { for(y in x); };
+Array.prototype[std_iterator] = function () { for(y in x); };
 for (var v of ['a', 'b', 'c', 'd'])
     s = v;
--- a/js/src/jit-test/tests/basic/expression-autopsy.js
+++ b/js/src/jit-test/tests/basic/expression-autopsy.js
@@ -1,17 +1,19 @@
+load(libdir + "asserts.js");
+
 function check_one(expected, f, err) {
     var failed = true;
     try {
         f();
         failed = false;
     } catch (ex) {
         var s = ex.toString();
         assertEq(s.slice(0, 11), "TypeError: ");
-        assertEq(s.slice(-err.length), err);
+        assertEq(s.slice(-err.length), err, "" + f);
         assertEq(s.slice(11, -err.length), expected);
     }
     if (!failed)
         throw new Error("didn't fail");
 }
 ieval = eval;
 function check(expr, expected=expr) {
     var end, err;
@@ -92,9 +94,9 @@ check("o[- (o)]");
 
 // A few one off tests
 check_one("6", (function () { 6() }), " is not a function");
 check_one("Array.prototype.reverse.call(...)", (function () { Array.prototype.reverse.call('123'); }), " is read-only");
 check_one("null", function () { var [{ x }] = [null, {}]; }, " has no properties");
 check_one("x", function () { ieval("let (x) { var [a, b, [c0, c1]] = [x, x, x]; }") }, " is undefined");
 
 // Check fallback behavior
-check_one("undefined", (function () { for (let x of undefined) {} }), " has no properties");
+assertThrowsInstanceOf(function () { for (let x of undefined) {} }, TypeError);
--- a/js/src/jit-test/tests/basic/spread-array.js
+++ b/js/src/jit-test/tests/basic/spread-array.js
@@ -1,9 +1,10 @@
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 load(libdir + "eqArrayHelper.js");
 
 assertEqArray([...[1, 2, 3]], [1, 2, 3]);
 assertEqArray([1, ...[2, 3, 4], 5], [1, 2, 3, 4, 5]);
 assertEqArray([1, ...[], 2], [1, 2]);
 assertEqArray([1, ...[2, 3], 4, ...[5, 6]], [1, 2, 3, 4, 5, 6]);
 assertEqArray([1, ...[], 2], [1, 2]);
 assertEqArray([1,, ...[2]], [1,, 2]);
@@ -14,37 +15,34 @@ assertEqArray([,,...[1, 2, 3],,,,], [,,1
 assertEqArray([...[undefined]], [undefined]);
 
 // other iterable objects
 assertEqArray([...Int32Array([1, 2, 3])], [1, 2, 3]);
 assertEqArray([..."abc"], ["a", "b", "c"]);
 assertEqArray([...[1, 2, 3].iterator()], [1, 2, 3]);
 assertEqArray([...Set([1, 2, 3])], [1, 2, 3]);
 assertEqArray([...Map([["a", "A"], ["b", "B"], ["c", "C"]])].map(([k, v]) => k + v), ["aA", "bB", "cC"]);
-let itr = {
-  iterator: function() {
+let itr = {};
+itr[std_iterator] = function () {
     return {
-      i: 1,
-      next: function() {
-        if (this.i < 4)
-          return this.i++;
-        else
-          throw StopIteration;
-      }
+        i: 1,
+        next: function() {
+            if (this.i < 4)
+                return { value: this.i++, done: false };
+            else
+                return { value: undefined, done: true };
+        }
     };
-  }
-};
+}
 assertEqArray([...itr], [1, 2, 3]);
-let gen = {
-  iterator: function() {
+function* gen() {
     for (let i = 1; i < 4; i ++)
-      yield i;
-  }
-};
-assertEqArray([...gen], [1, 2, 3]);
+        yield i;
+}
+assertEqArray([...gen()], [1, 2, 3]);
 
 let a, b = [1, 2, 3];
 assertEqArray([...a=b], [1, 2, 3]);
 
 // According to the draft spec, null and undefined are to be treated as empty
 // arrays. However, they are not iterable. If the spec is not changed to be in
 // terms of iterables, these tests should be fixed.
 //assertEqArray([1, ...null, 2], [1, 2]);
--- a/js/src/jit-test/tests/basic/spread-call-eval.js
+++ b/js/src/jit-test/tests/basic/spread-call-eval.js
@@ -1,9 +1,10 @@
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 assertEq(eval(...[]), undefined);
 assertEq(eval(...["1 + 2"]), 3);
 
 let a = 10, b = 1;
 assertEq(eval(...["a + b"]), 11);
 
 (function() {
@@ -20,37 +21,34 @@ try {             // line0 + 1
   eval(...["("]); // line0 + 2
 } catch (e) {
   assertEq(e.lineNumber, line0 + 2);
 }
 
 // other iterable objects
 assertEq(eval(...["a + b"].iterator()), 11);
 assertEq(eval(...Set(["a + b"])), 11);
-let itr = {
-  iterator: function() {
+let itr = {};
+itr[std_iterator] = function() {
     return {
-      i: 0,
-      next: function() {
-        this.i++;
-        if (this.i == 1)
-          return "a + b";
-        else
-          throw StopIteration;
-      }
+        i: 0,
+        next: function() {
+            this.i++;
+            if (this.i == 1)
+                return { value: "a + b", done: false };
+            else
+                return { value: undefined, done: true };
+        }
     };
-  }
 };
 assertEq(eval(...itr), 11);
-let gen = {
-  iterator: function() {
+function* gen() {
     yield "a + b";
-  }
-};
-assertEq(eval(...gen), 11);
+}
+assertEq(eval(...gen()), 11);
 
 let c = ["C"], d = "D";
 assertEq(eval(...c=["c[0] + d"]), "c[0] + dD");
 
 // According to the draft spec, null and undefined are to be treated as empty
 // arrays. However, they are not iterable. If the spec is not changed to be in
 // terms of iterables, these tests should be fixed.
 //assertEq(eval("a + b", ...null), 11);
--- a/js/src/jit-test/tests/basic/spread-call-funapply.js
+++ b/js/src/jit-test/tests/basic/spread-call-funapply.js
@@ -1,44 +1,42 @@
 load(libdir + "asserts.js");
 load(libdir + "eqArrayHelper.js");
+load(libdir + "iteration.js");
 
 function checkCommon(f) {
   assertEqArray(f.apply(null, ...[[1, 2, 3]]), [1, 2, 3]);
   assertEqArray(f.apply(...[null], [1, 2, 3]), [1, 2, 3]);
   assertEqArray(f.apply(...[null], ...[[1, 2, 3]]), [1, 2, 3]);
   assertEqArray(f.apply(...[null, [1, 2, 3]]), [1, 2, 3]);
 
   // other iterable objects
   assertEqArray(f.apply(...Set([null, [1, 2, 3]])), [1, 2, 3]);
   assertEqArray(f.apply(...[null, [1, 2, 3]].iterator()), [1, 2, 3]);
-  let itr = {
-    iterator: function() {
+  let itr = {};
+  itr[std_iterator] = function() {
       return {
-        i: 0,
-        next: function() {
-          this.i++;
-          if (this.i == 1)
-            return null;
-          else if (this.i == 2)
-            return [1, 2, 3];
-          else
-            throw StopIteration;
-        }
+          i: 0,
+          next: function() {
+              this.i++;
+              if (this.i == 1)
+                  return { value: null, done: false };
+              else if (this.i == 2)
+                  return { value: [1, 2, 3], done: false };
+              else
+                  return { value: undefined, done: true };
+          }
       };
-    }
   };
   assertEqArray(f.apply(...itr), [1, 2, 3]);
-  let gen = {
-    iterator: function() {
+  function* gen() {
       yield null;
       yield [1, 2, 3];
-    }
-  };
-  assertEqArray(f.apply(...gen), [1, 2, 3]);
+  }
+  assertEqArray(f.apply(...gen()), [1, 2, 3]);
 
   let a;
   assertEqArray(f.apply(null, ...a=[[1, 2, 3]]), [1, 2, 3]);
 
   // According to the draft spec, null and undefined are to be treated as empty
   // arrays. However, they are not iterable. If the spec is not changed to be in
   // terms of iterables, these tests should be fixed.
   //assertEqArray(f.apply(null, ...null, [1, 2, 3]), [1, 2, 3]);
--- a/js/src/jit-test/tests/basic/spread-call-length.js
+++ b/js/src/jit-test/tests/basic/spread-call-length.js
@@ -1,8 +1,10 @@
+load(libdir + 'iteration.js');
+
 let makeCall    = farg => Function("f", "arg", "return f(" + farg + ");");
 let makeFunCall = farg => Function("f", "arg", "return f.call(null, " + farg + ");");
 let makeNew     = farg => Function("f", "arg", "return new f(" + farg + ").length;");
 
 function checkLength(f, makeFn) {
   assertEq(makeFn("...[1, 2, 3]")(f), 3);
   assertEq(makeFn("1, ...[2], 3")(f), 3);
   assertEq(makeFn("1, ...[2], ...[3]")(f), 3);
@@ -17,37 +19,34 @@ function checkLength(f, makeFn) {
   assertEq(makeFn("...[undefined]")(f), 1);
 
   // other iterable objects
   assertEq(makeFn("...arg")(f, Int32Array([1, 2, 3])), 3);
   assertEq(makeFn("...arg")(f, "abc"), 3);
   assertEq(makeFn("...arg")(f, [1, 2, 3].iterator()), 3);
   assertEq(makeFn("...arg")(f, Set([1, 2, 3])), 3);
   assertEq(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])), 3);
-  let itr = {
-    iterator: function() {
+  let itr = {};
+  itr[std_iterator] = function() {
       return {
-        i: 1,
-        next: function() {
-          if (this.i < 4)
-            return this.i++;
-          else
-            throw StopIteration;
-        }
+          i: 1,
+          next: function() {
+              if (this.i < 4)
+                  return { value: this.i++, done: false };
+              else
+                  return { value: undefined, done: true };
+          }
       };
-    }
-  };
+  }
   assertEq(makeFn("...arg")(f, itr), 3);
-  let gen = {
-    iterator: function() {
+  function* gen() {
       for (let i = 1; i < 4; i ++)
-        yield i;
-    }
-  };
-  assertEq(makeFn("...arg")(f, gen), 3);
+          yield i;
+  }
+  assertEq(makeFn("...arg")(f, gen()), 3);
 }
 
 checkLength(function(x) arguments.length, makeCall);
 checkLength(function(x) arguments.length, makeFunCall);
 checkLength((x) => arguments.length, makeCall);
 checkLength((x) => arguments.length, makeFunCall);
 function lengthClass(x) {
   this.length = arguments.length;
--- a/js/src/jit-test/tests/basic/spread-call-maxarg.js
+++ b/js/src/jit-test/tests/basic/spread-call-maxarg.js
@@ -1,23 +1,30 @@
-let a = [];
-a.length = getMaxArgs() + 1;
 
-let f = function() {
-};
+var config = getBuildConfiguration();
 
-try {
-  f(...a);
-} catch (e) {
-  assertEq(e.message, "too many function arguments");
-}
+// FIXME: ASAN debug builds run this too slowly for now.  Re-enable
+// after bug 919948 lands.
+if (!(config.debug && config.asan)) {
+    let a = [];
+    a.length = getMaxArgs() + 1;
+
+    let f = function() {
+    };
 
-try {
-  new f(...a);
-} catch (e) {
-  assertEq(e.message, "too many constructor arguments");
-}
+    try {
+        f(...a);
+    } catch (e) {
+        assertEq(e.message, "too many function arguments");
+    }
 
-try {
-  eval(...a);
-} catch (e) {
-  assertEq(e.message, "too many function arguments");
+    try {
+        new f(...a);
+    } catch (e) {
+        assertEq(e.message, "too many constructor arguments");
+    }
+
+    try {
+        eval(...a);
+    } catch (e) {
+        assertEq(e.message, "too many function arguments");
+    }
 }
--- a/js/src/jit-test/tests/basic/spread-call-not-iterable.js
+++ b/js/src/jit-test/tests/basic/spread-call-not-iterable.js
@@ -1,16 +1,28 @@
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 assertThrowsInstanceOf(() => Math.sin(...true), TypeError);
 assertThrowsInstanceOf(() => Math.sin(...false), TypeError);
 assertThrowsInstanceOf(() => Math.sin(...new Date()), TypeError);
 assertThrowsInstanceOf(() => Math.sin(...Function("")), TypeError);
 assertThrowsInstanceOf(() => Math.sin(...function () {}), TypeError);
 assertThrowsInstanceOf(() => Math.sin(...(x => x)), TypeError);
 assertThrowsInstanceOf(() => Math.sin(...1), TypeError);
 assertThrowsInstanceOf(() => Math.sin(...{}), TypeError);
-assertThrowsInstanceOf(() => Math.sin(...{ iterator: 10 }), TypeError);
-assertThrowsInstanceOf(() => Math.sin(...{ iterator: function() undefined }), TypeError);
-assertThrowsInstanceOf(() => Math.sin(...{ iterator: function() this }), TypeError);
-assertThrowsValue(() => Math.sin(...{ iterator: function() this, next: function() { throw 10; } }), 10);
+var foo = {}
+
+foo[std_iterator] = 10;
+assertThrowsInstanceOf(() => Math.sin(...foo), TypeError);
+
+foo[std_iterator] = function() undefined;
+assertThrowsInstanceOf(() => Math.sin(...foo), TypeError);
+
+foo[std_iterator] = function() this;
+assertThrowsInstanceOf(() => Math.sin(...foo), TypeError);
+
+foo[std_iterator] = function() this;
+foo.next = function() { throw 10; };
+assertThrowsValue(() => Math.sin(...foo), 10);
+
 assertThrowsInstanceOf(() => Math.sin(.../a/), TypeError);
 assertThrowsInstanceOf(() => Math.sin(...new Error()), TypeError);
--- a/js/src/jit-test/tests/basic/spread-call.js
+++ b/js/src/jit-test/tests/basic/spread-call.js
@@ -1,10 +1,11 @@
 load(libdir + "asserts.js");
 load(libdir + "eqArrayHelper.js");
+load(libdir + "iteration.js");
 
 let makeCall    = farg => Function("f", "arg", "return f(" + farg + ");");
 let makeFunCall = farg => Function("f", "arg", "return f.call(null, " + farg + ");");
 let makeNew     = farg => Function("f", "arg", "return new f(" + farg + ").value;");
 
 function checkCommon(f, makeFn) {
   assertEqArray(makeFn("...[1, 2, 3]")(f), [1, 2, 3]);
   assertEqArray(makeFn("1, ...[2], 3")(f), [1, 2, 3]);
@@ -13,37 +14,34 @@ function checkCommon(f, makeFn) {
   assertEqArray(makeFn("1, ...[], 2, 3")(f), [1, 2, 3]);
 
   // other iterable objects
   assertEqArray(makeFn("...arg")(f, Int32Array([1, 2, 3])), [1, 2, 3]);
   assertEqArray(makeFn("...arg")(f, "abc"), ["a", "b", "c"]);
   assertEqArray(makeFn("...arg")(f, [1, 2, 3].iterator()), [1, 2, 3]);
   assertEqArray(makeFn("...arg")(f, Set([1, 2, 3])), [1, 2, 3]);
   assertEqArray(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])).map(([k, v]) => k + v), ["aA", "bB", "cC"]);
-  let itr = {
-    iterator: function() {
+  let itr = {};
+  itr[std_iterator] = function() {
       return {
-        i: 1,
-        next: function() {
-          if (this.i < 4)
-            return this.i++;
-          else
-            throw StopIteration;
-        }
+          i: 1,
+          next: function() {
+              if (this.i < 4)
+                  return { value: this.i++, done: false };
+              else
+                  return { value: undefined, done: true };
+          }
       };
-    }
   };
   assertEqArray(makeFn("...arg")(f, itr), [1, 2, 3]);
-  let gen = {
-    iterator: function() {
+  function gen() {
       for (let i = 1; i < 4; i ++)
-        yield i;
-    }
-  };
-  assertEqArray(makeFn("...arg")(f, gen), [1, 2, 3]);
+          yield i;
+  }
+  assertEqArray(makeFn("...arg")(f, gen()), [1, 2, 3]);
 
   assertEqArray(makeFn("...arg=[1, 2, 3]")(f), [1, 2, 3]);
 
   // According to the draft spec, null and undefined are to be treated as empty
   // arrays. However, they are not iterable. If the spec is not changed to be in
   // terms of iterables, these tests should be fixed.
   //assertEqArray(makeFn(1, ...null, 2, 3)(f), [1, 2, 3]);
   //assertEqArray(makeFn(1, ...undefined, 2, 3)(f), [1, 2, 3]);
--- a/js/src/jit-test/tests/collections/Map-clear-iterators-1.js
+++ b/js/src/jit-test/tests/collections/Map-clear-iterators-1.js
@@ -1,22 +1,22 @@
 // A Map iterator does not visit entries removed by clear().
 
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var m = Map();
-var it = m.iterator();
+var it = m[std_iterator]();
 m.clear();
-assertThrowsValue(it.next.bind(it), StopIteration);
+assertIteratorResult(it.next(), undefined, true);
 
 m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
-it = m.iterator();
-assertEq(it.next()[0], "a");
+it = m[std_iterator]();
+assertIteratorResult(it.next(), ["a", 1], false);
 m.clear();
-assertThrowsValue(it.next.bind(it), StopIteration);
+assertIteratorResult(it.next(), undefined, true);
 
 var log = "";
 m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
 for (var [k, v] of m) {
     log += k + v;
     if (k == "b")
         m.clear();
 }
--- a/js/src/jit-test/tests/collections/Map-clear-iterators-2.js
+++ b/js/src/jit-test/tests/collections/Map-clear-iterators-2.js
@@ -1,15 +1,12 @@
 // A Map iterator continues to visit entries added after a clear().
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var m = Map([["a", 1]]);
-var it = m.iterator();
-assertEq(it.next()[0], "a");
+var it = m[std_iterator]();
+assertIteratorResult(it.next(), ["a", 1], false);
 m.clear();
 m.set("b", 2);
-var pair = it.next()
-assertEq(pair[0], "b");
-assertEq(pair[1], 2);
-assertThrowsValue(it.next.bind(it), StopIteration);
-
-
+assertIteratorResult(it.next(), ["b", 2], false);
+assertIteratorResult(it.next(), undefined, true);
--- a/js/src/jit-test/tests/collections/Map-forEach.js
+++ b/js/src/jit-test/tests/collections/Map-forEach.js
@@ -1,32 +1,33 @@
 /* test Map.prototype.forEach */
 
 load(libdir + 'asserts.js');
+load(libdir + 'iteration.js');
 
 // testing success conditions of Map.prototype.forEach
 
 var testMap = new Map();
 
 function callback(value, key, map) {
     testMap.set(key, value);
     assertEq(map.has(key), true);
     assertEq(map.get(key), value);
 }
 
 var initialMap = new Map([['a', 1], ['b', 2.3], [false, undefined]]);
 initialMap.forEach(callback);
 
 // test that both the Maps are equal and are in same order
-var iterator = initialMap.iterator();
+var iterator = initialMap[std_iterator]();
 var count = 0;
 for (var [k, v] of testMap) {
     assertEq(initialMap.has(k), true);
     assertEq(initialMap.get(k), testMap.get(k));
-    assertEq(iterator.next()[1], testMap.get(k));
+    assertIteratorResult(iterator.next(), [k, testMap.get(k)], false);
     count++;
 }
 
 //check both the Maps we have are equal in size
 assertEq(initialMap.size, testMap.size);
 assertEq(initialMap.size, count);
 
 var x = { abc: 'test'};
@@ -47,12 +48,12 @@ var fn = 2;
 assertThrowsInstanceOf(function() {
     initialMap.forEach(fn);
 }, TypeError, "Map.prototype.forEach should raise TypeError if callback is not a function");
 
 // testing that Map#forEach uses internal next() function and does not stop when
 // StopIteration exception is thrown
 
 var m = new Map([["one", 1]]);
-Object.getPrototypeOf(m.iterator()).next = function () { throw "FAIL"; };
+Object.getPrototypeOf(m[std_iterator]()).next = function () { throw "FAIL"; };
 assertThrowsInstanceOf(function () {
   m.forEach(function () { throw StopIteration; });
 }, StopIteration, "Map.prototype.forEach should use intrinsic next method.");
--- a/js/src/jit-test/tests/collections/Map-iterator-add-2.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-add-2.js
@@ -1,11 +1,10 @@
 // A Map iterator does not iterate over new entries added after it throws StopIteration.
 
-load(libdir + "asserts.js");
-load(libdir + "eqArrayHelper.js");
+load(libdir + "iteration.js");
 
 var map = Map();
-var iter0 = map.iterator(), iter1 = map.iterator();
-assertThrowsValue(function () { iter0.next(); }, StopIteration);  // closes iter0
+var iter0 = map[std_iterator](), iter1 = map[std_iterator]();
+assertIteratorResult(iter0.next(), undefined, true);  // closes iter0
 map.set(1, 2);
-assertThrowsValue(function () { iter0.next(); }, StopIteration);  // already closed
-assertEqArray(iter1.next(), [1, 2]);  // was not yet closed
+assertIteratorResult(iter0.next(), undefined, true);  // already closed
+assertIteratorResult(iter1.next(), [1, 2], false);  // was not yet closed
--- a/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-pairs-1.js
@@ -1,13 +1,15 @@
 // mapiter.next() returns an actual array.
 
+load(libdir + "iteration.js");
+
 var key = {};
 var map = Map([[key, 'value']]);
-var entry = map.iterator().next();
+var entry = map[std_iterator]().next().value;
 assertEq(Array.isArray(entry), true);
 assertEq(Object.getPrototypeOf(entry), Array.prototype);
 assertEq(Object.isExtensible(entry), true);
 
 assertEq(entry.length, 2);
 var names = Object.getOwnPropertyNames(entry).sort();
 assertEq(names.length, 3);
 assertEq(names.join(","), "0,1,length");
--- a/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-pairs-2.js
@@ -1,10 +1,13 @@
 // mapiter.next() returns a fresh array each time.
 
+load(libdir + "iteration.js");
+
 var map = Map([['a', 1], ['b', 2]]);
-var iter = map.iterator();
+var iter = map[std_iterator]();
 var a = iter.next(), b = iter.next();
-assertEq(a !== b, true);
-assertEq(a[0], 'a');
-assertEq(b[0], 'b');
-var a1 = map.iterator().next();
-assertEq(a !== a1, true);
+assertIteratorResult(a, ['a', 1], false);
+assertIteratorResult(b, ['b', 2], false);
+assertEq(a.value !== b.value, true);
+var a1 = map[std_iterator]().next();
+assertIteratorResult(a1, ['a', 1], false);
+assertEq(a.value !== a1.value, true);
--- a/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-pairs-3.js
@@ -1,12 +1,13 @@
 // Modifying an array returned by mapiter.next() does not modify the Map.
 
+load(libdir + "iteration.js");
+
 var map = Map([['a', 1]]);
-var pair = map.iterator().next();
-assertEq(pair[0], 'a');
-pair[0] = 'b';
-pair[1] = 2;
-assertEq(pair[0], 'b');
-assertEq(pair[1], 2);
+var res = map[std_iterator]().next();
+assertIteratorResult(res, ['a', 1], false);
+res.value[0] = 'b';
+res.value[1] = 2;
+assertIteratorResult(res, ['b', 2], false);
 assertEq(map.get('a'), 1);
 assertEq(map.has('b'), false);
 assertEq(map.size, 1);
--- a/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-proxies-2.js
@@ -1,19 +1,21 @@
 // map.iterator() and iter.next() are non-generic but work on cross-compartment wrappers.
 
 load(libdir + "asserts.js");
 load(libdir + "eqArrayHelper.js");
+load(libdir + "iteration.js");
+
 var g = newGlobal();
 
-var iterator_fn = Map.prototype.iterator;
+var iterator_fn = Map.prototype[std_iterator];
 assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError);
 assertThrowsInstanceOf(function () { iterator_fn.call(Set()); }, TypeError);
 var mapw = g.eval("Map([['x', 1], ['y', 2]])");
-assertEqArray(iterator_fn.call(mapw).next(), ["x", 1]);
+assertEqArray(iterator_fn.call(mapw).next().value, ["x", 1]);
 
-var next_fn = Map().iterator().next;
+var next_fn = Map()[std_iterator]().next;
 assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError);
-assertThrowsInstanceOf(function () { next_fn.call(Set().iterator()); }, TypeError);
-var iterw = mapw.iterator();
-assertEqArray(next_fn.call(iterw), ["x", 1]);
-assertEqArray(next_fn.call(iterw), ["y", 2]);
-assertThrowsValue(function () { next_fn.call(iterw); }, g.StopIteration);
+assertThrowsInstanceOf(function () { next_fn.call(Set()[std_iterator]()); }, TypeError);
+var iterw = mapw[std_iterator]();
+assertEqArray(next_fn.call(iterw).value, ["x", 1]);
+assertEqArray(next_fn.call(iterw).value, ["y", 2]);
+assertEq(next_fn.call(iterw).done, true);
--- a/js/src/jit-test/tests/collections/Map-iterator-remove-2.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-remove-2.js
@@ -1,11 +1,13 @@
 // A map iterator can cope with removing the next entry.
 
+load(libdir + "iteration.js");
+
 var map = Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
-var iter = map.iterator();
+var iter = map[std_iterator]();
 var log = '';
 for (let [k, v] of iter) {
     log += k + v;
     if (k === 'b')
         map.delete('c');
 }
 assertEq(log, 'a0b1d3');
--- a/js/src/jit-test/tests/collections/Map-iterator-remove-3.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-remove-3.js
@@ -1,12 +1,13 @@
 // A map iterator can cope with removing the next entry, then the current entry.
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var map = Map([['a', 0], ['b', 1], ['c', 2], ['d', 3]]);
-var iter = map.iterator();
-assertEq(iter.next()[0], 'a');
-assertEq(iter.next()[0], 'b');
+var iter = map[std_iterator]();
+assertIteratorResult(iter.next(), ['a', 0], false);
+assertIteratorResult(iter.next(), ['b', 1], false);
 map.delete('c');
 map.delete('b');
-assertEq(iter.next()[0], 'd');
-assertThrowsValue(function () { iter.next(); }, StopIteration);
+assertIteratorResult(iter.next(), ['d', 3], false);
+assertIteratorResult(iter.next(), undefined, true);
--- a/js/src/jit-test/tests/collections/Map-iterator-remove-4.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-remove-4.js
@@ -1,32 +1,31 @@
 // Multiple live iterators on the same Map can cope with removing entries.
 
-load(libdir + "eqArrayHelper.js");
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 // Make a map.
 var map = Map();
 var SIZE = 7;
 for (var j = 0; j < SIZE; j++)
     map.set(j, j);
 
 // Make lots of iterators pointing to entry 2 of the map.
 var NITERS = 5;
 var iters = [];
 for (var i = 0; i < NITERS; i++) {
-    var iter = map.iterator();
-    assertEqArray(iter.next(), [0, 0]);
-    assertEqArray(iter.next(), [1, 1]);
+    var iter = map[std_iterator]();
+    assertIteratorResult(iter.next(), [0, 0], false);
+    assertIteratorResult(iter.next(), [1, 1], false);
     iters[i] = iter;
 }
 
 // Remove half of the map entries.
 for (var j = 0; j < SIZE; j += 2)
     map.delete(j);
 
 // Make sure all the iterators still work.
 for (var i = 0; i < NITERS; i++) {
     var iter = iters[i];
     for (var j = 3; j < SIZE; j += 2)
-        assertEqArray(iter.next(), [j, j]);
-    assertThrowsValue(function () { iter.next(); }, StopIteration);
+        assertIteratorResult(iter.next(), [j, j], false);
+    assertIteratorResult(iter.next(), undefined, true);
 }
--- a/js/src/jit-test/tests/collections/Map-iterator-remove-6.js
+++ b/js/src/jit-test/tests/collections/Map-iterator-remove-6.js
@@ -1,20 +1,21 @@
 // Removing many Map entries does not cause a live iterator to skip any of the
 // entries that were not removed. (Compacting a Map must not be observable to
 // script.)
 
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var map = Map();
 for (var i = 0; i < 32; i++)
     map.set(i, i);
-var iter = map.iterator();
-assertEq(iter.next()[0], 0);
+var iter = map[std_iterator]();
+assertIteratorResult(iter.next(), [0, 0], false);
 for (var i = 0; i < 30; i++)
     map.delete(i);
 assertEq(map.size, 2);
 for (var i = 32; i < 100; i++)
     map.set(i, i);  // eventually triggers compaction
 
 for (var i = 30; i < 100; i++)
-    assertEq(iter.next()[0], i);
-assertThrowsValue(function () { iter.next(); }, StopIteration);
+    assertIteratorResult(iter.next(), [i, i], false);
+
+assertIteratorResult(iter.next(), undefined, true);
--- a/js/src/jit-test/tests/collections/Map-iterators-3.js
+++ b/js/src/jit-test/tests/collections/Map-iterators-3.js
@@ -1,10 +1,10 @@
 // A closed Map iterator does not visit new entries added after a clear().
 
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var m = Map();
-var it = m.iterator();
-assertThrowsValue(it.next.bind(it), StopIteration);  // close the iterator
+var it = m[std_iterator]();
+assertIteratorResult(it.next(), undefined, true);  // close the iterator
 m.clear();
 m.set("a", 1);
-assertThrowsValue(it.next.bind(it), StopIteration);
+assertIteratorResult(it.next(), undefined, true);  // iterator still closed
--- a/js/src/jit-test/tests/collections/Map-surfaces-1.js
+++ b/js/src/jit-test/tests/collections/Map-surfaces-1.js
@@ -1,10 +1,12 @@
 // Map surfaces
 
+load(libdir + "iteration.js");
+
 var desc = Object.getOwnPropertyDescriptor(this, "Map");
 assertEq(desc.enumerable, false);
 assertEq(desc.configurable, true);
 assertEq(desc.writable, true);
 
 assertEq(typeof Map, 'function');
 assertEq(Object.keys(Map).length, 0);
 assertEq(Map.length, 1);
@@ -38,10 +40,10 @@ checkMethod("entries", 0);
 var desc = Object.getOwnPropertyDescriptor(Map.prototype, "size");
 assertEq(desc.enumerable, false);
 assertEq(desc.configurable, true);
 assertEq(typeof desc.get, 'function');
 assertEq(desc.get.length, 0);
 assertEq(desc.set, undefined);
 checkMethod("clear", 0);
 
-// Map.prototype.iterator and .entries are the same function object.
-assertEq(Map.prototype.iterator, Map.prototype.entries);
+// Map.prototype[@@iterator] and .entries are the same function object.
+assertEq(Map.prototype[std_iterator], Map.prototype.entries);
--- a/js/src/jit-test/tests/collections/Map-values-2.js
+++ b/js/src/jit-test/tests/collections/Map-values-2.js
@@ -1,18 +1,18 @@
 // map.keys() and map.values() return iterators over the key or the value,
 // respectively, of each key-value pair in the map.
 
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var data = [["one", 1], ["two", 2], ["three", 3], ["four", 4]];
 var m = Map(data);
 
 var ki = m.keys();
-assertEq(ki.next(), "one");
-assertEq(ki.next(), "two");
-assertEq(ki.next(), "three");
-assertEq(ki.next(), "four");
-assertThrowsValue(function () { ki.next(); }, StopIteration);
+assertIteratorResult(ki.next(), "one", false);
+assertIteratorResult(ki.next(), "two", false);
+assertIteratorResult(ki.next(), "three", false);
+assertIteratorResult(ki.next(), "four", false);
+assertIteratorResult(ki.next(), undefined, true);
 
 assertEq([k for (k of m.keys())].toSource(), ["one", "two", "three", "four"].toSource());
 assertEq([k for (k of m.values())].toSource(), [1, 2, 3, 4].toSource());
 assertEq([k for (k of m.entries())].toSource(), data.toSource());
--- a/js/src/jit-test/tests/collections/Set-clear-iterators-1.js
+++ b/js/src/jit-test/tests/collections/Set-clear-iterators-1.js
@@ -1,22 +1,22 @@
 // A Set iterator does not visit entries removed by clear().
 
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var s = Set();
-var it = s.iterator();
+var it = s[std_iterator]();
 s.clear();
-assertThrowsValue(it.next.bind(it), StopIteration);
+assertIteratorResult(it.next(), undefined, true);
 
 s = Set(["a", "b", "c", "d"]);
-it = s.iterator();
-assertEq(it.next()[0], "a");
+it = s[std_iterator]();
+assertIteratorResult(it.next(), "a", false);
 s.clear();
-assertThrowsValue(it.next.bind(it), StopIteration);
+assertIteratorResult(it.next(), undefined, true);
 
 var log = "";
 s = Set(["a", "b", "c", "d"]);
 for (var v of s) {
     log += v;
     if (v == "b")
         s.clear();
 }
--- a/js/src/jit-test/tests/collections/Set-clear-iterators-2.js
+++ b/js/src/jit-test/tests/collections/Set-clear-iterators-2.js
@@ -1,11 +1,11 @@
 // A Set iterator continues to visit entries added after a clear().
 
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var s = Set(["a"]);
-var it = s.iterator();
-assertEq(it.next(), "a");
+var it = s[std_iterator]();
+assertIteratorResult(it.next(), "a", false);
 s.clear();
 s.add("b");
-assertEq(it.next(), "b");
-assertThrowsValue(it.next.bind(it), StopIteration);
+assertIteratorResult(it.next(), "b", false);
+assertIteratorResult(it.next(), undefined, true);
--- a/js/src/jit-test/tests/collections/Set-clear-iterators-3.js
+++ b/js/src/jit-test/tests/collections/Set-clear-iterators-3.js
@@ -1,10 +1,10 @@
 // A closed Set iterator does not visit new entries added after a clear().
 
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var s = Set();
-var it = s.iterator();
-assertThrowsValue(it.next.bind(it), StopIteration);  // close the iterator
+var it = s[std_iterator]();
+assertIteratorResult(it.next(), undefined, true);  // close the iterator
 s.clear();
 s.add("a");
-assertThrowsValue(it.next.bind(it), StopIteration);
+assertIteratorResult(it.next(), undefined, true);
--- a/js/src/jit-test/tests/collections/Set-forEach.js
+++ b/js/src/jit-test/tests/collections/Set-forEach.js
@@ -1,31 +1,32 @@
 /* test Set.prototype.forEach */
 
 load(libdir + 'asserts.js');
+load(libdir + 'iteration.js');
 
 // testing success conditions of Set.prototype.forEach
 
 var testSet = new Set();
 
 function callback(value, key, set) {
     assertEq(value, key);
     testSet.add(value);
     assertEq(set.has(key), true);
 }
 
 var initialSet = new Set(['a', 1, undefined]);
 initialSet.forEach(callback);
 
 // test that both the Sets are equal and are in same order
-var iterator = initialSet.iterator();
+var iterator = initialSet[std_iterator]();
 var count = 0;
 for (var v of testSet) {
     assertEq(initialSet.has(v), true);
-    assertEq(iterator.next(), v);
+    assertIteratorResult(iterator.next(), v, false);
     count++;
 }
 
 //check both the Sets we have are equal in size
 assertEq(initialSet.size, testSet.size);
 assertEq(initialSet.size, count);
 
 var x = { abc: 'test'};
@@ -41,17 +42,8 @@ var m = new Map([['a', 1], ['b', 2.3], [
 assertThrowsInstanceOf(function() {
     Set.prototype.forEach.call(m, callback);
 }, TypeError, "Set.prototype.forEach should raise TypeError if not a used on a Set");
 
 var fn = 2;
 assertThrowsInstanceOf(function() {
     initialSet.forEach(fn);
 }, TypeError, "Set.prototype.forEach should raise TypeError if callback is not a function");
-
-// testing that Set#forEach uses internal next() function and does not stop when
-// StopIteration exception is thrown
-
-var s = new Set(["one", 1]);
-Object.getPrototypeOf(s.iterator()).next = function () { throw "FAIL"; };
-assertThrowsInstanceOf(function () {
-  s.forEach(function () { throw StopIteration; });
-}, StopIteration, "Set.prototype.forEach should use intrinsic next method.");
--- a/js/src/jit-test/tests/collections/Set-iterator-add-2.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-add-2.js
@@ -1,10 +1,10 @@
 // A Set iterator does not iterate over new entries added after it throws StopIteration.
 
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var set = Set();
-var iter0 = set.iterator(), iter1 = set.iterator();
-assertThrowsValue(function () { iter0.next(); }, StopIteration);  // closes iter0
+var iter0 = set[std_iterator](), iter1 = set[std_iterator]();
+assertIteratorResult(iter0.next(), undefined, true);  // closes iter0
 set.add("x");
-assertThrowsValue(function () { iter0.next(); }, StopIteration);  // already closed
-assertEq(iter1.next(), "x");  // was not yet closed
+assertIteratorResult(iter0.next(), undefined, true);  // already closed
+assertIteratorResult(iter1.next(), "x", false);  // was not yet closed
--- a/js/src/jit-test/tests/collections/Set-iterator-gc-1.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-gc-1.js
@@ -1,8 +1,10 @@
 // A Set iterator keeps the data alive.
 
 load(libdir + "referencesVia.js");
+load(libdir + "iteration.js");
+
 var key = {};
 var set = Set([key]);
-var iter = set.iterator();
+var iter = set[std_iterator]();
 referencesVia(iter, "**UNKNOWN SLOT 0**", set);
 referencesVia(set, "key", key);
--- a/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-proxies-2.js
@@ -1,18 +1,20 @@
 // map.iterator() and iter.next() are non-generic but work on cross-compartment wrappers.
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
+
 var g = newGlobal();
 
-var iterator_fn = Set.prototype.iterator;
+var iterator_fn = Set.prototype[std_iterator];
 assertThrowsInstanceOf(function () { iterator_fn.call({}); }, TypeError);
 assertThrowsInstanceOf(function () { iterator_fn.call(Map()); }, TypeError);
 var setw = g.eval("Set(['x', 'y'])");
-assertEq(iterator_fn.call(setw).next(), "x");
+assertIteratorResult(iterator_fn.call(setw).next(), "x", false);
 
-var next_fn = Set().iterator().next;
+var next_fn = Set()[std_iterator]().next;
 assertThrowsInstanceOf(function () { next_fn.call({}); }, TypeError);
-assertThrowsInstanceOf(function () { next_fn.call(Map().iterator()); }, TypeError);
-var iterw = setw.iterator();
-assertEq(next_fn.call(iterw), "x");
-assertEq(next_fn.call(iterw), "y");
-assertThrowsValue(function () { next_fn.call(iterw); }, g.StopIteration);
+assertThrowsInstanceOf(function () { next_fn.call(Map()[std_iterator]()); }, TypeError);
+var iterw = setw[std_iterator]();
+assertIteratorResult(next_fn.call(iterw), "x", false);
+assertIteratorResult(next_fn.call(iterw), "y", false);
+assertIteratorResult(next_fn.call(iterw), undefined, true);
--- a/js/src/jit-test/tests/collections/Set-iterator-remove-2.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-remove-2.js
@@ -1,11 +1,13 @@
 // A map iterator can cope with removing the next entry.
 
+load(libdir + "iteration.js");
+
 var set = Set("abcd");
-var iter = set.iterator();
+var iter = set[std_iterator]();
 var log = "";
 for (let x of iter) {
     log += x;
     if (x === "b")
         set.delete("c");
 }
 assertEq(log, "abd");
--- a/js/src/jit-test/tests/collections/Set-iterator-remove-3.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-remove-3.js
@@ -1,12 +1,12 @@
 // A set iterator can cope with removing the next entry, then the current entry.
 
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var set = Set("abcd");
-var iter = set.iterator();
-assertEq(iter.next(), "a");
-assertEq(iter.next(), "b");
+var iter = set[std_iterator]();
+assertIteratorResult(iter.next(), "a", false);
+assertIteratorResult(iter.next(), "b", false);
 set.delete("c");
 set.delete("b");
-assertEq(iter.next(), "d");
-assertThrowsValue(function () { iter.next(); }, StopIteration);
+assertIteratorResult(iter.next(), "d", false);
+assertIteratorResult(iter.next(), undefined, true);
--- a/js/src/jit-test/tests/collections/Set-iterator-remove-4.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-remove-4.js
@@ -1,31 +1,31 @@
 // Multiple live iterators on the same Set can cope with removing entries.
 
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 // Make a set.
 var set = Set();
 var SIZE = 7;
 for (var j = 0; j < SIZE; j++)
     set.add(j);
 
 // Make lots of iterators pointing to entry 2 of the set.
 var NITERS = 5;
 var iters = [];
 for (var i = 0; i < NITERS; i++) {
-    var iter = set.iterator();
-    assertEq(iter.next(), 0);
-    assertEq(iter.next(), 1);
+    var iter = set[std_iterator]();
+    assertIteratorResult(iter.next(), 0, false);
+    assertIteratorResult(iter.next(), 1, false);
     iters[i] = iter;
 }
 
 // Remove half of the set entries.
 for (var j = 0; j < SIZE; j += 2)
     set.delete(j);
 
 // Make sure all the iterators still work.
 for (var i = 0; i < NITERS; i++) {
     var iter = iters[i];
     for (var j = 3; j < SIZE; j += 2)
-        assertEq(iter.next(), j);
-    assertThrowsValue(function () { iter.next(); }, StopIteration);
+        assertIteratorResult(iter.next(), j, false);
+    assertIteratorResult(iter.next(), undefined, true);
 }
--- a/js/src/jit-test/tests/collections/Set-iterator-remove-6.js
+++ b/js/src/jit-test/tests/collections/Set-iterator-remove-6.js
@@ -1,20 +1,20 @@
 // Removing many Set entries does not cause a live iterator to skip any of the
 // entries that were not removed. (Compacting a Set must not be observable to
 // script.)
 
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var set = Set();
 for (var i = 0; i < 32; i++)
     set.add(i);
-var iter = set.iterator();
-assertEq(iter.next(), 0);
+var iter = set[std_iterator]();
+assertIteratorResult(iter.next(), 0, false);
 for (var i = 0; i < 30; i++)
     set.delete(i);
 assertEq(set.size, 2);
 for (var i = 32; i < 100; i++)
     set.add(i);  // eventually triggers compaction
 
 for (var i = 30; i < 100; i++)
-    assertEq(iter.next(), i);
-assertThrowsValue(function () { iter.next(); }, StopIteration);
+    assertIteratorResult(iter.next(), i, false);
+assertIteratorResult(iter.next(), undefined, true);
--- a/js/src/jit-test/tests/collections/Set-surfaces-1.js
+++ b/js/src/jit-test/tests/collections/Set-surfaces-1.js
@@ -1,10 +1,12 @@
 // Set surfaces
 
+load(libdir + "iteration.js");
+
 var desc = Object.getOwnPropertyDescriptor(this, "Set");
 assertEq(desc.enumerable, false);
 assertEq(desc.configurable, true);
 assertEq(desc.writable, true);
 
 assertEq(typeof Set, 'function');
 assertEq(Object.keys(Set).length, 0);
 assertEq(Set.length, 1);
@@ -38,9 +40,9 @@ assertEq(desc.enumerable, false);
 assertEq(desc.configurable, true);
 assertEq(typeof desc.get, 'function');
 assertEq(desc.get.length, 0);
 assertEq(desc.set, undefined);
 checkMethod("clear", 0);
 
 // Set.prototype.keys, .values, and .iterator are the same function object
 assertEq(Set.prototype.keys, Set.prototype.values);
-assertEq(Set.prototype.iterator, Set.prototype.values);
+assertEq(Set.prototype[std_iterator], Set.prototype.values);
--- a/js/src/jit-test/tests/collections/Set-values-2.js
+++ b/js/src/jit-test/tests/collections/Set-values-2.js
@@ -1,18 +1,18 @@
 // set.keys() and set.values() return iterators over the elements
 // and set.entries() returns an iterator that yields pairs [e, e].
 
-load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var data = [1, 2, 3, 4];
 var s = Set(data);
 
 var ki = s.keys();
-assertEq(ki.next(), 1);
-assertEq(ki.next(), 2);
-assertEq(ki.next(), 3);
-assertEq(ki.next(), 4);
-assertThrowsValue(function () { ki.next(); }, StopIteration);
+assertIteratorResult(ki.next(), 1, false);
+assertIteratorResult(ki.next(), 2, false);
+assertIteratorResult(ki.next(), 3, false);
+assertIteratorResult(ki.next(), 4, false);
+assertIteratorResult(ki.next(), undefined, true);
 
 assertEq([...s.keys()].toSource(), data.toSource());
 assertEq([...s.values()].toSource(), data.toSource());
 assertEq([...s.entries()].toSource(), [[1, 1], [2, 2], [3, 3], [4, 4]].toSource());
--- a/js/src/jit-test/tests/collections/iterator-1.js
+++ b/js/src/jit-test/tests/collections/iterator-1.js
@@ -1,12 +1,16 @@
 // collection.iterator() returns an Iterator object.
 
+load(libdir + "iteration.js");
+
 function test(obj, name) {
-    var iter = obj.iterator();
+    var iter = obj[std_iterator]();
     assertEq(typeof iter, "object");
     assertEq(iter instanceof Iterator, true);
     assertEq(iter.toString(), "[object " + obj.constructor.name + " Iterator]");
 }
 
-test([]);
+// FIXME: Until arrays are converted to use the new iteration protocol,
+// toString on this iterator doesn't work.  Bug 919948.
+// test([]);
 test(new Map);
 test(new Set);
--- a/js/src/jit-test/tests/collections/iterator-gc.js
+++ b/js/src/jit-test/tests/collections/iterator-gc.js
@@ -1,14 +1,16 @@
 // An iterator keeps its data alive.
 
+load(libdir + "iteration.js");
+
 load(libdir + "referencesVia.js");
 var key = {};
 
 function test(obj, edgeName) {
-    var iter = obj.iterator();
+    var iter = obj[std_iterator]();
     referencesVia(iter, "**UNKNOWN SLOT 0**", obj);
     referencesVia(obj, edgeName, key);
 }
 
 test([key],                 "element[0]");
 test(Map([[key, 'value']]), "key");
 test(Set([key]),            "key");
--- a/js/src/jit-test/tests/collections/iterator-proto-1.js
+++ b/js/src/jit-test/tests/collections/iterator-proto-1.js
@@ -1,13 +1,15 @@
 // All iterators of the same collection type share their immediate prototype.
 // Those prototype objects in turn inherit directly from Iterator.prototype.
 
+load(libdir + "iteration.js");
+
 function test(obj0, obj1) {
-    var iter0 = obj0.iterator(), iter1 = obj1.iterator();
+    var iter0 = obj0[std_iterator](), iter1 = obj1[std_iterator]();
     var proto = Object.getPrototypeOf(iter0);
     assertEq(Object.getPrototypeOf(iter1), proto);
     assertEq(Object.getPrototypeOf(proto), Iterator.prototype);
 }
 
 test([], [1]);
 test(Map(), Map([[1, 1]]));
 test(Set(), Set([1]));
--- a/js/src/jit-test/tests/collections/iterator-proto-2.js
+++ b/js/src/jit-test/tests/collections/iterator-proto-2.js
@@ -1,11 +1,13 @@
 // Iterators of different collection types have different prototypes.
 
-var aproto = Object.getPrototypeOf(Array().iterator());
-var mproto = Object.getPrototypeOf(Map().iterator());
-var sproto = Object.getPrototypeOf(Set().iterator());
+load(libdir + "iteration.js");
+
+var aproto = Object.getPrototypeOf(Array()[std_iterator]());
+var mproto = Object.getPrototypeOf(Map()[std_iterator]());
+var sproto = Object.getPrototypeOf(Set()[std_iterator]());
 assertEq(aproto !== mproto, true);
 assertEq(aproto !== sproto, true);
 assertEq(mproto !== sproto, true);
 assertEq(aproto.next !== mproto.next, true);
 assertEq(aproto.next !== sproto.next, true);
 assertEq(mproto.next !== sproto.next, true);
--- a/js/src/jit-test/tests/collections/iterator-proto-surfaces.js
+++ b/js/src/jit-test/tests/collections/iterator-proto-surfaces.js
@@ -1,22 +1,23 @@
 // Iterator prototype surfaces.
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 function test(constructor) {
-    var proto = Object.getPrototypeOf(constructor().iterator());
+    var proto = Object.getPrototypeOf(constructor()[std_iterator]());
     var names = Object.getOwnPropertyNames(proto);
-    assertEq(names.length, 1);
-    assertEq(names[0], 'next');
+    names.sort();
+    assertDeepEq(names, [std_iterator, 'next']);
 
     var desc = Object.getOwnPropertyDescriptor(proto, 'next');
     assertEq(desc.configurable, true);
     assertEq(desc.enumerable, false);
     assertEq(desc.writable, true);
 
-    assertEq(proto.iterator(), proto);
-    assertThrowsValue(function () { proto.next(); }, StopIteration);
+    assertEq(proto[std_iterator](), proto);
+    assertIteratorResult(proto.next(), undefined, true);
 }
 
 //test(Array);
 test(Map);
 test(Set);
--- a/js/src/jit-test/tests/for-of/arguments-1.js
+++ b/js/src/jit-test/tests/for-of/arguments-1.js
@@ -1,13 +1,15 @@
 // for-of can iterate arguments objects.
 
-// Arguments objects do not have a .iterator() method by default.
+load(libdir + "iteration.js");
+
+// Arguments objects do not have a .@@iterator() method by default.
 // Install one on Object.prototype.
-Object.prototype.iterator = Array.prototype.iterator;
+Object.prototype[std_iterator] = Array.prototype[std_iterator];
 
 var s;
 function test() {
     for (var v of arguments)
         s += v;
 }
 
 s = '';
--- a/js/src/jit-test/tests/for-of/arguments-2.js
+++ b/js/src/jit-test/tests/for-of/arguments-2.js
@@ -1,12 +1,14 @@
 // for-of can iterate arguments objects after returning.
 
+load(libdir + "iteration.js");
+
 function f() {
     return arguments;
 }
 
 var s = '';
 var args = f('a', 'b', 'c');
-Object.prototype.iterator = Array.prototype.iterator;
+Object.prototype[std_iterator] = Array.prototype[std_iterator];
 for (var v of args)
     s += v;
 assertEq(s, 'abc');
--- a/js/src/jit-test/tests/for-of/arguments-3.js
+++ b/js/src/jit-test/tests/for-of/arguments-3.js
@@ -1,11 +1,13 @@
 // for-of can iterate strict arguments objects.
 
-Object.prototype.iterator = Array.prototype.iterator;
+load(libdir + "iteration.js");
+
+Object.prototype[std_iterator] = Array.prototype[std_iterator];
 
 var s;
 function test() {
     "use strict";
     for (var v of arguments)
         s += v;
 }
 
--- a/js/src/jit-test/tests/for-of/arguments-4.js
+++ b/js/src/jit-test/tests/for-of/arguments-4.js
@@ -1,11 +1,13 @@
 // for-of can iterate arguments objects for other active frames.
 
-Object.prototype.iterator = Array.prototype.iterator;
+load(libdir + "iteration.js");
+
+Object.prototype[std_iterator] = Array.prototype[std_iterator];
 
 var s;
 function g(obj) {
     for (var v of obj)
         s += v;
 }
 
 function f() {
--- a/js/src/jit-test/tests/for-of/arguments-5.js
+++ b/js/src/jit-test/tests/for-of/arguments-5.js
@@ -1,11 +1,13 @@
 // for-of can iterate strict arguments objects in non-strict code.
 
-Object.prototype.iterator = Array.prototype.iterator;
+load(libdir + "iteration.js");
+
+Object.prototype[std_iterator] = Array.prototype[std_iterator];
 
 var s;
 function g(obj) {
     for (var v of obj)
         s += v;
 }
 
 function f() {
--- a/js/src/jit-test/tests/for-of/arguments-6.js
+++ b/js/src/jit-test/tests/for-of/arguments-6.js
@@ -1,11 +1,13 @@
 // Changing arguments.length affects a for-of loop iterating over arguments.
 
-Object.prototype.iterator = Array.prototype.iterator;
+load(libdir + "iteration.js");
+
+Object.prototype[std_iterator] = Array.prototype[std_iterator];
 
 var s;
 function f() {
     arguments.length = 2;
     for (var v of arguments)
         s += v;
 }
 
--- a/js/src/jit-test/tests/for-of/arguments-7.js
+++ b/js/src/jit-test/tests/for-of/arguments-7.js
@@ -1,11 +1,13 @@
 // Changing arguments.length during a for-of loop iterating over arguments affects the loop.
 
-Object.prototype.iterator = Array.prototype.iterator;
+load(libdir + "iteration.js");
+
+Object.prototype[std_iterator] = Array.prototype[std_iterator];
 
 var s;
 function f() {
     for (var v of arguments) {
         s += v;
         arguments.length--;
     }
 }
--- a/js/src/jit-test/tests/for-of/array-holes-4.js
+++ b/js/src/jit-test/tests/for-of/array-holes-4.js
@@ -1,11 +1,13 @@
 // for-of on an Array consults the prototype chain when it encounters a hole.
 
+load(libdir + "iteration.js");
+
 var m = {1: 'peek'};
 var a = [0, , 2, 3];
 a.__proto__ = m;
 var log = [];
-Object.prototype.iterator = Array.prototype.iterator;
+Object.prototype[std_iterator] = Array.prototype[std_iterator];
 for (var x of a)
     log.push(x);
 assertEq(log[1], 'peek');
 assertEq(log.join(), "0,peek,2,3");
--- a/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js
+++ b/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js
@@ -1,22 +1,24 @@
 // Superficial tests for iterators created by Array.prototype.iterator
 
-var proto = Object.getPrototypeOf([].iterator());
+load(libdir + "iteration.js");
+
+var proto = Object.getPrototypeOf([][std_iterator]());
 assertEq(Object.getPrototypeOf(proto), Iterator.prototype);
 
 function check(it) {
     assertEq(typeof it, 'object');
     assertEq(Object.getPrototypeOf(it), proto);
     assertEq(Object.getOwnPropertyNames(it).length, 0);
-    assertEq(it.iterator(), it);
+    assertEq(it[std_iterator](), it);
 
     // for-in enumerates the iterator's properties.
     it.x = 0;
     var s = '';
     for (var p in it)
         s += p + '.';
     assertEq(s, 'x.');
 }
 
-check([].iterator());
-check(Array.prototype.iterator.call({}));
-check(Array.prototype.iterator.call(undefined));
+check([][std_iterator]());
+check(Array.prototype[std_iterator].call({}));
+check(Array.prototype[std_iterator].call(undefined));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/completion.js
@@ -0,0 +1,6 @@
+// The completion value of a for-of loop is the completion value of the
+// last evaluation of the body, or undefined.
+
+assertEq(eval("for (let x of [1, 2, 3]) { x }"), 3);
+assertEq(eval("for (let x of [1, 2, 3]) { x * 2 }"), 6);
+assertEq(eval("for (let x of []) { x }"), undefined);
--- a/js/src/jit-test/tests/for-of/generators-5.js
+++ b/js/src/jit-test/tests/for-of/generators-5.js
@@ -1,18 +1,20 @@
-// Breaking out of a for-of loop over a generator-iterator closes the iterator.
+// Breaking out of a for-of loop over a generator-iterator does not close the iterator.
+
+load(libdir + "iteration.js");
 
 function range(n) {
     for (var i = 0; i < n; i++)
         yield i;
 }
 
 var r = range(10);
 var s = '';
 for (var x of r) {
     s += x;
     if (x == 4)
         break;
 }
 s += '/';
 for (var y of r)
     s += y;
-assertEq(s, '01234/');
+assertEq(s, '01234/56789');
deleted file mode 100644
--- a/js/src/jit-test/tests/for-of/generators-6.js
+++ /dev/null
@@ -1,43 +0,0 @@
-// Named break closes the right generator-iterators.
-
-function g() {
-    for (;;)
-        yield 1;
-}
-
-function isClosed(it) {
-    try {
-        it.next();
-        return false;
-    } catch (exc) {
-        if (exc !== StopIteration)
-            throw exc;
-        return true;
-    }
-}
-
-var a = g(), b = g(), c = g(), d = g(), e = g(), f = g();
-
-for (var aa of a) {
-    b_: for (var bb of b) {
-        c_: for (var cc of c) {
-            d_: for (var dd of d) {
-                e_: for (var ee of e) {
-                    for (var ff of f)
-                        break c_;
-                }
-            }
-        }
-        assertEq(isClosed(a), false);
-        assertEq(isClosed(b), false);
-        assertEq(isClosed(c), true);
-        assertEq(isClosed(d), true);
-        assertEq(isClosed(e), true);
-        assertEq(isClosed(f), true);
-        break b_;
-    }
-    assertEq(isClosed(a), false);
-    assertEq(isClosed(b), true);
-    break;
-}
-assertEq(isClosed(a), true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/manual-advance.js
@@ -0,0 +1,15 @@
+// Manually advancing the iterator.
+
+load(libdir + 'iteration.js');
+
+function* g(n) { for (var i=0; i<n; i++) yield i; }
+
+var inner = g(20);
+
+var n = 0;
+for (var x of inner) {
+    assertEq(x, n * 2);
+    assertIteratorResult(inner.next(), n * 2 + 1, false);
+    n++;
+}
+assertEq(n, 10);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/next-arity.js
@@ -0,0 +1,22 @@
+// For-of passes one arg to "next".
+
+load(libdir + 'iteration.js')
+
+var log = '';
+
+function Iter() {
+    function next() {
+        log += 'n';
+        assertEq(arguments.length, 1)
+        assertEq(arguments[0], undefined)
+        return { get value() { throw 42; }, done: true }
+    }
+
+    this[std_iterator] = function () { return this; }
+    this.next = next;
+}
+
+for (var x of new Iter())
+    throw 'not reached';
+
+assertEq(log, 'n');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/next-shenanigans.js
@@ -0,0 +1,41 @@
+// Test for-of with iter.next and monkeypatching.
+
+function* g(n) { for (var i=0; i<n; i++) yield i; }
+var GeneratorObjectPrototype = Object.getPrototypeOf(g).prototype;
+var GeneratorObjectPrototype_next = GeneratorObjectPrototype.next;
+
+// Monkeypatch next on an iterator.
+var inner = g(20);
+var n = 0;
+for (let x of inner) {
+    if (n == 0) {
+        assertEq(x, n++);
+    } else if (n == 1) {
+        assertEq(x, n++);
+        inner.next = function() { return { value: 42, done: false }; };
+    } else if (n == 2) {
+        assertEq(x, 42);
+        inner.next = function() { return { value: 100, done: true }; };
+    } else
+        throw 'not reached';
+}
+
+// Monkeypatch next on the prototype.
+var inner = g(20);
+var n = 0;
+for (let x of inner) {
+    if (n == 0) {
+        assertEq(x, n++);
+    } else if (n == 1) {
+        assertEq(x, n++);
+        GeneratorObjectPrototype.next =
+            function() { return { value: 42, done: false }; };
+    } else if (n == 2) {
+        n++;
+        assertEq(x, 42);
+        GeneratorObjectPrototype.next = GeneratorObjectPrototype_next;
+    } else if (n <= 20) {
+        assertEq(x, n++ - 1);
+    } else
+        throw 'not reached';
+}
--- a/js/src/jit-test/tests/for-of/proxy-3.js
+++ b/js/src/jit-test/tests/for-of/proxy-3.js
@@ -1,12 +1,13 @@
 // An exception thrown from a proxy trap while getting the .iterator method is propagated.
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var p = Proxy.create({
     getPropertyDescriptor: function (name) {
-        if (name == "iterator")
+        if (name == std_iterator)
             throw "fit";
         return undefined;
     }
 });
 assertThrowsValue(function () { for (var v of p) {} }, "fit");
--- a/js/src/jit-test/tests/for-of/semantics-01.js
+++ b/js/src/jit-test/tests/for-of/semantics-01.js
@@ -2,10 +2,12 @@
 // [[Get]] for properties named "iterator" and "next", and [[Call]]. These
 // "semantics" tests check that for-of really does appear to be implemented in
 // terms of those more basic operations, as required by the spec, even in
 // unusual cases.
 
 // Deleting Array.prototype.iterator makes for-of stop working on arrays.
 
 load(libdir + "asserts.js");
-delete Array.prototype.iterator;
+load(libdir + "iteration.js");
+
+delete Array.prototype[std_iterator];
 assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError);
--- a/js/src/jit-test/tests/for-of/semantics-02.js
+++ b/js/src/jit-test/tests/for-of/semantics-02.js
@@ -1,10 +1,12 @@
 // Replacing Array.prototype.iterator with something non-callable makes for-of throw.
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
+
 function test(v) {
-    Array.prototype.iterator = v;
+    Array.prototype[std_iterator] = v;
     assertThrowsInstanceOf(function () { for (var x of []) ; }, TypeError);
 }
 test(undefined);
 test(null);
 test({});
--- a/js/src/jit-test/tests/for-of/semantics-03.js
+++ b/js/src/jit-test/tests/for-of/semantics-03.js
@@ -1,11 +1,13 @@
 // Replacing Array.prototype.iterator with a generator affects for-of behavior.
 
-Array.prototype.iterator = function () {
+load(libdir + "iteration.js");
+
+Array.prototype[std_iterator] = function* () {
     for (var i = this.length; --i >= 0; )
         yield this[i];
 };
 
 var s = '';
 for (var v of ['a', 'b', 'c', 'd'])
     s += v;
 assertEq(s, 'dcba');
--- a/js/src/jit-test/tests/for-of/semantics-04.js
+++ b/js/src/jit-test/tests/for-of/semantics-04.js
@@ -1,15 +1,17 @@
 // Giving an Array an own .iterator property affects for-of.
 
+load(libdir + "asserts.js");
+load(libdir + "iteration.js");
+
 var a = [];
-a.iterator = function () {
+a[std_iterator] = function* () {
     yield 'o';
     yield 'k';
 };
 var s = '';
 for (var v of a)
     s += v;
 assertEq(s, 'ok');
 
-load(libdir + "asserts.js");
-a.iterator = undefined;
+a[std_iterator] = undefined;
 assertThrowsInstanceOf(function () { for (var v of a) ; }, TypeError);
--- a/js/src/jit-test/tests/for-of/semantics-05.js
+++ b/js/src/jit-test/tests/for-of/semantics-05.js
@@ -1,6 +1,8 @@
 // Deleting String.prototype.iterator makes for-of stop working on strings.
 
 load(libdir + "asserts.js");
-delete String.prototype.iterator;
+load(libdir + "iteration.js");
+
+delete String.prototype[std_iterator];
 assertThrowsInstanceOf(function () { for (var v of "abc") ; }, TypeError);
 assertThrowsInstanceOf(function () { for (var v of new String("abc")) ; }, TypeError);
--- a/js/src/jit-test/tests/for-of/semantics-07.js
+++ b/js/src/jit-test/tests/for-of/semantics-07.js
@@ -1,13 +1,15 @@
 // Deleting the .next method of an iterator in the middle of a for-of loop
 // causes a TypeError at the next iteration.
 
 load(libdir + "asserts.js");
-var iterProto = Object.getPrototypeOf([].iterator());
+load(libdir + "iteration.js");
+
+var iterProto = Object.getPrototypeOf([][std_iterator]());
 var s = '';
 assertThrowsInstanceOf(function () {
     for (var v of ['duck', 'duck', 'duck', 'goose', 'FAIL']) {
         s += v;
         if (v === 'goose')
             delete iterProto.next;
         s += '.';
     }
--- a/js/src/jit-test/tests/for-of/semantics-08.js
+++ b/js/src/jit-test/tests/for-of/semantics-08.js
@@ -1,7 +1,9 @@
-// A for-of loop exits if the iterator's .next method throws another compartment's StopIteration.
+// Results from another compartment are correctly interpreted by for-of.
+
+load(libdir + "iteration.js");
 
 var g = newGlobal();
-var it = g.eval("({ iterator: function () { return this; }, " +
-                "next: function () { throw StopIteration; } });");
+var it = g.eval("({ '" + std_iterator + "': function () { return this; }, " +
+                "next: function () { return { done: true } } });");
 for (x of it)
     throw 'FAIL';
--- a/js/src/jit-test/tests/for-of/semantics-11.js
+++ b/js/src/jit-test/tests/for-of/semantics-11.js
@@ -1,18 +1,20 @@
 // for-of on a proxy causes a predictable sequence of trap calls.
 
+load(libdir + "iteration.js");
+
 var s = '';
 
 var i = 0;
 var next_fn = Proxy.createFunction({}, function () {
     s += "n";
     if (i == 3)
-        throw StopIteration;
-    return i++;
+        return { value: undefined, done: true };
+    return { value: i++, done: false };
 });
 
 var it = Proxy.create({
     get: function (receiver, name) {
         if (name == 'toSource') {
             s += '?';
             return function () 'it';
         }
@@ -24,17 +26,17 @@ var it = Proxy.create({
 
 var iterator_fn = Proxy.createFunction({}, function () {
     s += 'i';
     return it;
 });
 
 var obj = Proxy.create({
     get: function (receiver, name) {
-        assertEq(name, "iterator");
+        assertEq(name, std_iterator);
         s += "I";
         return iterator_fn;
     }
 });
 
 for (var v of obj)
     s += v;
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/for-of/value-done-access.js
@@ -0,0 +1,23 @@
+// Test that each yield* loop just checks "done", and "value" is only
+// fetched once at the end.
+
+load(libdir + 'iteration.js');
+
+var log = "";
+
+function Iter(val, count) {
+    function next() {
+        return {
+            get done() { log += "d"; return count-- == 0; },
+            get value() { log += "v"; return val; }
+        }
+    }
+
+    this[std_iterator] = function() { return this; };
+    this.next = next;
+}
+
+for (var x of new Iter(42, 5))
+    assertEq(x, 42);
+
+assertEq(log, "dvdvdvdvdvd");
--- a/js/src/jit-test/tests/ion/bug800179.js
+++ b/js/src/jit-test/tests/ion/bug800179.js
@@ -1,9 +1,10 @@
-// |jit-test| error: is not iterable
+// |jit-test| error: TypeError
+
 try {
     x = []
     y = function() {}
     t = Uint8ClampedArray
     Object.defineProperty(x, 1, {
         get: (function() {
             for (v of t) {}
         })
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug916761.js
@@ -0,0 +1,11 @@
+if (typeof ParallelArray === "undefined")
+    quit();
+
+function toString(r) {
+    var result = "";
+    for (var i = 0; i < 5; i++)
+	result += r.get(i);
+    return result;
+}
+Array.prototype[2] = 'y';
+assertEq(toString(new ParallelArray(['x', 'x'])), "xxyundefinedundefined");
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2464,16 +2464,22 @@ BaselineCompiler::emit_JSOP_ENTERLET0()
 }
 
 bool
 BaselineCompiler::emit_JSOP_ENTERLET1()
 {
     return emitEnterBlock();
 }
 
+bool
+BaselineCompiler::emit_JSOP_ENTERLET2()
+{
+    return emitEnterBlock();
+}
+
 typedef bool (*LeaveBlockFn)(JSContext *, BaselineFrame *);
 static const VMFunction LeaveBlockInfo = FunctionInfo<LeaveBlockFn>(jit::LeaveBlock);
 
 bool
 BaselineCompiler::emitLeaveBlock()
 {
     // Call a stub to pop the block from the block chain.
     prepareVMCall();
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -143,16 +143,17 @@ namespace jit {
     _(JSOP_THROW)              \
     _(JSOP_TRY)                \
     _(JSOP_FINALLY)            \
     _(JSOP_GOSUB)              \
     _(JSOP_RETSUB)             \
     _(JSOP_ENTERBLOCK)         \
     _(JSOP_ENTERLET0)          \
     _(JSOP_ENTERLET1)          \
+    _(JSOP_ENTERLET2)          \
     _(JSOP_LEAVEBLOCK)         \
     _(JSOP_LEAVEBLOCKEXPR)     \
     _(JSOP_LEAVEFORLETIN)      \
     _(JSOP_EXCEPTION)          \
     _(JSOP_DEBUGGER)           \
     _(JSOP_ARGUMENTS)          \
     _(JSOP_RUNONCE)            \
     _(JSOP_REST)               \
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1612,26 +1612,28 @@ IonCompile(JSContext *cx, JSScript *scri
         return AbortReason_Alloc;
 
     JS_ASSERT(!GetIonScript(builder->script(), executionMode));
     JS_ASSERT(CanIonCompile(builder->script(), executionMode));
 
     RootedScript builderScript(cx, builder->script());
     IonSpewNewFunction(graph, builderScript);
 
-    if (!builder->build()) {
+    bool succeeded = builder->build();
+    builder->clearForBackEnd();
+
+    if (!succeeded) {
         if (cx->isExceptionPending()) {
             IonSpew(IonSpew_Abort, "Builder raised exception.");
             return AbortReason_Error;
         }
 
         IonSpew(IonSpew_Abort, "Builder failed to build.");
         return builder->abortReason();
     }
-    builder->clearForBackEnd();
 
     // If possible, compile the script off thread.
     if (OffThreadCompilationAvailable(cx)) {
         SetIonScript(builder->script(), executionMode, ION_COMPILING_SCRIPT);
 
         if (!StartOffThreadIonCompile(cx, builder)) {
             IonSpew(IonSpew_Abort, "Unable to start off-thread ion compilation.");
             return AbortReason_Alloc;
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2139,16 +2139,23 @@ jit::AnalyzeNewScriptProperties(JSContex
             {
                 if (exit == exit->immediateDominator()) {
                     definitelyExecuted = false;
                     break;
                 }
             }
         }
 
+        // Also check to see if the instruction is inside a loop body. Even if
+        // an access will always execute in the script, if it executes multiple
+        // times then we can get confused when rolling back objects while
+        // clearing the new script information.
+        if (ins->block()->loopDepth() != 0)
+            definitelyExecuted = false;
+
         bool handled = false;
         if (!AnalyzePoppedThis(cx, type, thisValue, ins, definitelyExecuted,
                                baseobj, initializerList, &accessedProperties, &handled))
         {
             return false;
         }
         if (!handled)
             return true;
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -61,16 +61,22 @@ IonBuilder::IonBuilder(JSContext *cx, Te
     pc = info->startPC();
 }
 
 void
 IonBuilder::clearForBackEnd()
 {
     cx = nullptr;
     baselineFrame_ = nullptr;
+
+    // The GSN cache allocates data from the malloc heap. Release this before
+    // later phases of compilation to avoid leaks, as the top level IonBuilder
+    // is not explicitly destroyed. Note that builders for inner scripts are
+    // constructed on the stack and will release this memory on destruction.
+    gsn.purge();
 }
 
 bool
 IonBuilder::abort(const char *message, ...)
 {
     // Don't call PCToLineNumber in release builds.
 #ifdef DEBUG
     va_list ap;
@@ -1237,16 +1243,17 @@ IonBuilder::snoopControlFlow(JSOp op)
           case SRC_CONTINUE:
             return processContinue(op);
 
           case SRC_SWITCHBREAK:
             return processSwitchBreak(op);
 
           case SRC_WHILE:
           case SRC_FOR_IN:
+          case SRC_FOR_OF:
             // while (cond) { }
             return whileOrForInLoop(sn);
 
           default:
             // Hard assert for now - make an error later.
             MOZ_ASSUME_UNREACHABLE("unknown goto case");
         }
         break;
@@ -2022,28 +2029,32 @@ IonBuilder::processDoWhileCondEnd(CFGSta
     MTest *test = MTest::New(vins, state.loop.entry, successor);
     current->end(test);
     return finishLoop(state, successor);
 }
 
 IonBuilder::ControlStatus
 IonBuilder::processWhileCondEnd(CFGState &state)
 {
-    JS_ASSERT(JSOp(*pc) == JSOP_IFNE);
+    JS_ASSERT(JSOp(*pc) == JSOP_IFNE || JSOp(*pc) == JSOP_IFEQ);
 
     // Balance the stack past the IFNE.
     MDefinition *ins = current->pop();
 
     // Create the body and successor blocks.
     MBasicBlock *body = newBlock(current, state.loop.bodyStart);
     state.loop.successor = newBlock(current, state.loop.exitpc, loopDepth_ - 1);
     if (!body || !state.loop.successor)
         return ControlStatus_Error;
 
-    MTest *test = MTest::New(ins, body, state.loop.successor);
+    MTest *test;
+    if (JSOp(*pc) == JSOP_IFNE)
+        test = MTest::New(ins, body, state.loop.successor);
+    else
+        test = MTest::New(ins, state.loop.successor, body);
     current->end(test);
 
     state.state = CFGState::WHILE_LOOP_BODY;
     state.stopAt = state.loop.bodyEnd;
     pc = state.loop.bodyStart;
     setCurrentAndSpecializePhis(body);
     return ControlStatus_Jumped;
 }
@@ -2605,17 +2616,17 @@ IonBuilder::whileOrForInLoop(jssrcnote *
     //    GOTO cond   ; SRC_WHILE (offset to IFNE)
     //    LOOPHEAD
     //    ...
     //  cond:
     //    LOOPENTRY
     //    ...
     //    IFNE        ; goes to LOOPHEAD
     // for (x in y) { } loops are similar; the cond will be a MOREITER.
-    JS_ASSERT(SN_TYPE(sn) == SRC_FOR_IN || SN_TYPE(sn) == SRC_WHILE);
+    JS_ASSERT(SN_TYPE(sn) == SRC_FOR_OF || SN_TYPE(sn) == SRC_FOR_IN || SN_TYPE(sn) == SRC_WHILE);
     int ifneOffset = js_GetSrcNoteOffset(sn, 0);
     jsbytecode *ifne = pc + ifneOffset;
     JS_ASSERT(ifne > pc);
 
     // Verify that the IFNE goes back to a loophead op.
     JS_ASSERT(JSOp(*GetNextPc(pc)) == JSOP_LOOPHEAD);
     JS_ASSERT(GetNextPc(pc) == ifne + GetJumpOffset(ifne));
 
@@ -3889,66 +3900,65 @@ IonBuilder::makeInliningDecision(JSFunct
     // Determine whether inlining is possible at callee site
     if (!canInlineTarget(target, callInfo.constructing()))
         return false;
 
     // Heuristics!
     JSScript *targetScript = target->nonLazyScript();
 
     // Skip heuristics if we have an explicit hint to inline.
-    if (targetScript->shouldInline)
-        return true;
-
-    // Cap the inlining depth.
-    if (IsSmallFunction(targetScript)) {
-        if (inliningDepth_ >= js_IonOptions.smallFunctionMaxInlineDepth) {
-            IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: exceeding allowed inline depth",
-                                      targetScript->filename(), targetScript->lineno);
-            return false;
+    if (!targetScript->shouldInline) {
+        // Cap the inlining depth.
+        if (IsSmallFunction(targetScript)) {
+            if (inliningDepth_ >= js_IonOptions.smallFunctionMaxInlineDepth) {
+                IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: exceeding allowed inline depth",
+                        targetScript->filename(), targetScript->lineno);
+                return false;
+            }
+        } else {
+            if (inliningDepth_ >= js_IonOptions.maxInlineDepth) {
+                IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: exceeding allowed inline depth",
+                        targetScript->filename(), targetScript->lineno);
+                return false;
+            }
+
+            if (targetScript->hasLoops()) {
+                IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: big function that contains a loop",
+                        targetScript->filename(), targetScript->lineno);
+                return false;
+            }
         }
-    } else {
-        if (inliningDepth_ >= js_IonOptions.maxInlineDepth) {
-            IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: exceeding allowed inline depth",
-                                      targetScript->filename(), targetScript->lineno);
-            return false;
-        }
-
-        if (targetScript->hasLoops()) {
-            IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: big function that contains a loop",
-                                      targetScript->filename(), targetScript->lineno);
+
+        // Callee must not be excessively large.
+        // This heuristic also applies to the callsite as a whole.
+        if (targetScript->length > js_IonOptions.inlineMaxTotalBytecodeLength) {
+            IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee excessively large.",
+                    targetScript->filename(), targetScript->lineno);
             return false;
         }
-     }
-
-    // Callee must not be excessively large.
-    // This heuristic also applies to the callsite as a whole.
-    if (targetScript->length > js_IonOptions.inlineMaxTotalBytecodeLength) {
-        IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee excessively large.",
-                                  targetScript->filename(), targetScript->lineno);
-        return false;
-    }
-
-    // Caller must be... somewhat hot. Ignore use counts when inlining for
-    // the definite properties analysis, as the caller has not run yet.
-    uint32_t callerUses = script()->getUseCount();
-    if (callerUses < js_IonOptions.usesBeforeInlining() &&
-        info().executionMode() != DefinitePropertiesAnalysis)
-    {
-        IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: caller is insufficiently hot.",
-                                  targetScript->filename(), targetScript->lineno);
-        return false;
-    }
-
-    // Callee must be hot relative to the caller.
-    if (targetScript->getUseCount() * js_IonOptions.inlineUseCountRatio < callerUses &&
-        info().executionMode() != DefinitePropertiesAnalysis)
-    {
-        IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee is not hot.",
-                                  targetScript->filename(), targetScript->lineno);
-        return false;
+
+        // Caller must be... somewhat hot. Ignore use counts when inlining for
+        // the definite properties analysis, as the caller has not run yet.
+        uint32_t callerUses = script()->getUseCount();
+        if (callerUses < js_IonOptions.usesBeforeInlining() &&
+            info().executionMode() != DefinitePropertiesAnalysis)
+        {
+            IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: caller is insufficiently hot.",
+                    targetScript->filename(), targetScript->lineno);
+            return false;
+        }
+
+        // Callee must be hot relative to the caller.
+        if (targetScript->getUseCount() * js_IonOptions.inlineUseCountRatio < callerUses &&
+            info().executionMode() != DefinitePropertiesAnalysis)
+        {
+            IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee is not hot.",
+                    targetScript->filename(), targetScript->lineno);
+            return false;
+        }
     }
 
     JS_ASSERT(!target->hasLazyType());
     types::TypeObject *targetType = target->getType(cx);
     JS_ASSERT(targetType && !targetType->unknownProperties());
 
     // TI calls ObjectStateChange to trigger invalidation of the caller.
     types::HeapTypeSet::WatchObjectStateChange(cx, targetType);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1165,32 +1165,31 @@ IonBuilder::InliningStatus
 IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo)
 {
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
     ExecutionMode executionMode = info().executionMode();
     switch (executionMode) {
       case SequentialExecution:
+      case DefinitePropertiesAnalysis:
         // In sequential mode, leave as is, because we'd have to
         // access the "in warmup" flag of the runtime.
         return InliningStatus_NotInlined;
 
       case ParallelExecution: {
         // During Parallel Exec, we always force sequential, so
         // replace with true.  This permits UCE to eliminate the
         // entire path as dead, which is important.
         callInfo.unwrapArgs();
         MConstant *ins = MConstant::New(BooleanValue(true));
         current->add(ins);
         current->push(ins);
         return InliningStatus_Inlined;
       }
-
-      default:;
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid execution mode");
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNewParallelArray(CallInfo &callInfo)
 {
@@ -1345,20 +1344,20 @@ IonBuilder::inlineNewDenseArray(CallInfo
     if (callInfo.constructing() || callInfo.argc() != 1)
         return InliningStatus_NotInlined;
 
     // For now, in seq. mode we just call the C function.  In
     // par. mode we use inlined MIR.
     ExecutionMode executionMode = info().executionMode();
     switch (executionMode) {
       case SequentialExecution:
+      case DefinitePropertiesAnalysis:
         return inlineNewDenseArrayForSequentialExecution(callInfo);
       case ParallelExecution:
         return inlineNewDenseArrayForParallelExecution(callInfo);
-      default:;
     }
 
     MOZ_ASSUME_UNREACHABLE("unknown ExecutionMode");
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNewDenseArrayForSequentialExecution(CallInfo &callInfo)
 {
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2045,18 +2045,20 @@ MToDouble::foldsTo(bool useValueNumbers)
         const Value &v = in->toConstant()->value();
         if (v.isNumber()) {
             double out = v.toNumber();
             return MConstant::New(DoubleValue(out));
         }
     }
 
     // Fold unnecessary numeric conversions.
-    if (input()->isToInt32())
+    if (input()->isToInt32()) {
         replaceOperand(0, input()->getOperand(0));
+        conversion_ = NonStringPrimitives;
+    }
 
     return this;
 }
 
 MDefinition *
 MToFloat32::foldsTo(bool useValueNumbers)
 {
     if (input()->type() == MIRType_Float32)
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -264,17 +264,17 @@ MSG_DEF(JSMSG_NAME_AFTER_FOR_PAREN,   21
 MSG_DEF(JSMSG_IN_AFTER_FOR_NAME,      211, 0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for")
 MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE,  212, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
 MSG_DEF(JSMSG_UNUSED213,              213, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_BAD_GENERATOR_YIELD,    214, 1, JSEXN_TYPEERR, "yield from closing generator {0}")
 MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX,   215, 1, JSEXN_SYNTAXERR, "{0} expression must be parenthesized")
 MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE,    216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
 MSG_DEF(JSMSG_UNUSED217,              217, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE,     218, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
-MSG_DEF(JSMSG_UNUSED219,              219, 0, JSEXN_NONE, "")
+MSG_DEF(JSMSG_BAD_SYMBOL,             219, 1, JSEXN_TYPEERR, "{0} is not a well-known @@-symbol")
 MSG_DEF(JSMSG_BAD_DELETE_OPERAND,     220, 0, JSEXN_REFERENCEERR, "invalid delete operand")
 MSG_DEF(JSMSG_BAD_INCOP_OPERAND,      221, 0, JSEXN_REFERENCEERR, "invalid increment/decrement operand")
 MSG_DEF(JSMSG_UNEXPECTED_TYPE,        222, 2, JSEXN_TYPEERR, "{0} is {1}")
 MSG_DEF(JSMSG_LET_DECL_NOT_IN_BLOCK,  223, 0, JSEXN_SYNTAXERR, "let declaration not directly within block")
 MSG_DEF(JSMSG_BAD_OBJECT_INIT,        224, 0, JSEXN_SYNTAXERR, "invalid object initializer")
 MSG_DEF(JSMSG_CANT_SET_ARRAY_ATTRS,   225, 0, JSEXN_INTERNALERR, "can't set attributes on indexed array properties")
 MSG_DEF(JSMSG_EVAL_ARITY,             226, 0, JSEXN_TYPEERR, "eval accepts only one parameter")
 MSG_DEF(JSMSG_MISSING_FUN_ARG,        227, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -247,16 +247,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
           case JSOP_DEFCONST:
           case JSOP_SETCONST:
             usesScopeChain_ = true; // Requires access to VarObj via ScopeChain.
             canTrackVars = false;
             break;
 
           case JSOP_EVAL:
           case JSOP_SPREADEVAL:
+          case JSOP_ENTERLET2:
           case JSOP_ENTERWITH:
             canTrackVars = false;
             break;
 
           case JSOP_TABLESWITCH: {
             unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
             jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
             int32_t low = GET_JUMP_OFFSET(pc2);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -280,19 +280,25 @@ JS_ConvertArgumentsVA(JSContext *cx, uns
                 if (!stable)
                     return false;
                 *va_arg(ap, const jschar **) = stable->chars().get();
             } else {
                 *va_arg(ap, JSString **) = str;
             }
             break;
           case 'o':
-            if (!js_ValueToObjectOrNull(cx, *sp, &obj))
-                return false;
-            *sp = OBJECT_TO_JSVAL(obj);
+            if (sp->isNullOrUndefined()) {
+                obj = nullptr;
+            } else {
+                RootedValue v(cx, *sp);
+                obj = ToObject(cx, v);
+                if (!obj)
+                    return false;
+            }
+            *sp = ObjectOrNullValue(obj);
             *va_arg(ap, JSObject **) = obj;
             break;
           case 'f':
             obj = ReportIfNotFunction(cx, *sp);
             if (!obj)
                 return false;
             *sp = OBJECT_TO_JSVAL(obj);
             *va_arg(ap, JSFunction **) = &obj->as<JSFunction>();
@@ -323,19 +329,24 @@ JS_ConvertValue(JSContext *cx, HandleVal
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, value);
     switch (type) {
       case JSTYPE_VOID:
         vp.setUndefined();
         ok = true;
         break;
       case JSTYPE_OBJECT:
-        ok = js_ValueToObjectOrNull(cx, value, &obj);
-        if (ok)
-            vp.setObjectOrNull(obj);
+        if (value.isNullOrUndefined()) {
+            obj.set(nullptr);
+        } else {
+            obj = ToObject(cx, value);
+            if (!obj)
+                return false;
+        }
+        ok = true;
         break;
       case JSTYPE_FUNCTION:
         vp.set(value);
         obj = ReportIfNotFunction(cx, vp);
         ok = (obj != NULL);
         break;
       case JSTYPE_STRING:
         str = ToString<CanGC>(cx, value);
@@ -363,17 +374,25 @@ JS_ConvertValue(JSContext *cx, HandleVal
 }
 
 JS_PUBLIC_API(bool)
 JS_ValueToObject(JSContext *cx, HandleValue value, MutableHandleObject objp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, value);
-    return js_ValueToObjectOrNull(cx, value, objp);
+    if (value.isNullOrUndefined()) {
+        objp.set(nullptr);
+        return true;
+    }
+    JSObject *obj = ToObject(cx, value);
+    if (!obj)
+        return false;
+    objp.set(obj);
+    return true;
 }
 
 JS_PUBLIC_API(JSFunction *)
 JS_ValueToFunction(JSContext *cx, HandleValue value)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, value);
@@ -4197,17 +4216,25 @@ JS_DefineFunctions(JSContext *cx, JSObje
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, objArg);
 
     RootedObject obj(cx, objArg);
     RootedObject ctor(cx);
 
     for (; fs->name; fs++) {
-        RootedAtom atom(cx, Atomize(cx, fs->name, strlen(fs->name)));
+        RootedAtom atom(cx);
+        // If the name starts with "@@", it must be a well-known symbol.
+        if (fs->name[0] != '@' || fs->name[1] != '@')
+            atom = Atomize(cx, fs->name, strlen(fs->name));
+        else if (strcmp(fs->name, "@@iterator") == 0)
+            // FIXME: This atom should be a symbol: bug 918828.
+            atom = cx->names().std_iterator;
+        else
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_SYMBOL, fs->name);
         if (!atom)
             return false;
 
         Rooted<jsid> id(cx, AtomToId(atom));
 
         /*
          * Define a generic arity N+1 static method for the arity N prototype
          * method if flags contains JSFUN_GENERIC_NATIVE.
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2342,28 +2342,35 @@ inline int CheckIsNative(JSNative native
 
 } // namespace detail
 } // namespace JS
 
 #define JS_CAST_NATIVE_TO(v, To) \
   (static_cast<void>(sizeof(JS::detail::CheckIsNative(v))), \
    reinterpret_cast<To>(v))
 
+#define JS_CHECK_ACCESSOR_FLAGS(flags) \
+  (static_cast<mozilla::EnableIf<!((flags) & (JSPROP_READONLY | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS))>::Type>(0), \
+   (flags))
+
 /*
  * JSPropertySpec uses JSAPI JSPropertyOp and JSStrictPropertyOp in function
- * signatures, but with JSPROP_NATIVE_ACCESSORS the actual values must be
- * JSNatives. To avoid widespread casting, have JS_PSG and JS_PSGS perform
- * type-safe casts.
+ * signatures.  These macros encapsulate the definition of JSNative-backed
+ * JSPropertySpecs, performing type-safe casts on the getter/setter functions
+ * and adding the necessary property flags to trigger interpretation as
+ * JSNatives.
  */
 #define JS_PSG(name, getter, flags) \
-    {name, 0, (flags) | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS, \
+    {name, 0, \
+     uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS), \
      JSOP_WRAPPER(JS_CAST_NATIVE_TO(getter, JSPropertyOp)), \
      JSOP_NULLWRAPPER}
 #define JS_PSGS(name, getter, setter, flags) \
-    {name, 0, (flags) | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS, \
+    {name, 0, \
+     uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS), \
      JSOP_WRAPPER(JS_CAST_NATIVE_TO(getter, JSPropertyOp)), \
      JSOP_WRAPPER(JS_CAST_NATIVE_TO(setter, JSStrictPropertyOp))}
 #define JS_PS_END {0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER}
 
 /*
  * To define a native function, set call to a JSNativeWrapper. To define a
  * self-hosted function, set selfHostedName to the name of a function
  * compiled during JSRuntime::initSelfHosting.
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2887,16 +2887,17 @@ static const JSFunctionSpec array_method
     JS_SELF_HOSTED_FN("scatterPar",  "ArrayScatterPar",  5,0),
     JS_SELF_HOSTED_FN("filterPar",   "ArrayFilterPar",   2,0),
 #endif
 
     /* ES6 additions */
     JS_SELF_HOSTED_FN("find",        "ArrayFind",        1,0),
     JS_SELF_HOSTED_FN("findIndex",   "ArrayFindIndex",   1,0),
 
+    JS_SELF_HOSTED_FN("@@iterator",  "ArrayIterator",    0,0),
     JS_FN("iterator",           JS_ArrayIterator,   0,0),
     JS_FS_END
 };
 
 static const JSFunctionSpec array_static_methods[] = {
     JS_FN("isArray",            array_isArray,      1,0),
     JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0),
     JS_SELF_HOSTED_FN("indexOf",     "ArrayStaticIndexOf", 2,0),
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -389,17 +389,19 @@ TryNewNurseryGCThing(ThreadSafeContext *
 template <typename T, AllowGC allowGC>
 inline T *
 NewGCThing(ThreadSafeContext *cx, AllocKind kind, size_t thingSize, InitialHeap heap)
 {
     JS_ASSERT(thingSize == js::gc::Arena::thingSize(kind));
 
     if (cx->isJSContext()) {
         JSContext *ncx = cx->asJSContext();
+#ifdef JS_GC_ZEAL
         JSRuntime *rt = ncx->runtime();
+#endif
         JS_ASSERT_IF(rt->isAtomsCompartment(ncx->compartment()),
                      kind == FINALIZE_STRING ||
                      kind == FINALIZE_SHORT_STRING ||
                      kind == FINALIZE_IONCODE);
         JS_ASSERT(!rt->isHeapBusy());
         JS_ASSERT(!rt->noGCOrAllocationCheck);
 
         /* For testing out of memory conditions */
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -560,22 +560,22 @@ UpdateNativeIterator(NativeIterator *ni,
     // SuppressDeletedPropertyHelper will recognize the iterator as a match.
     ni->obj = obj;
 }
 
 bool
 js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp)
 {
     if (flags == JSITER_FOR_OF) {
-        // for-of loop. The iterator is simply |obj.iterator()|.
+        // for-of loop. The iterator is simply |obj[@@iterator]()|.
         RootedValue method(cx);
-        if (!JSObject::getProperty(cx, obj, obj, cx->names().iterator, &method))
+        if (!JSObject::getProperty(cx, obj, obj, cx->names().std_iterator, &method))
             return false;
 
-        // Throw if obj.iterator isn't callable. js::Invoke is about to check
+        // Throw if obj[@@iterator] isn't callable. js::Invoke is about to check
         // for this kind of error anyway, but it would throw an inscrutable
         // error message about |method| rather than this nice one about |obj|.
         if (!method.isObject() || !method.toObject().isCallable()) {
             RootedValue val(cx, ObjectOrNullValue(obj));
             char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr());
             if (!bytes)
                 return false;
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_ITERABLE, bytes);
@@ -588,16 +588,18 @@ js::GetIterator(JSContext *cx, HandleObj
 
         JSObject *resultObj = ToObject(cx, vp);
         if (!resultObj)
             return false;
         vp.setObject(*resultObj);
         return true;
     }
 
+    JS_ASSERT(!(flags & JSITER_FOR_OF));
+
     Vector<Shape *, 8> shapes(cx);
     uint32_t key = 0;
 
     bool keysOnly = (flags == JSITER_ENUMERATE);
 
     if (obj) {
         if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) {
             JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
@@ -720,16 +722,40 @@ JSObject *
 js::GetIteratorObject(JSContext *cx, HandleObject obj, uint32_t flags)
 {
     RootedValue value(cx);
     if (!GetIterator(cx, obj, flags, &value))
         return NULL;
     return &value.toObject();
 }
 
+JSObject *
+js::CreateItrResultObject(JSContext *cx, HandleValue value, bool done)
+{
+    // FIXME: We can cache the iterator result object shape somewhere.
+    AssertHeapIsIdle(cx);
+
+    RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
+    if (!proto)
+        return nullptr;
+
+    RootedObject obj(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, cx->global()));
+    if (!obj)
+        return nullptr;
+
+    if (!JSObject::defineProperty(cx, obj, cx->names().value, value))
+        return nullptr;
+
+    RootedValue doneBool(cx, BooleanValue(done));
+    if (!JSObject::defineProperty(cx, obj, cx->names().done, doneBool))
+        return nullptr;
+
+    return obj;
+}
+
 bool
 js_ThrowStopIteration(JSContext *cx)
 {
     JS_ASSERT(!JS_IsExceptionPending(cx));
     RootedValue v(cx);
     if (js_FindClassObject(cx, JSProto_StopIteration, &v))
         cx->setPendingException(v);
     return false;
@@ -776,33 +802,25 @@ iterator_next_impl(JSContext *cx, CallAr
     if (!args.rval().toBoolean()) {
         js_ThrowStopIteration(cx);
         return false;
     }
 
     return js_IteratorNext(cx, thisObj, args.rval());
 }
 
-static bool
-iterator_iterator(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().set(args.thisv());
-    return true;
-}
-
 bool
 iterator_next(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsIterator, iterator_next_impl>(cx, args);
 }
 
 static const JSFunctionSpec iterator_methods[] = {
-    JS_FN("iterator",  iterator_iterator,   0, 0),
+    JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0),
     JS_FN("next",      iterator_next,       0, 0),
     JS_FS_END
 };
 
 static JSObject *
 iterator_iteratorObject(JSContext *cx, HandleObject obj, bool keysonly)
 {
     return obj;
@@ -943,16 +961,17 @@ const Class ElementIteratorObject::class
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     NULL                     /* finalize    */
 };
 
 const JSFunctionSpec ElementIteratorObject::methods[] = {
+    JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0),
     JS_FN("next", next, 0, 0),
     JS_FS_END
 };
 
 static bool
 CloseLegacyGenerator(JSContext *cx, HandleObject genobj);
 
 bool
@@ -969,28 +988,22 @@ js::ValueToIterator(JSContext *cx, unsig
     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
 
     RootedObject obj(cx);
     if (vp.isObject()) {
         /* Common case. */
         obj = &vp.toObject();
     } else {
         /*
-         * Enumerating over null and undefined gives an empty enumerator.
-         * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
-         * the first production in 12.6.4 and step 4 of the second production,
-         * but it's "web JS" compatible. ES5 fixed for-in to match this de-facto
-         * standard.
+         * Enumerating over null and undefined gives an empty enumerator, so
+         * that |for (var p in <null or undefined>) <loop>;| never executes
+         * <loop>, per ES5 12.6.4.
          */
-        if (flags & JSITER_ENUMERATE) {
-            if (!js_ValueToObjectOrNull(cx, vp, &obj))
-                return false;
-            /* fall through */
-        } else {
-            obj = js_ValueToNonNullObject(cx, vp);
+        if (!(flags & JSITER_ENUMERATE) || !vp.isNullOrUndefined()) {
+            obj = ToObject(cx, vp);
             if (!obj)
                 return false;
         }
     }
 
     return GetIterator(cx, obj, flags, vp);
 }
 
@@ -1303,16 +1316,48 @@ const Class StopIterationObject::class_ 
     JS_ConvertStub,
     NULL,                    /* finalize    */
     NULL,                    /* checkAccess */
     NULL,                    /* call        */
     stopiter_hasInstance,
     NULL                     /* construct   */
 };
 
+bool
+ForOfIterator::next(MutableHandleValue vp, bool *done)
+{
+    JS_ASSERT(iterator);
+
+    RootedValue method(cx);
+    if (!JSObject::getProperty(cx, iterator, iterator, cx->names().next, &method))
+        return false;
+
+    InvokeArgs args(cx);
+    if (!args.init(1))
+        return false;
+    args.setCallee(method);
+    args.setThis(ObjectValue(*iterator));
+    args[0].setUndefined();
+    if (!Invoke(cx, args))
+        return false;
+
+    RootedObject resultObj(cx, ToObject(cx, args.rval()));
+    if (!resultObj)
+        return false;
+    RootedValue doneVal(cx);
+    if (!JSObject::getProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
+        return false;
+    *done = ToBoolean(doneVal);
+    if (*done) {
+        vp.setUndefined();
+        return true;
+    }
+    return JSObject::getProperty(cx, resultObj, resultObj, cx->names().value, vp);
+}
+
 /*** Generators **********************************************************************************/
 
 template<typename T>
 static void
 FinalizeGenerator(FreeOp *fop, JSObject *obj)
 {
     JS_ASSERT(obj->is<T>());
     JSGenerator *gen = obj->as<T>().getGenerator();
@@ -1791,24 +1836,24 @@ NativeMethod(JSContext *cx, unsigned arg
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsObjectOfType<T>, Impl>(cx, args);
 }
 
 #define JSPROP_ROPERM   (JSPROP_READONLY | JSPROP_PERMANENT)
 #define JS_METHOD(name, T, impl, len, attrs) JS_FN(name, (NativeMethod<T,impl>), len, attrs)
 
 static const JSFunctionSpec star_generator_methods[] = {
-    JS_FN("iterator", iterator_iterator, 0, 0),
+    JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
     JS_METHOD("next", StarGeneratorObject, star_generator_next, 1, 0),
     JS_METHOD("throw", StarGeneratorObject, star_generator_throw, 1, 0),
     JS_FS_END
 };
 
 static const JSFunctionSpec legacy_generator_methods[] = {
-    JS_FN("iterator", iterator_iterator, 0, 0),
+    JS_SELF_HOSTED_FN("@@iterator", "LegacyGeneratorIteratorShim", 0, 0),
     // "send" is an alias for "next".
     JS_METHOD("next", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM),
     JS_METHOD("send", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM),
     JS_METHOD("throw", LegacyGeneratorObject, legacy_generator_throw, 1, JSPROP_ROPERM),
     JS_METHOD("close", LegacyGeneratorObject, legacy_generator_close, 0, JSPROP_ROPERM),
     JS_FS_END
 };
 
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -231,33 +231,16 @@ extern bool
 js_IteratorNext(JSContext *cx, js::HandleObject iterobj, js::MutableHandleValue rval);
 
 extern bool
 js_ThrowStopIteration(JSContext *cx);
 
 namespace js {
 
 /*
- * Get the next value from an iterator object.
- *
- * On success, store the next value in *vp and return true; if there are no
- * more values, store the magic value JS_NO_ITER_VALUE in *vp and return true.
- */
-inline bool
-Next(JSContext *cx, HandleObject iter, MutableHandleValue vp)
-{
-    if (!js_IteratorMore(cx, iter, vp))
-        return false;
-    if (vp.toBoolean())
-        return js_IteratorNext(cx, iter, vp);
-    vp.setMagic(JS_NO_ITER_VALUE);
-    return true;
-}
-
-/*
  * Convenience class for imitating a JS level for-of loop. Typical usage:
  *
  *     ForOfIterator it(cx, iterable);
  *     while (it.next()) {
  *        if (!DoStuff(cx, it.value()))
  *            return false;
  *     }
  *     if (!it.close())
@@ -270,66 +253,40 @@ Next(JSContext *cx, HandleObject iter, M
  * and the failure is allowed to propagate on cx, as in this example if DoStuff
  * fails. In that case, ForOfIterator's destructor does all necessary cleanup.
  */
 class ForOfIterator
 {
   private:
     JSContext *cx;
     RootedObject iterator;
-    RootedValue currentValue;
-    bool ok;
-    bool closed;
 
     ForOfIterator(const ForOfIterator &) MOZ_DELETE;
     ForOfIterator &operator=(const ForOfIterator &) MOZ_DELETE;
 
   public:
-    ForOfIterator(JSContext *cx, const Value &iterable)
-        : cx(cx), iterator(cx, NULL), currentValue(cx), closed(false)
-    {
-        RootedValue iterv(cx, iterable);
-        ok = ValueToIterator(cx, JSITER_FOR_OF, &iterv);
-        iterator = ok ? &iterv.get().toObject() : NULL;
-    }
+    ForOfIterator(JSContext *cx) : cx(cx), iterator(cx) { }
 
-    ~ForOfIterator() {
-        if (!closed)
-            close();
-    }
-
-    bool next() {
-        JS_ASSERT(!closed);
-        ok = ok && Next(cx, iterator, &currentValue);
-        return ok && !currentValue.get().isMagic(JS_NO_ITER_VALUE);
+    bool init(HandleValue iterable) {
+        RootedValue iterv(cx, iterable);
+        if (!ValueToIterator(cx, JSITER_FOR_OF, &iterv))
+            return false;
+        iterator = &iterv.get().toObject();
+        return true;
     }
 
-    MutableHandleValue value() {
-        JS_ASSERT(ok);
-        JS_ASSERT(!closed);
-        return &currentValue;
-    }
+    bool next(MutableHandleValue val, bool *done);
+};
 
-    bool close() {
-        JS_ASSERT(!closed);
-        closed = true;
-        if (!iterator)
-            return false;
-        bool throwing = cx->isExceptionPending();
-        RootedValue exc(cx);
-        if (throwing) {
-            exc = cx->getPendingException();
-            cx->clearPendingException();
-        }
-        bool closedOK = CloseIterator(cx, iterator);
-        if (throwing && closedOK)
-            cx->setPendingException(exc);
-        return ok && !throwing && closedOK;
-    }
-};
+/*
+ * Create an object of the form { value: VALUE, done: DONE }.
+ * ES6 draft from 2013-09-05, section 25.4.3.4.
+ */
+extern JSObject *
+CreateItrResultObject(JSContext *cx, js::HandleValue value, bool done);
 
 } /* namespace js */
 
 /*
  * Generator state codes.
  */
 enum JSGeneratorState
 {
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5142,34 +5142,16 @@ js::PrimitiveToObject(JSContext *cx, con
     }
     if (v.isNumber())
         return NumberObject::create(cx, v.toNumber());
 
     JS_ASSERT(v.isBoolean());
     return BooleanObject::create(cx, v.toBoolean());
 }
 
-bool
-js_ValueToObjectOrNull(JSContext *cx, const Value &v, MutableHandleObject objp)
-{
-    JSObject *obj;
-
-    if (v.isObjectOrNull()) {
-        obj = v.toObjectOrNull();
-    } else if (v.isUndefined()) {
-        obj = NULL;
-    } else {
-        obj = PrimitiveToObject(cx, v);
-        if (!obj)
-            return false;
-    }
-    objp.set(obj);
-    return true;
-}
-
 /* Callers must handle the already-object case . */
 JSObject *
 js::ToObjectSlow(JSContext *cx, HandleValue val, bool reportScanStack)
 {
     JS_ASSERT(!val.isMagic());
     JS_ASSERT(!val.isObject());
 
     if (val.isNullOrUndefined()) {
@@ -5180,30 +5162,16 @@ js::ToObjectSlow(JSContext *cx, HandleVa
                                  val.isNull() ? "null" : "undefined", "object");
         }
         return NULL;
     }
 
     return PrimitiveToObject(cx, val);
 }
 
-JSObject *
-js_ValueToNonNullObject(JSContext *cx, const Value &v)
-{
-    RootedObject obj(cx);
-
-    if (!js_ValueToObjectOrNull(cx, v, &obj))
-        return NULL;
-    if (!obj) {
-        RootedValue val(cx, v);
-        js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, val, NullPtr());
-    }
-    return obj;
-}
-
 void
 js_GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
 {
     JS_ASSERT(trc->debugPrinter == js_GetObjectSlotName);
 
     JSObject *obj = (JSObject *)trc->debugPrintArg;
     uint32_t slot = uint32_t(trc->debugPrintIndex);
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1460,23 +1460,16 @@ bool
 GetObjectElementOperationPure(ThreadSafeContext *cx, JSObject *obj, const Value &prop, Value *vp);
 
 /* Wrap boolean, number or string as Boolean, Number or String object. */
 extern JSObject *
 PrimitiveToObject(JSContext *cx, const Value &v);
 
 } /* namespace js */
 
-extern bool
-js_ValueToObjectOrNull(JSContext *cx, const js::Value &v, JS::MutableHandleObject objp);
-
-/* Throws if v could not be converted to an object. */
-extern JSObject *
-js_ValueToNonNullObject(JSContext *cx, const js::Value &v);
-
 namespace js {
 
 /*
  * Invokes the ES5 ToObject algorithm on vp, returning the result. If vp might
  * already be an object, use ToObject. reportCantConvert controls how null and
  * undefined errors are reported.
  */
 extern JSObject *
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -114,19 +114,21 @@ js_GetVariableBytecodeLength(jsbytecode 
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected op");
     }
 }
 
 static uint32_t
 NumBlockSlots(JSScript *script, jsbytecode *pc)
 {
-    JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1);
+    JS_ASSERT(*pc == JSOP_ENTERBLOCK ||
+              *pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1 || *pc == JSOP_ENTERLET2);
     JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH);
     JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET1_LENGTH);
+    JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET2_LENGTH);
 
     return script->getObject(GET_UINT32_INDEX(pc))->as<StaticBlockObject>().slotCount();
 }
 
 unsigned
 js::StackUses(JSScript *script, jsbytecode *pc)
 {
     JSOp op = (JSOp) *pc;
@@ -141,16 +143,18 @@ js::StackUses(JSScript *script, jsbyteco
       case JSOP_LEAVEBLOCK:
         return GET_UINT16(pc);
       case JSOP_LEAVEBLOCKEXPR:
         return GET_UINT16(pc) + 1;
       case JSOP_ENTERLET0:
         return NumBlockSlots(script, pc);
       case JSOP_ENTERLET1:
         return NumBlockSlots(script, pc) + 1;
+      case JSOP_ENTERLET2:
+        return NumBlockSlots(script, pc) + 2;
       default:
         /* stack: fun, this, [argc arguments] */
         JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL || op == JSOP_EVAL ||
                   op == JSOP_FUNCALL || op == JSOP_FUNAPPLY);
         return 2 + GET_ARGC(pc);
     }
 }
 
@@ -158,17 +162,21 @@ unsigned
 js::StackDefs(JSScript *script, jsbytecode *pc)
 {
     JSOp op = (JSOp) *pc;
     const JSCodeSpec &cs = js_CodeSpec[op];
     if (cs.ndefs >= 0)
         return cs.ndefs;
 
     uint32_t n = NumBlockSlots(script, pc);
-    return op == JSOP_ENTERLET1 ? n + 1 : n;
+    if (op == JSOP_ENTERLET1)
+        return n + 1;
+    if (op == JSOP_ENTERLET2)
+        return n + 2;
+    return n;
 }
 
 static const char * const countBaseNames[] = {
     "interp"
 };
 
 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) == PCCounts::BASE_LIMIT);
 
@@ -1046,17 +1054,18 @@ GetBlockChainAtPC(JSContext *cx, JSScrip
 
     JSObject *blockChain = NULL;
     for (jsbytecode *p = start; p < pc; p += GetBytecodeLength(p)) {
         JSOp op = JSOp(*p);
 
         switch (op) {
           case JSOP_ENTERBLOCK:
           case JSOP_ENTERLET0:
-          case JSOP_ENTERLET1: {
+          case JSOP_ENTERLET1:
+          case JSOP_ENTERLET2: {
             JSObject *child = script->getObject(p);
             JS_ASSERT_IF(blockChain, child->as<BlockObject>().stackDepth() >=
                                      blockChain->as<BlockObject>().stackDepth());
             blockChain = child;
             break;
           }
           case JSOP_LEAVEBLOCK:
           case JSOP_LEAVEBLOCKEXPR:
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -395,23 +395,24 @@ OPDEF(JSOP_UNUSED183,     183,"unused183
 
 OPDEF(JSOP_CALLPROP,      184,"callprop",   NULL,     5,  1,  1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3)
 
 /* Enter a let block/expr whose slots are at the top of the stack. */
 OPDEF(JSOP_ENTERLET0,     185,"enterlet0",  NULL,     5, -1, -1,  JOF_OBJECT)
 
 /* Enter a let block/expr whose slots are 1 below the top of the stack. */
 OPDEF(JSOP_ENTERLET1,     186,"enterlet1",  NULL,     5, -1, -1,  JOF_OBJECT)
+/* Enter a let block/expr whose slots are 2 below the top of the stack. */
+OPDEF(JSOP_ENTERLET2,     187,"enterlet2",  NULL,     5, -1, -1,  JOF_OBJECT)
 
 /*
  * Opcode to hold 24-bit immediate integer operands.
  */
-OPDEF(JSOP_UINT24,        187,"uint24",     NULL,     4,  0,  1, JOF_UINT24)
+OPDEF(JSOP_UINT24,        188,"uint24",     NULL,     4,  0,  1, JOF_UINT24)
 
-OPDEF(JSOP_UNUSED188,     188,"unused188",   NULL,    1,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED189,     189,"unused189",   NULL,    1,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED190,     190,"unused190",   NULL,    1,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED191,     191,"unused191",   NULL,    1,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED192,     192,"unused192",   NULL,    1,  0,  0,  JOF_BYTE)
 
 OPDEF(JSOP_CALLELEM,      193, "callelem",   NULL,    1,  2,  1, JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC)
 
 /*
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3653,16 +3653,17 @@ static const JSFunctionSpec string_metho
     JS_FN("strike",            str_strike,            0,0),
     JS_FN("small",             str_small,             0,0),
     JS_FN("big",               str_big,               0,0),
     JS_FN("blink",             str_blink,             0,0),
     JS_FN("sup",               str_sup,               0,0),
     JS_FN("sub",               str_sub,               0,0),
 #endif
 
+    JS_SELF_HOSTED_FN("@@iterator", "ArrayIterator",  0,0),
     JS_FN("iterator",          JS_ArrayIterator,      0,0),
     JS_FS_END
 };
 
 bool
 js_String(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -101,17 +101,17 @@ static const JSFunctionSpec pm_fns[] = {
     JS_FN("start",               pm_start,               0, PM_FATTRS),
     JS_FN("stop",                pm_stop,                0, PM_FATTRS),
     JS_FN("reset",               pm_reset,               0, PM_FATTRS),
     JS_FN("canMeasureSomething", pm_canMeasureSomething, 0, PM_FATTRS),
     JS_FS_END
 };
 
 const uint8_t PM_PATTRS =
-    JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED;
+    JSPROP_ENUMERATE | JSPROP_PERMANENT;
 
 #define GETTER(name)                            \
     JS_PSG(#name, pm_get_##name, PM_PATTRS)
 
 static const JSPropertySpec pm_props[] = {
     GETTER(cpu_cycles),
     GETTER(instructions),
     GETTER(cache_references),
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1799,16 +1799,17 @@ SrcNotes(JSContext *cx, HandleScript scr
                    unsigned(js_GetSrcNoteOffset(sn, 2)));
             break;
 
           case SRC_IF_ELSE:
             Sprint(sp, " else %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
             break;
 
           case SRC_FOR_IN:
+          case SRC_FOR_OF:
             Sprint(sp, " closingjump %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
             break;
 
           case SRC_COND:
           case SRC_WHILE:
           case SRC_NEXTCASE:
             Sprint(sp, " offset %u", unsigned(js_GetSrcNoteOffset(sn, 0)));
             break;
--- a/js/src/tests/ecma_6/Generators/runtime.js
+++ b/js/src/tests/ecma_6/Generators/runtime.js
@@ -12,16 +12,18 @@ function assertSyntaxError(str) {
 }
 
 
 function f() { }
 function* g() { yield 1; }
 var GeneratorFunctionPrototype = Object.getPrototypeOf(g);
 var GeneratorFunction = GeneratorFunctionPrototype.constructor;
 var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype;
+// FIXME: This should be a symbol.
+var std_iterator = "@@iterator";
 
 
 // A generator function should have the same set of properties as any
 // other function.
 function TestGeneratorFunctionInstance() {
     var f_own_property_names = Object.getOwnPropertyNames(f);
     var g_own_property_names = Object.getOwnPropertyNames(g);
 
@@ -59,17 +61,17 @@ TestGeneratorFunctionPrototype();
 // Functions that we associate with generator objects are actually defined by
 // a common prototype.
 function TestGeneratorObjectPrototype() {
     assertEq(Object.getPrototypeOf(GeneratorObjectPrototype),
                Object.prototype);
     assertEq(Object.getPrototypeOf((function*(){yield 1}).prototype),
                GeneratorObjectPrototype);
 
-    var expected_property_names = ["iterator", "next", "throw", "constructor"];
+    var expected_property_names = ["next", "throw", "constructor", std_iterator];
     var found_property_names =
         Object.getOwnPropertyNames(GeneratorObjectPrototype);
 
     expected_property_names.sort();
     found_property_names.sort();
 
     assertDeepEq(found_property_names, expected_property_names);
 }
--- a/js/src/tests/lib/jittests.py
+++ b/js/src/tests/lib/jittests.py
@@ -319,19 +319,26 @@ def run_test_remote(test, device, prefix
 
     out = buf.getvalue()
     # We can't distinguish between stdout and stderr so we pass
     # the same buffer to both.
     return TestOutput(test, cmd, out, out, returncode, None, False)
 
 def check_output(out, err, rc, test):
     if test.expect_error:
-        # We'd like to check the return code here, but doing so seems
-        # to return incorrect values occasionally. See bug 899697 and
-        # bug 922943.
+        # The shell exits with code 3 on uncaught exceptions.
+        # Sometimes 0 is returned on Windows for unknown reasons.
+        # See bug 899697.
+        if sys.platform in ['win32', 'cygwin']:
+            if rc != 3 and rc != 0:
+                return False
+        else:
+            if rc != 3:
+                return False
+
         return test.expect_error in err
 
     for line in out.split('\n'):
         if line.startswith('Trace stats check failed'):
             return False
 
     for line in err.split('\n'):
         if 'Assertion failed:' in line:
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -32,16 +32,17 @@
     macro(CollatorCompareGet, CollatorCompareGet, "Intl_Collator_compare_get") \
     macro(columnNumber, columnNumber, "columnNumber") \
     macro(compare, compare, "compare") \
     macro(configurable, configurable, "configurable") \
     macro(construct, construct, "construct") \
     macro(constructor, constructor, "constructor") \
     macro(currency, currency, "currency") \
     macro(currencyDisplay, currencyDisplay, "currencyDisplay") \
+    macro(std_iterator, std_iterator, "@@iterator") \
     macro(DateTimeFormat, DateTimeFormat, "DateTimeFormat") \
     macro(DateTimeFormatFormatGet, DateTimeFormatFormatGet, "Intl_DateTimeFormat_format_get") \
     macro(decodeURI, decodeURI, "decodeURI") \
     macro(decodeURIComponent, decodeURIComponent, "decodeURIComponent") \
     macro(defineProperty, defineProperty, "defineProperty") \
     macro(defineGetter, defineGetter, "__defineGetter__") \
     macro(defineSetter, defineSetter, "__defineSetter__") \
     macro(delete, delete_, "delete") \
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -820,17 +820,17 @@ js::TypeOfValue(const Value &v)
  */
 static bool
 EnterWith(JSContext *cx, AbstractFramePtr frame, HandleValue val, uint32_t stackDepth)
 {
     RootedObject obj(cx);
     if (val.isObject()) {
         obj = &val.toObject();
     } else {
-        obj = js_ValueToNonNullObject(cx, val);
+        obj = ToObject(cx, val);
         if (!obj)
             return false;
     }
 
     RootedObject scopeChain(cx, frame.scopeChain());
     WithObject *withobj = WithObject::create(cx, obj, scopeChain, stackDepth);
     if (!withobj)
         return false;
@@ -1480,17 +1480,16 @@ BEGIN_CASE(JSOP_UNUSED175)
 BEGIN_CASE(JSOP_UNUSED176)
 BEGIN_CASE(JSOP_UNUSED177)
 BEGIN_CASE(JSOP_UNUSED178)
 BEGIN_CASE(JSOP_UNUSED179)
 BEGIN_CASE(JSOP_UNUSED180)
 BEGIN_CASE(JSOP_UNUSED181)
 BEGIN_CASE(JSOP_UNUSED182)
 BEGIN_CASE(JSOP_UNUSED183)
-BEGIN_CASE(JSOP_UNUSED188)
 BEGIN_CASE(JSOP_UNUSED189)
 BEGIN_CASE(JSOP_UNUSED190)
 BEGIN_CASE(JSOP_UNUSED200)
 BEGIN_CASE(JSOP_UNUSED201)
 BEGIN_CASE(JSOP_UNUSED208)
 BEGIN_CASE(JSOP_UNUSED209)
 BEGIN_CASE(JSOP_UNUSED210)
 BEGIN_CASE(JSOP_UNUSED219)
@@ -3045,31 +3044,36 @@ BEGIN_CASE(JSOP_INITELEM_INC)
 END_CASE(JSOP_INITELEM_INC)
 
 BEGIN_CASE(JSOP_SPREAD)
 {
     int32_t count = regs.sp[-2].toInt32();
     RootedObject &arr = rootObject0;
     arr = &regs.sp[-3].toObject();
     const Value iterable = regs.sp[-1];
-    ForOfIterator iter(cx, iterable);
+    ForOfIterator iter(cx);
     RootedValue &iterVal = rootValue0;
-    while (iter.next()) {
+    iterVal.set(iterable);
+    if (!iter.init(iterVal))
+        goto error;
+    while (true) {
+        bool done;
+        if (!iter.next(&iterVal, &done))
+            goto error;
+        if (done)
+            break;
         if (count == INT32_MAX) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                                  JSMSG_SPREAD_TOO_LARGE);
             goto error;
         }
-        iterVal = iter.value();
         if (!JSObject::defineElement(cx, arr, count++, iterVal, nullptr, nullptr,
                                      JSPROP_ENUMERATE))
             goto error;
     }
-    if (!iter.close())
-        goto error;
     regs.sp[-2].setInt32(count);
     regs.sp--;
 }
 END_CASE(JSOP_SPREAD)
 
 {
 BEGIN_CASE(JSOP_GOSUB)
     PUSH_BOOLEAN(false);
@@ -3176,16 +3180,17 @@ BEGIN_CASE(JSOP_DEBUGGER)
       default:;
     }
 }
 END_CASE(JSOP_DEBUGGER)
 
 BEGIN_CASE(JSOP_ENTERBLOCK)
 BEGIN_CASE(JSOP_ENTERLET0)
 BEGIN_CASE(JSOP_ENTERLET1)
+BEGIN_CASE(JSOP_ENTERLET2)
 {
     StaticBlockObject &blockObj = script->getObject(regs.pc)->as<StaticBlockObject>();
 
     if (op == JSOP_ENTERBLOCK) {
         JS_ASSERT(regs.stackDepth() == blockObj.stackDepth());
         JS_ASSERT(regs.stackDepth() + blockObj.slotCount() <= script->nslots);
         Value *vp = regs.sp + blockObj.slotCount();
         SetValueRangeToUndefined(regs.sp, vp);
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1798,17 +1798,17 @@ DebugScopes::onPopCall(AbstractFramePtr 
         if (!frame.copyRawFrameSlots(&vec) || vec.length() == 0)
             return;
 
         /*
          * Copy in formals that are not aliased via the scope chain
          * but are aliased via the arguments object.
          */
         RootedScript script(cx, frame.script());
-        if (script->needsArgsObj() && frame.hasArgsObj()) {
+        if (script->analyzedArgsUsage() && script->needsArgsObj() && frame.hasArgsObj()) {
             for (unsigned i = 0; i < frame.numFormalArgs(); ++i) {
                 if (script->formalLivesInArgumentsObject(i))
                     vec[i] = frame.argsObj().arg(i);
             }
         }
 
         /*
          * Use a dense array as storage (since proxies do not have trace
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -450,16 +450,30 @@ js::intrinsic_HaveSameClass(JSContext *c
     JS_ASSERT(args.length() == 2);
     JS_ASSERT(args[0].isObject());
     JS_ASSERT(args[1].isObject());
 
     args.rval().setBoolean(args[0].toObject().getClass() == args[1].toObject().getClass());
     return true;
 }
 
+static bool
+intrinsic_GetIteratorPrototype(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(args.length() == 0);
+
+    JSObject *obj = cx->global()->getOrCreateIteratorPrototype(cx);
+    if (!obj)
+        return false;
+
+    args.rval().setObject(*obj);
+    return true;
+}
+
 /*
  * ParallelTestsShouldPass(): Returns false if we are running in a
  * mode (such as --ion-eager) that is known to cause additional
  * bailouts or disqualifications for parallel array tests.
  *
  * This is needed because the parallel tests generally assert that,
  * under normal conditions, they will run without bailouts or
  * compilation failures, but this does not hold under "stress-testing"
@@ -528,16 +542,18 @@ const JSFunctionSpec intrinsic_functions
     JS_FN("DecompileArg",            intrinsic_DecompileArg,            2,0),
     JS_FN("RuntimeDefaultLocale",    intrinsic_RuntimeDefaultLocale,    0,0),
 
     JS_FN("UnsafePutElements",               intrinsic_UnsafePutElements,               3,0),
     JS_FN("UnsafeSetReservedSlot",   intrinsic_UnsafeSetReservedSlot,   3,0),
     JS_FN("UnsafeGetReservedSlot",   intrinsic_UnsafeGetReservedSlot,   2,0),
     JS_FN("HaveSameClass",           intrinsic_HaveSameClass,           2,0),
 
+    JS_FN("GetIteratorPrototype",    intrinsic_GetIteratorPrototype,    0,0),
+
     JS_FN("ForkJoin",                intrinsic_ForkJoin,                2,0),
     JS_FN("ForkJoinSlices",          intrinsic_ForkJoinSlices,          0,0),
     JS_FN("NewParallelArray",        intrinsic_NewParallelArray,        3,0),
     JS_FN("NewDenseArray",           intrinsic_NewDenseArray,           1,0),
     JS_FN("ShouldForceSequential",   intrinsic_ShouldForceSequential,   0,0),
     JS_FN("ParallelTestsShouldPass", intrinsic_ParallelTestsShouldPass, 0,0),
 
     // See builtin/Intl.h for descriptions of the intl_* functions.
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -3421,25 +3421,27 @@ const JSFunctionSpec ArrayBufferObject::
 
 /*
  * TypedArrayObject boilerplate
  */
 
 #ifndef RELEASE_BUILD
 # define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                     \
 const JSFunctionSpec _typedArray##Object::jsfuncs[] = {                            \
+    JS_SELF_HOSTED_FN("@@iterator", "ArrayIterator", 0, 0),                        \
     JS_FN("iterator", JS_ArrayIterator, 0, 0),                                     \
     JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \
     JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE),           \
     JS_FN("move", _typedArray##Object::fun_move, 3, JSFUN_GENERIC_NATIVE),         \
     JS_FS_END                                                                      \
 }
 #else
 # define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                     \
 const JSFunctionSpec _typedArray##Object::jsfuncs[] = {                            \
+    JS_SELF_HOSTED_FN("@@iterator", "ArrayIterator", 0, 0),                        \
     JS_FN("iterator", JS_ArrayIterator, 0, 0),                                     \
     JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \
     JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE),           \
     JS_FS_END                                                                      \
 }
 #endif
 
 #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType)                                 \
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -17,17 +17,17 @@ namespace js {
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number is XDR'd near the front of xdr bytecode and
  * aborts deserialization if there is a mismatch between the current
  * and saved versions. If deserialization fails, the data should be
  * invalidated if possible.
  */
-static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 152);
+static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 153);
 
 class XDRBuffer {
   public:
     XDRBuffer(JSContext *cx)
       : context(cx), base(nullptr), cursor(nullptr), limit(nullptr) { }
 
     JSContext *cx() const {
         return context;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -65,16 +65,17 @@
 #include "nsFontFaceList.h"
 #include "nsFontInflationData.h"
 #include "nsSVGUtils.h"
 #include "nsSVGTextFrame2.h"
 #include "nsStyleStructInlines.h"
 #include "nsStyleTransformMatrix.h"
 #include "nsIFrameInlines.h"
 #include "ImageContainer.h"
+#include "nsComputedDOMStyle.h"
 
 #include "mozilla/Preferences.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
 #include "GeckoProfiler.h"
@@ -425,16 +426,32 @@ nsLayoutUtils::CSSFiltersEnabled()
     Preferences::AddBoolVarCache(&sCSSFiltersEnabled,
                                  "layout.css.filters.enabled",
                                  false);
   }
 
   return sCSSFiltersEnabled;
 }
 
+bool
+nsLayoutUtils::UnsetValueEnabled()
+{
+  static bool sUnsetValueEnabled;
+  static bool sUnsetValuePrefCached = false;
+
+  if (!sUnsetValuePrefCached) {
+    sUnsetValuePrefCached = true;
+    Preferences::AddBoolVarCache(&sUnsetValueEnabled,
+                                 "layout.css.unset-value.enabled",
+                                 false);
+  }
+
+  return sUnsetValueEnabled;
+}
+
 void
 nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame,
                                   nsOverflowAreas& aOverflowAreas)
 {
   // Iterate over all children except pop-ups.
   const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
                                     nsIFrame::kSelectPopupList);
   for (nsIFrame::ChildListIterator childLists(aFrame);
@@ -5053,31 +5070,35 @@ nsLayoutUtils::Initialize()
                                "nglayout.debug.invalidation");
 
   Preferences::RegisterCallback(FlexboxEnabledPrefChangeCallback,
                                 FLEXBOX_ENABLED_PREF_NAME);
   FlexboxEnabledPrefChangeCallback(FLEXBOX_ENABLED_PREF_NAME, nullptr);
   Preferences::RegisterCallback(StickyEnabledPrefChangeCallback,
                                 STICKY_ENABLED_PREF_NAME);
   StickyEnabledPrefChangeCallback(STICKY_ENABLED_PREF_NAME, nullptr);
+
+  nsComputedDOMStyle::RegisterPrefChangeCallbacks();
 }
 
 /* static */
 void
 nsLayoutUtils::Shutdown()
 {
   if (sContentMap) {
     delete sContentMap;
     sContentMap = nullptr;
   }
 
   Preferences::UnregisterCallback(FlexboxEnabledPrefChangeCallback,
                                   FLEXBOX_ENABLED_PREF_NAME);
   Preferences::UnregisterCallback(StickyEnabledPrefChangeCallback,
                                   STICKY_ENABLED_PREF_NAME);
+
+  nsComputedDOMStyle::UnregisterPrefChangeCallbacks();
 }
 
 /* static */
 void
 nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext,
                                     imgIRequest* aRequest,
                                     bool* aRequestRegistered)
 {
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1643,16 +1643,21 @@ public:
   static bool AnimatedImageLayersEnabled();
 
   /**
    * Checks if we should enable parsing for CSS Filters.
    */
   static bool CSSFiltersEnabled();
 
   /**
+   * Checks whether support for the CSS-wide "unset" value is enabled.
+   */
+  static bool UnsetValueEnabled();
+
+  /**
    * Unions the overflow areas of all non-popup children of aFrame with
    * aOverflowAreas.
    */
   static void UnionChildOverflow(nsIFrame* aFrame,
                                  nsOverflowAreas& aOverflowAreas);
 
   /**
    * Return the font size inflation *ratio* for a given frame.  This is
--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -156,28 +156,28 @@ Declaration::GetValue(nsCSSProperty aPro
   //   to the ruleset without changing in any way the rules already
   //   declared in the ruleset (i.e., by adding longhand rules that were
   //   previously not declared in the ruleset), then the empty string
   //   should be returned for the shorthand property.
   // This means we need to check a number of cases:
   //   (1) Since a shorthand sets all sub-properties, if some of its
   //       subproperties were not specified, we must return the empty
   //       string.
-  //   (2) Since 'inherit' and 'initial' can only be specified as the
-  //       values for entire properties, we need to return the empty
-  //       string if some but not all of the subproperties have one of
-  //       those values.
+  //   (2) Since 'inherit', 'initial' and 'unset' can only be specified
+  //       as the values for entire properties, we need to return the
+  //       empty string if some but not all of the subproperties have one
+  //       of those values.
   //   (3) Since a single value only makes sense with or without
   //       !important, we return the empty string if some values are
   //       !important and some are not.
   // Since we're doing this check for 'inherit' and 'initial' up front,
   // we can also simplify the property serialization code by serializing
   // those values up front as well.
   uint32_t totalCount = 0, importantCount = 0,
-           initialCount = 0, inheritCount = 0;
+           initialCount = 0, inheritCount = 0, unsetCount = 0;
   CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty) {
     if (*p == eCSSProperty__x_system_font ||
          nsCSSProps::PropHasFlags(*p, CSS_PROPERTY_DIRECTIONAL_SOURCE)) {
       // The system-font subproperty and the *-source properties don't count.
       continue;
     }
     ++totalCount;
     const nsCSSValue *val = mData->ValueFor(*p);
@@ -190,34 +190,41 @@ Declaration::GetValue(nsCSSProperty aPro
     if (!val) {
       // Case (1) above: some subproperties not specified.
       return;
     }
     if (val->GetUnit() == eCSSUnit_Inherit) {
       ++inheritCount;
     } else if (val->GetUnit() == eCSSUnit_Initial) {
       ++initialCount;
+    } else if (val->GetUnit() == eCSSUnit_Unset) {
+      ++unsetCount;
     }
   }
   if (importantCount != 0 && importantCount != totalCount) {
     // Case (3), no consistent importance.
     return;
   }
   if (initialCount == totalCount) {
     // Simplify serialization below by serializing initial up-front.
     nsCSSValue(eCSSUnit_Initial).AppendToString(eCSSProperty_UNKNOWN, aValue);
     return;
   }
   if (inheritCount == totalCount) {
     // Simplify serialization below by serializing inherit up-front.
     nsCSSValue(eCSSUnit_Inherit).AppendToString(eCSSProperty_UNKNOWN, aValue);
     return;
   }
-  if (initialCount != 0 || inheritCount != 0) {
-    // Case (2): partially initial or inherit.
+  if (unsetCount == totalCount) {
+    // Simplify serialization below by serializing unset up-front.
+    nsCSSValue(eCSSUnit_Unset).AppendToString(eCSSProperty_UNKNOWN, aValue);
+    return;
+  }
+  if (initialCount != 0 || inheritCount != 0 || unsetCount != 0) {
+    // Case (2): partially initial, inherit or unset.
     return;
   }
 
   nsCSSCompressedDataBlock *data = importantCount ? mImportantData : mData;
   switch (aProperty) {
     case eCSSProperty_margin: 
     case eCSSProperty_padding: 
     case eCSSProperty_border_color: 
@@ -847,16 +854,22 @@ Declaration::GetValue(nsCSSProperty aPro
       // shorthands that are just aliases with different parsing rules
       const nsCSSProperty* subprops =
         nsCSSProps::SubpropertyEntryFor(aProperty);
       NS_ABORT_IF_FALSE(subprops[1] == eCSSProperty_UNKNOWN,
                         "must have exactly one subproperty");
       AppendValueToString(subprops[0], aValue);
       break;
     }
+    case eCSSProperty_all:
+      // If we got here, then we didn't have all "inherit" or "initial" or
+      // "unset" values for all of the longhand property components of 'all'.
+      // There is no other possible value that is valid for all properties,
+      // so serialize as the empty string.
+      break;
     default:
       NS_ABORT_IF_FALSE(false, "no other shorthands");
       break;
   }
 }
 
 bool
 Declaration::GetValueIsImportant(const nsAString& aProperty) const
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -24,16 +24,17 @@ EXPORTS += [
     'nsCSSProps.h',
     'nsCSSPseudoClassList.h',
     'nsCSSPseudoClasses.h',
     'nsCSSPseudoElementList.h',
     'nsCSSPseudoElements.h',
     'nsCSSRuleProcessor.h',
     'nsCSSStyleSheet.h',
     'nsCSSValue.h',
+    'nsComputedDOMStylePropertyList.h',
     'nsDOMCSSAttrDeclaration.h',
     'nsDOMCSSDeclaration.h',
     'nsDOMCSSRGBColor.h',
     'nsDOMMediaQueryList.h',
     'nsICSSDeclaration.h',
     'nsICSSLoaderObserver.h',
     'nsICSSPseudoComparator.h',
     'nsICSSRuleList.h',
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -528,16 +528,17 @@ CSS_KEY(translatex, translatex)
 CSS_KEY(translatey, translatey)
 CSS_KEY(translatez, translatez)
 CSS_KEY(transparent, transparent) // for nsComputedDOMStyle only
 CSS_KEY(tri-state, tri_state)
 CSS_KEY(ultra-condensed, ultra_condensed)
 CSS_KEY(ultra-expanded, ultra_expanded)
 CSS_KEY(underline, underline)
 CSS_KEY(unicase, unicase)
+CSS_KEY(unset, unset)
 CSS_KEY(upper-alpha, upper_alpha)
 CSS_KEY(upper-latin, upper_latin)
 CSS_KEY(upper-roman, upper_roman)
 CSS_KEY(uppercase, uppercase)
 CSS_KEY(upright, upright)
 CSS_KEY(vertical, vertical)
 CSS_KEY(vertical-lr, vertical_lr)
 CSS_KEY(vertical-rl, vertical_rl)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -531,16 +531,17 @@ protected:
                                         size_t aNumProperties);
   bool ParseTransition();
   bool ParseAnimation();
 
   bool ParsePaint(nsCSSProperty aPropID);
   bool ParseDasharray();
   bool ParseMarker();
   bool ParsePaintOrder();
+  bool ParseAll();
 
   // Reused utility parsing routines
   void AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue);
   bool ParseBoxProperties(const nsCSSProperty aPropIDs[]);
   bool ParseGroupedBoxProperty(int32_t aVariantMask,
                                nsCSSValue& aValue);
   bool ParseDirectionalBoxProperty(nsCSSProperty aProperty,
                                      int32_t aSourceType);
@@ -5081,20 +5082,25 @@ CSSParserImpl::ParseVariant(nsCSSValue& 
       if ((aVariantMask & VARIANT_INHERIT) != 0) {
         // XXX Should we check IsParsingCompoundProperty, or do all
         // callers handle it?  (Not all callers set it, though, since
         // they want the quirks that are disabled by setting it.)
         if (eCSSKeyword_inherit == keyword) {
           aValue.SetInheritValue();
           return true;
         }
-        else if (eCSSKeyword_initial == keyword) { // anything that can inherit can also take an initial val.
+        else if (eCSSKeyword_initial == keyword) {
           aValue.SetInitialValue();
           return true;
         }
+        else if (eCSSKeyword_unset == keyword &&
+                 nsLayoutUtils::UnsetValueEnabled()) {
+          aValue.SetUnsetValue();
+          return true;
+        }
       }
       if ((aVariantMask & VARIANT_NONE) != 0) {
         if (eCSSKeyword_none == keyword) {
           aValue.SetNoneValue();
           return true;
         }
       }
       if ((aVariantMask & VARIANT_ALL) != 0) {
@@ -5255,17 +5261,19 @@ CSSParserImpl::ParseVariant(nsCSSValue& 
     aValue.SetStringValue(buffer, eCSSUnit_String);
     return true;
   }
   if (((aVariantMask &
         (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) &&
       (eCSSToken_Ident == tk->mType) &&
       ((aVariantMask & VARIANT_IDENTIFIER) != 0 ||
        !(tk->mIdent.LowerCaseEqualsLiteral("inherit") ||
-         tk->mIdent.LowerCaseEqualsLiteral("initial")))) {
+         tk->mIdent.LowerCaseEqualsLiteral("initial") ||
+         (tk->mIdent.LowerCaseEqualsLiteral("unset") &&
+          nsLayoutUtils::UnsetValueEnabled())))) {
     aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident);
     return true;
   }
   if (((aVariantMask & VARIANT_COUNTER) != 0) &&
       (eCSSToken_Function == tk->mType) &&
       (tk->mIdent.LowerCaseEqualsLiteral("counter") ||
        tk->mIdent.LowerCaseEqualsLiteral("counters"))) {
     return ParseCounter(aValue);
@@ -5461,17 +5469,17 @@ CSSParserImpl::SetValueToURL(nsCSSValue&
 /**
  * Parse the image-orientation property, which has the grammar:
  * <angle> flip? | flip | from-image
  */
 bool
 CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue)
 {
   if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
-    // 'inherit' and 'initial' must be alone
+    // 'inherit', 'initial' and 'unset' must be alone
     return true;
   }
 
   // Check for an angle with optional 'flip'.
   nsCSSValue angle;
   if (ParseVariant(angle, VARIANT_ANGLE, nullptr)) {
     nsCSSValue flip;
 
@@ -5573,17 +5581,17 @@ CSSParserImpl::ParseElement(nsCSSValue& 
   SkipUntil(')');
   return false;
 }
 
 // flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
 bool
 CSSParserImpl::ParseFlex()
 {
-  // First check for inherit / initial
+  // First check for inherit / initial / unset
   nsCSSValue tmpVal;
   if (ParseVariant(tmpVal, VARIANT_INHERIT, nullptr)) {
     AppendValue(eCSSProperty_flex_grow, tmpVal);
     AppendValue(eCSSProperty_flex_shrink, tmpVal);
     AppendValue(eCSSProperty_flex_basis, tmpVal);
     return true;
   }
 
@@ -6076,27 +6084,37 @@ CSSParserImpl::ParseChoice(nsCSSValue aV
         found = ((1 << aNumIDs) - 1);
       }
       else if (eCSSUnit_Initial == aValues[0].GetUnit()) { // one initial, all initial
         for (loop = 1; loop < aNumIDs; loop++) {
           aValues[loop].SetInitialValue();
         }
         found = ((1 << aNumIDs) - 1);
       }
-    }
-    else {  // more than one value, verify no inherits or initials
+      else if (eCSSUnit_Unset == aValues[0].GetUnit()) { // one unset, all unset
+        for (loop = 1; loop < aNumIDs; loop++) {
+          aValues[loop].SetUnsetValue();
+        }
+        found = ((1 << aNumIDs) - 1);
+      }
+    }
+    else {  // more than one value, verify no inherits, initials or unsets
       for (loop = 0; loop < aNumIDs; loop++) {
         if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
           found = -1;
           break;
         }
         else if (eCSSUnit_Initial == aValues[loop].GetUnit()) {
           found = -1;
           break;
         }
+        else if (eCSSUnit_Unset == aValues[loop].GetUnit()) {
+          found = -1;
+          break;
+        }
       }
     }
   }
   return found;
 }
 
 void
 CSSParserImpl::AppendValue(nsCSSProperty aPropID, const nsCSSValue& aValue)
@@ -6121,20 +6139,22 @@ CSSParserImpl::ParseBoxProperties(const 
       break;
     }
     count++;
   }
   if ((count == 0) || (false == ExpectEndProperty())) {
     return false;
   }
 
-  if (1 < count) { // verify no more than single inherit or initial
+  if (1 < count) { // verify no more than single inherit, initial or unset
     NS_FOR_CSS_SIDES (index) {
       nsCSSUnit unit = (result.*(nsCSSRect::sides[index])).GetUnit();
-      if (eCSSUnit_Inherit == unit || eCSSUnit_Initial == unit) {
+      if (eCSSUnit_Inherit == unit ||
+          eCSSUnit_Initial == unit ||
+          eCSSUnit_Unset == unit) {
         return false;
       }
     }
   }
 
   // Provide missing values by replicating some of the values found
   switch (count) {
     case 1: // Make right == top
@@ -6207,19 +6227,20 @@ CSSParserImpl::ParseDirectionalBoxProper
 bool
 CSSParserImpl::ParseBoxCornerRadius(nsCSSProperty aPropID)
 {
   nsCSSValue dimenX, dimenY;
   // required first value
   if (! ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr))
     return false;
 
-  // optional second value (forbidden if first value is inherit/initial)
+  // optional second value (forbidden if first value is inherit/initial/unset)
   if (dimenX.GetUnit() != eCSSUnit_Inherit &&
-      dimenX.GetUnit() != eCSSUnit_Initial) {
+      dimenX.GetUnit() != eCSSUnit_Initial &&
+      dimenX.GetUnit() != eCSSUnit_Unset) {
     ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr);
   }
 
   if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) {
     AppendValue(aPropID, dimenX);
   } else {
     nsCSSValue value;
     value.SetPairValue(dimenX, dimenY);
@@ -6256,20 +6277,22 @@ CSSParserImpl::ParseBoxCornerRadii(const
       countY++;
     }
     if (countY == 0)
       return false;
   }
   if (!ExpectEndProperty())
     return false;
 
-  // if 'initial' or 'inherit' was used, it must be the only value
+  // if 'initial', 'inherit' or 'unset' was used, it must be the only value
   if (countX > 1 || countY > 0) {
     nsCSSUnit unit = dimenX.mTop.GetUnit();
-    if (eCSSUnit_Inherit == unit || eCSSUnit_Initial == unit)
+    if (eCSSUnit_Inherit == unit ||
+        eCSSUnit_Initial == unit ||
+        eCSSUnit_Unset == unit)
       return false;
   }
 
   // if we have no Y-values, use the X-values
   if (countY == 0) {
     dimenY = dimenX;
     countY = countX;
   }
@@ -6583,16 +6606,18 @@ CSSParserImpl::ParsePropertyByFunction(n
   case eCSSProperty_stroke:
     return ParsePaint(aPropID);
   case eCSSProperty_stroke_dasharray:
     return ParseDasharray();
   case eCSSProperty_marker:
     return ParseMarker();
   case eCSSProperty_paint_order:
     return ParsePaintOrder();
+  case eCSSProperty_all:
+    return ParseAll();
   default:
     NS_ABORT_IF_FALSE(false, "should not be called");
     return false;
   }
 }
 
 // Bits used in determining which background position info we have
 #define BG_CENTER  NS_STYLE_BG_POSITION_CENTER
@@ -6736,16 +6761,17 @@ CSSParserImpl::ParseFontDescriptorValue(
     // property is VARIANT_HMK|VARIANT_SYSFONT
     return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL,
                         nsCSSProps::kFontStyleKTable);
 
   case eCSSFontDesc_Weight:
     return (ParseFontWeight(aValue) &&
             aValue.GetUnit() != eCSSUnit_Inherit &&
             aValue.GetUnit() != eCSSUnit_Initial &&
+            aValue.GetUnit() != eCSSUnit_Unset &&
             (aValue.GetUnit() != eCSSUnit_Enumerated ||
              (aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_BOLDER &&
               aValue.GetIntValue() != NS_STYLE_FONT_WEIGHT_LIGHTER)));
 
   case eCSSFontDesc_Stretch:
     // property is VARIANT_HK|VARIANT_SYSFONT
     return ParseVariant(aValue, VARIANT_KEYWORD,
                         nsCSSProps::kFontStretchKTable);
@@ -6809,17 +6835,17 @@ BoxPositionMaskToCSSValue(int32_t aMask,
 bool
 CSSParserImpl::ParseBackground()
 {
   nsAutoParseCompoundProperty compound(this);
 
   // background-color can only be set once, so it's not a list.
   nsCSSValue color;
 
-  // Check first for inherit/initial.
+  // Check first for inherit/initial/unset.
   if (ParseVariant(color, VARIANT_INHERIT, nullptr)) {
     // must be alone
     if (!ExpectEndProperty()) {
       return false;
     }
     for (const nsCSSProperty* subprops =
            nsCSSProps::SubpropertyEntryFor(eCSSProperty_background);
          *subprops != eCSSProperty_UNKNOWN; ++subprops) {
@@ -6924,17 +6950,18 @@ CSSParserImpl::ParseBackgroundItem(CSSPa
       // show up as one.
       break;
     }
 
     if (tt == eCSSToken_Ident) {
       nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
       int32_t dummy;
       if (keyword == eCSSKeyword_inherit ||
-          keyword == eCSSKeyword_initial) {
+          keyword == eCSSKeyword_initial ||
+          keyword == eCSSKeyword_unset) {
         return false;
       } else if (keyword == eCSSKeyword_none) {
         if (haveImage)
           return false;
         haveImage = true;
         if (!ParseSingleValueProperty(aState.mImage->mValue,
                                       eCSSProperty_background_image)) {
           NS_NOTREACHED("should be able to parse");
@@ -7061,17 +7088,17 @@ CSSParserImpl::ParseBackgroundItem(CSSPa
         }
         aState.mSize->mXValue = scratch.mXValue;
         aState.mSize->mYValue = scratch.mYValue;
       }
     } else {
       if (haveColor)
         return false;
       haveColor = true;
-      // Note: This parses 'inherit' and 'initial', but
+      // Note: This parses 'inherit', 'initial' and 'unset', but
       // we've already checked for them, so it's ok.
       if (!ParseSingleValueProperty(aState.mColor,
                                     eCSSProperty_background_color)) {
         return false;
       }
     }
     haveSomething = true;
   }
@@ -7082,17 +7109,17 @@ CSSParserImpl::ParseBackgroundItem(CSSPa
 // This function is very similar to ParseBackgroundPosition and
 // ParseBackgroundSize.
 bool
 CSSParserImpl::ParseValueList(nsCSSProperty aPropID)
 {
   // aPropID is a single value prop-id
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
-    // 'initial' and 'inherit' stand alone, no list permitted.
+    // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
     nsCSSValueList* item = value.SetListValue();
     for (;;) {
       if (!ParseSingleValueProperty(item->mValue, aPropID)) {
         return false;
@@ -7111,17 +7138,17 @@ CSSParserImpl::ParseValueList(nsCSSPrope
   return true;
 }
 
 bool
 CSSParserImpl::ParseBackgroundRepeat()
 {
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
-    // 'initial' and 'inherit' stand alone, no list permitted.
+    // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
     nsCSSValuePair valuePair;
     if (!ParseBackgroundRepeatValues(valuePair)) {
       return false;
     }
@@ -7170,17 +7197,17 @@ CSSParserImpl::ParseBackgroundRepeatValu
 }
 
 // This function is very similar to ParseBackgroundList and ParseBackgroundSize.
 bool
 CSSParserImpl::ParseBackgroundPosition()
 {
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
-    // 'initial' and 'inherit' stand alone, no list permitted.
+    // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
     nsCSSValue itemValue;
     if (!ParseBackgroundPositionValues(itemValue, false)) {
       return false;
     }
@@ -7211,32 +7238,34 @@ CSSParserImpl::ParseBackgroundPosition()
  * takes up to four values.)  Some current CSS specifications that
  * use background-position-like syntax still use this old syntax.
  **
  * Parses two values that correspond to positions in a box.  These can be
  * values corresponding to percentages of the box, raw offsets, or keywords
  * like "top," "left center," etc.
  *
  * @param aOut The nsCSSValuePair in which to place the result.
- * @param aAcceptsInherit If true, 'inherit' and 'initial' are legal values
+ * @param aAcceptsInherit If true, 'inherit', 'initial' and 'unset' are
+ *   legal values
  * @param aAllowExplicitCenter If true, 'center' is a legal value
  * @return Whether or not the operation succeeded.
  */
 bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut,
                                            bool aAcceptsInherit,
                                            bool aAllowExplicitCenter)
 {
   // First try a percentage or a length value
   nsCSSValue &xValue = aOut.mXValue,
              &yValue = aOut.mYValue;
   int32_t variantMask =
     (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC;
   if (ParseVariant(xValue, variantMask, nullptr)) {
     if (eCSSUnit_Inherit == xValue.GetUnit() ||
-        eCSSUnit_Initial == xValue.GetUnit()) {  // both are inherited or both are set to initial
+        eCSSUnit_Initial == xValue.GetUnit() ||
+        eCSSUnit_Unset == xValue.GetUnit()) {  // both are inherit, initial or unset
       yValue = xValue;
       return true;
     }
     // We have one percentage/length/calc. Get the optional second
     // percentage/length/calc/keyword.
     if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) {
       // We have two numbers
       return true;
@@ -7488,17 +7517,17 @@ bool CSSParserImpl::ParseBackgroundPosit
 
 // This function is very similar to ParseBackgroundList and
 // ParseBackgroundPosition.
 bool
 CSSParserImpl::ParseBackgroundSize()
 {
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
-    // 'initial' and 'inherit' stand alone, no list permitted.
+    // 'initial', 'inherit' and 'unset' stand alone, no list permitted.
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
     nsCSSValuePair valuePair;
     if (!ParseBackgroundSizeValues(valuePair)) {
       return false;
     }
@@ -7522,18 +7551,18 @@ CSSParserImpl::ParseBackgroundSize()
   AppendValue(eCSSProperty_background_size, value);
   return true;
 }
 
 /**
  * Parses two values that correspond to lengths for the background-size
  * property.  These can be one or two lengths (or the 'auto' keyword) or
  * percentages corresponding to the element's dimensions or the single keywords
- * 'contain' or 'cover'.  'initial' and 'inherit' must be handled by the caller
- * if desired.
+ * 'contain' or 'cover'.  'initial', 'inherit' and 'unset' must be handled by
+ * the caller if desired.
  *
  * @param aOut The nsCSSValuePair in which to place the result.
  * @return Whether or not the operation succeeded.
  */
 #define BG_SIZE_VARIANT (VARIANT_LP | VARIANT_AUTO | VARIANT_CALC)
 bool CSSParserImpl::ParseBackgroundSizeValues(nsCSSValuePair &aOut)
 {
   // First try a percentage or a length value
@@ -7622,17 +7651,18 @@ CSSParserImpl::ParseBorderImageSlice(boo
   // border-image-slice: initial | [<number>|<percentage>]{1,4} && fill?
   nsCSSValue value;
 
   if (aConsumedTokens) {
     *aConsumedTokens = true;
   }
 
   if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
-    // Keyword "inherit" can not be mixed, so we are done.
+    // Keywords "inherit", "initial" and "unset" can not be mixed, so we
+    // are done.
     AppendValue(eCSSProperty_border_image_slice, value);
     return true;
   }
 
   // Try parsing "fill" value.
   nsCSSValue imageSliceFillValue;
   bool hasFill = ParseEnum(imageSliceFillValue,
                            nsCSSProps::kBorderImageSliceKTable);
@@ -7670,17 +7700,18 @@ CSSParserImpl::ParseBorderImageSlice(boo
 
 bool
 CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit)
 {
   // border-image-width: initial | [<length>|<number>|<percentage>|auto]{1,4}
   nsCSSValue value;
 
   if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
-    // Keyword "inherit" can no be mixed, so we are done.
+    // Keywords "inherit", "initial" and "unset" can not be mixed, so we
+    // are done.
     AppendValue(eCSSProperty_border_image_width, value);
     return true;
   }
 
   // Parse the box dimensions.
   if (!ParseGroupedBoxProperty(VARIANT_ALPN, value)) {
     return false;
   }
@@ -7691,17 +7722,18 @@ CSSParserImpl::ParseBorderImageWidth(boo
 
 bool
 CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit)
 {
   // border-image-outset: initial | [<length>|<number>]{1,4}
   nsCSSValue value;
 
   if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
-    // Keyword "inherit" can not be mixed, so we are done.
+    // Keywords "inherit", "initial" and "unset" can not be mixed, so we
+    // are done.
     AppendValue(eCSSProperty_border_image_outset, value);
     return true;
   }
 
   // Parse the box dimensions.
   if (!ParseGroupedBoxProperty(VARIANT_LN, value)) {
     return false;
   }
@@ -7710,17 +7742,18 @@ CSSParserImpl::ParseBorderImageOutset(bo
   return true;
 }
 
 bool
 CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit)
 {
   nsCSSValue value;
   if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) {
-    // Keyword "inherit" can not be mixed, so we are done.
+    // Keywords "inherit", "initial" and "unset" can not be mixed, so we
+    // are done.
     AppendValue(eCSSProperty_border_image_repeat, value);
     return true;
   }
 
   nsCSSValuePair result;
   if (!ParseEnum(result.mXValue, nsCSSProps::kBorderImageRepeatKTable)) {
     return false;
   }
@@ -7749,17 +7782,17 @@ CSSParserImpl::ParseBorderImage()
 
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
     AppendValue(eCSSProperty_border_image_source, value);
     AppendValue(eCSSProperty_border_image_slice, value);
     AppendValue(eCSSProperty_border_image_width, value);
     AppendValue(eCSSProperty_border_image_outset, value);
     AppendValue(eCSSProperty_border_image_repeat, value);
-    // Keyword "inherit" (and "initial") can't be mixed, so we are done.
+    // Keywords "inherit", "initial" and "unset" can't be mixed, so we are done.
     return true;
   }
 
   // No empty property.
   if (CheckEndProperty()) {
     return false;
   }
 
@@ -7914,18 +7947,19 @@ CSSParserImpl::ParseBorderSide(const nsC
     };
 
     // Set the other properties that the border shorthand sets to their
     // initial values.
     nsCSSValue extraValue;
     switch (values[0].GetUnit()) {
     case eCSSUnit_Inherit:
     case eCSSUnit_Initial:
+    case eCSSUnit_Unset:
       extraValue = values[0];
-      // Set value of border-image properties to initial/inherit
+      // Set value of border-image properties to initial/inherit/unset
       AppendValue(eCSSProperty_border_image_source, extraValue);
       AppendValue(eCSSProperty_border_image_slice, extraValue);
       AppendValue(eCSSProperty_border_image_width, extraValue);
       AppendValue(eCSSProperty_border_image_outset, extraValue);
       AppendValue(eCSSProperty_border_image_repeat, extraValue);
       break;
     default:
       extraValue.SetNoneValue();
@@ -8011,17 +8045,17 @@ CSSParserImpl::ParseBorderWidth()
   return ParseBoxProperties(kBorderWidthIDs);
 }
 
 bool
 CSSParserImpl::ParseBorderColors(nsCSSProperty aProperty)
 {
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
-    // 'inherit', 'initial', and 'none' are only allowed on their own
+    // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
     nsCSSValueList *cur = value.SetListValue();
     for (;;) {
       if (!ParseVariant(cur->mValue, VARIANT_COLOR | VARIANT_KEYWORD,
                         nsCSSProps::kBorderColorKTable)) {
@@ -8325,16 +8359,25 @@ CSSParserImpl::ParseRect(nsCSSProperty a
         val.SetInheritValue();
         break;
       case eCSSKeyword_initial:
         if (!ExpectEndProperty()) {
           return false;
         }
         val.SetInitialValue();
         break;
+      case eCSSKeyword_unset:
+        if (nsLayoutUtils::UnsetValueEnabled()) {
+          if (!ExpectEndProperty()) {
+            return false;
+          }
+          val.SetUnsetValue();
+          break;
+        }
+        // fall through
       default:
         UngetToken();
         return false;
     }
   } else if (mToken.mType == eCSSToken_Function &&
              mToken.mIdent.LowerCaseEqualsLiteral("rect")) {
     nsCSSRect& rect = val.SetRectValue();
     bool useCommas;
@@ -8439,17 +8482,18 @@ CSSParserImpl::ParseContent()
                     nsCSSProps::kContentKTable[
                       ArrayLength(kContentListKWs) +
                       ArrayLength(kContentSolitaryKWs) - 3] == -1,
                     "content keyword tables out of sync");
 
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_HMK | VARIANT_NONE,
                    kContentSolitaryKWs)) {
-    // 'inherit', 'initial', 'normal', 'none', and 'alt-content' must be alone
+    // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must
+    // be alone
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
     nsCSSValueList* cur = value.SetListValue();
     for (;;) {
       if (!ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs)) {
         return false;
@@ -8499,17 +8543,17 @@ CSSParserImpl::ParseCounterData(nsCSSPro
   return true;
 }
 
 bool
 CSSParserImpl::ParseCursor()
 {
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT, nullptr)) {
-    // 'inherit' and 'initial' must be alone
+    // 'inherit', 'initial' and 'unset' must be alone
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
     nsCSSValueList* cur = value.SetListValue();
     for (;;) {
       if (!ParseVariant(cur->mValue, VARIANT_UK, nsCSSProps::kCursorKTable)) {
         return false;
@@ -8557,17 +8601,18 @@ CSSParserImpl::ParseFont()
 
   // font-variant-alternates enabled ==> layout.css.font-features.enabled is true
   bool featuresEnabled =
     nsCSSProps::IsEnabled(eCSSProperty_font_variant_alternates);
   nsCSSValue  family;
   if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) {
     if (ExpectEndProperty()) {
       if (eCSSUnit_Inherit == family.GetUnit() ||
-          eCSSUnit_Initial == family.GetUnit()) {
+          eCSSUnit_Initial == family.GetUnit() ||
+          eCSSUnit_Unset == family.GetUnit()) {
         AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
         AppendValue(eCSSProperty_font_family, family);
         AppendValue(eCSSProperty_font_style, family);
         AppendValue(eCSSProperty_font_variant, family);
         AppendValue(eCSSProperty_font_weight, family);
         AppendValue(eCSSProperty_font_size, family);
         AppendValue(eCSSProperty_line_height, family);
         AppendValue(eCSSProperty_font_stretch, family);
@@ -8613,18 +8658,20 @@ CSSParserImpl::ParseFont()
     }
     return false;
   }
 
   // Get optional font-style, font-variant and font-weight (in any order)
   const int32_t numProps = 3;
   nsCSSValue  values[numProps];
   int32_t found = ParseChoice(values, fontIDs, numProps);
-  if ((found < 0) || (eCSSUnit_Inherit == values[0].GetUnit()) ||
-      (eCSSUnit_Initial == values[0].GetUnit())) { // illegal data
+  if (found < 0 ||
+      eCSSUnit_Inherit == values[0].GetUnit() ||
+      eCSSUnit_Initial == values[0].GetUnit() ||
+      eCSSUnit_Unset == values[0].GetUnit()) { // illegal data
     return false;
   }
   if ((found & 1) == 0) {
     // Provide default font-style
     values[0].SetIntValue(NS_FONT_STYLE_NORMAL, eCSSUnit_Enumerated);
   }
   if ((found & 2) == 0) {
     // Provide default font-variant
@@ -8653,17 +8700,19 @@ CSSParserImpl::ParseFont()
   }
   else {
     lineHeight.SetNormalValue();
   }
 
   // Get final mandatory font-family
   nsAutoParseCompoundProperty compound(this);
   if (ParseFamily(family)) {
-    if ((eCSSUnit_Inherit != family.GetUnit()) && (eCSSUnit_Initial != family.GetUnit()) &&
+    if (eCSSUnit_Inherit != family.GetUnit() &&
+        eCSSUnit_Initial != family.GetUnit() &&
+        eCSSUnit_Unset != family.GetUnit() &&
         ExpectEndProperty()) {
       AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None));
       AppendValue(eCSSProperty_font_family, family);
       AppendValue(eCSSProperty_font_style, values[0]);
       AppendValue(eCSSProperty_font_variant, values[1]);
       AppendValue(eCSSProperty_font_weight, values[2]);
       AppendValue(eCSSProperty_font_size, size);
       AppendValue(eCSSProperty_line_height, lineHeight);
@@ -8702,17 +8751,18 @@ CSSParserImpl::ParseFontSynthesis(nsCSSV
   if (!ParseVariant(aValue, VARIANT_HK | VARIANT_NONE,
                     nsCSSProps::kFontSynthesisKTable)) {
     return false;
   }
 
   // first value 'none' ==> done
   if (eCSSUnit_None == aValue.GetUnit() ||
       eCSSUnit_Initial == aValue.GetUnit() ||
-      eCSSUnit_Inherit == aValue.GetUnit())
+      eCSSUnit_Inherit == aValue.GetUnit() ||
+      eCSSUnit_Unset == aValue.GetUnit())
   {
     return true;
   }
 
   // look for a second value
   int32_t intValue = aValue.GetIntValue();
   nsCSSValue nextValue;
 
@@ -8852,20 +8902,21 @@ bool
 CSSParserImpl::ParseBitmaskValues(nsCSSValue& aValue,
                                   const int32_t aKeywordTable[],
                                   const int32_t aMasks[])
 {
   if (!ParseVariant(aValue, VARIANT_HMK, aKeywordTable)) {
     return false;
   }
 
-  // first value 'normal', 'inherit', 'initial' ==> done
+  // first value 'normal', 'inherit', 'unset', 'initial', 'unset' ==> done
   if (eCSSUnit_Normal == aValue.GetUnit() ||
       eCSSUnit_Initial == aValue.GetUnit() ||
-      eCSSUnit_Inherit == aValue.GetUnit())
+      eCSSUnit_Inherit == aValue.GetUnit() ||
+      eCSSUnit_Unset == aValue.GetUnit())
   {
     return true;
   }
 
   // look for more values
   nsCSSValue nextValue;
   int32_t mergedValue = aValue.GetIntValue();
 
@@ -9043,16 +9094,21 @@ CSSParserImpl::ParseFamily(nsCSSValue& a
     // 605231 - don't parse unquoted 'default' reserved keyword
     if (keyword == eCSSKeyword_default) {
       return false;
     }
     if (keyword == eCSSKeyword_initial) {
       aValue.SetInitialValue();
       return true;
     }
+    if (keyword == eCSSKeyword_unset &&
+        nsLayoutUtils::UnsetValueEnabled()) {
+      aValue.SetUnsetValue();
+      return true;
+    }
     if (keyword == eCSSKeyword__moz_use_system_font &&
         !IsParsingCompoundProperty()) {
       aValue.SetSystemFontValue();
       return true;
     }
   }
 
   for (;;) {
@@ -9070,16 +9126,21 @@ CSSParserImpl::ParseFamily(nsCSSValue& a
     if (single) {
       nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(nextFamily);
       switch (keyword) {
         case eCSSKeyword_inherit:
         case eCSSKeyword_initial:
         case eCSSKeyword_default:
         case eCSSKeyword__moz_use_system_font:
           return false;
+        case eCSSKeyword_unset:
+          if (nsLayoutUtils::UnsetValueEnabled()) {
+            return false;
+          }
+          // fall through
         default:
           break;
       }
     }
 
     family.Append(nextFamily);
   }
 
@@ -9685,17 +9746,17 @@ CSSParserImpl::ParseTextDecorationLine(n
   }
   return false;
 }
 
 bool
 CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue)
 {
   if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
-    // 'inherit' and 'initial' must be alone
+    // 'inherit', 'initial' and 'unset' must be alone
     return true;
   }
 
   nsCSSValue left;
   if (!ParseVariant(left, VARIANT_KEYWORD | VARIANT_STRING,
                     nsCSSProps::kTextOverflowKTable))
     return false;
 
@@ -10069,17 +10130,17 @@ CSSParserImpl::ParseSingleTransform(bool
 
 /* Parses a transform property list by continuously reading in properties
  * and constructing a matrix from it.
  */
 bool CSSParserImpl::ParseTransform(bool aIsPrefixed)
 {
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
-    // 'inherit', 'initial', and 'none' must be alone
+    // 'inherit', 'initial', 'unset' and 'none' must be alone
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
     nsCSSValueList* cur = value.SetListValue();
     for (;;) {
       if (!ParseSingleTransform(aIsPrefixed, cur->mValue)) {
         return false;
@@ -10108,19 +10169,20 @@ bool CSSParserImpl::ParseTransformOrigin
     }
     prop = eCSSProperty_perspective_origin;
   }
 
   // Unlike many other uses of pairs, this position should always be stored
   // as a pair, even if the values are the same, so it always serializes as
   // a pair, and to keep the computation code simple.
   if (position.mXValue.GetUnit() == eCSSUnit_Inherit ||
-      position.mXValue.GetUnit() == eCSSUnit_Initial) {
+      position.mXValue.GetUnit() == eCSSUnit_Initial ||
+      position.mXValue.GetUnit() == eCSSUnit_Unset) {
     NS_ABORT_IF_FALSE(position.mXValue == position.mYValue,
-                      "inherit/initial only half?");
+                      "inherit/initial/unset only half?");
     AppendValue(prop, position.mXValue);
   } else {
     nsCSSValue value;
     if (aPerspective) {
       value.SetPairValue(position.mXValue, position.mYValue);
     } else {
       nsCSSValue depth;
       if (!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr)) {
@@ -10283,17 +10345,17 @@ CSSParserImpl::ParseSingleFilter(nsCSSVa
  * reference filter.
  * e.g. filter: url(#my-filter);
  */
 bool
 CSSParserImpl::ParseFilter()
 {
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
-    // 'inherit', 'initial', and 'none' must be alone
+    // 'inherit', 'initial', 'unset' and 'none' must be alone
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
     nsCSSValueList* cur = value.SetListValue();
     while (cur) {
       if (!ParseSingleFilter(&cur->mValue)) {
         return false;
@@ -10315,37 +10377,39 @@ CSSParserImpl::ParseFilter()
   return true;
 }
 
 bool
 CSSParserImpl::ParseTransitionProperty()
 {
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
-    // 'inherit', 'initial', and 'none' must be alone
+    // 'inherit', 'initial', 'unset' and 'none' must be alone
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
     // Accept a list of arbitrary identifiers.  They should be
     // CSS properties, but we want to accept any so that we
     // accept properties that we don't know about yet, e.g.
     // transition-property: invalid-property, left, opacity;
     nsCSSValueList* cur = value.SetListValue();
     for (;;) {
       if (!ParseVariant(cur->mValue, VARIANT_IDENTIFIER | VARIANT_ALL, nullptr)) {
         return false;
       }
       if (cur->mValue.GetUnit() == eCSSUnit_Ident) {
         nsDependentString str(cur->mValue.GetStringBufferValue());
-        // Exclude 'none' and 'inherit' and 'initial' according to the
+        // Exclude 'none', 'inherit', 'initial' and 'unset' according to the
         // same rules as for 'counter-reset' in CSS 2.1.
         if (str.LowerCaseEqualsLiteral("none") ||
             str.LowerCaseEqualsLiteral("inherit") ||
-            str.LowerCaseEqualsLiteral("initial")) {
+            str.LowerCaseEqualsLiteral("initial") ||
+            (str.LowerCaseEqualsLiteral("unset") &&
+             nsLayoutUtils::UnsetValueEnabled())) {
           return false;
         }
       }
       if (CheckEndProperty()) {
         break;
       }
       if (!ExpectSymbol(',', true)) {
         REPORT_UNEXPECTED_TOKEN(PEExpectedComma);
@@ -10473,17 +10537,17 @@ AppendValueToList(nsCSSValue& aContainer
 CSSParserImpl::ParseAnimationOrTransitionShorthandResult
 CSSParserImpl::ParseAnimationOrTransitionShorthand(
                  const nsCSSProperty* aProperties,
                  const nsCSSValue* aInitialValues,
                  nsCSSValue* aValues,
                  size_t aNumProperties)
 {
   nsCSSValue tempValue;
-  // first see if 'inherit' or 'initial' is specified.  If one is,
+  // first see if 'inherit', 'initial' or 'unset' is specified.  If one is,
   // it can be the only thing specified, so don't attempt to parse any
   // additional properties
   if (ParseVariant(tempValue, VARIANT_INHERIT, nullptr)) {
     for (uint32_t i = 0; i < aNumProperties; ++i) {
       AppendValue(aProperties[i], tempValue);
     }
     return eParseAnimationOrTransitionShorthand_Inherit;
   }
@@ -10588,19 +10652,17 @@ CSSParserImpl::ParseTransition()
                                         initialValues, values, numProps);
   if (spres != eParseAnimationOrTransitionShorthand_Values) {
     return spres != eParseAnimationOrTransitionShorthand_Error;
   }
 
   // Make two checks on the list for 'transition-property':
   //   + If there is more than one item, then none of the items can be
   //     'none' or 'all'.
-  //   + None of the items can be 'inherit' or 'initial' (this is the case,
-  //     like with counter-reset &c., where CSS 2.1 specifies 'initial', so
-  //     we should check it without the -moz- prefix).
+  //   + None of the items can be 'inherit', 'initial' or 'unset'.
   {
     NS_ABORT_IF_FALSE(kTransitionProperties[3] ==
                         eCSSProperty_transition_property,
                       "array index mismatch");
     nsCSSValueList *l = values[3].GetListValue();
     bool multipleItems = !!l->mNext;
     do {
       const nsCSSValue& val = l->mValue;
@@ -10611,17 +10673,20 @@ CSSParserImpl::ParseTransition()
         }
 
         // Unbox a solitary 'none'.
         values[3].SetNoneValue();
         break;
       }
       if (val.GetUnit() == eCSSUnit_Ident) {
         nsDependentString str(val.GetStringBufferValue());
-        if (str.EqualsLiteral("inherit") || str.EqualsLiteral("initial")) {
+        if (str.EqualsLiteral("inherit") ||
+            str.EqualsLiteral("initial") ||
+            (str.EqualsLiteral("unset") &&
+             nsLayoutUtils::UnsetValueEnabled())) {
           return false;
         }
       }
     } while ((l = l->mNext));
   }
 
   // Save all parsed transition sub-properties in mTempData
   for (uint32_t i = 0; i < numProps; ++i) {
@@ -10766,17 +10831,17 @@ CSSParserImpl::ParseShadowItem(nsCSSValu
 bool
 CSSParserImpl::ParseShadowList(nsCSSProperty aProperty)
 {
   nsAutoParseCompoundProperty compound(this);
   bool isBoxShadow = aProperty == eCSSProperty_box_shadow;
 
   nsCSSValue value;
   if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
-    // 'inherit', 'initial', and 'none' must be alone
+    // 'inherit', 'initial', 'unset' and 'none' must be alone
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
     nsCSSValueList* cur = value.SetListValue();
     for (;;) {
       if (!ParseShadowItem(cur->mValue, isBoxShadow)) {
         return false;
@@ -10861,17 +10926,17 @@ CSSParserImpl::ParsePaint(nsCSSProperty 
 bool
 CSSParserImpl::ParseDasharray()
 {
   nsCSSValue value;
 
   if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE |
                           VARIANT_OPENTYPE_SVG_KEYWORD,
                    nsCSSProps::kStrokeContextValueKTable)) {
-    // 'inherit', 'initial', and 'none' are only allowed on their own
+    // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own
     if (!ExpectEndProperty()) {
       return false;
     }
   } else {
     nsCSSValueList *cur = value.SetListValue();
     for (;;) {
       if (!ParseNonNegativeVariant(cur->mValue, VARIANT_LPN, nullptr)) {
         return false;
@@ -10987,16 +11052,30 @@ CSSParserImpl::ParsePaintOrder()
   if (!ExpectEndProperty()) {
     return false;
   }
 
   AppendValue(eCSSProperty_paint_order, value);
   return true;
 }
 
+bool
+CSSParserImpl::ParseAll()
+{
+  nsCSSValue value;
+  if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) {
+    return false;
+  }
+
+  CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, eCSSProperty_all) {
+    AppendValue(*p, value);
+  }
+  return true;
+}
+
 } // anonymous namespace
 
 // Recycling of parser implementation objects
 
 static CSSParserImpl* gFreeList = nullptr;
 
 nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader,
                          nsCSSStyleSheet* aSheet)
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -82,16 +82,21 @@
   CSS_PROP_PUBLIC_OR_PRIVATE(Moz ## name_, name_)
 
 #define CSS_PROP_NO_OFFSET (-1)
 
 // Callers may define CSS_PROP_LIST_EXCLUDE_INTERNAL if they want to
 // exclude internal properties that are not represented in the DOM (only
 // the DOM style code defines this).
 
+// Callers may also define CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
+// to exclude properties that are not considered to be components of the 'all'
+// shorthand property.  Currently this is just 'direction', 'unicode-bidi' and
+// a few internal properties.
+
 // A caller who wants all the properties can define the |CSS_PROP|
 // macro.
 #ifdef CSS_PROP
 
 #define USED_CSS_PROP
 #define CSS_PROP_FONT(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Font, stylestructoffset_, animtype_)
 #define CSS_PROP_COLOR(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Color, stylestructoffset_, animtype_)
 #define CSS_PROP_BACKGROUND(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, stylestructoffset_, animtype_) CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, Background, stylestructoffset_, animtype_)
@@ -338,30 +343,38 @@ CSS_PROP_TEXT(
     CSS_PROP_DOMPROP_PREFIXED(TabSize),
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_VALUE_NONNEGATIVE,
     "",
     VARIANT_HI,
     nullptr,
     offsetof(nsStyleText, mTabSize),
     eStyleAnimType_None)
+#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_FONT(
     -x-system-font,
     _x_system_font,
     CSS_PROP_DOMPROP_PREFIXED(SystemFont),
     CSS_PROPERTY_PARSE_INACCESSIBLE |
         CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE |
         CSS_PROPERTY_APPLIES_TO_PLACEHOLDER,
     "",
     0,
     kFontKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
-#endif
+#endif // !defined(CSS_PROP_LIST_EXCLUDE_INTERNAL)
+#endif // !defined(CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND)
+CSS_PROP_SHORTHAND(
+    all,
+    all,
+    All,
+    CSS_PROPERTY_PARSE_FUNCTION,
+    "layout.css.all-shorthand.enabled")
 CSS_PROP_SHORTHAND(
     animation,
     animation,
     Animation,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_DISPLAY(
     animation-delay,
@@ -1529,26 +1542,28 @@ CSS_PROP_USERINTERFACE(
         CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
         CSS_PROPERTY_START_IMAGE_LOADS |
         CSS_PROPERTY_IMAGE_IS_IN_ARRAY_0,
     "",
     0,
     kCursorKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
+#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 CSS_PROP_VISIBILITY(
     direction,
     direction,
     Direction,
     CSS_PROPERTY_PARSE_VALUE,
     "",
     VARIANT_HK,
     kDirectionKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
+#endif // !defined(CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND)
 CSS_PROP_DISPLAY(
     display,
     display,
     Display,
     CSS_PROPERTY_PARSE_VALUE |
         // This is allowed because we need to make the placeholder
         // pseudo-element an inline-block in the UA stylesheet. It is a block
         // by default.
@@ -3018,26 +3033,28 @@ CSS_PROP_DISPLAY(
     TransitionTimingFunction,
     CSS_PROPERTY_PARSE_VALUE_LIST |
         CSS_PROPERTY_VALUE_LIST_USES_COMMAS,
     "",
     VARIANT_KEYWORD | VARIANT_TIMING_FUNCTION, // used by list parsing
     kTransitionTimingFunctionKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
+#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 CSS_PROP_TEXTRESET(
     unicode-bidi,
     unicode_bidi,
     UnicodeBidi,
     CSS_PROPERTY_PARSE_VALUE,
     "",
     VARIANT_HK,
     kUnicodeBidiKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
+#endif // !defined(CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND)
 CSS_PROP_USERINTERFACE(
     -moz-user-focus,
     user_focus,
     CSS_PROP_DOMPROP_PREFIXED(UserFocus),
     CSS_PROPERTY_PARSE_VALUE,
     "",
     VARIANT_HK,
     kUserFocusKTable,
@@ -3276,16 +3293,17 @@ CSS_PROP_XUL(
     CSS_PROP_DOMPROP_PREFIXED(StackSizing),
     CSS_PROPERTY_PARSE_VALUE,
     "",
     VARIANT_HK,
     kStackSizingKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 
+#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 CSS_PROP_FONT(
     -moz-script-level,
     script_level,
     ScriptLevel,
     // REVIEW: no range restriction?
     // NOTE: CSSParserImpl::ParseSingleValueProperty only accepts this
     // property when mUnsafeRulesEnabled is set.
@@ -3314,17 +3332,18 @@ CSS_PROP_FONT(
     ScriptMinSize,
     // REVIEW: no range restriction?
     CSS_PROPERTY_PARSE_INACCESSIBLE,
     "",
     0,
     nullptr,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
-#endif
+#endif // !defined(CSS_PROP_LIST_EXCLUDE_INTERNAL)
+#endif // !defined(CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND)
 
 CSS_PROP_SVGRESET(
     clip-path,
     clip_path,
     ClipPath,
     CSS_PROPERTY_PARSE_VALUE,
     "",
     VARIANT_HUO,
@@ -3657,16 +3676,17 @@ CSS_PROP_SVGRESET(
 CSS_PROP_SHORTHAND(
     -moz-transform,
     _moz_transform,
     MozTransform,
     CSS_PROPERTY_PARSE_FUNCTION |
         CSS_PROPERTY_IS_ALIAS,
     "layout.css.prefixes.transforms")
 
+#ifndef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
 #ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL
 // We have a few properties that are in style structs but are not stored
 // in style sheets (or nsCSS* structs).  Some fields in these property
 // definitions are bogus (e.g., they work for nsRuleData* offsets but
 // not nsCSS* offsets).  Callers that care about these bogus fields can
 // define CSS_PROP_STUB_NOT_CSS to define a replacement for these
 // entries.
 #ifdef CSS_PROP_STUB_NOT_CSS
@@ -3699,17 +3719,18 @@ CSS_PROP_FONT(
     TextZoom,
     CSS_PROPERTY_PARSE_INACCESSIBLE,
     "",
     0,
     nullptr,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 #endif /* !defined(CSS_PROP_STUB_NOT_CSS) */
-#endif /* !defined(CSS_PROP_EXCLUDE_INTERNAL) */
+#endif /* !defined(CSS_PROP_LIST_EXCLUDE_INTERNAL) */
+#endif /* !defined(CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND) */
 
 #ifdef USED_CSS_PROP
 
 #undef USED_CSS_PROP
 #undef CSS_PROP_FONT
 #undef CSS_PROP_COLOR
 #undef CSS_PROP_BACKGROUND
 #undef CSS_PROP_LIST
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1921,16 +1921,27 @@ const uint32_t nsCSSProps::kFlagsTable[e
   flags_,
 #include "nsCSSPropList.h"
 #undef CSS_PROP
 #define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) flags_,
 #include "nsCSSPropList.h"
 #undef CSS_PROP_SHORTHAND
 };
 
+static const nsCSSProperty gAllSubpropTable[] = {
+#define CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
+#define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, kwtable_, \
+                 stylestruct_, stylestructoffset_, animtype_)                 \
+  eCSSProperty_##id_,
+#include "nsCSSPropList.h"
+#undef CSS_PROP
+#undef CSS_PROP_LIST_ONLY_COMPONENTS_OF_ALL_SHORTHAND
+  eCSSProperty_UNKNOWN
+};
+
 static const nsCSSProperty gAnimationSubpropTable[] = {
   eCSSProperty_animation_duration,
   eCSSProperty_animation_timing_function,
   eCSSProperty_animation_delay,
   eCSSProperty_animation_direction,
   eCSSProperty_animation_fill_mode,
   eCSSProperty_animation_iteration_count,
   // List animation-name last so we serialize it last, in case it has
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -29,17 +29,17 @@
 #define VARIANT_TIME            0x000200  // T
 #define VARIANT_STRING          0x000400  // S
 #define VARIANT_COUNTER         0x000800  //
 #define VARIANT_ATTR            0x001000  //
 #define VARIANT_IDENTIFIER      0x002000  // D
 #define VARIANT_IDENTIFIER_NO_INHERIT 0x004000 // like above, but excluding
 // 'inherit' and 'initial'
 #define VARIANT_AUTO            0x010000  // A
-#define VARIANT_INHERIT         0x020000  // H eCSSUnit_Initial, eCSSUnit_Inherit
+#define VARIANT_INHERIT         0x020000  // H eCSSUnit_Initial, eCSSUnit_Inherit, eCSSUnit_Unset
 #define VARIANT_NONE            0x040000  // O
 #define VARIANT_NORMAL          0x080000  // M
 #define VARIANT_SYSFONT         0x100000  // eCSSUnit_System_Font
 #define VARIANT_GRADIENT        0x200000  // eCSSUnit_Gradient
 #define VARIANT_TIMING_FUNCTION 0x400000  // cubic-bezier() and steps()
 #define VARIANT_ALL             0x800000  //
 #define VARIANT_IMAGE_RECT    0x01000000  // eCSSUnit_Function
 // This is an extra bit that says that a VARIANT_ANGLE allows unitless zero:
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -392,33 +392,37 @@ void nsCSSValue::SetPairValue(const nsCS
 {
   // pairs should not be used for null/inherit/initial values
   NS_ABORT_IF_FALSE(aValue &&
                     aValue->mXValue.GetUnit() != eCSSUnit_Null &&
                     aValue->mYValue.GetUnit() != eCSSUnit_Null &&
                     aValue->mXValue.GetUnit() != eCSSUnit_Inherit &&
                     aValue->mYValue.GetUnit() != eCSSUnit_Inherit &&
                     aValue->mXValue.GetUnit() != eCSSUnit_Initial &&
-                    aValue->mYValue.GetUnit() != eCSSUnit_Initial,
+                    aValue->mYValue.GetUnit() != eCSSUnit_Initial &&
+                    aValue->mXValue.GetUnit() != eCSSUnit_Unset &&
+                    aValue->mYValue.GetUnit() != eCSSUnit_Unset,
                     "missing or inappropriate pair value");
   Reset();
   mUnit = eCSSUnit_Pair;
   mValue.mPair = new nsCSSValuePair_heap(aValue->mXValue, aValue->mYValue);
   mValue.mPair->AddRef();
 }
 
 void nsCSSValue::SetPairValue(const nsCSSValue& xValue,
                               const nsCSSValue& yValue)
 {
   NS_ABORT_IF_FALSE(xValue.GetUnit() != eCSSUnit_Null &&
                     yValue.GetUnit() != eCSSUnit_Null &&
                     xValue.GetUnit() != eCSSUnit_Inherit &&
                     yValue.GetUnit() != eCSSUnit_Inherit &&
                     xValue.GetUnit() != eCSSUnit_Initial &&
-                    yValue.GetUnit() != eCSSUnit_Initial,
+                    yValue.GetUnit() != eCSSUnit_Initial &&
+                    xValue.GetUnit() != eCSSUnit_Unset &&
+                    yValue.GetUnit() != eCSSUnit_Unset,
                     "inappropriate pair value");
   Reset();
   mUnit = eCSSUnit_Pair;
   mValue.mPair = new nsCSSValuePair_heap(xValue, yValue);
   mValue.mPair->AddRef();
 }
 
 void nsCSSValue::SetTripletValue(const nsCSSValueTriplet* aValue)
@@ -428,17 +432,20 @@ void nsCSSValue::SetTripletValue(const n
                       aValue->mXValue.GetUnit() != eCSSUnit_Null &&
                       aValue->mYValue.GetUnit() != eCSSUnit_Null &&
                       aValue->mZValue.GetUnit() != eCSSUnit_Null &&
                       aValue->mXValue.GetUnit() != eCSSUnit_Inherit &&
                       aValue->mYValue.GetUnit() != eCSSUnit_Inherit &&
                       aValue->mZValue.GetUnit() != eCSSUnit_Inherit &&
                       aValue->mXValue.GetUnit() != eCSSUnit_Initial &&
                       aValue->mYValue.GetUnit() != eCSSUnit_Initial &&
-                      aValue->mZValue.GetUnit() != eCSSUnit_Initial,
+                      aValue->mZValue.GetUnit() != eCSSUnit_Initial &&
+                      aValue->mXValue.GetUnit() != eCSSUnit_Unset &&
+                      aValue->mYValue.GetUnit() != eCSSUnit_Unset &&
+                      aValue->mZValue.GetUnit() != eCSSUnit_Unset,
                       "missing or inappropriate triplet value");
     Reset();
     mUnit = eCSSUnit_Triplet;
     mValue.mTriplet = new nsCSSValueTriplet_heap(aValue->mXValue, aValue->mYValue, aValue->mZValue);
     mValue.mTriplet->AddRef();
 }
 
 void nsCSSValue::SetTripletValue(const nsCSSValue& xValue,
@@ -448,17 +455,20 @@ void nsCSSValue::SetTripletValue(const n
     // Only allow Null for the z component
     NS_ABORT_IF_FALSE(xValue.GetUnit() != eCSSUnit_Null &&
                       yValue.GetUnit() != eCSSUnit_Null &&
                       xValue.GetUnit() != eCSSUnit_Inherit &&
                       yValue.GetUnit() != eCSSUnit_Inherit &&
                       zValue.GetUnit() != eCSSUnit_Inherit &&
                       xValue.GetUnit() != eCSSUnit_Initial &&
                       yValue.GetUnit() != eCSSUnit_Initial &&
-                      zValue.GetUnit() != eCSSUnit_Initial,
+                      zValue.GetUnit() != eCSSUnit_Initial &&
+                      xValue.GetUnit() != eCSSUnit_Unset &&
+                      yValue.GetUnit() != eCSSUnit_Unset &&
+                      zValue.GetUnit() != eCSSUnit_Unset,
                       "inappropriate triplet value");
     Reset();
     mUnit = eCSSUnit_Triplet;
     mValue.mTriplet = new nsCSSValueTriplet_heap(xValue, yValue, zValue);
     mValue.mTriplet->AddRef();
 }
 
 nsCSSRect& nsCSSValue::SetRectValue()
@@ -519,16 +529,22 @@ void nsCSSValue::SetInheritValue()
 }
 
 void nsCSSValue::SetInitialValue()
 {
   Reset();
   mUnit = eCSSUnit_Initial;
 }
 
+void nsCSSValue::SetUnsetValue()
+{
+  Reset();
+  mUnit = eCSSUnit_Unset;
+}
+
 void nsCSSValue::SetNoneValue()
 {
   Reset();
   mUnit = eCSSUnit_None;
 }
 
 void nsCSSValue::SetAllValue()
 {
@@ -1160,16 +1176,17 @@ nsCSSValue::AppendToString(nsCSSProperty
     }
   }
 
   switch (unit) {
     case eCSSUnit_Null:         break;
     case eCSSUnit_Auto:         aResult.AppendLiteral("auto");     break;
     case eCSSUnit_Inherit:      aResult.AppendLiteral("inherit");  break;
     case eCSSUnit_Initial:      aResult.AppendLiteral("initial");  break;
+    case eCSSUnit_Unset:        aResult.AppendLiteral("unset");    break;
     case eCSSUnit_None:         aResult.AppendLiteral("none");     break;
     case eCSSUnit_Normal:       aResult.AppendLiteral("normal");   break;
     case eCSSUnit_System_Font:  aResult.AppendLiteral("-moz-use-system-font"); break;
     case eCSSUnit_All:          aResult.AppendLiteral("all"); break;
     case eCSSUnit_Dummy:
     case eCSSUnit_DummyInherit:
       NS_ABORT_IF_FALSE(false, "should never serialize");
       break;
@@ -1248,16 +1265,17 @@ nsCSSValue::SizeOfExcludingThis(mozilla:
   size_t n = 0;
 
   switch (GetUnit()) {
     // No value: nothing extra to measure.
     case eCSSUnit_Null:
     case eCSSUnit_Auto:
     case eCSSUnit_Inherit:
     case eCSSUnit_Initial:
+    case eCSSUnit_Unset:
     case eCSSUnit_None:
     case eCSSUnit_Normal:
     case eCSSUnit_System_Font:
     case eCSSUnit_All:
     case eCSSUnit_Dummy:
     case eCSSUnit_DummyInherit:
       break;
 
@@ -1486,17 +1504,18 @@ nsCSSRect::~nsCSSRect()
   MOZ_COUNT_DTOR(nsCSSRect);
 }
 
 void
 nsCSSRect::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const
 {
   NS_ABORT_IF_FALSE(mTop.GetUnit() != eCSSUnit_Null &&
                     mTop.GetUnit() != eCSSUnit_Inherit &&
-                    mTop.GetUnit() != eCSSUnit_Initial,
+                    mTop.GetUnit() != eCSSUnit_Initial &&
+                    mTop.GetUnit() != eCSSUnit_Unset,
                     "parser should have used a bare value");
 
   if (eCSSProperty_border_image_slice == aProperty ||
       eCSSProperty_border_image_width == aProperty ||
       eCSSProperty_border_image_outset == aProperty) {
     NS_NAMED_LITERAL_STRING(space, " ");
 
     mTop.AppendToString(aProperty, aResult);
@@ -1637,16 +1656,17 @@ nsCSSValuePairList::AppendToString(nsCSS
 {
   const nsCSSValuePairList* item = this;
   for (;;) {
     NS_ABORT_IF_FALSE(item->mXValue.GetUnit() != eCSSUnit_Null,
                       "unexpected null unit");
     item->mXValue.AppendToString(aProperty, aResult);
     if (item->mXValue.GetUnit() != eCSSUnit_Inherit &&
         item->mXValue.GetUnit() != eCSSUnit_Initial &&
+        item->mXValue.GetUnit() != eCSSUnit_Unset &&
         item->mYValue.GetUnit() != eCSSUnit_Null) {
       aResult.Append(PRUnichar(' '));
       item->mYValue.AppendToString(aProperty, aResult);
     }
     item = item->mNext;
     if (!item)
       break;
 
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -134,23 +134,24 @@ struct ImageValue : public URLValue {
 }
 }
 
 enum nsCSSUnit {
   eCSSUnit_Null         = 0,      // (n/a) null unit, value is not specified
   eCSSUnit_Auto         = 1,      // (n/a) value is algorithmic
   eCSSUnit_Inherit      = 2,      // (n/a) value is inherited
   eCSSUnit_Initial      = 3,      // (n/a) value is default UA value
-  eCSSUnit_None         = 4,      // (n/a) value is none
-  eCSSUnit_Normal       = 5,      // (n/a) value is normal (algorithmic, different than auto)
-  eCSSUnit_System_Font  = 6,      // (n/a) value is -moz-use-system-font
-  eCSSUnit_All          = 7,      // (n/a) value is all
-  eCSSUnit_Dummy        = 8,      // (n/a) a fake but specified value, used
+  eCSSUnit_Unset        = 4,      // (n/a) value equivalent to 'initial' if on a reset property, 'inherit' otherwise
+  eCSSUnit_None         = 5,      // (n/a) value is none
+  eCSSUnit_Normal       = 6,      // (n/a) value is normal (algorithmic, different than auto)
+  eCSSUnit_System_Font  = 7,      // (n/a) value is -moz-use-system-font
+  eCSSUnit_All          = 8,      // (n/a) value is all
+  eCSSUnit_Dummy        = 9,      // (n/a) a fake but specified value, used
                                   //       only in temporary values
-  eCSSUnit_DummyInherit = 9,      // (n/a) a fake but specified value, used
+  eCSSUnit_DummyInherit = 10,     // (n/a) a fake but specified value, used
                                   //       only in temporary values
 
   eCSSUnit_String       = 11,     // (PRUnichar*) a string value
   eCSSUnit_Ident        = 12,     // (PRUnichar*) a string value
   eCSSUnit_Families     = 13,     // (PRUnichar*) a string value
   eCSSUnit_Attr         = 14,     // (PRUnichar*) a attr(string) value
   eCSSUnit_Local_Font   = 15,     // (PRUnichar*) a local font name
   eCSSUnit_Font_Format  = 16,     // (PRUnichar*) a font format name
@@ -487,16 +488,17 @@ public:
   void SetPairValue(const nsCSSValue& xValue, const nsCSSValue& yValue);
   void SetDependentListValue(nsCSSValueList* aList);
   void SetDependentPairListValue(nsCSSValuePairList* aList);
   void SetTripletValue(const nsCSSValueTriplet* aTriplet);
   void SetTripletValue(const nsCSSValue& xValue, const nsCSSValue& yValue, const nsCSSValue& zValue);
   void SetAutoValue();
   void SetInheritValue();
   void SetInitialValue();
+  void SetUnsetValue();
   void SetNoneValue();
   void SetAllValue();
   void SetNormalValue();
   void SetSystemFontValue();
   void SetDummyValue();
   void SetDummyInheritValue();
 
   // These are a little different - they allocate storage for you and
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1,20 +1,21 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set tw=78 expandtab softtabstop=2 ts=2 sw=2: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* DOM object returned from element.getComputedStyle() */
 
+#include "nsComputedDOMStyle.h"
+
+#include "mozilla/Preferences.h"
 #include "mozilla/Util.h"
 
-#include "nsComputedDOMStyle.h"
-
 #include "nsError.h"
 #include "nsDOMString.h"
 #include "nsIDOMCSSPrimitiveValue.h"
 #include "nsStyleContext.h"
 #include "nsIScrollableFrame.h"
 #include "nsContentUtils.h"
 #include "nsIContent.h"
 
@@ -79,16 +80,159 @@ NS_NewComputedDOMStyle(dom::Element* aEl
 
     computedStyle = new nsComputedDOMStyle(aElement, aPseudoElt, aPresShell,
                                            aStyleType);
   }
 
   return computedStyle.forget();
 }
 
+/**
+ * An object that represents the ordered set of properties that are exposed on
+ * an nsComputedDOMStyle object and how their computed values can be obtained.
+ */
+struct nsComputedStyleMap
+{
+  friend class nsComputedDOMStyle;
+
+  struct Entry
+  {
+    // Create a pointer-to-member-function type.
+    typedef mozilla::dom::CSSValue* (nsComputedDOMStyle::*ComputeMethod)();
+
+    nsCSSProperty mProperty;
+    ComputeMethod mGetter;
+
+    bool IsLayoutFlushNeeded() const
+    {
+      return nsCSSProps::PropHasFlags(mProperty,
+                                      CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH);
+    }
+
+    bool IsEnabled() const
+    {
+      return nsCSSProps::IsEnabled(mProperty);
+    }
+  };
+
+  // We define this enum just to count the total number of properties that can
+  // be exposed on an nsComputedDOMStyle, including properties that may be
+  // disabled.
+  enum {
+#define COMPUTED_STYLE_PROP(prop_, method_) \
+    eComputedStyleProperty_##prop_,
+#include "nsComputedDOMStylePropertyList.h"
+#undef COMPUTED_STYLE_PROP
+    eComputedStyleProperty_COUNT
+  };
+
+  /**
+   * Returns the number of properties that should be exposed on an
+   * nsComputedDOMStyle, ecxluding any disabled properties.
+   */
+  uint32_t Length()
+  {
+    Update();
+    return mExposedPropertyCount;
+  }
+
+  /**
+   * Returns the property at the given index in the list of properties
+   * that should be exposed on an nsComputedDOMStyle, excluding any
+   * disabled properties.
+   */
+  nsCSSProperty PropertyAt(uint32_t aIndex)
+  {
+    Update();
+    return kEntries[EntryIndex(aIndex)].mProperty;
+  }
+
+  /**
+   * Searches for and returns the computed style map entry for the given
+   * property, or nullptr if the property is not exposed on nsComputedDOMStyle
+   * or is currently disabled.
+   */
+  const Entry* FindEntryForProperty(nsCSSProperty aPropID)
+  {
+    Update();
+    for (uint32_t i = 0; i < mExposedPropertyCount; i++) {
+      const Entry* entry = &kEntries[EntryIndex(i)];
+      if (entry->mProperty == aPropID) {
+        return entry;
+      }
+    }
+    return nullptr;
+  }
+
+  /**
+   * Records that mIndexMap needs updating, due to prefs changing that could
+   * affect the set of properties exposed on an nsComputedDOMStyle.
+   */
+  void MarkDirty() { mExposedPropertyCount = 0; }
+
+  // The member variables are public so that we can use an initializer in
+  // nsComputedDOMStyle::GetComputedStyleMap.  Use the member functions
+  // above to get information from this object.
+
+  /**
+   * An entry for each property that can be exposed on an nsComputedDOMStyle.
+   */
+  const Entry kEntries[eComputedStyleProperty_COUNT];
+
+  /**
+   * The number of properties that should be exposed on an nsComputedDOMStyle.
+   * This will be less than eComputedStyleProperty_COUNT if some property
+   * prefs are disabled.  A value of 0 indicates that it and mIndexMap are out
+   * of date.
+   */
+  uint32_t mExposedPropertyCount;
+
+  /**
+   * A map of indexes on the nsComputedDOMStyle object to indexes into kEntries.
+   */
+  uint32_t mIndexMap[eComputedStyleProperty_COUNT];
+
+private:
+  /**
+   * Returns whether mExposedPropertyCount and mIndexMap are out of date.
+   */
+  bool IsDirty() { return mExposedPropertyCount == 0; }
+
+  /**
+   * Updates mExposedPropertyCount and mIndexMap to take into account properties
+   * whose prefs are currently disabled.
+   */
+  void Update();
+
+  /**
+   * Maps an nsComputedDOMStyle indexed getter index to an index into kEntries.
+   */
+  uint32_t EntryIndex(uint32_t aIndex) const
+  {
+    MOZ_ASSERT(aIndex < mExposedPropertyCount);
+    return mIndexMap[aIndex];
+  }
+};
+
+void
+nsComputedStyleMap::Update()
+{
+  if (!IsDirty()) {
+    return;
+  }
+
+  uint32_t index = 0;
+  for (uint32_t i = 0; i < eComputedStyleProperty_COUNT; i++) {
+    if (kEntries[i].IsEnabled()) {
+      mIndexMap[index++] = i;
+    }
+  }
+  mExposedPropertyCount = index;
+}
+
 nsComputedDOMStyle::nsComputedDOMStyle(dom::Element* aElement,
                                        const nsAString& aPseudoElt,
                                        nsIPresShell* aPresShell,
                                        StyleType aStyleType)
   : mDocumentWeak(nullptr), mOuterFrame(nullptr),
     mInnerFrame(nullptr), mPresShell(nullptr),
     mStyleType(aStyleType),
     mExposeVisitedStyle(false)
@@ -220,17 +364,17 @@ nsComputedDOMStyle::SetCssText(const nsA
 }
 
 
 NS_IMETHODIMP
 nsComputedDOMStyle::GetLength(uint32_t* aLength)
 {
   NS_PRECONDITION(aLength, "Null aLength!  Prepare to die!");
 
-  (void)GetQueryablePropertyMap(aLength);
+  *aLength = GetComputedStyleMap()->Length();
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsComputedDOMStyle::GetParentRule(nsIDOMCSSRule** aParentRule)
 {
@@ -454,36 +598,28 @@ nsComputedDOMStyle::GetPropertyCSSValue(
 
   nsCSSProperty prop = nsCSSProps::LookupProperty(aPropertyName,
                                                   nsCSSProps::eEnabled);
 
   // We don't (for now, anyway, though it may make sense to change it
   // for all aliases, including those in nsCSSPropAliasList) want
   // aliases to be enumerable (via GetLength and IndexedGetter), so
   // handle them here rather than adding entries to
-  // GetQueryablePropertyMap.
+  // the nsComputedStyleMap.
   if (prop != eCSSProperty_UNKNOWN &&
       nsCSSProps::PropHasFlags(prop, CSS_PROPERTY_IS_ALIAS)) {
     const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(prop);
     NS_ABORT_IF_FALSE(subprops[1] == eCSSProperty_UNKNOWN,
                       "must have list of length 1");
     prop = subprops[0];
   }
 
-  const ComputedStyleMapEntry* propEntry = nullptr;
-  {
-    uint32_t length = 0;
-    const ComputedStyleMapEntry* propMap = GetQueryablePropertyMap(&length);
-    for (uint32_t i = 0; i < length; ++i) {
-      if (prop == propMap[i].mProperty) {
-        propEntry = &propMap[i];
-        break;
-      }
-    }
-  }
+  const nsComputedStyleMap::Entry* propEntry =
+    GetComputedStyleMap()->FindEntryForProperty(prop);
+
   if (!propEntry) {
 #ifdef DEBUG_ComputedDOMStyle
     NS_WARNING(PromiseFlatCString(NS_ConvertUTF16toUTF8(aPropertyName) +
                                   NS_LITERAL_CSTRING(" is not queryable!")).get());
 #endif
 
     // NOTE:  For branches, we should flush here for compatibility!
     return nullptr;
@@ -616,21 +752,20 @@ nsComputedDOMStyle::Item(uint32_t aIndex
 {
   return nsDOMCSSDeclaration::Item(aIndex, aReturn);
 }
 
 void
 nsComputedDOMStyle::IndexedGetter(uint32_t aIndex, bool& aFound,
                                   nsAString& aPropName)
 {
-  uint32_t length = 0;
-  const ComputedStyleMapEntry* propMap = GetQueryablePropertyMap(&length);
-  aFound = aIndex < length;
+  nsComputedStyleMap* map = GetComputedStyleMap();
+  aFound = aIndex < map->Length();
   if (aFound) {
-    CopyASCIItoUTF16(nsCSSProps::GetStringValue(propMap[aIndex].mProperty),
+    CopyASCIItoUTF16(nsCSSProps::GetStringValue(map->PropertyAt(aIndex)),
                      aPropName);
   }
 }
 
 // Property getters...
 
 CSSValue*
 nsComputedDOMStyle::DoGetBinding()
@@ -5014,290 +5149,64 @@ nsComputedDOMStyle::DoGetAnimationPlaySt
     playState->SetIdent(
       nsCSSProps::ValueToKeywordEnum(animation->GetPlayState(),
                                      nsCSSProps::kAnimationPlayStateKTable));
   } while (++i < display->mAnimationPlayStateCount);
 
   return valueList;
 }
 
-#define COMPUTED_STYLE_MAP_ENTRY(_prop, _method)              \
-  { eCSSProperty_##_prop, &nsComputedDOMStyle::DoGet##_method }
-
-const nsComputedDOMStyle::ComputedStyleMapEntry*
-nsComputedDOMStyle::GetQueryablePropertyMap(uint32_t* aLength)
-{
-  /* ******************************************************************* *\
-   * Properties below are listed in alphabetical order.                  *
-   * Please keep them that way.                                          *
-   *                                                                     *
-   * Properties commented out with // are not yet implemented            *
-   * Properties commented out with //// are shorthands and not queryable *
-  \* ******************************************************************* */
-  static const ComputedStyleMapEntry map[] = {
-    /* ***************************** *\
-     * Implementations of CSS styles *
-    \* ***************************** */
-
-    COMPUTED_STYLE_MAP_ENTRY(align_items,                   AlignItems),
-    COMPUTED_STYLE_MAP_ENTRY(align_self,                    AlignSelf),
-    //// COMPUTED_STYLE_MAP_ENTRY(animation,                Animation),
-    COMPUTED_STYLE_MAP_ENTRY(animation_delay,               AnimationDelay),
-    COMPUTED_STYLE_MAP_ENTRY(animation_direction,           AnimationDirection),
-    COMPUTED_STYLE_MAP_ENTRY(animation_duration,            AnimationDuration),
-    COMPUTED_STYLE_MAP_ENTRY(animation_fill_mode,           AnimationFillMode),
-    COMPUTED_STYLE_MAP_ENTRY(animation_iteration_count,     AnimationIterationCount),
-    COMPUTED_STYLE_MAP_ENTRY(animation_name,                AnimationName),
-    COMPUTED_STYLE_MAP_ENTRY(animation_play_state,          AnimationPlayState),
-    COMPUTED_STYLE_MAP_ENTRY(animation_timing_function,     AnimationTimingFunction),
-    COMPUTED_STYLE_MAP_ENTRY(backface_visibility,           BackfaceVisibility),
-    //// COMPUTED_STYLE_MAP_ENTRY(background,               Background),
-    COMPUTED_STYLE_MAP_ENTRY(background_attachment,         BackgroundAttachment),
-    COMPUTED_STYLE_MAP_ENTRY(background_clip,               BackgroundClip),
-    COMPUTED_STYLE_MAP_ENTRY(background_color,              BackgroundColor),
-    COMPUTED_STYLE_MAP_ENTRY(background_image,              BackgroundImage),
-    COMPUTED_STYLE_MAP_ENTRY(background_origin,             BackgroundOrigin),
-    COMPUTED_STYLE_MAP_ENTRY(background_position,           BackgroundPosition),
-    COMPUTED_STYLE_MAP_ENTRY(background_repeat,             BackgroundRepeat),
-    COMPUTED_STYLE_MAP_ENTRY(background_size,               BackgroundSize),
-    //// COMPUTED_STYLE_MAP_ENTRY(border,                   Border),
-    //// COMPUTED_STYLE_MAP_ENTRY(border_bottom,            BorderBottom),
-    COMPUTED_STYLE_MAP_ENTRY(border_bottom_color,           BorderBottomColor),
-    COMPUTED_STYLE_MAP_ENTRY(border_bottom_left_radius,     BorderBottomLeftRadius),
-    COMPUTED_STYLE_MAP_ENTRY(border_bottom_right_radius,    BorderBottomRightRadius),
-    COMPUTED_STYLE_MAP_ENTRY(border_bottom_style,           BorderBottomStyle),
-    COMPUTED_STYLE_MAP_ENTRY(border_bottom_width,           BorderBottomWidth),
-    COMPUTED_STYLE_MAP_ENTRY(border_collapse,               BorderCollapse),
-    //// COMPUTED_STYLE_MAP_ENTRY(border_color,             BorderColor),
-    //// COMPUTED_STYLE_MAP_ENTRY(border_image,             BorderImage),
-    COMPUTED_STYLE_MAP_ENTRY(border_image_outset,           BorderImageOutset),
-    COMPUTED_STYLE_MAP_ENTRY(border_image_repeat,           BorderImageRepeat),
-    COMPUTED_STYLE_MAP_ENTRY(border_image_slice,            BorderImageSlice),
-    COMPUTED_STYLE_MAP_ENTRY(border_image_source,           BorderImageSource),
-    COMPUTED_STYLE_MAP_ENTRY(border_image_width,            BorderImageWidth),
-    //// COMPUTED_STYLE_MAP_ENTRY(border_left,              BorderLeft),
-    COMPUTED_STYLE_MAP_ENTRY(border_left_color,             BorderLeftColor),
-    COMPUTED_STYLE_MAP_ENTRY(border_left_style,             BorderLeftStyle),
-    COMPUTED_STYLE_MAP_ENTRY(border_left_width,             BorderLeftWidth),
-    //// COMPUTED_STYLE_MAP_ENTRY(border_right,             BorderRight),
-    COMPUTED_STYLE_MAP_ENTRY(border_right_color,            BorderRightColor),
-    COMPUTED_STYLE_MAP_ENTRY(border_right_style,            BorderRightStyle),
-    COMPUTED_STYLE_MAP_ENTRY(border_right_width,            BorderRightWidth),
-    COMPUTED_STYLE_MAP_ENTRY(border_spacing,                BorderSpacing),
-    //// COMPUTED_STYLE_MAP_ENTRY(border_style,             BorderStyle),
-    //// COMPUTED_STYLE_MAP_ENTRY(border_top,               BorderTop),
-    COMPUTED_STYLE_MAP_ENTRY(border_top_color,              BorderTopColor),
-    COMPUTED_STYLE_MAP_ENTRY(border_top_left_radius,        BorderTopLeftRadius),
-    COMPUTED_STYLE_MAP_ENTRY(border_top_right_radius,       BorderTopRightRadius),
-    COMPUTED_STYLE_MAP_ENTRY(border_top_style,              BorderTopStyle),
-    COMPUTED_STYLE_MAP_ENTRY(border_top_width,              BorderTopWidth),
-    //// COMPUTED_STYLE_MAP_ENTRY(border_width,             BorderWidth),
-    COMPUTED_STYLE_MAP_ENTRY(bottom,                        Bottom),
-    COMPUTED_STYLE_MAP_ENTRY(box_shadow,                    BoxShadow),
-    COMPUTED_STYLE_MAP_ENTRY(caption_side,                  CaptionSide),
-    COMPUTED_STYLE_MAP_ENTRY(clear,                         Clear),
-    COMPUTED_STYLE_MAP_ENTRY(clip,                          Clip),
-    COMPUTED_STYLE_MAP_ENTRY(color,                         Color),
-    COMPUTED_STYLE_MAP_ENTRY(content,                       Content),
-    COMPUTED_STYLE_MAP_ENTRY(counter_increment,             CounterIncrement),
-    COMPUTED_STYLE_MAP_ENTRY(counter_reset,                 CounterReset),
-    COMPUTED_STYLE_MAP_ENTRY(cursor,                        Cursor),
-    COMPUTED_STYLE_MAP_ENTRY(direction,                     Direction),
-    COMPUTED_STYLE_MAP_ENTRY(display,                       Display),
-    COMPUTED_STYLE_MAP_ENTRY(empty_cells,                   EmptyCells),
-    COMPUTED_STYLE_MAP_ENTRY(flex_basis,                    FlexBasis),
-    COMPUTED_STYLE_MAP_ENTRY(flex_direction,                FlexDirection),
-    COMPUTED_STYLE_MAP_ENTRY(flex_grow,                     FlexGrow),
-    COMPUTED_STYLE_MAP_ENTRY(flex_shrink,                   FlexShrink),
-    COMPUTED_STYLE_MAP_ENTRY(float,                         Float),
-    //// COMPUTED_STYLE_MAP_ENTRY(font,                     Font),
-    COMPUTED_STYLE_MAP_ENTRY(font_family,                   FontFamily),
-    COMPUTED_STYLE_MAP_ENTRY(font_kerning,                  FontKerning),
-    COMPUTED_STYLE_MAP_ENTRY(font_size,                     FontSize),
-    COMPUTED_STYLE_MAP_ENTRY(font_size_adjust,              FontSizeAdjust),
-    COMPUTED_STYLE_MAP_ENTRY(font_stretch,                  FontStretch),
-    COMPUTED_STYLE_MAP_ENTRY(font_style,                    FontStyle),
-    COMPUTED_STYLE_MAP_ENTRY(font_synthesis,                FontSynthesis),
-    COMPUTED_STYLE_MAP_ENTRY(font_variant,                  FontVariant),
-    COMPUTED_STYLE_MAP_ENTRY(font_variant_alternates,       FontVariantAlternates),
-    COMPUTED_STYLE_MAP_ENTRY(font_variant_caps,             FontVariantCaps),
-    COMPUTED_STYLE_MAP_ENTRY(font_variant_east_asian,       FontVariantEastAsian),
-    COMPUTED_STYLE_MAP_ENTRY(font_variant_ligatures,        FontVariantLigatures),
-    COMPUTED_STYLE_MAP_ENTRY(font_variant_numeric,          FontVariantNumeric),
-    COMPUTED_STYLE_MAP_ENTRY(font_variant_position,         FontVariantPosition),
-    COMPUTED_STYLE_MAP_ENTRY(font_weight,                   FontWeight),
-    COMPUTED_STYLE_MAP_ENTRY(height,                        Height),
-    COMPUTED_STYLE_MAP_ENTRY(image_orientation,             ImageOrientation),
-    COMPUTED_STYLE_MAP_ENTRY(ime_mode,                      IMEMode),
-    COMPUTED_STYLE_MAP_ENTRY(justify_content,               JustifyContent),
-    COMPUTED_STYLE_MAP_ENTRY(left,                          Left),
-    COMPUTED_STYLE_MAP_ENTRY(letter_spacing,                LetterSpacing),
-    COMPUTED_STYLE_MAP_ENTRY(line_height,                   LineHeight),
-    //// COMPUTED_STYLE_MAP_ENTRY(list_style,               ListStyle),
-    COMPUTED_STYLE_MAP_ENTRY(list_style_image,              ListStyleImage),
-    COMPUTED_STYLE_MAP_ENTRY(list_style_position,           ListStylePosition),
-    COMPUTED_STYLE_MAP_ENTRY(list_style_type,               ListStyleType),
-    //// COMPUTED_STYLE_MAP_ENTRY(margin,                   Margin),
-    COMPUTED_STYLE_MAP_ENTRY(margin_bottom,                 MarginBottomWidth),
-    COMPUTED_STYLE_MAP_ENTRY(margin_left,                   MarginLeftWidth),
-    COMPUTED_STYLE_MAP_ENTRY(margin_right,                  MarginRightWidth),
-    COMPUTED_STYLE_MAP_ENTRY(margin_top,                    MarginTopWidth),
-    COMPUTED_STYLE_MAP_ENTRY(marker_offset,                 MarkerOffset),
-    // COMPUTED_STYLE_MAP_ENTRY(marks,                      Marks),
-    COMPUTED_STYLE_MAP_ENTRY(max_height,                    MaxHeight),
-    COMPUTED_STYLE_MAP_ENTRY(max_width,                     MaxWidth),
-    COMPUTED_STYLE_MAP_ENTRY(min_height,                    MinHeight),
-    COMPUTED_STYLE_MAP_ENTRY(min_width,                     MinWidth),
-    COMPUTED_STYLE_MAP_ENTRY(mix_blend_mode,                MixBlendMode),
-    COMPUTED_STYLE_MAP_ENTRY(opacity,                       Opacity),
-    // COMPUTED_STYLE_MAP_ENTRY(orphans,                    Orphans),
-    //// COMPUTED_STYLE_MAP_ENTRY(outline,                  Outline),
-    COMPUTED_STYLE_MAP_ENTRY(order,                         Order),
-    COMPUTED_STYLE_MAP_ENTRY(outline_color,                 OutlineColor),
-    COMPUTED_STYLE_MAP_ENTRY(outline_offset,                OutlineOffset),
-    COMPUTED_STYLE_MAP_ENTRY(outline_style,                 OutlineStyle),
-    COMPUTED_STYLE_MAP_ENTRY(outline_width,                 OutlineWidth),
-    COMPUTED_STYLE_MAP_ENTRY(overflow,                      Overflow),
-    COMPUTED_STYLE_MAP_ENTRY(overflow_x,                    OverflowX),
-    COMPUTED_STYLE_MAP_ENTRY(overflow_y,                    OverflowY),
-    //// COMPUTED_STYLE_MAP_ENTRY(padding,                  Padding),
-    COMPUTED_STYLE_MAP_ENTRY(padding_bottom,                PaddingBottom),
-    COMPUTED_STYLE_MAP_ENTRY(padding_left,                  PaddingLeft),
-    COMPUTED_STYLE_MAP_ENTRY(padding_right,                 PaddingRight),
-    COMPUTED_STYLE_MAP_ENTRY(padding_top,                   PaddingTop),
-    // COMPUTED_STYLE_MAP_ENTRY(page,                       Page),
-    COMPUTED_STYLE_MAP_ENTRY(page_break_after,              PageBreakAfter),
-    COMPUTED_STYLE_MAP_ENTRY(page_break_before,             PageBreakBefore),
-    COMPUTED_STYLE_MAP_ENTRY(page_break_inside,             PageBreakInside),
-    COMPUTED_STYLE_MAP_ENTRY(perspective,                   Perspective),
-    COMPUTED_STYLE_MAP_ENTRY(perspective_origin,            PerspectiveOrigin),
-    COMPUTED_STYLE_MAP_ENTRY(pointer_events,                PointerEvents),
-    COMPUTED_STYLE_MAP_ENTRY(position,                      Position),
-    COMPUTED_STYLE_MAP_ENTRY(quotes,                        Quotes),
-    COMPUTED_STYLE_MAP_ENTRY(resize,                        Resize),
-    COMPUTED_STYLE_MAP_ENTRY(right,                         Right),
-    //// COMPUTED_STYLE_MAP_ENTRY(size,                     Size),
-    COMPUTED_STYLE_MAP_ENTRY(table_layout,                  TableLayout),
-    COMPUTED_STYLE_MAP_ENTRY(text_align,                    TextAlign),
-    COMPUTED_STYLE_MAP_ENTRY(text_combine_horizontal,       TextCombineHorizontal),
-    COMPUTED_STYLE_MAP_ENTRY(text_decoration,               TextDecoration),
-    COMPUTED_STYLE_MAP_ENTRY(text_indent,                   TextIndent),
-    COMPUTED_STYLE_MAP_ENTRY(text_orientation,              TextOrientation),
-    COMPUTED_STYLE_MAP_ENTRY(text_overflow,                 TextOverflow),
-    COMPUTED_STYLE_MAP_ENTRY(text_shadow,                   TextShadow),
-    COMPUTED_STYLE_MAP_ENTRY(text_transform,                TextTransform),
-    COMPUTED_STYLE_MAP_ENTRY(top,                           Top),
-    COMPUTED_STYLE_MAP_ENTRY(transform,                     Transform),
-    COMPUTED_STYLE_MAP_ENTRY(transform_origin,              TransformOrigin),
-    COMPUTED_STYLE_MAP_ENTRY(transform_style,               TransformStyle),
-    //// COMPUTED_STYLE_MAP_ENTRY(transition,               Transition),
-    COMPUTED_STYLE_MAP_ENTRY(transition_delay,              TransitionDelay),
-    COMPUTED_STYLE_MAP_ENTRY(transition_duration,           TransitionDuration),
-    COMPUTED_STYLE_MAP_ENTRY(transition_property,           TransitionProperty),
-    COMPUTED_STYLE_MAP_ENTRY(transition_timing_function,    TransitionTimingFunction),
-    COMPUTED_STYLE_MAP_ENTRY(unicode_bidi,                  UnicodeBidi),
-    COMPUTED_STYLE_MAP_ENTRY(vertical_align,                VerticalAlign),
-    COMPUTED_STYLE_MAP_ENTRY(visibility,                    Visibility),
-    COMPUTED_STYLE_MAP_ENTRY(white_space,                   WhiteSpace),
-    // COMPUTED_STYLE_MAP_ENTRY(widows,                     Widows),
-    COMPUTED_STYLE_MAP_ENTRY(width,                         Width),
-    COMPUTED_STYLE_MAP_ENTRY(word_break,                    WordBreak),
-    COMPUTED_STYLE_MAP_ENTRY(word_spacing,                  WordSpacing),
-    COMPUTED_STYLE_MAP_ENTRY(word_wrap,                     WordWrap),
-    COMPUTED_STYLE_MAP_ENTRY(writing_mode,                  WritingMode),
-    COMPUTED_STYLE_MAP_ENTRY(z_index,                       ZIndex),
-
-    /* ******************************* *\
-     * Implementations of -moz- styles *
-    \* ******************************* */
-
-    COMPUTED_STYLE_MAP_ENTRY(appearance,                    Appearance),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_background_inline_policy, BackgroundInlinePolicy),
-    COMPUTED_STYLE_MAP_ENTRY(binding,                       Binding),
-    COMPUTED_STYLE_MAP_ENTRY(border_bottom_colors,          BorderBottomColors),
-    COMPUTED_STYLE_MAP_ENTRY(border_left_colors,            BorderLeftColors),
-    COMPUTED_STYLE_MAP_ENTRY(border_right_colors,           BorderRightColors),
-    COMPUTED_STYLE_MAP_ENTRY(border_top_colors,             BorderTopColors),
-    COMPUTED_STYLE_MAP_ENTRY(box_align,                     BoxAlign),
-    COMPUTED_STYLE_MAP_ENTRY(box_direction,                 BoxDirection),
-    COMPUTED_STYLE_MAP_ENTRY(box_flex,                      BoxFlex),
-    COMPUTED_STYLE_MAP_ENTRY(box_ordinal_group,             BoxOrdinalGroup),
-    COMPUTED_STYLE_MAP_ENTRY(box_orient,                    BoxOrient),
-    COMPUTED_STYLE_MAP_ENTRY(box_pack,                      BoxPack),
-    COMPUTED_STYLE_MAP_ENTRY(box_sizing,                    BoxSizing),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_column_count,             ColumnCount),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_column_fill,              ColumnFill),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_column_gap,               ColumnGap),
-    //// COMPUTED_STYLE_MAP_ENTRY(_moz_column_rule,         ColumnRule),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_column_rule_color,        ColumnRuleColor),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_column_rule_style,        ColumnRuleStyle),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_column_rule_width,        ColumnRuleWidth),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_column_width,             ColumnWidth),
-    COMPUTED_STYLE_MAP_ENTRY(float_edge,                    FloatEdge),
-    COMPUTED_STYLE_MAP_ENTRY(font_feature_settings,         FontFeatureSettings),
-    COMPUTED_STYLE_MAP_ENTRY(font_language_override,        FontLanguageOverride),
-    COMPUTED_STYLE_MAP_ENTRY(force_broken_image_icon,       ForceBrokenImageIcon),
-    COMPUTED_STYLE_MAP_ENTRY(hyphens,                       Hyphens),
-    COMPUTED_STYLE_MAP_ENTRY(image_region,                  ImageRegion),
-    COMPUTED_STYLE_MAP_ENTRY(orient,                        Orient),
-    COMPUTED_STYLE_MAP_ENTRY(osx_font_smoothing,            OSXFontSmoothing),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_outline_radius_bottomLeft, OutlineRadiusBottomLeft),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_outline_radius_bottomRight,OutlineRadiusBottomRight),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_outline_radius_topLeft,    OutlineRadiusTopLeft),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_outline_radius_topRight,   OutlineRadiusTopRight),
-    COMPUTED_STYLE_MAP_ENTRY(stack_sizing,                  StackSizing),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_tab_size,                 TabSize),
-    COMPUTED_STYLE_MAP_ENTRY(text_align_last,               TextAlignLast),
-    COMPUTED_STYLE_MAP_ENTRY(text_decoration_color,         TextDecorationColor),
-    COMPUTED_STYLE_MAP_ENTRY(text_decoration_line,          TextDecorationLine),
-    COMPUTED_STYLE_MAP_ENTRY(text_decoration_style,         TextDecorationStyle),
-    COMPUTED_STYLE_MAP_ENTRY(text_size_adjust,              TextSizeAdjust),
-    COMPUTED_STYLE_MAP_ENTRY(user_focus,                    UserFocus),
-    COMPUTED_STYLE_MAP_ENTRY(user_input,                    UserInput),
-    COMPUTED_STYLE_MAP_ENTRY(user_modify,                   UserModify),
-    COMPUTED_STYLE_MAP_ENTRY(user_select,                   UserSelect),
-    COMPUTED_STYLE_MAP_ENTRY(_moz_window_shadow,            WindowShadow),
-
-    /* ***************************** *\
-     * Implementations of SVG styles *
-    \* ***************************** */
-
-    COMPUTED_STYLE_MAP_ENTRY(clip_path,                     ClipPath),
-    COMPUTED_STYLE_MAP_ENTRY(clip_rule,                     ClipRule),
-    COMPUTED_STYLE_MAP_ENTRY(color_interpolation,           ColorInterpolation),
-    COMPUTED_STYLE_MAP_ENTRY(color_interpolation_filters,   ColorInterpolationFilters),
-    COMPUTED_STYLE_MAP_ENTRY(dominant_baseline,             DominantBaseline),
-    COMPUTED_STYLE_MAP_ENTRY(fill,                          Fill),
-    COMPUTED_STYLE_MAP_ENTRY(fill_opacity,                  FillOpacity),
-    COMPUTED_STYLE_MAP_ENTRY(fill_rule,                     FillRule),
-    COMPUTED_STYLE_MAP_ENTRY(filter,                        Filter),
-    COMPUTED_STYLE_MAP_ENTRY(flood_color,                   FloodColor),
-    COMPUTED_STYLE_MAP_ENTRY(flood_opacity,                 FloodOpacity),
-    COMPUTED_STYLE_MAP_ENTRY(image_rendering,               ImageRendering),
-    COMPUTED_STYLE_MAP_ENTRY(lighting_color,                LightingColor),
-    COMPUTED_STYLE_MAP_ENTRY(marker_end,                    MarkerEnd),
-    COMPUTED_STYLE_MAP_ENTRY(marker_mid,                    MarkerMid),
-    COMPUTED_STYLE_MAP_ENTRY(marker_start,                  MarkerStart),
-    COMPUTED_STYLE_MAP_ENTRY(mask,                          Mask),
-    COMPUTED_STYLE_MAP_ENTRY(mask_type,                     MaskType),
-    COMPUTED_STYLE_MAP_ENTRY(paint_order,                   PaintOrder),
-    COMPUTED_STYLE_MAP_ENTRY(shape_rendering,               ShapeRendering),
-    COMPUTED_STYLE_MAP_ENTRY(stop_color,                    StopColor),
-    COMPUTED_STYLE_MAP_ENTRY(stop_opacity,                  StopOpacity),
-    COMPUTED_STYLE_MAP_ENTRY(stroke,                        Stroke),
-    COMPUTED_STYLE_MAP_ENTRY(stroke_dasharray,              StrokeDasharray),
-    COMPUTED_STYLE_MAP_ENTRY(stroke_dashoffset,             StrokeDashoffset),
-    COMPUTED_STYLE_MAP_ENTRY(stroke_linecap,                StrokeLinecap),
-    COMPUTED_STYLE_MAP_ENTRY(stroke_linejoin,               StrokeLinejoin),
-    COMPUTED_STYLE_MAP_ENTRY(stroke_miterlimit,             StrokeMiterlimit),
-    COMPUTED_STYLE_MAP_ENTRY(stroke_opacity,                StrokeOpacity),
-    COMPUTED_STYLE_MAP_ENTRY(stroke_width,                  StrokeWidth),
-    COMPUTED_STYLE_MAP_ENTRY(text_anchor,                   TextAnchor),
-    COMPUTED_STYLE_MAP_ENTRY(text_rendering,                TextRendering),
-    COMPUTED_STYLE_MAP_ENTRY(vector_effect,                 VectorEffect)
-
+static int
+MarkComputedStyleMapDirty(const char* aPref, void* aData)
+{
+  static_cast<nsComputedStyleMap*>(aData)->MarkDirty();
+  return 0;
+}
+
+/* static */ nsComputedStyleMap*
+nsComputedDOMStyle::GetComputedStyleMap()
+{
+  static nsComputedStyleMap map = {
+    {
+#define COMPUTED_STYLE_PROP(prop_, method_) \
+  { eCSSProperty_##prop_, &nsComputedDOMStyle::DoGet##method_ },
+#include "nsComputedDOMStylePropertyList.h"
+#undef COMPUTED_STYLE_PROP
+    }
   };
-
-  *aLength = ArrayLength(map);
-
-  return map;
-}
-
+  return &map;
+}
+
+/* static */ void
+nsComputedDOMStyle::RegisterPrefChangeCallbacks()
+{
+  // Note that this will register callbacks for all properties with prefs, not
+  // just those that are implemented on computed style objects, as it's not
+  // easy to grab specific property data from nsCSSPropList.h based on the
+  // entries iterated in nsComputedDOMStylePropertyList.h.
+  nsComputedStyleMap* data = GetComputedStyleMap();
+#define REGISTER_CALLBACK(pref_)                                             \
+  if (pref_[0]) {                                                            \
+    Preferences::RegisterCallback(MarkComputedStyleMapDirty, pref_, data);   \
+  }
+#define CSS_PROP(prop_, id_, method_, flags_, pref_, parsevariant_,          \
+                 kwtable_, stylestruct_, stylestructoffset_, animtype_)      \
+  REGISTER_CALLBACK(pref_)
+#include "nsCSSPropList.h"
+#undef CSS_PROP
+#undef REGISTER_CALLBACK
+}
+
+/* static */ void
+nsComputedDOMStyle::UnregisterPrefChangeCallbacks()
+{
+  nsComputedStyleMap* data = GetComputedStyleMap();
+#define UNREGISTER_CALLBACK(pref_)                                             \
+  if (pref_[0]) {                                                              \
+    Preferences::UnregisterCallback(MarkComputedStyleMapDirty, pref_, data);   \
+  }
+#define CSS_PROP(prop_, id_, method_, flags_, pref_, parsevariant_,            \
+                 kwtable_, stylestruct_, stylestructoffset_, animtype_)        \
+  UNREGISTER_CALLBACK(pref_)
+#include "nsCSSPropList.h"
+#undef CSS_PROP
+#undef UNREGISTER_CALLBACK
+}
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -23,16 +23,17 @@
 #include "nsIContent.h"
 
 namespace mozilla {
 namespace dom {
 class Element;
 }
 }
 
+struct nsComputedStyleMap;
 class nsIFrame;
 class nsIPresShell;
 class nsDOMCSSValueList;
 class nsMargin;
 class nsROCSSPrimitiveValue;
 class nsStyleBackground;
 class nsStyleBorder;
 class nsStyleContent;
@@ -123,16 +124,19 @@ public:
   // compile errors.
   virtual mozilla::css::Declaration* GetCSSDeclaration(bool) MOZ_OVERRIDE;
   virtual nsresult SetCSSDeclaration(mozilla::css::Declaration*) MOZ_OVERRIDE;
   virtual nsIDocument* DocToUpdate() MOZ_OVERRIDE;
   virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) MOZ_OVERRIDE;
 
   static nsROCSSPrimitiveValue* MatrixToCSSValue(gfx3DMatrix& aMatrix);
 
+  static void RegisterPrefChangeCallbacks();
+  static void UnregisterPrefChangeCallbacks();
+
 private:
   void AssertFlushedPendingReflows() {
     NS_ASSERTION(mFlushedPendingReflows,
                  "property getter should have been marked layout-dependent");
   }
 
   nsMargin GetAdjustedValuesForBoxSizing();
 
@@ -532,32 +536,17 @@ private:
   bool GetFrameBorderRectWidth(nscoord& aWidth);
   bool GetFrameBorderRectHeight(nscoord& aHeight);
 
   /* Helper functions for computing the filter property style. */
   void SetCssTextToCoord(nsAString& aCssText, const nsStyleCoord& aCoord);
   mozilla::dom::CSSValue* CreatePrimitiveValueForStyleFilter(
     const nsStyleFilter& aStyleFilter);
 
-  struct ComputedStyleMapEntry
-  {
-    // Create a pointer-to-member-function type.
-    typedef mozilla::dom::CSSValue* (nsComputedDOMStyle::*ComputeMethod)();
-
-    nsCSSProperty mProperty;
-    ComputeMethod mGetter;
-
-    bool IsLayoutFlushNeeded() const
-    {
-      return nsCSSProps::PropHasFlags(mProperty,
-                                      CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH);
-    }
-  };
-
-  static const ComputedStyleMapEntry* GetQueryablePropertyMap(uint32_t* aLength);
+  static nsComputedStyleMap* GetComputedStyleMap();
 
   // We don't really have a good immutable representation of "presentation".
   // Given the way GetComputedStyle is currently used, we should just grab the
   // 0th presshell, if any, from the document.
   nsWeakPtr mDocumentWeak;
   nsCOMPtr<nsIContent> mContent;
 
   /*
new file mode 100644
--- /dev/null
+++ b/layout/style/nsComputedDOMStylePropertyList.h
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * a list of the CSS properties that are exposed on nsComputedDOMStyle
+ * objects, for preprocessing
+ */
+
+/******
+
+  This file contains the list of CSS properties that are exposed
+  on nsComputedDOMStyle objects.  It is designed to be included in
+  nsComputedDOMStyle.cpp to generate the "computed style map", a
+  table of property IDs and corresponding functions on nsComputedDOMStyle
+  that return the CSSValue representing that property's computed value.
+
+  The COMPUTED_STYLE_PROP macro is defined for each such property.
+  Its arguments are:
+
+  -. 'id' the nsCSSProperty ID, without the leading "nsCSSProperty_".
+
+  -. 'method' the nsComputedDOMStyle method name that returns the
+  CSSValue representing that property's computed value, without the leading
+  "Do".
+
+ ******/
+
+/* ******************************************************************* *\
+ * Properties below are listed in alphabetical order.                  *
+ * Please keep them that way.                                          *
+ *                                                                     *
+ * Properties commented out with // are not yet implemented            *
+ * Properties commented out with //// are shorthands and not queryable *
+\* ******************************************************************* */
+
+/* ***************************** *\
+ * Implementations of CSS styles *
+\* ***************************** */
+
+COMPUTED_STYLE_PROP(align_items,                   AlignItems)
+COMPUTED_STYLE_PROP(align_self,                    AlignSelf)
+//// COMPUTED_STYLE_PROP(animation,                Animation)
+COMPUTED_STYLE_PROP(animation_delay,               AnimationDelay)
+COMPUTED_STYLE_PROP(animation_direction,           AnimationDirection)
+COMPUTED_STYLE_PROP(animation_duration,            AnimationDuration)
+COMPUTED_STYLE_PROP(animation_fill_mode,           AnimationFillMode)
+COMPUTED_STYLE_PROP(animation_iteration_count,     AnimationIterationCount)
+COMPUTED_STYLE_PROP(animation_name,                AnimationName)
+COMPUTED_STYLE_PROP(animation_play_state,          AnimationPlayState)
+COMPUTED_STYLE_PROP(animation_timing_function,     AnimationTimingFunction)
+COMPUTED_STYLE_PROP(backface_visibility,           BackfaceVisibility)
+//// COMPUTED_STYLE_PROP(background,               Background)
+COMPUTED_STYLE_PROP(background_attachment,         BackgroundAttachment)
+COMPUTED_STYLE_PROP(background_clip,               BackgroundClip)
+COMPUTED_STYLE_PROP(background_color,              BackgroundColor)
+COMPUTED_STYLE_PROP(background_image,              BackgroundImage)
+COMPUTED_STYLE_PROP(background_origin,             BackgroundOrigin)
+COMPUTED_STYLE_PROP(background_position,           BackgroundPosition)
+COMPUTED_STYLE_PROP(background_repeat,             BackgroundRepeat)
+COMPUTED_STYLE_PROP(background_size,               BackgroundSize)
+//// COMPUTED_STYLE_PROP(border,                   Border)
+//// COMPUTED_STYLE_PROP(border_bottom,            BorderBottom)
+COMPUTED_STYLE_PROP(border_bottom_color,           BorderBottomColor)
+COMPUTED_STYLE_PROP(border_bottom_left_radius,     BorderBottomLeftRadius)
+COMPUTED_STYLE_PROP(border_bottom_right_radius,    BorderBottomRightRadius)
+COMPUTED_STYLE_PROP(border_bottom_style,           BorderBottomStyle)
+COMPUTED_STYLE_PROP(border_bottom_width,           BorderBottomWidth)
+COMPUTED_STYLE_PROP(border_collapse,               BorderCollapse)
+//// COMPUTED_STYLE_PROP(border_color,             BorderColor)
+//// COMPUTED_STYLE_PROP(border_image,             BorderImage)
+COMPUTED_STYLE_PROP(border_image_outset,           BorderImageOutset)
+COMPUTED_STYLE_PROP(border_image_repeat,           BorderImageRepeat)
+COMPUTED_STYLE_PROP(border_image_slice,            BorderImageSlice)
+COMPUTED_STYLE_PROP(border_image_source,           BorderImageSource)
+COMPUTED_STYLE_PROP(border_image_width,            BorderImageWidth)
+//// COMPUTED_STYLE_PROP(border_left,              BorderLeft)
+COMPUTED_STYLE_PROP(border_left_color,             BorderLeftColor)
+COMPUTED_STYLE_PROP(border_left_style,             BorderLeftStyle)
+COMPUTED_STYLE_PROP(border_left_width,             BorderLeftWidth)
+//// COMPUTED_STYLE_PROP(border_right,             BorderRight)
+COMPUTED_STYLE_PROP(border_right_color,            BorderRightColor)
+COMPUTED_STYLE_PROP(border_right_style,            BorderRightStyle)
+COMPUTED_STYLE_PROP(border_right_width,            BorderRightWidth)
+COMPUTED_STYLE_PROP(border_spacing,                BorderSpacing)
+//// COMPUTED_STYLE_PROP(border_style,             BorderStyle)
+//// COMPUTED_STYLE_PROP(border_top,               BorderTop)
+COMPUTED_STYLE_PROP(border_top_color,              BorderTopColor)
+COMPUTED_STYLE_PROP(border_top_left_radius,        BorderTopLeftRadius)
+COMPUTED_STYLE_PROP(border_top_right_radius,       BorderTopRightRadius)
+COMPUTED_STYLE_PROP(border_top_style,              BorderTopStyle)
+COMPUTED_STYLE_PROP(border_top_width,              BorderTopWidth)
+//// COMPUTED_STYLE_PROP(border_width,             BorderWidth)
+COMPUTED_STYLE_PROP(bottom,                        Bottom)
+COMPUTED_STYLE_PROP(box_shadow,                    BoxShadow)
+COMPUTED_STYLE_PROP(caption_side,                  CaptionSide)
+COMPUTED_STYLE_PROP(clear,                         Clear)
+COMPUTED_STYLE_PROP(clip,                          Clip)
+COMPUTED_STYLE_PROP(color,                         Color)
+COMPUTED_STYLE_PROP(content,                       Content)
+COMPUTED_STYLE_PROP(counter_increment,             CounterIncrement)
+COMPUTED_STYLE_PROP(counter_reset,                 CounterReset)
+COMPUTED_STYLE_PROP(cursor,                        Cursor)
+COMPUTED_STYLE_PROP(direction,                     Direction)
+COMPUTED_STYLE_PROP(display,                       Display)
+COMPUTED_STYLE_PROP(empty_cells,                   EmptyCells)
+COMPUTED_STYLE_PROP(flex_basis,                    FlexBasis)
+COMPUTED_STYLE_PROP(flex_direction,                FlexDirection)
+COMPUTED_STYLE_PROP(flex_grow,                     FlexGrow)
+COMPUTED_STYLE_PROP(flex_shrink,                   FlexShrink)
+COMPUTED_STYLE_PROP(float,                         Float)
+//// COMPUTED_STYLE_PROP(font,                     Font)
+COMPUTED_STYLE_PROP(font_family,                   FontFamily)
+COMPUTED_STYLE_PROP(font_kerning,                  FontKerning)
+COMPUTED_STYLE_PROP(font_size,                     FontSize)
+COMPUTED_STYLE_PROP(font_size_adjust,              FontSizeAdjust)
+COMPUTED_STYLE_PROP(font_stretch,                  FontStretch)
+COMPUTED_STYLE_PROP(font_style,                    FontStyle)
+COMPUTED_STYLE_PROP(font_synthesis,                FontSynthesis)
+COMPUTED_STYLE_PROP(font_variant,                  FontVariant)
+COMPUTED_STYLE_PROP(font_variant_alternates,       FontVariantAlternates)
+COMPUTED_STYLE_PROP(font_variant_caps,             FontVariantCaps)
+COMPUTED_STYLE_PROP(font_variant_east_asian,       FontVariantEastAsian)
+COMPUTED_STYLE_PROP(font_variant_ligatures,        FontVariantLigatures)
+COMPUTED_STYLE_PROP(font_variant_numeric,          FontVariantNumeric)
+COMPUTED_STYLE_PROP(font_variant_position,         FontVariantPosition)
+COMPUTED_STYLE_PROP(font_weight,                   FontWeight)
+COMPUTED_STYLE_PROP(height,                        Height)
+COMPUTED_STYLE_PROP(image_orientation,             ImageOrientation)
+COMPUTED_STYLE_PROP(ime_mode,                      IMEMode)
+COMPUTED_STYLE_PROP(justify_content,               JustifyContent)
+COMPUTED_STYLE_PROP(left,                          Left)
+COMPUTED_STYLE_PROP(letter_spacing,                LetterSpacing)
+COMPUTED_STYLE_PROP(line_height,                   LineHeight)
+//// COMPUTED_STYLE_PROP(list_style,               ListStyle)
+COMPUTED_STYLE_PROP(list_style_image,              ListStyleImage)
+COMPUTED_STYLE_PROP(list_style_position,           ListStylePosition)
+COMPUTED_STYLE_PROP(list_style_type,               ListStyleType)
+//// COMPUTED_STYLE_PROP(margin,                   Margin)
+COMPUTED_STYLE_PROP(margin_bottom,                 MarginBottomWidth)
+COMPUTED_STYLE_PROP(margin_left,                   MarginLeftWidth)
+COMPUTED_STYLE_PROP(margin_right,                  MarginRightWidth)
+COMPUTED_STYLE_PROP(margin_top,                    MarginTopWidth)
+COMPUTED_STYLE_PROP(marker_offset,                 MarkerOffset)
+// COMPUTED_STYLE_PROP(marks,                      Marks)
+COMPUTED_STYLE_PROP(max_height,                    MaxHeight)
+COMPUTED_STYLE_PROP(max_width,                     MaxWidth)
+COMPUTED_STYLE_PROP(min_height,                    MinHeight)
+COMPUTED_STYLE_PROP(min_width,                     MinWidth)
+COMPUTED_STYLE_PROP(mix_blend_mode,                MixBlendMode)
+COMPUTED_STYLE_PROP(opacity,                       Opacity)
+// COMPUTED_STYLE_PROP(orphans,                    Orphans)
+//// COMPUTED_STYLE_PROP(outline,                  Outline)
+COMPUTED_STYLE_PROP(order,                         Order)
+COMPUTED_STYLE_PROP(outline_color,                 OutlineColor)
+COMPUTED_STYLE_PROP(outline_offset,                OutlineOffset)
+COMPUTED_STYLE_PROP(outline_style,                 OutlineStyle)
+COMPUTED_STYLE_PROP(outline_width,                 OutlineWidth)
+COMPUTED_STYLE_PROP(overflow,                      Overflow)
+COMPUTED_STYLE_PROP(overflow_x,                    OverflowX)
+COMPUTED_STYLE_PROP(overflow_y,                    OverflowY)
+//// COMPUTED_STYLE_PROP(padding,                  Padding)
+COMPUTED_STYLE_PROP(padding_bottom,                PaddingBottom)
+COMPUTED_STYLE_PROP(padding_left,                  PaddingLeft)
+COMPUTED_STYLE_PROP(padding_right,                 PaddingRight)
+COMPUTED_STYLE_PROP(padding_top,                   PaddingTop)
+// COMPUTED_STYLE_PROP(page,                       Page)
+COMPUTED_STYLE_PROP(page_break_after,              PageBreakAfter)
+COMPUTED_STYLE_PROP(page_break_before,             PageBreakBefore)
+COMPUTED_STYLE_PROP(page_break_inside,             PageBreakInside)
+COMPUTED_STYLE_PROP(perspective,                   Perspective)
+COMPUTED_STYLE_PROP(perspective_origin,            PerspectiveOrigin)
+COMPUTED_STYLE_PROP(pointer_events,                PointerEvents)
+COMPUTED_STYLE_PROP(position,                      Position)
+COMPUTED_STYLE_PROP(quotes,                        Quotes)
+COMPUTED_STYLE_PROP(resize,                        Resize)
+COMPUTED_STYLE_PROP(right,                         Right)
+//// COMPUTED_STYLE_PROP(size,                     Size)
+COMPUTED_STYLE_PROP(table_layout,                  TableLayout)
+COMPUTED_STYLE_PROP(text_align,                    TextAlign)
+COMPUTED_STYLE_PROP(text_combine_horizontal,       TextCombineHorizontal)
+COMPUTED_STYLE_PROP(text_decoration,               TextDecoration)
+COMPUTED_STYLE_PROP(text_indent,                   TextIndent)
+COMPUTED_STYLE_PROP(text_orientation,              TextOrientation)
+COMPUTED_STYLE_PROP(text_overflow,                 TextOverflow)
+COMPUTED_STYLE_PROP(text_shadow,                   TextShadow)
+COMPUTED_STYLE_PROP(text_transform,                TextTransform)
+COMPUTED_STYLE_PROP(top,                           Top)
+COMPUTED_STYLE_PROP(transform,                     Transform)
+COMPUTED_STYLE_PROP(transform_origin,              TransformOrigin)
+COMPUTED_STYLE_PROP(transform_style,               TransformStyle)
+//// COMPUTED_STYLE_PROP(transition,               Transition)
+COMPUTED_STYLE_PROP(transition_delay,              TransitionDelay)
+COMPUTED_STYLE_PROP(transition_duration,           TransitionDuration)
+COMPUTED_STYLE_PROP(transition_property,           TransitionProperty)
+COMPUTED_STYLE_PROP(transition_timing_function,    TransitionTimingFunction)
+COMPUTED_STYLE_PROP(unicode_bidi,                  UnicodeBidi)
+COMPUTED_STYLE_PROP(vertical_align,                VerticalAlign)
+COMPUTED_STYLE_PROP(visibility,                    Visibility)
+COMPUTED_STYLE_PROP(white_space,                   WhiteSpace)
+// COMPUTED_STYLE_PROP(widows,                     Widows)
+COMPUTED_STYLE_PROP(width,                         Width)
+COMPUTED_STYLE_PROP(word_break,                    WordBreak)
+COMPUTED_STYLE_PROP(word_spacing,                  WordSpacing)
+COMPUTED_STYLE_PROP(word_wrap,                     WordWrap)
+COMPUTED_STYLE_PROP(writing_mode,                  WritingMode)
+COMPUTED_STYLE_PROP(z_index,                       ZIndex)
+
+/* ******************************* *\
+ * Implementations of -moz- styles *
+\* ******************************* */
+
+COMPUTED_STYLE_PROP(appearance,                    Appearance)
+COMPUTED_STYLE_PROP(_moz_background_inline_policy, BackgroundInlinePolicy)
+COMPUTED_STYLE_PROP(binding,                       Binding)
+COMPUTED_STYLE_PROP(border_bottom_colors,          BorderBottomColors)
+COMPUTED_STYLE_PROP(border_left_colors,            BorderLeftColors)
+COMPUTED_STYLE_PROP(border_right_colors,           BorderRightColors)
+COMPUTED_STYLE_PROP(border_top_colors,             BorderTopColors)
+COMPUTED_STYLE_PROP(box_align,                     BoxAlign)
+COMPUTED_STYLE_PROP(box_direction,                 BoxDirection)
+COMPUTED_STYLE_PROP(box_flex,                      BoxFlex)
+COMPUTED_STYLE_PROP(box_ordinal_group,             BoxOrdinalGroup)
+COMPUTED_STYLE_PROP(box_orient,                    BoxOrient)
+COMPUTED_STYLE_PROP(box_pack,                      BoxPack)
+COMPUTED_STYLE_PROP(box_sizing,                    BoxSizing)
+COMPUTED_STYLE_PROP(_moz_column_count,             ColumnCount)
+COMPUTED_STYLE_PROP(_moz_column_fill,              ColumnFill)
+COMPUTED_STYLE_PROP(_moz_column_gap,               ColumnGap)
+//// COMPUTED_STYLE_PROP(_moz_column_rule,         ColumnRule)
+COMPUTED_STYLE_PROP(_moz_column_rule_color,        ColumnRuleColor)
+COMPUTED_STYLE_PROP(_moz_column_rule_style,        ColumnRuleStyle)
+COMPUTED_STYLE_PROP(_moz_column_rule_width,        ColumnRuleWidth)
+COMPUTED_STYLE_PROP(_moz_column_width,             ColumnWidth)
+COMPUTED_STYLE_PROP(float_edge,                    FloatEdge)
+COMPUTED_STYLE_PROP(font_feature_settings,         FontFeatureSettings)
+COMPUTED_STYLE_PROP(font_language_override,        FontLanguageOverride)
+COMPUTED_STYLE_PROP(force_broken_image_icon,       ForceBrokenImageIcon)
+COMPUTED_STYLE_PROP(hyphens,                       Hyphens)
+COMPUTED_STYLE_PROP(image_region,                  ImageRegion)
+COMPUTED_STYLE_PROP(orient,                        Orient)
+COMPUTED_STYLE_PROP(osx_font_smoothing,            OSXFontSmoothing)
+COMPUTED_STYLE_PROP(_moz_outline_radius_bottomLeft, OutlineRadiusBottomLeft)
+COMPUTED_STYLE_PROP(_moz_outline_radius_bottomRight,OutlineRadiusBottomRight)
+COMPUTED_STYLE_PROP(_moz_outline_radius_topLeft,    OutlineRadiusTopLeft)
+COMPUTED_STYLE_PROP(_moz_outline_radius_topRight,   OutlineRadiusTopRight)
+COMPUTED_STYLE_PROP(stack_sizing,                  StackSizing)
+COMPUTED_STYLE_PROP(_moz_tab_size,                 TabSize)
+COMPUTED_STYLE_PROP(text_align_last,               TextAlignLast)
+COMPUTED_STYLE_PROP(text_decoration_color,         TextDecorationColor)
+COMPUTED_STYLE_PROP(text_decoration_line,          TextDecorationLine)
+COMPUTED_STYLE_PROP(text_decoration_style,         TextDecorationStyle)
+COMPUTED_STYLE_PROP(text_size_adjust,              TextSizeAdjust)
+COMPUTED_STYLE_PROP(user_focus,                    UserFocus)
+COMPUTED_STYLE_PROP(user_input,                    UserInput)
+COMPUTED_STYLE_PROP(user_modify,                   UserModify)
+COMPUTED_STYLE_PROP(user_select,                   UserSelect)
+COMPUTED_STYLE_PROP(_moz_window_shadow,            WindowShadow)
+
+/* ***************************** *\
+ * Implementations of SVG styles *
+\* ***************************** */
+
+COMPUTED_STYLE_PROP(clip_path,                     ClipPath)
+COMPUTED_STYLE_PROP(clip_rule,                     ClipRule)
+COMPUTED_STYLE_PROP(color_interpolation,           ColorInterpolation)
+COMPUTED_STYLE_PROP(color_interpolation_filters,   ColorInterpolationFilters)
+COMPUTED_STYLE_PROP(dominant_baseline,             DominantBaseline)
+COMPUTED_STYLE_PROP(fill,                          Fill)
+COMPUTED_STYLE_PROP(fill_opacity,                  FillOpacity)
+COMPUTED_STYLE_PROP(fill_rule,                     FillRule)
+COMPUTED_STYLE_PROP(filter,                        Filter)
+COMPUTED_STYLE_PROP(flood_color,                   FloodColor)
+COMPUTED_STYLE_PROP(flood_opacity,                 FloodOpacity)
+COMPUTED_STYLE_PROP(image_rendering,               ImageRendering)
+COMPUTED_STYLE_PROP(lighting_color,                LightingColor)
+COMPUTED_STYLE_PROP(marker_end,                    MarkerEnd)
+COMPUTED_STYLE_PROP(marker_mid,                    MarkerMid)
+COMPUTED_STYLE_PROP(marker_start,                  MarkerStart)
+COMPUTED_STYLE_PROP(mask,                          Mask)
+COMPUTED_STYLE_PROP(mask_type,                     MaskType)
+COMPUTED_STYLE_PROP(paint_order,                   PaintOrder)
+COMPUTED_STYLE_PROP(shape_rendering,               ShapeRendering)
+COMPUTED_STYLE_PROP(stop_color,                    StopColor)
+COMPUTED_STYLE_PROP(stop_opacity,                  StopOpacity)
+COMPUTED_STYLE_PROP(stroke,                        Stroke)
+COMPUTED_STYLE_PROP(stroke_dasharray,              StrokeDasharray)
+COMPUTED_STYLE_PROP(stroke_dashoffset,             StrokeDashoffset)
+COMPUTED_STYLE_PROP(stroke_linecap,                StrokeLinecap)
+COMPUTED_STYLE_PROP(stroke_linejoin,               StrokeLinejoin)
+COMPUTED_STYLE_PROP(stroke_miterlimit,             StrokeMiterlimit)
+COMPUTED_STYLE_PROP(stroke_opacity,                StrokeOpacity)
+COMPUTED_STYLE_PROP(stroke_width,                  StrokeWidth)
+COMPUTED_STYLE_PROP(text_anchor,                   TextAnchor)
+COMPUTED_STYLE_PROP(text_rendering,                TextRendering)
+COMPUTED_STYLE_PROP(vector_effect,                 VectorEffect)
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -648,16 +648,18 @@ GetFloatFromBoxPosition(int32_t aEnumVal
 #define SETCOORD_INITIAL_HUNDRED_PCT    0x00004000
 #define SETCOORD_INITIAL_FACTOR_ONE     0x00008000
 #define SETCOORD_INITIAL_FACTOR_ZERO    0x00010000
 #define SETCOORD_CALC_LENGTH_ONLY       0x00020000
 #define SETCOORD_CALC_CLAMP_NONNEGATIVE 0x00040000 // modifier for CALC_LENGTH_ONLY
 #define SETCOORD_STORE_CALC             0x00080000
 #define SETCOORD_BOX_POSITION           0x00100000 // exclusive with _ENUMERATED
 #define SETCOORD_ANGLE                  0x00200000
+#define SETCOORD_UNSET_INHERIT          0x00400000
+#define SETCOORD_UNSET_INITIAL          0x00800000
 
 #define SETCOORD_LP     (SETCOORD_LENGTH | SETCOORD_PERCENT)
 #define SETCOORD_LH     (SETCOORD_LENGTH | SETCOORD_INHERIT)
 #define SETCOORD_AH     (SETCOORD_AUTO | SETCOORD_INHERIT)
 #define SETCOORD_LAH    (SETCOORD_AUTO | SETCOORD_LENGTH | SETCOORD_INHERIT)
 #define SETCOORD_LPH    (SETCOORD_LP | SETCOORD_INHERIT)
 #define SETCOORD_LPAH   (SETCOORD_LP | SETCOORD_AH)
 #define SETCOORD_LPEH   (SETCOORD_LP | SETCOORD_ENUMERATED | SETCOORD_INHERIT)
@@ -709,18 +711,20 @@ static bool SetCoord(const nsCSSValue& a
   else if (((aMask & SETCOORD_BOX_POSITION) != 0) &&
            (aValue.GetUnit() == eCSSUnit_Enumerated)) {
     aCoord.SetPercentValue(GetFloatFromBoxPosition(aValue.GetIntValue()));
   }
   else if (((aMask & SETCOORD_AUTO) != 0) &&
            (aValue.GetUnit() == eCSSUnit_Auto)) {
     aCoord.SetAutoValue();
   }
-  else if (((aMask & SETCOORD_INHERIT) != 0) &&
-           (aValue.GetUnit() == eCSSUnit_Inherit)) {
+  else if ((((aMask & SETCOORD_INHERIT) != 0) &&
+            aValue.GetUnit() == eCSSUnit_Inherit) ||
+           (((aMask & SETCOORD_UNSET_INHERIT) != 0) &&
+            aValue.GetUnit() == eCSSUnit_Unset)) {
     aCoord = aParentCoord;  // just inherit value from parent
     aCanStoreInRuleTree = false;
   }
   else if (((aMask & SETCOORD_NORMAL) != 0) &&
            (aValue.GetUnit() == eCSSUnit_Normal)) {
     aCoord.SetNormalValue();
   }
   else if (((aMask & SETCOORD_NONE) != 0) &&
@@ -731,17 +735,19 @@ static bool SetCoord(const nsCSSValue& a
            (aValue.GetUnit() == eCSSUnit_Number)) {
     aCoord.SetFactorValue(aValue.GetFloatValue());
   }
   else if (((aMask & SETCOORD_STORE_CALC) != 0) &&
            (aValue.IsCalcUnit())) {
     SpecifiedCalcToComputedCalc(aValue, aCoord, aStyleContext,
                                 aCanStoreInRuleTree);
   }
-  else if (aValue.GetUnit() == eCSSUnit_Initial) {
+  else if (aValue.GetUnit() == eCSSUnit_Initial ||
+           (aValue.GetUnit() == eCSSUnit_Unset &&
+            ((aMask & SETCOORD_UNSET_INITIAL) != 0))) {
     if ((aMask & SETCOORD_INITIAL_AUTO) != 0) {
       aCoord.SetAutoValue();
     }
     else if ((aMask & SETCOORD_INITIAL_ZERO) != 0) {
       aCoord.SetCoordValue(0);
     }
     else if ((aMask & SETCOORD_INITIAL_FACTOR_ZERO) != 0) {
       aCoord.SetFactorValue(0.0f);
@@ -780,23 +786,25 @@ static bool SetCoord(const nsCSSValue& a
   }
   else {
     result = false;  // didn't set anything
   }
   return result;
 }
 
 // This inline function offers a shortcut for SetCoord() by refusing to accept
-// SETCOORD_LENGTH and SETCOORD_INHERIT masks.
+// SETCOORD_LENGTH, SETCOORD_INHERIT and SETCOORD_UNSET_* masks.
 static inline bool SetAbsCoord(const nsCSSValue& aValue,
                                  nsStyleCoord& aCoord,
                                  int32_t aMask)
 {
-  NS_ABORT_IF_FALSE((aMask & SETCOORD_LH) == 0,
-                    "does not handle SETCOORD_LENGTH and SETCOORD_INHERIT");
+  NS_ABORT_IF_FALSE((aMask & (SETCOORD_LH | SETCOORD_UNSET_INHERIT |
+                              SETCOORD_UNSET_INITIAL)) == 0,
+                    "does not handle SETCOORD_LENGTH, SETCOORD_INHERIT and "
+                    "SETCOORD_UNSET_*");
 
   // The values of the following variables will never be used; so it does not
   // matter what to set.
   const nsStyleCoord dummyParentCoord;
   nsStyleContext* dummyStyleContext = nullptr;
   nsPresContext* dummyPresContext = nullptr;
   bool dummyCanStoreInRuleTree = true;
 
@@ -1117,16 +1125,18 @@ static void SetStyleImage(nsStyleContext
 // where possible
 
 #define SETDSC_NORMAL                 0x01   // N
 #define SETDSC_AUTO                   0x02   // A
 #define SETDSC_INTEGER                0x40   // I
 #define SETDSC_ENUMERATED             0x80   // E
 #define SETDSC_NONE                   0x100  // O
 #define SETDSC_SYSTEM_FONT            0x2000
+#define SETDSC_UNSET_INHERIT          0x00400000
+#define SETDSC_UNSET_INITIAL          0x00800000
 
 // no caller cares whether aField was changed or not
 template <typename FieldT,
           typename T1, typename T2, typename T3, typename T4, typename T5>
 static void
 SetDiscrete(const nsCSSValue& aValue, FieldT & aField,
             bool& aCanStoreInRuleTree, uint32_t aMask,
             FieldT aParentValue,
@@ -1191,27 +1201,41 @@ SetDiscrete(const nsCSSValue& aValue, Fi
 
   case eCSSUnit_System_Font:
     if (aMask & SETDSC_SYSTEM_FONT) {
       aField = aSystemFontValue;
       return;
     }
     break;
 
+  case eCSSUnit_Unset:
+    if (aMask & SETDSC_UNSET_INHERIT) {
+      aCanStoreInRuleTree = false;
+      aField = aParentValue;
+      return;
+    }
+    if (aMask & SETDSC_UNSET_INITIAL) {
+      aField = aInitialValue;
+      return;
+    }
+    break;
+
   default:
     break;
   }
 
   NS_NOTREACHED("SetDiscrete: inappropriate unit");
 }
 
 // flags for SetFactor
 #define SETFCT_POSITIVE 0x01        // assert value is >= 0.0f
 #define SETFCT_OPACITY  0x02        // clamp value to [0.0f .. 1.0f]
 #define SETFCT_NONE     0x04        // allow _None (uses aInitialValue).
+#define SETFCT_UNSET_INHERIT  0x00400000
+#define SETFCT_UNSET_INITIAL  0x00800000
 
 static void
 SetFactor(const nsCSSValue& aValue, float& aField, bool& aCanStoreInRuleTree,
           float aParentValue, float aInitialValue, uint32_t aFlags = 0)
 {
   switch (aValue.GetUnit()) {
   case eCSSUnit_Null:
     return;
@@ -1242,16 +1266,28 @@ SetFactor(const nsCSSValue& aValue, floa
 
   case eCSSUnit_None:
     if (aFlags & SETFCT_NONE) {
       aField = aInitialValue;
       return;
     }
     break;
 
+  case eCSSUnit_Unset:
+    if (aFlags & SETFCT_UNSET_INHERIT) {
+      aCanStoreInRuleTree = false;
+      aField = aParentValue;
+      return;
+    }
+    if (aFlags & SETFCT_UNSET_INITIAL) {
+      aField = aInitialValue;
+      return;
+    }
+    break;
+
   default:
     break;
   }
 
   NS_NOTREACHED("SetFactor: inappropriate unit");
 }
 
 // Overloaded new operator. Initializes the memory to 0 and relies on an arena
@@ -1530,26 +1566,31 @@ nsRuleNode::PropagateDependentBit(nsStyl
  */
 typedef nsRuleNode::RuleDetail
   (* CheckCallbackFn)(const nsRuleData* aRuleData,
                       nsRuleNode::RuleDetail aResult);
 
 /**
  * @param aValue the value being examined
  * @param aSpecifiedCount to be incremented by one if the value is specified
- * @param aInherited to be incremented by one if the value is set to inherit
+ * @param aInheritedCount to be incremented by one if the value is set to inherit
+ * @param aUnsetCount to be incremented by one if the value is set to unset
  */
 inline void
 ExamineCSSValue(const nsCSSValue& aValue,
-                uint32_t& aSpecifiedCount, uint32_t& aInheritedCount)
+                uint32_t& aSpecifiedCount,
+                uint32_t& aInheritedCount,
+                uint32_t& aUnsetCount)
 {
   if (aValue.GetUnit() != eCSSUnit_Null) {
     ++aSpecifiedCount;
     if (aValue.GetUnit() == eCSSUnit_Inherit) {
       ++aInheritedCount;
+    } else if (aValue.GetUnit() == eCSSUnit_Unset) {
+      ++aUnsetCount;
     }
   }
 }
 
 static nsRuleNode::RuleDetail
 CheckFontCallback(const nsRuleData* aRuleData,
                   nsRuleNode::RuleDetail aResult)
 {
@@ -1798,27 +1839,34 @@ AreAllMathMLPropertiesUndefined(const ns
 
 inline nsRuleNode::RuleDetail
 nsRuleNode::CheckSpecifiedProperties(const nsStyleStructID aSID,
                                      const nsRuleData* aRuleData)
 {
   // Build a count of the:
   uint32_t total = 0,      // total number of props in the struct
            specified = 0,  // number that were specified for this node
-           inherited = 0;  // number that were 'inherit' (and not
+           inherited = 0,  // number that were 'inherit' (and not
                            //   eCSSUnit_Inherit) for this node
+           unset = 0;      // number that were 'unset'
 
   // See comment in nsRuleData.h above mValueOffsets.
   NS_ABORT_IF_FALSE(aRuleData->mValueOffsets[aSID] == 0,
                     "we assume the value offset is zero instead of adding it");
   for (nsCSSValue *values = aRuleData->mValueStorage,
               *values_end = values + nsCSSProps::PropertyCountInStruct(aSID);
        values != values_end; ++values) {
     ++total;
-    ExamineCSSValue(*values, specified, inherited);
+    ExamineCSSValue(*values, specified, inherited, unset);
+  }
+
+  if (!nsCachedStyleData::IsReset(aSID)) {
+    // For inherited properties, 'unset' means the same as 'inherit'.
+    inherited += unset;
+    unset = 0;
   }
 
 #if 0
   printf("CheckSpecifiedProperties: SID=%d total=%d spec=%d inh=%d.\n",
          aSID, total, specified, inherited);
 #endif
 
   NS_ASSERTION(aSID != eStyleStruct_Font ||
@@ -2980,17 +3028,18 @@ nsRuleNode::SetFontSize(nsPresContext* a
     // The calc ops will always zoom its result according to the value
     // of aParentFont->mAllowZoom.
     sizeIsZoomedAccordingToParent = true;
   }
   else if (eCSSUnit_System_Font == sizeValue->GetUnit()) {
     // this becomes our cascading size
     *aSize = aSystemFont.size;
   }
-  else if (eCSSUnit_Inherit == sizeValue->GetUnit()) {
+  else if (eCSSUnit_Inherit == sizeValue->GetUnit() ||
+           eCSSUnit_Unset == sizeValue->GetUnit()) {
     aCanStoreInRuleTree = false;
     // We apply scriptlevel change for this case, because the default is
     // to inherit and we don't want explicit "inherit" to differ from the
     // default.
     *aSize = aScriptLevelAdjustedParentSize;
     sizeIsZoomedAccordingToParent = true;
   }
   else if (eCSSUnit_Initial == sizeValue->GetUnit()) {
@@ -3171,17 +3220,18 @@ nsRuleNode::SetFont(nsPresContext* aPres
     // SetGenericFont (bug 380915).
     aFont->mGenericID = aGenericFontID;
   }
   else if (eCSSUnit_System_Font == familyValue->GetUnit()) {
     aFont->mFont.name = systemFont.name;
     aFont->mFont.systemFont = true;
     aFont->mGenericID = kGenericFont_NONE;
   }
-  else if (eCSSUnit_Inherit == familyValue->GetUnit()) {
+  else if (eCSSUnit_Inherit == familyValue->GetUnit() ||
+           eCSSUnit_Unset == familyValue->GetUnit()) {
     aCanStoreInRuleTree = false;
     aFont->mFont.name = aParentFont->mFont.name;
     aFont->mFont.systemFont = aParentFont->mFont.systemFont;
     aFont->mGenericID = aParentFont->mGenericID;
   }
   else if (eCSSUnit_Initial == familyValue->GetUnit()) {
     aFont->mFont.name = defaultVariableFont->name;
     aFont->mFont.systemFont = defaultVariableFont->systemFont;
@@ -3194,33 +3244,33 @@ nsRuleNode::SetFont(nsPresContext* aPres
   // ComputeFontData, because we could have a start struct.
   if (aGenericFontID != kGenericFont_NONE) {
     aFont->mGenericID = aGenericFontID;
   }
 
   // font-smoothing: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForOSXFontSmoothing(),
               aFont->mFont.smoothing, aCanStoreInRuleTree,
-              SETDSC_ENUMERATED,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
               aParentFont->mFont.smoothing,
               defaultVariableFont->smoothing,
               0, 0, 0, 0);
 
   // font-style: enum, inherit, initial, -moz-system-font
   SetDiscrete(*aRuleData->ValueForFontStyle(),
               aFont->mFont.style, aCanStoreInRuleTree,
-              SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT,
+              SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT,
               aParentFont->mFont.style,
               defaultVariableFont->style,
               0, 0, 0, systemFont.style);
 
   // font-variant: enum, inherit, initial, -moz-system-font
   SetDiscrete(*aRuleData->ValueForFontVariant(),
               aFont->mFont.variant, aCanStoreInRuleTree,
-              SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT,
+              SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT,
               aParentFont->mFont.variant,
               defaultVariableFont->variant,
               0, 0, 0, systemFont.variant);
 
   // font-weight: int, enum, inherit, initial, -moz-system-font
   // special handling for enum
   const nsCSSValue* weightValue = aRuleData->ValueForFontWeight();
   if (eCSSUnit_Enumerated == weightValue->GetUnit()) {
@@ -3252,25 +3302,25 @@ nsRuleNode::SetFont(nsPresContext* aPres
         } else {
           aFont->mFont.weight = 700;
         }
         break;
       }
     }
   } else
     SetDiscrete(*weightValue, aFont->mFont.weight, aCanStoreInRuleTree,
-                SETDSC_INTEGER | SETDSC_SYSTEM_FONT,
+                SETDSC_INTEGER | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT,
                 aParentFont->mFont.weight,
                 defaultVariableFont->weight,
                 0, 0, 0, systemFont.weight);
 
   // font-stretch: enum, inherit, initial, -moz-system-font
   SetDiscrete(*aRuleData->ValueForFontStretch(),
               aFont->mFont.stretch, aCanStoreInRuleTree,
-              SETDSC_SYSTEM_FONT | SETDSC_ENUMERATED,
+              SETDSC_SYSTEM_FONT | SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
               aParentFont->mFont.stretch,
               defaultVariableFont->stretch,
               0, 0, 0, systemFont.stretch);
 
   // Compute scriptlevel, scriptminsize and scriptsizemultiplier now so
   // they're available for font-size computation.
 
   // -moz-script-min-size: length
@@ -3286,60 +3336,63 @@ nsRuleNode::SetFont(nsPresContext* aPres
                      aCanStoreInRuleTree);
   }
 
   // -moz-script-size-multiplier: factor, inherit, initial
   SetFactor(*aRuleData->ValueForScriptSizeMultiplier(),
             aFont->mScriptSizeMultiplier,
             aCanStoreInRuleTree, aParentFont->mScriptSizeMultiplier,
             NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER,
-            SETFCT_POSITIVE);
+            SETFCT_POSITIVE | SETFCT_UNSET_INHERIT);
 
   // -moz-script-level: integer, number, inherit
   const nsCSSValue* scriptLevelValue = aRuleData->ValueForScriptLevel();
   if (eCSSUnit_Integer == scriptLevelValue->GetUnit()) {
     // "relative"
     aFont->mScriptLevel = ClampTo8Bit(aParentFont->mScriptLevel + scriptLevelValue->GetIntValue());
   }
   else if (eCSSUnit_Number == scriptLevelValue->GetUnit()) {
     // "absolute"
     aFont->mScriptLevel = ClampTo8Bit(int32_t(scriptLevelValue->GetFloatValue()));
   }
-  else if (eCSSUnit_Inherit == scriptLevelValue->GetUnit()) {
+  else if (eCSSUnit_Inherit == scriptLevelValue->GetUnit() ||
+           eCSSUnit_Unset == scriptLevelValue->GetUnit()) {
     aCanStoreInRuleTree = false;
     aFont->mScriptLevel = aParentFont->mScriptLevel;
   }
   else if (eCSSUnit_Initial == scriptLevelValue->GetUnit()) {
     aFont->mScriptLevel = 0;
   }
 
   // font-kerning: none, enum, inherit, initial, -moz-system-font
   SetDiscrete(*aRuleData->ValueForFontKerning(),
               aFont->mFont.kerning, aCanStoreInRuleTree,
-              SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT,
+              SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT,
               aParentFont->mFont.kerning,
               defaultVariableFont->kerning,
               0, 0, 0, systemFont.kerning);
 
   // font-synthesis: none, enum (bit field), inherit, initial, -moz-system-font
   SetDiscrete(*aRuleData->ValueForFontSynthesis(),
               aFont->mFont.synthesis, aCanStoreInRuleTree,
-              SETDSC_NONE | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT,
+              SETDSC_NONE | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT |
+                SETDSC_UNSET_INHERIT,
               aParentFont->mFont.synthesis,
               defaultVariableFont->synthesis,
               0, 0, 0, systemFont.synthesis);
 
   // font-variant-alternates: normal, enum (bit field) + functions, inherit,
   //                          initial, -moz-system-font
   const nsCSSValue* variantAlternatesValue =
     aRuleData->ValueForFontVariantAlternates();
   int32_t variantAlternates = 0;
 
   switch (variantAlternatesValue->GetUnit()) {
   case eCSSUnit_Inherit:
+  case eCSSUnit_Unset:
     aFont->mFont.CopyAlternates(aParentFont->mFont);
     aCanStoreInRuleTree = false;
     break;
 
   case eCSSUnit_Initial:
   case eCSSUnit_Normal:
     aFont->mFont.variantAlternates = 0;
     aFont->mFont.alternateValues.Clear();
@@ -3368,53 +3421,58 @@ nsRuleNode::SetFont(nsPresContext* aPres
 
   default:
     break;
   }
 
   // font-variant-caps: normal, enum, inherit, initial, -moz-system-font
   SetDiscrete(*aRuleData->ValueForFontVariantCaps(),
               aFont->mFont.variantCaps, aCanStoreInRuleTree,
-              SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT,
+              SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT |
+                SETDSC_UNSET_INHERIT,
               aParentFont->mFont.variantCaps,
               defaultVariableFont->variantCaps,
               0, 0, 0, systemFont.variantCaps);
 
   // font-variant-east-asian: normal, enum (bit field), inherit, initial,
   //                          -moz-system-font
   SetDiscrete(*aRuleData->ValueForFontVariantEastAsian(),
               aFont->mFont.variantEastAsian, aCanStoreInRuleTree,
-              SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT,
+              SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT |
+                SETDSC_UNSET_INHERIT,
               aParentFont->mFont.variantEastAsian,
               defaultVariableFont->variantEastAsian,
               0, 0, 0, systemFont.variantEastAsian);
 
   // font-variant-ligatures: normal, enum (bit field), inherit, initial,
   //                         -moz-system-font
   SetDiscrete(*aRuleData->ValueForFontVariantLigatures(),
               aFont->mFont.variantLigatures, aCanStoreInRuleTree,
-              SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT,
+              SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT |
+                SETDSC_UNSET_INHERIT,
               aParentFont->mFont.variantLigatures,
               defaultVariableFont->variantLigatures,
               0, 0, 0, systemFont.variantLigatures);
 
   // font-variant-numeric: normal, enum (bit field), inherit, initial,
   //                       -moz-system-font
   SetDiscrete(*aRuleData->ValueForFontVariantNumeric(),
               aFont->mFont.variantNumeric, aCanStoreInRuleTree,
-              SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT,
+              SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT |
+                SETDSC_UNSET_INHERIT,
               aParentFont->mFont.variantNumeric,
               defaultVariableFont->variantNumeric,
               0, 0, 0, systemFont.variantNumeric);
 
   // font-variant-position: normal, enum, inherit, initial,
   //                        -moz-system-font
   SetDiscrete(*aRuleData->ValueForFontVariantPosition(),
               aFont->mFont.variantPosition, aCanStoreInRuleTree,
-              SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT,
+              SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT |
+                SETDSC_UNSET_INHERIT,
               aParentFont->mFont.variantPosition,
               defaultVariableFont->variantPosition,
               0, 0, 0, systemFont.variantPosition);
 
   // font-feature-settings
   const nsCSSValue* featureSettingsValue =
     aRuleData->ValueForFontFeatureSettings();
 
@@ -3423,16 +3481,17 @@ nsRuleNode::SetFont(nsPresContext* aPres
     break;
 
   case eCSSUnit_Normal:
   case eCSSUnit_Initial:
     aFont->mFont.fontFeatureSettings.Clear();
     break;
 
   case eCSSUnit_Inherit:
+  case eCSSUnit_Unset:
     aCanStoreInRuleTree = false;
     aFont->mFont.fontFeatureSettings = aParentFont->mFont.fontFeatureSettings;
     break;
 
   case eCSSUnit_System_Font:
     aFont->mFont.fontFeatureSettings = systemFont.fontFeatureSettings;
     break;
 
@@ -3445,17 +3504,18 @@ nsRuleNode::SetFont(nsPresContext* aPres
   default:
     NS_ABORT_IF_FALSE(false, "unexpected value unit");
     break;
   }
 
   // font-language-override
   const nsCSSValue* languageOverrideValue =
     aRuleData->ValueForFontLanguageOverride();
-  if (eCSSUnit_Inherit == languageOverrideValue->GetUnit()) {
+  if (eCSSUnit_Inherit == languageOverrideValue->GetUnit() ||
+      eCSSUnit_Unset == languageOverrideValue->GetUnit()) {
     aCanStoreInRuleTree = false;
     aFont->mFont.languageOverride = aParentFont->mFont.languageOverride;
   } else if (eCSSUnit_Normal == languageOverrideValue->GetUnit() ||
              eCSSUnit_Initial == languageOverrideValue->GetUnit()) {
     aFont->mFont.languageOverride.Truncate();
   } else if (eCSSUnit_System_Font == languageOverrideValue->GetUnit()) {
     aFont->mFont.languageOverride = systemFont.languageOverride;
   } else if (eCSSUnit_String == languageOverrideValue->GetUnit()) {
@@ -3510,17 +3570,17 @@ nsRuleNode::SetFont(nsPresContext* aPres
 
   // font-size-adjust: number, none, inherit, initial, -moz-system-font
   const nsCSSValue* sizeAdjustValue = aRuleData->ValueForFontSizeAdjust();
   if (eCSSUnit_System_Font == sizeAdjustValue->GetUnit()) {
     aFont->mFont.sizeAdjust = systemFont.sizeAdjust;
   } else
     SetFactor(*sizeAdjustValue, aFont->mFont.sizeAdjust,
               aCanStoreInRuleTree, aParentFont->mFont.sizeAdjust, 0.0f,
-              SETFCT_NONE);
+              SETFCT_NONE | SETFCT_UNSET_INHERIT);
 }
 
 /* static */ void
 nsRuleNode::ComputeFontFeatures(const nsCSSValuePairList *aFeaturesList,
                                 nsTArray<gfxFontFeature>& aFeatureSettings)
 {
   aFeatureSettings.Clear();
   for (const nsCSSValuePairList* p = aFeaturesList; p; p = p->mNext) {
@@ -3835,34 +3895,35 @@ nsRuleNode::ComputeTextData(void* aStart
                             const RuleDetail aRuleDetail,
                             const bool aCanStoreInRuleTree)
 {
   COMPUTE_START_INHERITED(Text, (), text, parentText)
 
   // tab-size: integer, inherit
   SetDiscrete(*aRuleData->ValueForTabSize(),
               text->mTabSize, canStoreInRuleTree,
-              SETDSC_INTEGER, parentText->mTabSize,
+              SETDSC_INTEGER | SETDSC_UNSET_INHERIT, parentText->mTabSize,
               NS_STYLE_TABSIZE_INITIAL, 0, 0, 0, 0);
 
   // letter-spacing: normal, length, inherit
   SetCoord(*aRuleData->ValueForLetterSpacing(),
            text->mLetterSpacing, parentText->mLetterSpacing,
            SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL |
-             SETCOORD_CALC_LENGTH_ONLY,
+             SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INHERIT,
            aContext, mPresContext, canStoreInRuleTree);
 
   // text-shadow: none, list, inherit, initial
   const nsCSSValue* textShadowValue = aRuleData->ValueForTextShadow();
   if (textShadowValue->GetUnit() != eCSSUnit_Null) {
     text->mTextShadow = nullptr;
 
     // Don't need to handle none/initial explicitly: The above assignment
     // takes care of that
-    if (textShadowValue->GetUnit() == eCSSUnit_Inherit) {
+    if (textShadowValue->GetUnit() == eCSSUnit_Inherit ||
+        textShadowValue->GetUnit() == eCSSUnit_Unset) {
       canStoreInRuleTree = false;
       text->mTextShadow = parentText->mTextShadow;
     } else if (textShadowValue->GetUnit() == eCSSUnit_List ||
                textShadowValue->GetUnit() == eCSSUnit_ListDep) {
       // List of arrays
       text->mTextShadow = GetShadowData(textShadowValue->GetListValue(),
                                         aContext, false, canStoreInRuleTree);
     }
@@ -3878,17 +3939,18 @@ nsRuleNode::ComputeTextData(void* aStart
                        lineHeightValue->GetPercentValue()));
   }
   else if (eCSSUnit_Initial == lineHeightValue->GetUnit() ||
            eCSSUnit_System_Font == lineHeightValue->GetUnit()) {
     text->mLineHeight.SetNormalValue();
   }
   else {
     SetCoord(*lineHeightValue, text->mLineHeight, parentText->mLineHeight,
-             SETCOORD_LEH | SETCOORD_FACTOR | SETCOORD_NORMAL,
+             SETCOORD_LEH | SETCOORD_FACTOR | SETCOORD_NORMAL |
+               SETCOORD_UNSET_INHERIT,
              aContext, mPresContext, canStoreInRuleTree);
     if (lineHeightValue->IsLengthUnit() &&
         !lineHeightValue->IsRelativeLengthUnit()) {
       nscoord lh = nsStyleFont::ZoomText(mPresContext,
                                          text->mLineHeight.GetCoordValue());
 
       canStoreInRuleTree = false;
       const nsStyleFont *font = aContext->StyleFont();
@@ -3914,95 +3976,106 @@ nsRuleNode::ComputeTextData(void* aStart
              NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT ==
                textAlignValue->GetIntValue()) {
     canStoreInRuleTree = false;
     uint8_t parentAlign = parentText->mTextAlign;
     text->mTextAlign = (NS_STYLE_TEXT_ALIGN_DEFAULT == parentAlign) ?
       NS_STYLE_TEXT_ALIGN_CENTER : parentAlign;
   } else
     SetDiscrete(*textAlignValue, text->mTextAlign, canStoreInRuleTree,
-                SETDSC_ENUMERATED, parentText->mTextAlign,
-                NS_STYLE_TEXT_ALIGN_DEFAULT,
-                0, 0, 0, 0);
+                SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
+                parentText->mTextAlign,
+                NS_STYLE_TEXT_ALIGN_DEFAULT, 0, 0, 0, 0);
 
   // text-align-last: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForTextAlignLast(), text->mTextAlignLast,
-              canStoreInRuleTree, SETDSC_ENUMERATED, parentText->mTextAlignLast,
+              canStoreInRuleTree,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
+              parentText->mTextAlignLast,
               NS_STYLE_TEXT_ALIGN_AUTO, 0, 0, 0, 0);
 
   // text-indent: length, percent, calc, inherit, initial
   SetCoord(*aRuleData->ValueForTextIndent(), text->mTextIndent, parentText->mTextIndent,
-           SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC,
+           SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC |
+             SETCOORD_UNSET_INHERIT,
            aContext, mPresContext, canStoreInRuleTree);
 
   // text-transform: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForTextTransform(), text->mTextTransform, canStoreInRuleTree,
-              SETDSC_ENUMERATED, parentText->mTextTransform,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
+              parentText->mTextTransform,
               NS_STYLE_TEXT_TRANSFORM_NONE, 0, 0, 0, 0);
 
   // white-space: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForWhiteSpace(), text->mWhiteSpace, canStoreInRuleTree,
-              SETDSC_ENUMERATED, parentText->mWhiteSpace,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
+              parentText->mWhiteSpace,
               NS_STYLE_WHITESPACE_NORMAL, 0, 0, 0, 0);
 
   // word-break: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForWordBreak(), text->mWordBreak, canStoreInRuleTree,
-              SETDSC_ENUMERATED, parentText->mWordBreak,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
+              parentText->mWordBreak,
               NS_STYLE_WORDBREAK_NORMAL, 0, 0, 0, 0);
 
   // word-spacing: normal, length, inherit
   nsStyleCoord tempCoord;
   const nsCSSValue* wordSpacingValue = aRuleData->ValueForWordSpacing();
   if (SetCoord(*wordSpacingValue, tempCoord,
                nsStyleCoord(parentText->mWordSpacing,
                             nsStyleCoord::CoordConstructor),
                SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL |
-                 SETCOORD_CALC_LENGTH_ONLY,
+                 SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INHERIT,
                aContext, mPresContext, canStoreInRuleTree)) {
     if (tempCoord.GetUnit() == eStyleUnit_Coord) {
       text->mWordSpacing = tempCoord.GetCoordValue();
     } else if (tempCoord.GetUnit() == eStyleUnit_Normal) {
       text->mWordSpacing = 0;
     } else {
       NS_NOTREACHED("unexpected unit");
     }
   } else {
     NS_ASSERTION(wordSpacingValue->GetUnit() == eCSSUnit_Null,
                  "unexpected unit");
   }
 
   // word-wrap: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForWordWrap(), text->mWordWrap, canStoreInRuleTree,
-              SETDSC_ENUMERATED, parentText->mWordWrap,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
+              parentText->mWordWrap,
               NS_STYLE_WORDWRAP_NORMAL, 0, 0, 0, 0);
 
   // hyphens: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForHyphens(), text->mHyphens, canStoreInRuleTree,
-              SETDSC_ENUMERATED, parentText->mHyphens,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
+              parentText->mHyphens,
               NS_STYLE_HYPHENS_MANUAL, 0, 0, 0, 0);
 
   // text-size-adjust: none, auto, inherit, initial
   SetDiscrete(*aRuleData->ValueForTextSizeAdjust(), text->mTextSizeAdjust,
-              canStoreInRuleTree, SETDSC_NONE | SETDSC_AUTO,
+              canStoreInRuleTree,
+              SETDSC_NONE | SETDSC_AUTO | SETDSC_UNSET_INHERIT,
               parentText->mTextSizeAdjust,
               NS_STYLE_TEXT_SIZE_ADJUST_AUTO, // initial value
               NS_STYLE_TEXT_SIZE_ADJUST_AUTO, // auto value
               NS_STYLE_TEXT_SIZE_ADJUST_NONE, // none value
               0, 0);
 
   // text-orientation: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForTextOrientation(), text->mTextOrientation,
-              canStoreInRuleTree, SETDSC_ENUMERATED,
+              canStoreInRuleTree,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
               parentText->mTextOrientation,
               NS_STYLE_TEXT_ORIENTATION_AUTO, 0, 0, 0, 0);
 
   // text-combine-horizontal: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForTextCombineHorizontal(),
               text->mTextCombineHorizontal,
-              canStoreInRuleTree, SETDSC_ENUMERATED,
+              canStoreInRuleTree,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
               parentText->mTextCombineHorizontal,
               NS_STYLE_TEXT_COMBINE_HORIZ_NONE, 0, 0, 0, 0);
 
   COMPUTE_END_INHERITED(Text, text)
 }
 
 const void*
 nsRuleNode::ComputeTextResetData(void* aStartStruct,
@@ -4015,17 +4088,18 @@ nsRuleNode::ComputeTextResetData(void* a
   COMPUTE_START_RESET(TextReset, (), text, parentText)
 
   // vertical-align: enum, length, percent, calc, inherit
   const nsCSSValue* verticalAlignValue = aRuleData->ValueForVerticalAlign();
   if (!SetCoord(*verticalAlignValue, text->mVerticalAlign,
                 parentText->mVerticalAlign,
                 SETCOORD_LPH | SETCOORD_ENUMERATED | SETCOORD_STORE_CALC,
                 aContext, mPresContext, canStoreInRuleTree)) {
-    if (eCSSUnit_Initial == verticalAlignValue->GetUnit()) {
+    if (eCSSUnit_Initial == verticalAlignValue->GetUnit() ||
+        eCSSUnit_Unset == verticalAlignValue->GetUnit()) {
       text->mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE,
                                        eStyleUnit_Enumerated);
     }
   }
 
   // text-decoration-line: enum (bit field), inherit, initial
   const nsCSSValue* decorationLineValue =
     aRuleData->ValueForTextDecorationLine();
@@ -4040,17 +4114,18 @@ nsRuleNode::ComputeTextResetData(void* a
       }
       else {
         text->mTextDecorationLine &= ~NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
       }
     }
   } else if (eCSSUnit_Inherit == decorationLineValue->GetUnit()) {
     canStoreInRuleTree = false;
     text->mTextDecorationLine = parentText->mTextDecorationLine;
-  } else if (eCSSUnit_Initial == decorationLineValue->GetUnit()) {
+  } else if (eCSSUnit_Initial == decorationLineValue->GetUnit() ||
+             eCSSUnit_Unset == decorationLineValue->GetUnit()) {
     text->mTextDecorationLine = NS_STYLE_TEXT_DECORATION_LINE_NONE;
   }
 
   // text-decoration-color: color, string, enum, inherit, initial
   const nsCSSValue* decorationColorValue =
     aRuleData->ValueForTextDecorationColor();
   nscolor decorationColor;
   if (eCSSUnit_Inherit == decorationColorValue->GetUnit()) {
@@ -4071,40 +4146,43 @@ nsRuleNode::ComputeTextResetData(void* a
            decorationColorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR) {
     text->SetDecorationColorToForeground();
   }
   else if (SetColor(*decorationColorValue, 0, mPresContext, aContext,
                     decorationColor, canStoreInRuleTree)) {
     text->SetDecorationColor(decorationColor);
   }
   else if (eCSSUnit_Initial == decorationColorValue->GetUnit() ||
+           eCSSUnit_Unset == decorationColorValue->GetUnit() ||
            eCSSUnit_Enumerated == decorationColorValue->GetUnit()) {
     NS_ABORT_IF_FALSE(eCSSUnit_Enumerated != decorationColorValue->GetUnit() ||
                       decorationColorValue->GetIntValue() ==
                         NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR,
                       "unexpected enumerated value");
     text->SetDecorationColorToForeground();
   }
 
   // text-decoration-style: enum, inherit, initial
   const nsCSSValue* decorationStyleValue =
     aRuleData->ValueForTextDecorationStyle();
   if (eCSSUnit_Enumerated == decorationStyleValue->GetUnit()) {
     text->SetDecorationStyle(decorationStyleValue->GetIntValue());
   } else if (eCSSUnit_Inherit == decorationStyleValue->GetUnit()) {
     text->SetDecorationStyle(parentText->GetDecorationStyle());
     canStoreInRuleTree = false;
-  } else if (eCSSUnit_Initial == decorationStyleValue->GetUnit()) {
+  } else if (eCSSUnit_Initial == decorationStyleValue->GetUnit() ||
+             eCSSUnit_Unset == decorationStyleValue->GetUnit()) {
     text->SetDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID);
   }
 
   // text-overflow: enum, string, pair(enum|string), inherit, initial
   const nsCSSValue* textOverflowValue =
     aRuleData->ValueForTextOverflow();
-  if (eCSSUnit_Initial == textOverflowValue->GetUnit()) {
+  if (eCSSUnit_Initial == textOverflowValue->GetUnit() ||
+      eCSSUnit_Unset == textOverflowValue->GetUnit()) {
     text->mTextOverflow = nsStyleTextOverflow();
   } else if (eCSSUnit_Inherit == textOverflowValue->GetUnit()) {
     canStoreInRuleTree = false;
     text->mTextOverflow = parentText->mTextOverflow;
   } else if (eCSSUnit_Enumerated == textOverflowValue->GetUnit()) {
     // A single enumerated value.
     SetDiscrete(*textOverflowValue, text->mTextOverflow.mRight.mType,
                 canStoreInRuleTree,
@@ -4149,17 +4227,18 @@ nsRuleNode::ComputeTextResetData(void* a
     } else if (eCSSUnit_String == textOverflowRightValue->GetUnit()) {
       textOverflowRightValue->GetStringValue(text->mTextOverflow.mRight.mString);
       text->mTextOverflow.mRight.mType = NS_STYLE_TEXT_OVERFLOW_STRING;
     }
   }
 
   // unicode-bidi: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForUnicodeBidi(), text->mUnicodeBidi, canStoreInRuleTree,
-              SETDSC_ENUMERATED, parentText->mUnicodeBidi,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL,
+              parentText->mUnicodeBidi,
               NS_STYLE_UNICODE_BIDI_NORMAL, 0, 0, 0, 0);
 
   COMPUTE_END_RESET(TextReset, text)
 }
 
 const void*
 nsRuleNode::ComputeUserInterfaceData(void* aStartStruct,
                                      const nsRuleData* aRuleData,
@@ -4173,17 +4252,18 @@ nsRuleNode::ComputeUserInterfaceData(voi
   // cursor: enum, url, inherit
   const nsCSSValue* cursorValue = aRuleData->ValueForCursor();
   nsCSSUnit cursorUnit = cursorValue->GetUnit();
   if (cursorUnit != eCSSUnit_Null) {
     delete [] ui->mCursorArray;
     ui->mCursorArray = nullptr;
     ui->mCursorArrayLength = 0;
 
-    if (cursorUnit == eCSSUnit_Inherit) {
+    if (cursorUnit == eCSSUnit_Inherit ||
+        cursorUnit == eCSSUnit_Unset) {
       canStoreInRuleTree = false;
       ui->mCursor = parentUI->mCursor;
       ui->CopyCursorArrayFrom(*parentUI);
     }
     else if (cursorUnit == eCSSUnit_Initial) {
       ui->mCursor = NS_STYLE_CURSOR_AUTO;
     }
     else {
@@ -4229,30 +4309,33 @@ nsRuleNode::ComputeUserInterfaceData(voi
                    "Unexpected fallback value at end of cursor list");
       ui->mCursor = list->mValue.GetIntValue();
     }
   }
 
   // user-input: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForUserInput(),
               ui->mUserInput, canStoreInRuleTree,
-              SETDSC_ENUMERATED, parentUI->mUserInput,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
+              parentUI->mUserInput,
               NS_STYLE_USER_INPUT_AUTO, 0, 0, 0, 0);
 
   // user-modify: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForUserModify(),
               ui->mUserModify, canStoreInRuleTree,
-              SETDSC_ENUMERATED, parentUI->mUserModify,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
+              parentUI->mUserModify,
               NS_STYLE_USER_MODIFY_READ_ONLY,
               0, 0, 0, 0);
 
   // user-focus: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForUserFocus(),
               ui->mUserFocus, canStoreInRuleTree,
-              SETDSC_ENUMERATED, parentUI->mUserFocus,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
+              parentUI->mUserFocus,
               NS_STYLE_USER_FOCUS_NONE, 0, 0, 0, 0);
 
   COMPUTE_END_INHERITED(UserInterface, ui)
 }
 
 const void*
 nsRuleNode::ComputeUIResetData(void* aStartStruct,
                                const nsRuleData* aRuleData,
@@ -4261,37 +4344,40 @@ nsRuleNode::ComputeUIResetData(void* aSt
                                const RuleDetail aRuleDetail,
                                const bool aCanStoreInRuleTree)
 {
   COMPUTE_START_RESET(UIReset, (), ui, parentUI)
 
   // user-select: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForUserSelect(),
               ui->mUserSelect, canStoreInRuleTree,
-              SETDSC_ENUMERATED, parentUI->mUserSelect,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL,
+              parentUI->mUserSelect,
               NS_STYLE_USER_SELECT_AUTO, 0, 0, 0, 0);
 
   // ime-mode: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForImeMode(),
               ui->mIMEMode, canStoreInRuleTree,
-              SETDSC_ENUMERATED, parentUI->mIMEMode,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL,
+              parentUI->mIMEMode,
               NS_STYLE_IME_MODE_AUTO, 0, 0, 0, 0);
 
   // force-broken-image-icons: integer, inherit, initial
   SetDiscrete(*aRuleData->ValueForForceBrokenImageIcon(),
               ui->mForceBrokenImageIcon,
               canStoreInRuleTree,
-              SETDSC_INTEGER,
+              SETDSC_INTEGER | SETDSC_UNSET_INITIAL,
               parentUI->mForceBrokenImageIcon,
               0, 0, 0, 0, 0);
 
   // -moz-window-shadow: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForWindowShadow(),
               ui->mWindowShadow, canStoreInRuleTree,
-              SETDSC_ENUMERATED, parentUI->mWindowShadow,
+              SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL,
+              parentUI->mWindowShadow,
               NS_STYLE_WINDOW_SHADOW_DEFAULT, 0, 0, 0, 0);
 
   COMPUTE_END_RESET(UIReset, ui)
 }
 
 // Information about each transition or animation property that is
 // constant.
 struct TransitionPropInfo {
@@ -4530,17 +4616,18 @@ nsRuleNode::ComputeDisplayData(void* aSt
       // FIXME (Bug 522599) (for all transition properties): write a test that
       // detects when this was wrong for i >= delay.num if parent had
       // count for this property not equal to length
       NS_ABORT_IF_FALSE(i < parentDisplay->mTransitionDelayCount,
                         "delay.num computed incorrectly");
       NS_ABORT_IF_FALSE(!canStoreInRuleTree,
                         "should have made canStoreInRuleTree false above");
       transition->SetDelay(parentDisplay->mTransitions[i].GetDelay());
-    } else if (delay.unit == eCSSUnit_Initial) {
+    } else if (delay.unit == eCSSUnit_Initial ||
+               delay.unit == eCSSUnit_Unset) {
       transition->SetDelay(0.0);
     } else if (delay.list) {
       switch (delay.list->mValue.GetUnit()) {
         case eCSSUnit_Seconds:
           transition->SetDelay(PR_MSEC_PER_SEC *
                                delay.list->mValue.GetFloatValue());
           break;
         case eCSSUnit_Milliseconds:
@@ -4555,17 +4642,18 @@ nsRuleNode::ComputeDisplayData(void* aSt
       transition->SetDuration(
         display->mTransitions[i % duration.num].GetDuration());
     } else if (duration.unit == eCSSUnit_Inherit) {
       NS_ABORT_IF_FALSE(i < parentDisplay->mTransitionDurationCount,
                         "duration.num computed incorrectly");
       NS_ABORT_IF_FALSE(!canStoreInRuleTree,
                         "should have made canStoreInRuleTree false above");
       transition->SetDuration(parentDisplay->mTransitions[i].GetDuration());
-    } else if (duration.unit == eCSSUnit_Initial) {
+    } else if (duration.unit == eCSSUnit_Initial ||
+               duration.unit == eCSSUnit_Unset) {
       transition->SetDuration(0.0);
     } else if (duration.list) {
       switch (duration.list->mValue.GetUnit()) {
         case eCSSUnit_Seconds:
           transition->SetDuration(PR_MSEC_PER_SEC *
                                   duration.list->mValue.GetFloatValue());
           break;
         case eCSSUnit_Milliseconds:
@@ -4579,17 +4667,18 @@ nsRuleNode::ComputeDisplayData(void* aSt
     if (i >= property.num) {
       transition->CopyPropertyFrom(display->mTransitions[i % property.num]);
     } else if (property.unit == eCSSUnit_Inherit) {
       NS_ABORT_IF_FALSE(i < parentDisplay->mTransitionPropertyCount,
                         "property.num computed incorrectly");
       NS_ABORT_IF_FALSE(!canStoreInRuleTree,
                         "should have made canStoreInRuleTree false above");
       transition->CopyPropertyFrom(parentDisplay->mTransitions[i]);
-    } else if (property.unit == eCSSUnit_Initial) {
+    } else if (property.unit == eCSSUnit_Initial ||
+               property.unit == eCSSUnit_Unset) {
       transition->SetProperty(eCSSPropertyExtra_all_properties);
     } else if (property.unit == eCSSUnit_None) {
       transition->SetProperty(eCSSPropertyExtra_no_properties);
     } else if (property.list) {
       const nsCSSValue &val = property.list->mValue;
 
       if (val.GetUnit() == eCSSUnit_Ident) {
         nsDependentString
@@ -4614,17 +4703,18 @@ nsRuleNode::ComputeDisplayData(void* aSt
         display->mTransitions[i % timingFunction.num].GetTimingFunction());
     } else if (timingFunction.unit == eCSSUnit_Inherit) {
       NS_ABORT_IF_FALSE(i < parentDisplay->mTransitionTimingFunctionCount,
                         "timingFunction.num computed incorrectly");
       NS_ABORT_IF_FALSE(!canStoreInRuleTree,
                         "should have made canStoreInRuleTree false above");
       transition->SetTimingFunction(
         parentDisplay->mTransitions[i].GetTimingFunction());
-    } else if (timingFunction.unit == eCSSUnit_Initial) {
+    } else if (timingFunction.unit == eCSSUnit_Initial ||
+               timingFunction.unit == eCSSUnit_Unset) {
       transition->SetTimingFunction(
         nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
     } else if (timingFunction.list) {
       ComputeTimingFunction(timingFunction.list->mValue,
                             transition->TimingFunctionSlot());
     }
 
     FOR_ALL_TRANSITION_PROPS(p) {
@@ -4693,17 +4783,18 @@ nsRuleNode::ComputeDisplayData(void* aSt
       // FIXME (Bug 522599) (for all animation properties): write a test that
       // detects when this was wrong for i >= animDelay.num if parent had
       // count for this property not equal to length
       NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationDelayCount,
                         "animDelay.num computed incorrectly");
       NS_ABORT_IF_FALSE(!canStoreInRuleTree,
                         "should have made canStoreInRuleTree false above");
       animation->SetDelay(parentDisplay->mAnimations[i].GetDelay());
-    } else if (animDelay.unit == eCSSUnit_Initial) {
+    } else if (animDelay.unit == eCSSUnit_Initial ||
+               animDelay.unit == eCSSUnit_Unset) {
       animation->SetDelay(0.0);
     } else if (animDelay.list) {
       switch (animDelay.list->mValue.GetUnit()) {
         case eCSSUnit_Seconds:
           animation->SetDelay(PR_MSEC_PER_SEC *
                               animDelay.list->mValue.GetFloatValue());
           break;
         case eCSSUnit_Milliseconds:
@@ -4718,17 +4809,18 @@ nsRuleNode::ComputeDisplayData(void* aSt
       animation->SetDuration(
         display->mAnimations[i % animDuration.num].GetDuration());
     } else if (animDuration.unit == eCSSUnit_Inherit) {
       NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationDurationCount,
                         "animDuration.num computed incorrectly");
       NS_ABORT_IF_FALSE(!canStoreInRuleTree,
                         "should have made canStoreInRuleTree false above");
       animation->SetDuration(parentDisplay->mAnimations[i].GetDuration());
-    } else if (animDuration.unit == eCSSUnit_Initial) {
+    } else if (animDuration.unit == eCSSUnit_Initial ||
+               animDuration.unit == eCSSUnit_Unset) {
       animation->SetDuration(0.0);
     } else if (animDuration.list) {
       switch (animDuration.list->mValue.GetUnit()) {
         case eCSSUnit_Seconds:
           animation->SetDuration(PR_MSEC_PER_SEC *
                                  animDuration.list->mValue.GetFloatValue());
           break;
         case eCSSUnit_Milliseconds:
@@ -4742,17 +4834,18 @@ nsRuleNode::ComputeDisplayData(void* aSt
     if (i >= animName.num) {
       animation->SetName(display->mAnimations[i % animName.num].GetName());
     } else if (animName.unit == eCSSUnit_Inherit) {
       NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationNameCount,
                         "animName.num computed incorrectly");
       NS_ABORT_IF_FALSE(!canStoreInRuleTree,
                         "should have made canStoreInRuleTree false above");
       animation->SetName(parentDisplay->mAnimations[i].GetName());
-    } else if (animName.unit == eCSSUnit_Initial) {
+    } else if (animName.unit == eCSSUnit_Initial ||
+               animName.unit == eCSSUnit_Unset) {
       animation->SetName(EmptyString());
     } else if (animName.list) {
       switch (animName.list->mValue.GetUnit()) {
         case eCSSUnit_Ident: {
           nsDependentString
             nameStr(animName.list->mValue.GetStringBufferValue());
           animation->SetName(nameStr);
           break;
@@ -4773,33 +4866,35 @@ nsRuleNode::ComputeDisplayData(void* aSt
         display->mAnimations[i % animTimingFunction.num].GetTimingFunction());
     } else if (animTimingFunction.unit == eCSSUnit_Inherit) {
       NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationTimingFunctionCount,
                         "animTimingFunction.num computed incorrectly");
       NS_ABORT_IF_FALSE(!canStoreInRuleTree,
                         "should have made canStoreInRuleTree false above");
       animation->SetTimingFunction(
         parentDisplay->mAnimations[i].GetTimingFunction());
-    } else if (animTimingFunction.unit == eCSSUnit_Initial) {
+    } else if (animTimingFunction.unit == eCSSUnit_Initial ||
+               animTimingFunction.unit == eCSSUnit_Unset) {
       animation->SetTimingFunction(
         nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
     } else if (animTimingFunction.list) {
       ComputeTimingFunction(animTimingFunction.list->mValue,
                             animation->TimingFunctionSlot());
     }
 
     if (i >= animDirection.num) {
       animation->SetDirection(display->mAnimations[i % animDirection.num].GetDirection());
     } else if (animDirection.unit == eCSSUnit_Inherit) {
       NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationDirectionCount,
                         "animDirection.num computed incorrectly");
       NS_ABORT_IF_FALSE(!canStoreInRuleTree,
                         "should have made canStoreInRuleTree false above");
       animation->SetDirection(parentDisplay->mAnimations[i].GetDirection());
-    } else if (animDirection.unit == eCSSUnit_Initial) {
+    } else if (animDirection.unit == eCSSUnit_Initial ||
+               animDirection.unit == eCSSUnit_Unset) {
       animation->SetDirection(NS_STYLE_ANIMATION_DIRECTION_NORMAL);
     } else if (animDirection.list) {
       NS_ABORT_IF_FALSE(animDirection.list->mValue.GetUnit() == eCSSUnit_Enumerated,
                         nsPrintfCString("Invalid animation-direction unit %d",
                                         animDirection.list->mValue.GetUnit()).get());
 
       animation->SetDirection(animDirection.list->mValue.GetIntValue());
     }
@@ -4807,17 +4902,18 @@ nsRuleNode::ComputeDisplayData(void* aSt
     if (i >= animFillMode.num) {
       animation->SetFillMode(display->mAnimations[i % animFillMode.num].GetFillMode());
     } else if (animFillMode.unit == eCSSUnit_Inherit) {
       NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationFillModeCount,
                         "animFillMode.num computed incorrectly");
       NS_ABORT_IF_FALSE(!canStoreInRuleTree,
                         "should have made canStoreInRuleTree false above");
       animation->SetFillMode(parentDisplay->mAnimations[i].GetFillMode());
-    } else if (animFillMode.unit == eCSSUnit_Initial) {
+    } else if (animFillMode.unit == eCSSUnit_Initial ||
+               animFillMode.unit == eCSSUnit_Unset) {
       animation->SetFillMode(NS_STYLE_ANIMATION_FILL_MODE_NONE);
     } else if (animFillMode.list) {
       NS_ABORT_IF_FALSE(animFillMode.list->mValue.GetUnit() == eCSSUnit_Enumerated,
                         nsPrintfCString("Invalid animation-fill-mode unit %d",
                                         animFillMode.list->mValue.GetUnit()).get());