Merge m-c to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 17 Oct 2014 16:44:58 +0200
changeset 210931 12557e8376a092da0aa576848c4f1edf5679c544
parent 210930 3eb74a2872395f5d810b518544a0fb9e6c6e414a (current diff)
parent 210879 51892b39597a0e4df693061c2ad2f1e6b25a8f62 (diff)
child 210932 e413c11d4eee457e1e128dfc5bfb2b9555283e47
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
milestone36.0a1
Merge m-c to b2g-inbound
toolkit/themes/osx/mozapps/extensions/extensions.svg
toolkit/themes/windows/mozapps/extensions/extensions.svg
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1061335 - CLOBBER for Win32 compiler update.
+Bug 609976 - CLOBBER 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -777,17 +777,25 @@ function gKeywordURIFixup({ target: brow
     ];
     let notification =
       notificationBox.appendNotification(message,"keyword-uri-fixup", null,
                                          notificationBox.PRIORITY_INFO_HIGH,
                                          buttons);
     notification.persistence = 1;
   };
 
-  gDNSService.asyncResolve(hostName, 0, onLookupComplete, Services.tm.mainThread);
+  try {
+    gDNSService.asyncResolve(hostName, 0, onLookupComplete, Services.tm.mainThread);
+  } catch (ex) {
+    // Do nothing if the URL is invalid (we don't want to show a notification in that case).
+    if (ex.result != Cr.NS_ERROR_UNKNOWN_HOST) {
+      // ... otherwise, report:
+      Cu.reportError(ex);
+    }
+  }
 }
 
 // Called when a docshell has attempted to load a page in an incorrect process.
 // This function is responsible for loading the page in the correct process.
 function RedirectLoad({ target: browser, data }) {
   let tab = gBrowser.getTabForBrowser(browser);
   // Flush the tab state before getting it
   TabState.flush(browser);
@@ -6498,17 +6506,17 @@ var gIdentityHandler = {
     // anything useful here.
     let unknown = false;
     try {
       uri.host;
     } catch (e) { unknown = true; }
 
     // Chrome URIs however get special treatment. Some chrome URIs are
     // whitelisted to provide a positive security signal to the user.
-    let whitelist = /^about:(accounts|addons|app-manager|config|crashes|customizing|healthreport|home|newaddon|permissions|preferences|privatebrowsing|sessionrestore|support|welcomeback)/i;
+    let whitelist = /^about:(accounts|addons|app-manager|config|crashes|customizing|healthreport|home|license|newaddon|permissions|preferences|privatebrowsing|rights|sessionrestore|support|welcomeback)/i;
     let isChromeUI = uri.schemeIs("about") && whitelist.test(uri.spec);
     if (isChromeUI) {
       this.setMode(this.IDENTITY_MODE_CHROMEUI);
     } else if (unknown) {
       this.setMode(this.IDENTITY_MODE_UNKNOWN);
     } else if (state & nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) {
       this.setMode(this.IDENTITY_MODE_IDENTIFIED);
     } else if (state & nsIWebProgressListener.STATE_IS_SECURE) {
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -69,8 +69,18 @@ tabpanels {
  * Optimization for tabs that are restored lazily. We can save a good amount of
  * memory that to-be-restored tabs would otherwise consume simply by setting
  * their browsers to 'display: none' as that will prevent them from having to
  * create a presentation and the like.
  */
 browser[pending] {
   display: none;
 }
+
+browser[pendingpaint] {
+  opacity: 0;
+}
+
+tabbrowser[pendingpaint] {
+  background-image: url(chrome://global/skin/spinner.png);
+  background-repeat: no-repeat;
+  background-position: center center;
+}
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -131,16 +131,20 @@
       <field name="_previewMode">
         false
       </field>
 
       <field name="_lastFindValue">
         ""
       </field>
 
+      <field name="_contentWaitingCount">
+        0
+      </field>
+
       <property name="_numPinnedTabs" readonly="true">
         <getter><![CDATA[
           for (var i = 0; i < this.tabs.length; i++) {
             if (!this.tabs[i].pinned)
               break;
           }
           return i;
         ]]></getter>
@@ -3223,16 +3227,41 @@
         </body>
       </method>
       <method name="getStripVisibility">
         <body>
           return this.tabContainer.visible;
         </body>
       </method>
 
+      <method name="_showBusySpinnerRemoteBrowser">
+        <parameter name="aBrowser"/>
+        <body><![CDATA[
+          aBrowser.setAttribute("pendingpaint", "true");
+          if (this._contentWaitingCount <= 0) {
+            // We are not currently spinning
+            this.setAttribute("pendingpaint", "true");
+            this._contentWaitingCount = 1;
+          } else {
+            this._contentWaitingCount++;
+          }
+        ]]></body>
+      </method>
+
+      <method name="_hideBusySpinnerRemoteBrowser">
+        <parameter name="aBrowser"/>
+        <body><![CDATA[
+          aBrowser.removeAttribute("pendingpaint");
+          this._contentWaitingCount--;
+          if (this._contentWaitingCount <= 0) {
+            this.removeAttribute("pendingpaint");
+          }
+        ]]></body>
+      </method>
+
       <method name="_prepareForTabSwitch">
         <parameter name="toTab"/>
         <parameter name="fromTab"/>
         <body><![CDATA[
           const kTabSwitchTimeout = 300;
           let toBrowser = this.getBrowserForTab(toTab);
           let fromBrowser = fromTab ? this.getBrowserForTab(fromTab)
                                     : null;
@@ -3261,26 +3290,29 @@
                 // We switched away or closed the browser before we timed
                 // out. We reject, which will cancel the tab switch.
                 aReject();
               }
             };
 
             let timeoutPromise = new Promise((aResolve, aReject) => {
               timeoutId = setTimeout(() => {
+                this._showBusySpinnerRemoteBrowser(toBrowser);
                 attemptTabSwitch(aResolve, aReject);
               }, kTabSwitchTimeout);
             });
 
             let paintPromise = new Promise((aResolve, aReject) => {
-              toBrowser.addEventListener("MozAfterRemotePaint", function onRemotePaint() {
+              let onRemotePaint = () => {
                 toBrowser.removeEventListener("MozAfterRemotePaint", onRemotePaint);
+                this._hideBusySpinnerRemoteBrowser(toBrowser);
                 clearTimeout(timeoutId);
                 attemptTabSwitch(aResolve, aReject);
-              });
+              };
+              toBrowser.addEventListener("MozAfterRemotePaint", onRemotePaint);
               toBrowser.QueryInterface(Ci.nsIFrameLoaderOwner)
                        .frameLoader
                        .requestNotifyAfterRemotePaint();
                // We need to activate the docShell on the tab we're switching
                // to - otherwise, we won't initiate a remote paint request and
                // therefore we won't get the MozAfterRemotePaint event that we're
                // waiting for.
                // Note that this happens, as we require, even if the timeout in the
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -52,16 +52,17 @@ support-files =
   file_bug906190.sjs
   file_bug970276_popup1.html
   file_bug970276_popup2.html
   file_bug970276_favicon1.ico
   file_bug970276_favicon2.ico
   file_dom_notifications.html
   file_double_close_tab.html
   file_favicon_change.html
+  file_favicon_change_not_in_document.html
   file_fullscreen-window-open.html
   get_user_media.html
   head.js
   healthreport_testRemoteCommands.html
   moz.png
   offlineQuotaNotification.cacheManifest
   offlineQuotaNotification.html
   page_style_sample.html
@@ -319,16 +320,18 @@ skip-if = e10s # Bug 973001 - appears us
 skip-if = e10s # Bug 918663 - DOMLinkAdded events don't make their way to chrome
 [browser_double_close_tab.js]
 skip-if = e10s
 [browser_duplicateIDs.js]
 [browser_drag.js]
 skip-if = true # browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638.
 [browser_favicon_change.js]
 skip-if = e10s
+[browser_favicon_change_not_in_document.js]
+skip-if = e10s
 [browser_findbarClose.js]
 skip-if = e10s # Bug ?????? - test directly manipulates content (tries to grab an iframe directly from content)
 [browser_fullscreen-window-open.js]
 skip-if = buildapp == 'mulet' || e10s || os == "linux" # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly. Linux: Intermittent failures - bug 941575.
 [browser_fxa_oauth.js]
 [browser_gestureSupport.js]
 skip-if = e10s # Bug 863514 - no gesture support.
 [browser_getshortcutoruri.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_favicon_change_not_in_document.js
@@ -0,0 +1,34 @@
+"use strict";
+
+const TEST_URL = "http://mochi.test:8888/browser/browser/base/content/test/general/file_favicon_change_not_in_document.html"
+
+add_task(function*() {
+  let extraTab = gBrowser.selectedTab = gBrowser.addTab();
+  let tabLoaded = promiseTabLoaded(extraTab);
+  extraTab.linkedBrowser.loadURI(TEST_URL);
+  let expectedFavicon = "http://example.org/one-icon";
+  let haveChanged = new Promise.defer();
+  let observer = new MutationObserver(function(mutations) {
+    for (let mut of mutations) {
+      if (mut.attributeName != "image") {
+        continue;
+      }
+      let imageVal = extraTab.getAttribute("image").replace(/#.*$/, "");
+      if (!imageVal) {
+        // The value gets removed because it doesn't load.
+        continue;
+      }
+      is(imageVal, expectedFavicon, "Favicon image should correspond to expected image.");
+      haveChanged.resolve();
+    }
+  });
+  observer.observe(extraTab, {attributes: true});
+  yield tabLoaded;
+  expectedFavicon = "http://example.org/yet-another-icon";
+  haveChanged = new Promise.defer();
+  yield haveChanged.promise;
+  observer.disconnect();
+  gBrowser.removeTab(extraTab);
+});
+
+
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/file_favicon_change_not_in_document.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html><head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <link rel="icon" href="http://example.org/one-icon" type="image/ico" id="i">
+</head>
+<body onload="onload()">
+  <script>
+  function onload() {
+    var ico = document.createElement("link");
+    ico.setAttribute("rel", "icon");
+    ico.setAttribute("type", "image/ico");
+    ico.setAttribute("href", "http://example.org/other-icon");
+    setTimeout(function() {
+      ico.setAttribute("href", "http://example.org/yet-another-icon");
+      document.getElementById("i").remove();
+      document.head.appendChild(ico);
+    }, 1000);
+  }
+  </script>
+</body></html>
+
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -596,25 +596,28 @@ function injectLoopAPI(targetWindow) {
         }
         return appVersionInfo;
       }
     },
 
     /**
      * Composes an email via the external protocol service.
      *
-     * @param {String} subject Subject of the email to send
-     * @param {String} body    Body message of the email to send
+     * @param {String} subject   Subject of the email to send
+     * @param {String} body      Body message of the email to send
+     * @param {String} recipient Recipient email address (optional)
      */
     composeEmail: {
       enumerable: true,
       writable: true,
-      value: function(subject, body) {
-        let mailtoURL = "mailto:?subject=" + encodeURIComponent(subject) + "&" +
-                        "body=" + encodeURIComponent(body);
+      value: function(subject, body, recipient) {
+        recipient = recipient || "";
+        let mailtoURL = "mailto:" + encodeURIComponent(recipient) +
+                        "?subject=" + encodeURIComponent(subject) +
+                        "&body=" + encodeURIComponent(body);
         extProtocolSvc.loadURI(CommonUtils.makeURI(mailtoURL));
       }
     },
 
     /**
      * Adds a value to a telemetry histogram.
      *
      * @param  {string}  histogramId Name of the telemetry histogram to update.
--- a/browser/components/loop/content/js/conversationViews.js
+++ b/browser/components/loop/content/js/conversationViews.js
@@ -7,18 +7,30 @@
 /* global loop:true, React */
 
 var loop = loop || {};
 loop.conversationViews = (function(mozL10n) {
 
   var CALL_STATES = loop.store.CALL_STATES;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
   var sharedActions = loop.shared.actions;
+  var sharedUtils = loop.shared.utils;
   var sharedViews = loop.shared.views;
 
+  // This duplicates a similar function in contacts.jsx that isn't used in the
+  // conversation window. If we get too many of these, we might want to consider
+  // finding a logical place for them to be shared.
+  function _getPreferredEmail(contact) {
+    // A contact may not contain email addresses, but only a phone number.
+    if (!contact.email || contact.email.length === 0) {
+      return { value: "" };
+    }
+    return contact.email.find(e => e.pref) || contact.email[0];
+  }
+
   /**
    * Displays information about the call
    * Caller avatar, name & conversation creation date
    */
   var CallIdentifierView = React.createClass({displayName: 'CallIdentifierView',
     propTypes: {
       peerIdentifier: React.PropTypes.string,
       showIcons: React.PropTypes.bool.isRequired,
@@ -88,35 +100,24 @@ loop.conversationViews = (function(mozL1
    * Allows the view to be extended with different buttons and progress
    * via children properties.
    */
   var ConversationDetailView = React.createClass({displayName: 'ConversationDetailView',
     propTypes: {
       contact: React.PropTypes.object
     },
 
-    // This duplicates a similar function in contacts.jsx that isn't used in the
-    // conversation window. If we get too many of these, we might want to consider
-    // finding a logical place for them to be shared.
-    _getPreferredEmail: function(contact) {
-      // A contact may not contain email addresses, but only a phone number.
-      if (!contact.email || contact.email.length == 0) {
-        return { value: "" };
-      }
-      return contact.email.find(e => e.pref) || contact.email[0];
-    },
-
     render: function() {
       var contactName;
 
       if (this.props.contact.name &&
           this.props.contact.name[0]) {
         contactName = this.props.contact.name[0];
       } else {
-        contactName = this._getPreferredEmail(this.props.contact).value;
+        contactName = _getPreferredEmail(this.props.contact).value;
       }
 
       document.title = contactName;
 
       return (
         React.DOM.div({className: "call-window"}, 
           CallIdentifierView({
             peerIdentifier: contactName, 
@@ -165,64 +166,121 @@ loop.conversationViews = (function(mozL1
       });
 
       return (
         ConversationDetailView({contact: this.props.contact}, 
 
           React.DOM.p({className: "btn-label"}, pendingStateString), 
 
           React.DOM.div({className: "btn-group call-action-group"}, 
-            React.DOM.div({className: "fx-embedded-call-button-spacer"}), 
-              React.DOM.button({className: btnCancelStyles, 
-                      onClick: this.cancelCall}, 
-                mozL10n.get("initiate_call_cancel_button")
-              ), 
-            React.DOM.div({className: "fx-embedded-call-button-spacer"})
+            React.DOM.button({className: btnCancelStyles, 
+                    onClick: this.cancelCall}, 
+              mozL10n.get("initiate_call_cancel_button")
+            )
           )
 
         )
       );
     }
   });
 
   /**
    * Call failed view. Displayed when a call fails.
    */
   var CallFailedView = React.createClass({displayName: 'CallFailedView',
+    mixins: [Backbone.Events],
+
     propTypes: {
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
+      store: React.PropTypes.instanceOf(
+        loop.store.ConversationStore).isRequired,
+      contact: React.PropTypes.object.isRequired,
+      // This is used by the UI showcase.
+      emailLinkError: React.PropTypes.bool,
+    },
+
+    getInitialState: function() {
+      return {
+        emailLinkError: this.props.emailLinkError,
+        emailLinkButtonDisabled: false
+      };
+    },
+
+    componentDidMount: function() {
+      this.listenTo(this.props.store, "change:emailLink",
+                    this._onEmailLinkReceived);
+      this.listenTo(this.props.store, "error:emailLink",
+                    this._onEmailLinkError);
+    },
+
+    componentWillUnmount: function() {
+      this.stopListening(this.props.store);
+    },
+
+    _onEmailLinkReceived: function() {
+      var emailLink = this.props.store.get("emailLink");
+      var contactEmail = _getPreferredEmail(this.props.contact).value;
+      sharedUtils.composeCallUrlEmail(emailLink, contactEmail);
+      window.close();
+    },
+
+    _onEmailLinkError: function() {
+      this.setState({
+        emailLinkError: true,
+        emailLinkButtonDisabled: false
+      });
+    },
+
+    _renderError: function() {
+      if (!this.state.emailLinkError) {
+        return;
+      }
+      return React.DOM.p({className: "error"}, mozL10n.get("unable_retrieve_url"));
     },
 
     retryCall: function() {
       this.props.dispatcher.dispatch(new sharedActions.RetryCall());
     },
 
     cancelCall: function() {
       this.props.dispatcher.dispatch(new sharedActions.CancelCall());
     },
 
+    emailLink: function() {
+      this.setState({
+        emailLinkError: false,
+        emailLinkButtonDisabled: true
+      });
+
+      this.props.dispatcher.dispatch(new sharedActions.FetchEmailLink());
+    },
+
     render: function() {
       return (
         React.DOM.div({className: "call-window"}, 
           React.DOM.h2(null, mozL10n.get("generic_failure_title")), 
 
-          React.DOM.p({className: "btn-label"}, mozL10n.get("generic_failure_no_reason2")), 
+          React.DOM.p({className: "btn-label"}, mozL10n.get("generic_failure_with_reason2")), 
+
+          this._renderError(), 
 
           React.DOM.div({className: "btn-group call-action-group"}, 
-            React.DOM.div({className: "fx-embedded-call-button-spacer"}), 
-              React.DOM.button({className: "btn btn-accept btn-retry", 
-                      onClick: this.retryCall}, 
-                mozL10n.get("retry_call_button")
-              ), 
-            React.DOM.div({className: "fx-embedded-call-button-spacer"}), 
-              React.DOM.button({className: "btn btn-cancel", 
-                      onClick: this.cancelCall}, 
-                mozL10n.get("cancel_button")
-              ), 
-            React.DOM.div({className: "fx-embedded-call-button-spacer"})
+            React.DOM.button({className: "btn btn-cancel", 
+                    onClick: this.cancelCall}, 
+              mozL10n.get("cancel_button")
+            ), 
+            React.DOM.button({className: "btn btn-info btn-retry", 
+                    onClick: this.retryCall}, 
+              mozL10n.get("retry_call_button")
+            ), 
+            React.DOM.button({className: "btn btn-info btn-email", 
+                    onClick: this.emailLink, 
+                    disabled: this.state.emailLinkButtonDisabled}, 
+              mozL10n.get("share_button2")
+            )
           )
         )
       );
     }
   });
 
   var OngoingConversationView = React.createClass({displayName: 'OngoingConversationView',
     propTypes: {
@@ -420,17 +478,19 @@ loop.conversationViews = (function(mozL1
     render: function() {
       switch (this.state.callState) {
         case CALL_STATES.CLOSE: {
           this._closeWindow();
           return null;
         }
         case CALL_STATES.TERMINATED: {
           return (CallFailedView({
-            dispatcher: this.props.dispatcher}
+            dispatcher: this.props.dispatcher, 
+            store: this.props.store, 
+            contact: this.state.contact}
           ));
         }
         case CALL_STATES.ONGOING: {
           return (OngoingConversationView({
             dispatcher: this.props.dispatcher, 
             video: {enabled: !this.state.videoMuted}, 
             audio: {enabled: !this.state.audioMuted}}
             )
@@ -440,17 +500,17 @@ loop.conversationViews = (function(mozL1
           return this._renderFeedbackView();
         }
         default: {
           return (PendingConversationView({
             dispatcher: this.props.dispatcher, 
             callState: this.state.callState, 
             contact: this.state.contact, 
             enableCancelButton: this._isCancellable()}
-          ))
+          ));
         }
       }
     },
   });
 
   return {
     PendingConversationView: PendingConversationView,
     CallIdentifierView: CallIdentifierView,
--- a/browser/components/loop/content/js/conversationViews.jsx
+++ b/browser/components/loop/content/js/conversationViews.jsx
@@ -7,18 +7,30 @@
 /* global loop:true, React */
 
 var loop = loop || {};
 loop.conversationViews = (function(mozL10n) {
 
   var CALL_STATES = loop.store.CALL_STATES;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
   var sharedActions = loop.shared.actions;
+  var sharedUtils = loop.shared.utils;
   var sharedViews = loop.shared.views;
 
+  // This duplicates a similar function in contacts.jsx that isn't used in the
+  // conversation window. If we get too many of these, we might want to consider
+  // finding a logical place for them to be shared.
+  function _getPreferredEmail(contact) {
+    // A contact may not contain email addresses, but only a phone number.
+    if (!contact.email || contact.email.length === 0) {
+      return { value: "" };
+    }
+    return contact.email.find(e => e.pref) || contact.email[0];
+  }
+
   /**
    * Displays information about the call
    * Caller avatar, name & conversation creation date
    */
   var CallIdentifierView = React.createClass({
     propTypes: {
       peerIdentifier: React.PropTypes.string,
       showIcons: React.PropTypes.bool.isRequired,
@@ -88,35 +100,24 @@ loop.conversationViews = (function(mozL1
    * Allows the view to be extended with different buttons and progress
    * via children properties.
    */
   var ConversationDetailView = React.createClass({
     propTypes: {
       contact: React.PropTypes.object
     },
 
-    // This duplicates a similar function in contacts.jsx that isn't used in the
-    // conversation window. If we get too many of these, we might want to consider
-    // finding a logical place for them to be shared.
-    _getPreferredEmail: function(contact) {
-      // A contact may not contain email addresses, but only a phone number.
-      if (!contact.email || contact.email.length == 0) {
-        return { value: "" };
-      }
-      return contact.email.find(e => e.pref) || contact.email[0];
-    },
-
     render: function() {
       var contactName;
 
       if (this.props.contact.name &&
           this.props.contact.name[0]) {
         contactName = this.props.contact.name[0];
       } else {
-        contactName = this._getPreferredEmail(this.props.contact).value;
+        contactName = _getPreferredEmail(this.props.contact).value;
       }
 
       document.title = contactName;
 
       return (
         <div className="call-window">
           <CallIdentifierView
             peerIdentifier={contactName}
@@ -165,64 +166,121 @@ loop.conversationViews = (function(mozL1
       });
 
       return (
         <ConversationDetailView contact={this.props.contact}>
 
           <p className="btn-label">{pendingStateString}</p>
 
           <div className="btn-group call-action-group">
-            <div className="fx-embedded-call-button-spacer"></div>
-              <button className={btnCancelStyles}
-                      onClick={this.cancelCall}>
-                {mozL10n.get("initiate_call_cancel_button")}
-              </button>
-            <div className="fx-embedded-call-button-spacer"></div>
+            <button className={btnCancelStyles}
+                    onClick={this.cancelCall}>
+              {mozL10n.get("initiate_call_cancel_button")}
+            </button>
           </div>
 
         </ConversationDetailView>
       );
     }
   });
 
   /**
    * Call failed view. Displayed when a call fails.
    */
   var CallFailedView = React.createClass({
+    mixins: [Backbone.Events],
+
     propTypes: {
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
+      store: React.PropTypes.instanceOf(
+        loop.store.ConversationStore).isRequired,
+      contact: React.PropTypes.object.isRequired,
+      // This is used by the UI showcase.
+      emailLinkError: React.PropTypes.bool,
+    },
+
+    getInitialState: function() {
+      return {
+        emailLinkError: this.props.emailLinkError,
+        emailLinkButtonDisabled: false
+      };
+    },
+
+    componentDidMount: function() {
+      this.listenTo(this.props.store, "change:emailLink",
+                    this._onEmailLinkReceived);
+      this.listenTo(this.props.store, "error:emailLink",
+                    this._onEmailLinkError);
+    },
+
+    componentWillUnmount: function() {
+      this.stopListening(this.props.store);
+    },
+
+    _onEmailLinkReceived: function() {
+      var emailLink = this.props.store.get("emailLink");
+      var contactEmail = _getPreferredEmail(this.props.contact).value;
+      sharedUtils.composeCallUrlEmail(emailLink, contactEmail);
+      window.close();
+    },
+
+    _onEmailLinkError: function() {
+      this.setState({
+        emailLinkError: true,
+        emailLinkButtonDisabled: false
+      });
+    },
+
+    _renderError: function() {
+      if (!this.state.emailLinkError) {
+        return;
+      }
+      return <p className="error">{mozL10n.get("unable_retrieve_url")}</p>;
     },
 
     retryCall: function() {
       this.props.dispatcher.dispatch(new sharedActions.RetryCall());
     },
 
     cancelCall: function() {
       this.props.dispatcher.dispatch(new sharedActions.CancelCall());
     },
 
+    emailLink: function() {
+      this.setState({
+        emailLinkError: false,
+        emailLinkButtonDisabled: true
+      });
+
+      this.props.dispatcher.dispatch(new sharedActions.FetchEmailLink());
+    },
+
     render: function() {
       return (
         <div className="call-window">
           <h2>{mozL10n.get("generic_failure_title")}</h2>
 
-          <p className="btn-label">{mozL10n.get("generic_failure_no_reason2")}</p>
+          <p className="btn-label">{mozL10n.get("generic_failure_with_reason2")}</p>
+
+          {this._renderError()}
 
           <div className="btn-group call-action-group">
-            <div className="fx-embedded-call-button-spacer"></div>
-              <button className="btn btn-accept btn-retry"
-                      onClick={this.retryCall}>
-                {mozL10n.get("retry_call_button")}
-              </button>
-            <div className="fx-embedded-call-button-spacer"></div>
-              <button className="btn btn-cancel"
-                      onClick={this.cancelCall}>
-                {mozL10n.get("cancel_button")}
-              </button>
-            <div className="fx-embedded-call-button-spacer"></div>
+            <button className="btn btn-cancel"
+                    onClick={this.cancelCall}>
+              {mozL10n.get("cancel_button")}
+            </button>
+            <button className="btn btn-info btn-retry"
+                    onClick={this.retryCall}>
+              {mozL10n.get("retry_call_button")}
+            </button>
+            <button className="btn btn-info btn-email"
+                    onClick={this.emailLink}
+                    disabled={this.state.emailLinkButtonDisabled}>
+              {mozL10n.get("share_button2")}
+            </button>
           </div>
         </div>
       );
     }
   });
 
   var OngoingConversationView = React.createClass({
     propTypes: {
@@ -421,16 +479,18 @@ loop.conversationViews = (function(mozL1
       switch (this.state.callState) {
         case CALL_STATES.CLOSE: {
           this._closeWindow();
           return null;
         }
         case CALL_STATES.TERMINATED: {
           return (<CallFailedView
             dispatcher={this.props.dispatcher}
+            store={this.props.store}
+            contact={this.state.contact}
           />);
         }
         case CALL_STATES.ONGOING: {
           return (<OngoingConversationView
             dispatcher={this.props.dispatcher}
             video={{enabled: !this.state.videoMuted}}
             audio={{enabled: !this.state.audioMuted}}
             />
@@ -440,17 +500,17 @@ loop.conversationViews = (function(mozL1
           return this._renderFeedbackView();
         }
         default: {
           return (<PendingConversationView
             dispatcher={this.props.dispatcher}
             callState={this.state.callState}
             contact={this.state.contact}
             enableCancelButton={this._isCancellable()}
-          />)
+          />);
         }
       }
     },
   });
 
   return {
     PendingConversationView: PendingConversationView,
     CallIdentifierView: CallIdentifierView,
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -10,16 +10,17 @@
 var loop = loop || {};
 loop.panel = (function(_, mozL10n) {
   "use strict";
 
   var sharedViews = loop.shared.views;
   var sharedModels = loop.shared.models;
   var sharedMixins = loop.shared.mixins;
   var sharedActions = loop.shared.actions;
+  var sharedUtils = loop.shared.utils;
   var Button = sharedViews.Button;
   var ButtonGroup = sharedViews.ButtonGroup;
   var ContactsList = loop.contacts.ContactsList;
   var ContactDetailsForm = loop.contacts.ContactDetailsForm;
   var __ = mozL10n.get; // aliasing translation function as __ for concision
 
   var TabView = React.createClass({displayName: 'TabView',
     propTypes: {
@@ -357,21 +358,17 @@ loop.panel = (function(_, mozL10n) {
           this.setState(this.getInitialState());
         }
       }
     },
 
     handleEmailButtonClick: function(event) {
       this.handleLinkExfiltration(event);
 
-      navigator.mozLoop.composeEmail(
-        __("share_email_subject4", { clientShortname: __("clientShortname2")}),
-        __("share_email_body4", { callUrl: this.state.callUrl,
-                                  clientShortname: __("clientShortname2"),
-                                  learnMoreUrl: navigator.mozLoop.getLoopCharPref("learnMoreUrl") }));
+      sharedUtils.composeCallUrlEmail(this.state.callUrl);
     },
 
     handleCopyButtonClick: function(event) {
       this.handleLinkExfiltration(event);
       // XXX the mozLoop object should be passed as a prop, to ease testing and
       //     using a fake implementation in UI components showcase.
       navigator.mozLoop.copyString(this.state.callUrl);
       this.setState({copied: true});
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -10,16 +10,17 @@
 var loop = loop || {};
 loop.panel = (function(_, mozL10n) {
   "use strict";
 
   var sharedViews = loop.shared.views;
   var sharedModels = loop.shared.models;
   var sharedMixins = loop.shared.mixins;
   var sharedActions = loop.shared.actions;
+  var sharedUtils = loop.shared.utils;
   var Button = sharedViews.Button;
   var ButtonGroup = sharedViews.ButtonGroup;
   var ContactsList = loop.contacts.ContactsList;
   var ContactDetailsForm = loop.contacts.ContactDetailsForm;
   var __ = mozL10n.get; // aliasing translation function as __ for concision
 
   var TabView = React.createClass({
     propTypes: {
@@ -357,21 +358,17 @@ loop.panel = (function(_, mozL10n) {
           this.setState(this.getInitialState());
         }
       }
     },
 
     handleEmailButtonClick: function(event) {
       this.handleLinkExfiltration(event);
 
-      navigator.mozLoop.composeEmail(
-        __("share_email_subject4", { clientShortname: __("clientShortname2")}),
-        __("share_email_body4", { callUrl: this.state.callUrl,
-                                  clientShortname: __("clientShortname2"),
-                                  learnMoreUrl: navigator.mozLoop.getLoopCharPref("learnMoreUrl") }));
+      sharedUtils.composeCallUrlEmail(this.state.callUrl);
     },
 
     handleCopyButtonClick: function(event) {
       this.handleLinkExfiltration(event);
       // XXX the mozLoop object should be passed as a prop, to ease testing and
       //     using a fake implementation in UI components showcase.
       navigator.mozLoop.copyString(this.state.callUrl);
       this.setState({copied: true});
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -235,26 +235,37 @@
 .call-window {
   display: flex;
   flex-direction: column;
   align-items: center;
   justify-content: space-between;
   min-height: 230px;
 }
 
+.call-window > .btn-label {
+  text-align: center;
+}
+
+.call-window > .error {
+  text-align: center;
+  color: #f00;
+  font-size: 90%;
+}
+
 .call-action-group {
   display: flex;
-  padding: 2.5em 0 0 0;
+  padding: 2.5em 4px 0 4px;
   width: 100%;
-  justify-content: space-around;
 }
 
 .call-action-group > .btn {
-  margin-left: .5em;
   height: 26px;
+  border-radius: 2px;
+  margin: 0 4px;
+  min-width: 64px;
 }
 
 .call-action-group .btn-group-chevron,
 .call-action-group .btn-group {
   width: 100%;
 }
 
 /* XXX Once we get the incoming call avatar, bug 1047435, the H2 should
--- a/browser/components/loop/content/shared/js/actions.js
+++ b/browser/components/loop/content/shared/js/actions.js
@@ -26,16 +26,23 @@ loop.shared.actions = (function() {
   }
 
   Action.define = function(name, schema) {
     return Action.bind(null, name, schema);
   };
 
   return {
     /**
+     * Fetch a new call url from the server, intended to be sent over email when
+     * a contact can't be reached.
+     */
+    FetchEmailLink: Action.define("fetchEmailLink", {
+    }),
+
+    /**
      * Used to trigger gathering of initial call data.
      */
     GatherCallData: Action.define("gatherCallData", {
       // Specify the callId for an incoming call.
       callId: [String, null],
       outgoing: Boolean
     }),
 
--- a/browser/components/loop/content/shared/js/conversationStore.js
+++ b/browser/components/loop/content/shared/js/conversationStore.js
@@ -121,17 +121,18 @@ loop.store.ConversationStore = (function
         "connectionProgress",
         "gatherCallData",
         "connectCall",
         "hangupCall",
         "peerHungupCall",
         "cancelCall",
         "retryCall",
         "mediaConnected",
-        "setMute"
+        "setMute",
+        "fetchEmailLink"
       ]);
     },
 
     /**
      * Handles the connection failure action, setting the state to
      * terminated.
      *
      * @param {sharedActions.ConnectionFailure} actionData The action data.
@@ -299,25 +300,56 @@ loop.store.ConversationStore = (function
      * @param {sharedActions.setMute} actionData The mute state for the stream type.
      */
     setMute: function(actionData) {
       var muteType = actionData.type + "Muted";
       this.set(muteType, !actionData.enabled);
     },
 
     /**
+     * Fetches a new call URL intended to be sent over email when a contact
+     * can't be reached.
+     */
+    fetchEmailLink: function() {
+      // XXX This is an empty string as a conversation identifier. Bug 1015938 implements
+      // a user-set string.
+      this.client.requestCallUrl("", function(err, callUrlData) {
+        if (err) {
+          this.trigger("error:emailLink");
+          return;
+        }
+        this.set("emailLink", callUrlData.callUrl);
+      }.bind(this));
+    },
+
+    /**
      * Obtains the outgoing call data from the server and handles the
      * result.
      */
     _setupOutgoingCall: function() {
       var contactAddresses = [];
+      var contact = this.get("contact");
 
-      this.get("contact").email.forEach(function(address) {
-        contactAddresses.push(address.value);
-      });
+      function appendContactValues(property, strip) {
+        if (contact.hasOwnProperty(property)) {
+          contact[property].forEach(function(item) {
+            if (strip) {
+              contactAddresses.push(item.value
+                .replace(/^(\+)?(.*)$/g, function(m, prefix, number) {
+                  return (prefix || "") + number.replace(/[\D]+/g, "");
+                }));
+            } else {
+              contactAddresses.push(item.value);
+            }
+          });
+        }
+      }
+
+      appendContactValues("email");
+      appendContactValues("tel", true);
 
       this.client.setupOutgoingCall(contactAddresses,
         this.get("callType"),
         function(err, result) {
           if (err) {
             console.error("Failed to get outgoing call data", err);
             this.dispatcher.dispatch(
               new sharedActions.ConnectionFailure({reason: "setup"}));
--- a/browser/components/loop/content/shared/js/utils.js
+++ b/browser/components/loop/content/shared/js/utils.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* global loop:true */
 
 var loop = loop || {};
 loop.shared = loop.shared || {};
-loop.shared.utils = (function() {
+loop.shared.utils = (function(mozL10n) {
   "use strict";
 
   /**
    * Call types used for determining if a call is audio/video or audio-only.
    */
   var CALL_TYPES = {
     AUDIO_VIDEO: "audio-video",
     AUDIO_ONLY: "audio"
@@ -91,16 +91,42 @@ loop.shared.utils = (function() {
       return this._iOSRegex.test(platform);
     },
 
     locationHash: function() {
       return window.location.hash;
     }
   };
 
+  /**
+   * Generates and opens a mailto: url with call URL information prefilled.
+   * Note: This only works for Desktop.
+   *
+   * @param  {String} callUrl   The call URL.
+   * @param  {String} recipient The recipient email address (optional).
+   */
+  function composeCallUrlEmail(callUrl, recipient) {
+    if (typeof navigator.mozLoop === "undefined") {
+      console.warn("composeCallUrlEmail isn't available for Loop standalone.");
+      return;
+    }
+    navigator.mozLoop.composeEmail(
+      mozL10n.get("share_email_subject4", {
+        clientShortname: mozL10n.get("clientShortname2")
+      }),
+      mozL10n.get("share_email_body4", {
+        callUrl: callUrl,
+        clientShortname: mozL10n.get("clientShortname2"),
+        learnMoreUrl: navigator.mozLoop.getLoopCharPref("learnMoreUrl")
+      }),
+      recipient
+    );
+  }
+
   return {
     CALL_TYPES: CALL_TYPES,
     Helper: Helper,
+    composeCallUrlEmail: composeCallUrlEmail,
     formatDate: formatDate,
     getTargetPlatform: getTargetPlatform,
     getBoolPreference: getBoolPreference
   };
-})();
+})(document.mozL10n || navigator.mozL10n);
--- a/browser/components/loop/test/desktop-local/conversationViews_test.js
+++ b/browser/components/loop/test/desktop-local/conversationViews_test.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 var expect = chai.expect;
 
 describe("loop.conversationViews", function () {
   "use strict";
 
+  var sharedUtils = loop.shared.utils;
   var sandbox, oldTitle, view, dispatcher, contact;
 
   var CALL_STATES = loop.store.CALL_STATES;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
 
     oldTitle = document.title;
@@ -196,23 +197,35 @@ describe("loop.conversationViews", funct
 
         sinon.assert.calledOnce(dispatcher.dispatch);
         sinon.assert.calledWithMatch(dispatcher.dispatch,
           sinon.match.hasOwn("name", "cancelCall"));
       });
   });
 
   describe("CallFailedView", function() {
+    var store;
+
     function mountTestComponent(props) {
       return TestUtils.renderIntoDocument(
         loop.conversationViews.CallFailedView({
-          dispatcher: dispatcher
+          dispatcher: dispatcher,
+          store: store,
+          contact: {email: [{value: "test@test.tld"}]}
         }));
     }
 
+    beforeEach(function() {
+      store = new loop.store.ConversationStore({}, {
+        dispatcher: dispatcher,
+        client: {},
+        sdkDriver: {}
+      });
+    });
+
     it("should dispatch a retryCall action when the retry button is pressed",
       function() {
         view = mountTestComponent();
 
         var retryBtn = view.getDOMNode().querySelector('.btn-retry');
 
         React.addons.TestUtils.Simulate.click(retryBtn);
 
@@ -228,16 +241,76 @@ describe("loop.conversationViews", funct
         var cancelBtn = view.getDOMNode().querySelector('.btn-cancel');
 
         React.addons.TestUtils.Simulate.click(cancelBtn);
 
         sinon.assert.calledOnce(dispatcher.dispatch);
         sinon.assert.calledWithMatch(dispatcher.dispatch,
           sinon.match.hasOwn("name", "cancelCall"));
       });
+
+    it("should dispatch a fetchEmailLink action when the cancel button is pressed",
+      function() {
+        view = mountTestComponent();
+
+        var emailLinkBtn = view.getDOMNode().querySelector('.btn-email');
+
+        React.addons.TestUtils.Simulate.click(emailLinkBtn);
+
+        sinon.assert.calledOnce(dispatcher.dispatch);
+        sinon.assert.calledWithMatch(dispatcher.dispatch,
+          sinon.match.hasOwn("name", "fetchEmailLink"));
+      });
+
+    it("should disable the email link button once the action is dispatched",
+      function() {
+        view = mountTestComponent();
+        var emailLinkBtn = view.getDOMNode().querySelector('.btn-email');
+        React.addons.TestUtils.Simulate.click(emailLinkBtn);
+
+        expect(view.getDOMNode().querySelector(".btn-email").disabled).eql(true);
+      });
+
+    it("should compose an email once the email link is received", function() {
+      var composeCallUrlEmail = sandbox.stub(sharedUtils, "composeCallUrlEmail");
+      view = mountTestComponent();
+      store.set("emailLink", "http://fake.invalid/");
+
+      sinon.assert.calledOnce(composeCallUrlEmail);
+      sinon.assert.calledWithExactly(composeCallUrlEmail,
+        "http://fake.invalid/", "test@test.tld");
+    });
+
+    it("should close the conversation window once the email link is received",
+      function() {
+        sandbox.stub(window, "close");
+        view = mountTestComponent();
+
+        store.set("emailLink", "http://fake.invalid/");
+
+        sinon.assert.calledOnce(window.close);
+      });
+
+    it("should display an error message in case email link retrieval failed",
+      function() {
+        view = mountTestComponent();
+
+        store.trigger("error:emailLink");
+
+        expect(view.getDOMNode().querySelector(".error")).not.eql(null);
+      });
+
+    it("should allow retrying to get a call url if it failed previously",
+      function() {
+        view = mountTestComponent();
+
+        store.trigger("error:emailLink");
+
+        expect(view.getDOMNode().querySelector(".btn-email").disabled).eql(false);
+      });
   });
 
   describe("OngoingConversationView", function() {
     function mountTestComponent(props) {
       return TestUtils.renderIntoDocument(
         loop.conversationViews.OngoingConversationView(props));
     }
 
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -3,16 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*jshint newcap:false*/
 /*global loop, sinon */
 
 var expect = chai.expect;
 var TestUtils = React.addons.TestUtils;
 var sharedActions = loop.shared.actions;
+var sharedUtils = loop.shared.utils;
 
 describe("loop.panel", function() {
   "use strict";
 
   var sandbox, notifications, fakeXHR, requests = [];
 
   beforeEach(function(done) {
     sandbox = sinon.sandbox.create();
@@ -444,25 +445,28 @@ describe("loop.panel", function() {
       });
 
       it("should have 0 pending notifications", function() {
         expect(view.props.notifications.length).eql(0);
       });
 
       it("should display a share button for email", function() {
         fakeClient.requestCallUrl = sandbox.stub();
+        var composeCallUrlEmail = sandbox.stub(sharedUtils, "composeCallUrlEmail");
         var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
           notifications: notifications,
           client: fakeClient
         }));
         view.setState({pending: false, callUrl: "http://example.com"});
 
         TestUtils.findRenderedDOMComponentWithClass(view, "button-email");
         TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-email"));
-        sinon.assert.calledOnce(navigator.mozLoop.composeEmail);
+
+        sinon.assert.calledOnce(composeCallUrlEmail);
+        sinon.assert.calledWithExactly(composeCallUrlEmail, "http://example.com");
       });
 
       it("should feature a copy button capable of copying the call url when clicked", function() {
         fakeClient.requestCallUrl = sandbox.stub();
         var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
           notifications: notifications,
           client: fakeClient
         }));
--- a/browser/components/loop/test/shared/conversationStore_test.js
+++ b/browser/components/loop/test/shared/conversationStore_test.js
@@ -33,17 +33,18 @@ describe("loop.store.ConversationStore",
         type: "home",
         value: "fakeEmail",
         pref: true
       }]
     };
 
     dispatcher = new loop.Dispatcher();
     client = {
-      setupOutgoingCall: sinon.stub()
+      setupOutgoingCall: sinon.stub(),
+      requestCallUrl: sinon.stub()
     };
     sdkDriver = {
       connectSession: sinon.stub(),
       disconnectSession: sinon.stub()
     };
 
     wsCancelSpy = sinon.spy();
     wsCloseSpy = sinon.spy();
@@ -270,16 +271,89 @@ describe("loop.store.ConversationStore",
         dispatcher.dispatch(
           new sharedActions.GatherCallData(outgoingCallData));
 
         sinon.assert.calledOnce(client.setupOutgoingCall);
         sinon.assert.calledWith(client.setupOutgoingCall,
           ["fakeEmail"], sharedUtils.CALL_TYPES.AUDIO_VIDEO);
       });
 
+      it("should include all email addresses in the call data", function() {
+        contact = {
+          name: [ "Mr Smith" ],
+          email: [{
+            type: "home",
+            value: "fakeEmail",
+            pref: true
+          },
+          {
+            type: "work",
+            value: "emailFake",
+            pref: false
+          }]
+        };
+
+        dispatcher.dispatch(
+          new sharedActions.GatherCallData(outgoingCallData));
+
+        sinon.assert.calledOnce(client.setupOutgoingCall);
+        sinon.assert.calledWith(client.setupOutgoingCall,
+          ["fakeEmail", "emailFake"], sharedUtils.CALL_TYPES.AUDIO_VIDEO);
+      });
+
+      it("should include trim phone numbers for the call data", function() {
+        contact = {
+          name: [ "Mr Smith" ],
+          tel: [{
+            type: "home",
+            value: "+44-5667+345 496(2335)45+ 456+",
+            pref: true
+          }]
+        };
+
+        dispatcher.dispatch(
+          new sharedActions.GatherCallData(outgoingCallData));
+
+        sinon.assert.calledOnce(client.setupOutgoingCall);
+        sinon.assert.calledWith(client.setupOutgoingCall,
+          ["+445667345496233545456"], sharedUtils.CALL_TYPES.AUDIO_VIDEO);
+      });
+
+      it("should include all email and telephone values in the call data", function() {
+        contact = {
+          name: [ "Mr Smith" ],
+          email: [{
+            type: "home",
+            value: "fakeEmail",
+            pref: true
+          }, {
+            type: "work",
+            value: "emailFake",
+            pref: false
+          }],
+          tel: [{
+            type: "work",
+            value: "01234567890",
+            pref: false
+          }, {
+            type: "home",
+            value: "09876543210",
+            pref: false
+          }]
+        };
+
+        dispatcher.dispatch(
+          new sharedActions.GatherCallData(outgoingCallData));
+
+        sinon.assert.calledOnce(client.setupOutgoingCall);
+        sinon.assert.calledWith(client.setupOutgoingCall,
+          ["fakeEmail", "emailFake", "01234567890", "09876543210"],
+          sharedUtils.CALL_TYPES.AUDIO_VIDEO);
+      });
+
       describe("server response handling", function() {
         beforeEach(function() {
           sandbox.stub(dispatcher, "dispatch");
         });
 
         it("should dispatch a connect call action on success", function() {
           var callData = {
             apiKey: "fakeKey"
@@ -561,16 +635,47 @@ describe("loop.store.ConversationStore",
         type: "video",
         enabled: false
       }));
 
       expect(store.get("videoMuted")).eql(true);
     });
   });
 
+  describe("#fetchEmailLink", function() {
+    it("should request a new call url to the server", function() {
+      dispatcher.dispatch(new sharedActions.FetchEmailLink());
+
+      sinon.assert.calledOnce(client.requestCallUrl);
+      sinon.assert.calledWith(client.requestCallUrl, "");
+    });
+
+    it("should update the emailLink attribute when the new call url is received",
+      function() {
+        client.requestCallUrl = function(callId, cb) {
+          cb(null, {callUrl: "http://fake.invalid/"});
+        };
+        dispatcher.dispatch(new sharedActions.FetchEmailLink());
+
+        expect(store.get("emailLink")).eql("http://fake.invalid/");
+      });
+
+    it("should trigger an error:emailLink event in case of failure",
+      function() {
+        var trigger = sandbox.stub(store, "trigger");
+        client.requestCallUrl = function(callId, cb) {
+          cb("error");
+        };
+        dispatcher.dispatch(new sharedActions.FetchEmailLink());
+
+        sinon.assert.calledOnce(trigger);
+        sinon.assert.calledWithExactly(trigger, "error:emailLink");
+      });
+  });
+
   describe("Events", function() {
     describe("Websocket progress", function() {
       beforeEach(function() {
         dispatcher.dispatch(
           new sharedActions.ConnectCall({sessionData: fakeSessionData}));
 
         sandbox.stub(dispatcher, "dispatch");
       });
--- a/browser/components/loop/test/shared/utils_test.js
+++ b/browser/components/loop/test/shared/utils_test.js
@@ -13,16 +13,17 @@ describe("loop.shared.utils", function()
   var sandbox;
   var sharedUtils = loop.shared.utils;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
   });
 
   afterEach(function() {
+    navigator.mozLoop = undefined;
     sandbox.restore();
   });
 
   describe("Helper", function() {
     var helper;
 
     beforeEach(function() {
       helper = new sharedUtils.Helper();
@@ -105,17 +106,16 @@ describe("loop.shared.utils", function()
 
     it("should return the formatted string", function() {
       expect(sharedUtils.formatDate(1000)).eql("fake result");
     });
   });
 
   describe("#getBoolPreference", function() {
     afterEach(function() {
-      navigator.mozLoop = undefined;
       localStorage.removeItem("test.true");
     });
 
     describe("mozLoop set", function() {
       beforeEach(function() {
         navigator.mozLoop = {
           getLoopBoolPref: function(prefName) {
             return prefName === "test.true";
@@ -137,9 +137,36 @@ describe("loop.shared.utils", function()
     describe("mozLoop not set", function() {
       it("should return the localStorage value", function() {
         localStorage.setItem("test.true", true);
 
         expect(sharedUtils.getBoolPreference("test.true")).eql(true);
       });
     });
   });
+
+  describe("#composeCallUrlEmail", function() {
+    var composeEmail;
+
+    beforeEach(function() {
+      // fake mozL10n
+      sandbox.stub(navigator.mozL10n, "get", function(id) {
+        switch(id) {
+          case "share_email_subject4": return "subject";
+          case "share_email_body4":    return "body";
+        }
+      });
+      composeEmail = sandbox.spy();
+      navigator.mozLoop = {
+        getLoopCharPref: sandbox.spy(),
+        composeEmail: composeEmail
+      };
+    });
+
+    it("should compose a call url email", function() {
+      sharedUtils.composeCallUrlEmail("http://invalid", "fake@invalid.tld");
+
+      sinon.assert.calledOnce(composeEmail);
+      sinon.assert.calledWith(composeEmail,
+                              "subject", "body", "fake@invalid.tld");
+    });
+  });
 });
--- a/browser/components/loop/ui/ui-showcase.js
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -307,16 +307,22 @@
           ), 
 
           Section({name: "CallFailedView"}, 
             Example({summary: "Call Failed", dashed: "true", 
                      style: {width: "260px", height: "265px"}}, 
               React.DOM.div({className: "fx-embedded"}, 
                 CallFailedView({dispatcher: dispatcher})
               )
+            ), 
+            Example({summary: "Call Failed — with call URL error", dashed: "true", 
+                     style: {width: "260px", height: "265px"}}, 
+              React.DOM.div({className: "fx-embedded"}, 
+                CallFailedView({dispatcher: dispatcher, emailLinkError: true})
+              )
             )
           ), 
 
           Section({name: "StartConversationView"}, 
             Example({summary: "Start conversation view", dashed: "true"}, 
               React.DOM.div({className: "standalone"}, 
                 StartConversationView({conversation: mockConversationModel, 
                                        client: mockClient, 
--- a/browser/components/loop/ui/ui-showcase.jsx
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -308,16 +308,22 @@
 
           <Section name="CallFailedView">
             <Example summary="Call Failed" dashed="true"
                      style={{width: "260px", height: "265px"}}>
               <div className="fx-embedded">
                 <CallFailedView dispatcher={dispatcher} />
               </div>
             </Example>
+            <Example summary="Call Failed — with call URL error" dashed="true"
+                     style={{width: "260px", height: "265px"}}>
+              <div className="fx-embedded">
+                <CallFailedView dispatcher={dispatcher} emailLinkError={true} />
+              </div>
+            </Example>
           </Section>
 
           <Section name="StartConversationView">
             <Example summary="Start conversation view" dashed="true">
               <div className="standalone">
                 <StartConversationView conversation={mockConversationModel}
                                        client={mockClient}
                                        notifications={notifications} />
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -1371,17 +1371,17 @@ PlacesToolbar.prototype = {
       return null;
 
     let dropPoint = { ip: null, beforeIndex: null, folderElt: null };
     let elt = aEvent.target;
     if (elt._placesNode && elt != this._rootElt &&
         elt.localName != "menupopup") {
       let eltRect = elt.getBoundingClientRect();
       let eltIndex = Array.indexOf(this._rootElt.childNodes, elt);
-      if (PlacesUIUtils.nodeIsFolder(elt._placesNode) &&
+      if (PlacesUtils.nodeIsFolder(elt._placesNode) &&
           !PlacesUIUtils.isContentsReadOnly(elt._placesNode)) {
         // This is a folder.
         // If we are in the middle of it, drop inside it.
         // Otherwise, drop before it, with regards to RTL mode.
         let threshold = eltRect.width * 0.25;
         if (this.isRTL ? (aEvent.clientX > eltRect.right - threshold)
                        : (aEvent.clientX < eltRect.left + threshold)) {
           // Drop before this folder.
--- a/browser/devtools/fontinspector/font-inspector.js
+++ b/browser/devtools/fontinspector/font-inspector.js
@@ -36,17 +36,17 @@ FontInspector.prototype = {
            this.inspector.sidebar.getCurrentTabID() == "fontinspector";
   },
 
   /**
    * Remove listeners.
    */
   destroy: function FI_destroy() {
     this.chromeDoc = null;
-    this.inspector.sidebar.off("layoutview-selected", this.onNewNode);
+    this.inspector.sidebar.off("fontinspector-selected", this.onNewNode);
     this.inspector.selection.off("new-node", this.onNewNode);
     this.showAllButton.removeEventListener("click", this.showAll);
   },
 
   /**
    * Selection 'new-node' event handler.
    */
   onNewNode: function FI_onNewNode() {
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -132,16 +132,19 @@ DEFINES += -DMOZ_REPLACE_MALLOC
 endif
 ifdef MOZ_JEMALLOC3
 DEFINES += -DMOZ_JEMALLOC3
 endif
 DEFINES += -DMOZ_ICU_DBG_SUFFIX=$(MOZ_ICU_DBG_SUFFIX)
 ifdef CLANG_CXX
 DEFINES += -DCLANG_CXX
 endif
+ifdef CLANG_CL
+DEFINES += -DCLANG_CL
+endif
 
 libs::
 	$(MAKE) -C $(DEPTH)/browser/locales langpack
 
 ifeq (WINNT,$(OS_ARCH))
 PKGCOMP_FIND_OPTS =
 else
 PKGCOMP_FIND_OPTS = -L
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -916,9 +916,12 @@ bin/libfreebl_32int64_3.so
 @BINPATH@/components/DataStoreImpl.js
 @BINPATH@/components/dom_datastore.xpt
 
 
 #ifdef MOZ_ASAN
 #ifdef CLANG_CXX
 @BINPATH@/llvm-symbolizer
 #endif
+#ifdef CLANG_CL
+@BINPATH@/clang_rt.asan_dynamic-i386.dll
 #endif
+#endif
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -89,16 +89,19 @@ PluginContent.prototype = {
   },
 
   onPageHide: function (event) {
     // Ignore events that aren't from the main document.
     if (!this.content || event.target != this.content.document) {
       return;
     }
 
+    if (Services.telemetry.canSend) {
+      this._finishRecordingFlashPluginTelemetry();
+    }
     this.clearPluginDataCache();
   },
 
   getPluginUI: function (plugin, anonid) {
     return plugin.ownerDocument.
            getAnonymousElementByAttribute(plugin, "anonid", anonid);
   },
 
@@ -373,16 +376,21 @@ PluginContent.prototype = {
         shouldShowNotification = true;
         break;
 
       case "PluginInstantiated":
         shouldShowNotification = true;
         break;
     }
 
+    if (Services.telemetry.canSend && this._getPluginInfo(plugin).mimetype ===
+                                      "application/x-shockwave-flash") {
+      this._recordFlashPluginTelemetry(eventType, plugin);
+    }
+
     // Show the in-content UI if it's not too big. The crashed plugin handler already did this.
     if (eventType != "PluginCrashed") {
       let overlay = this.getPluginUI(plugin, "main");
       if (overlay != null) {
         this.setVisibility(plugin, overlay,
                            this.shouldShowOverlay(plugin, overlay));
         let resizeListener = (event) => {
           this.setVisibility(plugin, overlay,
@@ -402,16 +410,52 @@ PluginContent.prototype = {
       }, true);
     }
 
     if (shouldShowNotification) {
       this._showClickToPlayNotification(plugin, false);
     }
   },
 
+  _recordFlashPluginTelemetry: function (eventType, plugin) {
+    if (!this.flashPluginStats) {
+      this.flashPluginStats = {
+        instancesCount: 0,
+        plugins: new WeakSet()
+      };
+    }
+
+    if (!this.flashPluginStats.plugins.has(plugin)) {
+      // Reporting plugin instance and its dimensions only once.
+      this.flashPluginStats.plugins.add(plugin);
+
+      this.flashPluginStats.instancesCount++;
+
+      let pluginRect = plugin.getBoundingClientRect();
+      Services.telemetry.getHistogramById('FLASH_PLUGIN_WIDTH')
+                       .add(pluginRect.width);
+      Services.telemetry.getHistogramById('FLASH_PLUGIN_HEIGHT')
+                       .add(pluginRect.height);
+      Services.telemetry.getHistogramById('FLASH_PLUGIN_AREA')
+                       .add(pluginRect.width * pluginRect.height);
+
+      let state = this._getPluginInfo(plugin).fallbackType;
+      Services.telemetry.getHistogramById('FLASH_PLUGIN_STATES')
+                       .add(state);
+    }
+  },
+
+  _finishRecordingFlashPluginTelemetry: function () {
+    if (this.flashPluginStats) {
+      Services.telemetry.getHistogramById('FLASH_PLUGIN_INSTANCES_ON_PAGE')
+                        .add(this.flashPluginStats.instancesCount);
+    delete this.flashPluginStats;
+    }
+  },
+
   isKnownPlugin: function (objLoadingContent) {
     return (objLoadingContent.getContentTypeForMIMEType(objLoadingContent.actualType) ==
             Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
   },
 
   canActivatePlugin: function (objLoadingContent) {
     // if this isn't a known plugin, we can't activate it
     // (this also guards pluginHost.getPermissionStringForType against
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -61,16 +61,23 @@ LLDBINIT_OBJDIR := .lldbinit.in
 LLDBINIT_OBJDIR_PATH = $(DEPTH)
 LLDBINIT_OBJDIR_FLAGS += -Dtopsrcdir=$(abspath $(topsrcdir))
 PP_TARGETS += LLDBINIT_OBJDIR
 
 LLDBINIT_FINAL_TARGET_FILES := $(DEPTH)/.lldbinit
 LLDBINIT_FINAL_TARGET_DEST = $(FINAL_TARGET)
 INSTALL_TARGETS += LLDBINIT_FINAL_TARGET
 
+ifeq (1_1,$(MOZ_ASAN)_$(CLANG_CL))
+# Install the clang-cl runtime library for ASAN next to the binaries we produce.
+CLANG_RT_ASAN_FILES := $(MOZ_CLANG_RT_ASAN_LIB_PATH)
+CLANG_RT_ASAN_DEST = $(FINAL_TARGET)
+INSTALL_TARGETS += CLANG_RT_ASAN
+endif
+
 include $(topsrcdir)/config/rules.mk
 
 TARGET_DEPTH = ..
 include $(srcdir)/automation-build.mk
 
 ifdef MOZ_APP_BASENAME
 $(FINAL_TARGET)/application.ini: $(APP_INI_DEPS)
 
--- a/configure.in
+++ b/configure.in
@@ -501,16 +501,21 @@ case "$target" in
         AC_SUBST(MSVS_VERSION)
         AC_SUBST(MSVC_C_RUNTIME_DLL)
         AC_SUBST(MSVC_CXX_RUNTIME_DLL)
 
         # Disable SEH on clang-cl because it doesn't implement them yet.
         if test -z "$CLANG_CL"; then
             AC_DEFINE(HAVE_SEH_EXCEPTIONS)
         else
+            # Build on clang-cl with MSVC 2013 with fallback emulation.
+            CFLAGS="$CFLAGS -fmsc-version=1800 -fallback"
+            CXXFLAGS="$CXXFLAGS -fmsc-version=1800 -fallback"
+            # Send our CFLAGS to NSS too
+            MOZ_CFLAGS_NSS=1
             AC_DEFINE_UNQUOTED(GTEST_HAS_SEH, 0)
         fi
 
         if test -n "$WIN32_REDIST_DIR"; then
           if test ! -d "$WIN32_REDIST_DIR"; then
             AC_MSG_ERROR([Invalid Win32 Redist directory: ${WIN32_REDIST_DIR}])
           fi
           WIN32_REDIST_DIR=`cd "$WIN32_REDIST_DIR" && pwd`
@@ -1255,16 +1260,26 @@ dnl ====================================
 dnl = Use Address Sanitizer
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(address-sanitizer,
 [  --enable-address-sanitizer       Enable Address Sanitizer (default=no)],
     MOZ_ASAN=1,
     MOZ_ASAN= )
 if test -n "$MOZ_ASAN"; then
     MOZ_LLVM_HACKS=1
+    if test -n "$CLANG_CL"; then
+        # Look for clang_rt.asan_dynamic-i386.dll
+        MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-i386.dll
+        # We use MOZ_PATH_PROG in order to get a Windows style path.
+        MOZ_PATH_PROG(MOZ_CLANG_RT_ASAN_LIB_PATH, $MOZ_CLANG_RT_ASAN_LIB)
+        if test -z "$MOZ_CLANG_RT_ASAN_LIB_PATH"; then
+            AC_MSG_ERROR([Couldn't find $MOZ_CLANG_RT_ASAN_LIB.  It should be available in the same location as clang-cl.])
+        fi
+        AC_SUBST(MOZ_CLANG_RT_ASAN_LIB_PATH)
+    fi
     AC_DEFINE(MOZ_ASAN)
     MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
 fi
 AC_SUBST(MOZ_ASAN)
 
 dnl ========================================================
 dnl = Use Memory Sanitizer
 dnl ========================================================
@@ -3791,21 +3806,16 @@ fi
 
 AC_SUBST(MOZ_NATIVE_HUNSPELL)
 
 dnl ========================================================
 dnl system libffi Support
 dnl ========================================================
 MOZ_CONFIG_FFI()
 
-# split JS out by default to avoid VS2005 PGO crash (bug 591836).
-if test "$OS_ARCH" = "WINNT"; then
-  JS_SHARED_LIBRARY=1
-fi
-
 MOZ_ARG_ENABLE_BOOL(shared-js,
 [  --enable-shared-js
                           Create a shared JavaScript library.],
     JS_SHARED_LIBRARY=1,
     JS_SHARED_LIBRARY=)
 
 dnl ========================================================
 dnl Java SDK support
@@ -7948,18 +7958,17 @@ fi
 
 dnl ========================================================
 dnl =
 dnl = Static Build Options
 dnl =
 dnl ========================================================
 MOZ_ARG_HEADER(Static build options)
 
-if test "$OS_ARCH" = "WINNT"; then
-  GKMEDIAS_SHARED_LIBRARY=1
+if test -n "$GKMEDIAS_SHARED_LIBRARY"; then
   AC_DEFINE(GKMEDIAS_SHARED_LIBRARY)
 fi
 AC_SUBST(GKMEDIAS_SHARED_LIBRARY)
 
 if test -z "$MOZ_NATIVE_ZLIB"; then
 if test -n "$JS_SHARED_LIBRARY" -o "$GKMEDIAS_SHARED_LIBRARY"; then
   ZLIB_IN_MOZGLUE=1
   AC_DEFINE(ZLIB_IN_MOZGLUE)
--- a/content/base/public/FragmentOrElement.h
+++ b/content/base/public/FragmentOrElement.h
@@ -407,17 +407,17 @@ public:
     /**
      * XBL binding installed on the lement.
      */
     nsCOMPtr<nsIContent> mXBLInsertionParent;
 
     /**
      * Web components custom element data.
      */
-    nsAutoPtr<CustomElementData> mCustomElementData;
+    nsRefPtr<CustomElementData> mCustomElementData;
   };
 
 protected:
   void GetMarkup(bool aIncludeSelf, nsAString& aMarkup);
   void SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError);
 
   // Override from nsINode
   virtual nsINode::nsSlots* CreateSlots() MOZ_OVERRIDE;
--- a/content/base/src/DOMParser.cpp
+++ b/content/base/src/DOMParser.cpp
@@ -233,28 +233,25 @@ DOMParser::ParseFromStream(nsIInputStrea
 
   nsCOMPtr<nsIDOMDocument> domDocument;
   rv = SetUpDocument(svg ? DocumentFlavorSVG : DocumentFlavorLegacyGuess,
                      getter_AddRefs(domDocument));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Create a fake channel 
   nsCOMPtr<nsIChannel> parserChannel;
-  NS_NewInputStreamChannel(getter_AddRefs(parserChannel), mDocumentURI, nullptr,
-                           nsDependentCString(contentType), nullptr);
+  NS_NewInputStreamChannel(getter_AddRefs(parserChannel),
+                           mDocumentURI,
+                           nullptr, // aStream
+                           mOriginalPrincipal,
+                           nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
+                           nsIContentPolicy::TYPE_OTHER,
+                           nsDependentCString(contentType));
   NS_ENSURE_STATE(parserChannel);
 
-  // More principal-faking here
-  nsCOMPtr<nsILoadInfo> loadInfo =
-    new LoadInfo(mOriginalPrincipal,
-                 nullptr,
-                 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
-                 nsIContentPolicy::TYPE_OTHER);
-  parserChannel->SetLoadInfo(loadInfo);
-
   if (charset) {
     parserChannel->SetContentCharset(nsDependentCString(charset));
   }
 
   // Tell the document to start loading
   nsCOMPtr<nsIStreamListener> listener;
 
   // Have to pass false for reset here, else the reset will remove
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -188,16 +188,18 @@ public:
   // These attributes are used for error reporting.
   nsCString mScriptFile;
   uint32_t mScriptLine;
   uint64_t mInnerWindowID;
 
   WorkerPrivate* mWorkerPrivate;
   nsAutoPtr<WorkerFeature> mWorkerFeature;
 
+  nsWeakPtr mWeakLoadGroup;
+
 private:
   ~WebSocketImpl()
   {
     // If we threw during Init we never called disconnect
     if (!mDisconnected) {
       Disconnect();
     }
   }
@@ -523,18 +525,17 @@ WebSocketImpl::Disconnect()
   return NS_OK;
 }
 
 void
 WebSocketImpl::DisconnectInternal()
 {
   AssertIsOnMainThread();
 
-  nsCOMPtr<nsILoadGroup> loadGroup;
-  GetLoadGroup(getter_AddRefs(loadGroup));
+  nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakLoadGroup);
   if (loadGroup) {
     loadGroup->RemoveRequest(this, nullptr, NS_OK);
   }
 
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os) {
     os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
     os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
@@ -1385,16 +1386,18 @@ WebSocketImpl::InitializeConnection()
   // provide the http stack the loadgroup info too
   nsCOMPtr<nsILoadGroup> loadGroup;
   rv = GetLoadGroup(getter_AddRefs(loadGroup));
   if (loadGroup) {
     rv = wsChannel->SetLoadGroup(loadGroup);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = loadGroup->AddRequest(this, nullptr);
     NS_ENSURE_SUCCESS(rv, rv);
+
+    mWeakLoadGroup = do_GetWeakReference(loadGroup);
   }
 
   // manually adding loadinfo to the channel since it
   // was not set during channel creation.
   nsCOMPtr<nsIDocument> doc = do_QueryReferent(mOriginDocument);
   nsCOMPtr<nsILoadInfo> loadInfo =
     new LoadInfo(mPrincipal,
                  doc,
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -5839,22 +5839,17 @@ nsDocument::ProcessBaseElementQueue()
 }
 
 // static
 void
 nsDocument::ProcessTopElementQueue(bool aIsBaseQueue)
 {
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
 
-  if (sProcessingStack.isNothing()) {
-    // If XPCOM shutdown has reset the processing stack, don't do anything.
-    return;
-  }
-
-  nsTArray<CustomElementData*>& stack = *sProcessingStack;
+  nsTArray<nsRefPtr<CustomElementData>>& stack = *sProcessingStack;
   uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr);
 
   if (aIsBaseQueue && firstQueue != 0) {
     return;
   }
 
   for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) {
     // Callback queue may have already been processed in an earlier
@@ -5881,17 +5876,17 @@ bool
 nsDocument::RegisterEnabled()
 {
   static bool sPrefValue =
     Preferences::GetBool("dom.webcomponents.enabled", false);
   return sPrefValue;
 }
 
 // static
-Maybe<nsTArray<mozilla::dom::CustomElementData*>>
+Maybe<nsTArray<nsRefPtr<mozilla::dom::CustomElementData>>>
 nsDocument::sProcessingStack;
 
 // static
 bool
 nsDocument::sProcessingBaseElementQueue;
 
 void
 nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -320,16 +320,18 @@ private:
   // callback queue.
   CustomElementData* mOwnerData;
 };
 
 // Each custom element has an associated callback queue and an element is
 // being created flag.
 struct CustomElementData
 {
+  NS_INLINE_DECL_REFCOUNTING(CustomElementData)
+
   explicit CustomElementData(nsIAtom* aType);
   // Objects in this array are transient and empty after each microtask
   // checkpoint.
   nsTArray<nsAutoPtr<CustomElementCallback>> mCallbackQueue;
   // Custom element type, for <button is="x-button"> or <x-button>
   // this would be x-button.
   nsCOMPtr<nsIAtom> mType;
   // The callback that is next to be processed upon calling RunCallbackQueue.
@@ -341,16 +343,19 @@ struct CustomElementData
   bool mCreatedCallbackInvoked;
   // The microtask level associated with the callbacks in the callback queue,
   // it is used to determine if a new queue needs to be pushed onto the
   // processing stack.
   int32_t mAssociatedMicroTask;
 
   // Empties the callback queue.
   void RunCallbackQueue();
+
+private:
+  virtual ~CustomElementData() {}
 };
 
 // The required information for a custom element as defined in:
 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html
 struct CustomElementDefinition
 {
   CustomElementDefinition(JSObject* aPrototype,
                           nsIAtom* aType,
@@ -1515,17 +1520,17 @@ protected:
 
 private:
   // Array representing the processing stack in the custom elements
   // specification. The processing stack is conceptually a stack of
   // element queues. Each queue is represented by a sequence of
   // CustomElementData in this array, separated by nullptr that
   // represent the boundaries of the items in the stack. The first
   // queue in the stack is the base element queue.
-  static mozilla::Maybe<nsTArray<mozilla::dom::CustomElementData*>> sProcessingStack;
+  static mozilla::Maybe<nsTArray<nsRefPtr<mozilla::dom::CustomElementData>>> sProcessingStack;
 
   // Flag to prevent re-entrance into base element queue as described in the
   // custom elements speicification.
   static bool sProcessingBaseElementQueue;
 
   static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
 
 public:
--- a/content/base/src/nsHostObjectProtocolHandler.cpp
+++ b/content/base/src/nsHostObjectProtocolHandler.cpp
@@ -514,17 +514,21 @@ nsHostObjectProtocolHandler::NewChannel(
   FileImpl* blob = static_cast<FileImpl*>(blobImpl.get());
   nsCOMPtr<nsIInputStream> stream;
   nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
                                 uri,
-                                stream);
+                                stream,
+                                info->mPrincipal,
+                                nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
+                                nsIContentPolicy::TYPE_OTHER);
+
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsString type;
   blob->GetType(type);
 
   if (blob->IsFile()) {
     nsString filename;
     blob->GetName(filename);
@@ -532,22 +536,16 @@ nsHostObjectProtocolHandler::NewChannel(
   }
 
   ErrorResult error;
   uint64_t size = blob->GetSize(error);
   if (NS_WARN_IF(error.Failed())) {
     return error.ErrorCode();
   }
 
-  nsCOMPtr<nsILoadInfo> loadInfo =
-    new mozilla::LoadInfo(info->mPrincipal,
-                          nullptr,
-                          nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
-                          nsIContentPolicy::TYPE_OTHER);
-  channel->SetLoadInfo(loadInfo);
   channel->SetOriginalURI(uri);
   channel->SetContentType(NS_ConvertUTF16toUTF8(type));
   channel->SetContentLength(size);
 
   channel.forget(result);
 
   return NS_OK;
 }
--- a/content/base/test/chrome/cpows_child.js
+++ b/content/base/test/chrome/cpows_child.js
@@ -7,16 +7,19 @@ var done_count = 0;
 var is_remote;
 
 (function start() {
     [is_remote] = sendSyncMessage("cpows:is_remote");
     parent_test();
     error_reporting_test();
     dom_test();
     xray_test();
+    if (typeof Symbol === "function") {
+      symbol_test();
+    }
     compartment_test();
     regexp_test();
     sync_test();
     async_test();
     rpc_test();
     nested_sync_test();
     // The sync-ness of this call is important, because otherwise
     // we tear down the child's document while we are
@@ -114,25 +117,23 @@ function xray_test()
 
   sendSyncMessage("cpows:xray_test", {}, {element: element});
 }
 
 function symbol_test()
 {
   let iterator = Symbol.iterator;
   let named = Symbol.for("cpow-test");
-  // let unique = Symbol();
 
   let object = {
     [iterator]: iterator,
     [named]: named,
-    // [unique]: unique,
-    // "unique": unique
   };
-  sendSyncMessage("cpows:symbol_test", {}, object);
+  let test = ['a'];
+  sendSyncMessage("cpows:symbol_test", {}, {object: object, test: test});
 }
 
 // Parent->Child references should go X->parent.privilegedJunkScope->child.privilegedJunkScope->Y
 // Child->Parent references should go X->child.privilegedJunkScope->parent.unprivilegedJunkScope->Y
 function compartment_test()
 {
   // This test primarily checks various compartment invariants for CPOWs, and
   // doesn't make sense to run in-process.
--- a/content/base/test/chrome/cpows_parent.xul
+++ b/content/base/test/chrome/cpows_parent.xul
@@ -201,20 +201,26 @@
     }
 
     function recvXrayTest(message) {
       let element = message.objects.element;
       is(element.foo, undefined, "DOM element does not expose content properties");
     }
 
     function recvSymbolTest(message) {
-      let object = message.objects;
+      let object = message.objects.object;
       is(object[Symbol.iterator], Symbol.iterator, "Should use Symbol.iterator");
       is(Symbol.keyFor(object[Symbol.for("cpow-test")]), "cpow-test", "Symbols aren't registered correctly");
-      // is(object.unique, object[object.unique], "Unique symbols as ids and values don't seem to work");
+      let symbols = Object.getOwnPropertySymbols(object);
+      is(symbols.length, 2, "Object should have two symbol keys");
+      let test = undefined;
+      for (let x of message.objects.test) {
+        test = x;
+      }
+      is(test, "a", "for .. of iteration should work");
     }
 
     let systemGlobal = this;
     function recvCompartmentTest(message) {
       let getUnprivilegedObject = message.objects.getUnprivilegedObject;
       let testParentObject = message.objects.testParentObject;
 
       // Make sure that parent->child CPOWs live in the parent's privileged junk scope.
@@ -302,17 +308,19 @@
       mm.addMessageListener("cpows:reenter_sync", recvReenterSyncMessage);
       mm.addMessageListener("cpows:done", recvDoneMessage);
       mm.addMessageListener("cpows:fail", recvFailMessage);
       mm.addMessageListener("cpows:parent_test", recvParentTest);
       mm.addMessageListener("cpows:error_reporting_test", recvErrorReportingTest);
       mm.addMessageListener("cpows:dom_test", recvDomTest);
       mm.addMessageListener("cpows:dom_test_after_gc", recvDomTestAfterGC);
       mm.addMessageListener("cpows:xray_test", recvXrayTest);
-      mm.addMessageListener("cpows:symbol_test", recvSymbolTest);
+      if (typeof Symbol === "function") {
+        mm.addMessageListener("cpows:symbol_test", recvSymbolTest);
+      }
       mm.addMessageListener("cpows:compartment_test", recvCompartmentTest);
       mm.addMessageListener("cpows:regexp_test", recvRegExpTest);
       mm.addMessageListener("cpows:lifetime_test_1", recvLifetimeTest1);
       mm.addMessageListener("cpows:lifetime_test_2", recvLifetimeTest2);
       mm.loadFrameScript("chrome://mochitests/content/chrome/content/base/test/chrome/cpows_child.js", true);
     }
 
     function start() {
--- a/content/html/content/src/HTMLLinkElement.cpp
+++ b/content/html/content/src/HTMLLinkElement.cpp
@@ -302,17 +302,19 @@ HTMLLinkElement::SetAttr(int32_t aNameSp
 
   // The ordering of the parent class's SetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not set until SetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aName == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
     Link::ResetLinkState(!!aNotify, true);
-    CreateAndDispatchEvent(OwnerDoc(), NS_LITERAL_STRING("DOMLinkChanged"));
+    if (IsInUncomposedDoc()) {
+      CreateAndDispatchEvent(OwnerDoc(), NS_LITERAL_STRING("DOMLinkChanged"));
+    }
   }
 
   if (NS_SUCCEEDED(rv) && aNameSpaceID == kNameSpaceID_None &&
       (aName == nsGkAtoms::href ||
        aName == nsGkAtoms::rel ||
        aName == nsGkAtoms::title ||
        aName == nsGkAtoms::media ||
        aName == nsGkAtoms::type)) {
@@ -365,17 +367,19 @@ HTMLLinkElement::UnsetAttr(int32_t aName
 
   // The ordering of the parent class's UnsetAttr call and Link::ResetLinkState
   // is important here!  The attribute is not unset until UnsetAttr returns, and
   // we will need the updated attribute value because notifying the document
   // that content states have changed will call IntrinsicState, which will try
   // to get updated information about the visitedness from Link.
   if (aAttribute == nsGkAtoms::href && kNameSpaceID_None == aNameSpaceID) {
     Link::ResetLinkState(!!aNotify, false);
-    CreateAndDispatchEvent(OwnerDoc(), NS_LITERAL_STRING("DOMLinkChanged"));
+    if (IsInUncomposedDoc()) {
+      CreateAndDispatchEvent(OwnerDoc(), NS_LITERAL_STRING("DOMLinkChanged"));
+    }
   }
 
   return rv;
 }
 
 nsresult
 HTMLLinkElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
 {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1650,22 +1650,35 @@ nsDocShell::LoadStream(nsIInputStream *a
         // Get the appropriate LoadType from nsIDocShellLoadInfo type
         loadType = ConvertDocShellLoadInfoToLoadType(lt);
     }
 
     NS_ENSURE_SUCCESS(Stop(nsIWebNavigation::STOP_NETWORK), NS_ERROR_FAILURE);
 
     mLoadType = loadType;
 
+    nsCOMPtr<nsISupports> owner;
+    aLoadInfo->GetOwner(getter_AddRefs(owner));
+    nsCOMPtr<nsIPrincipal> requestingPrincipal = do_QueryInterface(owner);
+    if (!requestingPrincipal) {
+      requestingPrincipal = nsContentUtils::GetSystemPrincipal();
+    }
+
     // build up a channel for this stream.
     nsCOMPtr<nsIChannel> channel;
-    NS_ENSURE_SUCCESS(NS_NewInputStreamChannel
-                      (getter_AddRefs(channel), uri, aStream,
-                       aContentType, aContentCharset),
-                      NS_ERROR_FAILURE);
+    nsresult rv =
+      NS_NewInputStreamChannel(getter_AddRefs(channel),
+                               uri,
+                               aStream,
+                               requestingPrincipal,
+                               nsILoadInfo::SEC_NORMAL,
+                               nsIContentPolicy::TYPE_OTHER,
+                               aContentType,
+                               aContentCharset);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
     nsCOMPtr<nsIURILoader>
         uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID));
     NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
 
     NS_ENSURE_SUCCESS(DoChannelLoad(channel, uriLoader, false),
                       NS_ERROR_FAILURE);
     return NS_OK;
@@ -10261,35 +10274,38 @@ nsDocShell::DoURILoad(nsIURI * aURI,
 
         if (isViewSource) {
             nsViewSourceHandler *vsh = nsViewSourceHandler::GetInstance();
             NS_ENSURE_TRUE(vsh,NS_ERROR_FAILURE);
 
             rv = vsh->NewSrcdocChannel(aURI, aSrcdoc, aBaseURI,
                                        getter_AddRefs(channel));
             NS_ENSURE_SUCCESS(rv, rv);
+            nsCOMPtr<nsILoadInfo> loadInfo =
+              new LoadInfo(requestingPrincipal,
+                           requestingNode,
+                           securityFlags,
+                           aContentPolicyType);
+            channel->SetLoadInfo(loadInfo);
         }
         else {
-            rv = NS_NewInputStreamChannel(getter_AddRefs(channel),aURI,
-                                          aSrcdoc,
-                                          NS_LITERAL_CSTRING("text/html"),
-                                          true);
+            rv = NS_NewInputStreamChannelInternal(getter_AddRefs(channel),
+                                                  aURI,
+                                                  aSrcdoc,
+                                                  NS_LITERAL_CSTRING("text/html"),
+                                                  requestingNode,
+                                                  requestingPrincipal,
+                                                  securityFlags,
+                                                  aContentPolicyType,
+                                                  true);
             NS_ENSURE_SUCCESS(rv, rv);
             nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
             MOZ_ASSERT(isc);
             isc->SetBaseURI(aBaseURI);
         }
-        // NS_NewInputStreamChannel does not yet attach the loadInfo in nsNetutil.h,
-        // hence we have to manually attach the loadInfo for that channel.
-        nsCOMPtr<nsILoadInfo> loadInfo =
-          new LoadInfo(requestingPrincipal,
-                       requestingNode,
-                       securityFlags,
-                       aContentPolicyType);
-        channel->SetLoadInfo(loadInfo);
     }
 
     nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
         do_QueryInterface(channel);
     if (appCacheChannel) {
         // Any document load should not inherit application cache.
         appCacheChannel->SetInheritApplicationCache(false);
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -594,30 +594,17 @@ class nsOuterWindowProxy : public js::Wr
 {
 public:
   MOZ_CONSTEXPR nsOuterWindowProxy() : js::Wrapper(0) { }
 
   virtual bool finalizeInBackground(JS::Value priv) const MOZ_OVERRIDE {
     return false;
   }
 
-  virtual const char *className(JSContext *cx,
-                                JS::Handle<JSObject*> wrapper) const MOZ_OVERRIDE;
-  virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE;
-
-  // Fundamental traps
-  virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
-                            const MOZ_OVERRIDE;
-  virtual bool preventExtensions(JSContext *cx,
-                                 JS::Handle<JSObject*> proxy) const MOZ_OVERRIDE;
-  virtual bool getPropertyDescriptor(JSContext* cx,
-                                     JS::Handle<JSObject*> proxy,
-                                     JS::Handle<jsid> id,
-                                     JS::MutableHandle<JSPropertyDescriptor> desc)
-                                     const MOZ_OVERRIDE;
+  // Standard internal methods
   virtual bool getOwnPropertyDescriptor(JSContext* cx,
                                         JS::Handle<JSObject*> proxy,
                                         JS::Handle<jsid> id,
                                         JS::MutableHandle<JSPropertyDescriptor> desc)
                                         const MOZ_OVERRIDE;
   virtual bool defineProperty(JSContext* cx,
                               JS::Handle<JSObject*> proxy,
                               JS::Handle<jsid> id,
@@ -626,47 +613,61 @@ public:
   virtual bool ownPropertyKeys(JSContext *cx,
                                JS::Handle<JSObject*> proxy,
                                JS::AutoIdVector &props) const MOZ_OVERRIDE;
   virtual bool delete_(JSContext *cx, JS::Handle<JSObject*> proxy,
                        JS::Handle<jsid> id,
                        bool *bp) const MOZ_OVERRIDE;
   virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy,
                          JS::AutoIdVector &props) const MOZ_OVERRIDE;
-
-  virtual bool watch(JSContext *cx, JS::Handle<JSObject*> proxy,
-                     JS::Handle<jsid> id, JS::Handle<JSObject*> callable) const MOZ_OVERRIDE;
-  virtual bool unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
-                       JS::Handle<jsid> id) const MOZ_OVERRIDE;
-  virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE {
-    return false;
-  }
-  virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE {
-    return false;
-  }
-
-  // Derived traps
+  virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
+                            const MOZ_OVERRIDE;
+  virtual bool preventExtensions(JSContext *cx,
+                                 JS::Handle<JSObject*> proxy) const MOZ_OVERRIDE;
   virtual bool has(JSContext *cx, JS::Handle<JSObject*> proxy,
                    JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
-  virtual bool hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy,
-                      JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
   virtual bool get(JSContext *cx, JS::Handle<JSObject*> proxy,
                    JS::Handle<JSObject*> receiver,
                    JS::Handle<jsid> id,
                    JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
   virtual bool set(JSContext *cx, JS::Handle<JSObject*> proxy,
                    JS::Handle<JSObject*> receiver,
                    JS::Handle<jsid> id,
                    bool strict,
                    JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
-  virtual bool keys(JSContext *cx, JS::Handle<JSObject*> proxy,
-                    JS::AutoIdVector &props) const MOZ_OVERRIDE;
+
+  // SpiderMonkey extensions
+  virtual bool getPropertyDescriptor(JSContext* cx,
+                                     JS::Handle<JSObject*> proxy,
+                                     JS::Handle<jsid> id,
+                                     JS::MutableHandle<JSPropertyDescriptor> desc)
+                                     const MOZ_OVERRIDE;
+  virtual bool hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy,
+                      JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
+  virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> proxy,
+                                            JS::AutoIdVector &props) const MOZ_OVERRIDE;
   virtual bool iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
                        unsigned flags,
                        JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
+  virtual const char *className(JSContext *cx,
+                                JS::Handle<JSObject*> wrapper) const MOZ_OVERRIDE;
+
+  virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE;
+
+  virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE {
+    return false;
+  }
+  virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE {
+    return false;
+  }
+
+  virtual bool watch(JSContext *cx, JS::Handle<JSObject*> proxy,
+                     JS::Handle<jsid> id, JS::Handle<JSObject*> callable) const MOZ_OVERRIDE;
+  virtual bool unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
+                       JS::Handle<jsid> id) const MOZ_OVERRIDE;
 
   static void ObjectMoved(JSObject *obj, const JSObject *old);
 
   static const nsOuterWindowProxy singleton;
 
 protected:
   static nsGlobalWindow* GetWindow(JSObject *proxy)
   {
@@ -925,22 +926,22 @@ nsOuterWindowProxy::set(JSContext *cx, J
     }
     return true;
   }
 
   return js::Wrapper::set(cx, proxy, receiver, id, strict, vp);
 }
 
 bool
-nsOuterWindowProxy::keys(JSContext *cx, JS::Handle<JSObject*> proxy,
-                         JS::AutoIdVector &props) const
+nsOuterWindowProxy::getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> proxy,
+                                                 JS::AutoIdVector &props) const
 {
   // BaseProxyHandler::keys seems to do what we want here: call
   // ownPropertyKeys and then filter out the non-enumerable properties.
-  return js::BaseProxyHandler::keys(cx, proxy, props);
+  return js::BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props);
 }
 
 bool
 nsOuterWindowProxy::iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
                             unsigned flags, JS::MutableHandle<JS::Value> vp) const
 {
   // BaseProxyHandler::iterate seems to do what we want here: fall
   // back on the property names returned from keys() and enumerate().
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -77,14 +77,15 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_url_empty_port.html]
 [test_url_malformedHost.html]
 [test_urlExceptions.html]
 [test_urlSearchParams.html]
 [test_urlSearchParams_utf8.html]
 [test_urlutils_stringify.html]
 [test_window_constructor.html]
 [test_window_cross_origin_props.html]
+[test_window_define_symbol.html]
 [test_window_enumeration.html]
 [test_window_extensible.html]
 [test_window_indexing.html]
 [test_window_named_frame_enumeration.html]
 [test_writable-replaceable.html]
 [test_navigatorPrefOverride.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_window_define_symbol.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1082672
+-->
+<head>
+  <meta charset="UTF-8">
+  <title>Test for Bug 1082672 part 2</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1082672">Mozilla Bug 1082672</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+var sym = Symbol("ponies");
+Object.defineProperty(window, sym, {configurable: true, value: 3});
+is(window[sym], 3);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1401,18 +1401,21 @@ XrayAttributeOrMethodKeys(JSContext* cx,
                           unsigned flags, JS::AutoIdVector& props)
 {
   for (; list->specs; ++list) {
     if (list->isEnabled(cx, obj)) {
       // Set i to be the index into our full list of ids/specs that we're
       // looking at now.
       size_t i = list->specs - specList;
       for ( ; ids[i] != JSID_VOID; ++i) {
+        // Skip non-enumerable properties and symbol-keyed properties unless
+        // they are specially requested via flags.
         if (((flags & JSITER_HIDDEN) ||
              (specList[i].flags & JSPROP_ENUMERATE)) &&
+            ((flags & JSITER_SYMBOLS) || !JSID_IS_SYMBOL(ids[i])) &&
             !props.append(ids[i])) {
           return false;
         }
       }
     }
   }
   return true;
 }
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1680,17 +1680,17 @@ InitIds(JSContext* cx, const Prefable<Sp
 {
   MOZ_ASSERT(prefableSpecs);
   MOZ_ASSERT(prefableSpecs->specs);
   do {
     // We ignore whether the set of ids is enabled and just intern all the IDs,
     // because this is only done once per application runtime.
     Spec* spec = prefableSpecs->specs;
     do {
-      if (!InternJSString(cx, *ids, spec->name)) {
+      if (!JS::PropertySpecNameToPermanentId(cx, spec->name, ids)) {
         return false;
       }
     } while (++ids, (++spec)->name);
 
     // We ran out of ids for that pref.  Put a JSID_VOID in on the id
     // corresponding to the list terminator for the pref.
     *ids = JSID_VOID;
     ++ids;
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -272,17 +272,17 @@ DOMProxyHandler::delete_(JSContext* cx, 
 bool
 BaseDOMProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
                                AutoIdVector& props) const
 {
   JS::Rooted<JSObject*> proto(cx);
   if (!JS_GetPrototype(cx, proxy, &proto))  {
     return false;
   }
-  return keys(cx, proxy, props) &&
+  return getOwnEnumerablePropertyKeys(cx, proxy, props) &&
          (!proto || js::GetPropertyKeys(cx, proto, 0, &props));
 }
 
 bool
 BaseDOMProxyHandler::watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                            JS::Handle<JSObject*> callable) const
 {
   return js::WatchGuts(cx, proxy, id, callable);
@@ -294,23 +294,23 @@ BaseDOMProxyHandler::unwatch(JSContext* 
   return js::UnwatchGuts(cx, proxy, id);
 }
 
 bool
 BaseDOMProxyHandler::ownPropertyKeys(JSContext* cx,
                                      JS::Handle<JSObject*> proxy,
                                      JS::AutoIdVector& props) const
 {
-  return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN, props);
+  return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
 }
 
 bool
-BaseDOMProxyHandler::keys(JSContext* cx,
-                          JS::Handle<JSObject*> proxy,
-                          JS::AutoIdVector& props) const
+BaseDOMProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx,
+                                                  JS::Handle<JSObject*> proxy,
+                                                  JS::AutoIdVector& props) const
 {
   return ownPropNames(cx, proxy, JSITER_OWNONLY, props);
 }
 
 bool
 DOMProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp) const
 {
   if (!hasOwn(cx, proxy, id, bp)) {
@@ -340,17 +340,18 @@ DOMProxyHandler::has(JSContext* cx, JS::
 }
 
 int32_t
 IdToInt32(JSContext* cx, JS::Handle<jsid> id)
 {
   JS::Rooted<JS::Value> idval(cx);
   double array_index;
   int32_t i;
-  if (!::JS_IdToValue(cx, id, &idval) ||
+  if (JSID_IS_SYMBOL(id) ||
+      !::JS_IdToValue(cx, id, &idval) ||
       !JS::ToNumber(cx, idval, &array_index) ||
       !::JS_DoubleIsInt32(array_index, &i)) {
     return -1;
   }
 
   return i;
 }
 
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -45,44 +45,48 @@ template<typename T> struct Prefable;
 
 class BaseDOMProxyHandler : public js::BaseProxyHandler
 {
 public:
   explicit MOZ_CONSTEXPR BaseDOMProxyHandler(const void* aProxyFamily, bool aHasPrototype = false)
     : js::BaseProxyHandler(aProxyFamily, aHasPrototype)
   {}
 
-  // Implementations of traps that can be implemented in terms of
-  // fundamental traps.
+  // Implementations of methods that can be implemented in terms of
+  // other lower-level methods.
+  bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy,
+                                JS::Handle<jsid> id,
+                                JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+  virtual bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
+                               JS::AutoIdVector &props) const MOZ_OVERRIDE;
   bool enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
                  JS::AutoIdVector& props) const MOZ_OVERRIDE;
+
   bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy,
                              JS::Handle<jsid> id,
                              JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
-  bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy,
-                                JS::Handle<jsid> id,
-                                JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+
+
+  // We override getOwnEnumerablePropertyKeys() and implement it directly
+  // instead of using the default implementation, which would call
+  // ownPropertyKeys and then filter out the non-enumerable ones. This avoids
+  // unnecessary work during enumeration.
+  virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
+                                            JS::AutoIdVector &props) const MOZ_OVERRIDE;
 
   bool watch(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
              JS::Handle<JSObject*> callable) const MOZ_OVERRIDE;
   bool unwatch(JSContext* cx, JS::Handle<JSObject*> proxy,
                JS::Handle<jsid> id) const MOZ_OVERRIDE;
-  virtual bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
-                               JS::AutoIdVector &props) const MOZ_OVERRIDE;
-  // We override keys() and implement it directly instead of using the
-  // default implementation, which would getOwnPropertyNames and then
-  // filter out the non-enumerable ones.  This avoids doing
-  // unnecessary work during enumeration.
-  virtual bool keys(JSContext* cx, JS::Handle<JSObject*> proxy,
-                    JS::AutoIdVector &props) const MOZ_OVERRIDE;
 
 protected:
   // Hook for subclasses to implement shared ownPropertyKeys()/keys()
   // functionality.  The "flags" argument is either JSITER_OWNONLY (for keys())
-  // or JSITER_OWNONLY | JSITER_HIDDEN (for ownPropertyKeys()).
+  // or JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS (for
+  // ownPropertyKeys()).
   virtual bool ownPropNames(JSContext* cx, JS::Handle<JSObject*> proxy,
                             unsigned flags,
                             JS::AutoIdVector& props) const = 0;
 
   // Hook for subclasses to allow set() to ignore named props while other things
   // that look at property descriptors see them.  This is intentionally not
   // named getOwnPropertyDescriptor to avoid subclasses that override it hiding
   // our public getOwnPropertyDescriptor.
@@ -95,35 +99,35 @@ protected:
 
 class DOMProxyHandler : public BaseDOMProxyHandler
 {
 public:
   MOZ_CONSTEXPR DOMProxyHandler()
     : BaseDOMProxyHandler(&family)
   {}
 
-  bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy) const MOZ_OVERRIDE;
   bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                       JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE
   {
     bool unused;
     return defineProperty(cx, proxy, id, desc, &unused);
   }
   virtual bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                               JS::MutableHandle<JSPropertyDescriptor> desc, bool* defined)
                               const;
+  bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
+               JS::Handle<jsid> id, bool* bp) const MOZ_OVERRIDE;
+  bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
+                    const MOZ_OVERRIDE;
+  bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy) const MOZ_OVERRIDE;
+  bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
+           bool* bp) const MOZ_OVERRIDE;
   bool set(JSContext *cx, JS::Handle<JSObject*> proxy, JS::Handle<JSObject*> receiver,
            JS::Handle<jsid> id, bool strict, JS::MutableHandle<JS::Value> vp)
            const MOZ_OVERRIDE;
-  bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
-               JS::Handle<jsid> id, bool* bp) const MOZ_OVERRIDE;
-  bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
-           bool* bp) const MOZ_OVERRIDE;
-  bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
-                    const MOZ_OVERRIDE;
 
   /*
    * If assigning to proxy[id] hits a named setter with OverrideBuiltins or
    * an indexed setter, call it and set *done to true on success. Otherwise, set
    * *done to false.
    */
   virtual bool setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                          JS::MutableHandle<JS::Value> vp, bool *done) const;
--- a/dom/bluetooth2/bluedroid/b2g_bdroid_buildcfg.h
+++ b/dom/bluetooth2/bluedroid/b2g_bdroid_buildcfg.h
@@ -34,9 +34,14 @@
 #define BTIF_HF_FEATURES   ( BTA_AG_FEAT_3WAY | \
                              BTA_AG_FEAT_REJECT | \
                              BTA_AG_FEAT_ECS    | \
                              BTA_AG_FEAT_EXTERR)
 
 /* CHLD values */
 #define BTA_AG_CHLD_VAL    "(0,1,2,3)"
 
+/* BLE Feature */
+#define BTA_GATT_INCLUDED  TRUE
+#define BLE_INCLUDED       TRUE
+#define SMP_INCLUDED       TRUE
+
 #endif /* B2G_BDROID_BUILDCFG_H */
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -61,17 +61,21 @@ public:
 
 
     // -------------------------------------------------------------------------
     // Texture objects - WebGL2ContextTextures.cpp
 
     void TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
     void TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height,
                       GLsizei depth);
-
+    void TexImage3D(GLenum target, GLint level, GLenum internalformat,
+                    GLsizei width, GLsizei height, GLsizei depth,
+                    GLint border, GLenum format, GLenum type,
+                    const Nullable<dom::ArrayBufferView> &pixels,
+                    ErrorResult& rv);
     void TexSubImage3D(GLenum target, GLint level,
                        GLint xoffset, GLint yoffset, GLint zoffset,
                        GLsizei width, GLsizei height, GLsizei depth,
                        GLenum format, GLenum type, const Nullable<dom::ArrayBufferView>& pixels,
                        ErrorResult& rv);
     void TexSubImage3D(GLenum target, GLint level,
                        GLint xoffset, GLint yoffset, GLint zoffset,
                        GLenum format, GLenum type, dom::ImageData* data,
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -200,16 +200,129 @@ WebGL2Context::TexStorage3D(GLenum targe
                           WebGLImageDataStatus::UninitializedImageData);
         w = std::max(1, w >> 1);
         h = std::max(1, h >> 1);
         d = std::max(1, d >> 1);
     }
 }
 
 void
+WebGL2Context::TexImage3D(GLenum target, GLint level, GLenum internalformat,
+                          GLsizei width, GLsizei height, GLsizei depth,
+                          GLint border, GLenum format, GLenum type,
+                          const Nullable<dom::ArrayBufferView> &pixels,
+                          ErrorResult& rv)
+{
+    if (IsContextLost())
+        return;
+
+    void* data;
+    size_t dataLength;
+    js::Scalar::Type jsArrayType;
+    if (pixels.IsNull()) {
+        data = nullptr;
+        dataLength = 0;
+        jsArrayType = js::Scalar::TypeMax;
+    } else {
+        const ArrayBufferView& view = pixels.Value();
+        view.ComputeLengthAndData();
+
+        data = view.Data();
+        dataLength = view.Length();
+        jsArrayType = JS_GetArrayBufferViewType(view.Obj());
+    }
+
+    const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
+    const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D;
+
+    if (!ValidateTexImageTarget(target, func, dims))
+        return;
+
+    TexImageTarget texImageTarget = target;
+
+    if (!ValidateTexImage(texImageTarget, level, internalformat,
+                          0, 0, 0,
+                          width, height, depth,
+                          border, format, type, func, dims))
+    {
+        return;
+    }
+
+    if (!ValidateTexInputData(type, jsArrayType, func, dims))
+        return;
+
+    TexInternalFormat effectiveInternalFormat =
+        EffectiveInternalFormatFromInternalFormatAndType(internalformat, type);
+
+    if (effectiveInternalFormat == LOCAL_GL_NONE) {
+        return ErrorInvalidOperation("texImage3D: bad combination of internalformat and type");
+    }
+
+    // we need to find the exact sized format of the source data. Slightly abusing
+    // EffectiveInternalFormatFromInternalFormatAndType for that purpose. Really, an unsized source format
+    // is the same thing as an unsized internalformat.
+    TexInternalFormat effectiveSourceFormat =
+        EffectiveInternalFormatFromInternalFormatAndType(format, type);
+    MOZ_ASSERT(effectiveSourceFormat != LOCAL_GL_NONE); // should have validated format/type combo earlier
+    const size_t srcbitsPerTexel = GetBitsPerTexel(effectiveSourceFormat);
+    MOZ_ASSERT((srcbitsPerTexel % 8) == 0); // should not have compressed formats here.
+    size_t srcTexelSize = srcbitsPerTexel / 8;
+
+    CheckedUint32 checked_neededByteLength =
+        GetImageSize(height, width, depth, srcTexelSize, mPixelStoreUnpackAlignment);
+
+    if (!checked_neededByteLength.isValid())
+        return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
+
+    uint32_t bytesNeeded = checked_neededByteLength.value();
+
+    if (dataLength && dataLength < bytesNeeded)
+        return ErrorInvalidOperation("texImage3D: not enough data for operation (need %d, have %d)",
+                                 bytesNeeded, dataLength);
+
+    WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget);
+
+    if (!tex)
+        return ErrorInvalidOperation("texImage3D: no texture is bound to this target");
+
+    if (tex->IsImmutable()) {
+        return ErrorInvalidOperation(
+            "texImage3D: disallowed because the texture "
+            "bound to this target has already been made immutable by texStorage3D");
+    }
+
+    GLenum driverType = LOCAL_GL_NONE;
+    GLenum driverInternalFormat = LOCAL_GL_NONE;
+    GLenum driverFormat = LOCAL_GL_NONE;
+    DriverFormatsFromEffectiveInternalFormat(gl,
+                                             effectiveInternalFormat,
+                                             &driverInternalFormat,
+                                             &driverFormat,
+                                             &driverType);
+
+    MakeContextCurrent();
+    GetAndFlushUnderlyingGLErrors();
+    gl->fTexImage3D(texImageTarget.get(), level,
+                    driverInternalFormat,
+                    width, height, depth,
+                    0, driverFormat, driverType,
+                    data);
+    GLenum error = GetAndFlushUnderlyingGLErrors();
+    if (error) {
+        return GenerateWarning("texImage3D generated error %s", ErrorName(error));
+    }
+
+    tex->SetImageInfo(texImageTarget, level,
+                      width, height, depth,
+                      effectiveInternalFormat,
+                      data ? WebGLImageDataStatus::InitializedImageData
+                           : WebGLImageDataStatus::UninitializedImageData);
+}
+
+void
 WebGL2Context::TexSubImage3D(GLenum rawTarget, GLint level,
                              GLint xoffset, GLint yoffset, GLint zoffset,
                              GLsizei width, GLsizei height, GLsizei depth,
                              GLenum format, GLenum type, const Nullable<dom::ArrayBufferView>& pixels,
                              ErrorResult& rv)
 {
     if (IsContextLost())
         return;
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -917,17 +917,21 @@ WebGLContext::GenerateMipmap(GLenum rawT
     WebGLTexture *tex = activeBoundTextureForTarget(target);
 
     if (!tex)
         return ErrorInvalidOperation("generateMipmap: No texture is bound to this target.");
 
     const TexImageTarget imageTarget = (target == LOCAL_GL_TEXTURE_2D)
                                                   ? LOCAL_GL_TEXTURE_2D
                                                   : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
-    if (!tex->HasImageInfoAt(imageTarget, tex->GetBaseMipmapLevel()))
+    if (!tex->IsMipmapRangeValid())
+    {
+        return ErrorInvalidOperation("generateMipmap: Texture does not have a valid mipmap range.");
+    }
+    if (!tex->HasImageInfoAt(imageTarget, tex->EffectiveBaseMipmapLevel()))
     {
         return ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
     }
 
     if (!IsWebGL2() && !tex->IsFirstImagePowerOfTwo())
         return ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");
 
     TexInternalFormat internalformat = tex->ImageInfoAt(imageTarget, 0).EffectiveInternalFormat();
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -862,16 +862,17 @@ InfoFrom(WebGLTexImageFunc func, WebGLTe
         case WebGLTexImageFunc::CopyTexSubImage: return "copyTexSubImage2D";
         case WebGLTexImageFunc::CompTexImage:    return "compressedTexImage2D";
         case WebGLTexImageFunc::CompTexSubImage: return "compressedTexSubImage2D";
         default:
             MOZ_CRASH();
         }
     case WebGLTexDimensions::Tex3D:
         switch (func) {
+        case WebGLTexImageFunc::TexImage:        return "texImage3D";
         case WebGLTexImageFunc::TexSubImage:     return "texSubImage3D";
         case WebGLTexImageFunc::CopyTexSubImage: return "copyTexSubImage3D";
         case WebGLTexImageFunc::CompTexSubImage: return "compressedTexSubImage3D";
         default:
             MOZ_CRASH();
         }
     default:
         MOZ_CRASH();
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 #include "mozilla/Scoped.h"
 #include "ScopedGLHelpers.h"
 #include "WebGLContext.h"
 #include "WebGLContextUtils.h"
 #include "WebGLTexelConversions.h"
 
 #include <algorithm>
+#include "mozilla/MathAlgorithms.h"
 
 using namespace mozilla;
 
 JSObject*
 WebGLTexture::WrapObject(JSContext *cx) {
     return dom::WebGLTextureBinding::Wrap(cx, this);
 }
 
@@ -59,62 +60,75 @@ WebGLTexture::ImageInfo::MemoryUsage() c
 }
 
 size_t
 WebGLTexture::MemoryUsage() const {
     if (IsDeleted())
         return 0;
     size_t result = 0;
     for(size_t face = 0; face < mFacesCount; face++) {
-      for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++)
-        result += ImageInfoAtFace(face, level).MemoryUsage();
-    }
+            for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++)
+                result += ImageInfoAtFace(face, level).MemoryUsage();
+        }
     return result;
 }
 
+static inline size_t
+MipmapLevelsForSize(const WebGLTexture::ImageInfo &info)
+{
+    GLsizei size = std::max(std::max(info.Width(), info.Height()), info.Depth());
+
+    // Find floor(log2(size)). (ES 3.0.4, 3.8 - Mipmapping).
+    return mozilla::FloorLog2(size);
+}
+
 bool
 WebGLTexture::DoesMipmapHaveAllLevelsConsistentlyDefined(TexImageTarget texImageTarget) const
 {
+    // We could not have generated a mipmap if the base image wasn't defined.
     if (mHaveGeneratedMipmap)
         return true;
 
-    if (GetMaxMipmapLevel() < GetBaseMipmapLevel())
+    if (!IsMipmapRangeValid())
         return false;
 
     // We want a copy here so we can modify it temporarily.
-    ImageInfo expected = ImageInfoAt(texImageTarget, GetBaseMipmapLevel());
+    ImageInfo expected = ImageInfoAt(texImageTarget, EffectiveBaseMipmapLevel());
+    if (!expected.IsPositive())
+        return false;
 
-    // checks if custom level>0 images are all defined up to the highest level defined
-    // and have the expected dimensions
-    for (size_t level = GetBaseMipmapLevel(); level <= GetMaxMipmapLevel(); ++level) {
+    // If Level{max} is > mMaxLevelWithCustomImages, then check if we are
+    // missing any image levels.
+    if (mMaxMipmapLevel > mMaxLevelWithCustomImages) {
+        if (MipmapLevelsForSize(expected) > mMaxLevelWithCustomImages)
+            return false;
+    }
+
+    // Checks if custom images are all defined up to the highest level and
+    // have the expected dimensions.
+    for (size_t level = EffectiveBaseMipmapLevel(); level <= EffectiveMaxMipmapLevel(); ++level) {
         const ImageInfo& actual = ImageInfoAt(texImageTarget, level);
         if (actual != expected)
             return false;
 
-        // Check the raw value here, not the clamped one, since we don't want
-        // to terminate early if there aren't enough levels defined.
-        if (level == mMaxMipmapLevel)
-            return true;
-
         expected.mWidth = std::max(1, expected.mWidth / 2);
         expected.mHeight = std::max(1, expected.mHeight / 2);
         expected.mDepth = std::max(1, expected.mDepth / 2);
 
         // if the current level has size 1x1, we can stop here: the spec doesn't seem to forbid the existence
         // of extra useless levels.
         if (actual.mWidth == 1 &&
             actual.mHeight == 1 &&
             actual.mDepth == 1)
         {
             return true;
         }
     }
 
-    // if we're here, we've exhausted all levels without finding a 1x1 image
-    return false;
+    return true;
 }
 
 void
 WebGLTexture::Bind(TexTarget aTexTarget) {
     // this function should only be called by bindTexture().
     // it assumes that the GL context is already current.
 
     bool firstTimeThisTextureIsBound = !HasEverBeenBound();
@@ -172,35 +186,29 @@ WebGLTexture::SetGeneratedMipmap() {
         mHaveGeneratedMipmap = true;
         SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
     }
 }
 
 void
 WebGLTexture::SetCustomMipmap() {
     if (mHaveGeneratedMipmap) {
-        // if we were in GeneratedMipmap mode and are now switching to CustomMipmap mode,
-        // we need to compute now all the mipmap image info.
+        if (!IsMipmapRangeValid())
+            return;
 
-        // since we were in GeneratedMipmap mode, we know that the level 0 images all have the same info,
-        // and are power-of-two.
-        ImageInfo imageInfo = ImageInfoAtFace(0, GetBaseMipmapLevel());
+        // If we were in GeneratedMipmap mode and are now switching to CustomMipmap mode,
+        // we now need to compute all the mipmap image info.
+        ImageInfo imageInfo = ImageInfoAtFace(0, EffectiveBaseMipmapLevel());
         NS_ASSERTION(mContext->IsWebGL2() || imageInfo.IsPowerOfTwo(),
                      "this texture is NPOT, so how could GenerateMipmap() ever accept it?");
 
-        GLsizei size = std::max(std::max(imageInfo.mWidth, imageInfo.mHeight), imageInfo.mDepth);
+        size_t maxLevel = MipmapLevelsForSize(imageInfo);
+        EnsureMaxLevelWithCustomImagesAtLeast(EffectiveBaseMipmapLevel() + maxLevel);
 
-        // Find floor(log2(size)). (ES 3.0.4, 3.8 - Mipmapping).
-        size_t maxLevel = 0;
-        for (GLsizei n = size; n > 1; n >>= 1)
-            ++maxLevel;
-
-        EnsureMaxLevelWithCustomImagesAtLeast(maxLevel);
-
-        for (size_t level = GetBaseMipmapLevel() + 1; level <= GetMaxMipmapLevel(); ++level) {
+        for (size_t level = EffectiveBaseMipmapLevel() + 1; level <= EffectiveMaxMipmapLevel(); ++level) {
             imageInfo.mWidth = std::max(imageInfo.mWidth / 2, 1);
             imageInfo.mHeight = std::max(imageInfo.mHeight / 2, 1);
             imageInfo.mDepth = std::max(imageInfo.mDepth / 2, 1);
             for(size_t face = 0; face < mFacesCount; ++face)
                 ImageInfoAtFace(face, level) = imageInfo;
         }
     }
     mHaveGeneratedMipmap = false;
@@ -214,21 +222,16 @@ WebGLTexture::AreAllLevel0ImageInfosEqua
     }
     return true;
 }
 
 bool
 WebGLTexture::IsMipmapComplete() const {
     MOZ_ASSERT(mTarget == LOCAL_GL_TEXTURE_2D ||
                mTarget == LOCAL_GL_TEXTURE_3D);
-
-    if (!ImageInfoAtFace(0, GetBaseMipmapLevel()).IsPositive())
-        return false;
-    if (mHaveGeneratedMipmap)
-        return true;
     return DoesMipmapHaveAllLevelsConsistentlyDefined(LOCAL_GL_TEXTURE_2D);
 }
 
 bool
 WebGLTexture::IsCubeComplete() const {
     MOZ_ASSERT(mTarget == LOCAL_GL_TEXTURE_CUBE_MAP);
 
     const ImageInfo &first = ImageInfoAt(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0);
@@ -244,27 +247,42 @@ WebGLTexture::IsMipmapCubeComplete() con
     for (int i = 0; i < 6; i++) {
         const TexImageTarget face = TexImageTargetForTargetAndFace(LOCAL_GL_TEXTURE_CUBE_MAP, i);
         if (!DoesMipmapHaveAllLevelsConsistentlyDefined(face))
             return false;
     }
     return true;
 }
 
+bool
+WebGLTexture::IsMipmapRangeValid() const
+{
+    // In ES3, if a texture is immutable, the mipmap levels are clamped.
+    if (IsImmutable())
+        return true;
+    if (mBaseMipmapLevel > std::min(mMaxLevelWithCustomImages, mMaxMipmapLevel))
+        return false;
+    return true;
+}
+
 WebGLTextureFakeBlackStatus
 WebGLTexture::ResolvedFakeBlackStatus() {
     if (MOZ_LIKELY(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown)) {
         return mFakeBlackStatus;
     }
 
     // Determine if the texture needs to be faked as a black texture.
-    // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec.
-
+    // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec, and 3.8.13 in
+    // the OpenGL ES 3.0.4 spec.
+    if (!IsMipmapRangeValid()) {
+        mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
+        return mFakeBlackStatus;
+    }
     for (size_t face = 0; face < mFacesCount; ++face) {
-        if (ImageInfoAtFace(face, GetBaseMipmapLevel()).mImageDataStatus == WebGLImageDataStatus::NoImageData) {
+        if (ImageInfoAtFace(face, EffectiveBaseMipmapLevel()).mImageDataStatus == WebGLImageDataStatus::NoImageData) {
             // In case of undefined texture image, we don't print any message because this is a very common
             // and often legitimate case (asynchronous texture loading).
             mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
             return mFakeBlackStatus;
         }
     }
 
     const char *msg_rendering_as_black
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -12,16 +12,17 @@
 #include "WebGLStrongTypes.h"
 
 #include "nsWrapperCache.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Assertions.h"
 #include <algorithm>
+#include "nsAlgorithm.h"
 
 namespace mozilla {
 
 // Zero is not an integer power of two.
 inline bool is_pot_assuming_nonnegative(GLsizei x)
 {
     return x && (x & (x-1)) == 0;
 }
@@ -281,26 +282,32 @@ public:
 
     bool IsMipmapCubeComplete() const;
 
     void SetFakeBlackStatus(WebGLTextureFakeBlackStatus x);
 
     bool IsImmutable() const { return mImmutable; }
     void SetImmutable() { mImmutable = true; }
 
-    void SetBaseMipmapLevel(unsigned level) { mBaseMipmapLevel = level; }
-    void SetMaxMipmapLevel(unsigned level) { mMaxMipmapLevel = level; }
-    size_t GetBaseMipmapLevel() const {
-        // Clamp to [0, levels - 1]
-        return std::min(mBaseMipmapLevel, mMaxLevelWithCustomImages);
+    void SetBaseMipmapLevel(size_t level) { mBaseMipmapLevel = level; }
+    void SetMaxMipmapLevel(size_t level) { mMaxMipmapLevel = level; }
+
+    // Clamping (from ES 3.0.4, section 3.8 - Texturing). When not immutable,
+    // the ranges must be guarded.
+    size_t EffectiveBaseMipmapLevel() const {
+        if (IsImmutable())
+            return std::min(mBaseMipmapLevel, mMaxLevelWithCustomImages);
+        return mBaseMipmapLevel;
     }
-    size_t GetMaxMipmapLevel() const {
-        // Clamp to [base, levels - 1]
+    size_t EffectiveMaxMipmapLevel() const {
+        if (IsImmutable())
+            return mozilla::clamped(mMaxMipmapLevel, EffectiveBaseMipmapLevel(), mMaxLevelWithCustomImages);
         return std::min(mMaxMipmapLevel, mMaxLevelWithCustomImages);
     }
+    bool IsMipmapRangeValid() const;
 
     size_t MaxLevelWithCustomImages() const { return mMaxLevelWithCustomImages; }
 
     // Returns the current fake-black-status, except if it was Unknown,
     // in which case this function resolves it first, so it never returns Unknown.
     WebGLTextureFakeBlackStatus ResolvedFakeBlackStatus();
 };
 
--- a/dom/json/nsJSON.cpp
+++ b/dom/json/nsJSON.cpp
@@ -17,16 +17,17 @@
 #include "nsIUnicodeDecoder.h"
 #include "nsXPCOMStrings.h"
 #include "nsNetUtil.h"
 #include "nsContentUtils.h"
 #include "nsIScriptError.h"
 #include "nsCRTGlue.h"
 #include "nsAutoPtr.h"
 #include "nsIScriptSecurityManager.h"
+#include "nsNullPrincipal.h"
 #include "mozilla/Maybe.h"
 #include <algorithm>
 
 using mozilla::dom::EncodingUtils;
 
 #define JSON_STREAM_BUFSIZE 4096
 
 NS_INTERFACE_MAP_BEGIN(nsJSON)
@@ -404,19 +405,29 @@ nsJSON::DecodeInternal(JSContext* cx,
   // Consume the stream
   nsCOMPtr<nsIChannel> jsonChannel;
   if (!mURI) {
     NS_NewURI(getter_AddRefs(mURI), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
     if (!mURI)
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  nsresult rv =
-    NS_NewInputStreamChannel(getter_AddRefs(jsonChannel), mURI, aStream,
-                             NS_LITERAL_CSTRING("application/json"));
+  nsresult rv;
+  nsCOMPtr<nsIPrincipal> nullPrincipal =
+    do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = NS_NewInputStreamChannel(getter_AddRefs(jsonChannel),
+                                mURI,
+                                aStream,
+                                nullPrincipal,
+                                nsILoadInfo::SEC_NORMAL,
+                                nsIContentPolicy::TYPE_OTHER,
+                                NS_LITERAL_CSTRING("application/json"));
+
   if (!jsonChannel || NS_FAILED(rv))
     return NS_ERROR_FAILURE;
 
   nsRefPtr<nsJSONListener> jsonListener =
     new nsJSONListener(cx, aRetval.address(), aNeedsConverter);
 
   //XXX this stream pattern should be consolidated in netwerk
   rv = jsonListener->OnStartRequest(jsonChannel, nullptr);
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -31,16 +31,17 @@
 #include "nsXPIDLString.h"
 #include "prprf.h"
 #include "nsEscape.h"
 #include "nsIWebNavigation.h"
 #include "nsIDocShell.h"
 #include "nsIContentViewer.h"
 #include "nsIXPConnect.h"
 #include "nsContentUtils.h"
+#include "nsNullPrincipal.h"
 #include "nsJSUtils.h"
 #include "nsThreadUtils.h"
 #include "nsIScriptChannel.h"
 #include "nsIDocument.h"
 #include "nsILoadInfo.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIWritablePropertyBag2.h"
@@ -422,19 +423,28 @@ nsresult nsJSChannel::Init(nsIURI *aURI)
     if (!mIOThunk)
         return NS_ERROR_OUT_OF_MEMORY;
 
     // Create a stock input stream channel...
     // Remember, until AsyncOpen is called, the script will not be evaluated
     // and the underlying Input Stream will not be created...
     nsCOMPtr<nsIChannel> channel;
 
+    nsCOMPtr<nsIPrincipal> nullPrincipal =
+      do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     // If the resultant script evaluation actually does return a value, we
     // treat it as html.
-    rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, mIOThunk,
+    rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
+                                  aURI,
+                                  mIOThunk,
+                                  nullPrincipal,
+                                  nsILoadInfo::SEC_NORMAL,
+                                  nsIContentPolicy::TYPE_OTHER,
                                   NS_LITERAL_CSTRING("text/html"));
     if (NS_FAILED(rv)) return rv;
 
     rv = mIOThunk->Init(aURI);
     if (NS_SUCCEEDED(rv)) {
         mStreamChannel = channel;
         mPropertyBag = do_QueryInterface(channel);
         nsCOMPtr<nsIWritablePropertyBag2> writableBag =
--- a/dom/webidl/MediaKeySession.webidl
+++ b/dom/webidl/MediaKeySession.webidl
@@ -16,33 +16,29 @@ interface MediaKeySession : EventTarget 
   readonly attribute MediaKeyError? error;
 
   // session properties
   readonly attribute DOMString keySystem;
   readonly attribute DOMString sessionId;
 
   readonly attribute unrestricted double expiration;
 
-  // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457
   readonly attribute Promise<void> closed;
 
   [NewObject, Throws]
   Promise<void> generateRequest(DOMString initDataType, (ArrayBufferView or ArrayBuffer) initData);
 
   [NewObject, Throws]
   Promise<boolean> load(DOMString sessionId);
 
   // session operations
-  // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457
   [NewObject, Throws]
   Promise<void> update((ArrayBufferView or ArrayBuffer) response);
 
-  // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457
   [NewObject, Throws]
   Promise<void> close();
 
-  // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457
   [NewObject, Throws]
   Promise<void> remove();
 
   [NewObject, Throws]
   Promise<sequence<ArrayBuffer>> getUsableKeyIds();
 };
--- a/dom/webidl/MediaKeys.webidl
+++ b/dom/webidl/MediaKeys.webidl
@@ -15,17 +15,16 @@ enum SessionType { "temporary", "persist
 
 [Pref="media.eme.enabled"]
 interface MediaKeys {
   readonly attribute DOMString keySystem;
 
   [NewObject, Throws]
   MediaKeySession createSession(optional SessionType sessionType = "temporary");
 
-  // void, not any: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26457
   [NewObject, Throws]
   Promise<void> setServerCertificate((ArrayBufferView or ArrayBuffer) serverCertificate);
 
   [Throws,NewObject]
   static Promise<MediaKeys> create(DOMString keySystem);
   static IsTypeSupportedResult isTypeSupported(DOMString keySystem, optional DOMString initDataType, optional DOMString contentType, optional DOMString capability);
 
 };
--- a/dom/webidl/WebGL2RenderingContext.webidl
+++ b/dom/webidl/WebGL2RenderingContext.webidl
@@ -337,16 +337,21 @@ interface WebGL2RenderingContext : WebGL
 
     /* Renderbuffer objects */
     void renderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
 
     /* Texture objects */
     void texStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
     void texStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height,
                       GLsizei depth);
+    [Throws]
+    void texImage3D(GLenum target, GLint level, GLenum internalformat,
+                    GLsizei width, GLsizei height, GLsizei depth,
+                    GLint border, GLenum format,
+                    GLenum type, ArrayBufferView? pixels);
     [Throws] void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
                                 GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
                                 ArrayBufferView? pixels);
     [Throws] void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
                                 GLenum format, GLenum type, ImageData? data);
     [Throws] void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
                                 GLenum format, GLenum type, HTMLImageElement image);
     [Throws] void texSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
--- a/editor/libeditor/nsHTMLEditorEventListener.cpp
+++ b/editor/libeditor/nsHTMLEditorEventListener.cpp
@@ -67,16 +67,21 @@ nsHTMLEditorEventListener::MouseUp(nsIDO
 
   return nsEditorEventListener::MouseUp(aMouseEvent);
 }
 
 nsresult
 nsHTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
 {
   nsHTMLEditor* htmlEditor = GetHTMLEditor();
+  // Contenteditable should disregard mousedowns outside it.
+  // IsAcceptableInputEvent() checks it for a mouse event.
+  if (!htmlEditor->IsAcceptableInputEvent(aMouseEvent)) {
+    return NS_OK;
+  }
 
   // Detect only "context menu" click
   // XXX This should be easier to do!
   // But eDOMEvents_contextmenu and NS_CONTEXTMENU is not exposed in any event
   // interface :-(
   int16_t buttonNumber;
   nsresult rv = aMouseEvent->GetButton(&buttonNumber);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -88,21 +93,16 @@ nsHTMLEditorEventListener::MouseDown(nsI
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDOMEventTarget> target;
   rv = aMouseEvent->GetExplicitOriginalTarget(getter_AddRefs(target));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
 
-  // Contenteditable should disregard mousedowns outside it
-  if (element && !htmlEditor->IsDescendantOfEditorRoot(element)) {
-    return NS_OK;
-  }
-
   if (isContextClick || (buttonNumber == 0 && clickCount == 2)) {
     nsCOMPtr<nsISelection> selection;
     mEditor->GetSelection(getter_AddRefs(selection));
     NS_ENSURE_TRUE(selection, NS_OK);
 
     // Get location of mouse within target node
     nsCOMPtr<nsIDOMNode> parent;
     rv = aMouseEvent->GetRangeParent(getter_AddRefs(parent));
--- a/extensions/gio/nsGIOProtocolHandler.cpp
+++ b/extensions/gio/nsGIOProtocolHandler.cpp
@@ -12,16 +12,17 @@
 #include "nsIPrefBranch.h"
 #include "nsIObserver.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 #include "nsIStringBundle.h"
 #include "nsIStandardURL.h"
 #include "nsMimeTypes.h"
 #include "nsNetUtil.h"
+#include "nsNullPrincipal.h"
 #include "mozilla/Monitor.h"
 #include <gio/gio.h>
 #include <algorithm>
 
 #define MOZ_GIO_SCHEME              "moz-gio"
 #define MOZ_GIO_SUPPORTED_PROTOCOLS "network.gio.supported-protocols"
 
 //-----------------------------------------------------------------------------
@@ -1057,21 +1058,28 @@ nsGIOProtocolHandler::NewChannel(nsIURI 
 
   nsRefPtr<nsGIOInputStream> stream = new nsGIOInputStream(spec);
   if (!stream)
   {
     rv = NS_ERROR_OUT_OF_MEMORY;
   }
   else
   {
+    nsCOMPtr<nsIPrincipal> nullPrincipal =
+      do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     // start out assuming an unknown content-type.  we'll set the content-type
     // to something better once we open the URI.
     rv = NS_NewInputStreamChannel(aResult,
                                   aURI,
                                   stream,
+                                  nullPrincipal,
+                                  nsILoadInfo::SEC_NORMAL,
+                                  nsIContentPolicy::TYPE_OTHER,
                                   NS_LITERAL_CSTRING(UNKNOWN_CONTENT_TYPE));
     if (NS_SUCCEEDED(rv))
       stream->SetChannel(*aResult);
   }
   return rv;
 }
 
 NS_IMETHODIMP
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -543,21 +543,22 @@ Factory::SetDirect3D10Device(ID3D10Devic
   // On Windows 8 error codes are the default, but on Windows 7 the
   // default is to throw (or perhaps only with some drivers?)
   aDevice->SetExceptionMode(0);
   mD3D10Device = aDevice;
 }
 
 ID3D10Device1*
 Factory::GetDirect3D10Device()
-
 {
 #ifdef DEBUG
-  UINT mode = mD3D10Device->GetExceptionMode();
-  MOZ_ASSERT(0 == mode);
+  if (mD3D10Device) {
+    UINT mode = mD3D10Device->GetExceptionMode();
+    MOZ_ASSERT(0 == mode);
+  }
 #endif
   return mD3D10Device;
 }
 
 #ifdef USE_D2D1_1
 TemporaryRef<DrawTarget>
 Factory::CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat)
 {
--- a/gfx/angle/Makefile.in
+++ b/gfx/angle/Makefile.in
@@ -12,13 +12,8 @@ endif
 ifdef MOZ_D3DCOMPILER_XP_CAB
 	expand '$(MOZ_D3DCOMPILER_XP_CAB)' -F:$(MOZ_D3DCOMPILER_XP_DLL) '$(DIST)/bin'
 endif
 
 endif
 
 include $(topsrcdir)/config/rules.mk
 
-# We have to filter out -pedantic, because of
-# comma-at-end-of-enumerator list failures.  We can try to get this fixed
-# upstream at some point.
-CXXFLAGS := $(filter-out -pedantic,$(CXXFLAGS))
-CFLAGS   := $(filter-out -pedantic,$(CFLAGS))
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1248,17 +1248,17 @@ GLContext::InitWithPrefix(const char *pr
 
                 MarkUnsupported(GLFeature::map_buffer_range);
                 ClearSymbols(mapBufferRangeSymbols);
             }
         }
 
         if (IsSupported(GLFeature::texture_3D)) {
             SymLoadStruct coreSymbols[] = {
-                // TexImage3D is not required for WebGL2 so not queried here.
+                { (PRFuncPtr*) &mSymbols.fTexImage3D, { "TexImage3D", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fTexSubImage3D, { "TexSubImage3D", nullptr } },
                 END_SYMBOLS
             };
 
             SymLoadStruct extSymbols[] = {
                 { (PRFuncPtr*) &mSymbols.fTexSubImage3D, { "TexSubImage3DEXT", "TexSubImage3DOES", nullptr } },
                 END_SYMBOLS
             };
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -3127,16 +3127,31 @@ public:
         BEFORE_GL_CALL;
         ASSERT_SYMBOL_PRESENT(fTexStorage3D);
         mSymbols.fTexStorage3D(target, levels, internalformat, width, height, depth);
         AFTER_GL_CALL;
     }
 
 // -----------------------------------------------------------------------------
 // 3D Textures
+    void fTexImage3D(GLenum target, GLint level,
+                     GLint internalFormat,
+                     GLsizei width, GLsizei height, GLsizei depth,
+                     GLint border, GLenum format, GLenum type,
+                     const GLvoid * data)
+    {
+        BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fTexImage3D);
+        mSymbols.fTexImage3D(target, level, internalFormat,
+                             width, height, depth,
+                             border, format, type,
+                             data);
+        AFTER_GL_CALL;
+    }
+
     void fTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                         GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
                         GLenum format, GLenum type, const GLvoid* pixels)
     {
         BEFORE_GL_CALL;
         ASSERT_SYMBOL_PRESENT(fTexSubImage3D);
         mSymbols.fTexSubImage3D(target, level, xoffset, yoffset, zoffset,
                                 width, height, depth, format, type,
--- a/gfx/gl/GLContextSymbols.h
+++ b/gfx/gl/GLContextSymbols.h
@@ -637,16 +637,22 @@ struct GLContextSymbols
     typedef void (GLAPIENTRY * PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint* value);
     PFNGLUNIFORM3UIVPROC fUniform3uiv;
     typedef void (GLAPIENTRY * PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint* value);
     PFNGLUNIFORM4UIVPROC fUniform4uiv;
     typedef GLint (GLAPIENTRY * PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar* name);
     PFNGLGETFRAGDATALOCATIONPROC fGetFragDataLocation;
 
     // 3D Textures
+    typedef void (GLAPIENTRY * PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level,
+                                                     GLenum internalFormat,
+                                                     GLenum width, GLsizei height, GLsizei depth,
+                                                     GLint border, GLenum format, GLenum type,
+                                                     const GLvoid* pixels);
+    PFNGLTEXIMAGE3DPROC fTexImage3D;
     typedef void (GLAPIENTRY * PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset,
                                                         GLint yoffset, GLint zoffset, GLsizei width,
                                                         GLsizei height, GLsizei depth, GLenum format,
                                                         GLenum type, const GLvoid* pixels);
     PFNGLTEXSUBIMAGE3DPROC fTexSubImage3D;
     typedef void (GLAPIENTRY * PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset,
                                                             GLint yoffset, GLint zoffset, GLint x,
                                                             GLint y, GLsizei width, GLsizei height);
--- a/gfx/thebes/gfxSVGGlyphs.cpp
+++ b/gfx/thebes/gfxSVGGlyphs.cpp
@@ -364,27 +364,26 @@ gfxSVGGlyphsDocument::ParseDocument(cons
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIDocument> document(do_QueryInterface(domDoc));
     if (!document) {
         return NS_ERROR_FAILURE;
     }
 
     nsCOMPtr<nsIChannel> channel;
-    rv = NS_NewInputStreamChannel(getter_AddRefs(channel), uri, nullptr /* stream */,
-                                  SVG_CONTENT_TYPE, UTF8_CHARSET);
+    rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
+                                  uri,
+                                  nullptr, //aStream
+                                  principal,
+                                  nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
+                                  nsIContentPolicy::TYPE_OTHER,
+                                  SVG_CONTENT_TYPE,
+                                  UTF8_CHARSET);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsILoadInfo> loadInfo =
-      new LoadInfo(principal,
-                   nullptr,
-                   nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
-                   nsIContentPolicy::TYPE_OTHER);
-    channel->SetLoadInfo(loadInfo);
-
     // Set this early because various decisions during page-load depend on it.
     document->SetIsBeingUsedAsImage();
     document->SetReadyStateInternal(nsIDocument::READYSTATE_UNINITIALIZED);
 
     nsCOMPtr<nsIStreamListener> listener;
     rv = document->StartDocumentLoad("external-resource", channel,
                                      nullptr,    // aLoadGroup
                                      nullptr,    // aContainer
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -408,34 +408,34 @@ gfxUserFontEntry::LoadNextSrc()
         if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Local) {
             gfxFontEntry* fe =
                 gfxPlatform::GetPlatform()->LookupLocalFont(currSrc.mLocalName,
                                                             mWeight,
                                                             mStretch,
                                                             mItalic);
             mFontSet->SetLocalRulesUsed();
             if (fe) {
-                LOG(("fontset (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
+                LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
                      mFontSet, mSrcIndex,
                      NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
                      NS_ConvertUTF16toUTF8(mFamilyName).get(),
                      uint32_t(mFontSet->mGeneration)));
                 fe->mFeatureSettings.AppendElements(mFeatureSettings);
                 fe->mLanguageOverride = mLanguageOverride;
                 fe->mFamilyName = mFamilyName;
                 // For src:local(), we don't care whether the request is from
                 // a private window as there's no issue of caching resources;
                 // local fonts are just available all the time.
                 StoreUserFontData(fe, false, nsString(), nullptr, 0,
                                   gfxUserFontData::kUnknownCompression);
                 mPlatformFontEntry = fe;
                 SetLoadState(STATUS_LOADED);
                 return;
             } else {
-                LOG(("fontset (%p) [src %d] failed local: (%s) for (%s)\n",
+                LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n",
                      mFontSet, mSrcIndex,
                      NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
                      NS_ConvertUTF16toUTF8(mFamilyName).get()));
             }
         }
 
         // src url ==> start the load process
         else if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL) {
@@ -642,32 +642,32 @@ gfxUserFontEntry::LoadPlatformFont(const
         fe->mFamilyName = mFamilyName;
         StoreUserFontData(fe, mFontSet->GetPrivateBrowsing(), originalFullName,
                           &metadata, metaOrigLen, compression);
 #ifdef PR_LOGGING
         if (LOG_ENABLED()) {
             nsAutoCString fontURI;
             mSrcList[mSrcIndex].mURI->GetSpec(fontURI);
             LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n",
-                 this, mSrcIndex, fontURI.get(),
+                 mFontSet, mSrcIndex, fontURI.get(),
                  NS_ConvertUTF16toUTF8(mFamilyName).get(),
                  uint32_t(mFontSet->mGeneration)));
         }
 #endif
         mPlatformFontEntry = fe;
         SetLoadState(STATUS_LOADED);
         gfxUserFontSet::UserFontCache::CacheFont(fe);
     } else {
 #ifdef PR_LOGGING
         if (LOG_ENABLED()) {
             nsAutoCString fontURI;
             mSrcList[mSrcIndex].mURI->GetSpec(fontURI);
             LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s)"
                  " error making platform font\n",
-                 this, mSrcIndex, fontURI.get(),
+                 mFontSet, mSrcIndex, fontURI.get(),
                  NS_ConvertUTF16toUTF8(mFamilyName).get()));
         }
 #endif
     }
 
     // The downloaded data can now be discarded; the font entry is using the
     // sanitized copy
     moz_free((void*)aFontData);
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -462,16 +462,20 @@ public:
 
         static nsTHashtable<Entry>* sUserFonts;
     };
 
     void SetLocalRulesUsed() {
         mLocalRulesUsed = true;
     }
 
+#ifdef PR_LOGGING
+    static PRLogModuleInfo* GetUserFontsLog();
+#endif
+
 protected:
     // Protected destructor, to discourage deletion outside of Release():
     virtual ~gfxUserFontSet();
 
     // Return whether the font set is associated with a private-browsing tab.
     virtual bool GetPrivateBrowsing() = 0;
 
     // parse data for a data URL
@@ -507,18 +511,16 @@ protected:
     // font families defined by @font-face rules
     nsRefPtrHashtable<nsStringHashKey, gfxUserFontFamily> mFontFamilies;
 
     uint64_t        mGeneration;        // bumped on any font load change
     uint64_t        mRebuildGeneration; // only bumped on rebuilds
 
     // true when local names have been looked up, false otherwise
     bool mLocalRulesUsed;
-
-    static PRLogModuleInfo* GetUserFontsLog();
 };
 
 // acts a placeholder until the real font is downloaded
 
 class gfxUserFontEntry : public gfxFontEntry {
     friend class gfxUserFontSet;
     friend class nsUserFontSet;
     friend class nsFontFaceLoader;
--- a/image/decoders/icon/android/nsIconChannel.cpp
+++ b/image/decoders/icon/android/nsIconChannel.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/dom/ContentChild.h"
 #include "nsMimeTypes.h"
 #include "nsIURL.h"
 #include "nsXULAppAPI.h"
 #include "AndroidBridge.h"
 #include "nsIconChannel.h"
 #include "nsIStringStream.h"
 #include "nsNetUtil.h"
+#include "nsNullPrincipal.h"
 
 NS_IMPL_ISUPPORTS(nsIconChannel,
                   nsIRequest,
                   nsIChannel)
 
 using namespace mozilla;
 using mozilla::dom::ContentChild;
 
@@ -97,17 +98,26 @@ moz_icon_to_channel(nsIURI *aURI, const 
 
   nsCOMPtr<nsIStringInputStream> stream =
     do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = stream->AdoptData((char*)buf, buf_size);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return NS_NewInputStreamChannel(aChannel, aURI, stream,
+  nsCOMPtr<nsIPrincipal> nullPrincipal =
+    do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_NewInputStreamChannel(aChannel,
+                                  aURI,
+                                  stream,
+                                  nullPrincipal,
+                                  nsILoadInfo::SEC_NORMAL,
+                                  nsIContentPolicy::TYPE_OTHER,
                                   NS_LITERAL_CSTRING(IMAGE_ICON_MS));
 }
 
 nsresult
 nsIconChannel::Init(nsIURI* aURI)
 {
   nsCOMPtr<nsIMozIconURI> iconURI = do_QueryInterface(aURI);
   NS_ASSERTION(iconURI, "URI is not an nsIMozIconURI");
--- a/image/decoders/icon/gtk/nsIconChannel.cpp
+++ b/image/decoders/icon/gtk/nsIconChannel.cpp
@@ -28,16 +28,17 @@ extern "C" {
 #include <gtk/gtk.h>
 
 #include "nsMimeTypes.h"
 #include "nsIMIMEService.h"
 
 #include "nsIStringBundle.h"
 
 #include "nsNetUtil.h"
+#include "nsNullPrincipal.h"
 #include "nsIURL.h"
 #include "prlink.h"
 
 #include "nsIconChannel.h"
 
 NS_IMPL_ISUPPORTS(nsIconChannel,
                   nsIRequest,
                   nsIChannel)
@@ -132,19 +133,27 @@ moz_gdk_pixbuf_to_channel(GdkPixbuf* aPi
   // stream takes ownership of buf and will free it on destruction.
   // This function cannot fail.
   rv = stream->AdoptData((char*)buf, buf_size);
 
   // If this no longer holds then re-examine buf's lifetime.
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = NS_NewInputStreamChannel(aChannel, aURI, stream,
-                                NS_LITERAL_CSTRING(IMAGE_ICON_MS));
-  return rv;
+  nsCOMPtr<nsIPrincipal> nullPrincipal =
+    do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_NewInputStreamChannel(aChannel,
+                                  aURI,
+                                  stream,
+                                  nullPrincipal,
+                                  nsILoadInfo::SEC_NORMAL,
+                                  nsIContentPolicy::TYPE_OTHER,
+                                  NS_LITERAL_CSTRING(IMAGE_ICON_MS));
 }
 
 static GtkWidget *gProtoWindow = nullptr;
 static GtkWidget *gStockImageWidget = nullptr;
 #ifdef MOZ_ENABLE_GNOMEUI
 static GnomeIconTheme *gIconTheme = nullptr;
 #endif //MOZ_ENABLE_GNOMEUI
 
--- a/image/decoders/icon/qt/nsIconChannel.cpp
+++ b/image/decoders/icon/qt/nsIconChannel.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/Endian.h"
 
 #include "nsMimeTypes.h"
 #include "nsIMIMEService.h"
 
 #include "nsIStringBundle.h"
 
 #include "nsNetUtil.h"
+#include "nsNullPrincipal.h"
 #include "nsIURL.h"
 
 #include "nsIconChannel.h"
 #include "nsGtkQtIconsConverter.h"
 
 NS_IMPL_ISUPPORTS(nsIconChannel,
                   nsIRequest,
                   nsIChannel)
@@ -78,17 +79,26 @@ moz_qicon_to_channel(QImage *image, nsIU
   nsresult rv;
   nsCOMPtr<nsIStringInputStream> stream =
     do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = stream->AdoptData((char*)buf, buf_size);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return NS_NewInputStreamChannel(aChannel, aURI, stream,
+  nsCOMPtr<nsIPrincipal> nullPrincipal =
+    do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_NewInputStreamChannel(aChannel,
+                                  aURI,
+                                  stream,
+                                  nullPrincipal,
+                                  nsILoadInfo::SEC_NORMAL,
+                                  nsIContentPolicy::TYPE_OTHER,
                                   NS_LITERAL_CSTRING(IMAGE_ICON_MS));
 }
 
 nsresult
 nsIconChannel::Init(nsIURI* aURI)
 {
 
   nsCOMPtr<nsIMozIconURI> iconURI = do_QueryInterface(aURI);
--- a/ipc/chromium/src/base/message_loop.cc
+++ b/ipc/chromium/src/base/message_loop.cc
@@ -120,16 +120,19 @@ MessageLoop::MessageLoop(Type type)
   case TYPE_MOZILLA_NONMAINTHREAD:
     pump_ = new mozilla::ipc::MessagePumpForNonMainThreads();
     return;
 #if defined(OS_WIN)
   case TYPE_MOZILLA_NONMAINUITHREAD:
     pump_ = new mozilla::ipc::MessagePumpForNonMainUIThreads();
     return;
 #endif
+  default:
+    // Create one of Chromium's standard MessageLoop types below.
+    break;
   }
 
 #if defined(OS_WIN)
   // TODO(rvargas): Get rid of the OS guards.
   if (type_ == TYPE_DEFAULT) {
     pump_ = new base::MessagePumpDefault();
   } else if (type_ == TYPE_IO) {
     pump_ = new base::MessagePumpForIO();
--- a/js/ipc/JavaScriptBase.h
+++ b/js/ipc/JavaScriptBase.h
@@ -97,18 +97,18 @@ class JavaScriptBase : public WrapperOwn
     bool RecvClassName(const uint64_t &objId, nsString *result) {
         return Answer::RecvClassName(ObjectId::deserialize(objId), result);
     }
     bool RecvRegExpToShared(const uint64_t &objId, ReturnStatus *rs, nsString *source, uint32_t *flags) {
         return Answer::RecvRegExpToShared(ObjectId::deserialize(objId), rs, source, flags);
     }
 
     bool RecvGetPropertyKeys(const uint64_t &objId, const uint32_t &flags,
-                             ReturnStatus *rs, nsTArray<nsString> *names) {
-        return Answer::RecvGetPropertyKeys(ObjectId::deserialize(objId), flags, rs, names);
+                             ReturnStatus *rs, nsTArray<JSIDVariant> *ids) {
+        return Answer::RecvGetPropertyKeys(ObjectId::deserialize(objId), flags, rs, ids);
     }
     bool RecvInstanceOf(const uint64_t &objId, const JSIID &iid,
                           ReturnStatus *rs, bool *instanceof) {
         return Answer::RecvInstanceOf(ObjectId::deserialize(objId), iid, rs, instanceof);
     }
     bool RecvDOMInstanceOf(const uint64_t &objId, const int &prototypeID, const int &depth,
                              ReturnStatus *rs, bool *instanceof) {
         return Answer::RecvDOMInstanceOf(ObjectId::deserialize(objId), prototypeID, depth, rs, instanceof);
@@ -195,18 +195,18 @@ class JavaScriptBase : public WrapperOwn
     }
 
     bool SendRegExpToShared(const ObjectId &objId, ReturnStatus *rs,
                             nsString *source, uint32_t *flags) {
         return Base::SendRegExpToShared(objId.serialize(), rs, source, flags);
     }
 
     bool SendGetPropertyKeys(const ObjectId &objId, const uint32_t &flags,
-                             ReturnStatus *rs, nsTArray<nsString> *names) {
-        return Base::SendGetPropertyKeys(objId.serialize(), flags, rs, names);
+                             ReturnStatus *rs, nsTArray<JSIDVariant> *ids) {
+        return Base::SendGetPropertyKeys(objId.serialize(), flags, rs, ids);
     }
     bool SendInstanceOf(const ObjectId &objId, const JSIID &iid,
                         ReturnStatus *rs, bool *instanceof) {
         return Base::SendInstanceOf(objId.serialize(), iid, rs, instanceof);
     }
     bool SendDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth,
                            ReturnStatus *rs, bool *instanceof) {
         return Base::SendDOMInstanceOf(objId.serialize(), prototypeID, depth, rs, instanceof);
--- a/js/ipc/JavaScriptShared.cpp
+++ b/js/ipc/JavaScriptShared.cpp
@@ -435,17 +435,18 @@ JavaScriptShared::toSymbolVariant(JSCont
     }
     if (code == SymbolCode::InSymbolRegistry) {
         nsAutoJSString autoStr;
         if (!autoStr.init(cx, GetSymbolDescription(sym)))
             return false;
         *symVarp = RegisteredSymbol(autoStr);
         return true;
     }
-    MOZ_CRASH("unique symbols not yet implemented");
+
+    JS_ReportError(cx, "unique symbol can't be used with CPOW");
     return false;
 }
 
 JS::Symbol *
 JavaScriptShared::fromSymbolVariant(JSContext *cx, SymbolVariant symVar)
 {
     switch (symVar.type()) {
       case SymbolVariant::TWellKnownSymbol: {
--- a/js/ipc/PJavaScript.ipdl
+++ b/js/ipc/PJavaScript.ipdl
@@ -37,17 +37,17 @@ both:
 
     prio(high) sync IsExtensible(uint64_t objId) returns (ReturnStatus rs, bool result);
     prio(high) sync CallOrConstruct(uint64_t objId, JSParam[] argv, bool construct) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
     prio(high) sync HasInstance(uint64_t objId, JSVariant v) returns (ReturnStatus rs, bool has);
     prio(high) sync ObjectClassIs(uint64_t objId, uint32_t classValue) returns (bool result);
     prio(high) sync ClassName(uint64_t objId) returns (nsString name);
     prio(high) sync RegExpToShared(uint64_t objId) returns (ReturnStatus rs, nsString source, uint32_t flags);
 
-    prio(high) sync GetPropertyKeys(uint64_t objId, uint32_t flags) returns (ReturnStatus rs, nsString[] names);
+    prio(high) sync GetPropertyKeys(uint64_t objId, uint32_t flags) returns (ReturnStatus rs, JSIDVariant[] ids);
     prio(high) sync InstanceOf(uint64_t objId, JSIID iid) returns (ReturnStatus rs, bool instanceof);
     prio(high) sync DOMInstanceOf(uint64_t objId, int prototypeID, int depth) returns (ReturnStatus rs, bool instanceof);
 
     prio(high) sync IsCallable(uint64_t objId) returns (bool result);
     prio(high) sync IsConstructor(uint64_t objId) returns (bool result);
 
 parent:
     async __delete__();
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -575,17 +575,17 @@ WrapperAnswer::RecvRegExpToShared(const 
 
     *flags = JS_GetRegExpFlags(cx, obj);
 
     return ok(rs);
 }
 
 bool
 WrapperAnswer::RecvGetPropertyKeys(const ObjectId &objId, const uint32_t &flags,
-                                   ReturnStatus *rs, nsTArray<nsString> *names)
+                                   ReturnStatus *rs, nsTArray<JSIDVariant> *ids)
 {
     AutoSafeJSContext cx;
     JSAutoRequest request(cx);
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(cx, rs);
 
@@ -593,21 +593,21 @@ WrapperAnswer::RecvGetPropertyKeys(const
 
     LOG("%s.getPropertyKeys()", ReceiverObj(objId));
 
     AutoIdVector props(cx);
     if (!js::GetPropertyKeys(cx, obj, flags, &props))
         return fail(cx, rs);
 
     for (size_t i = 0; i < props.length(); i++) {
-        nsString name;
-        if (!convertIdToGeckoString(cx, props[i], &name))
+        JSIDVariant id;
+        if (!toJSIDVariant(cx, props[i], &id))
             return fail(cx, rs);
 
-        names->AppendElement(name);
+        ids->AppendElement(id);
     }
 
     return ok(rs);
 }
 
 bool
 WrapperAnswer::RecvInstanceOf(const ObjectId &objId, const JSIID &iid, ReturnStatus *rs,
                               bool *instanceof)
--- a/js/ipc/WrapperAnswer.h
+++ b/js/ipc/WrapperAnswer.h
@@ -50,17 +50,17 @@ class WrapperAnswer : public virtual Jav
                              nsTArray<JSParam> *outparams);
     bool RecvHasInstance(const ObjectId &objId, const JSVariant &v, ReturnStatus *rs, bool *bp);
     bool RecvObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
                            bool *result);
     bool RecvClassName(const ObjectId &objId, nsString *result);
     bool RecvRegExpToShared(const ObjectId &objId, ReturnStatus *rs, nsString *source, uint32_t *flags);
 
     bool RecvGetPropertyKeys(const ObjectId &objId, const uint32_t &flags,
-                             ReturnStatus *rs, nsTArray<nsString> *names);
+                             ReturnStatus *rs, nsTArray<JSIDVariant> *ids);
     bool RecvInstanceOf(const ObjectId &objId, const JSIID &iid,
                         ReturnStatus *rs, bool *instanceof);
     bool RecvDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth,
                            ReturnStatus *rs, bool *instanceof);
 
     bool RecvIsCallable(const ObjectId &objId, bool *result);
     bool RecvIsConstructor(const ObjectId &objId, bool *result);
 
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -58,39 +58,39 @@ class CPOWProxyHandler : public BaseProx
   public:
     MOZ_CONSTEXPR CPOWProxyHandler()
       : BaseProxyHandler(&family) {}
 
     virtual bool finalizeInBackground(Value priv) const MOZ_OVERRIDE {
         return false;
     }
 
-    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
-    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                       MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE;
-
+    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
+    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
-    virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
                      HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
                      JS::HandleId id, bool strict, JS::MutableHandleValue vp) const MOZ_OVERRIDE;
-    virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE;
-
-    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
+
+    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
+                                       MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+    virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
+    virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
+                                              AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool hasInstance(JSContext *cx, HandleObject proxy,
                              MutableHandleValue v, bool *bp) const MOZ_OVERRIDE;
     virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue,
                                JSContext *cx) const MOZ_OVERRIDE;
     virtual const char* className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE;
     virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE;
     virtual void objectMoved(JSObject *proxy, const JSObject *old) const MOZ_OVERRIDE;
@@ -227,17 +227,17 @@ CPOWProxyHandler::ownPropertyKeys(JSCont
                                   AutoIdVector &props) const
 {
     FORWARD(ownPropertyKeys, (cx, proxy, props));
 }
 
 bool
 WrapperOwner::ownPropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
-    return getPropertyKeys(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN, props);
+    return getPropertyKeys(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
 }
 
 bool
 CPOWProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const
 {
     FORWARD(delete_, (cx, proxy, id, bp));
 }
 
@@ -458,23 +458,24 @@ WrapperOwner::set(JSContext *cx, JS::Han
 
     if (!ok(cx, status))
         return false;
 
     return fromVariant(cx, result, vp);
 }
 
 bool
-CPOWProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const
+CPOWProxyHandler::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
+                                               AutoIdVector &props) const
 {
-    FORWARD(keys, (cx, proxy, props));
+    FORWARD(getOwnEnumerablePropertyKeys, (cx, proxy, props));
 }
 
 bool
-WrapperOwner::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
+WrapperOwner::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
     return getPropertyKeys(cx, proxy, JSITER_OWNONLY, props);
 }
 
 bool
 CPOWProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const
 {
     FORWARD(isExtensible, (cx, proxy, extensible));
@@ -771,30 +772,30 @@ WrapperOwner::init()
 }
 
 bool
 WrapperOwner::getPropertyKeys(JSContext *cx, HandleObject proxy, uint32_t flags, AutoIdVector &props)
 {
     ObjectId objId = idOf(proxy);
 
     ReturnStatus status;
-    InfallibleTArray<nsString> names;
-    if (!SendGetPropertyKeys(objId, flags, &status, &names))
+    InfallibleTArray<JSIDVariant> ids;
+    if (!SendGetPropertyKeys(objId, flags, &status, &ids))
         return ipcfail(cx);
 
     LOG_STACK();
 
     if (!ok(cx, status))
         return false;
 
-    for (size_t i = 0; i < names.Length(); i++) {
-        RootedId name(cx);
-        if (!convertGeckoStringToId(cx, names[i], &name))
+    for (size_t i = 0; i < ids.Length(); i++) {
+        RootedId id(cx);
+        if (!fromJSIDVariant(cx, ids[i], &id))
             return false;
-        if (!props.append(name))
+        if (!props.append(id))
             return false;
     }
 
     return true;
 }
 
 namespace mozilla {
 namespace jsipc {
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -26,47 +26,46 @@ class WrapperOwner : public virtual Java
   public:
     typedef mozilla::ipc::IProtocolManager<
                        mozilla::ipc::IProtocol>::ActorDestroyReason
            ActorDestroyReason;
 
     explicit WrapperOwner(JSRuntime *rt);
     bool init();
 
-    // Fundamental proxy traps. These are required.
+    // Standard internal methods.
     // (The traps should be in the same order like js/src/jsproxy.h)
-    bool preventExtensions(JSContext *cx, JS::HandleObject proxy);
-    bool getPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
-                               JS::MutableHandle<JSPropertyDescriptor> desc);
     bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                                   JS::MutableHandle<JSPropertyDescriptor> desc);
     bool defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                         JS::MutableHandle<JSPropertyDescriptor> desc);
     bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props);
     bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
     bool enumerate(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props);
-
-    // Derived proxy traps. Implementing these is useful for perfomance.
+    bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible);
+    bool preventExtensions(JSContext *cx, JS::HandleObject proxy);
     bool has(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
-    bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
     bool get(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
              JS::HandleId id, JS::MutableHandleValue vp);
     bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
              JS::HandleId id, bool strict, JS::MutableHandleValue vp);
-    bool keys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props);
-    // We use "iterate" provided by the base class here.
-
-    // SpiderMonkey Extensions.
-    bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible);
-    bool regexp_toShared(JSContext *cx, JS::HandleObject proxy, js::RegExpGuard *g);
     bool callOrConstruct(JSContext *cx, JS::HandleObject proxy, const JS::CallArgs &args,
                          bool construct);
+
+    // SpiderMonkey extensions.
+    bool getPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
+                               JS::MutableHandle<JSPropertyDescriptor> desc);
+    bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
+    bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::HandleObject proxy,
+                                      JS::AutoIdVector &props);
+    // We use "iterate" provided by the base class here.
     bool hasInstance(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool *bp);
     bool objectClassIs(JSContext *cx, JS::HandleObject obj, js::ESClassValue classValue);
     const char* className(JSContext *cx, JS::HandleObject proxy);
+    bool regexp_toShared(JSContext *cx, JS::HandleObject proxy, js::RegExpGuard *g);
     bool isCallable(JSObject *obj);
     bool isConstructor(JSObject *obj);
 
     nsresult instanceOf(JSObject *obj, const nsID *id, bool *bp);
 
     bool toString(JSContext *cx, JS::HandleObject callee, JS::CallArgs &args);
 
     /*
@@ -141,17 +140,17 @@ class WrapperOwner : public virtual Java
                                  ReturnStatus *rs, bool *bp) = 0;
     virtual bool SendObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
                                    bool *result) = 0;
     virtual bool SendClassName(const ObjectId &objId, nsString *result) = 0;
     virtual bool SendRegExpToShared(const ObjectId &objId, ReturnStatus *rs, nsString *source,
                                     uint32_t *flags) = 0;
 
     virtual bool SendGetPropertyKeys(const ObjectId &objId, const uint32_t &flags,
-                                     ReturnStatus *rs, nsTArray<nsString> *names) = 0;
+                                     ReturnStatus *rs, nsTArray<JSIDVariant> *ids) = 0;
     virtual bool SendInstanceOf(const ObjectId &objId, const JSIID &iid,
                                 ReturnStatus *rs, bool *instanceof) = 0;
     virtual bool SendDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth,
                                    ReturnStatus *rs, bool *instanceof) = 0;
 
     virtual bool SendIsCallable(const ObjectId &objId, bool *result) = 0;
     virtual bool SendIsConstructor(const ObjectId &objId, bool *result) = 0;
 };
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -310,16 +310,17 @@ export:: selfhosting
 selfhosting:: selfhosted.out.h
 
 selfhosting_srcs := \
   $(srcdir)/builtin/Utilities.js \
   $(srcdir)/builtin/ParallelUtilities.js \
   $(srcdir)/builtin/Array.js \
   $(srcdir)/builtin/Date.js \
   $(srcdir)/builtin/Error.js \
+  $(srcdir)/builtin/Generator.js \
   $(srcdir)/builtin/Intl.js \
   $(srcdir)/builtin/IntlData.js \
   $(srcdir)/builtin/Iterator.js \
   $(srcdir)/builtin/Map.js \
   $(srcdir)/builtin/Number.js \
   $(srcdir)/builtin/Object.js \
   $(srcdir)/builtin/String.js \
   $(srcdir)/builtin/Set.js \
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/Generator.js
@@ -0,0 +1,104 @@
+/* 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 StarGeneratorNext(val) {
+    if (!IsObject(this) || !IsStarGeneratorObject(this))
+        return callFunction(CallStarGeneratorMethodIfWrapped, this, val, "StarGeneratorNext");
+
+    if (StarGeneratorObjectIsClosed(this))
+        return { value: undefined, done: true };
+
+    if (GeneratorIsRunning(this))
+        ThrowError(JSMSG_NESTING_GENERATOR);
+
+    try {
+        return resumeGenerator(this, val, 'next');
+    } catch (e) {
+        if (!StarGeneratorObjectIsClosed(this))
+            GeneratorSetClosed(this);
+        throw e;
+    }
+}
+
+function StarGeneratorThrow(val) {
+    if (!IsObject(this) || !IsStarGeneratorObject(this))
+        return callFunction(CallStarGeneratorMethodIfWrapped, this, val, "StarGeneratorThrow");
+
+    if (StarGeneratorObjectIsClosed(this))
+        throw val;
+
+    if (GeneratorIsRunning(this))
+        ThrowError(JSMSG_NESTING_GENERATOR);
+
+    try {
+        return resumeGenerator(this, val, 'throw');
+    } catch (e) {
+        if (!StarGeneratorObjectIsClosed(this))
+            GeneratorSetClosed(this);
+        throw e;
+    }
+}
+
+function LegacyGeneratorNext(val) {
+    if (!IsObject(this) || !IsLegacyGeneratorObject(this))
+        return callFunction(CallLegacyGeneratorMethodIfWrapped, this, val, "LegacyGeneratorNext");
+
+    if (LegacyGeneratorObjectIsClosed(this))
+        ThrowStopIteration();
+
+    if (GeneratorIsRunning(this))
+        ThrowError(JSMSG_NESTING_GENERATOR);
+
+    try {
+        return resumeGenerator(this, val, 'next');
+    } catch(e) {
+        if (!LegacyGeneratorObjectIsClosed(this))
+            GeneratorSetClosed(this);
+        throw e;
+    }
+}
+
+function LegacyGeneratorThrow(val) {
+    if (!IsObject(this) || !IsLegacyGeneratorObject(this))
+        return callFunction(CallLegacyGeneratorMethodIfWrapped, this, val, "LegacyGeneratorThrow");
+
+    if (LegacyGeneratorObjectIsClosed(this))
+        throw val;
+
+    if (GeneratorIsRunning(this))
+        ThrowError(JSMSG_NESTING_GENERATOR);
+
+    try {
+        return resumeGenerator(this, val, 'throw');
+    } catch(e) {
+        if (!LegacyGeneratorObjectIsClosed(this))
+            GeneratorSetClosed(this);
+        throw e;
+    }
+}
+
+// Called by js::CloseIterator.
+function LegacyGeneratorCloseInternal() {
+    assert(IsObject(this), "Not an object: " + ToString(this));
+    assert(IsLegacyGeneratorObject(this), "Not a legacy generator object: " + ToString(this));
+    assert(!LegacyGeneratorObjectIsClosed(this), "Already closed: " + ToString(this));
+    assert(!CloseNewbornLegacyGeneratorObject(this), "Newborn: " + ToString(this));
+
+    if (GeneratorIsRunning(this))
+        ThrowError(JSMSG_NESTING_GENERATOR);
+
+    resumeGenerator(this, undefined, 'close');
+    if (!LegacyGeneratorObjectIsClosed(this))
+        CloseClosingLegacyGeneratorObject(this);
+}
+
+function LegacyGeneratorClose() {
+    if (!IsObject(this) || !IsLegacyGeneratorObject(this))
+        return callFunction(CallLegacyGeneratorMethodIfWrapped, this, "LegacyGeneratorClose");
+
+    if (LegacyGeneratorObjectIsClosed(this) || CloseNewbornLegacyGeneratorObject(this))
+        return;
+
+    callFunction(LegacyGeneratorCloseInternal, this);
+}
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1289,25 +1289,25 @@ if test "$GNU_CXX"; then
     # -Wtrigraphs - catches unlikely use of trigraphs
     # -Wtype-limits - catches overflow bugs, few false positives
     # -Wunknown-pragmas - catches unexpected #pragma directives
     # -Wunused-label - catches unused goto labels
     # -Wunused-value - catches unused expression results
     # -Wwrite-strings - catches non-const char* pointers to string literals
     #
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall"
-    _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wignored-qualifiers"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wsign-compare"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wtype-limits"
 
     # Treat some warnings as errors:
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=char-subscripts"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=comment"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=empty-body"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=endif-labels"
+    _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=ignored-qualifiers"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=int-to-pointer-cast"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=missing-braces"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=overloaded-virtual"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=pointer-arith"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=reorder"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=return-type"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=sequence-point"
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=switch"
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -2620,35 +2620,37 @@ ImplicitConvert(JSContext* cx,
       return TypeError(cx, "array", val);
     }
     break;
   }
   case TYPE_struct: {
     if (val.isObject() && !sourceData) {
       // Enumerate the properties of the object; if they match the struct
       // specification, convert the fields.
-      RootedObject iter(cx, JS_NewPropertyIterator(cx, valObj));
-      if (!iter)
+      AutoIdArray props(cx, JS_Enumerate(cx, valObj));
+      if (!props)
         return false;
 
       // Convert into an intermediate, in case of failure.
       size_t structSize = CType::GetSize(targetType);
       AutoPtr<char> intermediate(cx->pod_malloc<char>(structSize));
       if (!intermediate) {
         JS_ReportAllocationOverflow(cx);
         return false;
       }
 
+      const FieldInfoHash* fields = StructType::GetFieldInfo(targetType);
+      if (props.length() != fields->count()) {
+        JS_ReportError(cx, "missing fields");
+        return false;
+      }
+
       RootedId id(cx);
-      size_t i = 0;
-      while (1) {
-        if (!JS_NextProperty(cx, iter, &id))
-          return false;
-        if (JSID_IS_VOID(id))
-          break;
+      for (size_t i = 0; i < props.length(); ++i) {
+        id = props[i];
 
         if (!JSID_IS_STRING(id)) {
           JS_ReportError(cx, "property name is not a string");
           return false;
         }
 
         JSFlatString *name = JSID_TO_FLAT_STRING(id);
         const FieldInfo* field = StructType::LookupField(cx, targetType, name);
@@ -2658,24 +2660,16 @@ ImplicitConvert(JSContext* cx,
         RootedValue prop(cx);
         if (!JS_GetPropertyById(cx, valObj, id, &prop))
           return false;
 
         // Convert the field via ImplicitConvert().
         char* fieldData = intermediate.get() + field->mOffset;
         if (!ImplicitConvert(cx, prop, field->mType, fieldData, false, nullptr))
           return false;
-
-        ++i;
-      }
-
-      const FieldInfoHash* fields = StructType::GetFieldInfo(targetType);
-      if (i != fields->count()) {
-        JS_ReportError(cx, "missing fields");
-        return false;
       }
 
       memcpy(buffer, intermediate.get(), structSize);
       break;
     }
 
     return TypeError(cx, "struct", val);
   }
@@ -4700,43 +4694,33 @@ ArrayType::AddressOfElement(JSContext* c
 static JSFlatString*
 ExtractStructField(JSContext* cx, jsval val, MutableHandleObject typeObj)
 {
   if (val.isPrimitive()) {
     JS_ReportError(cx, "struct field descriptors require a valid name and type");
     return nullptr;
   }
 
-  RootedObject obj(cx, val.toObjectOrNull());
-  RootedObject iter(cx, JS_NewPropertyIterator(cx, obj));
-  if (!iter)
+  RootedObject obj(cx, &val.toObject());
+  AutoIdArray props(cx, JS_Enumerate(cx, obj));
+  if (!props)
     return nullptr;
 
-  RootedId nameid(cx);
-  if (!JS_NextProperty(cx, iter, &nameid))
+  // make sure we have one, and only one, property
+  if (props.length() != 1) {
+    JS_ReportError(cx, "struct field descriptors must contain one property");
     return nullptr;
-  if (JSID_IS_VOID(nameid)) {
-    JS_ReportError(cx, "struct field descriptors require a valid name and type");
-    return nullptr;
-  }
-
+  }
+
+  RootedId nameid(cx, props[0]);
   if (!JSID_IS_STRING(nameid)) {
     JS_ReportError(cx, "struct field descriptors require a valid name and type");
     return nullptr;
   }
 
-  // make sure we have one, and only one, property
-  RootedId id(cx);
-  if (!JS_NextProperty(cx, iter, &id))
-    return nullptr;
-  if (!JSID_IS_VOID(id)) {
-    JS_ReportError(cx, "struct field descriptors must contain one property");
-    return nullptr;
-  }
-
   RootedValue propVal(cx);
   if (!JS_GetPropertyById(cx, obj, nameid, &propVal))
     return nullptr;
 
   if (propVal.isPrimitive() || !CType::IsCType(&propVal.toObject())) {
     JS_ReportError(cx, "struct field descriptors require a valid name and type");
     return nullptr;
   }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -26,16 +26,17 @@
 #include "jsscript.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "asmjs/AsmJSLink.h"
 #include "frontend/Parser.h"
 #include "frontend/TokenStream.h"
 #include "vm/Debugger.h"
+#include "vm/GeneratorObject.h"
 #include "vm/Stack.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "frontend/ParseMaps-inl.h"
 #include "frontend/ParseNode-inl.h"
@@ -1984,20 +1985,20 @@ CheckSideEffects(ExclusiveContext *cx, B
             if (pn->isOp(JSOP_NOT)) {
                 /* ! does not convert its operand via toString or valueOf. */
                 return CheckSideEffects(cx, bce, pn->pn_kid, answer);
             }
             /* FALL THROUGH */
 
           default:
             /*
-             * All of PNK_INC, PNK_DEC, PNK_THROW, PNK_YIELD, and PNK_YIELD_STAR
-             * have direct effects. Of the remaining unary-arity node types, we
-             * can't easily prove that the operand never denotes an object with
-             * a toString or valueOf method.
+             * All of PNK_INC, PNK_DEC and PNK_THROW have direct effects. Of
+             * the remaining unary-arity node types, we can't easily prove that
+             * the operand never denotes an object with a toString or valueOf
+             * method.
              */
             *answer = true;
             return true;
         }
         MOZ_CRASH("We have a returning default case");
 
       case PN_NAME:
         /*
@@ -2968,51 +2969,53 @@ frontend::EmitFunctionScript(ExclusiveCo
             if (!EmitUnaliasedVarOp(cx, JSOP_SETLOCAL, varIndex, DontCheckLexical, bce))
                 return false;
         }
         if (Emit1(cx, bce, JSOP_POP) < 0)
             return false;
         bce->switchToMain();
     }
 
-    if (funbox->isGenerator()) {
-        bce->switchToProlog();
-        if (Emit1(cx, bce, JSOP_GENERATOR) < 0)
-            return false;
-        bce->switchToMain();
-    }
-
     /*
      * Emit a prologue for run-once scripts which will deoptimize JIT code if
      * the script ends up running multiple times via foo.caller related
      * shenanigans.
      */
     bool runOnce = bce->isRunOnceLambda();
     if (runOnce) {
         bce->switchToProlog();
         if (Emit1(cx, bce, JSOP_RUNONCE) < 0)
             return false;
         bce->switchToMain();
     }
 
     if (!EmitTree(cx, bce, body))
         return false;
 
-    // If we fall off the end of an ES6 generator, return a boxed iterator
-    // result object of the form { value: undefined, done: true }.
-    if (bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->isStarGenerator()) {
-        if (!EmitPrepareIteratorResult(cx, bce))
-            return false;
+    // If we fall off the end of a generator, do a final yield.
+    if (bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->isGenerator()) {
+        if (bce->sc->asFunctionBox()->isStarGenerator() && !EmitPrepareIteratorResult(cx, bce))
+            return false;
+
         if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
             return false;
-        if (!EmitFinishIteratorResult(cx, bce, true))
+
+        if (bce->sc->asFunctionBox()->isStarGenerator() && !EmitFinishIteratorResult(cx, bce, true))
+            return false;
+
+        ScopeCoordinate sc;
+        // We know that .generator is on the top scope chain node, as we are
+        // at the function end.
+        sc.setHops(0);
+        MOZ_ALWAYS_TRUE(LookupAliasedNameSlot(bce, bce->script, cx->names().dotGenerator, &sc));
+        if (!EmitAliasedVarOp(cx, JSOP_GETALIASEDVAR, sc, DontCheckLexical, bce))
             return false;
 
         // No need to check for finally blocks, etc as in EmitReturn.
-        if (Emit1(cx, bce, JSOP_RETURN) < 0)
+        if (Emit1(cx, bce, JSOP_FINALYIELD) < 0)
             return false;
     }
 
     /*
      * Always end the script with a JSOP_RETRVAL. Some other parts of the codebase
      * depend on this opcode, e.g. js_InternalInterpret.
      */
     if (Emit1(cx, bce, JSOP_RETRVAL) < 0)
@@ -5458,35 +5461,80 @@ EmitReturn(ExclusiveContext *cx, Bytecod
      * with the correct stack pointer (i.e., after popping any with,
      * for/in, etc., slots nested inside the finally's try).
      *
      * In this case we mutate JSOP_RETURN into JSOP_SETRVAL and add an
      * extra JSOP_RETRVAL after the fixups.
      */
     ptrdiff_t top = bce->offset();
 
-    if (Emit1(cx, bce, JSOP_RETURN) < 0)
-        return false;
+    bool isGenerator = bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->isGenerator();
+    if (Emit1(cx, bce, isGenerator ? JSOP_SETRVAL : JSOP_RETURN) < 0)
+         return false;
 
     NonLocalExitScope nle(cx, bce);
 
     if (!nle.prepareForNonLocalJump(nullptr))
         return false;
 
-    if (top + static_cast<ptrdiff_t>(JSOP_RETURN_LENGTH) != bce->offset()) {
+    if (isGenerator) {
+        ScopeCoordinate sc;
+        // We know that .generator is on the top scope chain node, as we just
+        // exited nested scopes.
+        sc.setHops(0);
+        MOZ_ALWAYS_TRUE(LookupAliasedNameSlot(bce, bce->script, cx->names().dotGenerator, &sc));
+        if (!EmitAliasedVarOp(cx, JSOP_GETALIASEDVAR, sc, DontCheckLexical, bce))
+            return false;
+        if (Emit1(cx, bce, JSOP_FINALYIELDRVAL) < 0)
+            return false;
+    } else if (top + static_cast<ptrdiff_t>(JSOP_RETURN_LENGTH) != bce->offset()) {
         bce->code()[top] = JSOP_SETRVAL;
         if (Emit1(cx, bce, JSOP_RETRVAL) < 0)
             return false;
     }
 
     return true;
 }
 
 static bool
-EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter)
+EmitYield(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+{
+    MOZ_ASSERT(bce->sc->isFunctionBox());
+
+    if (pn->getOp() == JSOP_YIELD) {
+        if (bce->sc->asFunctionBox()->isStarGenerator()) {
+            if (!EmitPrepareIteratorResult(cx, bce))
+                return false;
+        }
+        if (pn->pn_left) {
+            if (!EmitTree(cx, bce, pn->pn_left))
+                return false;
+        } else {
+            if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
+                return false;
+        }
+        if (bce->sc->asFunctionBox()->isStarGenerator()) {
+            if (!EmitFinishIteratorResult(cx, bce, false))
+                return false;
+        }
+    } else {
+        MOZ_ASSERT(pn->getOp() == JSOP_INITIALYIELD);
+    }
+
+    if (!EmitTree(cx, bce, pn->pn_right))
+        return false;
+
+    if (Emit1(cx, bce, pn->getOp()) < 0)
+        return false;
+
+    return true;
+}
+
+static bool
+EmitYieldStar(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *iter, ParseNode *gen)
 {
     MOZ_ASSERT(bce->sc->isFunctionBox());
     MOZ_ASSERT(bce->sc->asFunctionBox()->isStarGenerator());
 
     if (!EmitTree(cx, bce, iter))                                // ITERABLE
         return false;
 
     // Convert iterable to iterator.
@@ -5495,50 +5543,57 @@ EmitYieldStar(ExclusiveContext *cx, Byte
     if (!EmitAtomOp(cx, cx->names().std_iterator, JSOP_CALLPROP, bce)) // ITERABLE @@ITERATOR
         return false;
     if (Emit1(cx, bce, JSOP_SWAP) < 0)                           // @@ITERATOR ITERABLE
         return false;
     if (EmitCall(cx, bce, JSOP_CALL, 0, iter) < 0)               // ITER
         return false;
     CheckTypeSet(cx, bce, JSOP_CALL);
 
-    int depth = bce->stackDepth;
-    MOZ_ASSERT(depth >= 1);
-
     // Initial send value is undefined.
     if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)                      // ITER RECEIVED
         return false;
+
+    int depth = bce->stackDepth;
+    MOZ_ASSERT(depth >= 2);
+
     ptrdiff_t initialSend = -1;
     if (EmitBackPatchOp(cx, bce, &initialSend) < 0)              // goto initialSend
         return false;
 
     // Try prologue.                                             // ITER RESULT
     StmtInfoBCE stmtInfo(cx);
     PushStatementBCE(bce, &stmtInfo, STMT_TRY, bce->offset());
     ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_TRY);
+    ptrdiff_t tryStart = bce->offset();                          // tryStart:
     if (noteIndex < 0 || Emit1(cx, bce, JSOP_TRY) < 0)
         return false;
-    ptrdiff_t tryStart = bce->offset();                          // tryStart:
-    MOZ_ASSERT(bce->stackDepth == depth + 1);
+    MOZ_ASSERT(bce->stackDepth == depth);
+
+    // Load the generator object.
+    if (!EmitTree(cx, bce, gen))                                 // ITER RESULT GENOBJ
+        return false;
 
     // Yield RESULT as-is, without re-boxing.
     if (Emit1(cx, bce, JSOP_YIELD) < 0)                          // ITER RECEIVED
         return false;
 
     // Try epilogue.
-    if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, bce->offset() - tryStart + JSOP_TRY_LENGTH))
+    if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, bce->offset() - tryStart))
         return false;
     ptrdiff_t subsequentSend = -1;
     if (EmitBackPatchOp(cx, bce, &subsequentSend) < 0)           // goto subsequentSend
         return false;
     ptrdiff_t tryEnd = bce->offset();                            // tryEnd:
 
     // Catch location.
-    // THROW? = 'throw' in ITER                                  // ITER
-    bce->stackDepth = (uint32_t) depth;
+    bce->stackDepth = uint32_t(depth);                           // ITER RESULT
+    if (Emit1(cx, bce, JSOP_POP) < 0)                            // ITER
+        return false;
+    // THROW? = 'throw' in ITER
     if (Emit1(cx, bce, JSOP_EXCEPTION) < 0)                      // ITER EXCEPTION
         return false;
     if (Emit1(cx, bce, JSOP_SWAP) < 0)                           // EXCEPTION ITER
         return false;
     if (Emit1(cx, bce, JSOP_DUP) < 0)                            // EXCEPTION ITER ITER
         return false;
     if (!EmitAtomOp(cx, cx->names().throw_, JSOP_STRING, bce))   // EXCEPTION ITER ITER "throw"
         return false;
@@ -5552,42 +5607,42 @@ EmitYieldStar(ExclusiveContext *cx, Byte
         return false;
     if (Emit1(cx, bce, JSOP_POP) < 0)                            // EXCEPTION
         return false;
     if (Emit1(cx, bce, JSOP_THROW) < 0)                          // throw EXCEPTION
         return false;
 
     SetJumpOffsetAt(bce, checkThrow);                            // delegate:
     // RESULT = ITER.throw(EXCEPTION)                            // EXCEPTION ITER
-    bce->stackDepth = (uint32_t) depth + 1;
+    bce->stackDepth = uint32_t(depth);
     if (Emit1(cx, bce, JSOP_DUP) < 0)                            // EXCEPTION ITER ITER
         return false;
     if (Emit1(cx, bce, JSOP_DUP) < 0)                            // EXCEPTION ITER ITER ITER
         return false;
     if (!EmitAtomOp(cx, cx->names().throw_, JSOP_CALLPROP, bce)) // EXCEPTION ITER ITER THROW
         return false;
     if (Emit1(cx, bce, JSOP_SWAP) < 0)                           // EXCEPTION ITER THROW ITER
         return false;
     if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0)            // ITER THROW ITER EXCEPTION
         return false;
     if (EmitCall(cx, bce, JSOP_CALL, 1, iter) < 0)               // ITER RESULT
         return false;
     CheckTypeSet(cx, bce, JSOP_CALL);
-    MOZ_ASSERT(bce->stackDepth == depth + 1);
+    MOZ_ASSERT(bce->stackDepth == depth);
     ptrdiff_t checkResult = -1;
     if (EmitBackPatchOp(cx, bce, &checkResult) < 0)              // goto checkResult
         return false;
 
     // Catch epilogue.
     if (!PopStatementBCE(cx, bce))
         return false;
     // This is a peace offering to ReconstructPCStack.  See the note in EmitTry.
     if (Emit1(cx, bce, JSOP_NOP) < 0)
         return false;
-    if (!bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd))
+    if (!bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart + JSOP_TRY_LENGTH, tryEnd))
         return false;
 
     // After the try/catch block: send the received value to the iterator.
     if (!BackPatch(cx, bce, initialSend, bce->code().end(), JSOP_GOTO)) // initialSend:
         return false;
     if (!BackPatch(cx, bce, subsequentSend, bce->code().end(), JSOP_GOTO)) // subsequentSend:
         return false;
 
@@ -5603,17 +5658,17 @@ EmitYieldStar(ExclusiveContext *cx, Byte
         return false;
     if (Emit1(cx, bce, JSOP_SWAP) < 0)                           // RECEIVED ITER NEXT ITER
         return false;
     if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0)            // ITER NEXT ITER RECEIVED
         return false;
     if (EmitCall(cx, bce, JSOP_CALL, 1, iter) < 0)               // ITER RESULT
         return false;
     CheckTypeSet(cx, bce, JSOP_CALL);
-    MOZ_ASSERT(bce->stackDepth == depth + 1);
+    MOZ_ASSERT(bce->stackDepth == depth);
 
     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
         return false;
@@ -5624,17 +5679,17 @@ EmitYieldStar(ExclusiveContext *cx, Byte
     // result.value
     if (Emit1(cx, bce, JSOP_SWAP) < 0)                           // RESULT ITER
         return false;
     if (Emit1(cx, bce, JSOP_POP) < 0)                            // RESULT
         return false;
     if (!EmitAtomOp(cx, cx->names().value, JSOP_GETPROP, bce))   // VALUE
         return false;
 
-    MOZ_ASSERT(bce->stackDepth == depth);
+    MOZ_ASSERT(bce->stackDepth == depth - 1);
 
     return true;
 }
 
 static bool
 EmitStatementList(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
 {
     MOZ_ASSERT(pn->isArity(PN_LIST));
@@ -5803,16 +5858,90 @@ EmitDelete(ExclusiveContext *cx, Bytecod
 
     return true;
 }
 
 static bool
 EmitArray(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, uint32_t count);
 
 static bool
+EmitSelfHostedCallFunction(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+{
+    // Special-casing of callFunction to emit bytecode that directly
+    // invokes the callee with the correct |this| object and arguments.
+    // callFunction(fun, thisArg, arg0, arg1) thus becomes:
+    // - emit lookup for fun
+    // - emit lookup for thisArg
+    // - emit lookups for arg0, arg1
+    //
+    // argc is set to the amount of actually emitted args and the
+    // emitting of args below is disabled by setting emitArgs to false.
+    if (pn->pn_count < 3) {
+        bce->reportError(pn, JSMSG_MORE_ARGS_NEEDED, "callFunction", "1", "s");
+        return false;
+    }
+
+    ParseNode *pn2 = pn->pn_head;
+    ParseNode *funNode = pn2->pn_next;
+    if (!EmitTree(cx, bce, funNode))
+        return false;
+
+    ParseNode *thisArg = funNode->pn_next;
+    if (!EmitTree(cx, bce, thisArg))
+        return false;
+
+    bool oldEmittingForInit = bce->emittingForInit;
+    bce->emittingForInit = false;
+
+    for (ParseNode *argpn = thisArg->pn_next; argpn; argpn = argpn->pn_next) {
+        if (!EmitTree(cx, bce, argpn))
+            return false;
+    }
+
+    bce->emittingForInit = oldEmittingForInit;
+
+    uint32_t argc = pn->pn_count - 3;
+    if (EmitCall(cx, bce, pn->getOp(), argc) < 0)
+        return false;
+
+    CheckTypeSet(cx, bce, pn->getOp());
+    return true;
+}
+
+static bool
+EmitSelfHostedResumeGenerator(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+{
+    // Syntax: resumeGenerator(gen, value, 'next'|'throw'|'close')
+    if (pn->pn_count != 4) {
+        bce->reportError(pn, JSMSG_MORE_ARGS_NEEDED, "resumeGenerator", "1", "s");
+        return false;
+    }
+
+    ParseNode *funNode = pn->pn_head;  // The resumeGenerator node.
+
+    ParseNode *genNode = funNode->pn_next;
+    if (!EmitTree(cx, bce, genNode))
+        return false;
+
+    ParseNode *valNode = genNode->pn_next;
+    if (!EmitTree(cx, bce, valNode))
+        return false;
+
+    ParseNode *kindNode = valNode->pn_next;
+    MOZ_ASSERT(kindNode->isKind(PNK_STRING));
+    uint16_t operand = GeneratorObject::getResumeKind(cx, kindNode->pn_atom);
+    MOZ_ASSERT(!kindNode->pn_next);
+
+    if (EmitCall(cx, bce, JSOP_RESUME, operand) < 0)
+        return false;
+
+    return true;
+}
+
+static bool
 EmitCallOrNew(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     bool callop = pn->isKind(PNK_CALL) || pn->isKind(PNK_TAGGED_TEMPLATE);
     /*
      * Emit callable invocation or operator new (constructor call) code.
      * First, emit code for the left operand to evaluate the callable or
      * constructable object expression.
      *
@@ -5830,56 +5959,31 @@ EmitCallOrNew(ExclusiveContext *cx, Byte
 
     if (argc >= ARGC_LIMIT) {
         bce->parser->tokenStream.reportError(callop
                                              ? JSMSG_TOO_MANY_FUN_ARGS
                                              : JSMSG_TOO_MANY_CON_ARGS);
         return false;
     }
 
-    bool emitArgs = true;
     ParseNode *pn2 = pn->pn_head;
     bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
     switch (pn2->getKind()) {
       case PNK_NAME:
-        if (bce->emitterMode == BytecodeEmitter::SelfHosting &&
-            pn2->name() == cx->names().callFunction &&
-            !spread)
-        {
-            /*
-             * Special-casing of callFunction to emit bytecode that directly
-             * invokes the callee with the correct |this| object and arguments.
-             * callFunction(fun, thisArg, arg0, arg1) thus becomes:
-             * - emit lookup for fun
-             * - emit lookup for thisArg
-             * - emit lookups for arg0, arg1
-             *
-             * argc is set to the amount of actually emitted args and the
-             * emitting of args below is disabled by setting emitArgs to false.
-             */
-            if (pn->pn_count < 3) {
-                bce->reportError(pn, JSMSG_MORE_ARGS_NEEDED, "callFunction", "1", "s");
-                return false;
-            }
-            ParseNode *funNode = pn2->pn_next;
-            if (!EmitTree(cx, bce, funNode))
-                return false;
-            ParseNode *thisArg = funNode->pn_next;
-            if (!EmitTree(cx, bce, thisArg))
-                return false;
-            bool oldEmittingForInit = bce->emittingForInit;
-            bce->emittingForInit = false;
-            for (ParseNode *argpn = thisArg->pn_next; argpn; argpn = argpn->pn_next) {
-                if (!EmitTree(cx, bce, argpn))
-                    return false;
-            }
-            bce->emittingForInit = oldEmittingForInit;
-            argc -= 2;
-            emitArgs = false;
-            break;
+        if (bce->emitterMode == BytecodeEmitter::SelfHosting && !spread) {
+            // We shouldn't see foo(bar) = x in self-hosted code.
+            MOZ_ASSERT(!(pn->pn_xflags & PNX_SETCALL));
+
+            // Calls to "callFunction" or "resumeGenerator" in self-hosted code
+            // generate inline bytecode.
+            if (pn2->name() == cx->names().callFunction)
+                return EmitSelfHostedCallFunction(cx, bce, pn);
+            if (pn2->name() == cx->names().resumeGenerator)
+                return EmitSelfHostedResumeGenerator(cx, bce, pn);
+            // Fall through.
         }
         if (!EmitNameOp(cx, bce, pn2, callop))
             return false;
         break;
       case PNK_DOT:
         if (!EmitPropOp(cx, pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP, bce))
             return false;
         break;
@@ -5917,35 +6021,33 @@ EmitCallOrNew(ExclusiveContext *cx, Byte
         break;
     }
     if (!callop) {
         JSOp thisop = pn->isKind(PNK_GENEXP) ? JSOP_THIS : JSOP_UNDEFINED;
         if (Emit1(cx, bce, thisop) < 0)
             return false;
     }
 
-    if (emitArgs) {
-        /*
-         * Emit code for each argument in order, then emit the JSOP_*CALL or
-         * JSOP_NEW bytecode with a two-byte immediate telling how many args
-         * were pushed on the operand stack.
-         */
-        bool oldEmittingForInit = bce->emittingForInit;
-        bce->emittingForInit = false;
-        if (!spread) {
-            for (ParseNode *pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) {
-                if (!EmitTree(cx, bce, pn3))
-                    return false;
-            }
-        } else {
-            if (!EmitArray(cx, bce, pn2->pn_next, argc))
+    /*
+     * Emit code for each argument in order, then emit the JSOP_*CALL or
+     * JSOP_NEW bytecode with a two-byte immediate telling how many args
+     * were pushed on the operand stack.
+     */
+    bool oldEmittingForInit = bce->emittingForInit;
+    bce->emittingForInit = false;
+    if (!spread) {
+        for (ParseNode *pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) {
+            if (!EmitTree(cx, bce, pn3))
                 return false;
         }
-        bce->emittingForInit = oldEmittingForInit;
-    }
+    } else {
+        if (!EmitArray(cx, bce, pn2->pn_next, argc))
+            return false;
+    }
+    bce->emittingForInit = oldEmittingForInit;
 
     if (!spread) {
         if (EmitCall(cx, bce, pn->getOp(), argc, pn) < 0)
             return false;
     } else {
         if (Emit1(cx, bce, pn->getOp()) < 0)
             return false;
     }
@@ -6682,38 +6784,26 @@ frontend::EmitTree(ExclusiveContext *cx,
             return false;
         break;
 
       case PNK_RETURN:
         ok = EmitReturn(cx, bce, pn);
         break;
 
       case PNK_YIELD_STAR:
-        ok = EmitYieldStar(cx, bce, pn->pn_kid);
+        ok = EmitYieldStar(cx, bce, pn->pn_left, pn->pn_right);
+        break;
+
+      case PNK_GENERATOR:
+        if (Emit1(cx, bce, JSOP_GENERATOR) < 0)
+            return false;
         break;
 
       case PNK_YIELD:
-        MOZ_ASSERT(bce->sc->isFunctionBox());
-        if (bce->sc->asFunctionBox()->isStarGenerator()) {
-            if (!EmitPrepareIteratorResult(cx, bce))
-                return false;
-        }
-        if (pn->pn_kid) {
-            if (!EmitTree(cx, bce, pn->pn_kid))
-                return false;
-        } else {
-            if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
-                return false;
-        }
-        if (bce->sc->asFunctionBox()->isStarGenerator()) {
-            if (!EmitFinishIteratorResult(cx, bce, false))
-                return false;
-        }
-        if (Emit1(cx, bce, JSOP_YIELD) < 0)
-            return false;
+        ok = EmitYield(cx, bce, pn);
         break;
 
       case PNK_STATEMENTLIST:
         ok = EmitStatementList(cx, bce, pn, top);
         break;
 
       case PNK_SEQ:
         ok = EmitSyntheticStatements(cx, bce, pn, top);
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -318,16 +318,27 @@ class FullParseHandler
 
         ParseNode *propdef = newBinary(PNK_COLON, name, fn, op);
         if (!propdef)
             return false;
         literal->append(propdef);
         return true;
     }
 
+    ParseNode *newYieldExpression(uint32_t begin, ParseNode *value, ParseNode *gen,
+                                  JSOp op = JSOP_YIELD) {
+        TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
+        return new_<BinaryNode>(PNK_YIELD, op, pos, value, gen);
+    }
+
+    ParseNode *newYieldStarExpression(uint32_t begin, ParseNode *value, ParseNode *gen) {
+        TokenPos pos(begin, value->pn_pos.end);
+        return new_<BinaryNode>(PNK_YIELD_STAR, JSOP_NOP, pos, value, gen);
+    }
+
     // Statements
 
     ParseNode *newStatementList(unsigned blockid, const TokenPos &pos) {
         ParseNode *pn = new_<ListNode>(PNK_STATEMENTLIST, pos);
         if (pn)
             pn->pn_blockid = blockid;
         return pn;
     }
@@ -347,16 +358,41 @@ class FullParseHandler
                 MOZ_ASSERT_IF(pc->sc->isFunctionBox(),
                               pc->sc->asFunctionBox()->hasExtensibleScope());
             }
         }
 
         list->append(stmt);
     }
 
+    bool prependInitialYield(ParseNode *stmtList, ParseNode *genName) {
+        MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
+
+        TokenPos yieldPos(stmtList->pn_pos.begin, stmtList->pn_pos.begin + 1);
+        ParseNode *makeGen = new_<NullaryNode>(PNK_GENERATOR, yieldPos);
+        if (!makeGen)
+            return false;
+
+        MOZ_ASSERT(genName->getOp() == JSOP_NAME);
+        genName->setOp(JSOP_SETNAME);
+        genName->markAsAssigned();
+        ParseNode *genInit = newBinary(PNK_ASSIGN, genName, makeGen);
+
+        ParseNode *initialYield = newYieldExpression(yieldPos.begin, nullptr, genInit,
+                                                     JSOP_INITIALYIELD);
+        if (!initialYield)
+            return false;
+
+        initialYield->pn_next = stmtList->pn_head;
+        stmtList->pn_head = initialYield;
+        stmtList->pn_count++;
+
+        return true;
+    }
+
     ParseNode *newEmptyStatement(const TokenPos &pos) {
         return new_<UnaryNode>(PNK_SEMI, JSOP_NOP, pos, (ParseNode *) nullptr);
     }
 
     ParseNode *newImportDeclaration(ParseNode *importSpecSet,
                                     ParseNode *moduleSpec, const TokenPos &pos)
     {
         ParseNode *pn = new_<BinaryNode>(PNK_IMPORT, JSOP_NOP, pos,
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -119,16 +119,17 @@ class UpvarCookie
     F(NEW) \
     F(DELETE) \
     F(TRY) \
     F(CATCH) \
     F(CATCHLIST) \
     F(FINALLY) \
     F(THROW) \
     F(DEBUGGER) \
+    F(GENERATOR) \
     F(YIELD) \
     F(YIELD_STAR) \
     F(GENEXP) \
     F(ARRAYCOMP) \
     F(ARRAYPUSH) \
     F(LEXICALSCOPE) \
     F(LET) \
     F(IMPORT) \
@@ -417,16 +418,19 @@ enum ParseNodeKind
  * PNK_NUMBER   dval        pn_dval: double value of numeric literal
  * PNK_TRUE,    nullary     pn_op: JSOp bytecode
  * PNK_FALSE,
  * PNK_NULL,
  * PNK_THIS
  *
  * PNK_LEXICALSCOPE name    pn_objbox: block object in ObjectBox holder
  *                          pn_expr: block body
+ * PNK_GENERATOR    nullary
+ * PNK_YIELD,       binary  pn_left: expr or null; pn_right: generator object
+ * PNK_YIELD_STAR
  * PNK_ARRAYCOMP    list    pn_count: 1
  *                          pn_head: list of 1 element, which is block
  *                          enclosing for loop(s) and optionally
  *                          if-guarded PNK_ARRAYPUSH
  * PNK_ARRAYPUSH    unary   pn_op: JSOP_ARRAYCOMP
  *                          pn_kid: array comprehension expression
  * PNK_NOP          nullary
  */
@@ -768,18 +772,19 @@ class ParseNode
     /* Return true if this node appears in a Directive Prologue. */
     bool isDirectivePrologueMember() const { return pn_prologue; }
 
 #ifdef JS_HAS_GENERATOR_EXPRS
     ParseNode *generatorExpr() const {
         MOZ_ASSERT(isKind(PNK_GENEXP));
         ParseNode *callee = this->pn_head;
         ParseNode *body = callee->pn_body;
-        MOZ_ASSERT(body->isKind(PNK_LEXICALSCOPE) || body->isKind(PNK_FOR));
-        return body;
+        MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST));
+        MOZ_ASSERT(body->last()->isKind(PNK_LEXICALSCOPE) || body->last()->isKind(PNK_FOR));
+        return body->last();
     }
 #endif
 
     inline void markAsAssigned();
 
     /*
      * Compute a pointer to the last element in a singly-linked list. NB: list
      * must be non-empty for correct PN_LAST usage -- this is asserted!
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1030,16 +1030,33 @@ Parser<ParseHandler>::functionBody(Funct
         break;
 
       case StarGenerator:
         MOZ_ASSERT(kind != Arrow);
         MOZ_ASSERT(type == StatementListBody);
         break;
     }
 
+    if (pc->isGenerator()) {
+        MOZ_ASSERT(type == StatementListBody);
+        Node generator = newName(context->names().dotGenerator);
+        if (!generator)
+            return null();
+        if (!pc->define(tokenStream, context->names().dotGenerator, generator, Definition::VAR))
+            return null();
+
+        generator = newName(context->names().dotGenerator);
+        if (!generator)
+            return null();
+        if (!noteNameUse(context->names().dotGenerator, generator))
+            return null();
+        if (!handler.prependInitialYield(pn, generator))
+            return null();
+    }
+
     /* Define the 'arguments' binding if necessary. */
     if (!checkFunctionArguments())
         return null();
 
     return pn;
 }
 
 /* See comment for use in Parser::functionDef. */
@@ -5004,16 +5021,31 @@ Parser<ParseHandler>::returnStatement()
         return null();
     }
 
     return pn;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
+Parser<ParseHandler>::newYieldExpression(uint32_t begin, typename ParseHandler::Node expr,
+                                         bool isYieldStar)
+{
+    Node generator = newName(context->names().dotGenerator);
+    if (!generator)
+        return null();
+    if (!noteNameUse(context->names().dotGenerator, generator))
+        return null();
+    if (isYieldStar)
+        return handler.newYieldStarExpression(begin, expr, generator);
+    return handler.newYieldExpression(begin, expr, generator);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
 Parser<ParseHandler>::yieldExpression()
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
     uint32_t begin = pos().begin;
 
     switch (pc->generatorKind()) {
       case StarGenerator:
       {
@@ -5047,17 +5079,17 @@ Parser<ParseHandler>::yieldExpression()
             kind = PNK_YIELD_STAR;
             tokenStream.consumeKnownToken(TOK_MUL);
             // Fall through.
           default:
             exprNode = assignExpr();
             if (!exprNode)
                 return null();
         }
-        return handler.newUnary(kind, JSOP_NOP, begin, exprNode);
+        return newYieldExpression(begin, exprNode, kind == PNK_YIELD_STAR);
       }
 
       case NotGenerator:
         // We are in code that has not seen a yield, but we are in JS 1.7 or
         // later.  Try to transition to being a legacy generator.
         MOZ_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
         MOZ_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset);
 
@@ -5105,17 +5137,17 @@ Parser<ParseHandler>::yieldExpression()
             exprNode = null();
             break;
           default:
             exprNode = assignExpr();
             if (!exprNode)
                 return null();
         }
 
-        return handler.newUnary(PNK_YIELD, JSOP_NOP, begin, exprNode);
+        return newYieldExpression(begin, exprNode);
       }
     }
 
     MOZ_CRASH("yieldExpr");
 }
 
 template <>
 ParseNode *
@@ -6184,19 +6216,27 @@ LegacyCompExprTransplanter::transplant(P
             }
 
             RootedAtom atom(parser->context, pn->pn_atom);
 #ifdef DEBUG
             StmtInfoPC *stmt = LexicalLookup(pc, atom, nullptr, (StmtInfoPC *)nullptr);
             MOZ_ASSERT(!stmt || stmt != pc->topStmt);
 #endif
             if (isGenexp && !dn->isOp(JSOP_CALLEE)) {
-                MOZ_ASSERT(!pc->decls().lookupFirst(atom));
-
-                if (dn->pn_pos < root->pn_pos) {
+                MOZ_ASSERT_IF(atom != parser->context->names().dotGenerator,
+                              !pc->decls().lookupFirst(atom));
+
+                if (atom == parser->context->names().dotGenerator) {
+                    if (dn->dn_uses == pn) {
+                        if (!BumpStaticLevel(parser->tokenStream, dn, pc))
+                            return false;
+                        if (!AdjustBlockId(parser->tokenStream, dn, adjust, pc))
+                            return false;
+                    }
+                } else if (dn->pn_pos < root->pn_pos) {
                     /*
                      * The variable originally appeared to be a use of a
                      * definition or placeholder outside the generator, but now
                      * we know it is scoped within the legacy comprehension
                      * tail's clauses. Make it (along with any other uses within
                      * the generator) a use of a new placeholder in the
                      * generator's lexdeps.
                      */
@@ -6299,17 +6339,17 @@ LegacyComprehensionHeadBlockScopeDepth(P
  * or generator expression signified by this |for| keyword in context.
  *
  * Return null on failure, else return the top-most parse node for the array
  * comprehension or generator expression, with a unary node as the body of the
  * (possibly nested) for-loop, initialized by |kind, op, kid|.
  */
 template <>
 ParseNode *
-Parser<FullParseHandler>::legacyComprehensionTail(ParseNode *bodyStmt, unsigned blockid,
+Parser<FullParseHandler>::legacyComprehensionTail(ParseNode *bodyExpr, unsigned blockid,
                                                   GeneratorKind comprehensionKind,
                                                   ParseContext<FullParseHandler> *outerpc,
                                                   unsigned innerBlockScopeDepth)
 {
     /*
      * If we saw any inner functions while processing the generator expression
      * then they may have upvars referring to the let vars in this generator
      * which were not correctly processed. Bail out and start over without
@@ -6363,25 +6403,25 @@ Parser<FullParseHandler>::legacyComprehe
         MOZ_ASSERT(blockid <= pn->pn_blockid);
         MOZ_ASSERT(blockid < pc->blockidGen);
         MOZ_ASSERT(pc->bodyid < blockid);
         pn->pn_blockid = stmtInfo.blockid = blockid;
         MOZ_ASSERT(adjust < blockid);
         adjust = blockid - adjust;
     }
 
-    handler.setBeginPosition(pn, bodyStmt);
+    handler.setBeginPosition(pn, bodyExpr);
 
     pnp = &pn->pn_expr;
 
-    LegacyCompExprTransplanter transplanter(bodyStmt, this, outerpc, comprehensionKind, adjust);
+    LegacyCompExprTransplanter transplanter(bodyExpr, this, outerpc, comprehensionKind, adjust);
     if (!transplanter.init())
         return null();
 
-    if (!transplanter.transplant(bodyStmt))
+    if (!transplanter.transplant(bodyExpr))
         return null();
 
     MOZ_ASSERT(pc->staticScope && pc->staticScope == pn->pn_objbox->object);
     data.initLet(HoistVars, &pc->staticScope->as<StaticBlockObject>(), JSMSG_ARRAY_INIT_TOO_BIG);
 
     do {
         /*
          * FOR node is binary, left is loop control and right is body.  Use
@@ -6527,16 +6567,33 @@ Parser<FullParseHandler>::legacyComprehe
             return null();
         pn2->pn_kid1 = condition();
         if (!pn2->pn_kid1)
             return null();
         *pnp = pn2;
         pnp = &pn2->pn_kid2;
     }
 
+    ParseNode *bodyStmt;
+    if (isGenexp) {
+        ParseNode *yieldExpr = newYieldExpression(bodyExpr->pn_pos.begin, bodyExpr);
+        if (!yieldExpr)
+            return null();
+        yieldExpr->setInParens(true);
+
+        bodyStmt = handler.newExprStatement(yieldExpr, bodyExpr->pn_pos.end);
+        if (!bodyStmt)
+            return null();
+    } else {
+        bodyStmt = handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH,
+                                    bodyExpr->pn_pos.begin, bodyExpr);
+        if (!bodyStmt)
+            return null();
+    }
+
     *pnp = bodyStmt;
 
     pc->topStmt->innerBlockScopeDepth += innerBlockScopeDepth;
     PopStatementPC(tokenStream, pc);
 
     handler.setEndPosition(pn, pos().end);
 
     return pn;
@@ -6563,22 +6620,17 @@ Parser<FullParseHandler>::legacyArrayCom
     // Remove the single element from array's linked list, leaving us with an
     // empty array literal and a comprehension expression.
     MOZ_ASSERT(array->pn_count == 1);
     ParseNode *bodyExpr = array->last();
     array->pn_count = 0;
     array->pn_tail = &array->pn_head;
     *array->pn_tail = nullptr;
 
-    ParseNode *arrayPush = handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH,
-                                            bodyExpr->pn_pos.begin, bodyExpr);
-    if (!arrayPush)
-        return null();
-
-    ParseNode *comp = legacyComprehensionTail(arrayPush, array->pn_blockid, NotGenerator,
+    ParseNode *comp = legacyComprehensionTail(bodyExpr, array->pn_blockid, NotGenerator,
                                               nullptr, LegacyComprehensionHeadBlockScopeDepth(pc));
     if (!comp)
         return null();
 
     MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
 
     TokenPos p = handler.getPosition(array);
     p.end = pos().end;
@@ -6591,20 +6643,20 @@ Parser<SyntaxParseHandler>::legacyArrayC
 {
     abortIfSyntaxParser();
     return null();
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::generatorComprehensionLambda(GeneratorKind comprehensionKind,
-                                                   unsigned begin, Node innerStmt)
+                                                   unsigned begin, Node innerExpr)
 {
     MOZ_ASSERT(comprehensionKind == LegacyGenerator || comprehensionKind == StarGenerator);
-    MOZ_ASSERT(!!innerStmt == (comprehensionKind == LegacyGenerator));
+    MOZ_ASSERT(!!innerExpr == (comprehensionKind == LegacyGenerator));
 
     Node genfn = handler.newFunctionDefinition();
     if (!genfn)
         return null();
     handler.setOp(genfn, JSOP_LAMBDA);
 
     ParseContext<ParseHandler> *outerpc = pc;
 
@@ -6645,39 +6697,57 @@ Parser<ParseHandler>::generatorComprehen
     genFunbox->anyCxFlags = outerpc->sc->anyCxFlags;
     if (outerpc->sc->isFunctionBox())
         genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags;
 
     MOZ_ASSERT(genFunbox->generatorKind() == comprehensionKind);
     genFunbox->inGenexpLambda = true;
     handler.setBlockId(genfn, genpc.bodyid);
 
-    Node body;
-
+    Node generator = newName(context->names().dotGenerator);
+    if (!generator)
+        return null();
+    if (!pc->define(tokenStream, context->names().dotGenerator, generator, Definition::VAR))
+        return null();
+
+    Node body = handler.newStatementList(pc->blockid(), TokenPos(begin, pos().end));
+    if (!body)
+        return null();
+
+    Node comp;
     if (comprehensionKind == StarGenerator) {
-        body = comprehension(StarGenerator);
-        if (!body)
+        comp = comprehension(StarGenerator);
+        if (!comp)
             return null();
     } else {
         MOZ_ASSERT(comprehensionKind == LegacyGenerator);
-        body = legacyComprehensionTail(innerStmt, outerpc->blockid(), LegacyGenerator,
+        comp = legacyComprehensionTail(innerExpr, outerpc->blockid(), LegacyGenerator,
                                        outerpc, LegacyComprehensionHeadBlockScopeDepth(outerpc));
-        if (!body)
+        if (!comp)
             return null();
     }
 
     if (comprehensionKind == StarGenerator)
         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
 
-    handler.setBeginPosition(body, begin);
+    handler.setBeginPosition(comp, begin);
+    handler.setEndPosition(comp, pos().end);
+    handler.addStatementToList(body, comp, pc);
     handler.setEndPosition(body, pos().end);
-
     handler.setBeginPosition(genfn, begin);
     handler.setEndPosition(genfn, pos().end);
 
+    generator = newName(context->names().dotGenerator);
+    if (!generator)
+        return null();
+    if (!noteNameUse(context->names().dotGenerator, generator))
+        return null();
+    if (!handler.prependInitialYield(body, generator))
+        return null();
+
     // Note that if we ever start syntax-parsing generators, we will also
     // need to propagate the closed-over variable set to the inner
     // lazyscript, as in finishFunctionDefinition.
     handler.setFunctionBody(genfn, body);
 
     PropagateTransitiveParseFlags(genFunbox, outerpc->sc);
 
     if (!leaveFunction(genfn, outerpc, /* bodyLevelHoistedUse = */ false))
@@ -6704,29 +6774,18 @@ Parser<ParseHandler>::generatorComprehen
  * is merely sugar for a generator function expression and its application.
  */
 template <>
 ParseNode *
 Parser<FullParseHandler>::legacyGeneratorExpr(ParseNode *expr)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
-    /* Create a |yield| node for |kid|. */
-    ParseNode *yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, expr->pn_pos.begin, expr);
-    if (!yieldExpr)
-        return null();
-    yieldExpr->setInParens(true);
-
-    // A statement to wrap the yield expression.
-    ParseNode *yieldStmt = handler.newExprStatement(yieldExpr, expr->pn_pos.end);
-    if (!yieldStmt)
-        return null();
-
     /* Make a new node for the desugared generator function. */
-    ParseNode *genfn = generatorComprehensionLambda(LegacyGenerator, expr->pn_pos.begin, yieldStmt);
+    ParseNode *genfn = generatorComprehensionLambda(LegacyGenerator, expr->pn_pos.begin, expr);
     if (!genfn)
         return null();
 
     /*
      * Our result is a call expression that invokes the anonymous generator
      * function object.
      */
     ParseNode *result = ListNode::create(PNK_GENEXP, &handler);
@@ -6866,17 +6925,17 @@ Parser<ParseHandler>::comprehensionTail(
     Node bodyExpr = assignExpr();
     if (!bodyExpr)
         return null();
 
     if (comprehensionKind == NotGenerator)
         return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr);
 
     MOZ_ASSERT(comprehensionKind == StarGenerator);
-    Node yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, begin, bodyExpr);
+    Node yieldExpr = newYieldExpression(begin, bodyExpr);
     if (!yieldExpr)
         return null();
     handler.setInParens(yieldExpr);
 
     return handler.newExprStatement(yieldExpr, pos().end);
 }
 
 // Parse an ES6 generator or array comprehension, starting at the first 'for'.
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -459,16 +459,17 @@ class Parser : private JS::AutoGCRooter,
     Node stringLiteral();
     Node noSubstitutionTemplate();
     Node templateLiteral();
     bool taggedTemplate(Node nodeList, TokenKind tt);
     bool appendToCallSiteObj(Node callSiteObj);
     bool addExprAndGetNextTemplStrToken(Node nodeList, TokenKind &tt);
 
     inline Node newName(PropertyName *name);
+    inline Node newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false);
 
     inline bool abortIfSyntaxParser();
 
   public:
 
     /* Public entry points for parsing. */
     Node statement(bool canHaveDirectives = false);
     bool maybeParseDirective(Node list, Node pn, bool *cont);
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -130,21 +130,24 @@ class SyntaxParseHandler
     bool addElision(Node literal, const TokenPos &pos) { return true; }
     bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; }
     bool addArrayElement(Node literal, Node element) { return true; }
 
     Node newObjectLiteral(uint32_t begin) { return NodeGeneric; }
     bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
     bool addPropertyDefinition(Node literal, Node name, Node expr, bool isShorthand = false) { return true; }
     bool addMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
+    Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
+    Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
 
     // Statements
 
     Node newStatementList(unsigned blockid, const TokenPos &pos) { return NodeGeneric; }
     void addStatementToList(Node list, Node stmt, ParseContext<SyntaxParseHandler> *pc) {}
+    bool prependInitialYield(Node stmtList, Node gen) { return true; }
     Node newEmptyStatement(const TokenPos &pos) { return NodeGeneric; }
 
     Node newExprStatement(Node expr, uint32_t end) {
         return expr == NodeString ? NodeStringExprStatement : NodeGeneric;
     }
 
     Node newIfStatement(uint32_t begin, Node cond, Node then, Node else_) { return NodeGeneric; }
     Node newDoWhileStatement(Node body, Node cond, const TokenPos &pos) { return NodeGeneric; }
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -20,17 +20,17 @@
 /*
  * A write barrier is a mechanism used by incremental or generation GCs to
  * ensure that every value that needs to be marked is marked. In general, the
  * write barrier should be invoked whenever a write can cause the set of things
  * traced through by the GC to change. This includes:
  *   - writes to object properties
  *   - writes to array slots
  *   - writes to fields like JSObject::shape_ that we trace through
- *   - writes to fields in private data, like JSGenerator::obj
+ *   - writes to fields in private data
  *   - writes to non-markable fields like JSObject::private that point to
  *     markable data
  * The last category is the trickiest. Even though the private pointers does not
  * point to a GC thing, changing the private pointer may change the set of
  * objects that are traced by the GC. Therefore it needs a write barrier.
  *
  * Every barriered write should have the following form:
  *   <pre-barrier>
--- a/js/src/jit-test/tests/debug/Frame-onPop-generators-01.js
+++ b/js/src/jit-test/tests/debug/Frame-onPop-generators-01.js
@@ -1,10 +1,11 @@
+// |jit-test| error: StopIteration
 // Returning {throw:} from an onPop handler when yielding works and
-// does not close the generator-iterator.
+// does closes the generator-iterator.
 
 load(libdir + "asserts.js");
 
 var g = newGlobal();
 var dbg = new Debugger;
 var gw = dbg.addDebuggee(g);
 dbg.onDebuggerStatement = function handleDebugger(frame) {
     frame.onPop = function (c) {
@@ -12,9 +13,9 @@ dbg.onDebuggerStatement = function handl
     };
 };
 g.eval("function g() { for (var i = 0; i < 10; i++) { debugger; yield i; } }");
 g.eval("var it = g();");
 var rv = gw.evalInGlobal("it.next();");
 assertEq(rv.throw, "fit");
 
 dbg.enabled = false;
-assertEq(g.it.next(), 1);
+g.it.next();
--- a/js/src/jit-test/tests/debug/Frame-onPop-star-generators-01.js
+++ b/js/src/jit-test/tests/debug/Frame-onPop-star-generators-01.js
@@ -1,10 +1,10 @@
 // Returning {throw:} from an onPop handler when yielding works and
-// does not close the generator-iterator.
+// closes the generator-iterator.
 
 load(libdir + "iteration.js");
 
 var g = newGlobal();
 var dbg = new Debugger;
 var gw = dbg.addDebuggee(g);
 dbg.onDebuggerStatement = function handleDebugger(frame) {
     frame.onPop = function (c) {
@@ -12,9 +12,9 @@ dbg.onDebuggerStatement = function handl
     };
 };
 g.eval("function* g() { for (var i = 0; i < 10; i++) { debugger; yield i; } }");
 g.eval("var it = g();");
 var rv = gw.evalInGlobal("it.next();");
 assertEq(rv.throw, "fit");
 
 dbg.enabled = false;
-assertIteratorNext(g.it, 1);
+assertIteratorDone(g.it);
--- a/js/src/jit-test/tests/debug/resumption-04.js
+++ b/js/src/jit-test/tests/debug/resumption-04.js
@@ -1,14 +1,19 @@
+// |jit-test| error: already executing generator
 // Forced return from a generator frame.
 
 var g = newGlobal();
 g.debuggeeGlobal = this;
 g.eval("var dbg = new Debugger(debuggeeGlobal);" +
        "dbg.onDebuggerStatement = function () { return {return: '!'}; };");
 
 function gen() {
     yield '1';
     debugger;  // Force return here. The value is ignored.
     yield '2';
 }
-var x = [v for (v in gen())];
-assertEq(x.join(","), "1");
+
+var iter = gen();
+assertEq(iter.next(), "1");
+assertEq(iter.next(), "!");
+iter.next();
+assertEq(0, 1);
--- a/js/src/jit-test/tests/debug/resumption-06.js
+++ b/js/src/jit-test/tests/debug/resumption-06.js
@@ -1,8 +1,9 @@
+// |jit-test| error: already executing generator
 // Forced return from a star generator frame.
 
 load(libdir + 'asserts.js')
 load(libdir + 'iteration.js')
 
 var g = newGlobal();
 g.debuggeeGlobal = this;
 g.eval("var dbg = new Debugger(debuggeeGlobal);" +
@@ -11,9 +12,10 @@ g.eval("var dbg = new Debugger(debuggeeG
 function* gen() {
     yield '1';
     debugger;  // Force return here. The value is ignored.
     yield '2';
 }
 var iter = gen();
 assertIteratorNext(iter, '1');
 assertEq(iter.next(), '!');
-assertIteratorDone(iter);
+iter.next();
+assertEq(0, 1);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/generators/throw-closes.js
@@ -0,0 +1,49 @@
+// When a generator function throws, the generator is closed.
+
+load(libdir + "asserts.js");
+load(libdir + "iteration.js");
+
+// Star generator, next() throws.
+function *g() {
+    yield 1;
+    yield 2;
+    throw 3;
+    yield 4;
+}
+var i = g();
+assertIteratorNext(i, 1);
+assertIteratorNext(i, 2);
+assertThrowsValue(() => i.next(), 3);
+assertIteratorDone(i);
+assertIteratorDone(i);
+
+// Star generator, throw() throws.
+function *h() {
+    yield 1;
+    yield 2;
+}
+var i = h();
+assertIteratorNext(i, 1);
+assertThrowsValue(() => i.throw(4), 4);
+assertIteratorDone(i);
+
+// Legacy generator, throw() throws.
+function l1() {
+    yield 1;
+    yield 2;
+}
+var i = l1();
+assertEq(i.next(), 1);
+assertThrowsValue(() => i.throw(5), 5);
+assertThrowsInstanceOf(() => i.next(), StopIteration);
+
+// Legacy generator, next() throws.
+function l2() {
+    yield 1;
+    throw 6;
+    yield 2;
+}
+var i = l2();
+assertEq(i.next(), 1);
+assertThrowsValue(() => i.next(), 6);
+assertThrowsInstanceOf(() => i.next(), StopIteration);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/generators/wrappers.js
@@ -0,0 +1,34 @@
+// Generator methods work transparently on CrossCompartmentWrappers.
+
+load(libdir + "asserts.js");
+load(libdir + "iteration.js");
+
+function gen() { yield 1; yield 2; }
+var it = gen();
+
+var g = newGlobal();
+g.eval("function gen2() { yield 3; yield 4; }; var it2 = gen2();");
+
+// LegacyGenerator.next
+assertEq(it.next.call(g.it2), 3);
+
+// LegacyGenerator.throw
+assertThrowsValue(() => it.throw.call(g.it2, 7), 7);
+
+function *gen3() { yield 1; yield 2; }
+it = gen3();
+g.eval("function *gen4() { yield 5; yield 6; }; var it4 = gen4();");
+
+// StarGenerator.next
+assertIteratorResult(it.next.call(g.it4), 5, false)
+
+// StarGenerator.throw
+assertThrowsValue(() => it.throw.call(g.it4, 8), 8);
+
+// Other objects should throw.
+try {
+    it.next.call([]);
+    assertEq(0, 1);
+} catch (e) {
+    assertEq(e.toString().contains("called on incompatible Array"), true);
+}
--- a/js/src/jit-test/tests/saved-stacks/generators.js
+++ b/js/src/jit-test/tests/saved-stacks/generators.js
@@ -5,11 +5,12 @@ const { value: frame } = (function iife1
     yield (function iife2() {
       return saveStack();
     }());
   }()).next();
 }());
 
 assertEq(frame.functionDisplayName, "iife2");
 assertEq(frame.parent.functionDisplayName, "generator");
-assertEq(frame.parent.parent.functionDisplayName, "iife1");
-assertEq(frame.parent.parent.parent.functionDisplayName, null);
-assertEq(frame.parent.parent.parent.parent, null);
+assertEq(frame.parent.parent.functionDisplayName, "next");
+assertEq(frame.parent.parent.parent.functionDisplayName, "iife1");
+assertEq(frame.parent.parent.parent.parent.functionDisplayName, null);
+assertEq(frame.parent.parent.parent.parent.parent, null);
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -44,17 +44,19 @@ jit::Bailout(BailoutStack *sp, BaselineB
     TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
     TraceLogTimestamp(logger, TraceLogger::Bailout);
 
     JitSpew(JitSpew_IonBailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());
 
     MOZ_ASSERT(IsBaselineEnabled(cx));
 
     *bailoutInfo = nullptr;
-    uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo);
+    bool poppedLastSPSFrame = false;
+    uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo,
+                                           /* excInfo = */ nullptr, &poppedLastSPSFrame);
     MOZ_ASSERT(retval == BAILOUT_RETURN_OK ||
                retval == BAILOUT_RETURN_FATAL_ERROR ||
                retval == BAILOUT_RETURN_OVERRECURSED);
     MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);
 
     if (retval != BAILOUT_RETURN_OK) {
         // If the bailout failed, then bailout trampoline will pop the
         // current frame and jump straight to exception handling code when
@@ -63,17 +65,18 @@ jit::Bailout(BailoutStack *sp, BaselineB
         //
         // We call ExitScript here to ensure that if the ionScript had SPS
         // instrumentation, then the SPS entry for it is popped.
         //
         // However, if the bailout was during argument check, then a
         // pseudostack frame would not have been pushed in the first
         // place, so don't pop anything in that case.
         bool popSPSFrame = iter.ionScript()->hasSPSInstrumentation() &&
-                           (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck);
+                           (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck) &&
+                           !poppedLastSPSFrame;
         JSScript *script = iter.script();
         probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame);
 
         EnsureExitFrame(iter.jsFrame());
     }
 
     return retval;
 }
@@ -100,17 +103,19 @@ jit::InvalidationBailout(InvalidationBai
     JitSpew(JitSpew_IonBailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());
 
     // Note: the frame size must be computed before we return from this function.
     *frameSizeOut = iter.frameSize();
 
     MOZ_ASSERT(IsBaselineEnabled(cx));
 
     *bailoutInfo = nullptr;
-    uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, bailoutInfo);
+    bool poppedLastSPSFrame = false;
+    uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, bailoutInfo,
+                                           /* excInfo = */ nullptr, &poppedLastSPSFrame);
     MOZ_ASSERT(retval == BAILOUT_RETURN_OK ||
                retval == BAILOUT_RETURN_FATAL_ERROR ||
                retval == BAILOUT_RETURN_OVERRECURSED);
     MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);
 
     if (retval != BAILOUT_RETURN_OK) {
         // If the bailout failed, then bailout trampoline will pop the
         // current frame and jump straight to exception handling code when
@@ -119,17 +124,18 @@ jit::InvalidationBailout(InvalidationBai
         //
         // We call ExitScript here to ensure that if the ionScript had SPS
         // instrumentation, then the SPS entry for it is popped.
         //
         // However, if the bailout was during argument check, then a
         // pseudostack frame would not have been pushed in the first
         // place, so don't pop anything in that case.
         bool popSPSFrame = iter.ionScript()->hasSPSInstrumentation() &&
-                           (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck);
+                           (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck) &&
+                           !poppedLastSPSFrame;
         JSScript *script = iter.script();
         probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame);
 
         IonJSFrameLayout *frame = iter.jsFrame();
         JitSpew(JitSpew_IonInvalidate, "Bailout failed (%s): converting to exit frame",
                 (retval == BAILOUT_RETURN_FATAL_ERROR) ? "Fatal Error" : "Over Recursion");
         JitSpew(JitSpew_IonInvalidate, "   orig calleeToken %p", (void *) frame->calleeToken());
         JitSpew(JitSpew_IonInvalidate, "   orig frameSize %u", unsigned(frame->prevFrameLocalSize()));
@@ -175,17 +181,19 @@ jit::ExceptionHandlerBailout(JSContext *
     cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
     gc::AutoSuppressGC suppress(cx);
 
     JitActivationIterator jitActivations(cx->runtime());
     BailoutFrameInfo bailoutData(jitActivations, frame.frame());
     JitFrameIterator iter(jitActivations);
 
     BaselineBailoutInfo *bailoutInfo = nullptr;
-    uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, &bailoutInfo, &excInfo);
+    bool poppedLastSPSFrame = false;
+    uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true,
+                                           &bailoutInfo, &excInfo, &poppedLastSPSFrame);
 
     if (retval == BAILOUT_RETURN_OK) {
         MOZ_ASSERT(bailoutInfo);
 
         // Overwrite the kind so HandleException after the bailout returns
         // false, jumping directly to the exception tail.
         if (excInfo.propagatingIonExceptionForDebugMode())
             bailoutInfo->bailoutKind = Bailout_IonExceptionDebugMode;
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -510,19 +510,22 @@ GetNextNonLoopEntryPc(jsbytecode *pc)
 //                      |  ReturnAddr   | <-- return into ArgumentsRectifier after call
 //                      +===============+
 //
 static bool
 InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
                 HandleFunction fun, HandleScript script, IonScript *ionScript,
                 SnapshotIterator &iter, bool invalidate, BaselineStackBuilder &builder,
                 AutoValueVector &startFrameFormals, MutableHandleFunction nextCallee,
-                jsbytecode **callPC, const ExceptionBailoutInfo *excInfo)
+                jsbytecode **callPC, const ExceptionBailoutInfo *excInfo,
+                bool *poppedLastSPSFrameOut)
 {
     MOZ_ASSERT(script->hasBaselineScript());
+    MOZ_ASSERT(poppedLastSPSFrameOut);
+    MOZ_ASSERT(!*poppedLastSPSFrameOut);
 
     // Are we catching an exception?
     bool catchingException = excInfo && excInfo->catchingException();
 
     // If we are catching an exception, we are bailing out to a catch or
     // finally block and this is the frame where we will resume. Usually the
     // expression stack should be empty in this case but there can be
     // iterators on the stack.
@@ -1030,16 +1033,21 @@ InitFromBailout(JSContext *cx, HandleScr
                     // 4. If resuming into top-level code main body, an SPS entry will
                     // have been pushed, and can be left alone.
                     //
                     // Only need to handle case 3 here.
                     if (!caller && bailoutKind != Bailout_ArgumentCheck) {
                         JitSpew(JitSpew_BaselineBailouts,
                                 "      Popping SPS entry for outermost frame");
                         cx->runtime()->spsProfiler.exit(script, fun);
+
+                        // Notify caller that the last SPS frame was popped, so not
+                        // to do it again.
+                        if (poppedLastSPSFrameOut)
+                            *poppedLastSPSFrameOut = true;
                     }
                 }
             } else {
                 opReturnAddr = nativeCodeForPC;
             }
             builder.setResumeAddr(opReturnAddr);
             JitSpew(JitSpew_BaselineBailouts, "      Set resumeAddr=%p", opReturnAddr);
         }
@@ -1286,25 +1294,28 @@ InitFromBailout(JSContext *cx, HandleScr
         return false;
 
     return true;
 }
 
 uint32_t
 jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIterator &iter,
                           bool invalidate, BaselineBailoutInfo **bailoutInfo,
-                          const ExceptionBailoutInfo *excInfo)
+                          const ExceptionBailoutInfo *excInfo, bool *poppedLastSPSFrameOut)
 {
     // The Baseline frames we will reconstruct on the heap are not rooted, so GC
     // must be suppressed here.
     MOZ_ASSERT(cx->mainThread().suppressGC);
 
     MOZ_ASSERT(bailoutInfo != nullptr);
     MOZ_ASSERT(*bailoutInfo == nullptr);
 
+    MOZ_ASSERT(poppedLastSPSFrameOut);
+    MOZ_ASSERT(!*poppedLastSPSFrameOut);
+
     TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
     TraceLogStopEvent(logger, TraceLogger::IonMonkey);
     TraceLogStartEvent(logger, TraceLogger::Baseline);
 
     // The caller of the top frame must be one of the following:
     //      IonJS - Ion calling into Ion.
     //      BaselineStub - Baseline calling into Ion.
     //      Entry - Interpreter or other calling into Ion.
@@ -1424,17 +1435,18 @@ jit::BailoutIonToBaseline(JSContext *cx,
         // We also need to pass excInfo if we're bailing out in place for
         // debug mode.
         bool passExcInfo = handleException || propagatingExceptionForDebugMode;
 
         jsbytecode *callPC = nullptr;
         RootedFunction nextCallee(cx, nullptr);
         if (!InitFromBailout(cx, caller, callerPC, fun, scr, iter.ionScript(),
                              snapIter, invalidate, builder, startFrameFormals,
-                             &nextCallee, &callPC, passExcInfo ? excInfo : nullptr))
+                             &nextCallee, &callPC, passExcInfo ? excInfo : nullptr,
+                             poppedLastSPSFrameOut))
         {
             return BAILOUT_RETURN_FATAL_ERROR;
         }
 
         if (!snapIter.moreFrames()) {
             MOZ_ASSERT(!callPC);
             break;
         }
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -318,26 +318,23 @@ jit::CanEnterBaselineMethod(JSContext *c
 
         if (invoke.args().length() > BASELINE_MAX_ARGS_LENGTH) {
             JitSpew(JitSpew_BaselineAbort, "Too many arguments (%u)", invoke.args().length());
             return Method_CantCompile;
         }
 
         if (!state.maybeCreateThisForConstructor(cx))
             return Method_Skipped;
-    } else if (state.isExecute()) {
+    } else {
+        MOZ_ASSERT(state.isExecute());
         ExecuteType type = state.asExecute()->type();
         if (type == EXECUTE_DEBUG || type == EXECUTE_DEBUG_GLOBAL) {
             JitSpew(JitSpew_BaselineAbort, "debugger frame");
             return Method_CantCompile;
         }
-    } else {
-        MOZ_ASSERT(state.isGenerator());
-        JitSpew(JitSpew_BaselineAbort, "generator frame");
-        return Method_CantCompile;
     }
 
     RootedScript script(cx, state.script());
     return CanEnterBaselineJIT(cx, script, /* osr = */false);
 };
 
 BaselineScript *
 BaselineScript::New(JSScript *jsscript, uint32_t prologueOffset, uint32_t epilogueOffset,
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -434,17 +434,18 @@ struct BaselineBailoutInfo
 
     // The bailout kind.
     BailoutKind bailoutKind;
 };
 
 uint32_t
 BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIterator &iter,
                      bool invalidate, BaselineBailoutInfo **bailoutInfo,
-                     const ExceptionBailoutInfo *exceptionInfo = nullptr);
+                     const ExceptionBailoutInfo *exceptionInfo,
+                     bool *poppedLastSPSFrame);
 
 // Mark baseline scripts on the stack as active, so that they are not discarded
 // during GC.
 void
 MarkActiveBaselineScripts(Zone *zone);
 
 MethodStatus
 BaselineCompile(JSContext *cx, JSScript *script);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2396,20 +2396,16 @@ jit::CanEnter(JSContext *cx, RunState &s
         if (TooManyFormalArguments(invoke.args().callee().as<JSFunction>().nargs())) {
             JitSpew(JitSpew_IonAbort, "too many args");
             ForbidCompilation(cx, script);
             return Method_CantCompile;
         }
 
         if (!state.maybeCreateThisForConstructor(cx))
             return Method_Skipped;
-    } else if (state.isGenerator()) {
-        JitSpew(JitSpew_IonAbort, "generator frame");
-        ForbidCompilation(cx, script);
-        return Method_CantCompile;
     }
 
     // If --ion-eager is used, compile with Baseline first, so that we
     // can directly enter IonMonkey.
     if (js_JitOptions.eagerCompilation && !rscript->hasBaselineScript()) {
         MethodStatus status = CanEnterBaselineMethod(cx, state);
         if (status != Method_Compiled)
             return status;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2776,17 +2776,18 @@ JS_AlreadyHasOwnUCProperty(JSContext *cx
 {
     JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
     return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
 }
 
-/* Wrapper functions to create wrappers with no corresponding JSJitInfo from API
+/*
+ * Wrapper functions to create wrappers with no corresponding JSJitInfo from API
  * function arguments.
  */
 static JSPropertyOpWrapper
 GetterWrapper(JSPropertyOp getter)
 {
     JSPropertyOpWrapper ret;
     ret.op = getter;
     ret.info = nullptr;
@@ -2820,18 +2821,16 @@ DefinePropertyById(JSContext *cx, Handle
     // When we use DefineProperty, we need full scriptable Function objects rather
     // than JSNatives. However, we might be pulling this property descriptor off
     // of something with JSNative property descriptors. If we are, wrap them in
     // JS Function objects.
     if (attrs & JSPROP_NATIVE_ACCESSORS) {
         MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
         JSFunction::Flags zeroFlags = JSAPIToJSFunctionFlags(0);
 
-        // We can't just use JS_NewFunctionById here because it assumes a
-        // string id.
         RootedAtom atom(cx, JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : nullptr);
         attrs &= ~JSPROP_NATIVE_ACCESSORS;
         if (getter) {
             RootedObject global(cx, (JSObject*) &obj->global());
             JSFunction *getobj = NewFunction(cx, NullPtr(), (Native) getter, 0,
                                              zeroFlags, global, atom);
             if (!getobj)
                 return false;
@@ -2858,22 +2857,22 @@ DefinePropertyById(JSContext *cx, Handle
             attrs |= JSPROP_SETTER;
         }
     }
 
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id, value,
-                            (attrs & JSPROP_GETTER)
-                            ? JS_FUNC_TO_DATA_PTR(JSObject *, getter)
-                            : nullptr,
-                            (attrs & JSPROP_SETTER)
-                            ? JS_FUNC_TO_DATA_PTR(JSObject *, setter)
-                            : nullptr);
+                          (attrs & JSPROP_GETTER)
+                          ? JS_FUNC_TO_DATA_PTR(JSObject *, getter)
+                          : nullptr,
+                          (attrs & JSPROP_SETTER)
+                          ? JS_FUNC_TO_DATA_PTR(JSObject *, setter)
+                          : nullptr);
 
     return JSObject::defineGeneric(cx, obj, id, value, getter, setter, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
                       unsigned attrs, JSPropertyOp getter, JSStrictPropertyOp setter)
 {
@@ -3007,64 +3006,53 @@ DefineProperty(JSContext *cx, HandleObje
         if (!atom)
             return false;
         id = AtomToId(atom);
     }
 
     return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags);
 }
 
-
 static bool
-DefineSelfHostedProperty(JSContext *cx,
-                         HandleObject obj,
-                         const char *name,
-                         const char *getterName,
-                         const char *setterName,
-                         unsigned attrs,
-                         unsigned flags)
-{
-    RootedAtom nameAtom(cx, Atomize(cx, name, strlen(name)));
-    if (!nameAtom)
-        return false;
-
+DefineSelfHostedProperty(JSContext *cx, HandleObject obj, HandleId id,
+                         const char *getterName, const char *setterName,
+                         unsigned attrs, unsigned flags)
+{
     RootedAtom getterNameAtom(cx, Atomize(cx, getterName, strlen(getterName)));
     if (!getterNameAtom)
         return false;
 
+    RootedAtom name(cx, IdToFunctionName(cx, id));
+    if (!name)
+        return false;
+
     RootedValue getterValue(cx);
-    if (!cx->global()->getSelfHostedFunction(cx, getterNameAtom, nameAtom,
-                                             0, &getterValue))
-    {
+    if (!cx->global()->getSelfHostedFunction(cx, getterNameAtom, name, 0, &getterValue))
         return false;
-    }
     MOZ_ASSERT(getterValue.isObject() && getterValue.toObject().is<JSFunction>());
     RootedFunction getterFunc(cx, &getterValue.toObject().as<JSFunction>());
     JSPropertyOp getterOp = JS_DATA_TO_FUNC_PTR(PropertyOp, getterFunc.get());
 
     RootedFunction setterFunc(cx);
     if (setterName) {
         RootedAtom setterNameAtom(cx, Atomize(cx, setterName, strlen(setterName)));
         if (!setterNameAtom)
             return false;
 
         RootedValue setterValue(cx);
-        if (!cx->global()->getSelfHostedFunction(cx, setterNameAtom, nameAtom,
-                                                 0, &setterValue))
-        {
+        if (!cx->global()->getSelfHostedFunction(cx, setterNameAtom, name, 0, &setterValue))
             return false;
-        }
         MOZ_ASSERT(setterValue.isObject() && setterValue.toObject().is<JSFunction>());
         setterFunc = &getterValue.toObject().as<JSFunction>();
     }
     JSStrictPropertyOp setterOp = JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setterFunc.get());
 
-    return DefineProperty(cx, obj, name, JS::UndefinedHandleValue,
-                          GetterWrapper(getterOp), SetterWrapper(setterOp),
-                          attrs, flags);
+    return DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue,
+                              GetterWrapper(getterOp), SetterWrapper(setterOp),
+                              attrs, flags);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext *cx, HandleObject obj, const char *name, HandleValue value,
                   unsigned attrs,
                   PropertyOp getter /* = nullptr */, JSStrictPropertyOp setter /* = nullptr */)
 {
     return DefineProperty(cx, obj, name, value, GetterWrapper(getter), SetterWrapper(setter),
@@ -3251,47 +3239,91 @@ JS_DefineConstDoubles(JSContext *cx, Han
     return DefineConstScalar(cx, obj, cds);
 }
 JS_PUBLIC_API(bool)
 JS_DefineConstIntegers(JSContext *cx, HandleObject obj, const JSConstIntegerSpec *cis)
 {
     return DefineConstScalar(cx, obj, cis);
 }
 
+static JS::SymbolCode
+PropertySpecNameToSymbolCode(const char *name)
+{
+    MOZ_ASSERT(JS::PropertySpecNameIsSymbol(name));
+    uintptr_t u = reinterpret_cast<uintptr_t>(name);
+    return JS::SymbolCode(u - 1);
+}
+
+static bool
+PropertySpecNameToId(JSContext *cx, const char *name, MutableHandleId id,
+                     js::InternBehavior ib = js::DoNotInternAtom)
+{
+    if (JS::PropertySpecNameIsSymbol(name)) {
+        JS::SymbolCode which = PropertySpecNameToSymbolCode(name);
+        id.set(SYMBOL_TO_JSID(cx->wellKnownSymbols().get(which)));
+    } else {
+        JSAtom *atom = Atomize(cx, name, strlen(name), ib);
+        if (!atom)
+            return false;
+        id.set(AtomToId(atom));
+    }
+    return true;
+}
+
+JS_PUBLIC_API(bool)
+JS::PropertySpecNameToPermanentId(JSContext *cx, const char *name, jsid *idp)
+{
+    // We are calling fromMarkedLocation(idp) even though idp points to a
+    // location that will never be marked. This is OK because the whole point
+    // of this API is to populate *idp with a jsid that does not need to be
+    // marked.
+    return PropertySpecNameToId(cx, name, MutableHandleId::fromMarkedLocation(idp),
+                                js::InternAtom);
+}
+
 JS_PUBLIC_API(bool)
 JS_DefineProperties(JSContext *cx, HandleObject obj, const JSPropertySpec *ps)
 {
-    bool ok;
-    for (ok = true; ps->name; ps++) {
+    RootedId id(cx);
+
+    for (; ps->name; ps++) {
+        if (!PropertySpecNameToId(cx, ps->name, &id))
+            return false;
+
         if (ps->flags & JSPROP_NATIVE_ACCESSORS) {
             // If you declare native accessors, then you should have a native
             // getter.
             MOZ_ASSERT(ps->getter.propertyOp.op);
+
             // If you do not have a self-hosted getter, you should not have a
             // self-hosted setter. This is the closest approximation to that
             // assertion we can have with our setup.
             MOZ_ASSERT_IF(ps->setter.propertyOp.info, ps->setter.propertyOp.op);
 
-            ok = DefineProperty(cx, obj, ps->name, JS::UndefinedHandleValue,
-                                ps->getter.propertyOp, ps->setter.propertyOp, ps->flags, 0);
+            if (!DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue,
+                                    ps->getter.propertyOp, ps->setter.propertyOp, ps->flags, 0))
+            {
+                return false;
+            }
         } else {
             // If you have self-hosted getter/setter, you can't have a
             // native one.
             MOZ_ASSERT(!ps->getter.propertyOp.op && !ps->setter.propertyOp.op);
             MOZ_ASSERT(ps->flags & JSPROP_GETTER);
 
-            ok = DefineSelfHostedProperty(cx, obj, ps->name,
+            if (!DefineSelfHostedProperty(cx, obj, id,
                                           ps->getter.selfHosted.funname,
                                           ps->setter.selfHosted.funname,
-                                          ps->flags, 0);
+                                          ps->flags, 0))
+            {
+                return false;
+            }
         }
-        if (!ok)
-            break;
     }
-    return ok;
+    return true;
 }
 
 JS_PUBLIC_API(bool)
 JS::ParsePropertyDescriptorObject(JSContext *cx,
                                   HandleObject obj,
                                   HandleValue descObj,
                                   MutableHandle<JSPropertyDescriptor> desc)
 {
@@ -3615,147 +3647,16 @@ JS_Enumerate(JSContext *cx, HandleObject
 
     AutoIdVector props(cx);
     JSIdArray *ida;
     if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &props) || !VectorToIdArray(cx, props, &ida))
         return nullptr;
     return ida;
 }
 
-/*
- * XXX reverse iterator for properties, unreverse and meld with jsinterp.c's
- *     prop_iterator_class somehow...
- * + preserve the obj->enumerate API while optimizing the native object case
- * + native case here uses a JSShape *, but that iterates in reverse!
- * + so we make non-native match, by reverse-iterating after JS_Enumerating
- */
-static const uint32_t JSSLOT_ITER_INDEX = 0;
-
-static void
-prop_iter_finalize(FreeOp *fop, JSObject *obj)
-{
-    void *pdata = obj->as<NativeObject>().getPrivate();
-    if (!pdata)
-        return;
-
-    if (obj->as<NativeObject>().getSlot(JSSLOT_ITER_INDEX).toInt32() >= 0) {
-        /* Non-native case: destroy the ida enumerated when obj was created. */
-        JSIdArray *ida = (JSIdArray *) pdata;
-        fop->free_(ida);
-    }
-}
-
-static void
-prop_iter_trace(JSTracer *trc, JSObject *obj)
-{
-    void *pdata = obj->as<NativeObject>().getPrivate();
-    if (!pdata)
-        return;
-
-    if (obj->as<NativeObject>().getSlot(JSSLOT_ITER_INDEX).toInt32() < 0) {
-        /*
-         * Native case: just mark the next property to visit. We don't need a
-         * barrier here because the pointer is updated via setPrivate, which
-         * always takes a barrier.
-         */
-        Shape *tmp = static_cast<Shape *>(pdata);
-        MarkShapeUnbarriered(trc, &tmp, "prop iter shape");
-        obj->as<NativeObject>().setPrivateUnbarriered(tmp);
-    } else {
-        /* Non-native case: mark each id in the JSIdArray private. */
-        JSIdArray *ida = (JSIdArray *) pdata;
-        MarkIdRange(trc, ida->length, ida->vector, "prop iter");
-    }
-}
-
-static const Class prop_iter_class = {
-    "PropertyIterator",
-    JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(1),
-    JS_PropertyStub,         /* addProperty */
-    JS_DeletePropertyStub,   /* delProperty */
-    JS_PropertyStub,         /* getProperty */
-    JS_StrictPropertyStub,   /* setProperty */
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    JS_ConvertStub,
-    prop_iter_finalize,
-    nullptr,        /* call        */
-    nullptr,        /* hasInstance */
-    nullptr,        /* construct   */
-    prop_iter_trace
-};
-
-JS_PUBLIC_API(JSObject *)
-JS_NewPropertyIterator(JSContext *cx, HandleObject obj)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj);
-
-    RootedNativeObject iterobj(cx, NewNativeObjectWithClassProto(cx, &prop_iter_class,
-                                                                 nullptr, obj));
-    if (!iterobj)
-        return nullptr;
-
-    int index;
-    if (obj->isNative()) {
-        /* Native case: start with the last property in obj. */
-        iterobj->setPrivateGCThing(obj->lastProperty());
-        index = -1;
-    } else {
-        /* Non-native case: enumerate a JSIdArray and keep it via private. */
-        JSIdArray *ida = JS_Enumerate(cx, obj);
-        if (!ida)
-            return nullptr;
-        iterobj->setPrivate((void *)ida);
-        index = ida->length;
-    }
-
-    /* iterobj cannot escape to other threads here. */
-    iterobj->setSlot(JSSLOT_ITER_INDEX, Int32Value(index));
-    return iterobj;
-}
-
-JS_PUBLIC_API(bool)
-JS_NextProperty(JSContext *cx, HandleObject iterobj, MutableHandleId idp)
-{
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, iterobj);
-    int32_t i = iterobj->as<NativeObject>().getSlot(JSSLOT_ITER_INDEX).toInt32();
-    if (i < 0) {
-        /* Native case: private data is a property tree node pointer. */
-        MOZ_ASSERT(iterobj->getParent()->isNative());
-        Shape *shape = static_cast<Shape *>(iterobj->as<NativeObject>().getPrivate());
-
-        while (shape->previous() && !shape->enumerable())
-            shape = shape->previous();
-
-        if (!shape->previous()) {
-            MOZ_ASSERT(shape->isEmptyShape());
-            idp.set(JSID_VOID);
-        } else {
-            iterobj->as<NativeObject>().setPrivateGCThing(const_cast<Shape *>(shape->previous().get()));
-            idp.set(shape->propid());
-        }
-    } else {
-        /* Non-native case: use the ida enumerated when iterobj was created. */
-        JSIdArray *ida = (JSIdArray *) iterobj->as<NativeObject>().getPrivate();
-        MOZ_ASSERT(i <= ida->length);
-        STATIC_ASSUME(i <= ida->length);
-        if (i == 0) {
-            idp.set(JSID_VOID);
-        } else {
-            idp.set(ida->vector[--i]);
-            iterobj->as<NativeObject>().setSlot(JSSLOT_ITER_INDEX, Int32Value(i));
-        }
-    }
-    return true;
-}
-
 JS_PUBLIC_API(jsval)
 JS_GetReservedSlot(JSObject *obj, uint32_t index)
 {
     return obj->fakeNativeGetReservedSlot(index);
 }
 
 JS_PUBLIC_API(void)
 JS_SetReservedSlot(JSObject *obj, uint32_t index, Value value)
@@ -3894,22 +3795,24 @@ JS_NewFunctionById(JSContext *cx, JSNati
     RootedAtom name(cx, JSID_TO_ATOM(id));
     JSFunction::Flags funFlags = JSAPIToJSFunctionFlags(flags);
     return NewFunction(cx, NullPtr(), native, nargs, funFlags, parent, name);
 }
 
 JS_PUBLIC_API(JSFunction *)
 JS::GetSelfHostedFunction(JSContext *cx, const char *selfHostedName, HandleId id, unsigned nargs)
 {
-    MOZ_ASSERT(JSID_IS_STRING(id));
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
-    RootedAtom name(cx, JSID_TO_ATOM(id));
+    RootedAtom name(cx, IdToFunctionName(cx, id));
+    if (!name)
+        return nullptr;
+
     RootedAtom shName(cx, Atomize(cx, selfHostedName, strlen(selfHostedName)));
     if (!shName)
         return nullptr;
     RootedValue funVal(cx);
     if (!cx->global()->getSelfHostedFunction(cx, shName, name, nargs, &funVal))
         return nullptr;
     return &funVal.toObject().as<JSFunction>();
 }
@@ -4063,31 +3966,21 @@ js_generic_native_method_dispatcher(JSCo
 JS_PUBLIC_API(bool)
 JS_DefineFunctions(JSContext *cx, HandleObject obj, const JSFunctionSpec *fs)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
+    RootedId id(cx);
     for (; fs->name; fs++) {
-        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)
+        if (!PropertySpecNameToId(cx, fs->name, &id))
             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.
          */
         unsigned flags = fs->flags;
         if (flags & JSFUN_GENERIC_NATIVE) {
             // We require that any consumers using JSFUN_GENERIC_NATIVE stash
             // the prototype and constructor in the global slots before invoking
@@ -4119,18 +4012,21 @@ JS_DefineFunctions(JSContext *cx, Handle
          */
         if (fs->selfHostedName) {
             MOZ_ASSERT(!fs->call.op);
             MOZ_ASSERT(!fs->call.info);
 
             RootedAtom shName(cx, Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName)));
             if (!shName)
                 return false;
+            RootedAtom name(cx, IdToFunctionName(cx, id));
+            if (!name)
+                return false;
             RootedValue funVal(cx);
-            if (!cx->global()->getSelfHostedFunction(cx, shName, atom, fs->nargs, &funVal))
+            if (!cx->global()->getSelfHostedFunction(cx, shName, name, fs->nargs, &funVal))
                 return false;
             if (!JSObject::defineGeneric(cx, obj, id, funVal, nullptr, nullptr, flags))
                 return false;
         } else {
             JSFunction *fun = DefineFunction(cx, obj, id, fs->call.op, fs->nargs, flags);
             if (!fun)
                 return false;
             if (fs->call.info)
@@ -5623,16 +5519,43 @@ JS::GetSymbolCode(Handle<Symbol*> symbol
 }
 
 JS_PUBLIC_API(JS::Symbol *)
 JS::GetWellKnownSymbol(JSContext *cx, JS::SymbolCode which)
 {
     return cx->runtime()->wellKnownSymbols->get(uint32_t(which));
 }
 
+static bool
+PropertySpecNameIsDigits(const char *s) {
+    if (JS::PropertySpecNameIsSymbol(s))
+        return false;
+    if (!*s)
+        return false;
+    for (; *s; s++) {
+        if (*s < '0' || *s > '9')
+            return false;
+    }
+    return true;
+}
+
+JS_PUBLIC_API(bool)
+JS::PropertySpecNameEqualsId(const char *name, HandleId id)
+{
+    if (JS::PropertySpecNameIsSymbol(name)) {
+        if (!JSID_IS_SYMBOL(id))
+            return false;
+        Symbol *sym = JSID_TO_SYMBOL(id);
+        return sym->isWellKnownSymbol() && sym->code() == PropertySpecNameToSymbolCode(name);
+    }
+
+    MOZ_ASSERT(!PropertySpecNameIsDigits(name));
+    return JSID_IS_ATOM(id) && JS_FlatStringEqualsAscii(JSID_TO_ATOM(id), name);
+}
+
 JS_PUBLIC_API(bool)
 JS_Stringify(JSContext *cx, MutableHandleValue vp, HandleObject replacer,
              HandleValue space, JSONWriteCallback callback, void *data)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, replacer, space);
     StringBuffer sb(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2470,25 +2470,37 @@ struct JSFunctionSpec {
 #define JS_FS_END JS_FS(nullptr,nullptr,0,0)
 
 /*
  * Initializer macros for a JSFunctionSpec array element. JS_FN (whose name pays
  * homage to the old JSNative/JSFastNative split) simply adds the flag
  * JSFUN_STUB_GSOPS. JS_FNINFO allows the simple adding of
  * JSJitInfos. JS_SELF_HOSTED_FN declares a self-hosted function. Finally
  * JS_FNSPEC has slots for all the fields.
+ *
+ * The _SYM variants allow defining a function with a symbol key rather than a
+ * string key. For example, use JS_SYM_FN(iterator, ...) to define an
+ * @@iterator method.
  */
 #define JS_FS(name,call,nargs,flags)                                          \
     JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr)
 #define JS_FN(name,call,nargs,flags)                                          \
     JS_FNSPEC(name, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
+#define JS_SYM_FN(name,call,nargs,flags)                                      \
+    JS_SYM_FNSPEC(symbol, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
 #define JS_FNINFO(name,call,info,nargs,flags)                                 \
     JS_FNSPEC(name, call, info, nargs, flags, nullptr)
 #define JS_SELF_HOSTED_FN(name,selfHostedName,nargs,flags)                    \
     JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName)
+#define JS_SELF_HOSTED_SYM_FN(symbol, selfHostedName, nargs, flags)           \
+    JS_SYM_FNSPEC(symbol, nullptr, nullptr, nargs, flags, selfHostedName)
+#define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName)       \
+    JS_FNSPEC(reinterpret_cast<const char *>(                                 \
+                  uint32_t(::JS::SymbolCode::symbol) + 1),                    \
+              call, info, nargs, flags, selfHostedName)
 #define JS_FNSPEC(name,call,info,nargs,flags,selfHostedName)                  \
     {name, {call, info}, nargs, flags, selfHostedName}
 
 extern JS_PUBLIC_API(JSObject *)
 JS_InitClass(JSContext *cx, JS::HandleObject obj, JS::HandleObject parent_proto,
              const JSClass *clasp, JSNative constructor, unsigned nargs,
              const JSPropertySpec *ps, const JSFunctionSpec *fs,
              const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs);
@@ -3300,32 +3312,16 @@ JS_CreateMappedArrayBufferContents(int f
  * release the resource used by the object.
  */
 extern JS_PUBLIC_API(void)
 JS_ReleaseMappedArrayBufferContents(void *contents, size_t length);
 
 extern JS_PUBLIC_API(JSIdArray *)
 JS_Enumerate(JSContext *cx, JS::HandleObject obj);
 
-/*
- * Create an object to iterate over enumerable properties of obj, in arbitrary
- * property definition order.  NB: This differs from longstanding for..in loop
- * order, which uses order of property definition in obj.
- */
-extern JS_PUBLIC_API(JSObject *)
-JS_NewPropertyIterator(JSContext *cx, JS::Handle<JSObject*> obj);
-
-/*
- * Return true on success with *idp containing the id of the next enumerable
- * property to visit using iterobj, or JSID_IS_VOID if there is no such property
- * left to visit.  Return false on error.
- */
-extern JS_PUBLIC_API(bool)
-JS_NextProperty(JSContext *cx, JS::HandleObject iterobj, JS::MutableHandleId idp);
-
 extern JS_PUBLIC_API(jsval)
 JS_GetReservedSlot(JSObject *obj, uint32_t index);
 
 extern JS_PUBLIC_API(void)
 JS_SetReservedSlot(JSObject *obj, uint32_t index, jsval v);
 
 /************************************************************************/
 
@@ -4505,16 +4501,41 @@ GetSymbolCode(Handle<Symbol*> symbol);
  * Get one of the well-known symbols defined by ES6. A single set of well-known
  * symbols is shared by all compartments in a JSRuntime.
  *
  * `which` must be in the range [0, WellKnownSymbolLimit).
  */
 JS_PUBLIC_API(Symbol *)
 GetWellKnownSymbol(JSContext *cx, SymbolCode which);
 
+/*
+ * Return true if the given JSPropertySpec::name or JSFunctionSpec::name value
+ * is actually a symbol code and not a string. See JS_SYM_FN.
+ */
+inline bool
+PropertySpecNameIsSymbol(const char *name)
+{
+    uintptr_t u = reinterpret_cast<uintptr_t>(name);
+    return u != 0 && u - 1 < WellKnownSymbolLimit;
+}
+
+JS_PUBLIC_API(bool)
+PropertySpecNameEqualsId(const char *name, HandleId id);
+
+/*
+ * Create a jsid that does not need to be marked for GC.
+ *
+ * 'name' is a JSPropertySpec::name or JSFunctionSpec::name value. The
+ * resulting jsid, on success, is either an interned string or a well-known
+ * symbol; either way it is immune to GC so there is no need to visit *idp
+ * during GC marking.
+ */
+JS_PUBLIC_API(bool)
+PropertySpecNameToPermanentId(JSContext *cx, const char *name, jsid *idp);
+
 } /* namespace JS */
 
 /************************************************************************/
 /*
  * JSON functions
  */
 typedef bool (* JSONWriteCallback)(const char16_t *buf, uint32_t len, void *data);
 
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -53,17 +53,16 @@ JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRIN
 FOR_EACH_COMMON_PROPERTYNAME(CONST_CHAR_STR)
 #undef CONST_CHAR_STR
 
 /* Constant strings that are not atomized. */
 const char js_break_str[]           = "break";
 const char js_case_str[]            = "case";
 const char js_catch_str[]           = "catch";
 const char js_class_str[]           = "class";
-const char js_close_str[]           = "close";
 const char js_const_str[]           = "const";
 const char js_continue_str[]        = "continue";
 const char js_debugger_str[]        = "debugger";
 const char js_default_str[]         = "default";
 const char js_do_str[]              = "do";
 const char js_else_str[]            = "else";
 const char js_enum_str[]            = "enum";
 const char js_export_str[]          = "export";
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1104,21 +1104,20 @@ JSContext::JSContext(JSRuntime *rt)
     reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
     resolvingList(nullptr),
     generatingError(false),
     savedFrameChains_(),
     cycleDetectorSet(MOZ_THIS_IN_INITIALIZER_LIST()),
     data(nullptr),
     data2(nullptr),
     outstandingRequests(0),
-    jitIsBroken(false),
+    jitIsBroken(false)
 #ifdef MOZ_TRACE_JSCALLS
-    functionCallback(nullptr),
+  , functionCallback(nullptr)
 #endif
-    innermostGenerator_(nullptr)
 {
     MOZ_ASSERT(static_cast<ContextFriendFields*>(this) ==
                ContextFriendFields::get(this));
 }
 
 JSContext::~JSContext()
 {
     /* Free the stuff hanging off of cx. */
@@ -1141,33 +1140,16 @@ JSContext::getPendingException(MutableHa
 }
 
 bool
 JSContext::isThrowingOutOfMemory()
 {
     return throwing && unwrappedException_ == StringValue(names().outOfMemory);
 }
 
-void
-JSContext::enterGenerator(JSGenerator *gen)
-{
-    MOZ_ASSERT(!gen->prevGenerator);
-    gen->prevGenerator = innermostGenerator_;
-    innermostGenerator_ = gen;
-}
-
-void
-JSContext::leaveGenerator(JSGenerator *gen)
-{
-    MOZ_ASSERT(innermostGenerator_ == gen);
-    innermostGenerator_ = innermostGenerator_->prevGenerator;
-    gen->prevGenerator = nullptr;
-}
-
-
 bool
 JSContext::saveFrameChain()
 {
     if (!savedFrameChains_.append(SavedFrameChain(compartment(), enterCompartmentDepth_)))
         return false;
 
     if (Activation *act = mainThread().activation())
         act->saveFrameChain();
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -279,16 +279,17 @@ struct ThreadSafeContext : ContextFriend
     void reportAllocationOverflow() {
         js_ReportAllocationOverflow(this);
     }
 
     // Accessors for immutable runtime data.
     JSAtomState &names() { return *runtime_->commonNames; }
     StaticStrings &staticStrings() { return *runtime_->staticStrings; }
     AtomSet &permanentAtoms() { return *runtime_->permanentAtoms; }
+    WellKnownSymbols &wellKnownSymbols() { return *runtime_->wellKnownSymbols; }
     const JS::AsmJSCacheOps &asmJSCacheOps() { return runtime_->asmJSCacheOps; }
     PropertyName *emptyString() { return runtime_->emptyString; }
     FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
     void *runtimeAddressForJit() { return runtime_; }
     void *runtimeAddressOfInterrupt() { return &runtime_->interrupt; }
     void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
     void *stackLimitAddressForJitCode(StackKind kind);
     size_t gcSystemPageSize() { return gc::SystemPageSize(); }
@@ -546,24 +547,17 @@ struct JSContext : public js::ExclusiveC
     void minorGC(JS::gcreason::Reason reason) {
         runtime_->gc.minorGC(this, reason);
     }
 
     void gcIfNeeded() {
         runtime_->gc.gcIfNeeded(this);
     }
 
-  private:
-    /* Innermost-executing generator or null if no generator are executing. */
-    JSGenerator *innermostGenerator_;
   public:
-    JSGenerator *innermostGenerator() const { return innermostGenerator_; }
-    void enterGenerator(JSGenerator *gen);
-    void leaveGenerator(JSGenerator *gen);
-
     bool isExceptionPending() {
         return throwing;
     }
 
     MOZ_WARN_UNUSED_RESULT
     bool getPendingException(JS::MutableHandleValue rval);
 
     bool isThrowingOutOfMemory();
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -966,17 +966,17 @@ CopyStringChars(JSContext *cx, char16_t 
 
 inline void
 CopyFlatStringChars(char16_t *dest, JSFlatString *s, size_t len)
 {
     CopyLinearStringChars(dest, FlatStringToLinearString(s), len);
 }
 
 JS_FRIEND_API(bool)
-GetPropertyKeys(JSContext *cx, JSObject *obj, unsigned flags, JS::AutoIdVector *props);
+GetPropertyKeys(JSContext *cx, JS::HandleObject obj, unsigned flags, JS::AutoIdVector *props);
 
 JS_FRIEND_API(bool)
 AppendUnique(JSContext *cx, JS::AutoIdVector &base, JS::AutoIdVector &others);
 
 JS_FRIEND_API(bool)
 GetGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, JS::Value *vp);
 
 JS_FRIEND_API(bool)
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2058,26 +2058,50 @@ js::CloneFunctionObject(JSContext *cx, H
      * no enclosing lexical scope (only the global scope).
      */
     if (cloneRoot->isInterpreted() && !CloneFunctionScript(cx, fun, cloneRoot, newKindArg))
         return nullptr;
 
     return cloneRoot;
 }
 
+/*
+ * Return an atom for use as the name of a builtin method with the given
+ * property id.
+ *
+ * Function names are always strings. If id is the well-known @@iterator
+ * symbol, this returns "[Symbol.iterator]".
+ *
+ * Implements step 4 of SetFunctionName in ES6 draft rev 27 (24 Aug 2014).
+ */
+JSAtom *
+js::IdToFunctionName(JSContext *cx, HandleId id)
+{
+    if (JSID_IS_ATOM(id))
+        return JSID_TO_ATOM(id);
+
+    if (JSID_IS_SYMBOL(id)) {
+        RootedAtom desc(cx, JSID_TO_SYMBOL(id)->description());
+        StringBuffer sb(cx);
+        if (!sb.append('[') || !sb.append(desc) || !sb.append(']'))
+            return nullptr;
+        return sb.finishAtom();
+    }
+
+    RootedValue idv(cx, IdToValue(id));
+    return ToAtom<CanGC>(cx, idv);
+}
+
 JSFunction *
 js::DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native,
                    unsigned nargs, unsigned flags, AllocKind allocKind /* = FinalizeKind */,
                    NewObjectKind newKind /* = GenericObject */)
 {
     PropertyOp gop;
     StrictPropertyOp sop;
-
-    RootedFunction fun(cx);
-
     if (flags & JSFUN_STUB_GSOPS) {
         /*
          * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
          * the defined property's attributes. This allows us to encode another,
          * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
          * for more on this.
          */
         flags &= ~JSFUN_STUB_GSOPS;
@@ -2088,18 +2112,23 @@ js::DefineFunction(JSContext *cx, Handle
         sop = nullptr;
     }
 
     JSFunction::Flags funFlags;
     if (!native)
         funFlags = JSFunction::INTERPRETED_LAZY;
     else
         funFlags = JSAPIToJSFunctionFlags(flags);
-    RootedAtom atom(cx, JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : nullptr);
-    fun = NewFunction(cx, NullPtr(), native, nargs, funFlags, obj, atom, allocKind, newKind);
+
+    RootedAtom atom(cx, IdToFunctionName(cx, id));
+    if (!atom)
+        return nullptr;
+
+    RootedFunction fun(cx, NewFunction(cx, NullPtr(), native, nargs, funFlags, obj, atom,
+                                       allocKind, newKind));
     if (!fun)
         return nullptr;
 
     RootedValue funVal(cx, ObjectValue(*fun));
     if (!JSObject::defineGeneric(cx, obj, id, funVal, gop, sop, flags & ~JSFUN_FLAGS_MASK))
         return nullptr;
 
     return fun;
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -516,16 +516,19 @@ NewFunction(ExclusiveContext *cx, Handle
 
 // If proto is nullptr, Function.prototype is used instead.
 extern JSFunction *
 NewFunctionWithProto(ExclusiveContext *cx, HandleObject funobj, JSNative native, unsigned nargs,
                      JSFunction::Flags flags, HandleObject parent, HandleAtom atom,
                      JSObject *proto, gc::AllocKind allocKind = JSFunction::FinalizeKind,
                      NewObjectKind newKind = GenericObject);
 
+extern JSAtom *
+IdToFunctionName(JSContext *cx, HandleId id);
+
 extern JSFunction *
 DefineFunction(JSContext *cx, HandleObject obj, HandleId id, JSNative native,
                unsigned nargs, unsigned flags,
                gc::AllocKind allocKind = JSFunction::FinalizeKind,
                NewObjectKind newKind = GenericObject);
 
 bool
 FunctionHasResolveHook(const JSAtomState &atomState, PropertyName *name);
@@ -567,17 +570,16 @@ class FunctionExtended : public JSFuncti
     HeapValue extendedSlots[NUM_EXTENDED_SLOTS];
 };
 
 extern JSFunction *
 CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
                     gc::AllocKind kind = JSFunction::FinalizeKind,
                     NewObjectKind newKindArg = GenericObject);
 
-
 extern bool
 FindBody(JSContext *cx, HandleFunction fun, HandleLinearString src, size_t *bodyStart,
          size_t *bodyEnd);
 
 } // namespace js
 
 inline js::FunctionExtended *
 JSFunction::toExtended()
@@ -659,17 +661,17 @@ extern const JSFunctionSpec function_met
 } /* namespace js */
 
 extern bool
 js_fun_apply(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern bool
 js_fun_call(JSContext *cx, unsigned argc, js::Value *vp);
 
-extern JSObject*
+extern JSObject *
 js_fun_bind(JSContext *cx, js::HandleObject target, js::HandleValue thisArg,
             js::Value *boundArgs, unsigned argslen);
 
 #ifdef DEBUG
 namespace JS {
 namespace detail {
 
 JS_PUBLIC_API(void)
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2259,16 +2259,18 @@ types::UseNewType(JSContext *cx, JSScrip
      *
      * Sub1.prototype = new Super();
      * Sub2.prototype = new Super();
      *
      * Using distinct type objects for the particular prototypes of Sub1 and
      * Sub2 lets us continue to distinguish the two subclasses and any extra
      * properties added to those prototype objects.
      */
+    if (script->isGenerator())
+        return false;
     if (JSOp(*pc) != JSOP_NEW)
         return false;
     pc += JSOP_NEW_LENGTH;
     if (JSOp(*pc) == JSOP_SETPROP) {
         jsid id = GetAtomId(cx, script, pc, 0);
         if (id == id_prototype(cx))
             return true;
     }
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -266,17 +266,17 @@ struct SortComparatorIds
         *lessOrEqualp = (result <= 0);
         return true;
     }
 };
 
 #endif /* JS_MORE_DETERMINISTIC */
 
 static bool
-Snapshot(JSContext *cx, JSObject *pobj_, unsigned flags, AutoIdVector *props)
+Snapshot(JSContext *cx, HandleObject pobj_, unsigned flags, AutoIdVector *props)
 {
     IdSet ht(cx);
     if (!ht.init(32))
         return false;
 
     RootedObject pobj(cx, pobj_);
 
     do {
@@ -295,17 +295,17 @@ Snapshot(JSContext *cx, JSObject *pobj_,
                 if (flags & JSITER_OWNONLY) {
                     if (flags & JSITER_HIDDEN) {
                         // This gets all property keys, both strings and
                         // symbols.  The call to Enumerate in the loop below
                         // will filter out unwanted keys, per the flags.
                         if (!Proxy::ownPropertyKeys(cx, pobj, proxyProps))
                             return false;
                     } else {
-                        if (!Proxy::keys(cx, pobj, proxyProps))
+                        if (!Proxy::getOwnEnumerablePropertyKeys(cx, pobj, proxyProps))
                             return false;
                     }
                 } else {
                     if (!Proxy::enumerate(cx, pobj, proxyProps))
                         return false;
                 }
 
                 for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
@@ -391,17 +391,17 @@ js::VectorToIdArray(JSContext *cx, AutoI
     jsid *v = props.begin();
     for (int i = 0; i < ida->length; i++)
         ida->vector[i].init(v[i]);
     *idap = ida;
     return true;
 }
 
 JS_FRIEND_API(bool)
-js::GetPropertyKeys(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props)
+js::GetPropertyKeys(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector *props)
 {
     return Snapshot(cx, obj,
                     flags & (JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY),
                     props);
 }
 
 size_t sCustomIteratorCount = 0;
 
@@ -1004,19 +1004,16 @@ const Class StringIteratorObject::class_
 };
 
 static const JSFunctionSpec string_iterator_methods[] = {
     JS_SELF_HOSTED_FN("@@iterator", "StringIteratorIdentity", 0, 0),
     JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0),
     JS_FS_END
 };
 
-static bool
-CloseLegacyGenerator(JSContext *cx, HandleObject genobj);
-
 bool
 js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
 {
     /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
     MOZ_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
 
     RootedObject obj(cx);
     if (vp.isObject()) {
@@ -1053,17 +1050,24 @@ js::CloseIterator(JSContext *cx, HandleO
 
             /*
              * Reset the enumerator; it may still be in the cached iterators
              * for this thread, and can be reused.
              */
             ni->props_cursor = ni->props_array;
         }
     } else if (obj->is<LegacyGeneratorObject>()) {
-        return CloseLegacyGenerator(cx, obj);
+        Rooted<LegacyGeneratorObject*> genObj(cx, &obj->as<LegacyGeneratorObject>());
+        if (genObj->isClosed())
+            return true;
+        if (genObj->isRunning() || genObj->isClosing()) {
+            // Nothing sensible to do.
+            return true;
+        }
+        return LegacyGeneratorObject::close(cx, obj);
     }
     return true;
 }
 
 bool
 js::UnwindIteratorForException(JSContext *cx, HandleObject obj)
 {
     RootedValue v(cx);
@@ -1506,516 +1510,16 @@ ForOfIterator::materializeArrayIterator(
         return false;
 
     index = NOT_ARRAY;
     // Result of call to ArrayValuesAt must be an object.
     iterator = &args.rval().toObject();
     return true;
 }
 
-/*** Generators **********************************************************************************/
-
-template<typename T>
-static void
-FinalizeGenerator(FreeOp *fop, JSObject *obj)
-{
-    MOZ_ASSERT(obj->is<T>());
-    JSGenerator *gen = obj->as<T>().getGenerator();
-    MOZ_ASSERT(gen);
-    // gen is open when a script has not called its close method while
-    // explicitly manipulating it.
-    MOZ_ASSERT(gen->state == JSGEN_NEWBORN ||
-               gen->state == JSGEN_CLOSED ||
-               gen->state == JSGEN_OPEN);
-    // If gen->state is JSGEN_CLOSED, gen->fp may be nullptr.
-    if (gen->fp)
-        JS_POISON(gen->fp, JS_SWEPT_FRAME_PATTERN, sizeof(InterpreterFrame));
-    JS_POISON(gen, JS_SWEPT_FRAME_PATTERN, sizeof(JSGenerator));
-    fop->free_(gen);
-}
-
-static void
-MarkGeneratorFrame(JSTracer *trc, JSGenerator *gen)
-{
-    gen->obj = MaybeForwarded(gen->obj.get());
-    MarkObject(trc, &gen->obj, "Generator Object");
-    MarkValueRange(trc,
-                   HeapValueify(gen->fp->generatorArgsSnapshotBegin()),
-                   HeapValueify(gen->fp->generatorArgsSnapshotEnd()),
-                   "Generator Floating Args");
-    gen->fp->mark(trc);
-    MarkValueRange(trc,
-                   HeapValueify(gen->fp->generatorSlotsSnapshotBegin()),
-                   HeapValueify(gen->regs.sp),
-                   "Generator Floating Stack");
-}
-
-static void
-GeneratorWriteBarrierPre(JSContext *cx, JSGenerator *gen)
-{
-    JS::Zone *zone = cx->zone();
-    if (zone->needsIncrementalBarrier())
-        MarkGeneratorFrame(zone->barrierTracer(), gen);
-}
-
-static void
-GeneratorWriteBarrierPost(JSContext *cx, JSGenerator *gen)
-{
-#ifdef JSGC_GENERATIONAL
-    cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(gen->obj);
-#endif
-}
-
-/*
- * Only mark generator frames/slots when the generator is not active on the
- * stack or closed. Barriers when copying onto the stack or closing preserve
- * gc invariants.
- */
-static bool
-GeneratorHasMarkableFrame(JSGenerator *gen)
-{
-    return gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN;
-}
-
-/*
- * When a generator is closed, the GC things reachable from the contained frame
- * and slots become unreachable and thus require a write barrier.
- */
-static void
-SetGeneratorClosed(JSContext *cx, JSGenerator *gen)
-{
-    MOZ_ASSERT(gen->state != JSGEN_CLOSED);
-    if (GeneratorHasMarkableFrame(gen))
-        GeneratorWriteBarrierPre(cx, gen);
-    gen->state = JSGEN_CLOSED;
-
-#ifdef DEBUG
-    MakeRangeGCSafe(gen->fp->generatorArgsSnapshotBegin(),
-                    gen->fp->generatorArgsSnapshotEnd());
-    MakeRangeGCSafe(gen->fp->generatorSlotsSnapshotBegin(),
-                    gen->regs.sp);
-    PodZero(&gen->regs, 1);
-    gen->fp = nullptr;
-#endif
-}
-
-template<typename T>
-static void
-TraceGenerator(JSTracer *trc, JSObject *obj)
-{
-    MOZ_ASSERT(obj->is<T>());
-    JSGenerator *gen = obj->as<T>().getGenerator();
-    MOZ_ASSERT(gen);
-    if (GeneratorHasMarkableFrame(gen))
-        MarkGeneratorFrame(trc, gen);
-}
-
-GeneratorState::GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState)
-  : RunState(cx, Generator, gen->fp->script()),
-    cx_(cx),
-    gen_(gen),
-    futureState_(futureState),
-    entered_(false)
-{ }
-
-GeneratorState::~GeneratorState()
-{
-    gen_->fp->setSuspended();
-
-    if (entered_)
-        cx_->leaveGenerator(gen_);
-}
-
-InterpreterFrame *
-GeneratorState::pushInterpreterFrame(JSContext *cx)
-{
-    /*
-     * Write barrier is needed since the generator stack can be updated,
-     * and it's not barriered in any other way. We need to do it before
-     * gen->state changes, which can cause us to trace the generator
-     * differently.
-     *
-     * We could optimize this by setting a bit on the generator to signify
-     * that it has been marked. If this bit has already been set, there is no
-     * need to mark again. The bit would have to be reset before the next GC,
-     * or else some kind of epoch scheme would have to be used.
-     */
-    GeneratorWriteBarrierPre(cx, gen_);
-    gen_->state = futureState_;
-
-    gen_->fp->clearSuspended();
-
-    cx->enterGenerator(gen_);   /* OOM check above. */
-    entered_ = true;
-    return gen_->fp;
-}
-
-const Class LegacyGeneratorObject::class_ = {
-    "Generator",
-    JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
-    JS_PropertyStub,         /* addProperty */
-    JS_DeletePropertyStub,   /* delProperty */
-    JS_PropertyStub,         /* getProperty */
-    JS_StrictPropertyStub,   /* setProperty */
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    JS_ConvertStub,
-    FinalizeGenerator<LegacyGeneratorObject>,
-    nullptr,                 /* call        */
-    nullptr,                 /* hasInstance */
-    nullptr,                 /* construct   */
-    TraceGenerator<LegacyGeneratorObject>,
-    JS_NULL_CLASS_SPEC,
-    {
-        nullptr,             /* outerObject    */
-        nullptr,             /* innerObject    */
-        iterator_iteratorObject,
-    }
-};
-
-const Class StarGeneratorObject::class_ = {
-    "Generator",
-    JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
-    JS_PropertyStub,         /* addProperty */
-    JS_DeletePropertyStub,   /* delProperty */
-    JS_PropertyStub,         /* getProperty */
-    JS_StrictPropertyStub,   /* setProperty */
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    JS_ConvertStub,
-    FinalizeGenerator<StarGeneratorObject>,
-    nullptr,                 /* call        */
-    nullptr,                 /* hasInstance */
-    nullptr,                 /* construct   */
-    TraceGenerator<StarGeneratorObject>,
-};
-
-/*
- * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
- * to the frame by which the generator function was activated.  Create a new
- * JSGenerator object, which contains its own InterpreterFrame that we populate
- * from *fp.  We know that upon return, the JSOP_GENERATOR opcode will return
- * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
- * if they are non-null.
- */
-JSObject *
-js_NewGenerator(JSContext *cx, const InterpreterRegs &stackRegs)
-{
-    MOZ_ASSERT(stackRegs.stackDepth() == 0);
-    InterpreterFrame *stackfp = stackRegs.fp();
-
-    MOZ_ASSERT(stackfp->script()->isGenerator());
-
-    Rooted<GlobalObject*> global(cx, &stackfp->global());
-    RootedNativeObject obj(cx);
-    if (stackfp->script()->isStarGenerator()) {
-        RootedValue pval(cx);
-        RootedObject fun(cx, stackfp->fun());
-        // FIXME: This would be faster if we could avoid doing a lookup to get
-        // the prototype for the instance.  Bug 906600.
-        if (!JSObject::getProperty(cx, fun, fun, cx->names().prototype, &pval))
-            return nullptr;
-        JSObject *proto = pval.isObject() ? &pval.toObject() : nullptr;
-        if (!proto) {
-            proto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
-            if (!proto)
-                return nullptr;
-        }
-        obj = NewNativeObjectWithGivenProto(cx, &StarGeneratorObject::class_, proto, global);
-    } else {
-        MOZ_ASSERT(stackfp->script()->isLegacyGenerator());
-        JSObject *proto = GlobalObject::getOrCreateLegacyGeneratorObjectPrototype(cx, global);
-        if (!proto)
-            return nullptr;
-        obj = NewNativeObjectWithGivenProto(cx, &LegacyGeneratorObject::class_, proto, global);
-    }
-    if (!obj)
-        return nullptr;
-
-    /* Load and compute stack slot counts. */
-    Value *stackvp = stackfp->generatorArgsSnapshotBegin();
-    unsigned vplen = stackfp->generatorArgsSnapshotEnd() - stackvp;
-
-    static_assert(sizeof(InterpreterFrame) % sizeof(HeapValue) == 0,
-                  "The Values stored after InterpreterFrame must be aligned.");
-    unsigned nvals = vplen + VALUES_PER_STACK_FRAME + stackfp->script()->nslots();
-    JSGenerator *gen = obj->zone()->pod_calloc_with_extra<JSGenerator, HeapValue>(nvals);
-    if (!gen)
-        return nullptr;
-
-    /* Cut up floatingStack space. */
-    HeapValue *genvp = gen->stackSnapshot();
-    SetValueRangeToUndefined((Value *)genvp, vplen);
-
-    InterpreterFrame *genfp = reinterpret_cast<InterpreterFrame *>(genvp + vplen);
-
-    /* Initialize JSGenerator. */
-    gen->obj.init(obj);
-    gen->state = JSGEN_NEWBORN;
-    gen->fp = genfp;
-    gen->prevGenerator = nullptr;
-
-    /* Copy from the stack to the generator's floating frame. */
-    gen->regs.rebaseFromTo(stackRegs, *genfp);
-    genfp->copyFrameAndValues<InterpreterFrame::DoPostBarrier>(cx, (Value *)genvp, stackfp,
-                                                         stackvp, stackRegs.sp);
-    genfp->setSuspended();
-    obj->setPrivate(gen);
-    return obj;
-}
-
-static void
-SetGeneratorClosed(JSContext *cx, JSGenerator *gen);
-
-typedef enum JSGeneratorOp {
-    JSGENOP_NEXT,
-    JSGENOP_SEND,
-    JSGENOP_THROW,
-    JSGENOP_CLOSE
-} JSGeneratorOp;
-
-/*
- * Start newborn or restart yielding generator and perform the requested
- * operation inside its frame.
- */
-static bool
-SendToGenerator(JSContext *cx, JSGeneratorOp op, HandleObject obj,
-                JSGenerator *gen, HandleValue arg, GeneratorKind generatorKind,
-                MutableHandleValue rval)
-{
-    MOZ_ASSERT(generatorKind == LegacyGenerator || generatorKind == StarGenerator);
-
-    if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NESTING_GENERATOR);
-        return false;
-    }
-
-    JSGeneratorState futureState;
-    MOZ_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
-    switch (op) {
-      case JSGENOP_NEXT:
-      case JSGENOP_SEND:
-        if (gen->state == JSGEN_OPEN) {
-            /*
-             * Store the argument to send as the result of the yield
-             * expression. The generator stack is not barriered, so we need
-             * write barriers here.
-             */
-            HeapValue::writeBarrierPre(gen->regs.sp[-1]);
-            gen->regs.sp[-1] = arg;
-            HeapValue::writeBarrierPost(gen->regs.sp[-1], &gen->regs.sp[-1]);
-        }
-        futureState = JSGEN_RUNNING;
-        break;
-
-      case JSGENOP_THROW:
-        cx->setPendingException(arg);
-        futureState = JSGEN_RUNNING;
-        break;
-
-      default:
-        MOZ_ASSERT(op == JSGENOP_CLOSE);
-        MOZ_ASSERT(generatorKind == LegacyGenerator);
-        cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
-        futureState = JSGEN_CLOSING;
-        break;
-    }
-
-    bool ok;
-    {
-        GeneratorState state(cx, gen, futureState);
-        ok = RunScript(cx, state);
-        if (!ok && gen->state == JSGEN_CLOSED)
-            return false;
-    }
-
-    if (gen->fp->isYielding()) {
-        /*
-         * Yield is ordinarily infallible, but ok can be false here if a
-         * Debugger.Frame.onPop hook fails.
-         */
-        MOZ_ASSERT(gen->state == JSGEN_RUNNING);
-        MOZ_ASSERT(op != JSGENOP_CLOSE);
-        gen->fp->clearYielding();
-        gen->state = JSGEN_OPEN;
-        GeneratorWriteBarrierPost(cx, gen);
-        rval.set(gen->fp->returnValue());
-        return ok;
-    }
-
-    if (ok) {
-        if (generatorKind == StarGenerator) {
-            // Star generators return a {value:FOO, done:true} object.
-            rval.set(gen->fp->returnValue());
-        } else {
-            MOZ_ASSERT(generatorKind == LegacyGenerator);
-
-            // Otherwise we discard the return value and throw a StopIteration
-            // if needed.
-            rval.setUndefined();
-            if (op != JSGENOP_CLOSE)
-                ok = ThrowStopIteration(cx);
-        }
-    }
-
-    SetGeneratorClosed(cx, gen);
-    return ok;
-}
-
-MOZ_ALWAYS_INLINE bool
-star_generator_next(JSContext *cx, CallArgs args)
-{
-    RootedObject thisObj(cx, &args.thisv().toObject());
-    JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator();
-
-    if (gen->state == JSGEN_CLOSED) {
-        RootedObject obj(cx, CreateItrResultObject(cx, JS::UndefinedHandleValue, true));
-        if (!obj)
-            return false;
-        args.rval().setObject(*obj);
-        return true;
-    }
-
-    return SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0), StarGenerator,
-                           args.rval());
-}
-
-MOZ_ALWAYS_INLINE bool
-star_generator_throw(JSContext *cx, CallArgs args)
-{
-    RootedObject thisObj(cx, &args.thisv().toObject());
-
-    JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator();
-    if (gen->state == JSGEN_CLOSED) {
-        cx->setPendingException(args.get(0));
-        return false;
-    }
-
-    return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), StarGenerator,
-                           args.rval());
-}
-
-MOZ_ALWAYS_INLINE bool
-legacy_generator_next(JSContext *cx, CallArgs args)
-{
-    RootedObject thisObj(cx, &args.thisv().toObject());
-
-    JSGenerator *gen = thisObj->as<LegacyGeneratorObject>().getGenerator();
-    if (gen->state == JSGEN_CLOSED)
-        return ThrowStopIteration(cx);
-
-    return SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0), LegacyGenerator,
-                           args.rval());
-}
-
-MOZ_ALWAYS_INLINE bool
-legacy_generator_throw(JSContext *cx, CallArgs args)
-{
-    RootedObject thisObj(cx, &args.thisv().toObject());
-
-    JSGenerator *gen = thisObj->as<LegacyGeneratorObject>().getGenerator();
-    if (gen->state == JSGEN_CLOSED) {
-        cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue());
-        return false;
-    }
-
-    return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), LegacyGenerator,
-                           args.rval());
-}
-
-static bool
-CloseLegacyGenerator(JSContext *cx, HandleObject obj, MutableHandleValue rval)
-{
-    MOZ_ASSERT(obj->is<LegacyGeneratorObject>());
-
-    JSGenerator *gen = obj->as<LegacyGeneratorObject>().getGenerator();
-
-    if (gen->state == JSGEN_CLOSED) {
-        rval.setUndefined();
-        return true;
-    }
-
-    if (gen->state == JSGEN_NEWBORN) {
-        SetGeneratorClosed(cx, gen);
-        rval.setUndefined();
-        return true;
-    }
-
-    return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JS::UndefinedHandleValue, LegacyGenerator,
-                           rval);
-}
-
-static bool
-CloseLegacyGenerator(JSContext *cx, HandleObject obj)
-{
-    RootedValue rval(cx);
-    return CloseLegacyGenerator(cx, obj, &rval);
-}
-
-MOZ_ALWAYS_INLINE bool
-legacy_generator_close(JSContext *cx, CallArgs args)
-{
-    RootedObject thisObj(cx, &args.thisv().toObject());
-
-    return CloseLegacyGenerator(cx, thisObj, args.rval());
-}
-
-template<typename T>
-MOZ_ALWAYS_INLINE bool
-IsObjectOfType(HandleValue v)
-{
-    return v.isObject() && v.toObject().is<T>();
-}
-
-template<typename T, NativeImpl Impl>
-static bool
-NativeMethod(JSContext *cx, unsigned argc, Value *vp)
-{
-    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_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_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
-};
-
-static JSObject*
-NewSingletonObjectWithObjectPrototype(JSContext *cx, Handle<GlobalObject *> global)
-{
-    JSObject *proto = global->getOrCreateObjectPrototype(cx);
-    if (!proto)
-        return nullptr;
-    return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject);
-}
-
-static JSObject*
-NewSingletonObjectWithFunctionPrototype(JSContext *cx, Handle<GlobalObject *> global)
-{
-    JSObject *proto = global->getOrCreateFunctionPrototype(cx);
-    if (!proto)
-        return nullptr;
-    return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject);
-}
-
 /* static */ bool
 GlobalObject::initIteratorClasses(JSContext *cx, Handle<GlobalObject *> global)
 {
     RootedObject iteratorProto(cx);
     Value iteratorProtoVal = global->getPrototype(JSProto_Iterator);
     if (iteratorProtoVal.isObject()) {
         iteratorProto = &iteratorProtoVal.toObject();
     } else {
@@ -2058,53 +1562,16 @@ GlobalObject::initIteratorClasses(JSCont
     if (global->getSlot(STRING_ITERATOR_PROTO).isUndefined()) {
         const Class *cls = &StringIteratorPrototypeClass;
         proto = global->createBlankPrototype(cx, cls);
         if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, string_iterator_methods))
             return false;
         global->setReservedSlot(STRING_ITERATOR_PROTO, ObjectValue(*proto));
     }
 
-    if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) {
-        proto = NewSingletonObjectWithObjectPrototype(cx, global);
-        if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods))
-            return false;
-        global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto));
-    }
-
-    if (global->getSlot(STAR_GENERATOR_OBJECT_PROTO).isUndefined()) {
-        RootedObject genObjectProto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
-        if (!genObjectProto)
-            return false;
-        if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, star_generator_methods))
-            return false;
-
-        RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
-        if (!genFunctionProto)
-            return false;
-        if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto))
-            return false;
-
-        RootedValue function(cx, global->getConstructor(JSProto_Function));
-        if (!function.toObjectOrNull())
-            return false;
-        RootedAtom name(cx, cx->names().GeneratorFunction);
-        RootedObject genFunction(cx, NewFunctionWithProto(cx, NullPtr(), Generator, 1,
-                                                          JSFunction::NATIVE_CTOR, global, name,
-                                                          &function.toObject()));
-        if (!genFunction)
-            return false;
-        if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
-            return false;
-
-        global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
-        global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction));
-        global->setPrototype(JSProto_GeneratorFunction, ObjectValue(*genFunctionProto));
-    }
-
     return GlobalObject::initStopIterationClass(cx, global);
 }
 
 /* static */ bool
 GlobalObject::initStopIterationClass(JSContext *cx, Handle<GlobalObject *> global)
 {
     if (!global->getPrototype(JSProto_StopIteration).isUndefined())
         return true;
@@ -2123,10 +1590,12 @@ GlobalObject::initStopIterationClass(JSC
 }
 
 JSObject *
 js_InitIteratorClasses(JSContext *cx, HandleObject obj)
 {
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     if (!GlobalObject::initIteratorClasses(cx, global))
         return nullptr;
+    if (!GlobalObject::initGeneratorClasses(cx, global))
+        return nullptr;
     return global->getIteratorPrototype();
 }
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -214,45 +214,12 @@ ThrowStopIteration(JSContext *cx);
  * 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, HandleValue value, bool done);
 
 } /* namespace js */
 
-/*
- * Generator state codes.
- */
-enum JSGeneratorState
-{
-    JSGEN_NEWBORN,  /* not yet started */
-    JSGEN_OPEN,     /* started by a .next() or .send(undefined) call */
-    JSGEN_RUNNING,  /* currently executing via .next(), etc., call */
-    JSGEN_CLOSING,  /* close method is doing asynchronous return */
-    JSGEN_CLOSED    /* closed, cannot be started or closed again */
-};
-
-struct JSGenerator
-{
-    js::HeapPtrObject    obj;
-    JSGeneratorState     state;
-    js::InterpreterRegs  regs;
-    JSGenerator          *prevGenerator;
-    js::InterpreterFrame *fp;
-#if JS_BITS_PER_WORD == 32
-    uint32_t             padding;
-#endif
-
-    js::HeapValue *stackSnapshot() {
-        static_assert(sizeof(JSGenerator) % sizeof(js::HeapValue) == 0,
-                      "The generator must have Value alignment for JIT access.");
-        return reinterpret_cast<js::HeapValue *>(this + 1);
-    }
-};
-
-extern JSObject *
-js_NewGenerator(JSContext *cx, const js::InterpreterRegs &regs);
-
 extern JSObject *
 js_InitIteratorClasses(JSContext *cx, js::HandleObject obj);
 
 #endif /* jsiter_h */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4245,18 +4245,16 @@ js_DumpInterpreterFrame(JSContext *cx, I
 
         fprintf(stderr, "  flags:");
         if (i.isConstructing())
             fprintf(stderr, " constructing");
         if (!i.isJit() && i.interpFrame()->isDebuggerFrame())
             fprintf(stderr, " debugger");
         if (i.isEvalFrame())
             fprintf(stderr, " eval");
-        if (!i.isJit() && i.interpFrame()->isYielding())
-            fprintf(stderr, " yielding");
         if (!i.isJit() && i.interpFrame()->isGeneratorFrame())
             fprintf(stderr, " generator");
         fputc('\n', stderr);
 
         fprintf(stderr, "  scopeChain: (JSObject *) %p\n", (void *) i.scopeChain());
 
         fputc('\n', stderr);
     }
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -293,16 +293,18 @@ IsJumpOpcode(JSOp op)
 static inline bool
 BytecodeFallsThrough(JSOp op)
 {
     switch (op) {
       case JSOP_GOTO:
       case JSOP_DEFAULT:
       case JSOP_RETURN:
       case JSOP_RETRVAL:
+      case JSOP_FINALYIELD:
+      case JSOP_FINALYIELDRVAL:
       case JSOP_THROW:
       case JSOP_TABLESWITCH:
         return false;
       case JSOP_GOSUB:
         /* These fall through indirectly, after executing a 'finally'. */
         return true;
       default:
         return true;
@@ -594,18 +596,28 @@ BytecodeFlowsToBitop(jsbytecode *pc)
 
 extern bool
 IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset);
 
 inline bool
 FlowsIntoNext(JSOp op)
 {
     /* JSOP_YIELD is considered to flow into the next instruction, like JSOP_CALL. */
-    return op != JSOP_RETRVAL && op != JSOP_RETURN && op != JSOP_THROW &&
-           op != JSOP_GOTO && op != JSOP_RETSUB;
+    switch (op) {
+      case JSOP_RETRVAL:
+      case JSOP_RETURN:
+      case JSOP_THROW:
+      case JSOP_GOTO:
+      case JSOP_RETSUB:
+      case JSOP_FINALYIELD:
+      case JSOP_FINALYIELDRVAL:
+        return false;
+      default:
+        return true;
+    }
 }
 
 inline bool
 IsArgOp(JSOp op)
 {
     return JOF_OPTYPE(op) == JOF_QARG;
 }
 
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -28,67 +28,130 @@ using JS::MutableHandleValue;
 using JS::NativeImpl;
 using JS::PrivateValue;
 using JS::Value;
 
 class RegExpGuard;
 class JS_FRIEND_API(Wrapper);
 
 /*
- * A proxy is a JSObject that implements generic behavior by providing custom
- * implementations for each object trap. The implementation for each trap is
- * provided by a C++ object stored on the proxy, known as its handler.
+ * A proxy is a JSObject with highly customizable behavior. ES6 specifies a
+ * single kind of proxy, but the customization mechanisms we use to implement
+ * ES6 Proxy objects are also useful wherever an object with weird behavior is
+ * wanted. Proxies are used to implement:
+ *
+ * -   the scope objects used by the Debugger's frame.eval() method
+ *     (see js::GetDebugScopeForFunction)
+ *
+ * -   the khuey hack, whereby a whole compartment can be blown away
+ *     even if other compartments hold references to objects in it
+ *     (see js::NukeCrossCompartmentWrappers)
  *
- * A major use case for proxies is to forward each trap to another object,
- * known as its target. The target can be an arbitrary C++ object. Not every
- * proxy has the notion of a target, however.
+ * -   XPConnect security wrappers, which protect chrome from malicious content
+ *     (js/xpconnect/wrappers)
+ *
+ * -   DOM objects with special property behavior, like named getters
+ *     (dom/bindings/Codegen.py generates these proxies from WebIDL)
  *
- * Proxy traps are grouped into fundamental and derived traps. Every proxy has
- * to at least provide implementations for the fundamental traps, but the
- * derived traps can be implemented in terms of the fundamental ones
- * BaseProxyHandler provides implementations of the derived traps in terms of
- * the (pure virtual) fundamental traps.
+ * -   semi-transparent use of objects that live in other processes
+ *     (CPOWs, implemented in js/ipc)
+ *
+ * ### Proxies and internal methods
+ *
+ * ES6 draft rev 27 (24 August 2014) specifies 14 internal methods. The runtime
+ * semantics of just about everything a script can do to an object is specified
+ * in terms of these internal methods. For example:
  *
- * In addition to the normal traps, there are two models for proxy prototype
- * chains. First, proxies may opt to use the standard prototype mechanism used
- * throughout the engine. To do so, simply pass a prototype to NewProxyObject()
- * at creation time. All prototype accesses will then "just work" to treat the
- * proxy as a "normal" object. Alternatively, if instead the proxy wishes to
- * implement more complicated prototype semantics (if, for example, it wants to
- * delegate the prototype lookup to a wrapped object), it may pass Proxy::LazyProto
- * as the prototype at create time and opt in to the trapped prototype system,
- * which guarantees that their trap will be called on any and every prototype
- * chain access of the object.
+ *     JS code                      ES6 internal method that gets called
+ *     ---------------------------  --------------------------------
+ *     obj.prop                     obj.[[Get]](obj, "prop")
+ *     "prop" in obj                obj.[[HasProperty]]("prop")
+ *     new obj()                    obj.[[Construct]](<empty argument List>)
+ *     for (k in obj) {}            obj.[[Enumerate]]()
+ *
+ * With regard to the implementation of these internal methods, there are three
+ * very different kinds of object in SpiderMonkey.
+ *
+ * 1.  Native objects' internal methods are implemented in js::baseops in
+ *     vm/NativeObject.cpp, with duplicate (but functionally identical)
+ *     implementations scattered through the ICs and JITs.
  *
- * This system is implemented with two traps: {get,set}PrototypeOf. The default
- * implementation of setPrototypeOf throws a TypeError. Since it is not possible
- * to create an object without a sense of prototype chain, handler implementors
- * must provide a getPrototypeOf trap if opting in to the dynamic prototype system.
+ * 2.  Certain non-native objects have internal methods that are implemented as
+ *     magical js::ObjectOps hooks. We're trying to get rid of these.
+ * 
+ * 3.  All other objects are proxies. A proxy's internal methods are
+ *     implemented in C++, as the virtual methods of a C++ object stored on the
+ *     proxy, known as its handler.
+ *
+ * This means that just about anything you do to a proxy will end up going
+ * through a C++ virtual method call. Possibly several. There's no reason the
+ * JITs and ICs can't specialize for particular proxies, based on the handler;
+ * but currently we don't do much of this, so the virtual method overhead
+ * typically is actually incurred.
+ *
+ * ### The proxy handler hierarchy
+ *
+ * A major use case for proxies is to forward each internal method call to
+ * another object, known as its target. The target can be an arbitrary JS
+ * object. Not every proxy has the notion of a target, however.
  *
  * To minimize code duplication, a set of abstract proxy handler classes is
- * provided, from which other handlers may inherit. These abstract classes
- * are organized in the following hierarchy:
+ * provided, from which other handlers may inherit. These abstract classes are
+ * organized in the following hierarchy:
+ *
+ *     BaseProxyHandler
+ *     |
+ *     DirectProxyHandler        // has a target
+ *     |
+ *     Wrapper                   // can be unwrapped, revealing target
+ *     |                         // (see js::CheckedUnwrap)
+ *     |
+ *     CrossCompartmentWrapper   // target is in another compartment;
+ *                               // implements membrane between compartments
+ *
+ * Example: Some DOM objects (including all the arraylike DOM objects) are
+ * implemented as proxies. Since these objects don't need to forward operations
+ * to any underlying JS object, DOMJSProxyHandler directly subclasses
+ * BaseProxyHandler.
+ *
+ * Gecko's security wrappers are examples of cross-compartment wrappers.
  *
- * BaseProxyHandler
- * |
- * DirectProxyHandler
- * |
- * Wrapper
+ * ### Proxy prototype chains
+ *
+ * In addition to the normal methods, there are two models for proxy prototype
+ * chains.
+ *
+ * 1.  Proxies can use the standard prototype mechanism used throughout the
+ *     engine. To do so, simply pass a prototype to NewProxyObject() at
+ *     creation time. All prototype accesses will then "just work" to treat the
+ *     proxy as a "normal" object.
+ *
+ * 2.  A proxy can implement more complicated prototype semantics (if, for
+ *     example, it wants to delegate the prototype lookup to a wrapped object)
+ *     by passing Proxy::LazyProto as the prototype at create time. This
+ *     guarantees that the getPrototypeOf() handler method will be called every
+ *     time the object's prototype chain is accessed.
+ *
+ *     This system is implemented with two methods: {get,set}PrototypeOf. The
+ *     default implementation of setPrototypeOf throws a TypeError. Since it is
+ *     not possible to create an object without a sense of prototype chain,
+ *     handlers must implement getPrototypeOf if opting in to the dynamic
+ *     prototype system.
  */
 
 /*
  * BaseProxyHandler is the most generic kind of proxy handler. It does not make
  * any assumptions about the target. Consequently, it does not provide any
- * default implementation for the fundamental traps. It does, however, implement
- * the derived traps in terms of the fundamental ones. This allows consumers of
- * this class to define any custom behavior they want.
+ * default implementation for most methods. As a convenience, a few high-level
+ * methods, like get() and set(), are given default implementations that work by
+ * calling the low-level methods, like getOwnPropertyDescriptor().
  *
- * Important: If you add a trap here, you should probably also add a Proxy::foo
- * entry point with an AutoEnterPolicy. If you don't, you need an explicit
- * override for the trap in SecurityWrapper. See bug 945826 comment 0.
+ * Important: If you add a method here, you should probably also add a
+ * Proxy::foo entry point with an AutoEnterPolicy. If you don't, you need an
+ * explicit override for the method in SecurityWrapper. See bug 945826 comment 0.
  */
 class JS_FRIEND_API(BaseProxyHandler)
 {
     /*
      * Sometimes it's desirable to designate groups of proxy handlers as "similar".
      * For this, we use the notion of a "family": A consumer-provided opaque pointer
      * that designates the larger group to which this proxy belongs.
      *
@@ -98,19 +161,20 @@ class JS_FRIEND_API(BaseProxyHandler)
     const void *mFamily;
 
     /*
      * Proxy handlers can use mHasPrototype to request the following special
      * treatment from the JS engine:
      *
      *   - When mHasPrototype is true, the engine never calls these methods:
      *     getPropertyDescriptor, has, set, enumerate, iterate.  Instead, for
-     *     these operations, it calls the "own" traps like
-     *     getOwnPropertyDescriptor, hasOwn, defineProperty, keys, etc., and
-     *     consults the prototype chain if needed.
+     *     these operations, it calls the "own" methods like
+     *     getOwnPropertyDescriptor, hasOwn, defineProperty,
+     *     getOwnEnumerablePropertyKeys, etc., and consults the prototype chain
+     *     if needed.
      *
      *   - When mHasPrototype is true, the engine calls handler->get() only if
      *     handler->hasOwn() says an own property exists on the proxy. If not,
      *     it consults the prototype chain.
      *
      * This is useful because it frees the ProxyHandler from having to implement
      * any behavior having to do with the prototype chain.
      */
@@ -150,24 +214,24 @@ class JS_FRIEND_API(BaseProxyHandler)
     virtual bool finalizeInBackground(Value priv) const {
         /*
          * Called on creation of a proxy to determine whether its finalize
          * method can be finalized on the background thread.
          */
         return true;
     }
 
-    /* Policy enforcement traps.
+    /* Policy enforcement methods.
      *
      * enter() allows the policy to specify whether the caller may perform |act|
      * on the proxy's |id| property. In the case when |act| is CALL, |id| is
      * generally JSID_VOID.
      *
      * The |act| parameter to enter() specifies the action being performed.
-     * If |bp| is false, the trap suggests that the caller throw (though it
+     * If |bp| is false, the method suggests that the caller throw (though it
      * may still decide to squelch the error).
      *
      * We make these OR-able so that assertEnteredPolicy can pass a union of them.
      * For example, get{,Own}PropertyDescriptor is invoked by calls to ::get()
      * ::set(), in addition to being invoked on its own, so there are several
      * valid Actions that could have been entered.
      */
     typedef uint32_t Action;
@@ -178,56 +242,83 @@ class JS_FRIEND_API(BaseProxyHandler)
         CALL      = 0x04,
         ENUMERATE = 0x08,
         GET_PROPERTY_DESCRIPTOR = 0x10
     };
 
     virtual bool enter(JSContext *cx, HandleObject wrapper, HandleId id, Action act,
                        bool *bp) const;
 
-    /* ES5 Harmony fundamental proxy traps. */
-    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const = 0;
-    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                       MutableHandle<JSPropertyDescriptor> desc) const = 0;
-    virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy,
-                                          HandleId id, MutableHandle<JSPropertyDescriptor> desc) const = 0;
+    /* Standard internal methods. */
+    virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
+                                          MutableHandle<JSPropertyDescriptor> desc) const = 0;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const = 0;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const = 0;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const = 0;
     virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const = 0;
+    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const = 0;
+    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const = 0;
 
-    /* ES5 Harmony derived proxy traps. */
+    /*
+     * These methods are standard, but the engine does not normally call them.
+     * They're opt-in. See "Proxy prototype chains" above.
+     *
+     * getPrototypeOf() crashes if called. setPrototypeOf() throws a TypeError.
+     */
+    virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const;
+    virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) const;
+
+    /*
+     * These standard internal methods are implemented, as a convenience, so
+     * that ProxyHandler subclasses don't have to provide every single method.
+     *
+     * The base-class implementations work by calling getPropertyDescriptor().
+     * They do not follow any standard. When in doubt, override them.
+     */
     virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const;
-    virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const;
     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
                      HandleId id, MutableHandleValue vp) const;
     virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver,
                      HandleId id, bool strict, MutableHandleValue vp) const;
-    virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const;
+
+    /*
+     * [[Call]] and [[Construct]] are standard internal methods but according
+     * to the spec, they are not present on every object.
+     *
+     * SpiderMonkey never calls a proxy's call()/construct() internal method
+     * unless isCallable()/isConstructor() returns true for that proxy.
+     *
+     * BaseProxyHandler::isCallable()/isConstructor() always return false, and
+     * BaseProxyHandler::call()/construct() crash if called. So if you're
+     * creating a kind of that is never callable, you don't have to override
+     * anything, but otherwise you probably want to override all four.
+     */
+    virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const;
+    virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const;
+
+    /* SpiderMonkey extensions. */
+    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
+                                       MutableHandle<JSPropertyDescriptor> desc) const = 0;
+    virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const;
+    virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
+                                              AutoIdVector &props) const;
     virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
                          MutableHandleValue vp) const;
-
-    /* Spidermonkey extensions. */
-    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const = 0;
-    virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const;
-    virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) const;
     virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) const;
     virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) const;
     virtual const char *className(JSContext *cx, HandleObject proxy) const;
     virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const;
     virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const;
     virtual bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const;
     virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp) const;
     virtual void finalize(JSFreeOp *fop, JSObject *proxy) const;
     virtual void objectMoved(JSObject *proxy, const JSObject *old) const;
-    virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const;
-    virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) const;
 
     // Allow proxies, wrappers in particular, to specify callability at runtime.
     // Note: These do not take const JSObject *, but they do in spirit.
     //       We are not prepared to do this, as there's little const correctness
     //       in the external APIs that handle proxies.
     virtual bool isCallable(JSObject *obj) const;
     virtual bool isConstructor(JSObject *obj) const;
 
@@ -241,84 +332,82 @@ class JS_FRIEND_API(BaseProxyHandler)
                        HandleObject result) const;
 
     /* See comment for weakmapKeyDelegateOp in js/Class.h. */
     virtual JSObject *weakmapKeyDelegate(JSObject *proxy) const;
     virtual bool isScripted() const { return false; }
 };
 
 /*
- * DirectProxyHandler includes a notion of a target object. All traps are
+ * DirectProxyHandler includes a notion of a target object. All methods are
  * reimplemented such that they forward their behavior to the target. This
  * allows consumers of this class to forward to another object as transparently
  * and efficiently as possible.
  *
- * Important: If you add a trap implementation here, you probably also need to
- * add an override in CrossCompartmentWrapper. If you don't, you risk
+ * Important: If you add a method implementation here, you probably also need
+ * to add an override in CrossCompartmentWrapper. If you don't, you risk
  * compartment mismatches. See bug 945826 comment 0.
  */
 class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler
 {
   public:
     explicit MOZ_CONSTEXPR DirectProxyHandler(const void *aFamily, bool aHasPrototype = false,
                                               bool aHasSecurityPolicy = false)
       : BaseProxyHandler(aFamily, aHasPrototype, aHasSecurityPolicy)
     { }
 
-    /* ES5 Harmony fundamental proxy traps. */
-    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
-    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                       MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+    /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id,
                          bool *bp) const MOZ_OVERRIDE;
     virtual bool enumerate(JSContext *cx, HandleObject proxy,
                            AutoIdVector &props) const MOZ_OVERRIDE;
-
-    /* ES5 Harmony derived proxy traps. */
+    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
+    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
+    virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
+                                MutableHandleObject protop) const MOZ_OVERRIDE;
+    virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto,
+                                bool *bp) const MOZ_OVERRIDE;
     virtual bool has(JSContext *cx, HandleObject proxy, HandleId id,
                      bool *bp) const MOZ_OVERRIDE;
-    virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id,
-                        bool *bp) const MOZ_OVERRIDE;
     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,
                      HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver,
                      HandleId id, bool strict, MutableHandleValue vp) const MOZ_OVERRIDE;
-    virtual bool keys(JSContext *cx, HandleObject proxy,
-                      AutoIdVector &props) const MOZ_OVERRIDE;
+    virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
+    virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
+
+    /* SpiderMonkey extensions. */
+    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
+                                       MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+    virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id,
+                        bool *bp) const MOZ_OVERRIDE;
+    virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
+                                              AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
                          MutableHandleValue vp) const MOZ_OVERRIDE;
-
-    /* Spidermonkey extensions. */
-    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
-    virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
-    virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                             CallArgs args) const MOZ_OVERRIDE;
     virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
                              bool *bp) const MOZ_OVERRIDE;
-    virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
-                                MutableHandleObject protop) const MOZ_OVERRIDE;
-    virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto,
-                                bool *bp) const MOZ_OVERRIDE;
     virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
                                JSContext *cx) const MOZ_OVERRIDE;
     virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, HandleObject proxy,
                                    unsigned indent) const MOZ_OVERRIDE;
     virtual bool regexp_toShared(JSContext *cx, HandleObject proxy,
                                  RegExpGuard *g) const MOZ_OVERRIDE;
     virtual bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const;
+    virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE;
     virtual JSObject *weakmapKeyDelegate(JSObject *proxy) const MOZ_OVERRIDE;
-    virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE;
 };
 
 extern JS_FRIEND_DATA(const js::Class* const) ProxyClassPtr;
 
 inline bool IsProxy(JSObject *obj)
 {
     return GetObjectClass(obj)->isProxy();
 }
@@ -464,18 +553,18 @@ class JS_FRIEND_API(AutoEnterPolicy)
 
 #ifdef JS_DEBUG
     JSContext *context;
     mozilla::Maybe<HandleObject> enteredProxy;
     mozilla::Maybe<HandleId> enteredId;
     Action                   enteredAction;
 
     // NB: We explicitly don't track the entered action here, because sometimes
-    // SET traps do an implicit GET during their implementation, leading to
-    // spurious assertions.
+    // set() methods do an implicit get() during their implementation, leading
+    // to spurious assertions.
     AutoEnterPolicy *prev;
     void recordEnter(JSContext *cx, HandleObject proxy, HandleId id, Action act);
     void recordLeave();
 
     friend JS_FRIEND_API(void) assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id, Action act);
 #else
     inline void recordEnter(JSContext *cx, JSObject *proxy, jsid id, Action act) {}
     inline void recordLeave() {}
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -2660,21 +2660,21 @@ ASTSerializer::generatorExpression(Parse
     if (next->isKind(PNK_IF)) {
         if (!optExpression(next->pn_kid1, &filter))
             return false;
         next = next->pn_kid2;
     }
 
     LOCAL_ASSERT(next->isKind(PNK_SEMI) &&
                  next->pn_kid->isKind(PNK_YIELD) &&
-                 next->pn_kid->pn_kid);
+                 next->pn_kid->pn_left);
 
     RootedValue body(cx);
 
-    return expression(next->pn_kid->pn_kid, &body) &&
+    return expression(next->pn_kid->pn_left, &body) &&
            builder.generatorExpression(body, blocks, filter, isLegacy, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::expression(ParseNode *pn, MutableHandleValue dst)
 {
     JS_CHECK_RECURSION(cx, return false);
     switch (pn->getKind()) {
@@ -2991,29 +2991,29 @@ ASTSerializer::expression(ParseNode *pn,
       case PNK_NUMBER:
       case PNK_TRUE:
       case PNK_FALSE:
       case PNK_NULL:
         return literal(pn, dst);
 
       case PNK_YIELD_STAR:
       {
-        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+        MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
 
         RootedValue arg(cx);
-        return expression(pn->pn_kid, &arg) &&
+        return expression(pn->pn_left, &arg) &&
                builder.yieldExpression(arg, Delegating, &pn->pn_pos, dst);
       }
 
       case PNK_YIELD:
       {
-        MOZ_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+        MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos));
 
         RootedValue arg(cx);
-        return optExpression(pn->pn_kid, &arg) &&
+        return optExpression(pn->pn_left, &arg) &&
                builder.yieldExpression(arg, NotDelegating, &pn->pn_pos, dst);
       }
 
       case PNK_ARRAYCOMP:
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_head->pn_pos));
 
         /* NB: it's no longer the case that pn_count could be 2. */
         LOCAL_ASSERT(pn->pn_count == 1);
@@ -3308,16 +3308,22 @@ ASTSerializer::functionArgsAndBody(Parse
       }
 
       case PNK_STATEMENTLIST:     /* statement closure */
       {
         ParseNode *pnstart = (pnbody->pn_xflags & PNX_DESTRUCT)
                                ? pnbody->pn_head->pn_next
                                : pnbody->pn_head;
 
+        // Skip over initial yield in generator.
+        if (pnstart && pnstart->isKind(PNK_YIELD)) {
+            MOZ_ASSERT(pnstart->getOp() == JSOP_INITIALYIELD);
+            pnstart = pnstart->pn_next;
+        }
+
         return functionArgs(pn, pnargs, pndestruct, pnbody, args, defaults, rest) &&
                functionBody(pnstart, &pnbody->pn_pos, body);
       }
 
       default:
         LOCAL_NOT_REACHED("unexpected function contents");
     }
 }
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -41,22 +41,23 @@ class MOZ_STACK_CLASS WrapperOptions : p
     }
 
   private:
     mozilla::Maybe<JS::RootedObject> proto_;
 };
 
 /*
  * A wrapper is a proxy with a target object to which it generally forwards
- * operations, but may restrict access to certain operations or instrument
- * the trap operations in various ways. A wrapper is distinct from a Direct Proxy
- * Handler in the sense that it can be "unwrapped" in C++, exposing the underlying
+ * operations, but may restrict access to certain operations or instrument the
+ * methods in various ways. A wrapper is distinct from a Direct Proxy Handler
+ * in the sense that it can be "unwrapped" in C++, exposing the underlying
  * object (Direct Proxy Handlers have an underlying target object, but don't
  * expect to expose this object via any kind of unwrapping operation). Callers
- * should be careful to avoid unwrapping security wrappers in the wrong context.
+ * should be careful to avoid unwrapping security wrappers in the wrong
+ * context.
  */
 class JS_FRIEND_API(Wrapper) : public DirectProxyHandler
 {
     unsigned mFlags;
 
   public:
     using BaseProxyHandler::Action;
 
@@ -107,59 +108,58 @@ WrapperOptions::proto() const
 class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
 {
   public:
     explicit MOZ_CONSTEXPR CrossCompartmentWrapper(unsigned aFlags, bool aHasPrototype = false,
                                                    bool aHasSecurityPolicy = false)
       : Wrapper(CROSS_COMPARTMENT | aFlags, aHasPrototype, aHasSecurityPolicy)
     { }
 
-    /* ES5 Harmony fundamental wrapper traps. */
-    virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) const MOZ_OVERRIDE;
-    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
-                                       MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+    /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject wrapper,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const MOZ_OVERRIDE;
-
-    /* ES5 Harmony derived wrapper traps. */
+    virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const MOZ_OVERRIDE;
+    virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) const MOZ_OVERRIDE;
+    virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
+                                MutableHandleObject protop) const MOZ_OVERRIDE;
+    virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto,
+                                bool *bp) const MOZ_OVERRIDE;
     virtual bool has(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE;
-    virtual bool hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool get(JSContext *cx, HandleObject wrapper, HandleObject receiver,
                      HandleId id, MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver,
                      HandleId id, bool strict, MutableHandleValue vp) const MOZ_OVERRIDE;
-    virtual bool keys(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const MOZ_OVERRIDE;
+    virtual bool call(JSContext *cx, HandleObject wrapper, const CallArgs &args) const MOZ_OVERRIDE;
+    virtual bool construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) const MOZ_OVERRIDE;
+
+    /* SpiderMonkey extensions. */
+    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
+                                       MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+    virtual bool hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE;
+    virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject wrapper,
+                                              AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool iterate(JSContext *cx, HandleObject wrapper, unsigned flags,
                          MutableHandleValue vp) const MOZ_OVERRIDE;
-
-    /* Spidermonkey extensions. */
-    virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const MOZ_OVERRIDE;
-    virtual bool call(JSContext *cx, HandleObject wrapper, const CallArgs &args) const MOZ_OVERRIDE;
-    virtual bool construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                             CallArgs args) const MOZ_OVERRIDE;
     virtual bool hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v,
                              bool *bp) const MOZ_OVERRIDE;
     virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, HandleObject wrapper,
                                    unsigned indent) const MOZ_OVERRIDE;
     virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE;
     virtual bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint,
                               MutableHandleValue vp) const MOZ_OVERRIDE;
-    virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
-                                MutableHandleObject protop) const MOZ_OVERRIDE;
-    virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto,
-                                bool *bp) const MOZ_OVERRIDE;
 
     static const CrossCompartmentWrapper singleton;
     static const CrossCompartmentWrapper singletonWithPrototype;
 };
 
 /*
  * Base class for security wrappers. A security wrapper is potentially hiding
  * all or part of some wrapped object thus SecurityWrapper defaults to denying
@@ -172,41 +172,42 @@ class JS_FRIEND_API(CrossCompartmentWrap
 template <class Base>
 class JS_FRIEND_API(SecurityWrapper) : public Base
 {
   public:
     explicit MOZ_CONSTEXPR SecurityWrapper(unsigned flags, bool hasPrototype = false)
       : Base(flags, hasPrototype, /* hasSecurityPolicy = */ true)
     { }
 
-    virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const MOZ_OVERRIDE;
-    virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) const MOZ_OVERRIDE;
     virtual bool enter(JSContext *cx, HandleObject wrapper, HandleId id, Wrapper::Action act,
                        bool *bp) const MOZ_OVERRIDE;
+
+    virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
+                                MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+    virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) const MOZ_OVERRIDE;
+    virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) const MOZ_OVERRIDE;
+    virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto,
+                                bool *bp) const MOZ_OVERRIDE;
+
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                             CallArgs args) const MOZ_OVERRIDE;
-    virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint,
-                              MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
                                JSContext *cx) const MOZ_OVERRIDE;
     virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE;
     virtual bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp) const MOZ_OVERRIDE;
-    virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
-                                MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+    virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint,
+                              MutableHandleValue vp) const MOZ_OVERRIDE;
 
-    virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto,
-                                bool *bp) const MOZ_OVERRIDE;
+    // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded
+    // against.
 
     virtual bool watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                        JS::HandleObject callable) const MOZ_OVERRIDE;
     virtual bool unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id) const MOZ_OVERRIDE;
 
-    // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded
-    // against.
-
     /*
      * Allow our subclasses to select the superclass behavior they want without
      * needing to specify an exact superclass.
      */
     typedef Base Permissive;
     typedef SecurityWrapper<Base> Restrictive;
 };
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -238,16 +238,17 @@ UNIFIED_SOURCES += [
     'vm/CallNonGenericMethod.cpp',
     'vm/CharacterEncoding.cpp',
     'vm/Compression.cpp',
     'vm/DateTime.cpp',
     'vm/Debugger.cpp',
     'vm/DebuggerMemory.cpp',
     'vm/ErrorObject.cpp',
     'vm/ForkJoin.cpp',
+    'vm/GeneratorObject.cpp',
     'vm/GlobalObject.cpp',
     'vm/HelperThreads.cpp',
     'vm/Id.cpp',
     'vm/Interpreter.cpp',
     'vm/JSONParser.cpp',
     'vm/MemoryMetrics.cpp',
     'vm/Monitor.cpp',
     'vm/NativeObject.cpp',
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -160,17 +160,18 @@ js::SetPropertyIgnoringNamedGetter(JSCon
     desc.value().set(vp.get());
     desc.setAttributes(JSPROP_ENUMERATE);
     desc.setGetter(nullptr);
     desc.setSetter(nullptr); // Pick up the class getter/setter.
     return handler->defineProperty(cx, receiver, id, desc);
 }
 
 bool
-BaseProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const
+BaseProxyHandler::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
+                                               AutoIdVector &props) const
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
     MOZ_ASSERT(props.length() == 0);
 
     if (!ownPropertyKeys(cx, proxy, props))
         return false;
 
     /* Select only the enumerable properties through in-place iteration. */
@@ -199,17 +200,17 @@ BaseProxyHandler::keys(JSContext *cx, Ha
 bool
 BaseProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
                           MutableHandleValue vp) const
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
 
     AutoIdVector props(cx);
     if ((flags & JSITER_OWNONLY)
-        ? !keys(cx, proxy, props)
+        ? !getOwnEnumerablePropertyKeys(cx, proxy, props)
         : !enumerate(cx, proxy, props)) {
         return false;
     }
 
     return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
 }
 
 bool
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -146,21 +146,22 @@ CrossCompartmentWrapper::set(JSContext *
     PIERCE(cx, wrapper,
            cx->compartment()->wrap(cx, &receiverCopy) &&
            cx->compartment()->wrap(cx, vp),
            Wrapper::set(cx, wrapper, receiverCopy, id, strict, vp),
            NOTHING);
 }
 
 bool
-CrossCompartmentWrapper::keys(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const
+CrossCompartmentWrapper::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject wrapper,
+                                                      AutoIdVector &props) const
 {
     PIERCE(cx, wrapper,
            NOTHING,
-           Wrapper::keys(cx, wrapper, props),
+           Wrapper::getOwnEnumerablePropertyKeys(cx, wrapper, props),
            NOTHING);
 }
 
 /*
  * We can reify non-escaping iterator objects instead of having to wrap them. This
  * allows fast iteration over objects across a compartment boundary.
  */
 static bool
--- a/js/src/proxy/DeadObjectProxy.h
+++ b/js/src/proxy/DeadObjectProxy.h
@@ -13,46 +13,46 @@ namespace js {
 
 class DeadObjectProxy : public BaseProxyHandler
 {
   public:
     explicit MOZ_CONSTEXPR DeadObjectProxy()
       : BaseProxyHandler(&family)
     { }
 
-    /* ES5 Harmony fundamental wrapper traps. */
-    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
-    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
-                                       MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+    /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject wrapper,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) const MOZ_OVERRIDE;
-
-    /* Spidermonkey extensions. */
     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
+    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
+    virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
+                                MutableHandleObject protop) const MOZ_OVERRIDE;
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
+
+    /* SpiderMonkey extensions. */
+    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
+                                       MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                             CallArgs args) const MOZ_OVERRIDE;
     virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
                              bool *bp) const MOZ_OVERRIDE;
     virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
                                JSContext *cx) const MOZ_OVERRIDE;
     virtual const char *className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE;
     virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE;
     virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint,
                               MutableHandleValue vp) const MOZ_OVERRIDE;
-    virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
-                                MutableHandleObject protop) const MOZ_OVERRIDE;
 
     static const char family;
     static const DeadObjectProxy singleton;
 };
 
 bool
 IsDeadProxyObject(JSObject *obj);
 
--- a/js/src/proxy/DirectProxyHandler.cpp
+++ b/js/src/proxy/DirectProxyHandler.cpp
@@ -213,17 +213,18 @@ DirectProxyHandler::set(JSContext *cx, H
                         HandleId id, bool strict, MutableHandleValue vp) const
 {
     assertEnteredPolicy(cx, proxy, id, SET);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return JSObject::setGeneric(cx, target, receiver, id, vp, strict);
 }
 
 bool
-DirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const
+DirectProxyHandler::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
+                                                 AutoIdVector &props) const
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return GetPropertyKeys(cx, target, JSITER_OWNONLY, &props);
 }
 
 bool
 DirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -211,17 +211,17 @@ Proxy::enumerate(JSContext *cx, HandleOb
 {
     JS_CHECK_RECURSION(cx, return false);
     const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
     if (!policy.allowed())
         return policy.returnValue();
     if (!handler->hasPrototype())
         return proxy->as<ProxyObject>().handler()->enumerate(cx, proxy, props);
-    if (!handler->keys(cx, proxy, props))
+    if (!handler->getOwnEnumerablePropertyKeys(cx, proxy, props))
         return false;
     AutoIdVector protoProps(cx);
     INVOKE_ON_PROTOTYPE(cx, handler, proxy,
                         GetPropertyKeys(cx, proto, 0, &protoProps) &&
                         AppendUnique(cx, props, protoProps));
 }
 
 bool
@@ -336,24 +336,24 @@ Proxy::set(JSContext *cx, HandleObject p
         newDesc.setAttributes(desc.attributes());
     else
         newDesc.setAttributes(JSPROP_ENUMERATE);
     newDesc.value().set(vp);
     return handler->defineProperty(cx, receiver, id, &newDesc);
 }
 
 bool
-Proxy::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
+Proxy::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
     JS_CHECK_RECURSION(cx, return false);
     const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
     if (!policy.allowed())
         return policy.returnValue();
-    return handler->keys(cx, proxy, props);
+    return handler->getOwnEnumerablePropertyKeys(cx, proxy, props);
 }
 
 bool
 Proxy::iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp)
 {
     JS_CHECK_RECURSION(cx, return false);
     const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
     vp.setUndefined(); // default result if we refuse to perform this action
@@ -367,17 +367,17 @@ Proxy::iterate(JSContext *cx, HandleObje
             return policy.returnValue() &&
                    EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
         }
         return handler->iterate(cx, proxy, flags, vp);
     }
     AutoIdVector props(cx);
     // The other Proxy::foo methods do the prototype-aware work for us here.
     if ((flags & JSITER_OWNONLY)
-        ? !Proxy::keys(cx, proxy, props)
+        ? !Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props)
         : !Proxy::enumerate(cx, proxy, props)) {
         return false;
     }
     return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp);
 }
 
 bool
 Proxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -13,63 +13,62 @@
 
 namespace js {
 
 class RegExpGuard;
 
 /*
  * Dispatch point for handlers that executes the appropriate C++ or scripted traps.
  *
- * Important: All proxy traps need either (a) an AutoEnterPolicy in their
+ * Important: All proxy methods need either (a) an AutoEnterPolicy in their
  * Proxy::foo entry point below or (b) an override in SecurityWrapper. See bug
  * 945826 comment 0.
  */
 class Proxy
 {
   public:
-    /* ES5 Harmony fundamental proxy traps. */
-    static bool preventExtensions(JSContext *cx, HandleObject proxy);
-    static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                      MutableHandle<JSPropertyDescriptor> desc);
-    static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                      MutableHandleValue vp);
+    /* Standard internal methods. */
     static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                          MutableHandle<JSPropertyDescriptor> desc);
     static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                          MutableHandleValue vp);
     static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                MutableHandle<JSPropertyDescriptor> desc);
     static bool ownPropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props);
     static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
     static bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props);
-
-    /* ES5 Harmony derived proxy traps. */
+    static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible);
+    static bool preventExtensions(JSContext *cx, HandleObject proxy);
+    static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
+    static bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp);
     static bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
-    static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
     static bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                     MutableHandleValue vp);
     static bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                     bool strict, MutableHandleValue vp);
-    static bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props);
-    static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp);
-
-    /* Spidermonkey extensions. */
-    static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible);
     static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args);
     static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args);
+
+    /* SpiderMonkey extensions. */
+    static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
+                                      MutableHandle<JSPropertyDescriptor> desc);
+    static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
+                                      MutableHandleValue vp);
+    static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
+    static bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
+                                             AutoIdVector &props);
+    static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp);
     static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
     static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
     static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
     static const char *className(JSContext *cx, HandleObject proxy);
     static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
     static bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g);
     static bool boxedValue_unbox(JSContext *cx, HandleObject proxy, MutableHandleValue vp);
     static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
-    static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
-    static bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp);
 
     static bool watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable);
     static bool unwatch(JSContext *cx, HandleObject proxy, HandleId id);
 
     static bool slice(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end,
                       HandleObject result);
 
     /* IC entry path for handling __noSuchMethod__ on access. */
--- a/js/src/proxy/ScriptedDirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp
@@ -167,17 +167,18 @@ ReportInvalidTrapResult(JSContext *cx, J
     RootedValue v(cx, ObjectOrNullValue(proxy));
     JSAutoByteString bytes;
     if (!AtomToPrintableString(cx, atom, &bytes))
         return;
     js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_IGNORE_STACK, v,
                          js::NullPtr(), bytes.ptr());
 }
 
-// This function is shared between ownPropertyKeys, enumerate, and keys
+// This function is shared between ownPropertyKeys, enumerate, and
+// getOwnEnumerablePropertyKeys.
 static bool
 ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleValue v,
                 AutoIdVector &props, unsigned flags, JSAtom *trapName_)
 {
     MOZ_ASSERT(v.isObject());
     RootedObject array(cx, &v.toObject());
     RootedAtom trapName(cx, trapName_);
 
--- a/js/src/proxy/ScriptedDirectProxyHandler.h
+++ b/js/src/proxy/ScriptedDirectProxyHandler.h
@@ -13,52 +13,53 @@ namespace js {
 
 /* Derived class for all scripted direct proxy handlers. */
 class ScriptedDirectProxyHandler : public DirectProxyHandler {
   public:
     MOZ_CONSTEXPR ScriptedDirectProxyHandler()
       : DirectProxyHandler(&family)
     { }
 
-    /* ES5 Harmony fundamental proxy traps. */
-    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
-    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                       MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+    /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE;
-
-    /* ES5 Harmony derived proxy traps. */
+    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
+    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
-    virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE {
-        return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
-    }
     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                      MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                      bool strict, MutableHandleValue vp) const MOZ_OVERRIDE;
-    // Kick keys out to getOwnPropertyName and then filter. [[GetOwnProperty]] could potentially
-    // change the enumerability of the target's properties.
-    virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE {
-        return BaseProxyHandler::keys(cx, proxy, props);
+    virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
+    virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
+
+    /* SpiderMonkey extensions. */
+    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
+                                       MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+    virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE {
+        return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
+    }
+
+
+    // Kick getOwnEnumerablePropertyKeys out to ownPropertyKeys and then
+    // filter. [[GetOwnProperty]] could potentially change the enumerability of
+    // the target's properties.
+    virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
+                                              AutoIdVector &props) const MOZ_OVERRIDE {
+        return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props);
     }
     virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
                          MutableHandleValue vp) const MOZ_OVERRIDE;
 
-    /* ES6 Harmony traps */
-    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
-
-    /* Spidermonkey extensions. */
-    virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
-    virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE;
     virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE {
         // For now we maintain the broken behavior that a scripted proxy is constructable if it's
         // callable. See bug 929467.
         return isCallable(obj);
     }
     virtual bool isScripted() const MOZ_OVERRIDE { return true; }
 
--- a/js/src/proxy/ScriptedIndirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedIndirectProxyHandler.cpp
@@ -297,24 +297,25 @@ ScriptedIndirectProxyHandler::set(JSCont
     if (!GetDerivedTrap(cx, handler, cx->names().set, &fval))
         return false;
     if (!IsCallable(fval))
         return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp);
     return Trap(cx, handler, fval, 3, argv.begin(), &idv);
 }
 
 bool
-ScriptedIndirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const
+ScriptedIndirectProxyHandler::getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
+                                                           AutoIdVector &props) const
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue value(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().keys, &value))
         return false;
     if (!IsCallable(value))
-        return BaseProxyHandler::keys(cx, proxy, props);
+        return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props);
     return Trap(cx, handler, value, 0, nullptr, &value) &&
            ArrayToIdVector(cx, value, props);
 }
 
 bool
 ScriptedIndirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
                                       MutableHandleValue vp) const
 {
--- a/js/src/proxy/ScriptedIndirectProxyHandler.h
+++ b/js/src/proxy/ScriptedIndirectProxyHandler.h
@@ -14,42 +14,41 @@ namespace js {
 /* Derived class for all scripted indirect proxy handlers. */
 class ScriptedIndirectProxyHandler : public BaseProxyHandler
 {
   public:
     MOZ_CONSTEXPR ScriptedIndirectProxyHandler()
       : BaseProxyHandler(&family)
     { }
 
-    /* ES5 Harmony fundamental proxy traps. */
-    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
-    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                       MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+    /* Standard internal methods. */
     virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                           MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
     virtual bool ownPropertyKeys(JSContext *cx, HandleObject proxy,
                                  AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE;
-
-    /* ES5 Harmony derived proxy traps. */
+    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
+    virtual bool preventExtensions(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
     virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
-    virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
     virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                      MutableHandleValue vp) const MOZ_OVERRIDE;
     virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                      bool strict, MutableHandleValue vp) const MOZ_OVERRIDE;
-    virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) const MOZ_OVERRIDE;
+
+    /* SpiderMonkey extensions. */
+    virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
+                                       MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
+    virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) const MOZ_OVERRIDE;
+    virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
+                                              AutoIdVector &props) const MOZ_OVERRIDE;
     virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags,
                          MutableHandleValue vp) const MOZ_OVERRIDE;
-
-    /* Spidermonkey extensions. */
-    virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                             CallArgs args) const MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) const MOZ_OVERRIDE;
     virtual bool isScripted() const MOZ_OVERRIDE { return true; }
 
     static const char family;
     static const ScriptedIndirectProxyHandler singleton;
 };
--- a/js/src/tests/ecma_6/Symbol/equality.js
+++ b/js/src/tests/ecma_6/Symbol/equality.js
@@ -5,17 +5,18 @@ if (typeof Symbol === "function") {
     // Symbol.for returns the same symbol whenever the same argument is passed.
     assertEq(Symbol.for("q") === Symbol.for("q"), true);
 
     // Several distinct Symbol values.
     var symbols = [
         Symbol(),
         Symbol("Symbol.iterator"),
         Symbol("Symbol.iterator"),  // distinct new symbol with the same description
-        Symbol.for("Symbol.iterator")
+        Symbol.for("Symbol.iterator"),
+        Symbol.iterator
     ];
 
     // Distinct symbols are never equal to each other, even if they have the same
     // description.
     for (var i = 0; i < symbols.length; i++) {
         for (var j = i; j < symbols.length; j++) {
             var expected = (i === j);
             assertEq(symbols[i] == symbols[j], expected);
--- a/js/src/tests/ecma_6/Symbol/surfaces.js
+++ b/js/src/tests/ecma_6/Symbol/surfaces.js
@@ -17,15 +17,20 @@ if (typeof Symbol === "function") {
     assertEq(desc.writable, false);
 
     assertEq(Symbol.prototype.constructor, Symbol);
     desc = Object.getOwnPropertyDescriptor(Symbol.prototype, "constructor");
     assertEq(desc.configurable, true);
     assertEq(desc.enumerable, false);
     assertEq(desc.writable, true);
 
+    desc = Object.getOwnPropertyDescriptor(Symbol, "iterator");
+    assertEq(desc.configurable, false);
+    assertEq(desc.enumerable, false);
+    assertEq(desc.writable, false);
+
     assertEq(Symbol.for.length, 1);
     assertEq(Symbol.prototype.toString.length, 0);
     assertEq(Symbol.prototype.valueOf.length, 0);
 }
 
 if (typeof reportCompare === "function")
     reportCompare(0, 0);
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -28,16 +28,17 @@
     macro(bytes, bytes, "bytes") \
     macro(BYTES_PER_ELEMENT, BYTES_PER_ELEMENT, "BYTES_PER_ELEMENT") \
     macro(call, call, "call") \
     macro(callee, callee, "callee") \
     macro(caller, caller, "caller") \
     macro(callFunction, callFunction, "callFunction") \
     macro(caseFirst, caseFirst, "caseFirst") \
     macro(class_, class_, "class") \
+    macro(close, close, "close") \
     macro(Collator, Collator, "Collator") \
     macro(CollatorCompareGet, CollatorCompareGet, "Intl_Collator_compare_get") \
     macro(columnNumber, columnNumber, "columnNumber") \
     macro(comma, comma, ",") \
     macro(compare, compare, "compare") \
     macro(configurable, configurable, "configurable") \
     macro(construct, construct, "construct") \
     macro(constructor, constructor, "constructor") \
@@ -53,16 +54,17 @@
     macro(default_, default_, "default") \
     macro(defineProperty, defineProperty, "defineProperty") \
     macro(defineGetter, defineGetter, "__defineGetter__") \
     macro(defineSetter, defineSetter, "__defineSetter__") \
     macro(delete, delete_, "delete") \
     macro(deleteProperty, deleteProperty, "deleteProperty") \
     macro(displayURL, displayURL, "displayURL") \
     macro(done, done, "done") \
+    macro(dotGenerator, dotGenerator, ".generator") \
     macro(each, each, "each") \
     macro(elementType, elementType, "elementType") \
     macro(empty, empty, "") \
     macro(encodeURI, encodeURI, "encodeURI") \
     macro(encodeURIComponent, encodeURIComponent, "encodeURIComponent") \
     macro(enumerable, enumerable, "enumerable") \
     macro(enumerate, enumerate, "enumerate") \
     macro(escape, escape, "escape") \
@@ -105,16 +107,17 @@
     macro(int8, int8, "int8") \
     macro(int16, int16, "int16") \
     macro(int32, int32, "int32") \
     macro(isExtensible, isExtensible, "isExtensible") \
     macro(iteratorIntrinsic, iteratorIntrinsic, "__iterator__") \
     macro(join, join, "join") \
     macro(keys, keys, "keys") \
     macro(lastIndex, lastIndex, "lastIndex") \
+    macro(LegacyGeneratorCloseInternal, LegacyGeneratorCloseInternal, "LegacyGeneratorCloseInternal") \
     macro(length, length, "length") \
     macro(let, let, "let") \
     macro(line, line, "line") \
     macro(lineNumber, lineNumber, "lineNumber") \
     macro(loc, loc, "loc") \
     macro(locale, locale, "locale") \
     macro(lookupGetter, lookupGetter, "__lookupGetter__") \
     macro(lookupSetter, lookupSetter, "__lookupSetter__") \
@@ -157,16 +160,17 @@
     macro(parseInt, parseInt, "parseInt") \
     macro(pattern, pattern, "pattern") \
     macro(preventExtensions, preventExtensions, "preventExtensions") \
     macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \
     macro(proto, proto, "__proto__") \
     macro(prototype, prototype, "prototype") \
     macro(proxy, proxy, "proxy") \
     macro(Reify, Reify, "Reify") \
+    macro(resumeGenerator, resumeGenerator, "resumeGenerator") \
     macro(return, return_, "return") \
     macro(revoke, revoke, "revoke") \
     macro(scripts, scripts, "scripts") \
     macro(sensitivity, sensitivity, "sensitivity") \
     macro(set, set, "set") \
     macro(shape, shape, "shape") \
     macro(signMask, signMask, "signMask") \
     macro(source, source, "source") \
new file mode 100644
--- /dev/null
+++ b/js/src/vm/GeneratorObject.cpp
@@ -0,0 +1,329 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "vm/GeneratorObject.h"
+
+#include "jsatominlines.h"
+#include "jsscriptinlines.h"
+
+#include "vm/NativeObject-inl.h"
+#include "vm/Stack-inl.h"
+
+using namespace js;
+using namespace js::types;
+
+JSObject *
+GeneratorObject::create(JSContext *cx, const InterpreterRegs &regs)
+{
+    MOZ_ASSERT(regs.stackDepth() == 0);
+    InterpreterFrame *fp = regs.fp();
+
+    MOZ_ASSERT(fp->script()->isGenerator());
+
+    Rooted<GlobalObject*> global(cx, &fp->global());
+    RootedNativeObject obj(cx);
+    if (fp->script()->isStarGenerator()) {
+        RootedValue pval(cx);
+        RootedObject fun(cx, fp->fun());
+        // FIXME: This would be faster if we could avoid doing a lookup to get
+        // the prototype for the instance.  Bug 906600.
+        if (!JSObject::getProperty(cx, fun, fun, cx->names().prototype, &pval))
+            return nullptr;
+        JSObject *proto = pval.isObject() ? &pval.toObject() : nullptr;
+        if (!proto) {
+            proto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
+            if (!proto)
+                return nullptr;
+        }
+        obj = NewNativeObjectWithGivenProto(cx, &StarGeneratorObject::class_, proto, global);
+    } else {
+        MOZ_ASSERT(fp->script()->isLegacyGenerator());
+        JSObject *proto = GlobalObject::getOrCreateLegacyGeneratorObjectPrototype(cx, global);
+        if (!proto)
+            return nullptr;
+        obj = NewNativeObjectWithGivenProto(cx, &LegacyGeneratorObject::class_, proto, global);
+    }
+    if (!obj)
+        return nullptr;
+
+    Rooted<GeneratorObject *> genObj(cx, &obj->as<GeneratorObject>());
+
+    genObj->setCallee(fp->callee());
+    genObj->setThisValue(fp->thisValue());
+    genObj->setScopeChain(*fp->scopeChain());
+    if (fp->script()->needsArgsObj())
+        genObj->setArgsObj(fp->argsObj());
+    genObj->clearExpressionStack();
+
+    return obj;
+}
+
+bool
+GeneratorObject::suspend(JSContext *cx, HandleObject obj, InterpreterFrame *fp, jsbytecode *pc,
+                         Value *vp, unsigned nvalues, GeneratorObject::SuspendKind suspendKind)
+{
+    Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
+    MOZ_ASSERT(!genObj->hasExpressionStack());
+
+    if (suspendKind == NORMAL && genObj->isClosing()) {
+        MOZ_ASSERT(genObj->is<LegacyGeneratorObject>());
+        RootedValue val(cx, ObjectValue(fp->callee()));
+        js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, JSDVG_SEARCH_STACK, val, js::NullPtr());
+        return false;
+    }
+
+    genObj->setSuspendedBytecodeOffset(pc - fp->script()->code(), suspendKind == INITIAL);
+    genObj->setScopeChain(*fp->scopeChain());
+
+    if (nvalues) {
+        ArrayObject *stack = NewDenseCopiedArray(cx, nvalues, vp);
+        if (!stack)
+            return false;
+        genObj->setExpressionStack(*stack);
+    }
+
+    return true;
+}
+
+bool
+GeneratorObject::finalSuspend(JSContext *cx, HandleObject obj)
+{
+    Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
+    MOZ_ASSERT(genObj->isRunning() || genObj->isClosing());
+
+    bool closing = genObj->isClosing();
+    MOZ_ASSERT_IF(closing, genObj->is<LegacyGeneratorObject>());
+    genObj->setClosed();
+
+    if (genObj->is<LegacyGeneratorObject>() && !closing)
+        return ThrowStopIteration(cx);
+
+    return true;
+}
+
+bool
+GeneratorObject::resume(JSContext *cx, InterpreterActivation &activation,
+                        HandleObject obj, HandleValue arg, GeneratorObject::ResumeKind resumeKind)
+{
+    Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
+    MOZ_ASSERT(genObj->isSuspended());
+
+    RootedFunction callee(cx, &genObj->callee());
+    RootedValue thisv(cx, genObj->thisValue());
+    RootedObject scopeChain(cx, &genObj->scopeChain());
+    if (!activation.resumeGeneratorFrame(callee, thisv, scopeChain))
+        return false;
+
+    if (genObj->hasArgsObj())
+        activation.regs().fp()->initArgsObj(genObj->argsObj());
+
+    if (genObj->hasExpressionStack()) {
+        uint32_t len = genObj->expressionStack().length();
+        MOZ_ASSERT(activation.regs().spForStackDepth(len));
+        RootedObject array(cx, &genObj->expressionStack());
+        GetElements(cx, array, len, activation.regs().sp);
+        activation.regs().sp += len;
+        genObj->clearExpressionStack();
+    }
+
+    activation.regs().pc = callee->nonLazyScript()->code() + genObj->suspendedBytecodeOffset();
+
+    // If we are resuming a JSOP_YIELD, always push on a value, even if we are
+    // raising an exception.  In the exception case, the stack needs to have
+    // something on it so that exception handling doesn't skip the catch
+    // blocks.  See TryNoteIter::settle.
+    if (!genObj->isNewborn()) {
+        activation.regs().sp++;
+        MOZ_ASSERT(activation.regs().spForStackDepth(activation.regs().stackDepth()));
+        activation.regs().sp[-1] = arg;
+    }
+
+    switch (resumeKind) {
+      case NEXT:
+        genObj->setRunning();
+        return true;
+
+      case THROW:
+        cx->setPendingException(arg);
+        if (genObj->isNewborn())
+            genObj->setClosed();
+        else
+            genObj->setRunning();
+        return false;
+
+      case CLOSE:
+        MOZ_ASSERT(genObj->is<LegacyGeneratorObject>());
+        cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
+        genObj->setClosing();
+        return false;
+
+      default:
+        MOZ_CRASH("bad resumeKind");
+    }
+}
+
+bool
+LegacyGeneratorObject::close(JSContext *cx, HandleObject obj)
+{
+     Rooted<LegacyGeneratorObject*> genObj(cx, &obj->as<LegacyGeneratorObject>());
+
+    // Avoid calling back into JS unless it is necessary.
+     if (genObj->isClosed())
+        return true;
+
+    if (genObj->isNewborn()) {
+        genObj->setClosed();
+        return true;
+    }
+
+    RootedValue rval(cx);
+
+    RootedValue closeValue(cx);
+    if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().LegacyGeneratorCloseInternal,
+                                         &closeValue))
+    {
+        return false;
+    }
+    MOZ_ASSERT(closeValue.isObject());
+    MOZ_ASSERT(closeValue.toObject().is<JSFunction>());
+
+    InvokeArgs args(cx);
+    if (!args.init(0))
+        return false;
+
+    args.setCallee(closeValue);
+    args.setThis(ObjectValue(*genObj));
+
+    return Invoke(cx, args);
+}
+
+static JSObject *
+iterator_iteratorObject(JSContext *cx, HandleObject obj, bool keysonly)
+{
+    return obj;
+}
+
+const Class LegacyGeneratorObject::class_ = {
+    "Generator",
+    JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS),
+    JS_PropertyStub,         /* addProperty */
+    JS_DeletePropertyStub,   /* delProperty */
+    JS_PropertyStub,         /* getProperty */
+    JS_StrictPropertyStub,   /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    nullptr,                 /* finalize    */
+    nullptr,                 /* call        */
+    nullptr,                 /* hasInstance */
+    nullptr,                 /* construct   */
+    nullptr,                 /* trace       */
+    JS_NULL_CLASS_SPEC,
+    {
+        nullptr,             /* outerObject    */
+        nullptr,             /* innerObject    */
+        iterator_iteratorObject,
+    }
+};
+
+const Class StarGeneratorObject::class_ = {
+    "Generator",
+    JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS),
+    JS_PropertyStub,         /* addProperty */
+    JS_DeletePropertyStub,   /* delProperty */
+    JS_PropertyStub,         /* getProperty */
+    JS_StrictPropertyStub,   /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    nullptr,                 /* finalize    */
+    nullptr,                 /* call        */
+    nullptr,                 /* hasInstance */
+    nullptr,                 /* construct   */
+    nullptr,                 /* trace       */
+};
+
+static const JSFunctionSpec star_generator_methods[] = {
+    JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
+    JS_SELF_HOSTED_FN("next", "StarGeneratorNext", 1, 0),
+    JS_SELF_HOSTED_FN("throw", "StarGeneratorThrow", 1, 0),
+    JS_FS_END
+};
+
+#define JSPROP_ROPERM   (JSPROP_READONLY | JSPROP_PERMANENT)
+
+static const JSFunctionSpec legacy_generator_methods[] = {
+    JS_SELF_HOSTED_FN("@@iterator", "LegacyGeneratorIteratorShim", 0, 0),
+    // "send" is an alias for "next".
+    JS_SELF_HOSTED_FN("next", "LegacyGeneratorNext", 1, JSPROP_ROPERM),
+    JS_SELF_HOSTED_FN("send", "LegacyGeneratorNext", 1, JSPROP_ROPERM),
+    JS_SELF_HOSTED_FN("throw", "LegacyGeneratorThrow", 1, JSPROP_ROPERM),
+    JS_SELF_HOSTED_FN("close", "LegacyGeneratorClose", 0, JSPROP_ROPERM),
+    JS_FS_END
+};
+
+#undef JSPROP_ROPERM
+
+static JSObject*
+NewSingletonObjectWithObjectPrototype(JSContext *cx, Handle<GlobalObject *> global)
+{
+    JSObject *proto = global->getOrCreateObjectPrototype(cx);
+    if (!proto)
+        return nullptr;
+    return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject);
+}
+
+static JSObject*
+NewSingletonObjectWithFunctionPrototype(JSContext *cx, Handle<GlobalObject *> global)
+{
+    JSObject *proto = global->getOrCreateFunctionPrototype(cx);
+    if (!proto)
+        return nullptr;
+    return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject);
+}
+
+/* static */ bool
+GlobalObject::initGeneratorClasses(JSContext *cx, Handle<GlobalObject *> global)
+{
+    if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) {
+        RootedObject proto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
+        if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods))
+            return false;
+        global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto));
+    }
+
+    if (global->getSlot(STAR_GENERATOR_OBJECT_PROTO).isUndefined()) {
+        RootedObject genObjectProto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
+        if (!genObjectProto)
+            return false;
+        if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, star_generator_methods))
+            return false;
+
+        RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
+        if (!genFunctionProto)
+            return false;
+        if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto))
+            return false;
+
+        RootedValue function(cx, global->getConstructor(JSProto_Function));
+        if (!function.toObjectOrNull())
+            return false;
+        RootedAtom name(cx, cx->names().GeneratorFunction);
+        RootedObject genFunction(cx, NewFunctionWithProto(cx, NullPtr(), Generator, 1,
+                                                          JSFunction::NATIVE_CTOR, global, name,
+                                                          &function.toObject()));
+        if (!genFunction)
+            return false;
+        if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
+            return false;
+
+        global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
+        global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction));
+        global->setPrototype(JSProto_GeneratorFunction, ObjectValue(*genFunctionProto));
+    }
+
+    return true;
+}
--- a/js/src/vm/GeneratorObject.h
+++ b/js/src/vm/GeneratorObject.h
@@ -2,31 +2,204 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef vm_GeneratorObject_h
 #define vm_GeneratorObject_h
 
+#include "jscntxt.h"
 #include "jsobj.h"
 
+#include "vm/ArgumentsObject.h"
+#include "vm/ArrayObject.h"
+#include "vm/Stack.h"
+
 namespace js {
 
-class LegacyGeneratorObject : public NativeObject
+class GeneratorObject : public NativeObject
+{
+    static const int32_t MAX_BYTECODE_OFFSET = INT32_MAX >> 1;
+
+  public:
+    enum {
+        CALLEE_SLOT = 0,
+        THIS_SLOT,
+        SCOPE_CHAIN_SLOT,
+        ARGS_OBJ_SLOT,
+        EXPRESSION_STACK_SLOT,
+        BYTECODE_OFFSET_SLOT,
+        RESERVED_SLOTS
+    };
+
+    enum SuspendKind { INITIAL, NORMAL, FINAL };
+    enum ResumeKind { NEXT, THROW, CLOSE };
+
+    static inline ResumeKind getResumeKind(jsbytecode *pc) {
+        MOZ_ASSERT(*pc == JSOP_RESUME);
+        unsigned arg = GET_UINT16(pc);
+        MOZ_ASSERT(arg <= CLOSE);
+        return static_cast<ResumeKind>(arg);
+    }
+
+    static inline ResumeKind getResumeKind(ExclusiveContext *cx, JSAtom *atom) {
+        if (atom == cx->names().next)
+            return NEXT;
+        if (atom == cx->names().throw_)
+            return THROW;
+        MOZ_ASSERT(atom == cx->names().close);
+        return CLOSE;
+    }
+
+    static JSObject *create(JSContext *cx, const InterpreterRegs &regs);
+
+    static bool suspend(JSContext *cx, HandleObject obj, InterpreterFrame *fp, jsbytecode *pc,
+                        Value *vp, unsigned nvalues, SuspendKind kind);
+
+    static bool resume(JSContext *cx, InterpreterActivation &activation,
+                       HandleObject obj, HandleValue arg, ResumeKind resumeKind);
+
+    static bool initialSuspend(JSContext *cx, HandleObject obj, InterpreterFrame *fp, jsbytecode *pc) {
+        return suspend(cx, obj, fp, pc, nullptr, 0, INITIAL);
+    }
+
+    static bool normalSuspend(JSContext *cx, HandleObject obj, InterpreterFrame *fp, jsbytecode *pc,
+                              Value *vp, unsigned nvalues) {
+        return suspend(cx, obj, fp, pc, vp, nvalues, NORMAL);
+    }
+
+    static bool finalSuspend(JSContext *cx, HandleObject obj);
+
+    JSFunction &callee() const {
+        return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
+    }
+    void setCallee(JSFunction &callee) {
+        setFixedSlot(CALLEE_SLOT, ObjectValue(callee));
+    }
+
+    const Value &thisValue() const {
+        return getFixedSlot(THIS_SLOT);
+    }
+    void setThisValue(Value &thisv) {
+        setFixedSlot(THIS_SLOT, thisv);
+    }
+
+    JSObject &scopeChain() const {
+        return getFixedSlot(SCOPE_CHAIN_SLOT).toObject();
+    }
+    void setScopeChain(JSObject &scopeChain) {
+        setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(scopeChain));
+    }
+
+    bool hasArgsObj() const {
+        return getFixedSlot(ARGS_OBJ_SLOT).isObject();
+    }
+    ArgumentsObject &argsObj() const {
+        return getFixedSlot(ARGS_OBJ_SLOT).toObject().as<ArgumentsObject>();
+    }
+    void setArgsObj(ArgumentsObject &argsObj) {
+        setFixedSlot(ARGS_OBJ_SLOT, ObjectValue(argsObj));
+    }
+
+    bool hasExpressionStack() const {
+        return getFixedSlot(EXPRESSION_STACK_SLOT).isObject();
+    }
+    ArrayObject &expressionStack() const {
+        return getFixedSlot(EXPRESSION_STACK_SLOT).toObject().as<ArrayObject>();
+    }
+    void setExpressionStack(ArrayObject &expressionStack) {
+        setFixedSlot(EXPRESSION_STACK_SLOT, ObjectValue(expressionStack));
+    }
+    void clearExpressionStack() {
+        setFixedSlot(EXPRESSION_STACK_SLOT, NullValue());
+    }
+
+    // The bytecode offset slot is abused for a few purposes.  It's undefined if
+    // it hasn't been set yet (before the initial yield), and null if the
+    // generator is closed.  Otherwise, the lower bit is set if the generator
+    // is in the "suspendedStart" set, and cleared otherwise.  The upper bits
+    // are used for the PC offset of the suspended continuation, and are zero if
+    // the generator is running.  If the generator is in that bizarre "closing"
+    // state, all upper bits are set.
+
+    bool isRunning() const {
+        MOZ_ASSERT(!isClosed());
+        return getFixedSlot(BYTECODE_OFFSET_SLOT).toInt32() == 0;
+    }
+    bool isClosing() const {
+        return getFixedSlot(BYTECODE_OFFSET_SLOT).toInt32() == MAX_BYTECODE_OFFSET << 1;
+    }
+    bool isSuspended() const {
+        MOZ_ASSERT(!isClosed());
+        // Note that newborn objects also count as suspended.
+        return !isRunning() && !isClosing();
+    }
+    bool isNewborn() const {
+        MOZ_ASSERT(!isClosed());
+        return getFixedSlot(BYTECODE_OFFSET_SLOT).toInt32() & 0x1;
+    }
+    void setRunning() {
+        MOZ_ASSERT(isSuspended());
+        setFixedSlot(BYTECODE_OFFSET_SLOT, Int32Value(0));
+    }
+    void setClosing() {
+        MOZ_ASSERT(isSuspended());
+        setFixedSlot(BYTECODE_OFFSET_SLOT, Int32Value(MAX_BYTECODE_OFFSET << 1));
+    }
+    ptrdiff_t suspendedBytecodeOffset() const {
+        MOZ_ASSERT(isSuspended());
+        return getFixedSlot(BYTECODE_OFFSET_SLOT).toInt32() >> 1;
+    }
+    void setSuspendedBytecodeOffset(ptrdiff_t offset, bool newborn) {
+        MOZ_ASSERT(newborn ? getFixedSlot(BYTECODE_OFFSET_SLOT).isUndefined() : isRunning());
+        MOZ_ASSERT(offset > 0 && offset < MAX_BYTECODE_OFFSET);
+        setFixedSlot(BYTECODE_OFFSET_SLOT, Int32Value((offset << 1) | (newborn ? 0x1 : 0)));
+        MOZ_ASSERT(isSuspended());
+    }
+    bool isClosed() const {
+        return getFixedSlot(CALLEE_SLOT).isNull();
+    }
+    void setClosed() {
+        setFixedSlot(CALLEE_SLOT, NullValue());
+        setFixedSlot(THIS_SLOT, NullValue());
+        setFixedSlot(SCOPE_CHAIN_SLOT, NullValue());
+        setFixedSlot(ARGS_OBJ_SLOT, NullValue());
+        setFixedSlot(EXPRESSION_STACK_SLOT, NullValue());
+        setFixedSlot(BYTECODE_OFFSET_SLOT, NullValue());
+    }
+};
+
+class LegacyGeneratorObject : public GeneratorObject
 {
   public:
     static const Class class_;
 
-    JSGenerator *getGenerator() { return static_cast<JSGenerator*>(getPrivate()); }
+    static bool close(JSContext *cx, HandleObject obj);
+
+    // Unlike most other methods returning boolean, if this returns false, it
+    // doesn't mean that an error was raised -- it just means that the object
+    // wasn't newborn.
+    static bool maybeCloseNewborn(LegacyGeneratorObject *genObj) {
+        if (genObj->isNewborn()) {
+            genObj->setClosed();
+            return true;
+        }
+        return false;
+    }
 };
 
-class StarGeneratorObject : public NativeObject
+class StarGeneratorObject : public GeneratorObject
 {
   public:
     static const Class class_;
-
-    JSGenerator *getGenerator() { return static_cast<JSGenerator*>(getPrivate()); }
 };
 
 } // namespace js
 
+template<>
+inline bool
+JSObject::is<js::GeneratorObject>() const
+{
+    return is<js::LegacyGeneratorObject>() || is<js::StarGeneratorObject>();
+}
+
 #endif /* vm_GeneratorObject_h */
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -610,16 +610,19 @@ class GlobalObject : public NativeObject
 
     // Infallibly test whether the given value is the eval function for this global.
     bool valueIsEval(Value val);
 
     // Implemented in jsiter.cpp.
     static bool initIteratorClasses(JSContext *cx, Handle<GlobalObject*> global);
     static bool initStopIterationClass(JSContext *cx, Handle<GlobalObject*> global);
 
+    // Implemented in vm/GeneratorObject.cpp.
+    static bool initGeneratorClasses(JSContext *cx, Handle<GlobalObject*> global);
+
     // Implemented in builtin/MapObject.cpp.
     static bool initMapIteratorProto(JSContext *cx, Handle<GlobalObject*> global);
     static bool initSetIteratorProto(JSContext *cx, Handle<GlobalObject*> global);
 
     // Implemented in Intl.cpp.
     static bool initIntlObject(JSContext *cx, Handle<GlobalObject*> global);
     static bool initCollatorProto(JSContext *cx, Handle<GlobalObject*> global);
     static bool initNumberFormatProto(JSContext *cx, Handle<GlobalObject*> global);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -30,16 +30,17 @@
 #include "jsscript.h"
 #include "jsstr.h"
 
 #include "builtin/Eval.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "vm/Debugger.h"
+#include "vm/GeneratorObject.h"
 #include "vm/Opcodes.h"
 #include "vm/Shape.h"
 #include "vm/TraceLogging.h"
 
 #include "jsatominlines.h"
 #include "jsboolinlines.h"
 #include "jsfuninlines.h"
 #include "jsinferinlines.h"
@@ -1652,20 +1653,16 @@ CASE(JSOP_UNUSED183)
 CASE(JSOP_UNUSED185)
 CASE(JSOP_UNUSED186)
 CASE(JSOP_UNUSED187)
 CASE(JSOP_UNUSED189)
 CASE(JSOP_UNUSED190)
 CASE(JSOP_UNUSED191)
 CASE(JSOP_UNUSED192)
 CASE(JSOP_UNUSED196)
-CASE(JSOP_UNUSED201)
-CASE(JSOP_UNUSED205)
-CASE(JSOP_UNUSED206)
-CASE(JSOP_UNUSED207)
 CASE(JSOP_UNUSED208)
 CASE(JSOP_UNUSED209)
 CASE(JSOP_UNUSED210)
 CASE(JSOP_UNUSED211)
 CASE(JSOP_UNUSED212)
 CASE(JSOP_UNUSED213)
 CASE(JSOP_UNUSED219)
 CASE(JSOP_UNUSED220)
@@ -1769,32 +1766,26 @@ CASE(JSOP_RETRVAL)
      * When the inlined frame exits with an exception or an error, ok will be
      * false after the inline_return label.
      */
     CHECK_BRANCH();
 
   successful_return_continuation:
     interpReturnOK = true;
   return_continuation:
-    if (activation.entryFrame() != REGS.fp())
-  inline_return:
-    {
+    if (activation.entryFrame() != REGS.fp()) {
         // Stop the engine. (No details about which engine exactly, could be
         // interpreter, Baseline or IonMonkey.)
         TraceLogStopEvent(logger);
         // Stop the script. (Again no details about which script exactly.)
         TraceLogStopEvent(logger);
 
         interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), interpReturnOK);
 
-        if (!REGS.fp()->isYielding())
-            REGS.fp()->epilogue(cx);
-        else
-            probes::ExitScript(cx, script, script->functionNonDelazifying(),
-                               REGS.fp()->hasPushedSPSFrame());
+        REGS.fp()->epilogue(cx);
 
   jit_return_pop_frame:
 
         activation.popInlineFrame(REGS.fp());
         SET_SCRIPT(REGS.fp()->script());
 
   jit_return:
 
@@ -3375,42 +3366,90 @@ CASE(JSOP_DEBUGLEAVEBLOCK)
         DebugScopes::onPopBlock(cx, REGS.fp(), REGS.pc);
 }
 END_CASE(JSOP_DEBUGLEAVEBLOCK)
 
 CASE(JSOP_GENERATOR)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
     REGS.fp()->initGeneratorFrame();
-    REGS.pc += JSOP_GENERATOR_LENGTH;
-    JSObject *obj = js_NewGenerator(cx, REGS);
+    JSObject *obj = GeneratorObject::create(cx, REGS);
     if (!obj)
         goto error;
-    REGS.fp()->setReturnValue(ObjectValue(*obj));
-    REGS.fp()->setYielding();
-    interpReturnOK = true;
-    if (activation.entryFrame() != REGS.fp())
-        goto inline_return;
-    goto exit;
+    PUSH_OBJECT(*obj);
+}
+END_CASE(JSOP_GENERATOR)
+
+CASE(JSOP_INITIALYIELD)
+{
+    MOZ_ASSERT(!cx->isExceptionPending());
+    MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame());
+    RootedObject &obj = rootObject0;
+    obj = &REGS.sp[-1].toObject();
+    POP_RETURN_VALUE();
+    MOZ_ASSERT(REGS.stackDepth() == 0);
+    if (!GeneratorObject::initialSuspend(cx, obj, REGS.fp(), REGS.pc + JSOP_INITIALYIELD_LENGTH))
+        goto error;
+    goto successful_return_continuation;
 }
 
 CASE(JSOP_YIELD)
+{
     MOZ_ASSERT(!cx->isExceptionPending());
     MOZ_ASSERT(REGS.fp()->isNonEvalFunctionFrame());
-    if (cx->innermostGenerator()->state == JSGEN_CLOSING) {
-        RootedValue &val = rootValue0;
-        val.setObject(REGS.fp()->callee());
-        js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, JSDVG_SEARCH_STACK, val, js::NullPtr());
+    RootedObject &obj = rootObject0;
+    obj = &REGS.sp[-1].toObject();
+    if (!GeneratorObject::normalSuspend(cx, obj, REGS.fp(), REGS.pc + JSOP_YIELD_LENGTH,
+                                        REGS.spForStackDepth(0), REGS.stackDepth() - 2))
+    {
         goto error;
     }
-    REGS.fp()->setReturnValue(REGS.sp[-1]);
-    REGS.fp()->setYielding();
-    REGS.pc += JSOP_YIELD_LENGTH;
-    interpReturnOK = true;
-    goto exit;
+
+    REGS.sp--;
+    POP_RETURN_VALUE();
+
+    goto successful_return_continuation;
+}
+
+CASE(JSOP_RESUME)
+{
+    RootedObject &gen = rootObject0;
+    RootedValue &val = rootValue0;
+    val = REGS.sp[-1];
+    gen = &REGS.sp[-2].toObject();
+    // popInlineFrame expects there to be an additional value on the stack to
+    // pop off, so leave "gen" on the stack.
+
+    GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(REGS.pc);
+    bool ok = GeneratorObject::resume(cx, activation, gen, val, resumeKind);
+    SET_SCRIPT(REGS.fp()->script());
+    if (!ok)
+        goto error;
+
+    ADVANCE_AND_DISPATCH(0);
+}
+
+CASE(JSOP_FINALYIELD)
+    REGS.fp()->setReturnValue(REGS.sp[-2]);
+    REGS.sp[-2] = REGS.sp[-1];
+    REGS.sp--;
+    /* FALL THROUGH */
+CASE(JSOP_FINALYIELDRVAL)
+{
+    RootedObject &gen = rootObject0;
+    gen = &REGS.sp[-1].toObject();
+    REGS.sp--;
+
+    if (!GeneratorObject::finalSuspend(cx, gen)) {
+        interpReturnOK = false;
+        goto return_continuation;
+    }
+
+    goto successful_return_continuation;
+}
 
 CASE(JSOP_ARRAYPUSH)
 {
     RootedObject &obj = rootObject0;
     obj = &REGS.sp[-1].toObject();
     if (!NewbornArrayPush(cx, obj, REGS.sp[-2]))
         goto error;
     REGS.sp -= 2;
@@ -3458,21 +3497,17 @@ DEFAULT()
         ADVANCE_AND_DISPATCH(0);
     }
 
     MOZ_CRASH("Invalid HandleError continuation");
 
   exit:
     interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), interpReturnOK);
 
-    if (!REGS.fp()->isYielding())
-        REGS.fp()->epilogue(cx);
-    else
-        probes::ExitScript(cx, script, script->functionNonDelazifying(),
-                           REGS.fp()->hasPushedSPSFrame());
+    REGS.fp()->epilogue(cx);
 
     gc::MaybeVerifyBarriers(cx, true);
 
     TraceLogStopEvent(logger);
     TraceLogStopEvent(logger, scriptLogId);
 
     /*
      * This path is used when it's guaranteed the method can be finished
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -106,64 +106,57 @@ ExecuteKernel(JSContext *cx, HandleScrip
               ExecuteType type, AbstractFramePtr evalInFrame, Value *result);
 
 /* Execute a script with the given scopeChain as global code. */
 extern bool
 Execute(JSContext *cx, HandleScript script, JSObject &scopeChain, Value *rval);
 
 class ExecuteState;
 class InvokeState;
-class GeneratorState;
 
 // RunState is passed to RunScript and RunScript then eiter passes it to the
 // interpreter or to the JITs. RunState contains all information we need to
 // construct an interpreter or JIT frame.
 class RunState
 {
   protected:
-    enum Kind { Execute, Invoke, Generator };
+    enum Kind { Execute, Invoke };
     Kind kind_;
 
     RootedScript script_;
 
     explicit RunState(JSContext *cx, Kind kind, JSScript *script)
       : kind_(kind),
         script_(cx, script)
     { }
 
   public:
     bool isExecute() const { return kind_ == Execute; }
     bool isInvoke() const { return kind_ == Invoke; }
-    bool isGenerator() const { return kind_ == Generator; }
 
     ExecuteState *asExecute() const {
         MOZ_ASSERT(isExecute());
         return (ExecuteState *)this;
     }
     InvokeState *asInvoke() const {
         MOZ_ASSERT(isInvoke());
         return (InvokeState *)this;
     }
-    GeneratorState *asGenerator() const {
-        MOZ_ASSERT(isGenerator());
-        return (GeneratorState *)this;
-    }
 
     JS::HandleScript script() const { return script_; }
 
     virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx) = 0;
     virtual void setReturnValue(Value v) = 0;
 
     bool maybeCreateThisForConstructor(JSContext *cx);
 
   private:
     RunState(const RunState &other) MOZ_DELETE;
     RunState(const ExecuteState &other) MOZ_DELETE;
     RunState(const InvokeState &other) MOZ_DELETE;
-    RunState(const GeneratorState &other) MOZ_DELETE;
     void operator=(const RunState &other) MOZ_DELETE;
 };
 
 // Eval or global script.
 class ExecuteState : public RunState
 {
     ExecuteType type_;
 
@@ -219,34 +212,16 @@ class InvokeState : public RunState
 
     virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx);
 
     virtual void setReturnValue(Value v) {
         args_.rval().set(v);
     }
 };
 
-// Generator script.
-class GeneratorState : public RunState
-{
-    JSContext *cx_;
-    JSGenerator *gen_;
-    JSGeneratorState futureState_;
-    bool entered_;
-
-  public:
-    GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState);
-    ~GeneratorState();
-
-    virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx);
-    virtual void setReturnValue(Value) { }
-
-    JSGenerator *gen() const { return gen_; }
-};
-
 extern bool
 RunScript(JSContext *cx, RunState &state);
 
 extern bool
 StrictlyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *equal);
 
 extern bool
 LooselyEqual(JSContext *cx, const Value &lval, const Value &rval, bool *equal);
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1546,53 +1546,83 @@ 1234567890123456789012345678901234567890
      * The opcode to assist the debugger.
      *   Category: Statements
      *   Type: Debugger
      *   Operands:
      *   Stack: =>
      */ \
     macro(JSOP_DEBUGLEAVEBLOCK, 200,"debugleaveblock", NULL, 1,  0,  0,  JOF_BYTE) \
     \
-    macro(JSOP_UNUSED201,     201,"unused201",  NULL,     1,  0,  0,  JOF_BYTE) \
-    \
+    /*
+     * Initializes generator frame, creates a generator and pushes it on the
+     * stack.
+     *   Category: Statements
+     *   Type: Generator
+     *   Operands:
+     *   Stack: => generator
+     */ \
+    macro(JSOP_GENERATOR,     201,"generator",   NULL,    1,  0,  1,  JOF_BYTE) \
     /*
-     * Initializes generator frame, creates a generator, sets 'YIELDING' flag,
-     * stops interpretation and returns the generator.
+     * Pops the generator from the top of the stack, suspends it and stops
+     * interpretation.
+     *   Category: Statements
+     *   Type: Generator
+     *   Operands:
+     *   Stack: generator =>
+     */ \
+    macro(JSOP_INITIALYIELD,  202,"initialyield", NULL,   1,  1,  0,  JOF_BYTE) \
+    /*
+     * Pops the generator and the return value 'rval1', stops interpretation and
+     * returns 'rval1'. Pushes sent value from 'send()' onto the stack.
      *   Category: Statements
      *   Type: Generator
      *   Operands:
-     *   Stack: =>
+     *   Stack: rval1, gen => rval2
      */ \
-    macro(JSOP_GENERATOR,     202,"generator",   NULL,    1,  0,  0,  JOF_BYTE) \
+    macro(JSOP_YIELD,         203,"yield",       NULL,    1,  2,  1,  JOF_BYTE) \
     /*
-     * Pops the top of stack value as 'rval1', sets 'YIELDING' flag,
-     * stops interpretation and returns 'rval1', pushes sent value from
-     * 'send()' onto the stack.
+     * Pops the generator and the value to yield from the stack. Then suspends
+     * and closes the generator.
      *   Category: Statements
      *   Type: Generator
      *   Operands:
-     *   Stack: rval1 => rval2
+     *   Stack: gen, val =>
+     */ \