Merge m-c to autoland, a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 10 Aug 2016 16:56:26 -0700
changeset 309109 a2a3d88d28d0875938104b4352817abeaf7974c9
parent 309108 548a0e44482a6835ca7e870ecd40fa5ad5410de8 (current diff)
parent 308976 0502bd9e025edde29777ba1de4280f9b52af4663 (diff)
child 309110 6c2bac3a1afce83def2133e58c30ca1cf49887ff
push id20299
push userkwierso@gmail.com
push dateFri, 12 Aug 2016 23:39:35 +0000
treeherderfx-team@1ba6215e84c3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone51.0a1
Merge m-c to autoland, a=merge
devtools/client/debugger/new/images/moz.build
old-configure.in
taskcluster/ci/legacy/tasks/builds/dbg_linux64-asan.yml
taskcluster/ci/legacy/tasks/builds/opt_linux64-asan.yml
testing/web-platform/meta/html/syntax/serializing-html-fragments/initial-linefeed-pre.html.ini
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1457,22 +1457,23 @@ DocAccessible::DoInitialUpdate()
   // this reorder event is processed by parent document then events targeted to
   // this document may be fired prior to this reorder event. If this is
   // a problem then consider to keep event processing per tab document.
   if (!IsRoot()) {
     RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent());
     ParentDocument()->FireDelayedEvent(reorderEvent);
   }
 
+  TreeMutation mt(this);
   uint32_t childCount = ChildCount();
   for (uint32_t i = 0; i < childCount; i++) {
     Accessible* child = GetChildAt(i);
-    RefPtr<AccShowEvent> event = new AccShowEvent(child);
-  FireDelayedEvent(event);
+    mt.AfterInsertion(child);
   }
+  mt.Done();
 }
 
 void
 DocAccessible::ProcessLoad()
 {
   mLoadState |= eCompletelyLoaded;
 
 #ifdef A11Y_LOG
--- a/accessible/html/HTMLSelectAccessible.cpp
+++ b/accessible/html/HTMLSelectAccessible.cpp
@@ -11,16 +11,17 @@
 #include "DocAccessible.h"
 #include "nsEventShell.h"
 #include "nsTextEquivUtils.h"
 #include "Role.h"
 #include "States.h"
 
 #include "nsCOMPtr.h"
 #include "mozilla/dom/HTMLOptionElement.h"
+#include "mozilla/dom/HTMLSelectElement.h"
 #include "nsIComboboxControlFrame.h"
 #include "nsContainerFrame.h"
 #include "nsIListControlFrame.h"
 
 using namespace mozilla::a11y;
 using namespace mozilla::dom;
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -493,29 +494,25 @@ HTMLComboboxAccessible::SetCurrentItem(A
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLComboboxAccessible: protected
 
 Accessible*
 HTMLComboboxAccessible::SelectedOption() const
 {
-  nsIFrame* frame = GetFrame();
-  nsIComboboxControlFrame* comboboxFrame = do_QueryFrame(frame);
-  if (!comboboxFrame)
-    return nullptr;
+  HTMLSelectElement* select = HTMLSelectElement::FromContent(mContent);
+  int32_t selectedIndex = select->SelectedIndex();
 
-  nsIListControlFrame* listControlFrame =
-    do_QueryFrame(comboboxFrame->GetDropDown());
-  if (listControlFrame) {
-    nsCOMPtr<nsIContent> activeOptionNode = listControlFrame->GetCurrentOption();
-    if (activeOptionNode) {
+  if (selectedIndex >= 0) {
+    HTMLOptionElement* option = select->Item(selectedIndex);
+    if (option) {
       DocAccessible* document = Document();
       if (document)
-        return document->GetAccessible(activeOptionNode);
+        return document->GetAccessible(option);
     }
   }
 
   return nullptr;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/b2g/components/B2GPresentationDevicePrompt.js
+++ b/b2g/components/B2GPresentationDevicePrompt.js
@@ -42,23 +42,23 @@ B2GPresentationDevicePrompt.prototype = 
       SystemAppProxy.removeEventListener("mozContentEvent", contentEvent);
 
       switch (detail.type) {
         case "presentation-select-result":
           debug("device " + detail.deviceId + " is selected by user");
           let device = self._getDeviceById(detail.deviceId);
           if (!device) {
             debug("cancel request because device is not found");
-            aRequest.cancel();
+            aRequest.cancel(Cr.NS_ERROR_DOM_NOT_FOUND_ERR);
           }
           aRequest.select(device);
           break;
         case "presentation-select-deny":
           debug("request canceled by user");
-          aRequest.cancel();
+          aRequest.cancel(Cr.NS_ERROR_DOM_NOT_ALLOWED_ERR);
           break;
       }
     });
 
     let detail = {
       type: "presentation-select-device",
       origin: aRequest.origin,
       requestURL: aRequest.requestURL,
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -199,16 +199,23 @@ static int do_main(int argc, char* argv[
 
   if (appini) {
     nsXREAppData *appData;
     rv = XRE_CreateAppData(appini, &appData);
     if (NS_FAILED(rv)) {
       Output("Couldn't read application.ini");
       return 255;
     }
+#if defined(HAS_DLL_BLOCKLIST)
+    // The dll blocklist operates in the exe vs. xullib. Pass a flag to
+    // xullib so automated tests can check the result once the browser
+    // is up and running.
+    appData->flags |=
+      DllBlocklist_CheckStatus() ? NS_XRE_DLL_BLOCKLIST_ENABLED : 0;
+#endif
     // xreDirectory already has a refcount from NS_NewLocalFile
     appData->xreDirectory = xreDirectory;
     int result = XRE_main(argc, argv, appData, mainFlags);
     XRE_FreeAppData(appData);
     return result;
   }
 
   ScopedAppData appData(&sAppData);
@@ -227,16 +234,21 @@ static int do_main(int argc, char* argv[
   nsCOMPtr<nsIFile> appSubdir;
   greDir->Clone(getter_AddRefs(appSubdir));
   appSubdir->Append(NS_LITERAL_STRING(kDesktopFolder));
 
   SetStrongPtr(appData.directory, static_cast<nsIFile*>(appSubdir.get()));
   // xreDirectory already has a refcount from NS_NewLocalFile
   appData.xreDirectory = xreDirectory;
 
+#if defined(HAS_DLL_BLOCKLIST)
+  appData.flags |=
+    DllBlocklist_CheckStatus() ? NS_XRE_DLL_BLOCKLIST_ENABLED : 0;
+#endif
+
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
   sandbox::BrokerServices* brokerServices =
     sandboxing::GetInitializedBrokerServices();
 #if defined(MOZ_CONTENT_SANDBOX)
   if (!brokerServices) {
     Output("Couldn't initialize the broker services.\n");
     return 255;
   }
--- a/browser/base/content/browser-media.js
+++ b/browser/base/content/browser-media.js
@@ -62,17 +62,20 @@ var gEMEHandler = {
     }
 
     let notificationId;
     let buttonCallback;
     let params = [];
     switch (status) {
       case "available":
       case "cdm-created":
-        this.showPopupNotificationForSuccess(browser, keySystem);
+        // Only show the chain icon for proprietary CDMs. Clearkey is not one.
+        if (keySystem != "org.w3.clearkey") {
+          this.showPopupNotificationForSuccess(browser, keySystem);
+        }
         // ... and bail!
         return;
 
       case "api-disabled":
       case "cdm-disabled":
         notificationId = "drmContentDisabled";
         buttonCallback = gEMEHandler.ensureEMEEnabled.bind(gEMEHandler, browser, keySystem)
         params = [this.getLearnMoreLink(notificationId)];
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1414,24 +1414,31 @@ var BookmarkingUI = {
 
     let onBookmarksMenuHidden = event => {
       if (event.target == event.currentTarget) {
         updatePlacesContextMenu(true);
 
         Services.prefs.removeObserver(this.RECENTLY_BOOKMARKED_PREF, prefObserver, false);
         PlacesUtils.bookmarks.removeObserver(this._recentlyBookmarkedObserver);
         this._recentlyBookmarkedObserver = null;
-        placesContextMenu.removeEventListener("popupshowing", onPlacesContextMenuShowing);
+        if (placesContextMenu) {
+          placesContextMenu.removeEventListener("popupshowing", onPlacesContextMenuShowing);
+        }
         bookmarksMenu.removeEventListener("popuphidden", onBookmarksMenuHidden);
       }
     };
 
     Services.prefs.addObserver(this.RECENTLY_BOOKMARKED_PREF, prefObserver, false);
     PlacesUtils.bookmarks.addObserver(this._recentlyBookmarkedObserver, true);
-    placesContextMenu.addEventListener("popupshowing", onPlacesContextMenuShowing);
+
+    // The context menu doesn't exist in non-browser windows on Mac
+    if (placesContextMenu) {
+      placesContextMenu.addEventListener("popupshowing", onPlacesContextMenuShowing);
+    }
+
     bookmarksMenu.addEventListener("popuphidden", onBookmarksMenuHidden);
   },
 
   _populateRecentBookmarks(aHeaderItem, aExtraCSSClass = "") {
     while (aHeaderItem.nextSibling &&
            aHeaderItem.nextSibling.localName == "menuitem") {
       aHeaderItem.nextSibling.remove();
     }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6772,16 +6772,32 @@ var gIdentityHandler = {
     // this is called before the first page load in the window for any reason.
     if (!this._uri) {
       Cu.reportError("Unexpected early call to refreshForInsecureLoginForms.");
       return;
     }
     this.refreshIdentityBlock();
   },
 
+  updateSharingIndicator() {
+    let tab = gBrowser.selectedTab;
+    let sharing = tab.getAttribute("sharing");
+    if (sharing)
+      this._identityBox.setAttribute("sharing", sharing);
+    else
+      this._identityBox.removeAttribute("sharing");
+
+    this._sharingState = tab._sharingState;
+
+    if (this._identityPopup.state == "open") {
+      this.updateSitePermissions();
+      this._identityPopupMultiView.setHeightToFit();
+    }
+  },
+
   /**
    * Attempt to provide proper IDN treatment for host names
    */
   getEffectiveHost: function() {
     if (!this._IDNService)
       this._IDNService = Cc["@mozilla.org/network/idn-service;1"]
                          .getService(Ci.nsIIDNService);
     try {
@@ -7228,48 +7244,99 @@ var gIdentityHandler = {
   },
 
   updateSitePermissions: function () {
     while (this._permissionList.hasChildNodes())
       this._permissionList.removeChild(this._permissionList.lastChild);
 
     let uri = gBrowser.currentURI;
 
-    for (let permission of SitePermissions.getPermissionDetailsByURI(uri)) {
+    let permissions = SitePermissions.getPermissionDetailsByURI(uri);
+    if (this._sharingState) {
+      // If WebRTC device or screen permissions are in use, we need to find
+      // the associated permission item to set the inUse field to true.
+      for (let id of ["camera", "microphone", "screen"]) {
+        if (this._sharingState[id]) {
+          let found = false;
+          for (let permission of permissions) {
+            if (permission.id != id)
+              continue;
+            found = true;
+            permission.inUse = true;
+            break;
+          }
+          if (!found) {
+            // If the permission item we were looking for doesn't exist,
+            // the user has temporarily allowed sharing and we need to add
+            // an item in the permissions array to reflect this.
+            let permission = SitePermissions.getPermissionItem(id);
+            permission.inUse = true;
+            permissions.push(permission);
+          }
+        }
+      }
+    }
+    for (let permission of permissions) {
       let item = this._createPermissionItem(permission);
       this._permissionList.appendChild(item);
     }
   },
 
   _createPermissionItem: function (aPermission) {
     let container = document.createElement("hbox");
     container.setAttribute("class", "identity-popup-permission-item");
     container.setAttribute("align", "center");
 
     let img = document.createElement("image");
-    let isBlocked = (aPermission.state == SitePermissions.BLOCK) ? " blocked" : "";
-    img.setAttribute("class",
-      "identity-popup-permission-icon " + aPermission.id + "-icon" + isBlocked);
+    let classes = "identity-popup-permission-icon " + aPermission.id + "-icon";
+    if (aPermission.state == SitePermissions.BLOCK)
+      classes += " blocked";
+    if (aPermission.inUse)
+      classes += " in-use";
+    img.setAttribute("class", classes);
 
     let nameLabel = document.createElement("label");
     nameLabel.setAttribute("flex", "1");
     nameLabel.setAttribute("class", "identity-popup-permission-label");
     nameLabel.textContent = SitePermissions.getPermissionLabel(aPermission.id);
 
     let stateLabel = document.createElement("label");
     stateLabel.setAttribute("flex", "1");
     stateLabel.setAttribute("class", "identity-popup-permission-state-label");
     stateLabel.textContent = SitePermissions.getStateLabel(
-      aPermission.id, aPermission.state);
+      aPermission.id, aPermission.state, aPermission.inUse || false);
 
     let button = document.createElement("button");
     button.setAttribute("class", "identity-popup-permission-remove-button");
     button.addEventListener("command", () => {
       this._permissionList.removeChild(container);
       this._identityPopupMultiView.setHeightToFit();
+      if (aPermission.inUse &&
+          ["camera", "microphone", "screen"].includes(aPermission.id)) {
+        let windowId = this._sharingState.windowId;
+        if (aPermission.id == "screen") {
+          windowId = "screen:" + windowId;
+        } else {
+          // If we set persistent permissions or the sharing has
+          // started due to existing persistent permissions, we need
+          // to handle removing these even for frames with different hostnames.
+          let uris = gBrowser.selectedBrowser._devicePermissionURIs || [];
+          for (let uri of uris) {
+            // It's not possible to stop sharing one of camera/microphone
+            // without the other.
+            for (let id of ["camera", "microphone"]) {
+              if (this._sharingState[id] &&
+                  SitePermissions.get(uri, id) == SitePermissions.ALLOW)
+                SitePermissions.remove(uri, id);
+            }
+          }
+        }
+        let mm = gBrowser.selectedBrowser.messageManager;
+        mm.sendAsyncMessage("webrtc:StopSharing", windowId);
+      }
       SitePermissions.remove(gBrowser.currentURI, aPermission.id);
     });
 
     container.appendChild(img);
     container.appendChild(nameLabel);
     container.appendChild(stateLabel);
     container.appendChild(button);
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -701,16 +701,17 @@
                    align="center"
                    aria-label="&urlbar.viewSiteInfo.label;"
                    onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
                    onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
                    ondragstart="gIdentityHandler.onDragStart(event);">
                 <image id="identity-icon"
                        consumeanchor="identity-box"
                        onclick="PageProxyClickHandler(event);"/>
+                <image id="sharing-icon" mousethrough="always"/>
                 <box id="blocked-permissions-container" align="center" tooltiptext="">
                   <image data-permission-id="geo" class="notification-anchor-icon geo-icon blocked" role="button"
                          aria-label="&urlbar.geolocationNotificationAnchor.label;"/>
                   <image data-permission-id="desktop-notification" class="notification-anchor-icon desktop-notification-icon blocked" role="button"
                          aria-label="&urlbar.webNotsNotificationAnchor3.label;"/>
                   <image data-permission-id="camera" class="notification-anchor-icon camera-icon blocked" role="button"
                          aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
                   <image data-permission-id="indexedDB" class="notification-anchor-icon indexedDB-icon blocked" role="button"
@@ -741,26 +742,20 @@
                   <image id="password-notification-icon" class="notification-anchor-icon login-icon" role="button"
                          aria-label="&urlbar.passwordNotificationAnchor.label;"/>
                   <image id="plugins-notification-icon" class="notification-anchor-icon plugin-icon" role="button"
                          aria-label="&urlbar.pluginsNotificationAnchor.label;"/>
                   <image id="web-notifications-notification-icon" class="notification-anchor-icon desktop-notification-icon" role="button"
                          aria-label="&urlbar.webNotsNotificationAnchor3.label;"/>
                   <image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon camera-icon" role="button"
                          aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
-                  <image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon camera-icon in-use" role="button"
-                         aria-label="&urlbar.webRTCSharingDevicesNotificationAnchor.label;"/>
                   <image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon microphone-icon" role="button"
                          aria-label="&urlbar.webRTCShareMicrophoneNotificationAnchor.label;"/>
-                  <image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon microphone-icon in-use" role="button"
-                         aria-label="&urlbar.webRTCSharingMicrophoneNotificationAnchor.label;"/>
                   <image id="webRTC-shareScreen-notification-icon" class="notification-anchor-icon screen-icon" role="button"
                          aria-label="&urlbar.webRTCShareScreenNotificationAnchor.label;"/>
-                  <image id="webRTC-sharingScreen-notification-icon" class="notification-anchor-icon screen-icon in-use" role="button"
-                         aria-label="&urlbar.webRTCSharingScreenNotificationAnchor.label;"/>
                   <image id="pointerLock-notification-icon" class="notification-anchor-icon pointerLock-icon" role="button"
                          aria-label="&urlbar.pointerLockNotificationAnchor.label;"/>
                   <image id="servicesInstall-notification-icon" class="notification-anchor-icon service-icon" role="button"
                          aria-label="&urlbar.servicesNotificationAnchor.label;"/>
                   <image id="translate-notification-icon" class="notification-anchor-icon translation-icon" role="button"
                          aria-label="&urlbar.translateNotificationAnchor.label;"/>
                   <image id="translated-notification-icon" class="notification-anchor-icon translation-icon in-use" role="button"
                          aria-label="&urlbar.translatedNotificationAnchor.label;"/>
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -730,17 +730,17 @@ nsContextMenu.prototype = {
       else if (this.target instanceof HTMLCanvasElement) {
         this.onCanvas = true;
       }
       else if (this.target instanceof HTMLVideoElement) {
         let mediaURL = this.target.currentSrc || this.target.src;
         if (this.isMediaURLReusable(mediaURL)) {
           this.mediaURL = mediaURL;
         }
-        if (this.target.isEncrypted) {
+        if (this._isProprietaryDRM()) {
           this.onDRMMedia = true;
         }
         // Firefox always creates a HTMLVideoElement when loading an ogg file
         // directly. If the media is actually audio, be smarter and provide a
         // context menu with audio operations.
         if (this.target.readyState >= this.target.HAVE_METADATA &&
             (this.target.videoWidth == 0 || this.target.videoHeight == 0)) {
           this.onAudio = true;
@@ -749,17 +749,17 @@ nsContextMenu.prototype = {
         }
       }
       else if (this.target instanceof HTMLAudioElement) {
         this.onAudio = true;
         let mediaURL = this.target.currentSrc || this.target.src;
         if (this.isMediaURLReusable(mediaURL)) {
           this.mediaURL = mediaURL;
         }
-        if (this.target.isEncrypted) {
+        if (this._isProprietaryDRM()) {
           this.onDRMMedia = true;
         }
       }
       else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) {
         this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
         this.onNumeric = (editFlags & SpellCheckHelper.NUMERIC) !== 0;
         this.onEditableArea = (editFlags & SpellCheckHelper.EDITABLE) !== 0;
         this.onPassword = (editFlags & SpellCheckHelper.PASSWORD) !== 0;
@@ -963,16 +963,21 @@ nsContextMenu.prototype = {
     }
     if (!editable) {
       return false;
     }
     // Otherwise make sure that nothing in the parent chain disables spellchecking
     return aNode.spellcheck;
   },
 
+  _isProprietaryDRM: function() {
+    return this.target.isEncrypted && this.target.mediaKeys &&
+           this.target.mediaKeys.keySystem != "org.w3.clearkey";
+  },
+
   _openLinkInParameters : function (extra) {
     let params = { charset: gContextMenuContentData.charSet,
                    referrerURI: gContextMenuContentData.documentURIObject,
                    referrerPolicy: gContextMenuContentData.referrerPolicy,
                    noReferrer: this.linkHasNoReferrer };
     for (let p in extra) {
       params[p] = extra[p];
     }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1220,16 +1220,17 @@
                 document.activeElement.blur();
               }
 
               if (!gMultiProcessBrowser)
                 this._adjustFocusAfterTabSwitch(this.mCurrentTab);
             }
 
             updateUserContextUIIndicator();
+            gIdentityHandler.updateSharingIndicator();
 
             this.tabContainer._setPositionalAttributes();
 
             if (!gMultiProcessBrowser) {
               let event = new CustomEvent("TabSwitchDone", {
                 bubbles: true,
                 cancelable: true
               });
@@ -1364,27 +1365,42 @@
             }
           });
           aTab.dispatchEvent(event);
         ]]></body>
       </method>
 
       <method name="setBrowserSharing">
         <parameter name="aBrowser"/>
-        <parameter name="aSharingState"/>
+        <parameter name="aState"/>
         <body><![CDATA[
           let tab = this.getTabForBrowser(aBrowser);
           if (!tab)
             return;
 
-          if (aSharingState)
-            tab.setAttribute("sharing", aSharingState);
-          else
+          let sharing;
+          if (aState.screen) {
+            sharing = "screen";
+          } else if (aState.camera) {
+            sharing = "camera";
+          } else if (aState.microphone) {
+            sharing = "microphone";
+          }
+
+          if (sharing) {
+            tab.setAttribute("sharing", sharing);
+            tab._sharingState = aState;
+          } else {
             tab.removeAttribute("sharing");
+            tab._sharingState = null;
+          }
           this._tabAttrModified(tab, ["sharing"]);
+
+          if (aBrowser == this.mCurrentBrowser)
+            gIdentityHandler.updateSharingIndicator();
         ]]></body>
       </method>
 
 
       <method name="setTabTitleLoading">
         <parameter name="aTab"/>
         <body>
           <![CDATA[
--- a/browser/base/content/test/urlbar/browser_urlbarSearchSingleWordNotification.js
+++ b/browser/base/content/test/urlbar/browser_urlbarSearchSingleWordNotification.js
@@ -61,23 +61,29 @@ function* runURLBarSearchTest(valueToOpe
 
 add_task(function* test_navigate_full_domain() {
   let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
   yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   yield* runURLBarSearchTest("www.mozilla.org", false, false);
   gBrowser.removeTab(tab);
 });
 
-add_task(function* test_navigate_numbers() {
+add_task(function* test_navigate_valid_numbers() {
   let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
   yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
-  yield* runURLBarSearchTest("1234", true, false);
+  yield* runURLBarSearchTest("1234", true, true);
   gBrowser.removeTab(tab);
 });
 
+add_task(function* test_navigate_invalid_numbers() {
+  let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
+  yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  yield* runURLBarSearchTest("123456789012345", true, false);
+  gBrowser.removeTab(tab);
+});
 function get_test_function_for_localhost_with_hostname(hostName, isPrivate) {
   return function* test_navigate_single_host() {
     const pref = "browser.fixup.domainwhitelist.localhost";
     let win;
     if (isPrivate) {
       win = yield promiseOpenAndLoadWindow({private: true}, true);
       let deferredOpenFocus = Promise.defer();
       waitForFocus(deferredOpenFocus.resolve, win);
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media.js
@@ -134,31 +134,17 @@ var gTests = [
     yield expectObserverCalled("getUserMedia:response:allow");
     yield expectObserverCalled("recording-device-events");
     is((yield getMediaCaptureState()), "CameraAndMicrophone",
        "expected camera and microphone to be shared");
 
     yield indicator;
     yield checkSharingUI({video: true, audio: true});
 
-    yield promiseNotificationShown(PopupNotifications.getNotification("webRTC-sharingDevices"));
-    activateSecondaryAction(kActionDeny);
-
-    yield promiseObserverCalled("recording-device-events");
-    yield expectObserverCalled("getUserMedia:revoke");
-
-    yield promiseNoPopupNotification("webRTC-sharingDevices");
-    yield expectObserverCalled("recording-window-ended");
-
-    if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
-      todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
-    }
-
-    yield expectNoObserverCalled();
-    yield checkNotSharing();
+    yield stopSharing();
 
     // the stream is already closed, but this will do some cleanup anyway
     yield closeStream(true);
   }
 },
 
 {
   desc: "getUserMedia audio+video: reloading the page removes all gUM UI",
@@ -176,24 +162,21 @@ var gTests = [
     yield expectObserverCalled("getUserMedia:response:allow");
     yield expectObserverCalled("recording-device-events");
     is((yield getMediaCaptureState()), "CameraAndMicrophone",
        "expected camera and microphone to be shared");
 
     yield indicator;
     yield checkSharingUI({video: true, audio: true});
 
-    yield promiseNotificationShown(PopupNotifications.getNotification("webRTC-sharingDevices"));
-
     info("reloading the web page");
     promise = promiseObserverCalled("recording-device-events");
     content.location.reload();
     yield promise;
 
-    yield promiseNoPopupNotification("webRTC-sharingDevices");
     if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
       todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
     }
 
     yield expectObserverCalled("recording-window-ended");
     yield expectNoObserverCalled();
     yield checkNotSharing();
   }
@@ -414,37 +397,17 @@ var gTests = [
       yield promise;
 
       yield expectObserverCalled("getUserMedia:request");
       yield expectObserverCalled("getUserMedia:response:allow");
       yield expectObserverCalled("recording-device-events");
       yield indicator;
       yield checkSharingUI({video: aRequestVideo, audio: aRequestAudio});
 
-      yield promiseNotificationShown(PopupNotifications.getNotification("webRTC-sharingDevices"));
-      let expectedIcon = "webRTC-sharingDevices";
-      if (aRequestAudio && !aRequestVideo)
-        expectedIcon = "webRTC-sharingMicrophone";
-      is(PopupNotifications.getNotification("webRTC-sharingDevices").anchorID,
-         expectedIcon + "-notification-icon", "anchored to correct icon");
-      is(PopupNotifications.panel.firstChild.getAttribute("popupid"), expectedIcon,
-         "panel using correct icon");
-
-      // Stop sharing.
-      activateSecondaryAction(kActionDeny);
-
-      yield promiseObserverCalled("recording-device-events");
-      yield expectObserverCalled("getUserMedia:revoke");
-
-      yield promiseNoPopupNotification("webRTC-sharingDevices");
-      yield expectObserverCalled("recording-window-ended");
-
-      if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
-        todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
-      }
+      yield stopSharing(aRequestVideo ? "camera" : "microphone");
 
       // Check that permissions have been removed as expected.
       let audioPerm = Perms.testExactPermission(uri, "microphone");
       if (aRequestAudio)
         is(audioPerm, Perms.UNKNOWN_ACTION, "microphone permissions removed");
       else
         is(audioPerm, Perms.ALLOW_ACTION, "microphone permissions untouched");
 
@@ -466,18 +429,18 @@ var gTests = [
     info("request audio, stop sharing resets audio only");
     yield stopAndCheckPerm(true, false);
     info("request video, stop sharing resets video only");
     yield stopAndCheckPerm(false, true);
   }
 },
 
 {
-  desc: "test showSharingDoorhanger",
-  run: function checkShowSharingDoorhanger() {
+  desc: "test showControlCenter",
+  run: function checkShowControlCenter() {
     let promise = promisePopupNotificationShown("webRTC-shareDevices");
     yield promiseRequestDevice(false, true);
     yield promise;
     yield expectObserverCalled("getUserMedia:request");
     checkDeviceSelectors(false, true);
 
     let indicator = promiseIndicatorWindow();
     yield promiseMessage("ok", () => {
@@ -485,31 +448,31 @@ var gTests = [
     });
     yield expectObserverCalled("getUserMedia:response:allow");
     yield expectObserverCalled("recording-device-events");
     is((yield getMediaCaptureState()), "Camera", "expected camera to be shared");
 
     yield indicator;
     yield checkSharingUI({video: true});
 
-    yield promisePopupNotificationShown("webRTC-sharingDevices", () => {
-      if ("nsISystemStatusBar" in Ci) {
-        let activeStreams = webrtcUI.getActiveStreams(true, false, false);
-        webrtcUI.showSharingDoorhanger(activeStreams[0], "Devices");
-      }
-      else {
-        let win =
-          Services.wm.getMostRecentWindow("Browser:WebRTCGlobalIndicator");
-        let elt = win.document.getElementById("audioVideoButton");
-        EventUtils.synthesizeMouseAtCenter(elt, {}, win);
-      }
-    });
+    ok(gIdentityHandler._identityPopup.hidden, "control center should be hidden");
+    if ("nsISystemStatusBar" in Ci) {
+      let activeStreams = webrtcUI.getActiveStreams(true, false, false);
+      webrtcUI.showSharingDoorhanger(activeStreams[0], "Devices");
+    }
+    else {
+      let win =
+        Services.wm.getMostRecentWindow("Browser:WebRTCGlobalIndicator");
+      let elt = win.document.getElementById("audioVideoButton");
+      EventUtils.synthesizeMouseAtCenter(elt, {}, win);
+      yield promiseWaitForCondition(() => !gIdentityHandler._identityPopup.hidden);
+    }
+    ok(!gIdentityHandler._identityPopup.hidden, "control center should be open");
 
-    PopupNotifications.panel.firstChild.button.click();
-    ok(!PopupNotifications.isPanelOpen, "notification panel closed");
+    gIdentityHandler._identityPopup.hidden = true;
     yield expectNoObserverCalled();
 
     yield closeStream();
   }
 },
 
 {
   desc: "'Always Allow' ignored and not shown on http pages",
@@ -561,16 +524,18 @@ function test() {
 
   browser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
 
   browser.addEventListener("load", function onload() {
     browser.removeEventListener("load", onload, true);
 
     is(PopupNotifications._currentNotifications.length, 0,
        "should start the test without any prior popup notification");
+    ok(gIdentityHandler._identityPopup.hidden,
+       "should start the test with the control center hidden");
 
     Task.spawn(function () {
       yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
 
       for (let test of gTests) {
         info(test.desc);
         yield test.run();
 
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_in_frame.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_in_frame.js
@@ -66,31 +66,17 @@ var gTests = [
 
     let Perms = Services.perms;
     let uri = Services.io.newURI("https://example.com/", null, null);
     is(Perms.testExactPermission(uri, "microphone"), Perms.ALLOW_ACTION,
                                  "microphone persistently allowed");
     is(Perms.testExactPermission(uri, "camera"), Perms.ALLOW_ACTION,
                                  "camera persistently allowed");
 
-    yield promiseNotificationShown(PopupNotifications.getNotification("webRTC-sharingDevices"));
-    activateSecondaryAction(kActionDeny);
-
-    yield promiseObserverCalled("recording-device-events");
-    yield expectObserverCalled("getUserMedia:revoke");
-
-    yield promiseNoPopupNotification("webRTC-sharingDevices");
-    yield expectObserverCalled("recording-window-ended");
-
-    if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
-      todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
-    }
-
-    yield expectNoObserverCalled();
-    yield checkNotSharing();
+    yield stopSharing();
 
     // The persistent permissions for the frame should have been removed.
     is(Perms.testExactPermission(uri, "microphone"), Perms.UNKNOWN_ACTION,
                                  "microphone not persistently allowed");
     is(Perms.testExactPermission(uri, "camera"), Perms.UNKNOWN_ACTION,
                                  "camera not persistently allowed");
 
     // the stream is already closed, but this will do some cleanup anyway
@@ -119,17 +105,16 @@ var gTests = [
     yield indicator;
     yield checkSharingUI({video: true, audio: true});
 
     info("reloading the frame");
     promise = promiseObserverCalled("recording-device-events");
     yield promiseReloadFrame("frame1");
     yield promise;
 
-    yield promiseNoPopupNotification("webRTC-sharingDevices");
     if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
       todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
     }
     yield expectObserverCalled("recording-window-ended");
     yield expectNoObserverCalled();
     yield checkNotSharing();
   }
 },
@@ -196,25 +181,24 @@ var gTests = [
     yield checkSharingUI({video: true, audio: true});
     yield expectNoObserverCalled();
 
     info("reloading the second frame");
     promise = promiseObserverCalled("recording-device-events");
     yield promiseReloadFrame("frame2");
     yield promise;
 
+    yield expectObserverCalled("recording-window-ended");
     yield checkSharingUI({video: false, audio: true});
-    yield expectObserverCalled("recording-window-ended");
     if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
       todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
     }
     yield expectNoObserverCalled();
 
     yield closeStream(false, "frame1");
-    yield promiseNoPopupNotification("webRTC-sharingDevices");
     yield expectNoObserverCalled();
     yield checkNotSharing();
   }
 }
 
 ];
 
 function test() {
--- a/browser/base/content/test/webrtc/head.js
+++ b/browser/base/content/test/webrtc/head.js
@@ -326,49 +326,68 @@ function getMediaCaptureState() {
     let mm = _mm();
     mm.addMessageListener("Test:MediaCaptureState", ({data}) => {
       resolve(data);
     });
     mm.sendAsyncMessage("Test:GetMediaCaptureState");
   });
 }
 
+function* stopSharing(aType = "camera") {
+  let promiseRecordingEvent = promiseObserverCalled("recording-device-events");
+  gIdentityHandler._identityBox.click();
+  let permissions = document.getElementById("identity-popup-permission-list");
+  let cancelButton =
+    permissions.querySelector(".identity-popup-permission-icon." + aType + "-icon ~ " +
+                              ".identity-popup-permission-remove-button");
+  cancelButton.click();
+  gIdentityHandler._identityPopup.hidden = true;
+  yield promiseRecordingEvent;
+  yield expectObserverCalled("getUserMedia:revoke");
+  yield expectObserverCalled("recording-window-ended");
+
+  if ((yield promiseTodoObserverNotCalled("recording-device-events")) == 1) {
+    todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
+  }
+
+  yield expectNoObserverCalled();
+  yield* checkNotSharing();
+}
+
 function promiseRequestDevice(aRequestAudio, aRequestVideo, aFrameId, aType) {
   info("requesting devices");
   return ContentTask.spawn(gBrowser.selectedBrowser,
                            {aRequestAudio, aRequestVideo, aFrameId, aType},
                            function*(args) {
     let global = content.wrappedJSObject;
     if (args.aFrameId)
       global = global.document.getElementById(args.aFrameId).contentWindow;
     global.requestDevice(args.aRequestAudio, args.aRequestVideo, args.aType);
   });
 }
 
 function* closeStream(aAlreadyClosed, aFrameId) {
   yield expectNoObserverCalled();
 
-  let promise;
-  if (!aAlreadyClosed)
-    promise = promiseObserverCalled("recording-device-events");
+  let promises;
+  if (!aAlreadyClosed) {
+    promises = [promiseObserverCalled("recording-device-events"),
+                promiseObserverCalled("recording-window-ended")];
+  }
 
   info("closing the stream");
   yield ContentTask.spawn(gBrowser.selectedBrowser, aFrameId, function*(aFrameId) {
     let global = content.wrappedJSObject;
     if (aFrameId)
       global = global.document.getElementById(aFrameId).contentWindow;
     global.closeStream();
   });
 
-  if (!aAlreadyClosed)
-    yield promise;
-
-  yield promiseNoPopupNotification("webRTC-sharingDevices");
-  if (!aAlreadyClosed)
-    yield expectObserverCalled("recording-window-ended");
+  if (promises)
+    yield Promise.all(promises);
 
   yield* assertWebRTCIndicatorStatus(null);
 }
 
 function checkDeviceSelectors(aAudio, aVideo) {
   let micSelector = document.getElementById("webRTC-selectMicrophone");
   if (aAudio)
     ok(!micSelector.hidden, "microphone selector visible");
@@ -378,21 +397,61 @@ function checkDeviceSelectors(aAudio, aV
   let cameraSelector = document.getElementById("webRTC-selectCamera");
   if (aVideo)
     ok(!cameraSelector.hidden, "camera selector visible");
   else
     ok(cameraSelector.hidden, "camera selector hidden");
 }
 
 function* checkSharingUI(aExpected) {
-  yield promisePopupNotification("webRTC-sharingDevices");
+  // First check the icon above the control center (i) icon.
+  let identityBox = document.getElementById("identity-box");
+  ok(identityBox.hasAttribute("sharing"), "sharing attribute is set");
+  let sharing = identityBox.getAttribute("sharing");
+  if (aExpected.video)
+    is(sharing, "camera", "showing camera icon on the control center icon");
+  else if (aExpected.audio)
+    is(sharing, "microphone", "showing mic icon on the control center icon");
 
+  // Then check the sharing indicators inside the control center panel.
+  identityBox.click();
+  let permissions = document.getElementById("identity-popup-permission-list");
+  for (let id of ["microphone", "camera", "screen"]) {
+    let convertId = id => {
+      if (id == "camera")
+        return "video";
+      if (id == "microphone")
+        return "audio";
+      return id;
+    };
+    let expected = aExpected[convertId(id)];
+    is(!!gIdentityHandler._sharingState[id], !!expected,
+       "sharing state for " + id + " as expected");
+    let icon = permissions.querySelectorAll(
+      ".identity-popup-permission-icon." + id + "-icon");
+    if (expected) {
+      is(icon.length, 1, "should show " + id + " icon in control center panel");
+      ok(icon[0].classList.contains("in-use"), "icon should have the in-use class");
+    } else {
+      if (!icon.length) {
+        ok(true, "should not show " + id + " icon in the control center panel");
+      } else {
+        // This will happen if there are persistent permissions set.
+        ok(!icon[0].classList.contains("in-use"),
+           "if shown, the " + id + " icon should not have the in-use class");
+        is(icon.length, 1, "should not show more than 1 " + id + " icon");
+      }
+    }
+  }
+  gIdentityHandler._identityPopup.hidden = true;
+
+  // Check the global indicators.
   yield* assertWebRTCIndicatorStatus(aExpected);
 }
 
 function* checkNotSharing() {
   is((yield getMediaCaptureState()), "none", "expected nothing to be shared");
 
-  ok(!PopupNotifications.getNotification("webRTC-sharingDevices"),
-     "no webRTC-sharingDevices popup notification");
+  ok(!document.getElementById("identity-box").hasAttribute("sharing"),
+     "no sharing indicator on the control center icon");
 
   yield* assertWebRTCIndicatorStatus(null);
 }
--- a/browser/components/sessionstore/SessionStorage.jsm
+++ b/browser/components/sessionstore/SessionStorage.jsm
@@ -117,17 +117,17 @@ var SessionStorageInternal = {
       }
 
       let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
       let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
 
       // There is no need to pass documentURI, it's only used to fill documentURI property of
       // domstorage event, which in this case has no consumer. Prevention of events in case
       // of missing documentURI will be solved in a followup bug to bug 600307.
-      let storage = storageManager.createStorage(window, principal, "", aDocShell.usePrivateBrowsing);
+      let storage = storageManager.createStorage(window, principal, "");
 
       for (let key of Object.keys(data)) {
         try {
           storage.setItem(key, data[key]);
         } catch (e) {
           // throws e.g. for URIs that can't have sessionStorage
           console.error(e);
         }
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -215,21 +215,18 @@ These should match what Safari and other
 <!ENTITY urlbar.addonsNotificationAnchor.label          "View the add-on install message">
 <!ENTITY urlbar.indexedDBNotificationAnchor.label       "View the app-offline storage message">
 <!ENTITY urlbar.loginFillNotificationAnchor.label       "Manage your login information">
 <!ENTITY urlbar.passwordNotificationAnchor.label        "Check if you want to save your password">
 <!ENTITY urlbar.pluginsNotificationAnchor.label         "Manage plugin usage on this page">
 <!ENTITY urlbar.webNotsNotificationAnchor3.label        "Change whether you can receive notifications from the site">
 
 <!ENTITY urlbar.webRTCShareDevicesNotificationAnchor.label      "Manage sharing your camera and/or microphone with the site">
-<!ENTITY urlbar.webRTCSharingDevicesNotificationAnchor.label    "You are sharing your camera and/or microphone with the site">
 <!ENTITY urlbar.webRTCShareMicrophoneNotificationAnchor.label   "Manage sharing your microphone with the site">
-<!ENTITY urlbar.webRTCSharingMicrophoneNotificationAnchor.label "You are sharing your microphone with the site">
 <!ENTITY urlbar.webRTCShareScreenNotificationAnchor.label       "Manage sharing your windows or screen with the site">
-<!ENTITY urlbar.webRTCSharingScreenNotificationAnchor.label     "You are sharing a window or your screen with the site">
 
 <!ENTITY urlbar.pointerLockNotificationAnchor.label     "Change whether the site can hide the pointer">
 <!ENTITY urlbar.servicesNotificationAnchor.label        "View the service install message">
 <!ENTITY urlbar.translateNotificationAnchor.label       "Translate this page">
 <!ENTITY urlbar.translatedNotificationAnchor.label      "Manage page translation">
 <!ENTITY urlbar.emeNotificationAnchor.label             "Manage use of DRM software">
 
 <!ENTITY urlbar.openHistoryPopup.tooltip                "Show history">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -514,28 +514,16 @@ getUserMedia.shareApplication.label = Sh
 getUserMedia.shareWindow.label = Share Selected Window
 getUserMedia.shareSelectedItems.label = Share Selected Items
 getUserMedia.always.label = Always Share
 getUserMedia.always.accesskey = A
 getUserMedia.denyRequest.label = Don’t Share
 getUserMedia.denyRequest.accesskey = D
 getUserMedia.never.label = Never Share
 getUserMedia.never.accesskey = N
-getUserMedia.sharingCamera.message2 = You are currently sharing your camera with this page.
-getUserMedia.sharingMicrophone.message2 = You are currently sharing your microphone with this page.
-getUserMedia.sharingCameraAndMicrophone.message2 = You are currently sharing your camera and microphone with this page.
-getUserMedia.sharingApplication.message = You are currently sharing an application with this page.
-getUserMedia.sharingScreen.message = You are currently sharing your screen with this page.
-getUserMedia.sharingWindow.message = You are currently sharing a window with this page.
-getUserMedia.sharingBrowser.message = You are currently sharing a tab with this page.
-getUserMedia.sharingAudioCapture.message = You are currently sharing a tab’s audio with this page.
-getUserMedia.continueSharing.label = Continue Sharing
-getUserMedia.continueSharing.accesskey = C
-getUserMedia.stopSharing.label = Stop Sharing
-getUserMedia.stopSharing.accesskey = S
 
 getUserMedia.sharingMenu.label = Tabs sharing devices
 getUserMedia.sharingMenu.accesskey = d
 # LOCALIZATION NOTE (getUserMedia.sharingMenuCamera
 #                    getUserMedia.sharingMenuMicrophone,
 #                    getUserMedia.sharingMenuAudioCapture,
 #                    getUserMedia.sharingMenuApplication,
 #                    getUserMedia.sharingMenuScreen,
--- a/browser/locales/en-US/chrome/browser/sitePermissions.properties
+++ b/browser/locales/en-US/chrome/browser/sitePermissions.properties
@@ -1,18 +1,20 @@
 # 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/.
 
 allow = Allow
 allowForSession = Allow for Session
+allowTemporarily = Allow Temporarily
 block = Block
 alwaysAsk = Always Ask
 
 permission.cookie.label = Set Cookies
 permission.desktop-notification2.label = Receive Notifications
 permission.image.label = Load Images
 permission.camera.label = Use the Camera
 permission.microphone.label = Use the Microphone
+permission.screen.label = Share the Screen
 permission.install.label = Install Add-ons
 permission.popup.label = Open Pop-up Windows
 permission.geo.label = Access Your Location
 permission.indexedDB.label = Maintain Offline Storage
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -42,36 +42,44 @@ this.SitePermissions = {
           state: permission.capability,
         });
       }
     }
 
     return result;
   },
 
-  /* Returns a list of objects representing all permissions that are currently
-   * set for the given URI. Each object contains the following keys:
+  /* Returns an object representing the aId permission. It contains the
+   * following keys:
    * - id: the permissionID of the permission
    * - label: the localized label
-   * - state: a constant representing the current permission state
-   *   (e.g. SitePermissions.ALLOW)
+   * - state: a constant representing the aState permission state
+   *   (e.g. SitePermissions.ALLOW), or the default if aState is omitted
    * - availableStates: an array of all available states for that permission,
    *   represented as objects with the keys:
    *   - id: the state constant
    *   - label: the translated label of that state
    */
+  getPermissionItem: function (aId, aState) {
+    let availableStates = this.getAvailableStates(aId).map(state => {
+      return { id: state, label: this.getStateLabel(aId, state) };
+    });
+    if (aState == undefined)
+      aState = this.getDefault(aId);
+    return {id: aId, label: this.getPermissionLabel(aId),
+            state: aState, availableStates};
+  },
+
+  /* Returns a list of objects representing all permissions that are currently
+   * set for the given URI. See getPermissionItem for the content of each object.
+   */
   getPermissionDetailsByURI: function (aURI) {
     let permissions = [];
     for (let {state, id} of this.getAllByURI(aURI)) {
-      let availableStates = this.getAvailableStates(id).map( state => {
-        return { id: state, label: this.getStateLabel(id, state) };
-      });
-      let label = this.getPermissionLabel(id);
-
-      permissions.push({id, label, state, availableStates});
+      permissions.push(this.getPermissionItem(id, state));
     }
 
     return permissions;
   },
 
   /* Checks whether a UI for managing permissions should be exposed for a given
    * URI. This excludes file URIs, for instance, as they don't have a host,
    * even though nsIPermissionManager can still handle them.
@@ -154,19 +162,21 @@ this.SitePermissions = {
   getPermissionLabel: function (aPermissionID) {
     let labelID = gPermissionObject[aPermissionID].labelID || aPermissionID;
     return gStringBundle.GetStringFromName("permission." + labelID + ".label");
   },
 
   /* Returns the localized label for the given permission state, to be used in
    * a UI for managing permissions.
    */
-  getStateLabel: function (aPermissionID, aState) {
+  getStateLabel: function (aPermissionID, aState, aInUse = false) {
     switch (aState) {
       case this.UNKNOWN:
+        if (aInUse)
+          return gStringBundle.GetStringFromName("allowTemporarily");
         return gStringBundle.GetStringFromName("alwaysAsk");
       case this.ALLOW:
         return gStringBundle.GetStringFromName("allow");
       case this.SESSION:
         return gStringBundle.GetStringFromName("allowForSession");
       case this.BLOCK:
         return gStringBundle.GetStringFromName("block");
       default:
@@ -220,16 +230,19 @@ var gPermissionObject = {
 
   "desktop-notification": {
     exactHostMatch: true,
     labelID: "desktop-notification2",
   },
 
   "camera": {},
   "microphone": {},
+  "screen": {
+    states: [ SitePermissions.UNKNOWN, SitePermissions.BLOCK ],
+  },
 
   "popup": {
     getDefault: function () {
       return Services.prefs.getBoolPref("dom.disable_open_during_load") ?
                SitePermissions.BLOCK : SitePermissions.ALLOW;
     }
   },
 
--- a/browser/modules/test/xpcshell/test_SitePermissions.js
+++ b/browser/modules/test/xpcshell/test_SitePermissions.js
@@ -4,17 +4,17 @@
 "use strict";
 
 Components.utils.import("resource:///modules/SitePermissions.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 add_task(function* testPermissionsListing() {
   Assert.deepEqual(SitePermissions.listPermissions().sort(),
     ["camera","cookie","desktop-notification","geo","image",
-     "indexedDB","install","microphone","popup"],
+     "indexedDB","install","microphone","popup", "screen"],
     "Correct list of all permissions");
 });
 
 add_task(function* testGetAllByURI() {
   // check that it returns an empty array on an invalid URI
   // like a file URI, which doesn't support site permissions
   let wrongURI = Services.io.newURI("file:///example.js", null, null)
   Assert.deepEqual(SitePermissions.getAllByURI(wrongURI), []);
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -131,31 +131,29 @@ this.webrtcUI = {
   showSharingDoorhanger: function(aActiveStream, aType) {
     let browserWindow = aActiveStream.browser.ownerGlobal;
     if (aActiveStream.tab) {
       browserWindow.gBrowser.selectedTab = aActiveStream.tab;
     } else {
       aActiveStream.browser.focus();
     }
     browserWindow.focus();
-    let PopupNotifications = browserWindow.PopupNotifications;
-    let notif = PopupNotifications.getNotification("webRTC-sharing" + aType,
-                                                   aActiveStream.browser);
+    let identityBox = browserWindow.document.getElementById("identity-box");
     if (AppConstants.platform == "macosx" && !Services.focus.activeWindow) {
       browserWindow.addEventListener("activate", function onActivate() {
         browserWindow.removeEventListener("activate", onActivate);
         Services.tm.mainThread.dispatch(function() {
-          notif.reshow();
+          identityBox.click();
         }, Ci.nsIThread.DISPATCH_NORMAL);
       });
       Cc["@mozilla.org/widget/macdocksupport;1"].getService(Ci.nsIMacDockSupport)
         .activateApplication(true);
       return;
     }
-    notif.reshow();
+    identityBox.click();
   },
 
   updateMainActionLabel: function(aMenuList) {
     let type = aMenuList.selectedItem.getAttribute("devicetype");
     let document = aMenuList.ownerDocument;
     document.getElementById("webRTC-all-windows-shared").hidden = type != "Screen";
 
     // If we are also requesting audio in addition to screen sharing,
@@ -232,17 +230,19 @@ this.webrtcUI = {
             break;
         }
         // If there's no documentURI, the update is actually a removal of the
         // stream, triggered by the recording-window-ended notification.
         if (!aMessage.data.documentURI && index < webrtcUI._streams.length)
           webrtcUI._streams.splice(index, 1);
         else
           webrtcUI._streams[index] = {browser: aMessage.target, state: aMessage.data};
-        updateBrowserSpecificIndicator(aMessage.target, aMessage.data);
+        let tabbrowser = aMessage.target.ownerGlobal.gBrowser;
+        if (tabbrowser)
+          tabbrowser.setBrowserSharing(aMessage.target, aMessage.data);
         break;
       case "child-process-shutdown":
         webrtcUI.processIndicators.delete(aMessage.target);
         updateIndicators(null, null);
         break;
     }
   }
 };
@@ -861,150 +861,8 @@ function updateIndicators(data, target) 
       gIndicatorWindow = getGlobalIndicator();
     else
       gIndicatorWindow.updateIndicatorState();
   } else if (gIndicatorWindow) {
     gIndicatorWindow.close();
     gIndicatorWindow = null;
   }
 }
-
-function updateBrowserSpecificIndicator(aBrowser, aState) {
-  let chromeWin = aBrowser.ownerGlobal;
-  let tabbrowser = chromeWin.gBrowser;
-  if (tabbrowser) {
-    let sharing;
-    if (aState.screen) {
-      sharing = "screen";
-    } else if (aState.camera) {
-      sharing = "camera";
-    } else if (aState.microphone) {
-      sharing = "microphone";
-    }
-
-    tabbrowser.setBrowserSharing(aBrowser, sharing);
-  }
-
-  let captureState;
-  if (aState.camera && aState.microphone) {
-    captureState = "CameraAndMicrophone";
-  } else if (aState.camera) {
-    captureState = "Camera";
-  } else if (aState.microphone) {
-    captureState = "Microphone";
-  }
-
-  let stringBundle = chromeWin.gNavigatorBundle;
-
-  let windowId = aState.windowId;
-  let notification; // Used by action callbacks.
-  let mainAction = {
-    label: stringBundle.getString("getUserMedia.continueSharing.label"),
-    accessKey: stringBundle.getString("getUserMedia.continueSharing.accesskey"),
-    callback: function () {},
-    dismiss: true
-  };
-  let secondaryActions = [{
-    label: stringBundle.getString("getUserMedia.stopSharing.label"),
-    accessKey: stringBundle.getString("getUserMedia.stopSharing.accesskey"),
-    callback: function () {
-      let uris = aBrowser._devicePermissionURIs || [];
-      uris = uris.concat(Services.io.newURI(aState.documentURI, null, null));
-      let perms = Services.perms;
-      for (let uri of uris) {
-        if (aState.camera &&
-            perms.testExactPermission(uri, "camera") == perms.ALLOW_ACTION)
-          perms.remove(uri, "camera");
-        if (aState.microphone &&
-            perms.testExactPermission(uri, "microphone") == perms.ALLOW_ACTION)
-          perms.remove(uri, "microphone");
-      }
-      let mm = notification.browser.messageManager;
-      mm.sendAsyncMessage("webrtc:StopSharing", windowId);
-    }
-  }];
-  let options = {
-    hideNotNow: true,
-    dismissed: true,
-    eventCallback: function(aTopic, aNewBrowser) {
-      if (aTopic == "shown") {
-        let popupId = captureState == "Microphone" ? "Microphone" : "Devices";
-        this.browser.ownerDocument
-            .getElementById("webRTC-sharingDevices-notification")
-            .setAttribute("popupid", "webRTC-sharing" + popupId);
-      }
-
-      if (aTopic == "swapping") {
-        webrtcUI.swapBrowserForNotification(this.browser, aNewBrowser);
-        return true;
-      }
-
-      return false;
-    }
-  };
-  if (captureState) {
-    let anchorId = captureState == "Microphone" ? "webRTC-sharingMicrophone-notification-icon"
-                                                : "webRTC-sharingDevices-notification-icon";
-    let message = stringBundle.getString("getUserMedia.sharing" + captureState + ".message2");
-    notification =
-      chromeWin.PopupNotifications.show(aBrowser, "webRTC-sharingDevices", message,
-                                        anchorId, mainAction, secondaryActions, options);
-  }
-  else {
-    removeBrowserNotification(aBrowser, "webRTC-sharingDevices");
-    aBrowser._devicePermissionURIs = null;
-  }
-
-  // Now handle the screen sharing indicator.
-  if (!aState.screen) {
-    removeBrowserNotification(aBrowser, "webRTC-sharingScreen");
-    return;
-  }
-
-  let screenSharingNotif; // Used by action callbacks.
-  let isBrowserSharing = aState.screen == "Browser";
-  options = {
-    hideNotNow: !isBrowserSharing,
-    dismissed: true,
-    eventCallback: function(aTopic, aNewBrowser) {
-      if (aTopic == "shown") {
-        this.browser.ownerDocument
-            .getElementById("webRTC-sharingScreen-notification")
-            .setAttribute("popupid", "webRTC-sharingScreen");
-      }
-
-      if (aTopic == "swapping") {
-        webrtcUI.swapBrowserForNotification(this.browser, aNewBrowser);
-        return true;
-      }
-
-      return false;
-    }
-  };
-  secondaryActions = [{
-    label: stringBundle.getString("getUserMedia.stopSharing.label"),
-    accessKey: stringBundle.getString("getUserMedia.stopSharing.accesskey"),
-    callback: function () {
-      let mm = screenSharingNotif.browser.messageManager;
-      mm.sendAsyncMessage("webrtc:StopSharing", "screen:" + windowId);
-    }
-  }];
-
-  // Ending browser-sharing from the gUM doorhanger is not supported at the moment.
-  // See bug 1142091.
-  if (isBrowserSharing)
-    mainAction = secondaryActions = null;
-  // If we are sharing both a window and the screen, we show 'Screen'.
-  let stringId = "getUserMedia.sharing" + aState.screen;
-  screenSharingNotif =
-    chromeWin.PopupNotifications.show(aBrowser, "webRTC-sharingScreen",
-                                      stringBundle.getString(stringId + ".message"),
-                                      "webRTC-sharingScreen-notification-icon",
-                                      mainAction, secondaryActions, options);
-}
-
-function removeBrowserNotification(aBrowser, aNotificationId) {
-  let win = aBrowser.ownerGlobal;
-  let notification =
-    win.PopupNotifications.getNotification(aNotificationId, aBrowser);
-  if (notification)
-    win.PopupNotifications.remove(notification);
-}
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1719,17 +1719,17 @@ toolbarbutton.chevron > .toolbarbutton-i
 .ctrlTab-favicon[src] {
   background-color: white;
   width: 20px;
   height: 20px;
   padding: 2px;
 }
 
 .ctrlTab-preview-inner > .tabPreview-canvas {
-  box-shadow: 1px 1px 2px rgb(12%,12%,12%);
+  box-shadow: 1px 1px 2px hsl(0,0%,12%);
 }
 
 .ctrlTab-preview:not(#ctrlTab-showAll) > * > .ctrlTab-preview-inner > .tabPreview-canvas {
   margin-bottom: 2px;
 }
 
 .ctrlTab-preview-inner {
   padding-bottom: 10px;
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3126,17 +3126,17 @@ menulist.translate-infobar-element > .me
 .ctrlTab-favicon[src] {
   background-color: white;
   width: 20px;
   height: 20px;
   padding: 2px;
 }
 
 .ctrlTab-preview-inner > .tabPreview-canvas {
-  box-shadow: 1px 1px 2px rgb(12%,12%,12%);
+  box-shadow: 1px 1px 2px hsl(0,0%,12%);
 }
 
 .ctrlTab-preview:not(#ctrlTab-showAll) > * > .ctrlTab-preview-inner > .tabPreview-canvas {
   margin-bottom: 2px;
 }
 
 .ctrlTab-preview-inner {
   padding-bottom: 10px;
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -376,16 +376,25 @@ description#identity-popup-content-verif
   display: none;
 }
 
 .identity-popup-permission-icon {
   width: 16px;
   height: 16px;
 }
 
+.identity-popup-permission-icon.in-use {
+  fill: rgb(224, 41, 29);
+  animation: 1.5s ease in-use-blink infinite;
+}
+
+@keyframes in-use-blink {
+  50% { opacity: 0; }
+}
+
 .identity-popup-permission-label {
   margin-inline-start: 1em;
 }
 
 .identity-popup-permission-state-label {
   margin-inline-end: 5px;
   text-align: end;
   opacity: 0.6;
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -89,16 +89,59 @@
 #urlbar[actiontype="searchengine"] > #identity-box > #identity-icon {
   -moz-image-region: inherit;
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
   width: 16px;
   height: 16px;
   opacity: 1;
 }
 
+/* SHARING ICON */
+
+#sharing-icon {
+  width: 16px;
+  height: 16px;
+  margin-inline-start: -16px;
+  position: relative;
+  display: none;
+}
+
+#identity-box[sharing="camera"] > #sharing-icon {
+  list-style-image: url("chrome://browser/skin/notification-icons.svg#camera");
+}
+
+#identity-box[sharing="microphone"] > #sharing-icon {
+  list-style-image: url("chrome://browser/skin/notification-icons.svg#microphone");
+}
+
+#identity-box[sharing="screen"] > #sharing-icon {
+  list-style-image: url("chrome://browser/skin/notification-icons.svg#screen");
+}
+
+#identity-box[sharing] > #sharing-icon {
+  display: -moz-box;
+  filter: url("chrome://browser/skin/filters.svg#fill");
+  fill: rgb(224, 41, 29);
+  animation-delay: -1.5s;
+}
+
+#identity-box[sharing] > #identity-icon,
+#sharing-icon {
+  animation: 3s linear pulse infinite;
+}
+
+@keyframes pulse {
+  0%, 16.66%, 83.33%, 100% {
+    opacity: 0;
+  }
+  33.33%, 66.66% {
+    opacity: 1;
+  }
+}
+
 /* TRACKING PROTECTION ICON */
 
 #tracking-protection-icon {
   width: 16px;
   height: 16px;
   margin-inline-start: 2px;
   margin-inline-end: 0;
   list-style-image: url(chrome://browser/skin/tracking-protection-16.svg);
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -36,26 +36,22 @@
 }
 
 .popup-notification-icon {
   width: 64px;
   height: 64px;
   margin-inline-end: 10px;
 }
 
-#notification-popup-box > .notification-anchor-icon:not(.in-use):hover {
+#notification-popup-box > .notification-anchor-icon:hover {
   fill: #606060;
 }
 
 /* INDIVIDUAL NOTIFICATIONS */
 
-/* For the moment we apply the color filter only on the icons listed here.
-   The first two selectors are used by socialchat.xml (bug 1275558). */
-.webRTC-sharingDevices-notification-icon,
-.webRTC-sharingMicrophone-notification-icon,
 .camera-icon,
 .geo-icon,
 .indexedDB-icon,
 .install-icon,
 .login-icon,
 .microphone-icon,
 .plugin-icon,
 .pointerLock-icon,
@@ -64,32 +60,21 @@
 .desktop-notification-icon,
 .popup-notification-icon[popupid="geolocation"],
 .popup-notification-icon[popupid="indexedDB-permissions-prompt"],
 .popup-notification-icon[popupid="password"],
 .popup-notification-icon[popupid="pointerLock"],
 .popup-notification-icon[popupid="webRTC-shareDevices"],
 .popup-notification-icon[popupid="webRTC-shareMicrophone"],
 .popup-notification-icon[popupid="webRTC-shareScreen"],
-.popup-notification-icon[popupid="webRTC-sharingDevices"],
-.popup-notification-icon[popupid="webRTC-sharingMicrophone"],
-.popup-notification-icon[popupid="webRTC-sharingScreen"],
 .popup-notification-icon[popupid="web-notifications"] {
   filter: url(chrome://browser/skin/filters.svg#fill);
   fill: #999;
 }
 
-/* The first two selectors are used by socialchat.xml (bug 1275558). The
-   notifications in the chat window are only shown when they are in use. */
-.webRTC-sharingDevices-notification-icon,
-.webRTC-sharingMicrophone-notification-icon,
-.in-use {
-  fill: #fea01b;
-}
-
 .popup-notification-icon[popupid="web-notifications"],
 .desktop-notification-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#desktop-notification);
 }
 
 .desktop-notification-icon.blocked {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#desktop-notification-blocked);
 }
@@ -141,45 +126,38 @@
   list-style-image: url(chrome://browser/skin/notification-icons.svg#login-detailed);
 }
 
 #login-fill-notification-icon {
   /* Temporary solution until the capture and fill doorhangers are unified. */
   transform: scaleX(-1);
 }
 
-/* The first selector is used by socialchat.xml (bug 1275558). */
-.webRTC-sharingDevices-notification-icon,
 .camera-icon,
-.popup-notification-icon[popupid="webRTC-shareDevices"],
-.popup-notification-icon[popupid="webRTC-sharingDevices"] {
+.popup-notification-icon[popupid="webRTC-shareDevices"] {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#camera);
 }
 
 .camera-icon.blocked {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#camera-blocked);
 }
 
-/* The first selector is used by socialchat.xml (bug 1275558). */
-.webRTC-sharingMicrophone-notification-icon,
 .microphone-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone);
 }
 
 .microphone-icon.blocked {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone-blocked);
 }
 
-.popup-notification-icon[popupid="webRTC-shareMicrophone"],
-.popup-notification-icon[popupid="webRTC-sharingMicrophone"] {
+.popup-notification-icon[popupid="webRTC-shareMicrophone"] {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone-detailed);
 }
 
 .popup-notification-icon[popupid="webRTC-shareScreen"],
-.popup-notification-icon[popupid="webRTC-sharingScreen"],
 .screen-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#screen);
 }
 
 .screen-icon.blocked {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#screen-blocked);
 }
 
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2373,17 +2373,17 @@ notification[value="translation"] {
 .ctrlTab-favicon[src] {
   background-color: white;
   width: 20px;
   height: 20px;
   padding: 2px;
 }
 
 .ctrlTab-preview-inner > .tabPreview-canvas {
-  box-shadow: 1px 1px 2px rgb(12%,12%,12%);
+  box-shadow: 1px 1px 2px hsl(0,0%,12%);
 }
 
 .ctrlTab-preview:not(#ctrlTab-showAll) > * > .ctrlTab-preview-inner > .tabPreview-canvas {
   margin-bottom: 2px;
 }
 
 .ctrlTab-preview-inner {
   padding-bottom: 10px;
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -561,16 +561,23 @@ BasePrincipal::GetAddonId(nsAString& aAd
 NS_IMETHODIMP
 BasePrincipal::GetUserContextId(uint32_t* aUserContextId)
 {
   *aUserContextId = UserContextId();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+BasePrincipal::GetPrivateBrowsingId(uint32_t* aPrivateBrowsingId)
+{
+  *aPrivateBrowsingId = PrivateBrowsingId();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 BasePrincipal::GetIsInIsolatedMozBrowserElement(bool* aIsInIsolatedMozBrowserElement)
 {
   *aIsInIsolatedMozBrowserElement = IsInIsolatedMozBrowserElement();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasePrincipal::GetUnknownAppId(bool* aUnknownAppId)
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -259,29 +259,31 @@ public:
   NS_IMETHOD GetOriginAttributes(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) final;
   NS_IMETHOD GetOriginSuffix(nsACString& aOriginSuffix) final;
   NS_IMETHOD GetAppStatus(uint16_t* aAppStatus) final;
   NS_IMETHOD GetAppId(uint32_t* aAppStatus) final;
   NS_IMETHOD GetAddonId(nsAString& aAddonId) final;
   NS_IMETHOD GetIsInIsolatedMozBrowserElement(bool* aIsInIsolatedMozBrowserElement) final;
   NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId) final;
   NS_IMETHOD GetUserContextId(uint32_t* aUserContextId) final;
+  NS_IMETHOD GetPrivateBrowsingId(uint32_t* aPrivateBrowsingId) final;
 
   virtual bool IsOnCSSUnprefixingWhitelist() override { return false; }
 
   virtual bool IsCodebasePrincipal() const { return false; };
 
   static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast<BasePrincipal*>(aPrin); }
   static already_AddRefed<BasePrincipal>
   CreateCodebasePrincipal(nsIURI* aURI, const PrincipalOriginAttributes& aAttrs);
   static already_AddRefed<BasePrincipal> CreateCodebasePrincipal(const nsACString& aOrigin);
 
   const PrincipalOriginAttributes& OriginAttributesRef() { return mOriginAttributes; }
   uint32_t AppId() const { return mOriginAttributes.mAppId; }
   uint32_t UserContextId() const { return mOriginAttributes.mUserContextId; }
+  uint32_t PrivateBrowsingId() const { return mOriginAttributes.mPrivateBrowsingId; }
   bool IsInIsolatedMozBrowserElement() const { return mOriginAttributes.mInIsolatedMozBrowser; }
 
   enum PrincipalKind {
     eNullPrincipal,
     eCodebasePrincipal,
     eExpandedPrincipal,
     eSystemPrincipal
   };
--- a/caps/nsIPrincipal.idl
+++ b/caps/nsIPrincipal.idl
@@ -303,16 +303,23 @@ interface nsIPrincipal : nsISerializable
     /**
      * Gets the id of the user context this principal is inside.  If this
      * principal is inside the default userContext, this returns
      * nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID.
      */
     [infallible] readonly attribute unsigned long userContextId;
 
     /**
+     * Gets the id of the private browsing state of the context containing
+     * this principal. If the principal has a private browsing value of 0, it
+     * is not in private browsing.
+     */
+    [infallible] readonly attribute unsigned long privateBrowsingId;
+
+    /**
      * Returns true iff the principal is inside an isolated mozbrowser element.
      * <iframe mozbrowser mozapp> and <xul:browser> are not considered to be
      * mozbrowser elements.  <iframe mozbrowser noisolation> does not count as
      * isolated since isolation is disabled.  Isolation can only be disabled if
      * the containing document is chrome.
      */
     [infallible] readonly attribute boolean isInIsolatedMozBrowserElement;
 
--- a/config/nsinstall.py
+++ b/config/nsinstall.py
@@ -36,17 +36,17 @@ def _nsinstall_internal(argv):
                help="Link prefix (ignored)")
   p.add_option('-X', action="append", metavar="file",
                help="Ignore a file when installing a directory recursively.")
 
   # The remaining arguments are not used in our tree, thus they're not
   # implented.
   def BadArg(option, opt, value, parser):
     parser.error('option not supported: {0}'.format(opt))
-    
+
   p.add_option('-C', action="callback", metavar="CWD",
                callback=BadArg,
                help="NOT SUPPORTED")
   p.add_option('-o', action="callback", callback=BadArg,
                help="Set owner (NOT SUPPORTED)", metavar="owner")
   p.add_option('-g', action="callback", callback=BadArg,
                help="Set group (NOT SUPPORTED)", metavar="group")
 
--- a/config/tests/unit-nsinstall.py
+++ b/config/tests/unit-nsinstall.py
@@ -114,17 +114,17 @@ class TestNsinstall(unittest.TestCase):
         self.assertEqual(os.stat(testfile).st_mtime,
                          os.stat(destfile).st_mtime)
 
     if sys.platform != "win32":
         # can't run this test on windows, don't have real file modes there
         def test_nsinstall_m(self):
             "Test that nsinstall -m works (set mode)"
             testfile = self.touch("testfile")
-            mode = 0600
+            mode = 0o600
             os.chmod(testfile, mode)
             testdir = self.mkdirs("testdir")
             self.assertEqual(nsinstall(["-m", "{0:04o}"
                                         .format(mode), testfile, testdir]), 0)
             destfile = os.path.join(testdir, "testfile")
             self.assert_(os.path.isfile(destfile))
             self.assertEqual(os.stat(testfile).st_mode,
                              os.stat(destfile).st_mode)
--- a/configure.py
+++ b/configure.py
@@ -76,17 +76,17 @@ def config_status(config):
 if __name__ == '__main__':
     args = dict([(name, globals()[name]) for name in __all__])
     from mozbuild.config_status import config_status
     config_status(**args)
 ''')
 
     # Other things than us are going to run this file, so we need to give it
     # executable permissions.
-    os.chmod('config.status', 0755)
+    os.chmod('config.status', 0o755)
     if config.get('MOZ_BUILD_APP') != 'js' or config.get('JS_STANDALONE'):
         os.environ['WRITE_MOZINFO'] = '1'
         # Until we have access to the virtualenv from this script, execute
         # config.status externally, with the virtualenv python.
         return subprocess.call([config['PYTHON'], 'config.status'])
     return 0
 
 
deleted file mode 100644
--- a/devtools/client/debugger/new/images/moz.build
+++ /dev/null
@@ -1,23 +0,0 @@
-DevToolsModules(
-    'angle-brackets.svg',
-    'arrow.svg',
-    'blackBox.svg',
-    'breakpoint.svg',
-    'close.svg',
-    'disableBreakpoints.svg',
-    'domain.svg',
-    'file.svg',
-    'folder.svg',
-    'globe.svg',
-    'pause-circle.svg',
-    'pause.svg',
-    'play.svg',
-    'prettyPrint.svg',
-    'resume.svg',
-    'settings.svg',
-    'stepIn.svg',
-    'stepOut.svg',
-    'stepOver.svg',
-    'subSettings.svg',
-    'worker.svg'
-)
\ No newline at end of file
--- a/devtools/client/debugger/new/moz.build
+++ b/devtools/client/debugger/new/moz.build
@@ -1,10 +1,12 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
     'bundle.js',
     'panel.js',
+    'pretty-print-worker.js',
+    'source-map-worker.js',
     'styles.css'
 )
--- a/devtools/client/inspector/inspector-panel.js
+++ b/devtools/client/inspector/inspector-panel.js
@@ -208,17 +208,17 @@ InspectorPanel.prototype = {
 
     if (this.target.isLocalTab) {
       // Show a warning when the debugger is paused.
       // We show the warning only when the inspector
       // is selected.
       this.updateDebuggerPausedWarning = () => {
         let notificationBox = this._toolbox.getNotificationBox();
         let notification =
-            notificationBox.getNotificationWithValue("inspector-script-paused");
+          notificationBox.getNotificationWithValue("inspector-script-paused");
         if (!notification && this._toolbox.currentToolId == "inspector" &&
             this._toolbox.threadClient.paused) {
           let message = strings.GetStringFromName("debuggerPausedWarning.message");
           notificationBox.appendNotification(message,
             "inspector-script-paused", "", notificationBox.PRIORITY_WARNING_HIGH);
         }
 
         if (notification && this._toolbox.currentToolId != "inspector") {
@@ -348,19 +348,20 @@ InspectorPanel.prototype = {
     this.isDirty = true;
   },
 
   /**
    * Hooks the searchbar to show result and auto completion suggestions.
    */
   setupSearchBox: function () {
     this.searchBox = this.panelDoc.getElementById("inspector-searchbox");
+    this.searchClearButton = this.panelDoc.getElementById("inspector-searchinput-clear");
     this.searchResultsLabel = this.panelDoc.getElementById("inspector-searchlabel");
 
-    this.search = new InspectorSearch(this, this.searchBox);
+    this.search = new InspectorSearch(this, this.searchBox, this.searchClearButton);
     this.search.on("search-cleared", this._updateSearchResultsLabel);
     this.search.on("search-result", this._updateSearchResultsLabel);
 
     let shortcuts = new KeyShortcuts({
       window: this.panelDoc.defaultView,
     });
     let key = strings.GetStringFromName("inspector.searchHTML.key");
     shortcuts.on(key, (name, event) => {
@@ -1450,17 +1451,18 @@ InspectorPanel.prototype = {
     let node = this.selection.nodeFront;
     return this.markup.getNodeInnerHTML(node).then(oldContent => {
       this.markup.updateNodeInnerHTML(node, content, oldContent);
     });
   },
 
   /**
    * Paste the contents of the clipboard as adjacent HTML to the selected Node.
-   * @param position The position as specified for Element.insertAdjacentHTML
+   * @param position
+   *        The position as specified for Element.insertAdjacentHTML
    *        (i.e. "beforeBegin", "afterBegin", "beforeEnd", "afterEnd").
    */
   pasteAdjacentHTML: function (position) {
     let content = this._getClipboardContentForPaste();
     if (!content) {
       return promise.reject("No clipboard content for paste");
     }
 
@@ -1510,31 +1512,31 @@ InspectorPanel.prototype = {
     if (container && container.isPreviewable()) {
       container.copyImageDataUri();
     }
   },
 
   /**
    * Copy the content of a longString (via a promise resolving a
    * LongStringActor) to the clipboard
-   * @param  {Promise} longStringActorPromise promise expected to
-   *         resolve a LongStringActor instance
+   * @param  {Promise} longStringActorPromise
+   *         promise expected to resolve a LongStringActor instance
    * @return {Promise} promise resolving (with no argument) when the
    *         string is sent to the clipboard
    */
   _copyLongString: function (longStringActorPromise) {
     return this._getLongString(longStringActorPromise).then(string => {
       clipboardHelper.copyString(string);
     }).catch(e => console.error(e));
   },
 
   /**
    * Retrieve the content of a longString (via a promise resolving a LongStringActor)
-   * @param  {Promise} longStringActorPromise promise expected to
-   *         resolve a LongStringActor instance
+   * @param  {Promise} longStringActorPromise
+   *         promise expected to resolve a LongStringActor instance
    * @return {Promise} promise resolving with the retrieved string as argument
    */
   _getLongString: function (longStringActorPromise) {
     return longStringActorPromise.then(longStringActor => {
       return longStringActor.string().then(string => {
         longStringActor.release().catch(e => console.error(e));
         return string;
       });
--- a/devtools/client/inspector/inspector-search.js
+++ b/devtools/client/inspector/inspector-search.js
@@ -1,48 +1,54 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-/* eslint-disable mozilla/reject-some-requires */
-const {Ci} = require("chrome");
-/* eslint-enable mozilla/reject-some-requires */
 const promise = require("promise");
 const {Task} = require("devtools/shared/task");
 
 const system = require("devtools/shared/system");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {AutocompletePopup} = require("devtools/client/shared/autocomplete-popup");
 
 // Maximum number of selector suggestions shown in the panel.
 const MAX_SUGGESTIONS = 15;
 
 /**
  * Converts any input field into a document search box.
  *
- * @param {InspectorPanel} inspector The InspectorPanel whose `walker` attribute
- * should be used for document traversal.
- * @param {DOMNode} input The input element to which the panel will be attached
- * and from where search input will be taken.
+ * @param {InspectorPanel} inspector
+ *        The InspectorPanel whose `walker` attribute should be used for
+ *        document traversal.
+ * @param {DOMNode} input
+ *        The input element to which the panel will be attached and from where
+ *        search input will be taken.
+ * @param {DOMNode} clearBtn
+ *        The clear button in the input field that will clear the input value.
  *
  * Emits the following events:
  * - search-cleared: when the search box is emptied
  * - search-result: when a search is made and a result is selected
  */
-function InspectorSearch(inspector, input) {
+function InspectorSearch(inspector, input, clearBtn) {
   this.inspector = inspector;
   this.searchBox = input;
+  this.searchClearButton = clearBtn;
   this._lastSearched = null;
 
+  this.searchClearButton.hidden = true;
+
   this._onKeyDown = this._onKeyDown.bind(this);
-  this._onCommand = this._onCommand.bind(this);
+  this._onInput = this._onInput.bind(this);
+  this._onClearSearch = this._onClearSearch.bind(this);
   this.searchBox.addEventListener("keydown", this._onKeyDown, true);
-  this.searchBox.addEventListener("command", this._onCommand, true);
+  this.searchBox.addEventListener("input", this._onInput, true);
+  this.searchClearButton.addEventListener("click", this._onClearSearch);
 
   // For testing, we need to be able to wait for the most recent node request
   // to finish.  Tests can watch this promise for that.
   this._lastQuery = promise.resolve(null);
 
   this.autocompleter = new SelectorAutocompleter(inspector, input);
   EventEmitter.decorate(this);
 }
@@ -51,18 +57,20 @@ exports.InspectorSearch = InspectorSearc
 
 InspectorSearch.prototype = {
   get walker() {
     return this.inspector.walker;
   },
 
   destroy: function () {
     this.searchBox.removeEventListener("keydown", this._onKeyDown, true);
-    this.searchBox.removeEventListener("command", this._onCommand, true);
+    this.searchBox.removeEventListener("input", this._onInput, true);
+    this.searchClearButton.removeEventListener("click", this._onClearSearch);
     this.searchBox = null;
+    this.searchClearButton = null;
     this.autocompleter.destroy();
   },
 
   _onSearch: function (reverse = false) {
     this.doFullTextSearch(this.searchBox.value, reverse)
         .catch(e => console.error(e));
   },
 
@@ -92,38 +100,45 @@ InspectorSearch.prototype = {
       res.query = query;
       this.emit("search-result", res);
     } else {
       this.searchBox.classList.add("devtools-style-searchbox-no-match");
       this.emit("search-result");
     }
   }),
 
-  _onCommand: function () {
+  _onInput: function () {
     if (this.searchBox.value.length === 0) {
       this._onSearch();
     }
   },
 
   _onKeyDown: function (event) {
     if (this.searchBox.value.length === 0) {
+      this.searchClearButton.hidden = true;
       this.searchBox.removeAttribute("filled");
     } else {
+      this.searchClearButton.hidden = false;
       this.searchBox.setAttribute("filled", true);
     }
     if (event.keyCode === event.DOM_VK_RETURN) {
       this._onSearch(event.shiftKey);
     }
 
     const modifierKey = system.constants.platform === "macosx"
                         ? event.metaKey : event.ctrlKey;
-    if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_G && modifierKey) {
+    if (event.keyCode === event.DOM_VK_G && modifierKey) {
       this._onSearch(event.shiftKey);
       event.preventDefault();
     }
+  },
+
+  _onClearSearch: function () {
+    this.searchBox.value = "";
+    this.searchClearButton.hidden = true;
   }
 };
 
 /**
  * Converts any input box on a page to a CSS selector search and suggestion box.
  *
  * Emits 'processing-done' event when it is done processing the current
  * keypress, search request or selection from the list, whether that led to a
--- a/devtools/client/inspector/inspector.xul
+++ b/devtools/client/inspector/inspector.xul
@@ -35,24 +35,25 @@
       <html:div id="inspector-toolbar"
         class="devtools-toolbar"
         nowindowdrag="true">
         <html:button id="inspector-element-add-button"
           title="&inspectorAddNode.label;"
           class="devtools-button" />
         <html:div class="devtools-toolbar-spacer" />
         <html:span id="inspector-searchlabel" />
-        <textbox id="inspector-searchbox"
-          type="search"
-          timeout="50"
-          class="devtools-searchinput"
-          placeholder="&inspectorSearchHTML.label3;"/>
+        <html:div id="inspector-search" class="devtools-searchbox">
+          <html:input id="inspector-searchbox" class="devtools-searchinput"
+                      type="search"
+                      placeholder="&inspectorSearchHTML.label3;"/>
+          <html:button id="inspector-searchinput-clear" class="devtools-searchinput-clear" tabindex="-1"></html:button>
+        </html:div>
         <html:button id="inspector-eyedropper-toggle"
-          title="&inspectorEyeDropper.label;"
-          class="devtools-button command-button-invertable" />
+                     title="&inspectorEyeDropper.label;"
+                     class="devtools-button command-button-invertable" />
         <div xmlns="http://www.w3.org/1999/xhtml"
           id="inspector-sidebar-toggle-box" />
       </html:div>
       <vbox flex="1" id="markup-box">
       </vbox>
       <html:div id="inspector-breadcrumbs-toolbar" class="devtools-toolbar">
         <html:div id="inspector-breadcrumbs" class="breadcrumbs-widget-container"
                   role="group" aria-label="&inspectorBreadcrumbsGroup;" tabindex="0" />
--- a/devtools/client/inspector/markup/test/actor_events_form.js
+++ b/devtools/client/inspector/markup/test/actor_events_form.js
@@ -3,52 +3,59 @@
 
 "use strict";
 
 // This test actor is used for testing the addition of custom form data
 // on NodeActor. Custom form property is set when 'form' event is sent
 // by NodeActor actor (see 'onNodeActorForm' method).
 
 const Events = require("sdk/event/core");
-const {ActorClass, Actor, FrontClass, Front, method} =
+const {ActorClass, Actor, FrontClass, Front, generateActorSpec} =
   require("devtools/shared/protocol");
 
 const {NodeActor} = require("devtools/server/actors/inspector");
 
-var EventsFormActor = ActorClass({
+var eventsSpec = generateActorSpec({
   typeName: "eventsFormActor",
 
+  methods: {
+    attach: {
+      request: {},
+      response: {}
+    },
+    detach: {
+      request: {},
+      response: {}
+    }
+  }
+});
+
+var EventsFormActor = ActorClass(eventsSpec, {
   initialize: function () {
     Actor.prototype.initialize.apply(this, arguments);
   },
 
-  attach: method(function () {
+  attach: function () {
     Events.on(NodeActor, "form", this.onNodeActorForm);
-  }, {
-    request: {},
-    response: {}
-  }),
+  },
 
-  detach: method(function () {
+  detach: function () {
     Events.off(NodeActor, "form", this.onNodeActorForm);
-  }, {
-    request: {},
-    response: {}
-  }),
+  },
 
   onNodeActorForm: function (event) {
     let nodeActor = event.target;
     if (nodeActor.rawNode.id == "container") {
       let form = event.data;
       form.setFormProperty("test-property", "test-value");
     }
   }
 });
 
-var EventsFormFront = FrontClass(EventsFormActor, {
+var EventsFormFront = FrontClass(eventsSpec, {
   initialize: function (client, form) {
     Front.prototype.initialize.apply(this, arguments);
 
     this.actorID = form[EventsFormActor.prototype.typeName];
     this.manage(this);
   }
 });
 
--- a/devtools/client/inspector/test/browser_inspector_search-02.js
+++ b/devtools/client/inspector/test/browser_inspector_search-02.js
@@ -136,17 +136,17 @@ add_task(function* () {
   let searchBox = inspector.searchBox;
   let popup = inspector.searchSuggestions.searchPopup;
 
   yield focusSearchBoxUsingShortcut(inspector.panelWin);
 
   for (let { key, suggestions } of TEST_DATA) {
     info("Pressing " + key + " to get " + formatSuggestions(suggestions));
 
-    let command = once(searchBox, "command");
+    let command = once(searchBox, "input");
     EventUtils.synthesizeKey(key, {}, inspector.panelWin);
     yield command;
 
     info("Waiting for search query to complete");
     yield inspector.searchSuggestions._lastQuery;
 
     info("Query completed. Performing checks for input '" + searchBox.value +
       "' - key pressed: " + key);
--- a/devtools/client/inspector/test/browser_inspector_search-03.js
+++ b/devtools/client/inspector/test/browser_inspector_search-03.js
@@ -217,17 +217,17 @@ add_task(function* () {
   let searchBox = inspector.searchBox;
   let popup = inspector.searchSuggestions.searchPopup;
 
   yield focusSearchBoxUsingShortcut(inspector.panelWin);
 
   for (let { key, suggestions } of TEST_DATA) {
     info("Pressing " + key + " to get " + formatSuggestions(suggestions));
 
-    let command = once(searchBox, "command");
+    let command = once(searchBox, "input");
     EventUtils.synthesizeKey(key, {}, inspector.panelWin);
     yield command;
 
     info("Waiting for search query to complete");
     yield inspector.searchSuggestions._lastQuery;
 
     info("Query completed. Performing checks for input '" +
          searchBox.value + "'");
--- a/devtools/client/inspector/test/browser_inspector_search-04.js
+++ b/devtools/client/inspector/test/browser_inspector_search-04.js
@@ -79,17 +79,17 @@ add_task(function* () {
   let searchBox = inspector.searchBox;
   let popup = inspector.searchSuggestions.searchPopup;
 
   yield focusSearchBoxUsingShortcut(inspector.panelWin);
 
   for (let {key, suggestions} of TEST_DATA) {
     info("Pressing " + key + " to get " + formatSuggestions(suggestions));
 
-    let command = once(searchBox, "command");
+    let command = once(searchBox, "input");
     EventUtils.synthesizeKey(key, {}, inspector.panelWin);
     yield command;
 
     info("Waiting for search query to complete");
     yield inspector.searchSuggestions._lastQuery;
 
     info("Query completed. Performing checks for input '" +
          searchBox.value + "'");
--- a/devtools/client/inspector/test/browser_inspector_search-07.js
+++ b/devtools/client/inspector/test/browser_inspector_search-07.js
@@ -1,49 +1,49 @@
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-"use strict";
-
-// Test that searching for classes on SVG elements does work (see bug 1219920).
-
-const TEST_URL = URL_ROOT + "doc_inspector_search-svg.html";
-
-// An array of (key, suggestions) pairs where key is a key to press and
-// suggestions is an array of suggestions that should be shown in the popup.
-const TEST_DATA = [{
-  key: "c",
-  suggestions: ["circle", "clipPath", ".class1", ".class2"]
-}, {
-  key: "VK_BACK_SPACE",
-  suggestions: []
-}, {
-  key: ".",
-  suggestions: [".class1", ".class2"]
-}];
-
-add_task(function* () {
-  let {inspector} = yield openInspectorForURL(TEST_URL);
-  let {searchBox} = inspector;
-  let popup = inspector.searchSuggestions.searchPopup;
-
-  yield focusSearchBoxUsingShortcut(inspector.panelWin);
-
-  for (let {key, suggestions} of TEST_DATA) {
-    info("Pressing " + key + " to get " + suggestions);
-
-    let command = once(searchBox, "command");
-    EventUtils.synthesizeKey(key, {}, inspector.panelWin);
-    yield command;
-
-    info("Waiting for search query to complete and getting the suggestions");
-    yield inspector.searchSuggestions._lastQuery;
-    let actualSuggestions = popup.getItems().reverse();
-
-    is(popup.isOpen ? actualSuggestions.length : 0, suggestions.length,
-       "There are expected number of suggestions.");
-
-    for (let i = 0; i < suggestions.length; i++) {
-      is(actualSuggestions[i].label, suggestions[i],
-         "The suggestion at " + i + "th index is correct.");
-    }
-  }
-});
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that searching for classes on SVG elements does work (see bug 1219920).
+
+const TEST_URL = URL_ROOT + "doc_inspector_search-svg.html";
+
+// An array of (key, suggestions) pairs where key is a key to press and
+// suggestions is an array of suggestions that should be shown in the popup.
+const TEST_DATA = [{
+  key: "c",
+  suggestions: ["circle", "clipPath", ".class1", ".class2"]
+}, {
+  key: "VK_BACK_SPACE",
+  suggestions: []
+}, {
+  key: ".",
+  suggestions: [".class1", ".class2"]
+}];
+
+add_task(function* () {
+  let {inspector} = yield openInspectorForURL(TEST_URL);
+  let {searchBox} = inspector;
+  let popup = inspector.searchSuggestions.searchPopup;
+
+  yield focusSearchBoxUsingShortcut(inspector.panelWin);
+
+  for (let {key, suggestions} of TEST_DATA) {
+    info("Pressing " + key + " to get " + suggestions);
+
+    let command = once(searchBox, "input");
+    EventUtils.synthesizeKey(key, {}, inspector.panelWin);
+    yield command;
+
+    info("Waiting for search query to complete and getting the suggestions");
+    yield inspector.searchSuggestions._lastQuery;
+    let actualSuggestions = popup.getItems().reverse();
+
+    is(popup.isOpen ? actualSuggestions.length : 0, suggestions.length,
+       "There are expected number of suggestions.");
+
+    for (let i = 0; i < suggestions.length; i++) {
+      is(actualSuggestions[i].label, suggestions[i],
+         "The suggestion at " + i + "th index is correct.");
+    }
+  }
+});
--- a/devtools/client/inspector/test/browser_inspector_search-08.js
+++ b/devtools/client/inspector/test/browser_inspector_search-08.js
@@ -1,64 +1,64 @@
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-"use strict";
-
-// Test that searching for namespaced elements does work.
-
-const XHTML = `
-  <!DOCTYPE html>
-  <html xmlns="http://www.w3.org/1999/xhtml"
-        xmlns:svg="http://www.w3.org/2000/svg">
-    <body>
-      <svg:svg width="100" height="100">
-        <svg:clipPath>
-          <svg:rect x="0" y="0" width="10" height="5"></svg:rect>
-        </svg:clipPath>
-        <svg:circle cx="0" cy="0" r="5"></svg:circle>
-      </svg:svg>
-    </body>
-  </html>
-`;
-
-const TEST_URI = "data:application/xhtml+xml;charset=utf-8," + encodeURI(XHTML);
-
-// An array of (key, suggestions) pairs where key is a key to press and
-// suggestions is an array of suggestions that should be shown in the popup.
-const TEST_DATA = [{
-  key: "c",
-  suggestions: ["circle", "clipPath"]
-}, {
-  key: "VK_BACK_SPACE",
-  suggestions: []
-}, {
-  key: "s",
-  suggestions: ["svg"]
-}];
-
-add_task(function* () {
-  let {inspector} = yield openInspectorForURL(TEST_URI);
-  let {searchBox} = inspector;
-  let popup = inspector.searchSuggestions.searchPopup;
-
-  yield focusSearchBoxUsingShortcut(inspector.panelWin);
-
-  for (let {key, suggestions} of TEST_DATA) {
-    info("Pressing " + key + " to get " + suggestions.join(", "));
-
-    let command = once(searchBox, "command");
-    EventUtils.synthesizeKey(key, {}, inspector.panelWin);
-    yield command;
-
-    info("Waiting for search query to complete and getting the suggestions");
-    yield inspector.searchSuggestions._lastQuery;
-    let actualSuggestions = popup.getItems().reverse();
-
-    is(popup.isOpen ? actualSuggestions.length : 0, suggestions.length,
-       "There are expected number of suggestions.");
-
-    for (let i = 0; i < suggestions.length; i++) {
-      is(actualSuggestions[i].label, suggestions[i],
-         "The suggestion at " + i + "th index is correct.");
-    }
-  }
-});
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that searching for namespaced elements does work.
+
+const XHTML = `
+  <!DOCTYPE html>
+  <html xmlns="http://www.w3.org/1999/xhtml"
+        xmlns:svg="http://www.w3.org/2000/svg">
+    <body>
+      <svg:svg width="100" height="100">
+        <svg:clipPath>
+          <svg:rect x="0" y="0" width="10" height="5"></svg:rect>
+        </svg:clipPath>
+        <svg:circle cx="0" cy="0" r="5"></svg:circle>
+      </svg:svg>
+    </body>
+  </html>
+`;
+
+const TEST_URI = "data:application/xhtml+xml;charset=utf-8," + encodeURI(XHTML);
+
+// An array of (key, suggestions) pairs where key is a key to press and
+// suggestions is an array of suggestions that should be shown in the popup.
+const TEST_DATA = [{
+  key: "c",
+  suggestions: ["circle", "clipPath"]
+}, {
+  key: "VK_BACK_SPACE",
+  suggestions: []
+}, {
+  key: "s",
+  suggestions: ["svg"]
+}];
+
+add_task(function* () {
+  let {inspector} = yield openInspectorForURL(TEST_URI);
+  let {searchBox} = inspector;
+  let popup = inspector.searchSuggestions.searchPopup;
+
+  yield focusSearchBoxUsingShortcut(inspector.panelWin);
+
+  for (let {key, suggestions} of TEST_DATA) {
+    info("Pressing " + key + " to get " + suggestions.join(", "));
+
+    let command = once(searchBox, "input");
+    EventUtils.synthesizeKey(key, {}, inspector.panelWin);
+    yield command;
+
+    info("Waiting for search query to complete and getting the suggestions");
+    yield inspector.searchSuggestions._lastQuery;
+    let actualSuggestions = popup.getItems().reverse();
+
+    is(popup.isOpen ? actualSuggestions.length : 0, suggestions.length,
+       "There are expected number of suggestions.");
+
+    for (let i = 0; i < suggestions.length; i++) {
+      is(actualSuggestions[i].label, suggestions[i],
+         "The suggestion at " + i + "th index is correct.");
+    }
+  }
+});
--- a/devtools/client/inspector/test/browser_inspector_search-reserved.js
+++ b/devtools/client/inspector/test/browser_inspector_search-reserved.js
@@ -99,17 +99,17 @@ add_task(function* () {
   let searchBox = inspector.searchBox;
   let popup = inspector.searchSuggestions.searchPopup;
 
   yield focusSearchBoxUsingShortcut(inspector.panelWin);
 
   for (let { key, suggestions } of TEST_DATA) {
     info("Pressing " + key + " to get " + formatSuggestions(suggestions));
 
-    let command = once(searchBox, "command");
+    let command = once(searchBox, "input");
     EventUtils.synthesizeKey(key, {}, inspector.panelWin);
     yield command;
 
     info("Waiting for search query to complete");
     yield inspector.searchSuggestions._lastQuery;
 
     info("Query completed. Performing checks for input '" +
          searchBox.value + "'");
--- a/devtools/client/inspector/test/browser_inspector_search-suggests-ids-and-classes.js
+++ b/devtools/client/inspector/test/browser_inspector_search-suggests-ids-and-classes.js
@@ -57,17 +57,17 @@ add_task(function* () {
   let popup = inspector.searchSuggestions.searchPopup;
 
   yield focusSearchBoxUsingShortcut(inspector.panelWin);
 
   for (let [key, expectedSuggestions] of KEY_STATES) {
     info("pressing key " + key + " to get suggestions " +
          JSON.stringify(expectedSuggestions));
 
-    let onCommand = once(searchBox, "command", true);
+    let onCommand = once(searchBox, "input", true);
     EventUtils.synthesizeKey(key, {}, inspector.panelWin);
     yield onCommand;
 
     info("Waiting for the suggestions to be retrieved");
     yield inspector.searchSuggestions._lastQuery;
 
     let actualSuggestions = popup.getItems();
     is(popup.isOpen ? actualSuggestions.length : 0, expectedSuggestions.length,
--- a/devtools/client/shared/test/test-actor.js
+++ b/devtools/client/shared/test/test-actor.js
@@ -41,19 +41,235 @@ var dumpn = msg => {
  */
 function getHighlighterCanvasFrameHelper(conn, actorID) {
   let actor = conn.getActor(actorID);
   if (actor && actor._highlighter) {
     return actor._highlighter.markup;
   }
 }
 
-var TestActor = exports.TestActor = protocol.ActorClass({
+var testSpec = protocol.generateActorSpec({
   typeName: "testActor",
 
+  methods: {
+    getNumberOfElementMatches: {
+      request: {
+        selector: Arg(0, "string"),
+      },
+      response: {
+        value: RetVal("number")
+      }
+    },
+    getHighlighterAttribute: {
+      request: {
+        nodeID: Arg(0, "string"),
+        name: Arg(1, "string"),
+        actorID: Arg(2, "string")
+      },
+      response: {
+        value: RetVal("string")
+      }
+    },
+    getHighlighterNodeTextContent: {
+      request: {
+        nodeID: Arg(0, "string"),
+        actorID: Arg(1, "string")
+      },
+      response: {
+        value: RetVal("string")
+      }
+    },
+    getSelectorHighlighterBoxNb: {
+      request: {
+        highlighter: Arg(0, "string"),
+      },
+      response: {
+        value: RetVal("number")
+      }
+    },
+    changeHighlightedNodeWaitForUpdate: {
+      request: {
+        name: Arg(0, "string"),
+        value: Arg(1, "string"),
+        actorID: Arg(2, "string")
+      },
+      response: {}
+    },
+    waitForHighlighterEvent: {
+      request: {
+        event: Arg(0, "string"),
+        actorID: Arg(1, "string")
+      },
+      response: {}
+    },
+    waitForEventOnNode: {
+      request: {
+        eventName: Arg(0, "string"),
+        selector: Arg(1, "nullable:string")
+      },
+      response: {}
+    },
+    changeZoomLevel: {
+      request: {
+        level: Arg(0, "string"),
+        actorID: Arg(1, "string"),
+      },
+      response: {}
+    },
+    assertElementAtPoint: {
+      request: {
+        x: Arg(0, "number"),
+        y: Arg(1, "number"),
+        selector: Arg(2, "string")
+      },
+      response: {
+        value: RetVal("boolean")
+      }
+    },
+    getAllAdjustedQuads: {
+      request: {
+        selector: Arg(0, "string")
+      },
+      response: {
+        value: RetVal("json")
+      }
+    },
+    synthesizeMouse: {
+      request: {
+        object: Arg(0, "json")
+      },
+      response: {}
+    },
+    synthesizeKey: {
+      request: {
+        args: Arg(0, "json")
+      },
+      response: {}
+    },
+    hasPseudoClassLock: {
+      request: {
+        selector: Arg(0, "string"),
+        pseudo: Arg(1, "string")
+      },
+      response: {
+        value: RetVal("boolean")
+      }
+    },
+    loadAndWaitForCustomEvent: {
+      request: {
+        url: Arg(0, "string")
+      },
+      response: {}
+    },
+    hasNode: {
+      request: {
+        selector: Arg(0, "string")
+      },
+      response: {
+        value: RetVal("boolean")
+      }
+    },
+    getBoundingClientRect: {
+      request: {
+        selector: Arg(0, "string"),
+      },
+      response: {
+        value: RetVal("json")
+      }
+    },
+    setProperty: {
+      request: {
+        selector: Arg(0, "string"),
+        property: Arg(1, "string"),
+        value: Arg(2, "string")
+      },
+      response: {}
+    },
+    getProperty: {
+      request: {
+        selector: Arg(0, "string"),
+        property: Arg(1, "string")
+      },
+      response: {
+        value: RetVal("string")
+      }
+    },
+    getAttribute: {
+      request: {
+        selector: Arg(0, "string"),
+        property: Arg(1, "string")
+      },
+      response: {
+        value: RetVal("string")
+      }
+    },
+    setAttribute: {
+      request: {
+        selector: Arg(0, "string"),
+        property: Arg(1, "string"),
+        value: Arg(2, "string")
+      },
+      response: {}
+    },
+    removeAttribute: {
+      request: {
+        selector: Arg(0, "string"),
+        property: Arg(1, "string")
+      },
+      response: {}
+    },
+    reload: {
+      request: {},
+      response: {}
+    },
+    reloadFrame: {
+      request: {
+        selector: Arg(0, "string"),
+      },
+      response: {}
+    },
+    eval: {
+      request: {
+        js: Arg(0, "string")
+      },
+      response: {
+        value: RetVal("nullable:json")
+      }
+    },
+    scrollWindow: {
+      request: {
+        x: Arg(0, "number"),
+        y: Arg(1, "number"),
+        relative: Arg(2, "nullable:boolean"),
+      },
+      response: {
+        value: RetVal("json")
+      }
+    },
+    reflow: {},
+    getNodeRect: {
+      request: {
+        selector: Arg(0, "string")
+      },
+      response: {
+        value: RetVal("json")
+      }
+    },
+    getNodeInfo: {
+      request: {
+        selector: Arg(0, "string")
+      },
+      response: {
+        value: RetVal("json")
+      }
+    }
+  }
+});
+
+var TestActor = exports.TestActor = protocol.ActorClass(testSpec, {
   initialize: function (conn, tabActor, options) {
     this.conn = conn;
     this.tabActor = tabActor;
   },
 
   get content() {
     return this.tabActor.window;
   },
@@ -89,178 +305,127 @@ var TestActor = exports.TestActor = prot
       throw new Error("Unable to find element with selector \"" + selector + "\"");
     }
     return node;
   },
   /**
    * Helper to get the number of elements matching a selector
    * @param {string} CSS selector.
    */
-  getNumberOfElementMatches: protocol.method(function (selector,
-                                                       root = this.content.document) {
+  getNumberOfElementMatches: function (selector, root = this.content.document) {
     return root.querySelectorAll(selector).length;
-  }, {
-    request: {
-      selector: Arg(0, "string"),
-    },
-    response: {
-      value: RetVal("number")
-    }
-  }),
+  },
 
   /**
    * Get a value for a given attribute name, on one of the elements of the box
    * model highlighter, given its ID.
    * @param {Object} msg The msg.data part expects the following properties
    * - {String} nodeID The full ID of the element to get the attribute for
    * - {String} name The name of the attribute to get
    * - {String} actorID The highlighter actor ID
    * @return {String} The value, if found, null otherwise
    */
-  getHighlighterAttribute: protocol.method(function (nodeID, name, actorID) {
+  getHighlighterAttribute: function (nodeID, name, actorID) {
     let helper = getHighlighterCanvasFrameHelper(this.conn, actorID);
     if (helper) {
       return helper.getAttributeForElement(nodeID, name);
     }
-  }, {
-    request: {
-      nodeID: Arg(0, "string"),
-      name: Arg(1, "string"),
-      actorID: Arg(2, "string")
-    },
-    response: {
-      value: RetVal("string")
-    }
-  }),
+  },
 
   /**
    * Get the textcontent of one of the elements of the box model highlighter,
    * given its ID.
    * @param {String} nodeID The full ID of the element to get the attribute for
    * @param {String} actorID The highlighter actor ID
    * @return {String} The textcontent value
    */
-  getHighlighterNodeTextContent: protocol.method(function (nodeID, actorID) {
+  getHighlighterNodeTextContent: function (nodeID, actorID) {
     let value;
     let helper = getHighlighterCanvasFrameHelper(this.conn, actorID);
     if (helper) {
       value = helper.getTextContentForElement(nodeID);
     }
     return value;
-  }, {
-    request: {
-      nodeID: Arg(0, "string"),
-      actorID: Arg(1, "string")
-    },
-    response: {
-      value: RetVal("string")
-    }
-  }),
+  },
 
   /**
    * Get the number of box-model highlighters created by the SelectorHighlighter
    * @param {String} actorID The highlighter actor ID
    * @return {Number} The number of box-model highlighters created, or null if the
    * SelectorHighlighter was not found.
    */
-  getSelectorHighlighterBoxNb: protocol.method(function (actorID) {
+  getSelectorHighlighterBoxNb: function (actorID) {
     let highlighter = this.conn.getActor(actorID);
     let {_highlighter: h} = highlighter;
     if (!h || !h._highlighters) {
       return null;
     } else {
       return h._highlighters.length;
     }
-  }, {
-    request: {
-      highlighter: Arg(0, "string"),
-    },
-    response: {
-      value: RetVal("number")
-    }
-  }),
+  },
 
   /**
    * Subscribe to the box-model highlighter's update event, modify an attribute of
    * the currently highlighted node and send a message when the highlighter has
    * updated.
    * @param {String} the name of the attribute to be changed
    * @param {String} the new value for the attribute
    * @param {String} actorID The highlighter actor ID
    */
-  changeHighlightedNodeWaitForUpdate: protocol.method(function (name, value, actorID) {
+  changeHighlightedNodeWaitForUpdate: function (name, value, actorID) {
     let deferred = defer();
 
     let highlighter = this.conn.getActor(actorID);
     let {_highlighter: h} = highlighter;
 
     h.once("updated", () => {
       deferred.resolve();
     });
 
     h.currentNode.setAttribute(name, value);
 
     return deferred.promise;
-  }, {
-    request: {
-      name: Arg(0, "string"),
-      value: Arg(1, "string"),
-      actorID: Arg(2, "string")
-    },
-    response: {}
-  }),
+  },
 
   /**
    * Subscribe to a given highlighter event and respond when the event is received.
    * @param {String} event The name of the highlighter event to listen to
    * @param {String} actorID The highlighter actor ID
    */
-  waitForHighlighterEvent: protocol.method(function (event, actorID) {
+  waitForHighlighterEvent: function (event, actorID) {
     let highlighter = this.conn.getActor(actorID);
     let {_highlighter: h} = highlighter;
 
     return h.once(event);
-  }, {
-    request: {
-      event: Arg(0, "string"),
-      actorID: Arg(1, "string")
-    },
-    response: {}
-  }),
+  },
 
   /**
    * Wait for a specific event on a node matching the provided selector.
    * @param {String} eventName The name of the event to listen to
    * @param {String} selector Optional:  css selector of the node which should
    *        trigger the event. If ommitted, target will be the content window
    */
-  waitForEventOnNode: protocol.method(function (eventName, selector) {
+  waitForEventOnNode: function (eventName, selector) {
     return new Promise(resolve => {
       let node = selector ? this._querySelector(selector) : this.content;
       node.addEventListener(eventName, function onEvent() {
         node.removeEventListener(eventName, onEvent);
         resolve();
       });
     });
-  }, {
-    request: {
-      eventName: Arg(0, "string"),
-      selector: Arg(1, "nullable:string")
-    },
-    response: {}
-  }),
+  },
 
   /**
    * Change the zoom level of the page.
    * Optionally subscribe to the box-model highlighter's update event and waiting
    * for it to refresh before responding.
    * @param {Number} level The new zoom level
    * @param {String} actorID Optional. The highlighter actor ID
    */
-  changeZoomLevel: protocol.method(function (level, actorID) {
+  changeZoomLevel: function (level, actorID) {
     dumpn("Zooming page to " + level);
     let deferred = defer();
 
     if (actorID) {
       let actor = this.conn.getActor(actorID);
       let {_highlighter: h} = actor;
       h.once("updated", () => {
         deferred.resolve();
@@ -270,399 +435,272 @@ var TestActor = exports.TestActor = prot
     }
 
     let docShell = this.content.QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIWebNavigation)
                                .QueryInterface(Ci.nsIDocShell);
     docShell.contentViewer.fullZoom = level;
 
     return deferred.promise;
-  }, {
-    request: {
-      level: Arg(0, "string"),
-      actorID: Arg(1, "string"),
-    },
-    response: {}
-  }),
+  },
 
-  assertElementAtPoint: protocol.method(function (x, y, selector) {
+  assertElementAtPoint: function (x, y, selector) {
     let elementAtPoint = getElementFromPoint(this.content.document, x, y);
     if (!elementAtPoint) {
       throw new Error("Unable to find element at (" + x + ", " + y + ")");
     }
     let node = this._querySelector(selector);
     return node == elementAtPoint;
-  }, {
-    request: {
-      x: Arg(0, "number"),
-      y: Arg(1, "number"),
-      selector: Arg(2, "string")
-    },
-    response: {
-      value: RetVal("boolean")
-    }
-  }),
-
+  },
 
   /**
    * Get all box-model regions' adjusted boxquads for the given element
    * @param {String} selector The node selector to target a given element
    * @return {Object} An object with each property being a box-model region, each
    * of them being an object with the p1/p2/p3/p4 properties
    */
-  getAllAdjustedQuads: protocol.method(function (selector) {
+  getAllAdjustedQuads: function (selector) {
     let regions = {};
     let node = this._querySelector(selector);
     for (let boxType of ["content", "padding", "border", "margin"]) {
       regions[boxType] = getAdjustedQuads(this.content, node, boxType);
     }
 
     return regions;
-  }, {
-    request: {
-      selector: Arg(0, "string")
-    },
-    response: {
-      value: RetVal("json")
-    }
-  }),
+  },
 
   /**
    * Synthesize a mouse event on an element, after ensuring that it is visible
    * in the viewport. This handler doesn't send a message back. Consumers
    * should listen to specific events on the inspector/highlighter to know when
    * the event got synthesized.
    * @param {String} selector The node selector to get the node target for the event
    * @param {Number} x
    * @param {Number} y
    * @param {Boolean} center If set to true, x/y will be ignored and
    *                  synthesizeMouseAtCenter will be used instead
    * @param {Object} options Other event options
    */
-  synthesizeMouse: protocol.method(function ({ selector, x, y, center, options }) {
+  synthesizeMouse: function ({ selector, x, y, center, options }) {
     let node = this._querySelector(selector);
     node.scrollIntoView();
     if (center) {
       EventUtils.synthesizeMouseAtCenter(node, options, node.ownerDocument.defaultView);
     } else {
       EventUtils.synthesizeMouse(node, x, y, options, node.ownerDocument.defaultView);
     }
-  }, {
-    request: {
-      object: Arg(0, "json")
-    },
-    response: {}
-  }),
+  },
 
   /**
   * Synthesize a key event for an element. This handler doesn't send a message
   * back. Consumers should listen to specific events on the inspector/highlighter
   * to know when the event got synthesized.
   */
-  synthesizeKey: protocol.method(function ({key, options, content}) {
+  synthesizeKey: function ({key, options, content}) {
     EventUtils.synthesizeKey(key, options, this.content);
-  }, {
-    request: {
-      args: Arg(0, "json")
-    },
-    response: {}
-  }),
+  },
 
   /**
    * Check that an element currently has a pseudo-class lock.
    * @param {String} selector The node selector to get the pseudo-class from
    * @param {String} pseudo The pseudoclass to check for
    * @return {Boolean}
    */
-  hasPseudoClassLock: protocol.method(function (selector, pseudo) {
+  hasPseudoClassLock: function (selector, pseudo) {
     let node = this._querySelector(selector);
     return DOMUtils.hasPseudoClassLock(node, pseudo);
-  }, {
-    request: {
-      selector: Arg(0, "string"),
-      pseudo: Arg(1, "string")
-    },
-    response: {
-      value: RetVal("boolean")
-    }
-  }),
+  },
 
-  loadAndWaitForCustomEvent: protocol.method(function (url) {
+  loadAndWaitForCustomEvent: function (url) {
     let deferred = defer();
     let self = this;
     // Wait for DOMWindowCreated first, as listening on the current outerwindow
     // doesn't allow receiving test-page-processing-done.
     this.tabActor.chromeEventHandler.addEventListener("DOMWindowCreated", function onWindowCreated() {
       self.tabActor.chromeEventHandler.removeEventListener("DOMWindowCreated", onWindowCreated);
       self.content.addEventListener("test-page-processing-done", function onEvent() {
         self.content.removeEventListener("test-page-processing-done", onEvent);
         deferred.resolve();
       });
     });
 
     this.content.location = url;
     return deferred.promise;
-  }, {
-    request: {
-      url: Arg(0, "string")
-    },
-    response: {}
-  }),
+  },
 
-  hasNode: protocol.method(function (selector) {
+  hasNode: function (selector) {
     try {
       // _querySelector throws if the node doesn't exists
       this._querySelector(selector);
       return true;
     } catch (e) {
       return false;
     }
-  }, {
-    request: {
-      selector: Arg(0, "string")
-    },
-    response: {
-      value: RetVal("boolean")
-    }
-  }),
+  },
 
   /**
    * Get the bounding rect for a given DOM node once.
    * @param {String} selector selector identifier to select the DOM node
    * @return {json} the bounding rect info
    */
-  getBoundingClientRect: protocol.method(function (selector) {
+  getBoundingClientRect: function (selector) {
     let node = this._querySelector(selector);
     return node.getBoundingClientRect();
-  }, {
-    request: {
-      selector: Arg(0, "string"),
-    },
-    response: {
-      value: RetVal("json")
-    }
-  }),
+  },
 
   /**
    * Set a JS property on a DOM Node.
    * @param {String} selector The node selector
    * @param {String} property The property name
    * @param {String} value The attribute value
    */
-  setProperty: protocol.method(function (selector, property, value) {
+  setProperty: function (selector, property, value) {
     let node = this._querySelector(selector);
     node[property] = value;
-  }, {
-    request: {
-      selector: Arg(0, "string"),
-      property: Arg(1, "string"),
-      value: Arg(2, "string")
-    },
-    response: {}
-  }),
+  },
 
   /**
    * Get a JS property on a DOM Node.
    * @param {String} selector The node selector
    * @param {String} property The property name
    * @return {String} value The attribute value
    */
-  getProperty: protocol.method(function (selector, property) {
+  getProperty: function (selector, property) {
     let node = this._querySelector(selector);
     return node[property];
-  }, {
-    request: {
-      selector: Arg(0, "string"),
-      property: Arg(1, "string")
-    },
-    response: {
-      value: RetVal("string")
-    }
-  }),
+  },
 
   /**
    * Get an attribute on a DOM Node.
    * @param {String} selector The node selector
    * @param {String} attribute The attribute name
    * @return {String} value The attribute value
    */
-  getAttribute: protocol.method(function (selector, attribute) {
+  getAttribute: function (selector, attribute) {
     let node = this._querySelector(selector);
     return node.getAttribute(attribute);
-  }, {
-    request: {
-      selector: Arg(0, "string"),
-      property: Arg(1, "string")
-    },
-    response: {
-      value: RetVal("string")
-    }
-  }),
+  },
 
   /**
    * Set an attribute on a DOM Node.
    * @param {String} selector The node selector
    * @param {String} attribute The attribute name
    * @param {String} value The attribute value
    */
-  setAttribute: protocol.method(function (selector, attribute, value) {
+  setAttribute: function (selector, attribute, value) {
     let node = this._querySelector(selector);
     node.setAttribute(attribute, value);
-  }, {
-    request: {
-      selector: Arg(0, "string"),
-      property: Arg(1, "string"),
-      value: Arg(2, "string")
-    },
-    response: {}
-  }),
+  },
 
   /**
    * Remove an attribute from a DOM Node.
    * @param {String} selector The node selector
    * @param {String} attribute The attribute name
    */
-  removeAttribute: protocol.method(function (selector, attribute) {
+  removeAttribute: function (selector, attribute) {
     let node = this._querySelector(selector);
     node.removeAttribute(attribute);
-  }, {
-    request: {
-      selector: Arg(0, "string"),
-      property: Arg(1, "string")
-    },
-    response: {}
-  }),
+  },
 
   /**
    * Reload the content window.
    */
-  reload: protocol.method(function () {
+  reload: function () {
     this.content.location.reload();
-  }, {
-    request: {},
-    response: {}
-  }),
+  },
 
   /**
    * Reload an iframe and wait for its load event.
    * @param {String} selector The node selector
    */
-  reloadFrame: protocol.method(function (selector) {
+  reloadFrame: function (selector) {
     let node = this._querySelector(selector);
 
     let deferred = defer();
 
     let onLoad = function () {
       node.removeEventListener("load", onLoad);
       deferred.resolve();
     };
     node.addEventListener("load", onLoad);
 
     node.contentWindow.location.reload();
     return deferred.promise;
-  }, {
-    request: {
-      selector: Arg(0, "string"),
-    },
-    response: {}
-  }),
+  },
 
   /**
    * Evaluate a JS string in the context of the content document.
    * @param {String} js JS string to evaluate
    * @return {json} The evaluation result
    */
-  eval: protocol.method(function (js) {
+  eval: function (js) {
     // We have to use a sandbox, as CSP prevent us from using eval on apps...
     let sb = Cu.Sandbox(this.content, { sandboxPrototype: this.content });
     return Cu.evalInSandbox(js, sb);
-  }, {
-    request: {
-      js: Arg(0, "string")
-    },
-    response: {
-      value: RetVal("nullable:json")
-    }
-  }),
+  },
 
   /**
    * Scrolls the window to a particular set of coordinates in the document, or
    * by the given amount if `relative` is set to `true`.
    *
    * @param {Number} x
    * @param {Number} y
    * @param {Boolean} relative
    *
    * @return {Object} An object with x / y properties, representing the number
    * of pixels that the document has been scrolled horizontally and vertically.
    */
-  scrollWindow: protocol.method(function (x, y, relative) {
+  scrollWindow: function (x, y, relative) {
     if (isNaN(x) || isNaN(y)) {
       return {};
     }
 
     let deferred = defer();
     this.content.addEventListener("scroll", function onScroll(event) {
       this.removeEventListener("scroll", onScroll);
 
       let data = {x: this.content.scrollX, y: this.content.scrollY};
       deferred.resolve(data);
     });
 
     this.content[relative ? "scrollBy" : "scrollTo"](x, y);
 
     return deferred.promise;
-  }, {
-    request: {
-      x: Arg(0, "number"),
-      y: Arg(1, "number"),
-      relative: Arg(2, "nullable:boolean"),
-    },
-    response: {
-      value: RetVal("json")
-    }
-  }),
+  },
 
   /**
    * Forces the reflow and waits for the next repaint.
    */
-  reflow: protocol.method(function () {
+  reflow: function () {
     let deferred = defer();
     this.content.document.documentElement.offsetWidth;
     this.content.requestAnimationFrame(deferred.resolve);
 
     return deferred.promise;
-  }),
+  },
 
-  getNodeRect: protocol.method(Task.async(function* (selector) {
+  getNodeRect: Task.async(function* (selector) {
     let node = this._querySelector(selector);
     return getRect(this.content, node, this.content);
-  }), {
-    request: {
-      selector: Arg(0, "string")
-    },
-    response: {
-      value: RetVal("json")
-    }
   }),
 
   /**
    * Get information about a DOM element, identified by a selector.
    * @param {String} selector The CSS selector to get the node (can be an array
    * of selectors to get elements in an iframe).
    * @return {Object} data Null if selector didn't match any node, otherwise:
    * - {String} tagName.
    * - {String} namespaceURI.
    * - {Number} numChildren The number of children in the element.
    * - {Array} attributes An array of {name, value, namespaceURI} objects.
    * - {String} outerHTML.
    * - {String} innerHTML.
    * - {String} textContent.
    */
-  getNodeInfo: protocol.method(function (selector) {
+  getNodeInfo: function (selector) {
     let node = this._querySelector(selector);
     let info = null;
 
     if (node) {
       info = {
         tagName: node.tagName,
         namespaceURI: node.namespaceURI,
         numChildren: node.children.length,
@@ -671,27 +709,20 @@ var TestActor = exports.TestActor = prot
         }),
         outerHTML: node.outerHTML,
         innerHTML: node.innerHTML,
         textContent: node.textContent
       };
     }
 
     return info;
-  }, {
-    request: {
-      selector: Arg(0, "string")
-    },
-    response: {
-      value: RetVal("json")
-    }
-  })
+  }
 });
 
-var TestActorFront = exports.TestActorFront = protocol.FrontClass(TestActor, {
+var TestActorFront = exports.TestActorFront = protocol.FrontClass(testSpec, {
   initialize: function (client, { testActor }, toolbox) {
     protocol.Front.prototype.initialize.call(this, client, { actor: testActor });
     this.manage(this);
     this.toolbox = toolbox;
   },
 
   /**
    * Zoom the current page to a given level.
--- a/devtools/client/themes/inspector.css
+++ b/devtools/client/themes/inspector.css
@@ -22,16 +22,28 @@
 
 #inspector-toolbar.devtools-toolbar .devtools-toolbar-spacer {
   flex-grow: 1;
   display: inline-block;
 }
 
 #inspector-searchlabel {
   overflow: hidden;
+  margin-inline-end: 2px;
+}
+
+#inspector-search {
+  flex: unset;
+}
+
+/* TODO: bug 1265759: should apply to .devtools-searchinput once all searchbox
+   is converted to html*/
+#inspector-searchbox {
+  flex: 1;
+  width: 100%;
 }
 
 /* Make sure the text is vertically centered in Inspector's
    search box. This can be removed when the search box is
    switched to HTML.
    See also: https://bugzilla.mozilla.org/show_bug.cgi?id=1265759 */
 .theme-dark #inspector-searchbox,
 .theme-light #inspector-searchbox {
--- a/devtools/client/webconsole/test/browser_webconsole_split_focus.js
+++ b/devtools/client/webconsole/test/browser_webconsole_split_focus.js
@@ -21,38 +21,34 @@
     toolbox = yield gDevTools.showToolbox(target, "inspector");
 
     ok(!toolbox.splitConsole, "Split console is hidden by default");
 
     info("Focusing the search box before opening the split console");
     let inspector = toolbox.getPanel("inspector");
     inspector.searchBox.focus();
 
-    // Use the binding element since inspector.searchBox is a XUL element.
     let activeElement = getActiveElement(inspector.panelDoc);
-    activeElement = activeElement.ownerDocument.getBindingParent(activeElement);
     is(activeElement, inspector.searchBox, "Search box is focused");
 
     yield toolbox.openSplitConsole();
 
     ok(toolbox.splitConsole, "Split console is now visible");
 
     // Use the binding element since jsterm.inputNode is a XUL textarea element.
     activeElement = getActiveElement(toolbox.doc);
     activeElement = activeElement.ownerDocument.getBindingParent(activeElement);
     let inputNode = toolbox.getPanel("webconsole").hud.jsterm.inputNode;
     is(activeElement, inputNode, "Split console input is focused by default");
 
     yield toolbox.closeSplitConsole();
 
     info("Making sure that the search box is refocused after closing the " +
          "split console");
-    // Use the binding element since inspector.searchBox is a XUL element.
     activeElement = getActiveElement(inspector.panelDoc);
-    activeElement = activeElement.ownerDocument.getBindingParent(activeElement);
     is(activeElement, inspector.searchBox, "Search box is focused");
 
     yield toolbox.destroy();
   }
 
   function getActiveElement(doc) {
     let activeElement = doc.activeElement;
     while (activeElement && activeElement.contentDocument) {
--- a/devtools/server/actors/actor-registry.js
+++ b/devtools/server/actors/actor-registry.js
@@ -12,33 +12,33 @@ const Services = require("Services");
 const { DebuggerServer } = require("devtools/server/main");
 const { registerActor, unregisterActor } = require("devtools/server/actors/utils/actor-registry-utils");
 const { actorActorSpec, actorRegistrySpec } = require("devtools/shared/specs/actor-registry");
 
 /**
  * The ActorActor gives you a handle to an actor you've dynamically
  * registered and allows you to unregister it.
  */
-const ActorActor = protocol.ActorClassWithSpec(actorActorSpec, {
+const ActorActor = protocol.ActorClass(actorActorSpec, {
   initialize: function (conn, options) {
     protocol.Actor.prototype.initialize.call(this, conn);
 
     this.options = options;
   },
 
   unregister: function () {
     unregisterActor(this.options);
   }
 });
 
 /*
  * The ActorRegistryActor allows clients to define new actors on the
  * server. This is particularly useful for addons.
  */
-const ActorRegistryActor = protocol.ActorClassWithSpec(actorRegistrySpec, {
+const ActorRegistryActor = protocol.ActorClass(actorRegistrySpec, {
   initialize: function (conn) {
     protocol.Actor.prototype.initialize.call(this, conn);
   },
 
   registerActor: function (sourceText, fileName, options) {
     return registerActor(sourceText, fileName, options).then(() => {
       let { constructor, type } = options;
 
--- a/devtools/server/actors/addons.js
+++ b/devtools/server/actors/addons.js
@@ -5,17 +5,17 @@
 "use strict";
 
 const {AddonManager} = require("resource://gre/modules/AddonManager.jsm");
 const protocol = require("devtools/shared/protocol");
 const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
 const {Task} = require("devtools/shared/task");
 const {addonsSpec} = require("devtools/shared/specs/addons");
 
-const AddonsActor = protocol.ActorClassWithSpec(addonsSpec, {
+const AddonsActor = protocol.ActorClass(addonsSpec, {
 
   initialize: function (conn) {
     protocol.Actor.prototype.initialize.call(this, conn);
   },
 
   installTemporaryAddon: Task.async(function* (addonPath) {
     let addonFile;
     let addon;
--- a/devtools/server/actors/animation.js
+++ b/devtools/server/actors/animation.js
@@ -24,18 +24,17 @@
  * - WebAnimation WebIDL files:
  *   /dom/webidl/Animation*.webidl
  */
 
 const {Cu} = require("chrome");
 const promise = require("promise");
 const {Task} = require("devtools/shared/task");
 const protocol = require("devtools/shared/protocol");
-const {ActorClass, Actor, FrontClass, Front,
-       Arg, method, RetVal, types} = protocol;
+const {Actor, ActorClass} = protocol;
 const {animationPlayerSpec, animationsSpec} = require("devtools/shared/specs/animation");
 const events = require("sdk/event/core");
 
 // Types of animations.
 const ANIMATION_TYPES = {
   CSS_ANIMATION: "cssanimation",
   CSS_TRANSITION: "csstransition",
   SCRIPT_ANIMATION: "scriptanimation",
@@ -47,17 +46,17 @@ exports.ANIMATION_TYPES = ANIMATION_TYPE
  * The AnimationPlayerActor provides information about a given animation: its
  * startTime, currentTime, current state, etc.
  *
  * Since the state of a player changes as the animation progresses it is often
  * useful to call getCurrentState at regular intervals to get the current state.
  *
  * This actor also allows playing, pausing and seeking the animation.
  */
-var AnimationPlayerActor = protocol.ActorClassWithSpec(animationPlayerSpec, {
+var AnimationPlayerActor = protocol.ActorClass(animationPlayerSpec, {
   /**
    * @param {AnimationsActor} The main AnimationsActor instance
    * @param {AnimationPlayer} The player object returned by getAnimationPlayers
    */
   initialize: function (animationsActor, player) {
     Actor.prototype.initialize.call(this, animationsActor.conn);
 
     this.onAnimationMutation = this.onAnimationMutation.bind(this);
@@ -429,17 +428,17 @@ var AnimationPlayerActor = protocol.Acto
   }
 });
 
 exports.AnimationPlayerActor = AnimationPlayerActor;
 
 /**
  * The Animations actor lists animation players for a given node.
  */
-var AnimationsActor = exports.AnimationsActor = protocol.ActorClassWithSpec(animationsSpec, {
+var AnimationsActor = exports.AnimationsActor = protocol.ActorClass(animationsSpec, {
   initialize: function(conn, tabActor) {
     Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
 
     this.onWillNavigate = this.onWillNavigate.bind(this);
     this.onNavigate = this.onNavigate.bind(this);
     this.onAnimationMutation = this.onAnimationMutation.bind(this);
 
--- a/devtools/server/actors/breakpoint.js
+++ b/devtools/server/actors/breakpoint.js
@@ -1,17 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { ActorClassWithSpec } = require("devtools/shared/protocol");
+const { ActorClass } = require("devtools/shared/protocol");
 const { breakpointSpec } = require("devtools/shared/specs/breakpoint");
 
 /**
  * Set breakpoints on all the given entry points with the given
  * BreakpointActor as the handler.
  *
  * @param BreakpointActor actor
  *        The actor handling the breakpoint hits.
@@ -29,17 +29,17 @@ function setBreakpointAtEntryPoints(acto
 
 exports.setBreakpointAtEntryPoints = setBreakpointAtEntryPoints;
 
 /**
  * BreakpointActors exist for the lifetime of their containing thread and are
  * responsible for deleting breakpoints, handling breakpoint hits and
  * associating breakpoints with scripts.
  */
-let BreakpointActor = ActorClassWithSpec(breakpointSpec, {
+let BreakpointActor = ActorClass(breakpointSpec, {
   /**
    * Create a Breakpoint actor.
    *
    * @param ThreadActor threadActor
    *        The parent thread actor that contains this breakpoint.
    * @param OriginalLocation originalLocation
    *        The original location of the breakpoint.
    */
--- a/devtools/server/actors/call-watcher.js
+++ b/devtools/server/actors/call-watcher.js
@@ -13,17 +13,17 @@ const {method, Arg, Option, RetVal} = pr
 
 const { functionCallSpec, callWatcherSpec } = require("devtools/shared/specs/call-watcher");
 const { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");
 
 /**
  * This actor contains information about a function call, like the function
  * type, name, stack, arguments, returned value etc.
  */
-var FunctionCallActor = protocol.ActorClassWithSpec(functionCallSpec, {
+var FunctionCallActor = protocol.ActorClass(functionCallSpec, {
   /**
    * Creates the function call actor.
    *
    * @param DebuggerServerConnection conn
    *        The server connection.
    * @param DOMWindow window
    *        The content window.
    * @param string global
@@ -221,17 +221,17 @@ var FunctionCallActor = protocol.ActorCl
     }
     return data + "";
   }
 });
 
 /**
  * This actor observes function calls on certain objects or globals.
  */
-var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClassWithSpec(callWatcherSpec, {
+var CallWatcherActor = exports.CallWatcherActor = protocol.ActorClass(callWatcherSpec, {
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
     this._onGlobalCreated = this._onGlobalCreated.bind(this);
     this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this);
     this._onContentFunctionCall = this._onContentFunctionCall.bind(this);
     on(this.tabActor, "window-ready", this._onGlobalCreated);
     on(this.tabActor, "window-destroyed", this._onGlobalDestroyed);
--- a/devtools/server/actors/canvas.js
+++ b/devtools/server/actors/canvas.js
@@ -25,17 +25,17 @@ const {CanvasFront} = require("devtools/
 const {on, once, off, emit} = events;
 const {method, custom, Arg, Option, RetVal} = protocol;
 
 /**
  * This actor represents a recorded animation frame snapshot, along with
  * all the corresponding canvas' context methods invoked in that frame,
  * thumbnails for each draw call and a screenshot of the end result.
  */
-var FrameSnapshotActor = protocol.ActorClassWithSpec(frameSnapshotSpec, {
+var FrameSnapshotActor = protocol.ActorClass(frameSnapshotSpec, {
   /**
    * Creates the frame snapshot call actor.
    *
    * @param DebuggerServerConnection conn
    *        The server connection.
    * @param HTMLCanvasElement canvas
    *        A reference to the content canvas.
    * @param array calls
@@ -115,17 +115,17 @@ var FrameSnapshotActor = protocol.ActorC
   }
 });
 
 /**
  * This Canvas Actor handles simple instrumentation of all the methods
  * of a 2D or WebGL context, to provide information regarding all the calls
  * made when drawing frame inside an animation loop.
  */
-var CanvasActor = exports.CanvasActor = protocol.ActorClassWithSpec(canvasSpec, {
+var CanvasActor = exports.CanvasActor = protocol.ActorClass(canvasSpec, {
   // Reset for each recording, boolean indicating whether or not
   // any draw calls were called for a recording.
   _animationContainsDrawCall: false,
 
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
     this._webGLPrimitiveCounter = new WebGLPrimitiveCounter(tabActor);
--- a/devtools/server/actors/common.js
+++ b/devtools/server/actors/common.js
@@ -506,16 +506,16 @@ function actorBridge(methodName, definit
   return method(function () {
     return this.bridge[methodName].apply(this.bridge, arguments);
   }, definition);
 }
 exports.actorBridge = actorBridge;
 
 /**
  * Like `actorBridge`, but without a spec definition, for when the actor is
- * created with `ActorClassWithSpec` rather than vanilla `ActorClass`.
+ * created with `ActorClass` rather than vanilla `ActorClass`.
  */
 function actorBridgeWithSpec (methodName) {
   return method(function () {
     return this.bridge[methodName].apply(this.bridge, arguments);
   });
 }
 exports.actorBridgeWithSpec = actorBridgeWithSpec;
--- a/devtools/server/actors/css-properties.js
+++ b/devtools/server/actors/css-properties.js
@@ -10,22 +10,22 @@ loader.lazyGetter(this, "DOMUtils", () =
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
 
 loader.lazyGetter(this, "appInfo", () => {
   return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
 });
 
 const protocol = require("devtools/shared/protocol");
-const { ActorClassWithSpec, Actor } = protocol;
+const { ActorClass, Actor } = protocol;
 const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
 const { CSS_PROPERTIES, CSS_TYPES } = require("devtools/shared/css-properties-db");
 const { cssColors } = require("devtools/shared/css-color-db");
 
-exports.CssPropertiesActor = ActorClassWithSpec(cssPropertiesSpec, {
+exports.CssPropertiesActor = ActorClass(cssPropertiesSpec, {
   typeName: "cssProperties",
 
   initialize(conn, parent) {
     Actor.prototype.initialize.call(this, conn);
     this.parent = parent;
   },
 
   destroy() {
--- a/devtools/server/actors/csscoverage.js
+++ b/devtools/server/actors/csscoverage.js
@@ -62,17 +62,17 @@ const l10n = exports.l10n = {
  *         cssText: "p.quote { color: red; }",
  *         isUsed: true,
  *         presentOn: Set([ "http://eg.com/page1.html", ... ]),
  *         preLoadOn: Set([ "http://eg.com/page1.html" ]),
  *         isError: false,
  *       }, ...
  *     });
  */
-var CSSUsageActor = protocol.ActorClassWithSpec(cssUsageSpec, {
+var CSSUsageActor = protocol.ActorClass(cssUsageSpec, {
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
 
     this._tabActor = tabActor;
     this._running = false;
 
     this._onTabLoad = this._onTabLoad.bind(this);
     this._onChange = this._onChange.bind(this);
--- a/devtools/server/actors/device.js
+++ b/devtools/server/actors/device.js
@@ -10,17 +10,17 @@ const protocol = require("devtools/share
 const promise = require("promise");
 const {LongStringActor} = require("devtools/server/actors/string");
 const {DebuggerServer} = require("devtools/server/main");
 const {getSystemInfo, getSetting} = require("devtools/shared/system");
 const {deviceSpec} = require("devtools/shared/specs/device");
 const FileReader = require("FileReader");
 const {PermissionsTable} = require("resource://gre/modules/PermissionsTable.jsm");
 
-var DeviceActor = exports.DeviceActor = protocol.ActorClassWithSpec(deviceSpec, {
+var DeviceActor = exports.DeviceActor = protocol.ActorClass(deviceSpec, {
   _desc: null,
 
   getDescription: function () {
     return getSystemInfo();
   },
 
   getWallpaper: function () {
     let deferred = promise.defer();
--- a/devtools/server/actors/director-manager.js
+++ b/devtools/server/actors/director-manager.js
@@ -33,17 +33,17 @@ const ERR_MESSAGEPORT_FINALIZED = "messa
 
 const ERR_DIRECTOR_UNKNOWN_SCRIPTID = "unkown director-script id";
 const ERR_DIRECTOR_UNINSTALLED_SCRIPTID = "uninstalled director-script id";
 
 /**
  * A MessagePort Actor allowing communication through messageport events
  * over the remote debugging protocol.
  */
-var MessagePortActor = exports.MessagePortActor = protocol.ActorClassWithSpec(messagePortSpec, {
+var MessagePortActor = exports.MessagePortActor = protocol.ActorClass(messagePortSpec, {
   /**
    * Create a MessagePort actor.
    *
    * @param DebuggerServerConnection conn
    *        The server connection.
    * @param MessagePort port
    *        The wrapped MessagePort.
    */
@@ -144,17 +144,17 @@ var MessagePortActor = exports.MessagePo
  *
  * After retrieving an instance of this actor (from the tab director actor), you'll need to set it up
  * by calling setup().
  *
  * After the setup, this actor will automatically attach/detach the content script (and optionally a
  * directly connect the debugger client and the content script using a MessageChannel) on tab
  * navigation.
  */
-var DirectorScriptActor = exports.DirectorScriptActor = protocol.ActorClassWithSpec(directorScriptSpec, {
+var DirectorScriptActor = exports.DirectorScriptActor = protocol.ActorClass(directorScriptSpec, {
   /**
    * Creates the director script actor
    *
    * @param DebuggerServerConnection conn
    *        The server connection.
    * @param Actor tabActor
    *        The tab (or root) actor.
    * @param String scriptId
@@ -332,17 +332,17 @@ var DirectorScriptActor = exports.Direct
       port: this._messagePortActor
     });
   }
 });
 
 /**
  * The DirectorManager Actor is a tab actor which manages enabling/disabling director scripts.
  */
-const DirectorManagerActor = exports.DirectorManagerActor = protocol.ActorClassWithSpec(directorManagerSpec, {
+const DirectorManagerActor = exports.DirectorManagerActor = protocol.ActorClass(directorManagerSpec, {
   /* init & destroy methods */
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
     this._directorScriptActorsMap = new Map();
   },
   destroy: function (conn) {
     protocol.Actor.prototype.destroy.call(this, conn);
--- a/devtools/server/actors/director-registry.js
+++ b/devtools/server/actors/director-registry.js
@@ -150,17 +150,17 @@ exports.setupParentProcess = function se
     }
   }
 };
 
 /**
  * The DirectorRegistry Actor is a global actor which manages install/uninstall of
  * director scripts definitions.
  */
-const DirectorRegistryActor = exports.DirectorRegistryActor = protocol.ActorClassWithSpec(directorRegistrySpec, {
+const DirectorRegistryActor = exports.DirectorRegistryActor = protocol.ActorClass(directorRegistrySpec, {
   /* init & destroy methods */
   initialize: function (conn, parentActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.maybeSetupChildProcess(conn);
   },
   destroy: function (conn) {
     protocol.Actor.prototype.destroy.call(this, conn);
     this.finalize();
--- a/devtools/server/actors/environment.js
+++ b/devtools/server/actors/environment.js
@@ -1,30 +1,30 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { ActorClassWithSpec } = require("devtools/shared/protocol");
+const { ActorClass } = require("devtools/shared/protocol");
 const { createValueGrip } = require("devtools/server/actors/object");
 const { environmentSpec } = require("devtools/shared/specs/environment");
 
 /**
  * Creates an EnvironmentActor. EnvironmentActors are responsible for listing
  * the bindings introduced by a lexical environment and assigning new values to
  * those identifier bindings.
  *
  * @param Debugger.Environment aEnvironment
  *        The lexical environment that will be used to create the actor.
  * @param ThreadActor aThreadActor
  *        The parent thread actor that contains this environment.
  */
-let EnvironmentActor = ActorClassWithSpec(environmentSpec, {
+let EnvironmentActor = ActorClass(environmentSpec, {
   initialize: function (environment, threadActor) {
     this.obj = environment;
     this.threadActor = threadActor;
   },
 
   /**
    * Return an environment form for use in a protocol message.
    */
--- a/devtools/server/actors/eventlooplag.js
+++ b/devtools/server/actors/eventlooplag.js
@@ -8,21 +8,21 @@
  * The eventLoopLag actor emits "event-loop-lag" events when the event
  * loop gets unresponsive. The event comes with a "time" property (the
  * duration of the lag in milliseconds).
  */
 
 const {Ci} = require("chrome");
 const Services = require("Services");
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
-const {Actor, ActorClassWithSpec} = require("devtools/shared/protocol");
+const {Actor, ActorClass} = require("devtools/shared/protocol");
 const events = require("sdk/event/core");
 const {eventLoopLagSpec} = require("devtools/shared/specs/eventlooplag");
 
-var EventLoopLagActor = exports.EventLoopLagActor = ActorClassWithSpec(eventLoopLagSpec, {
+var EventLoopLagActor = exports.EventLoopLagActor = ActorClass(eventLoopLagSpec, {
   _observerAdded: false,
 
   /**
    * Start tracking the event loop lags.
    */
   start: function () {
     if (!this._observerAdded) {
       Services.obs.addObserver(this, "event-loop-lag", false);
--- a/devtools/server/actors/frame.js
+++ b/devtools/server/actors/frame.js
@@ -3,23 +3,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { ActorPool } = require("devtools/server/actors/common");
 const { createValueGrip } = require("devtools/server/actors/object");
-const { ActorClassWithSpec } = require("devtools/shared/protocol");
+const { ActorClass } = require("devtools/shared/protocol");
 const { frameSpec } = require("devtools/shared/specs/frame");
 
 /**
  * An actor for a specified stack frame.
  */
-let FrameActor = ActorClassWithSpec(frameSpec, {
+let FrameActor = ActorClass(frameSpec, {
   /**
    * Creates the Frame actor.
    *
    * @param frame Debugger.Frame
    *        The debuggee frame.
    * @param threadActor ThreadActor
    *        The parent thread actor for this frame.
    */
--- a/devtools/server/actors/framerate.js
+++ b/devtools/server/actors/framerate.js
@@ -1,26 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
+const { Actor, ActorClass } = require("devtools/shared/protocol");
 const { actorBridgeWithSpec } = require("devtools/server/actors/common");
 const { on, once, off, emit } = require("sdk/event/core");
 const { Framerate } = require("devtools/server/performance/framerate");
 const { framerateSpec } = require("devtools/shared/specs/framerate");
 
 /**
  * An actor wrapper around Framerate. Uses exposed
  * methods via bridge and provides RDP definitions.
  *
  * @see devtools/server/performance/framerate.js for documentation.
  */
-var FramerateActor = exports.FramerateActor = ActorClassWithSpec(framerateSpec, {
+var FramerateActor = exports.FramerateActor = ActorClass(framerateSpec, {
   initialize: function (conn, tabActor) {
     Actor.prototype.initialize.call(this, conn);
     this.bridge = new Framerate(tabActor);
   },
   destroy: function (conn) {
     Actor.prototype.destroy.call(this, conn);
     this.bridge.destroy();
   },
--- a/devtools/server/actors/gcli.js
+++ b/devtools/server/actors/gcli.js
@@ -1,26 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Task } = require("devtools/shared/task");
 const {
-  method, Arg, Option, RetVal, Actor, ActorClassWithSpec
+  method, Arg, Option, RetVal, Actor, ActorClass
 } = require("devtools/shared/protocol");
 const { gcliSpec } = require("devtools/shared/specs/gcli");
 const events = require("sdk/event/core");
 const { createSystem } = require("gcli/system");
 
 /**
  * Manage remote connections that want to talk to GCLI
  */
-const GcliActor = ActorClassWithSpec(gcliSpec, {
+const GcliActor = ActorClass(gcliSpec, {
   initialize: function (conn, tabActor) {
     Actor.prototype.initialize.call(this, conn);
 
     this._commandsChanged = this._commandsChanged.bind(this);
 
     this._tabActor = tabActor;
     this._requisitionPromise = undefined; // see _getRequisition()
   },
--- a/devtools/server/actors/heap-snapshot-file.js
+++ b/devtools/server/actors/heap-snapshot-file.js
@@ -18,17 +18,17 @@ loader.lazyRequireGetter(this, "HeapSnap
                          "devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
 
 /**
  * The HeapSnapshotFileActor handles transferring heap snapshot files from the
  * server to the client. This has to be a global actor in the parent process
  * because child processes are sandboxed and do not have access to the file
  * system.
  */
-exports.HeapSnapshotFileActor = protocol.ActorClassWithSpec(heapSnapshotFileSpec, {
+exports.HeapSnapshotFileActor = protocol.ActorClass(heapSnapshotFileSpec, {
   initialize: function (conn, parent) {
     if (Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT) {
       const err = new Error("Attempt to create a HeapSnapshotFileActor in a " +
                             "child process! The HeapSnapshotFileActor *MUST* " +
                             "be in the parent process!");
       DevToolsUtils.reportException(
         "HeapSnapshotFileActor.prototype.initialize", err);
       return;
--- a/devtools/server/actors/highlighters.js
+++ b/devtools/server/actors/highlighters.js
@@ -77,17 +77,17 @@ exports.register = register;
  *
  * Other types of highlighter actors exist and can be accessed via the
  * InspectorActor's 'getHighlighterByType' method.
  */
 
 /**
  * The HighlighterActor class
  */
-var HighlighterActor = exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
+var HighlighterActor = exports.HighlighterActor = protocol.ActorClass(highlighterSpec, {
   initialize: function (inspector, autohide) {
     protocol.Actor.prototype.initialize.call(this, null);
 
     this._autohide = autohide;
     this._inspector = inspector;
     this._walker = this._inspector.walker;
     this._tabActor = this._inspector.tabActor;
     this._highlighterEnv = new HighlighterEnvironment();
@@ -402,17 +402,17 @@ var HighlighterActor = exports.Highlight
     }
   }
 });
 
 /**
  * A generic highlighter actor class that instantiate a highlighter given its
  * type name and allows to show/hide it.
  */
-var CustomHighlighterActor = exports.CustomHighlighterActor = protocol.ActorClassWithSpec(customHighlighterSpec, {
+var CustomHighlighterActor = exports.CustomHighlighterActor = protocol.ActorClass(customHighlighterSpec, {
   /**
    * Create a highlighter instance given its typename
    * The typename must be one of HIGHLIGHTER_CLASSES and the class must
    * implement constructor(tabActor), show(node), hide(), destroy()
    */
   initialize: function (inspector, typeName) {
     protocol.Actor.prototype.initialize.call(this, null);
 
--- a/devtools/server/actors/inspector.js
+++ b/devtools/server/actors/inspector.js
@@ -192,17 +192,17 @@ exports.setInspectingNode = function (va
 const getNodeDisplayName = function (rawNode) {
   return (rawNode.prefix ? rawNode.prefix + ":" : "") + rawNode.localName;
 };
 exports.getNodeDisplayName = getNodeDisplayName;
 
 /**
  * Server side of the node actor.
  */
-var NodeActor = exports.NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
+var NodeActor = exports.NodeActor = protocol.ActorClass(nodeSpec, {
   initialize: function (walker, node) {
     protocol.Actor.prototype.initialize.call(this, null);
     this.walker = walker;
     this.rawNode = node;
     this._eventParsers = new EventParsers().parsers;
 
     // Storing the original display of the node, to track changes when reflows
     // occur
@@ -703,17 +703,17 @@ var NodeActor = exports.NodeActor = prot
 
     return { data: LongStringActor(this.conn, dataURL), size: size };
   }
 });
 
 /**
  * Server side of a node list as returned by querySelectorAll()
  */
-var NodeListActor = exports.NodeListActor = protocol.ActorClassWithSpec(nodeListSpec, {
+var NodeListActor = exports.NodeListActor = protocol.ActorClass(nodeListSpec, {
   typeName: "domnodelist",
 
   initialize: function (walker, nodeList) {
     protocol.Actor.prototype.initialize.call(this);
     this.walker = walker;
     this.nodeList = nodeList || [];
   },
 
@@ -761,17 +761,17 @@ var NodeListActor = exports.NodeListActo
   },
 
   release: function () {}
 });
 
 /**
  * Server side of the DOM walker.
  */
-var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
+var WalkerActor = protocol.ActorClass(walkerSpec, {
   /**
    * Create the WalkerActor
    * @param DebuggerServerConnection conn
    *    The server connection.
    */
   initialize: function (conn, tabActor, options) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
@@ -2573,17 +2573,17 @@ var WalkerActor = protocol.ActorClassWit
     return this.attachElement(obj);
   },
 });
 
 /**
  * Server side of the inspector actor, which is used to create
  * inspector-related actors, including the walker.
  */
-exports.InspectorActor = protocol.ActorClassWithSpec(inspectorSpec, {
+exports.InspectorActor = protocol.ActorClass(inspectorSpec, {
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
 
     this._onColorPicked = this._onColorPicked.bind(this);
     this._onColorPickCanceled = this._onColorPickCanceled.bind(this);
     this.destroyEyeDropper = this.destroyEyeDropper.bind(this);
   },
--- a/devtools/server/actors/layout.js
+++ b/devtools/server/actors/layout.js
@@ -31,17 +31,17 @@ const {method, Arg} = protocol;
 const events = require("sdk/event/core");
 const Heritage = require("sdk/core/heritage");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {reflowSpec} = require("devtools/shared/specs/layout");
 
 /**
  * The reflow actor tracks reflows and emits events about them.
  */
-var ReflowActor = exports.ReflowActor = protocol.ActorClassWithSpec(reflowSpec, {
+var ReflowActor = exports.ReflowActor = protocol.ActorClass(reflowSpec, {
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
 
     this.tabActor = tabActor;
     this._onReflow = this._onReflow.bind(this);
     this.observer = getLayoutChangesObserver(tabActor);
     this._isStarted = false;
   },
--- a/devtools/server/actors/memory.js
+++ b/devtools/server/actors/memory.js
@@ -18,17 +18,17 @@ loader.lazyRequireGetter(this, "StackFra
  * parent tab. A global-scoped instance however, will measure the memory
  * footprint of the chrome window referenced by the root actor.
  *
  * This actor wraps the Memory module at devtools/server/performance/memory.js
  * and provides RDP definitions.
  *
  * @see devtools/server/performance/memory.js for documentation.
  */
-exports.MemoryActor = protocol.ActorClassWithSpec(memorySpec, {
+exports.MemoryActor = protocol.ActorClass(memorySpec, {
   initialize: function (conn, parent, frameCache = new StackFrameCache()) {
     protocol.Actor.prototype.initialize.call(this, conn);
 
     this._onGarbageCollection = this._onGarbageCollection.bind(this);
     this._onAllocations = this._onAllocations.bind(this);
     this.bridge = new Memory(parent, frameCache);
     this.bridge.on("garbage-collection", this._onGarbageCollection);
     this.bridge.on("allocations", this._onAllocations);
--- a/devtools/server/actors/performance-entries.js
+++ b/devtools/server/actors/performance-entries.js
@@ -4,60 +4,48 @@
 
 /**
  * The performanceEntries actor emits events corresponding to performance
  * entries. It receives `performanceentry` events containing the performance
  * entry details and emits an event containing the name, type, origin, and
  * epoch of the performance entry.
  */
 
-const {
-  method, Arg, Option, RetVal, Front, FrontClass, Actor, ActorClass
-} = require("devtools/shared/protocol");
+const { Actor, ActorClass } = require("devtools/shared/protocol");
+const performanceSpec = require("devtools/shared/specs/performance-entries");
 const events = require("sdk/event/core");
 
-var PerformanceEntriesActor = exports.PerformanceEntriesActor = ActorClass({
-
-  typeName: "performanceEntries",
-
+var PerformanceEntriesActor = ActorClass(performanceSpec, {
   listenerAdded: false,
 
-  events: {
-    "entry" : {
-      type: "entry",
-      detail: Arg(0, "json") // object containing performance entry name, type,
-                             // origin, and epoch.
-    }
-  },
-
   initialize: function (conn, tabActor) {
     Actor.prototype.initialize.call(this, conn);
     this.window = tabActor.window;
   },
 
   /**
    * Start tracking the user timings.
    */
-  start: method(function () {
+  start: function () {
     if (!this.listenerAdded) {
       this.onPerformanceEntry = this.onPerformanceEntry.bind(this);
       this.window.addEventListener("performanceentry", this.onPerformanceEntry, true);
       this.listenerAdded = true;
     }
-  }),
+  },
 
   /**
    * Stop tracking the user timings.
    */
-  stop: method(function () {
+  stop: function () {
     if (this.listenerAdded) {
       this.window.removeEventListener("performanceentry", this.onPerformanceEntry, true);
       this.listenerAdded = false;
     }
-  }),
+  },
 
   disconnect: function () {
     this.destroy();
   },
 
   destroy: function () {
     this.stop();
     Actor.prototype.destroy.call(this);
@@ -69,15 +57,9 @@ var PerformanceEntriesActor = exports.Pe
       name: e.name,
       origin: e.origin,
       epoch: e.epoch
     };
     events.emit(this, "entry", emitDetail);
   }
 });
 
-exports.PerformanceEntriesFront = FrontClass(PerformanceEntriesActor, {
-  initialize: function (client, form) {
-    Front.prototype.initialize.call(this, client);
-    this.actorID = form.performanceEntriesActor;
-    this.manage(this);
-  },
-});
+exports.PerformanceEntriesActor = PerformanceEntriesActor;
--- a/devtools/server/actors/performance-recording.js
+++ b/devtools/server/actors/performance-recording.js
@@ -1,31 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Cu } = require("chrome");
-const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
+const { Actor, ActorClass } = require("devtools/shared/protocol");
 const { performanceRecordingSpec } = require("devtools/shared/specs/performance-recording");
 
 loader.lazyRequireGetter(this, "merge", "sdk/util/object", true);
 loader.lazyRequireGetter(this, "RecordingUtils",
   "devtools/shared/performance/recording-utils");
 loader.lazyRequireGetter(this, "PerformanceRecordingCommon",
   "devtools/shared/performance/recording-common", true);
 
 /**
  * This actor wraps the Performance module at devtools/shared/shared/performance.js
  * and provides RDP definitions.
  *
  * @see devtools/shared/shared/performance.js for documentation.
  */
-const PerformanceRecordingActor = ActorClassWithSpec(performanceRecordingSpec, merge({
+const PerformanceRecordingActor = ActorClass(performanceRecordingSpec, merge({
   form: function (detail) {
     if (detail === "actorid") {
       return this.actorID;
     }
 
     let form = {
       actor: this.actorID,  // actorID is set when this is added to a pool
       configuration: this._configuration,
--- a/devtools/server/actors/performance.js
+++ b/devtools/server/actors/performance.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/. */
 
 "use strict";
 
 const { Cu } = require("chrome");
 const { Task } = require("devtools/shared/task");
-const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
+const { Actor, ActorClass } = require("devtools/shared/protocol");
 const { actorBridgeWithSpec } = require("devtools/server/actors/common");
 const { performanceSpec } = require("devtools/shared/specs/performance");
 
 loader.lazyRequireGetter(this, "events", "sdk/event/core");
 loader.lazyRequireGetter(this, "extend", "sdk/util/object", true);
 
 loader.lazyRequireGetter(this, "PerformanceRecorder",
   "devtools/server/performance/recorder", true);
@@ -28,17 +28,17 @@ const RECORDING_STATE_CHANGE_EVENTS = ne
 ]);
 
 /**
  * This actor wraps the Performance module at devtools/shared/shared/performance.js
  * and provides RDP definitions.
  *
  * @see devtools/shared/shared/performance.js for documentation.
  */
-var PerformanceActor = ActorClassWithSpec(performanceSpec, {
+var PerformanceActor = ActorClass(performanceSpec, {
   traits: {
     features: {
       withMarkers: true,
       withTicks: true,
       withMemory: true,
       withFrames: true,
       withGCEvents: true,
       withDocLoadingEvents: true,
--- a/devtools/server/actors/preference.js
+++ b/devtools/server/actors/preference.js
@@ -10,17 +10,17 @@ const {preferenceSpec} = require("devtoo
 
 exports.register = function (handle) {
   handle.addGlobalActor(PreferenceActor, "preferenceActor");
 };
 
 exports.unregister = function (handle) {
 };
 
-var PreferenceActor = exports.PreferenceActor = protocol.ActorClassWithSpec(preferenceSpec, {
+var PreferenceActor = exports.PreferenceActor = protocol.ActorClass(preferenceSpec, {
   typeName: "preference",
 
   getBoolPref: function (name) {
     return Services.prefs.getBoolPref(name);
   },
 
   getCharPref: function (name) {
     return Services.prefs.getCharPref(name);
--- a/devtools/server/actors/profiler.js
+++ b/devtools/server/actors/profiler.js
@@ -1,27 +1,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
+const { Actor, ActorClass } = require("devtools/shared/protocol");
 const { Profiler } = require("devtools/server/performance/profiler");
 const { actorBridgeWithSpec } = require("devtools/server/actors/common");
 const { profilerSpec } = require("devtools/shared/specs/profiler");
 
 loader.lazyRequireGetter(this, "events", "sdk/event/core");
 
 /**
  * This actor wraps the Profiler module at devtools/server/performance/profiler.js
  * and provides RDP definitions.
  *
  * @see devtools/server/performance/profiler.js for documentation.
  */
-var ProfilerActor = exports.ProfilerActor = ActorClassWithSpec(profilerSpec, {
+var ProfilerActor = exports.ProfilerActor = ActorClass(profilerSpec, {
   initialize: function (conn) {
     Actor.prototype.initialize.call(this, conn);
     this._onProfilerEvent = this._onProfilerEvent.bind(this);
 
     this.bridge = new Profiler();
     events.on(this.bridge, "*", this._onProfilerEvent);
   },
 
--- a/devtools/server/actors/promises.js
+++ b/devtools/server/actors/promises.js
@@ -10,17 +10,17 @@ const { expectState, ActorPool } = requi
 const { ObjectActor, createValueGrip } = require("devtools/server/actors/object");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 loader.lazyRequireGetter(this, "events", "sdk/event/core");
 
 /**
  * The Promises Actor provides support for getting the list of live promises and
  * observing changes to their settlement state.
  */
-var PromisesActor = protocol.ActorClassWithSpec(promisesSpec, {
+var PromisesActor = protocol.ActorClass(promisesSpec, {
   /**
    * @param conn DebuggerServerConnection.
    * @param parent TabActor|RootActor
    */
   initialize: function (conn, parent) {
     protocol.Actor.prototype.initialize.call(this, conn);
 
     this.conn = conn;
--- a/devtools/server/actors/script.js
+++ b/devtools/server/actors/script.js
@@ -10,17 +10,17 @@ const Services = require("Services");
 const { Cc, Ci, Cu, Cr, components, ChromeWorker } = require("chrome");
 const { ActorPool, OriginalLocation, GeneratedLocation } = require("devtools/server/actors/common");
 const { BreakpointActor, setBreakpointAtEntryPoints } = require("devtools/server/actors/breakpoint");
 const { EnvironmentActor } = require("devtools/server/actors/environment");
 const { FrameActor } = require("devtools/server/actors/frame");
 const { ObjectActor, createValueGrip, longStringGrip } = require("devtools/server/actors/object");
 const { SourceActor, getSourceURL } = require("devtools/server/actors/source");
 const { DebuggerServer } = require("devtools/server/main");
-const { ActorClassWithSpec } = require("devtools/shared/protocol");
+const { ActorClass } = require("devtools/shared/protocol");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const flags = require("devtools/shared/flags");
 const { assert, dumpn, update, fetch } = DevToolsUtils;
 const promise = require("promise");
 const xpcInspector = require("xpcInspector");
 const { DevToolsWorker } = require("devtools/shared/worker/worker");
 const object = require("sdk/util/object");
 const { threadSpec } = require("devtools/shared/specs/script");
@@ -403,17 +403,17 @@ EventLoop.prototype = {
  *          - preNest: Function called before entering a nested event loop.
  *          - postNest: Function called after exiting a nested event loop.
  *          - makeDebugger: A function that takes no arguments and instantiates
  *            a Debugger that manages its globals on its own.
  * @param aGlobal object [optional]
  *        An optional (for content debugging only) reference to the content
  *        window.
  */
-const ThreadActor = ActorClassWithSpec(threadSpec, {
+const ThreadActor = ActorClass(threadSpec, {
   initialize: function (aParent, aGlobal) {
     this._state = "detached";
     this._frameActors = [];
     this._parent = aParent;
     this._dbg = null;
     this._gripDepth = 0;
     this._threadLifetimePool = null;
     this._tabClosed = false;
--- a/devtools/server/actors/settings.js
+++ b/devtools/server/actors/settings.js
@@ -59,17 +59,17 @@ function loadSettingsFile() {
     }
   }
 
   if (settingsFile.exists()) {
     getDefaultSettings();
   }
 }
 
-var SettingsActor = exports.SettingsActor = protocol.ActorClassWithSpec(settingsSpec, {
+var SettingsActor = exports.SettingsActor = protocol.ActorClass(settingsSpec, {
   _getSettingsService: function () {
     let win = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
     return win.navigator.mozSettings;
   },
 
   getSetting: function (name) {
     let deferred = promise.defer();
     let lock = this._getSettingsService().createLock();
--- a/devtools/server/actors/source.js
+++ b/devtools/server/actors/source.js
@@ -6,17 +6,17 @@
 
 "use strict";
 
 const { Cc, Ci } = require("chrome");
 const Services = require("Services");
 const { BreakpointActor, setBreakpointAtEntryPoints } = require("devtools/server/actors/breakpoint");
 const { OriginalLocation, GeneratedLocation } = require("devtools/server/actors/common");
 const { createValueGrip } = require("devtools/server/actors/object");
-const { ActorClassWithSpec, Arg, RetVal, method } = require("devtools/shared/protocol");
+const { ActorClass, Arg, RetVal, method } = require("devtools/shared/protocol");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { assert, fetch } = DevToolsUtils;
 const { joinURI } = require("devtools/shared/path");
 const promise = require("promise");
 const { defer, resolve, reject, all } = promise;
 const { sourceSpec } = require("devtools/shared/specs/source");
 
 loader.lazyRequireGetter(this, "SourceMapConsumer", "source-map", true);
@@ -136,17 +136,17 @@ function resolveURIToLocalPath(aURI) {
  * @param Debugger.Source generatedSource
  *        Optional, passed in when aSourceMap is also passed in. The generated
  *        source object that introduced this source.
  * @param Boolean isInlineSource
  *        Optional. True if this is an inline source from a HTML or XUL page.
  * @param String contentType
  *        Optional. The content type of this source, if immediately available.
  */
-let SourceActor = ActorClassWithSpec(sourceSpec, {
+let SourceActor = ActorClass(sourceSpec, {
   typeName: "source",
 
   initialize: function ({ source, thread, originalUrl, generatedSource,
                           isInlineSource, contentType }) {
     this._threadActor = thread;
     this._originalUrl = originalUrl;
     this._source = source;
     this._generatedSource = generatedSource;
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -374,17 +374,17 @@ StorageActors.createActor = function (op
     options.typeName,
     options.observationTopic || null
   );
   for (let key in overrides) {
     actorObject[key] = overrides[key];
   }
 
   let actorSpec = specs.childSpecs[options.typeName];
-  let actor = protocol.ActorClassWithSpec(actorSpec, actorObject);
+  let actor = protocol.ActorClass(actorSpec, actorObject);
   storageTypePool.set(actorObject.typeName, actor);
 };
 
 /**
  * The Cookies actor and front.
  */
 StorageActors.createActor({
   typeName: "cookies"
@@ -2164,17 +2164,17 @@ exports.setupParentProcessForIndexedDB =
     mm.removeMessageListener("storage:storage-indexedDB-request-parent",
                              indexedDBHelpers.handleChildRequest);
   }
 };
 
 /**
  * The main Storage Actor.
  */
-let StorageActor = protocol.ActorClassWithSpec(specs.storageSpec, {
+let StorageActor = protocol.ActorClass(specs.storageSpec, {
   typeName: "storage",
 
   get window() {
     return this.parentActor.window;
   },
 
   get document() {
     return this.parentActor.window.document;
--- a/devtools/server/actors/string.js
+++ b/devtools/server/actors/string.js
@@ -6,17 +6,17 @@
 
 var {DebuggerServer} = require("devtools/server/main");
 
 var promise = require("promise");
 
 var protocol = require("devtools/shared/protocol");
 const {longStringSpec} = require("devtools/shared/specs/string");
 
-exports.LongStringActor = protocol.ActorClassWithSpec(longStringSpec, {
+exports.LongStringActor = protocol.ActorClass(longStringSpec, {
   initialize: function (conn, str) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.str = str;
     this.short = (this.str.length < DebuggerServer.LONG_STRING_LENGTH);
   },
 
   destroy: function () {
     this.str = null;
--- a/devtools/server/actors/styleeditor.js
+++ b/devtools/server/actors/styleeditor.js
@@ -23,17 +23,17 @@ var TRANSITION_RULE = "\
 transition-duration: " + TRANSITION_DURATION_MS + "ms !important; \
 transition-delay: 0ms !important;\
 transition-timing-function: ease-out !important;\
 transition-property: all !important;\
 }";
 
 var LOAD_ERROR = "error-load";
 
-var OldStyleSheetActor = protocol.ActorClassWithSpec(oldStyleSheetSpec, {
+var OldStyleSheetActor = protocol.ActorClass(oldStyleSheetSpec, {
   toString: function() {
     return "[OldStyleSheetActor " + this.actorID + "]";
   },
 
   /**
    * Window of target
    */
   get window() {
@@ -316,17 +316,17 @@ var OldStyleSheetActor = protocol.ActorC
 });
 
 exports.OldStyleSheetActor = OldStyleSheetActor;
 
 /**
  * Creates a StyleEditorActor. StyleEditorActor provides remote access to the
  * stylesheets of a document.
  */
-var StyleEditorActor = exports.StyleEditorActor = protocol.ActorClassWithSpec(styleEditorSpec, {
+var StyleEditorActor = exports.StyleEditorActor = protocol.ActorClass(styleEditorSpec, {
   /**
    * The window we work with, taken from the parent actor.
    */
   get window() {
     return this.parentActor.window;
   },
 
   /**
--- a/devtools/server/actors/styles.js
+++ b/devtools/server/actors/styles.js
@@ -34,17 +34,17 @@ const NORMAL_FONT_WEIGHT = 400;
 const BOLD_FONT_WEIGHT = 700;
 // Offset (in px) to avoid cutting off text edges of italic fonts.
 const FONT_PREVIEW_OFFSET = 4;
 
 /**
  * The PageStyle actor lets the client look at the styles on a page, as
  * they are applied to a given node.
  */
-var PageStyleActor = protocol.ActorClassWithSpec(pageStyleSpec, {
+var PageStyleActor = protocol.ActorClass(pageStyleSpec, {
   /**
    * Create a PageStyleActor.
    *
    * @param inspector
    *    The InspectorActor that owns this PageStyleActor.
    *
    * @constructor
    */
@@ -920,17 +920,17 @@ exports.PageStyleActor = PageStyleActor;
 /**
  * An actor that represents a CSS style object on the protocol.
  *
  * We slightly flatten the CSSOM for this actor, it represents
  * both the CSSRule and CSSStyle objects in one actor.  For nodes
  * (which have a CSSStyle but no CSSRule) we create a StyleRuleActor
  * with a special rule type (100).
  */
-var StyleRuleActor = protocol.ActorClassWithSpec(styleRuleSpec, {
+var StyleRuleActor = protocol.ActorClass(styleRuleSpec, {
   initialize: function (pageStyle, item) {
     protocol.Actor.prototype.initialize.call(this, null);
     this.pageStyle = pageStyle;
     this.rawStyle = item.style;
     this._parentSheet = null;
     this._onStyleApplied = this._onStyleApplied.bind(this);
 
     if (item instanceof (Ci.nsIDOMCSSRule)) {
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -55,17 +55,17 @@ exports.UPDATE_GENERAL = UPDATE_GENERAL;
 // is used so that navigation by the user will eventually cause the
 // edited text to be collected.
 let modifiedStyleSheets = new WeakMap();
 
 /**
  * Actor representing an original source of a style sheet that was specified
  * in a source map.
  */
-var OriginalSourceActor = protocol.ActorClassWithSpec(originalSourceSpec, {
+var OriginalSourceActor = protocol.ActorClass(originalSourceSpec, {
   initialize: function (aUrl, aSourceMap, aParentActor) {
     protocol.Actor.prototype.initialize.call(this, null);
 
     this.url = aUrl;
     this.sourceMap = aSourceMap;
     this.parentActor = aParentActor;
     this.conn = this.parentActor.conn;
 
@@ -108,17 +108,17 @@ var OriginalSourceActor = protocol.Actor
     });
   }
 });
 
 /**
  * A MediaRuleActor lives on the server and provides access to properties
  * of a DOM @media rule and emits events when it changes.
  */
-var MediaRuleActor = protocol.ActorClassWithSpec(mediaRuleSpec, {
+var MediaRuleActor = protocol.ActorClass(mediaRuleSpec, {
   get window() {
     return this.parentActor.window;
   },
 
   get document() {
     return this.window.document;
   },
 
@@ -178,17 +178,17 @@ var MediaRuleActor = protocol.ActorClass
   _matchesChange: function () {
     events.emit(this, "matches-change", this.matches);
   }
 });
 
 /**
  * A StyleSheetActor represents a stylesheet on the server.
  */
-var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
+var StyleSheetActor = protocol.ActorClass(styleSheetSpec, {
   /* List of original sources that generated this stylesheet */
   _originalSources: null,
 
   toString: function () {
     return "[StyleSheetActor " + this.actorID + "]";
   },
 
   /**
@@ -781,17 +781,17 @@ var StyleSheetActor = protocol.ActorClas
 });
 
 exports.StyleSheetActor = StyleSheetActor;
 
 /**
  * Creates a StyleSheetsActor. StyleSheetsActor provides remote access to the
  * stylesheets of a document.
  */
-var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
+var StyleSheetsActor = protocol.ActorClass(styleSheetsSpec, {
   /**
    * The window we work with, taken from the parent actor.
    */
   get window() {
     return this.parentActor.window;
   },
 
   /**
--- a/devtools/server/actors/timeline.js
+++ b/devtools/server/actors/timeline.js
@@ -21,17 +21,17 @@ const { Option, RetVal } = protocol;
 const { actorBridgeWithSpec } = require("devtools/server/actors/common");
 const { Timeline } = require("devtools/server/performance/timeline");
 const { timelineSpec } = require("devtools/shared/specs/timeline");
 const events = require("sdk/event/core");
 
 /**
  * The timeline actor pops and forwards timeline markers registered in docshells.
  */
-var TimelineActor = exports.TimelineActor = protocol.ActorClassWithSpec(timelineSpec, {
+var TimelineActor = exports.TimelineActor = protocol.ActorClass(timelineSpec, {
   /**
    * Initializes this actor with the provided connection and tab actor.
    */
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
     this.bridge = new Timeline(tabActor);
 
--- a/devtools/server/actors/webaudio.js
+++ b/devtools/server/actors/webaudio.js
@@ -33,17 +33,17 @@ const AUTOMATION_GRANULARITY_MAX = 6000;
 const AUDIO_GLOBALS = [
   "AudioContext", "AudioNode", "AudioParam"
 ];
 
 /**
  * An Audio Node actor allowing communication to a specific audio node in the
  * Audio Context graph.
  */
-var AudioNodeActor = exports.AudioNodeActor = protocol.ActorClassWithSpec(audionodeSpec, {
+var AudioNodeActor = exports.AudioNodeActor = protocol.ActorClass(audionodeSpec, {
   form: function (detail) {
     if (detail === "actorid") {
       return this.actorID;
     }
 
     return {
       actor: this.actorID, // actorID is set when this is added to a pool
       type: this.type,
@@ -399,17 +399,17 @@ var AudioNodeActor = exports.AudioNodeAc
   }
 });
 
 /**
  * The Web Audio Actor handles simple interaction with an AudioContext
  * high-level methods. After instantiating this actor, you'll need to set it
  * up by calling setup().
  */
-var WebAudioActor = exports.WebAudioActor = protocol.ActorClassWithSpec(webAudioSpec, {
+var WebAudioActor = exports.WebAudioActor = protocol.ActorClass(webAudioSpec, {
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
 
     this._onContentFunctionCall = this._onContentFunctionCall.bind(this);
 
     // Store ChromeOnly ID (`nativeID` property on AudioNodeActor) mapped
     // to the associated actorID, so we don't have to expose `nativeID`
--- a/devtools/server/actors/webgl.js
+++ b/devtools/server/actors/webgl.js
@@ -23,17 +23,17 @@ const PROGRAM_DEFAULT_TRAITS = 0;
 const PROGRAM_BLACKBOX_TRAIT = 1;
 const PROGRAM_HIGHLIGHT_TRAIT = 2;
 
 /**
  * A WebGL Shader contributing to building a WebGL Program.
  * You can either retrieve, or compile the source of a shader, which will
  * automatically inflict the necessary changes to the WebGL state.
  */
-var ShaderActor = protocol.ActorClassWithSpec(shaderSpec, {
+var ShaderActor = protocol.ActorClass(shaderSpec, {
   /**
    * Create the shader actor.
    *
    * @param DebuggerServerConnection conn
    *        The server connection.
    * @param WebGLProgram program
    *        The WebGL program being linked.
    * @param WebGLShader shader
@@ -78,17 +78,17 @@ var ShaderActor = protocol.ActorClassWit
     return undefined;
   }
 });
 
 /**
  * A WebGL program is composed (at the moment, analogue to OpenGL ES 2.0)
  * of two shaders: a vertex shader and a fragment shader.
  */
-var ProgramActor = protocol.ActorClassWithSpec(programSpec, {
+var ProgramActor = protocol.ActorClass(programSpec, {
   /**
    * Create the program actor.
    *
    * @param DebuggerServerConnection conn
    *        The server connection.
    * @param WebGLProgram program
    *        The WebGL program being linked.
    * @param WebGLShader[] shaders
@@ -179,17 +179,17 @@ var ProgramActor = protocol.ActorClassWi
   }
 });
 
 /**
  * The WebGL Actor handles simple interaction with a WebGL context via a few
  * high-level methods. After instantiating this actor, you'll need to set it
  * up by calling setup().
  */
-var WebGLActor = exports.WebGLActor = protocol.ActorClassWithSpec(webGLSpec, {
+var WebGLActor = exports.WebGLActor = protocol.ActorClass(webGLSpec, {
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
     this._onGlobalCreated = this._onGlobalCreated.bind(this);
     this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this);
     this._onProgramLinked = this._onProgramLinked.bind(this);
   },
   destroy: function (conn) {
--- a/devtools/server/actors/worker.js
+++ b/devtools/server/actors/worker.js
@@ -50,17 +50,17 @@ function matchWorkerDebugger(dbg, option
     if (window !== options.window) {
       return false;
     }
   }
 
   return true;
 }
 
-let WorkerActor = protocol.ActorClassWithSpec(workerSpec, {
+let WorkerActor = protocol.ActorClass(workerSpec, {
   initialize(conn, dbg) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this._dbg = dbg;
     this._attached = false;
     this._threadActor = null;
     this._transport = null;
   },
 
@@ -308,17 +308,17 @@ WorkerActorList.prototype = {
     if (matchWorkerDebugger(dbg, this._options)) {
       this._notifyListChanged();
     }
   }
 };
 
 exports.WorkerActorList = WorkerActorList;
 
-let PushSubscriptionActor = protocol.ActorClassWithSpec(pushSubscriptionSpec, {
+let PushSubscriptionActor = protocol.ActorClass(pushSubscriptionSpec, {
   initialize(conn, subscription) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this._subscription = subscription;
   },
 
   form(detail) {
     if (detail === "actorid") {
       return this.actorID;
@@ -338,17 +338,17 @@ let PushSubscriptionActor = protocol.Act
     this._subscription = null;
   },
 });
 
 // Lazily load the service-worker-child.js process script only once.
 let _serviceWorkerProcessScriptLoaded = false;
 
 let ServiceWorkerRegistrationActor =
-protocol.ActorClassWithSpec(serviceWorkerRegistrationSpec, {
+protocol.ActorClass(serviceWorkerRegistrationSpec, {
   /**
    * Create the ServiceWorkerRegistrationActor
    * @param DebuggerServerConnection conn
    *   The server connection.
    * @param ServiceWorkerRegistrationInfo registration
    *   The registration's information.
    */
   initialize(conn, registration) {
--- a/devtools/server/tests/mochitest/hello-actor.js
+++ b/devtools/server/tests/mochitest/hello-actor.js
@@ -9,17 +9,17 @@ const helloSpec = protocol.generateActor
   methods: {
     count: {
       request: {},
       response: {count: protocol.RetVal("number")}
     }
   }
 });
 
-var HelloActor = protocol.ActorClassWithSpec(helloSpec, {
+var HelloActor = protocol.ActorClass(helloSpec, {
   initialize: function () {
     protocol.Actor.prototype.initialize.apply(this, arguments);
     this.counter = 0;
   },
 
   count: function () {
     return ++this.counter;
   }
--- a/devtools/server/tests/unit/hello-actor.js
+++ b/devtools/server/tests/unit/hello-actor.js
@@ -6,13 +6,13 @@ const protocol = require("devtools/share
 const helloSpec = protocol.generateActorSpec({
   typeName: "helloActor",
 
   methods: {
     hello: {}
   }
 });
 
-var HelloActor = protocol.ActorClassWithSpec(helloSpec, {
+var HelloActor = protocol.ActorClass(helloSpec, {
   hello: function () {
     return;
   }
 });
--- a/devtools/server/tests/unit/registertestactors-03.js
+++ b/devtools/server/tests/unit/registertestactors-03.js
@@ -1,40 +1,40 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-var {method, RetVal, Actor, ActorClassWithSpec, Front, FrontClassWithSpec,
+var {method, RetVal, Actor, ActorClass, Front, FrontClass,
      generateActorSpec} = require("devtools/shared/protocol");
 var Services = require("Services");
 
 const lazySpec = generateActorSpec({
   typeName: "lazy",
 
   methods: {
     hello: {
       response: { str: RetVal("string") }
     }
   }
 });
 
-exports.LazyActor = ActorClassWithSpec(lazySpec, {
+exports.LazyActor = ActorClass(lazySpec, {
   initialize: function (conn, id) {
     Actor.prototype.initialize.call(this, conn);
 
     Services.obs.notifyObservers(null, "actor", "instantiated");
   },
 
   hello: function (str) {
     return "world";
   }
 });
 
 Services.obs.notifyObservers(null, "actor", "loaded");
 
-exports.LazyFront = FrontClassWithSpec(lazySpec, {
+exports.LazyFront = FrontClass(lazySpec, {
   initialize: function (client, form) {
     Front.prototype.initialize.call(this, client);
     this.actorID = form.lazyActor;
 
     client.addActorPool(this);
     this.manage(this);
   }
 });
--- a/devtools/server/tests/unit/test_protocol_abort.js
+++ b/devtools/server/tests/unit/test_protocol_abort.js
@@ -23,34 +23,34 @@ const rootSpec = protocol.generateActorS
 
   methods: {
     simpleReturn: {
       response: { value: RetVal() }
     }
   }
 });
 
-var RootActor = protocol.ActorClassWithSpec(rootSpec, {
+var RootActor = protocol.ActorClass(rootSpec, {
   typeName: "root",
   initialize: function (conn) {
     protocol.Actor.prototype.initialize.call(this, conn);
     // Root actor owns itself.
     this.manage(this);
     this.actorID = "root";
     this.sequence = 0;
   },
 
   sayHello: simpleHello,
 
   simpleReturn: function () {
     return this.sequence++;
   }
 });
 
-var RootFront = protocol.FrontClassWithSpec(rootSpec, {
+var RootFront = protocol.FrontClass(rootSpec, {
   initialize: function (client) {
     this.actorID = "root";
     protocol.Front.prototype.initialize.call(this, client);
     // Root owns itself.
     this.manage(this);
   }
 });
 
--- a/devtools/server/tests/unit/test_protocol_async.js
+++ b/devtools/server/tests/unit/test_protocol_async.js
@@ -34,17 +34,17 @@ const rootSpec = protocol.generateActorS
       response: { value: RetVal("number") }
     },
     promiseThrow: {
       response: { value: RetVal("number") },
     }
   }
 });
 
-var RootActor = protocol.ActorClassWithSpec(rootSpec, {
+var RootActor = protocol.ActorClass(rootSpec, {
   initialize: function (conn) {
     protocol.Actor.prototype.initialize.call(this, conn);
     // Root actor owns itself.
     this.manage(this);
     this.actorID = "root";
     this.sequence = 0;
   },
 
@@ -84,17 +84,17 @@ var RootActor = protocol.ActorClassWithS
     // This should be enough to force a failure if the code is broken.
     do_timeout(150, () => {
       deferred.reject(sequence++);
     });
     return deferred.promise;
   }
 });
 
-var RootFront = protocol.FrontClassWithSpec(rootSpec, {
+var RootFront = protocol.FrontClass(rootSpec, {
   initialize: function (client) {
     this.actorID = "root";
     protocol.Front.prototype.initialize.call(this, client);
     // Root owns itself.
     this.manage(this);
   }
 });
 
--- a/devtools/server/tests/unit/test_protocol_children.js
+++ b/devtools/server/tests/unit/test_protocol_children.js
@@ -83,17 +83,17 @@ const childSpec = protocol.generateActor
       response: { value: "correct response" },
     },
     release: {
       release: true
     }
   }
 });
 
-var ChildActor = protocol.ActorClassWithSpec(childSpec, {
+var ChildActor = protocol.ActorClass(childSpec, {
   // Actors returned by this actor should be owned by the root actor.
   marshallPool: function () { return this.parent(); },
 
   toString: function () { return "[ChildActor " + this.childID + "]"; },
 
   initialize: function (conn, id) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.childID = id;
@@ -151,17 +151,17 @@ var ChildActor = protocol.ActorClassWith
     events.emit(this, "named-event", 1, 2, 3);
     events.emit(this, "object-event", this);
     events.emit(this, "array-object-event", [this]);
   },
 
   release: function () { },
 });
 
-var ChildFront = protocol.FrontClassWithSpec(childSpec, {
+var ChildFront = protocol.FrontClass(childSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client, form);
   },
 
   destroy: function () {
     this.destroyed = true;
     protocol.Front.prototype.destroy.call(this);
   },
@@ -221,17 +221,17 @@ const rootSpec = protocol.generateActorS
       request: { id: Arg(0) },
       response: { child: RetVal("temp:childActor") }
     },
     clearTemporaryChildren: {}
   }
 });
 
 var rootActor = null;
-var RootActor = protocol.ActorClassWithSpec(rootSpec, {
+var RootActor = protocol.ActorClass(rootSpec, {
   toString: function () { return "[root actor]"; },
 
   initialize: function (conn) {
     rootActor = this;
     this.actorID = "root";
     this._children = {};
     protocol.Actor.prototype.initialize.call(this, conn);
     // Root actor owns itself.
@@ -282,17 +282,17 @@ var RootActor = protocol.ActorClassWithS
     if (!this._temporaryHolder) {
       return;
     }
     this._temporaryHolder.destroy();
     delete this._temporaryHolder;
   }
 });
 
-var RootFront = protocol.FrontClassWithSpec(rootSpec, {
+var RootFront = protocol.FrontClass(rootSpec, {
   toString: function () { return "[root front]"; },
   initialize: function (client) {
     this.actorID = "root";
     protocol.Front.prototype.initialize.call(this, client);
     // Root actor owns itself.
     this.manage(this);
   },
 
--- a/devtools/server/tests/unit/test_protocol_formtype.js
+++ b/devtools/server/tests/unit/test_protocol_formtype.js
@@ -10,34 +10,34 @@ const childSpec = protocol.generateActor
   methods: {
     getChild: {
       response: RetVal("child")
     }
   }
 });
 
 // The child actor doesn't provide a form description
-var ChildActor = protocol.ActorClassWithSpec(childSpec, {
+var ChildActor = protocol.ActorClass(childSpec, {
   initialize(conn) {
     protocol.Actor.prototype.initialize.call(this, conn);
   },
 
   form(detail) {
     return {
       actor: this.actorID,
       extra: "extra"
     };
   },
 
   getChild: function () {
     return this;
   }
 });
 
-var ChildFront = protocol.FrontClassWithSpec(childSpec, {
+var ChildFront = protocol.FrontClass(childSpec, {
   initialize(client) {
     protocol.Front.prototype.initialize.call(this, client);
   },
 
   form(v, ctx, detail) {
     this.extra = v.extra;
   }
 });
@@ -72,17 +72,17 @@ const rootSpec = protocol.generateActorS
     },
     getUnknownDetail: {
       response: RetVal("root#unknownDetail")
     }
   }
 });
 
 // The root actor does provide a form description.
-var RootActor = protocol.ActorClassWithSpec(rootSpec, {
+var RootActor = protocol.ActorClass(rootSpec, {
   initialize(conn) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.manage(this);
     this.child = new ChildActor();
   },
 
   sayHello() {
     return {
@@ -120,17 +120,17 @@ var RootActor = protocol.ActorClassWithS
     return this;
   },
 
   getUnknownDetail: function () {
     return this;
   }
 });
 
-var RootFront = protocol.FrontClassWithSpec(rootSpec, {
+var RootFront = protocol.FrontClass(rootSpec, {
   initialize(client) {
     this.actorID = "root";
     protocol.Front.prototype.initialize.call(this, client);
 
     // Root owns itself.
     this.manage(this);
   },
 
--- a/devtools/server/tests/unit/test_protocol_longstring.js
+++ b/devtools/server/tests/unit/test_protocol_longstring.js
@@ -44,17 +44,17 @@ const rootSpec = protocol.generateActorS
       oneway: true,
     },
     emitLongString: {
       oneway: true,
     }
   }
 });
 
-var RootActor = protocol.ActorClassWithSpec(rootSpec, {
+var RootActor = protocol.ActorClass(rootSpec, {
   initialize: function (conn) {
     rootActor = this;
     protocol.Actor.prototype.initialize.call(this, conn);
     // Root actor owns itself.
     this.manage(this);
     this.actorID = "root";
   },
 
@@ -72,17 +72,17 @@ var RootActor = protocol.ActorClassWithS
     events.emit(this, "string-event", new LongStringActor(this.conn, SHORT_STR));
   },
 
   emitLongString: function () {
     events.emit(this, "string-event", new LongStringActor(this.conn, LONG_STR));
   },
 });
 
-var RootFront = protocol.FrontClassWithSpec(rootSpec, {
+var RootFront = protocol.FrontClass(rootSpec, {
   initialize: function (client) {
     this.actorID = "root";
     protocol.Front.prototype.initialize.call(this, client);
     // Root owns itself.
     this.manage(this);
   }
 });
 
--- a/devtools/server/tests/unit/test_protocol_simple.js
+++ b/devtools/server/tests/unit/test_protocol_simple.js
@@ -96,17 +96,17 @@ const rootSpec = protocol.generateActorS
       oneway: true
     },
     emitFalsyOptions: {
       oneway: true
     }
   }
 });
 
-var RootActor = protocol.ActorClassWithSpec(rootSpec, {
+var RootActor = protocol.ActorClass(rootSpec, {
   initialize: function (conn) {
     protocol.Actor.prototype.initialize.call(this, conn);
     // Root actor owns itself.
     this.manage(this);
     this.actorID = "root";
   },
 
   sayHello: simpleHello,
@@ -159,34 +159,34 @@ var RootActor = protocol.ActorClassWithS
     events.emit(this, "oneway", a);
   },
 
   emitFalsyOptions: function () {
     events.emit(this, "falsyOptions", { zero: 0, farce: false });
   }
 });
 
-var RootFront = protocol.FrontClassWithSpec(rootSpec, {
+var RootFront = protocol.FrontClass(rootSpec, {
   initialize: function (client) {
     this.actorID = "root";
     protocol.Front.prototype.initialize.call(this, client);
     // Root owns itself.
     this.manage(this);
   }
 });
 
 function run_test()
 {
   DebuggerServer.createRootActor = (conn => {
     return RootActor(conn);
   });
   DebuggerServer.init();
 
   check_except(() => {
-    let badActor = ActorClassWithSpec({}, {
+    let badActor = ActorClass({}, {
       missing: preEvent("missing-event", function () {
       })
     });
   });
 
   protocol.types.getType("array:array:array:number");
   protocol.types.getType("array:array:array:number");
 
--- a/devtools/server/tests/unit/test_protocol_stack.js
+++ b/devtools/server/tests/unit/test_protocol_stack.js
@@ -27,33 +27,33 @@ const rootSpec = protocol.generateActorS
 
   methods: {
     simpleReturn: {
       response: { value: RetVal() },
     }
   }
 });
 
-var RootActor = protocol.ActorClassWithSpec(rootSpec, {
+var RootActor = protocol.ActorClass(rootSpec, {
   initialize: function (conn) {
     protocol.Actor.prototype.initialize.call(this, conn);
     // Root actor owns itself.
     this.manage(this);
     this.actorID = "root";
     this.sequence = 0;
   },
 
   sayHello: simpleHello,
 
   simpleReturn: function () {
     return this.sequence++;
   }
 });
 
-var RootFront = protocol.FrontClassWithSpec(rootSpec, {
+var RootFront = protocol.FrontClass(rootSpec, {
   initialize: function (client) {
     this.actorID = "root";
     protocol.Front.prototype.initialize.call(this, client);
     // Root owns itself.
     this.manage(this);
   }
 });
 
--- a/devtools/shared/fronts/actor-registry.js
+++ b/devtools/shared/fronts/actor-registry.js
@@ -6,17 +6,17 @@
 const { components } = require("chrome");
 const Services = require("Services");
 const { actorActorSpec, actorRegistrySpec } = require("devtools/shared/specs/actor-registry");
 const protocol = require("devtools/shared/protocol");
 const { custom } = protocol;
 
 loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
 
-const ActorActorFront = protocol.FrontClassWithSpec(actorActorSpec, {
+const ActorActorFront = protocol.FrontClass(actorActorSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client, form);
   }
 });
 
 exports.ActorActorFront = ActorActorFront;
 
 function request(uri) {
@@ -41,17 +41,17 @@ function request(uri) {
 
       let source = NetUtil.readInputStreamToString(stream, stream.available());
       stream.close();
       resolve(source);
     });
   });
 }
 
-const ActorRegistryFront = protocol.FrontClassWithSpec(actorRegistrySpec, {
+const ActorRegistryFront = protocol.FrontClass(actorRegistrySpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client,
                                              { actor: form.actorRegistryActor });
 
     this.manage(this);
   },
 
   registerActor: custom(function (uri, options) {
--- a/devtools/shared/fronts/addons.js
+++ b/devtools/shared/fronts/addons.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/. */
 "use strict";
 
 const {addonsSpec} = require("devtools/shared/specs/addons");
 const protocol = require("devtools/shared/protocol");
 
-const AddonsFront = protocol.FrontClassWithSpec(addonsSpec, {
+const AddonsFront = protocol.FrontClass(addonsSpec, {
   initialize: function (client, {addonsActor}) {
     protocol.Front.prototype.initialize.call(this, client);
     this.actorID = addonsActor;
     this.manage(this);
   }
 });
 
 exports.AddonsFront = AddonsFront;
--- a/devtools/shared/fronts/animation.js
+++ b/devtools/shared/fronts/animation.js
@@ -1,26 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {
   Front,
-  FrontClassWithSpec,
+  FrontClass,
   custom,
   preEvent
 } = require("devtools/shared/protocol");
 const {
   animationPlayerSpec,
   animationsSpec
 } = require("devtools/shared/specs/animation");
 const { Task } = require("devtools/shared/task");
 
-const AnimationPlayerFront = FrontClassWithSpec(animationPlayerSpec, {
+const AnimationPlayerFront = FrontClass(animationPlayerSpec, {
   initialize: function (conn, form, detail, ctx) {
     Front.prototype.initialize.call(this, conn, form, detail, ctx);
 
     this.state = {};
   },
 
   form: function (form, detail) {
     if (detail === "actorid") {
@@ -118,17 +118,17 @@ const AnimationPlayerFront = FrontClassW
     }
 
     return {state: data, hasChanged};
   }
 });
 
 exports.AnimationPlayerFront = AnimationPlayerFront;
 
-const AnimationsFront = FrontClassWithSpec(animationsSpec, {
+const AnimationsFront = FrontClass(animationsSpec, {
   initialize: function (client, {animationsActor}) {
     Front.prototype.initialize.call(this, client, {actor: animationsActor});
     this.manage(this);
   },
 
   destroy: function () {
     Front.prototype.destroy.call(this);
   }
--- a/devtools/shared/fronts/call-watcher.js
+++ b/devtools/shared/fronts/call-watcher.js
@@ -4,17 +4,17 @@
 "use strict";
 
 const { functionCallSpec, callWatcherSpec } = require("devtools/shared/specs/call-watcher");
 const protocol = require("devtools/shared/protocol");
 
 /**
  * The corresponding Front object for the FunctionCallActor.
  */
-const FunctionCallFront = protocol.FrontClassWithSpec(functionCallSpec, {
+const FunctionCallFront = protocol.FrontClass(functionCallSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client, form);
   },
 
   /**
    * Adds some generic information directly to this instance,
    * to avoid extra roundtrips.
    */
@@ -33,17 +33,17 @@ const FunctionCallFront = protocol.Front
 
 exports.FunctionCallFront = FunctionCallFront;
 
 /**
  * The corresponding Front object for the CallWatcherActor.
  */
 var CallWatcherFront =
 exports.CallWatcherFront =
-protocol.FrontClassWithSpec(callWatcherSpec, {
+protocol.FrontClass(callWatcherSpec, {
   initialize: function (client, { callWatcherActor }) {
     protocol.Front.prototype.initialize.call(this, client, { actor: callWatcherActor });
     this.manage(this);
   }
 });
 
 /**
  * Constants.
--- a/devtools/shared/fronts/canvas.js
+++ b/devtools/shared/fronts/canvas.js
@@ -13,17 +13,17 @@ const {
   INTERESTING_CALLS,
 } = require("devtools/shared/specs/canvas");
 const protocol = require("devtools/shared/protocol");
 const promise = require("promise");
 
 /**
  * The corresponding Front object for the FrameSnapshotActor.
  */
-const FrameSnapshotFront = protocol.FrontClassWithSpec(frameSnapshotSpec, {
+const FrameSnapshotFront = protocol.FrontClass(frameSnapshotSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client, form);
     this._animationFrameEndScreenshot = null;
     this._cachedScreenshots = new WeakMap();
   },
 
   /**
    * This implementation caches the animation frame end screenshot to optimize
@@ -59,17 +59,17 @@ const FrameSnapshotFront = protocol.Fron
   })
 });
 
 exports.FrameSnapshotFront = FrameSnapshotFront;
 
 /**
  * The corresponding Front object for the CanvasActor.
  */
-const CanvasFront = protocol.FrontClassWithSpec(canvasSpec, {
+const CanvasFront = protocol.FrontClass(canvasSpec, {
   initialize: function (client, { canvasActor }) {
     protocol.Front.prototype.initialize.call(this, client, { actor: canvasActor });
     this.manage(this);
   }
 });
 
 /**
  * Constants.
--- a/devtools/shared/fronts/css-properties.js
+++ b/devtools/shared/fronts/css-properties.js
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { FrontClassWithSpec, Front } = require("devtools/shared/protocol");
+const { FrontClass, Front } = require("devtools/shared/protocol");
 const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
 const { Task } = require("devtools/shared/task");
 const { CSS_PROPERTIES_DB } = require("devtools/shared/css-properties-db");
 const { cssColors } = require("devtools/shared/css-color-db");
 
 /**
  * Build up a regular expression that matches a CSS variable token. This is an
  * ident token that starts with two dashes "--".
@@ -34,17 +34,17 @@ function isCssVariable(input) {
 var cachedCssProperties = new WeakMap();
 
 /**
  * The CssProperties front provides a mechanism to have a one-time asynchronous
  * load of a CSS properties database. This is then fed into the CssProperties
  * interface that provides synchronous methods for finding out what CSS
  * properties the current server supports.
  */
-const CssPropertiesFront = FrontClassWithSpec(cssPropertiesSpec, {
+const CssPropertiesFront = FrontClass(cssPropertiesSpec, {
   initialize: function (client, { cssPropertiesActor }) {
     Front.prototype.initialize.call(this, client, {actor: cssPropertiesActor});
     this.manage(this);
   }
 });
 
 /**
  * Ask questions to a CSS database. This class does not care how the database
--- a/devtools/shared/fronts/csscoverage.js
+++ b/devtools/shared/fronts/csscoverage.js
@@ -31,17 +31,17 @@ const l10n = exports.l10n = {
 var isRunning = false;
 var notification;
 var target;
 var chromeWindow;
 
 /**
  * Front for CSSUsageActor
  */
-const CSSUsageFront = protocol.FrontClassWithSpec(cssUsageSpec, {
+const CSSUsageFront = protocol.FrontClass(cssUsageSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client, form);
     this.actorID = form.cssUsageActor;
     this.manage(this);
   },
 
   _onStateChange: protocol.preEvent("state-change", function (ev) {
     isRunning = ev.isRunning;
--- a/devtools/shared/fronts/device.js
+++ b/devtools/shared/fronts/device.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 const {deviceSpec} = require("devtools/shared/specs/device");
 const protocol = require("devtools/shared/protocol");
 const defer = require("devtools/shared/defer");
 
-const DeviceFront = protocol.FrontClassWithSpec(deviceSpec, {
+const DeviceFront = protocol.FrontClass(deviceSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client);
     this.actorID = form.deviceActor;
     this.manage(this);
   },
 
   screenshotToBlob: function () {
     return this.screenshotToDataURL().then(longstr => {
--- a/devtools/shared/fronts/director-manager.js
+++ b/devtools/shared/fronts/director-manager.js
@@ -8,39 +8,39 @@ const {
   directorScriptSpec,
   directorManagerSpec,
 } = require("devtools/shared/specs/director-manager");
 const protocol = require("devtools/shared/protocol");
 
 /**
  * The corresponding Front object for the MessagePortActor.
  */
-const MessagePortFront = protocol.FrontClassWithSpec(messagePortSpec, {
+const MessagePortFront = protocol.FrontClass(messagePortSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client, form);
   }
 });
 
 exports.MessagePortFront = MessagePortFront;
 
 /**
  * The corresponding Front object for the DirectorScriptActor.
  */
-const DirectorScriptFront = protocol.FrontClassWithSpec(directorScriptSpec, {
+const DirectorScriptFront = protocol.FrontClass(directorScriptSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client, form);
   }
 });
 
 exports.DirectorScriptFront = DirectorScriptFront;
 
 /**
  * The corresponding Front object for the DirectorManagerActor.
  */
-const DirectorManagerFront = protocol.FrontClassWithSpec(directorManagerSpec, {
+const DirectorManagerFront = protocol.FrontClass(directorManagerSpec, {
   initialize: function (client, { directorManagerActor }) {
     protocol.Front.prototype.initialize.call(this, client, {
       actor: directorManagerActor
     });
     this.manage(this);
   }
 });
 
--- a/devtools/shared/fronts/director-registry.js
+++ b/devtools/shared/fronts/director-registry.js
@@ -4,17 +4,17 @@
 "use strict";
 
 const {directorRegistrySpec} = require("devtools/shared/specs/director-registry");
 const protocol = require("devtools/shared/protocol");
 
 /**
  * The corresponding Front object for the DirectorRegistryActor.
  */
-const DirectorRegistryFront = protocol.FrontClassWithSpec(directorRegistrySpec, {
+const DirectorRegistryFront = protocol.FrontClass(directorRegistrySpec, {
   initialize: function (client, { directorRegistryActor }) {
     protocol.Front.prototype.initialize.call(this, client, {
       actor: directorRegistryActor
     });
     this.manage(this);
   }
 });
 
--- a/devtools/shared/fronts/eventlooplag.js
+++ b/devtools/shared/fronts/eventlooplag.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
+const { Front, FrontClass } = require("devtools/shared/protocol");
 const { eventLoopLagSpec } = require("devtools/shared/specs/eventlooplag");
 
-exports.EventLoopLagFront = FrontClassWithSpec(eventLoopLagSpec, {
+exports.EventLoopLagFront = FrontClass(eventLoopLagSpec, {
   initialize: function (client, form) {
     Front.prototype.initialize.call(this, client);
     this.actorID = form.eventLoopLagActor;
     this.manage(this);
   },
 });
--- a/devtools/shared/fronts/framerate.js
+++ b/devtools/shared/fronts/framerate.js
@@ -1,19 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
+const { Front, FrontClass } = require("devtools/shared/protocol");
 const { framerateSpec } = require("devtools/shared/specs/framerate");
 
 /**
  * The corresponding Front object for the FramerateActor.
  */
-var FramerateFront = exports.FramerateFront = FrontClassWithSpec(framerateSpec, {
+var FramerateFront = exports.FramerateFront = FrontClass(framerateSpec, {
   initialize: function (client, { framerateActor }) {
     Front.prototype.initialize.call(this, client, { actor: framerateActor });
     this.manage(this);
   }
 });
 
 exports.FramerateFront = FramerateFront;
--- a/devtools/shared/fronts/gcli.js
+++ b/devtools/shared/fronts/gcli.js
@@ -1,20 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
+const { Front, FrontClass } = require("devtools/shared/protocol");
 const { gcliSpec } = require("devtools/shared/specs/gcli");
 
 /**
  *
  */
-const GcliFront = exports.GcliFront = FrontClassWithSpec(gcliSpec, {
+const GcliFront = exports.GcliFront = FrontClass(gcliSpec, {
   initialize: function (client, tabForm) {
     Front.prototype.initialize.call(this, client);
     this.actorID = tabForm.gcliActor;
 
     // XXX: This is the first actor type in its hierarchy to use the protocol
     // library, so we're going to self-own on the client side for now.
     this.manage(this);
   },
--- a/devtools/shared/fronts/highlighters.js
+++ b/devtools/shared/fronts/highlighters.js
@@ -1,25 +1,25 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { FrontClassWithSpec } = require("devtools/shared/protocol");
+const { FrontClass } = require("devtools/shared/protocol");
 const {
   customHighlighterSpec,
   highlighterSpec
 } = require("devtools/shared/specs/highlighters");
 
-const HighlighterFront = FrontClassWithSpec(highlighterSpec, {
+const HighlighterFront = FrontClass(highlighterSpec, {
   // Update the object given a form representation off the wire.
   form: function (json) {
     this.actorID = json.actor;
     // FF42+ HighlighterActors starts exposing custom form, with traits object
     this.traits = json.traits || {};
   }
 });
 
 exports.HighlighterFront = HighlighterFront;
 
-const CustomHighlighterFront = FrontClassWithSpec(customHighlighterSpec, {});
+const CustomHighlighterFront = FrontClass(customHighlighterSpec, {});
 
 exports.CustomHighlighterFront = CustomHighlighterFront;
--- a/devtools/shared/fronts/inspector.js
+++ b/devtools/shared/fronts/inspector.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 require("devtools/shared/fronts/styles");
 require("devtools/shared/fronts/highlighters");
 const { SimpleStringFront } = require("devtools/shared/fronts/string");
 const {
   Front,
-  FrontClassWithSpec,
+  FrontClass,
   custom,
   preEvent,
   types
 } = require("devtools/shared/protocol.js");
 const {
   inspectorSpec,
   nodeSpec,
   nodeListSpec,
@@ -79,17 +79,17 @@ const AttributeModificationList = Class(
  *  - The order of children isn't guaranteed to be the same as the DOM.
  * Children are stored in a doubly-linked list, to make addition/removal
  * and traversal quick.
  *
  * Due to the order/incompleteness of the child list, it is safe to use
  * the parent node from clients, but the `children` request should be used
  * to traverse children.
  */
-const NodeFront = FrontClassWithSpec(nodeSpec, {
+const NodeFront = FrontClass(nodeSpec, {
   initialize: function (conn, form, detail, ctx) {
     // The parent node
     this._parent = null;
     // The first child of this node.
     this._child = null;
     // The next sibling of this node.
     this._next = null;
     // The previous sibling of this node.
@@ -439,17 +439,17 @@ const NodeFront = FrontClassWithSpec(nod
   }
 });
 
 exports.NodeFront = NodeFront;
 
 /**
  * Client side of a node list as returned by querySelectorAll()
  */
-const NodeListFront = FrontClassWithSpec(nodeListSpec, {
+const NodeListFront = FrontClass(nodeListSpec, {
   initialize: function (client, form) {
     Front.prototype.initialize.call(this, client, form);
   },
 
   destroy: function () {
     Front.prototype.destroy.call(this);
   },
 
@@ -479,17 +479,17 @@ const NodeListFront = FrontClassWithSpec
   })
 });
 
 exports.NodeListFront = NodeListFront;
 
 /**
  * Client side of the DOM walker.
  */
-const WalkerFront = FrontClassWithSpec(walkerSpec, {
+const WalkerFront = FrontClass(walkerSpec, {
   // Set to true if cleanup should be requested after every mutation list.
   autoCleanup: true,
 
   /**
    * This is kept for backward-compatibility reasons with older remote target.
    * Targets previous to bug 916443
    */
   pick: custom(function () {
@@ -935,17 +935,17 @@ const WalkerFront = FrontClassWithSpec(w
 });
 
 exports.WalkerFront = WalkerFront;
 
 /**
  * Client side of the inspector actor, which is used to create
  * inspector-related actors, including the walker.
  */
-var InspectorFront = FrontClassWithSpec(inspectorSpec, {
+var InspectorFront = FrontClass(inspectorSpec, {
   initialize: function (client, tabForm) {
     Front.prototype.initialize.call(this, client);
     this.actorID = tabForm.inspectorActor;
 
     // XXX: This is the first actor type in its hierarchy to use the protocol
     // library, so we're going to self-own on the client side for now.
     this.manage(this);
   },
--- a/devtools/shared/fronts/layout.js
+++ b/devtools/shared/fronts/layout.js
@@ -9,17 +9,17 @@ const protocol = require("devtools/share
 /**
  * Usage example of the reflow front:
  *
  * let front = ReflowFront(toolbox.target.client, toolbox.target.form);
  * front.on("reflows", this._onReflows);
  * front.start();
  * // now wait for events to come
  */
-const ReflowFront = protocol.FrontClassWithSpec(reflowSpec, {
+const ReflowFront = protocol.FrontClass(reflowSpec, {
   initialize: function (client, {reflowActor}) {
     protocol.Front.prototype.initialize.call(this, client, {actor: reflowActor});
     this.manage(this);
   },
 
   destroy: function () {
     protocol.Front.prototype.destroy.call(this);
   },
--- a/devtools/shared/fronts/memory.js
+++ b/devtools/shared/fronts/memory.js
@@ -7,17 +7,17 @@ const { memorySpec } = require("devtools
 const { Task } = require("devtools/shared/task");
 const protocol = require("devtools/shared/protocol");
 
 loader.lazyRequireGetter(this, "FileUtils",
                          "resource://gre/modules/FileUtils.jsm", true);
 loader.lazyRequireGetter(this, "HeapSnapshotFileUtils",
                          "devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
 
-const MemoryFront = protocol.FrontClassWithSpec(memorySpec, {
+const MemoryFront = protocol.FrontClass(memorySpec, {
   initialize: function (client, form, rootForm = null) {
     protocol.Front.prototype.initialize.call(this, client, form);
     this._client = client;
     this.actorID = form.memoryActor;
     this.heapSnapshotFileActorID = rootForm
       ? rootForm.heapSnapshotFileActor
       : null;
     this.manage(this);
--- a/devtools/shared/fronts/moz.build
+++ b/devtools/shared/fronts/moz.build
@@ -17,16 +17,17 @@ DevToolsModules(
     'director-registry.js',
     'eventlooplag.js',
     'framerate.js',
     'gcli.js',
     'highlighters.js',
     'inspector.js',
     'layout.js',
     'memory.js',
+    'performance-entries.js',
     'performance-recording.js',
     'performance.js',
     'preference.js',
     'profiler.js',
     'promises.js',
     'settings.js',
     'storage.js',
     'string.js',
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/performance-entries.js
@@ -0,0 +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/. */
+"use strict";
+
+const { Front, FrontClass } = require("devtools/shared/protocol");
+const performanceSpec = require("devtools/shared/specs/performance-entries");
+
+var PerformanceEntriesFront = FrontClass(performanceSpec, {
+  initialize: function (client, form) {
+    Front.prototype.initialize.call(this, client);
+    this.actorID = form.performanceEntriesActor;
+    this.manage(this);
+  },
+});
+
+exports.PerformanceEntriesFront = PerformanceEntriesFront;
--- a/devtools/shared/fronts/performance-recording.js
+++ b/devtools/shared/fronts/performance-recording.js
@@ -1,29 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
+const { Front, FrontClass } = require("devtools/shared/protocol");
 const { performanceRecordingSpec } = require("devtools/shared/specs/performance-recording");
 
 loader.lazyRequireGetter(this, "PerformanceIO",
   "devtools/client/performance/modules/io");
 loader.lazyRequireGetter(this, "PerformanceRecordingCommon",
   "devtools/shared/performance/recording-common", true);
 loader.lazyRequireGetter(this, "RecordingUtils",
   "devtools/shared/performance/recording-utils");
 loader.lazyRequireGetter(this, "merge", "sdk/util/object", true);
 
 /**
  * This can be used on older Profiler implementations, but the methods cannot
  * be changed -- you must introduce a new method, and detect the server.
  */
-const PerformanceRecordingFront = FrontClassWithSpec(performanceRecordingSpec, merge({
+const PerformanceRecordingFront = FrontClass(performanceRecordingSpec, merge({
   form: function (form, detail) {
     if (detail === "actorid") {
       this.actorID = form;
       return;
     }
     this.actorID = form.actor;
     this._form = form;
     this._configuration = form.configuration;
--- a/devtools/shared/fronts/performance.js
+++ b/devtools/shared/fronts/performance.js
@@ -1,27 +1,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Cu } = require("chrome");
-const { Front, FrontClassWithSpec, custom, preEvent } = require("devtools/shared/protocol");
+const { Front, FrontClass, custom, preEvent } = require("devtools/shared/protocol");
 const { PerformanceRecordingFront } = require("devtools/shared/fronts/performance-recording");
 const { performanceSpec } = require("devtools/shared/specs/performance");
 const { Task } = require("devtools/shared/task");
 
 loader.lazyRequireGetter(this, "PerformanceIO",
   "devtools/client/performance/modules/io");
 loader.lazyRequireGetter(this, "LegacyPerformanceFront",
   "devtools/client/performance/legacy/front", true);
 loader.lazyRequireGetter(this, "getSystemInfo",
   "devtools/shared/system", true);
 
-const PerformanceFront = FrontClassWithSpec(performanceSpec, {
+const PerformanceFront = FrontClass(performanceSpec, {
   initialize: function (client, form) {
     Front.prototype.initialize.call(this, client, form);
     this.actorID = form.performanceActor;
     this.manage(this);
   },
 
   destroy: function () {
     Front.prototype.destroy.call(this);
--- a/devtools/shared/fronts/preference.js
+++ b/devtools/shared/fronts/preference.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/. */
 "use strict";
 
 const {preferenceSpec} = require("devtools/shared/specs/preference");
 const protocol = require("devtools/shared/protocol");
 
-const PreferenceFront = protocol.FrontClassWithSpec(preferenceSpec, {
+const PreferenceFront = protocol.FrontClass(preferenceSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client);
     this.actorID = form.preferenceActor;
     this.manage(this);
   },
 });
 
 const _knownPreferenceFronts = new WeakMap();
--- a/devtools/shared/fronts/profiler.js
+++ b/devtools/shared/fronts/profiler.js
@@ -1,29 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Cu } = require("chrome");
 const {
   Front,
-  FrontClassWithSpec,
+  FrontClass,
   custom
 } = require("devtools/shared/protocol");
 const { profilerSpec } = require("devtools/shared/specs/profiler");
 
 loader.lazyRequireGetter(this, "events", "sdk/event/core");
 loader.lazyRequireGetter(this, "extend", "sdk/util/object", true);
 
 /**
  * This can be used on older Profiler implementations, but the methods cannot
  * be changed -- you must introduce a new method, and detect the server.
  */
-exports.ProfilerFront = FrontClassWithSpec(profilerSpec, {
+exports.ProfilerFront = FrontClass(profilerSpec, {
   initialize: function (client, form) {
     Front.prototype.initialize.call(this, client, form);
     this.actorID = form.profilerActor;
     this.manage(this);
 
     this._onProfilerEvent = this._onProfilerEvent.bind(this);
     events.on(this, "*", this._onProfilerEvent);
   },
--- a/devtools/shared/fronts/promises.js
+++ b/devtools/shared/fronts/promises.js
@@ -1,23 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {
   Front,
-  FrontClassWithSpec,
+  FrontClass,
 } = require("devtools/shared/protocol");
 const { promisesSpec } = require("devtools/shared/specs/promises");
 
 /**
  * PromisesFront, the front for the PromisesActor.
  */
-const PromisesFront = FrontClassWithSpec(promisesSpec, {
+const PromisesFront = FrontClass(promisesSpec, {
   initialize: function (client, form) {
     Front.prototype.initialize.call(this, client, form);
     this.actorID = form.promisesActor;
     this.manage(this);
   },
 
   destroy: function () {
     Front.prototype.destroy.call(this);
--- a/devtools/shared/fronts/settings.js
+++ b/devtools/shared/fronts/settings.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/. */
 "use strict";
 
 const {settingsSpec} = require("devtools/shared/specs/settings");
 const protocol = require("devtools/shared/protocol");
 
-const SettingsFront = protocol.FrontClassWithSpec(settingsSpec, {
+const SettingsFront = protocol.FrontClass(settingsSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client);
     this.actorID = form.settingsActor;
     this.manage(this);
   },
 });
 
 const _knownSettingsFronts = new WeakMap();
--- a/devtools/shared/fronts/storage.js
+++ b/devtools/shared/fronts/storage.js
@@ -2,31 +2,31 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const protocol = require("devtools/shared/protocol");
 const specs = require("devtools/shared/specs/storage");
 
 for (let childSpec of Object.values(specs.childSpecs)) {
-  protocol.FrontClassWithSpec(childSpec, {
+  protocol.FrontClass(childSpec, {
     form(form, detail) {
       if (detail === "actorid") {
         this.actorID = form;
         return null;
       }
 
       this.actorID = form.actor;
       this.hosts = form.hosts;
       return null;
     }
   });
 }
 
-const StorageFront = protocol.FrontClassWithSpec(specs.storageSpec, {
+const StorageFront = protocol.FrontClass(specs.storageSpec, {
   initialize(client, tabForm) {
     protocol.Front.prototype.initialize.call(this, client);
     this.actorID = tabForm.storageActor;
     this.manage(this);
   }
 });
 
 exports.StorageFront = StorageFront;
--- a/devtools/shared/fronts/string.js
+++ b/devtools/shared/fronts/string.js
@@ -4,17 +4,17 @@
 "use strict";
 
 const {DebuggerServer} = require("devtools/server/main");
 const promise = require("promise");
 const {Class} = require("sdk/core/heritage");
 const {longStringSpec} = require("devtools/shared/specs/string");
 const protocol = require("devtools/shared/protocol");
 
-const LongStringFront = protocol.FrontClassWithSpec(longStringSpec, {
+const LongStringFront = protocol.FrontClass(longStringSpec, {
   initialize: function (client) {
     protocol.Front.prototype.initialize.call(this, client);
   },
 
   destroy: function () {
     this.initial = null;
     this.length = null;
     this.strPromise = null;
--- a/devtools/shared/fronts/styleeditor.js
+++ b/devtools/shared/fronts/styleeditor.js
@@ -1,27 +1,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { SimpleStringFront } = require("devtools/shared/fronts/string");
-const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
+const { Front, FrontClass } = require("devtools/shared/protocol");
 const {
   oldStyleSheetSpec,
   styleEditorSpec
 } = require("devtools/shared/specs/styleeditor");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const events = require("sdk/event/core");
 
 /**
  * StyleSheetFront is the client-side counterpart to a StyleSheetActor.
  */
-const OldStyleSheetFront = FrontClassWithSpec(oldStyleSheetSpec, {
+const OldStyleSheetFront = FrontClass(oldStyleSheetSpec, {
   initialize: function (conn, form, ctx, detail) {
     Front.prototype.initialize.call(this, conn, form, ctx, detail);
 
     this._onPropertyChange = this._onPropertyChange.bind(this);
     events.on(this, "property-change", this._onPropertyChange);
   },
 
   destroy: function () {
@@ -82,17 +82,17 @@ const OldStyleSheetFront = FrontClassWit
   }
 });
 
 exports.OldStyleSheetFront = OldStyleSheetFront;
 
 /**
  * The corresponding Front object for the StyleEditorActor.
  */
-const StyleEditorFront = FrontClassWithSpec(styleEditorSpec, {
+const StyleEditorFront = FrontClass(styleEditorSpec, {
   initialize: function (client, tabForm) {
     Front.prototype.initialize.call(this, client);
     this.actorID = tabForm.styleEditorActor;
     this.manage(this);
   },
 
   getStyleSheets: function () {
     let deferred = defer();
--- a/devtools/shared/fronts/styles.js
+++ b/devtools/shared/fronts/styles.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/. */
 "use strict";
 
 require("devtools/shared/fronts/stylesheets");
 const {
   Front,
-  FrontClassWithSpec,
+  FrontClass,
   custom,
   preEvent
 } = require("devtools/shared/protocol");
 const {
   pageStyleSpec,
   styleRuleSpec
 } = require("devtools/shared/specs/styles");
 const promise = require("promise");
@@ -20,17 +20,17 @@ const { Class } = require("sdk/core/heri
 
 loader.lazyGetter(this, "RuleRewriter", () => {
   return require("devtools/shared/css-parsing-utils").RuleRewriter;
 });
 
 /**
  * PageStyleFront, the front object for the PageStyleActor
  */
-const PageStyleFront = FrontClassWithSpec(pageStyleSpec, {
+const PageStyleFront = FrontClass(pageStyleSpec, {
   initialize: function (conn, form, ctx, detail) {
     Front.prototype.initialize.call(this, conn, form, ctx, detail);
     this.inspector = this.parent();
   },
 
   form: function (form, detail) {
     if (detail === "actorid") {
       this.actorID = form;
@@ -88,17 +88,17 @@ const PageStyleFront = FrontClassWithSpe
   })
 });
 
 exports.PageStyleFront = PageStyleFront;
 
 /**
  * StyleRuleFront, the front for the StyleRule actor.
  */
-const StyleRuleFront = FrontClassWithSpec(styleRuleSpec, {
+const StyleRuleFront = FrontClass(styleRuleSpec, {
   initialize: function (client, form, ctx, detail) {
     Front.prototype.initialize.call(this, client, form, ctx, detail);
   },
 
   destroy: function () {
     Front.prototype.destroy.call(this);
   },
 
--- a/devtools/shared/fronts/stylesheets.js
+++ b/devtools/shared/fronts/stylesheets.js
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
+const { Front, FrontClass } = require("devtools/shared/protocol");
 const {
   getIndentationFromPrefs,
   getIndentationFromString
 } = require("devtools/shared/indentation");
 const {
   originalSourceSpec,
   mediaRuleSpec,
   styleSheetSpec,
@@ -16,17 +16,17 @@ const {
 } = require("devtools/shared/specs/stylesheets");
 const promise = require("promise");
 const { Task } = require("devtools/shared/task");
 const events = require("sdk/event/core");
 
 /**
  * The client-side counterpart for an OriginalSourceActor.
  */
-const OriginalSourceFront = FrontClassWithSpec(originalSourceSpec, {
+const OriginalSourceFront = FrontClass(originalSourceSpec, {
   initialize: function (client, form) {
     Front.prototype.initialize.call(this, client, form);
 
     this.isOriginalSource = true;
   },
 
   form: function (form, detail) {
     if (detail === "actorid") {
@@ -45,17 +45,17 @@ const OriginalSourceFront = FrontClassWi
   }
 });
 
 exports.OriginalSourceFront = OriginalSourceFront;
 
 /**
  * Corresponding client-side front for a MediaRuleActor.
  */
-const MediaRuleFront = FrontClassWithSpec(mediaRuleSpec, {
+const MediaRuleFront = FrontClass(mediaRuleSpec, {
   initialize: function (client, form) {
     Front.prototype.initialize.call(this, client, form);
 
     this._onMatchesChange = this._onMatchesChange.bind(this);
     events.on(this, "matches-change", this._onMatchesChange);
   },
 
   _onMatchesChange: function (matches) {
@@ -91,17 +91,17 @@ const MediaRuleFront = FrontClassWithSpe
   }
 });
 
 exports.MediaRuleFront = MediaRuleFront;
 
 /**
  * StyleSheetFront is the client-side counterpart to a StyleSheetActor.
  */
-const StyleSheetFront = FrontClassWithSpec(styleSheetSpec, {
+const StyleSheetFront = FrontClass(styleSheetSpec, {
   initialize: function (conn, form) {
     Front.prototype.initialize.call(this, conn, form);
 
     this._onPropertyChange = this._onPropertyChange.bind(this);
     events.on(this, "property-change", this._onPropertyChange);
   },
 
   destroy: function () {
@@ -168,17 +168,17 @@ const StyleSheetFront = FrontClassWithSp
   }
 });
 
 exports.StyleSheetFront = StyleSheetFront;
 
 /**
  * The corresponding Front object for the StyleSheetsActor.
  */
-const StyleSheetsFront = FrontClassWithSpec(styleSheetsSpec, {
+const StyleSheetsFront = FrontClass(styleSheetsSpec, {
   initialize: function (client, tabForm) {
     Front.prototype.initialize.call(this, client);
     this.actorID = tabForm.styleSheetsActor;
     this.manage(this);
   }
 });
 
 exports.StyleSheetsFront = StyleSheetsFront;
--- a/devtools/shared/fronts/timeline.js
+++ b/devtools/shared/fronts/timeline.js
@@ -1,23 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {
   Front,
-  FrontClassWithSpec,
+  FrontClass,
 } = require("devtools/shared/protocol");
 const { timelineSpec } = require("devtools/shared/specs/timeline");
 
 /**
  * TimelineFront, the front for the TimelineActor.
  */
-const TimelineFront = FrontClassWithSpec(timelineSpec, {
+const TimelineFront = FrontClass(timelineSpec, {
   initialize: function (client, { timelineActor }) {
     Front.prototype.initialize.call(this, client, { actor: timelineActor });
     this.manage(this);
   },
   destroy: function () {
     Front.prototype.destroy.call(this);
   },
 });
--- a/devtools/shared/fronts/webaudio.js
+++ b/devtools/shared/fronts/webaudio.js
@@ -20,17 +20,17 @@ const AUDIO_NODE_DEFINITION = require("d
  *            The type of audio node, like "OscillatorNode", "MediaElementAudioSourceNode"
  * @attribute {Boolean} source
  *            Boolean indicating if the node is a source node, like BufferSourceNode,
  *            MediaElementAudioSourceNode, OscillatorNode, etc.
  * @attribute {Boolean} bypassable
  *            Boolean indicating if the audio node is bypassable (splitter,
  *            merger and destination nodes, for example, are not)
  */
-const AudioNodeFront = protocol.FrontClassWithSpec(audionodeSpec, {
+const AudioNodeFront = protocol.FrontClass(audionodeSpec, {
   form: function (form, detail) {
     if (detail === "actorid") {
       this.actorID = form;
       return;
     }
 
     this.actorID = form.actor;
     this.type = form.type;
@@ -48,17 +48,17 @@ const AudioNodeFront = protocol.FrontCla
   }
 });
 
 exports.AudioNodeFront = AudioNodeFront;
 
 /**
  * The corresponding Front object for the WebAudioActor.
  */
-const WebAudioFront = protocol.FrontClassWithSpec(webAudioSpec, {
+const WebAudioFront = protocol.FrontClass(webAudioSpec, {
   initialize: function (client, { webaudioActor }) {
     protocol.Front.prototype.initialize.call(this, client, { actor: webaudioActor });
     this.manage(this);
   },
 
   /**
    * If connecting to older geckos (<Fx43), where audio node actor's do not
    * contain `type`, `source` and `bypassable` properties, fetch
--- a/devtools/shared/fronts/webgl.js
+++ b/devtools/shared/fronts/webgl.js
@@ -8,38 +8,38 @@ const {
   programSpec,
   webGLSpec,
 } = require("devtools/shared/specs/webgl");
 const protocol = require("devtools/shared/protocol");
 
 /**
  * The corresponding Front object for the ShaderActor.
  */
-const ShaderFront = protocol.FrontClassWithSpec(shaderSpec, {
+const ShaderFront = protocol.FrontClass(shaderSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client, form);
   }
 });
 
 exports.ShaderFront = ShaderFront;
 
 /**
  * The corresponding Front object for the ProgramActor.
  */
-const ProgramFront = protocol.FrontClassWithSpec(programSpec, {
+const ProgramFront = protocol.FrontClass(programSpec, {
   initialize: function (client, form) {
     protocol.Front.prototype.initialize.call(this, client, form);
   }
 });
 
 exports.ProgramFront = ProgramFront;
 
 /**
  * The corresponding Front object for the WebGLActor.
  */
-const WebGLFront = protocol.FrontClassWithSpec(webGLSpec, {
+const WebGLFront = protocol.FrontClass(webGLSpec, {
   initialize: function (client, { webglActor }) {
     protocol.Front.prototype.initialize.call(this, client, { actor: webglActor });
     this.manage(this);
   }
 });
 
 exports.WebGLFront = WebGLFront;
--- a/devtools/shared/protocol.js
+++ b/devtools/shared/protocol.js
@@ -1086,46 +1086,35 @@ var generateRequestHandlers = function (
   });
 
   actorProto._actorSpec = actorSpec;
 
   return actorProto;
 };
 
 /**
- * Create an actor class for the given actor prototype.
- *
- * @param object actorProto
- *    The actor prototype.  Must have a 'typeName' property,
- *    should have method definitions, can have event definitions.
- */
-exports.ActorClass = function (actorProto) {
-  return ActorClassWithSpec(generateActorSpec(actorProto), actorProto);
-};
-
-/**
  * Create an actor class for the given actor specification and prototype.
  *
  * @param object actorSpec
  *    The actor specification. Must have a 'typeName' property.
  * @param object actorProto
  *    The actor prototype. Should have method definitions, can have event
  *    definitions.
  */
-var ActorClassWithSpec = function (actorSpec, actorProto) {
+var ActorClass = function (actorSpec, actorProto) {
   if (!actorSpec.typeName) {
     throw Error("Actor specification must have a typeName member.");
   }
 
   actorProto.extends = Actor;
   let cls = Class(generateRequestHandlers(actorSpec, actorProto));
 
   return cls;
 };
-exports.ActorClassWithSpec = ActorClassWithSpec;
+exports.ActorClass = ActorClass;
 
 /**
  * Base class for client-side actor fronts.
  */
 var Front = Class({
   extends: Pool,
 
   actorID: null,
@@ -1418,49 +1407,36 @@ var generateRequestMethods = function (a
   }
 
   frontProto._actorSpec = actorSpec;
 
   return frontProto;
 };
 
 /**
- * Create a front class for the given actor class and front prototype.
- *
- * @param ActorClass actorType
- *    The actor class you're creating a front for.
- * @param object frontProto
- *    The front prototype.  Must have a 'typeName' property,
- *    should have method definitions, can have event definitions.
- */
-exports.FrontClass = function (actorType, frontProto) {
-  return FrontClassWithSpec(prototypeOf(actorType)._actorSpec, frontProto);
-};
-
-/**
  * Create a front class for the given actor specification and front prototype.
  *
  * @param object actorSpec
  *    The actor specification you're creating a front for.
  * @param object proto
  *    The object prototype.  Must have a 'typeName' property,
  *    should have method definitions, can have event definitions.
  */
-var FrontClassWithSpec = function (actorSpec, frontProto) {
+var FrontClass = function (actorSpec, frontProto) {
   frontProto.extends = Front;
   let cls = Class(generateRequestMethods(actorSpec, frontProto));
 
   if (!registeredTypes.has(actorSpec.typeName)) {
     types.addActorType(actorSpec.typeName);
   }
   registeredTypes.get(actorSpec.typeName).frontClass = cls;
 
   return cls;
 };
-exports.FrontClassWithSpec = FrontClassWithSpec;
+exports.FrontClass = FrontClass;
 
 exports.dumpActorSpec = function (type) {
   let actorSpec = type.actorSpec;
   let ret = {
     category: "actor",
     typeName: type.name,
     methods: [],
     events: {}
--- a/devtools/shared/specs/moz.build
+++ b/devtools/shared/specs/moz.build
@@ -22,16 +22,17 @@ DevToolsModules(
     'framerate.js',
     'gcli.js',
     'heap-snapshot-file.js',
     'highlighters.js',
     'inspector.js',
     'layout.js',
     'memory.js',
     'node.js',
+    'performance-entries.js',
     'performance-recording.js',
     'performance.js',
     'preference.js',
     'profiler.js',
     'promises.js',
     'script.js',
     'settings.js',
     'source.js',
new file mode 100644
--- /dev/null
+++ b/devtools/shared/specs/performance-entries.js
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Arg, generateActorSpec } = require("devtools/shared/protocol");
+
+const performanceEntriesSpec = generateActorSpec({
+  typeName: "performanceEntries",
+
+  events: {
+    "entry": {
+      type: "entry",
+      // object containing performance entry name, type, origin, and epoch.
+      detail: Arg(0, "json")
+    }
+  },
+
+  methods: {
+    start: {},
+    stop: {}
+  }
+});
+
+exports.performanceEntriesSpec = performanceEntriesSpec;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -2979,21 +2979,21 @@ nsDocShell::GetSessionStorageForPrincipa
     return NS_ERROR_UNEXPECTED;
   }
 
   nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow();
 
   AssertOriginAttributesMatchPrivateBrowsing();
   if (aCreate) {
     return manager->CreateStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
-                                  aDocumentURI, UsePrivateBrowsing(), aStorage);
+                                  aDocumentURI, aStorage);
   }
 
   return manager->GetStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
-                             UsePrivateBrowsing(), aStorage);
+                             aStorage);
 }
 
 nsresult
 nsDocShell::AddSessionStorage(nsIPrincipal* aPrincipal, nsIDOMStorage* aStorage)
 {
   RefPtr<DOMStorage> storage = static_cast<DOMStorage*>(aStorage);
   if (!storage) {
     return NS_ERROR_UNEXPECTED;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -1,16 +1,17 @@
 /* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 #include "nsIDocShellTreeItem.idl"
+#include "nsIRequest.idl"
 
 %{ C++
 #include "js/TypeDecls.h"
 class nsPresContext;
 class nsIPresShell;
 %}
 
 /**
@@ -41,18 +42,16 @@ interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
 interface nsITabChild;
 interface nsICommandManager;
 interface nsICommandParams;
 native TabChildRef(already_AddRefed<nsITabChild>);
 
-typedef unsigned long nsLoadFlags;
-
 [scriptable, builtinclass, uuid(049234fe-da10-478b-bc5d-bc6f9a1ba63d)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
--- a/docshell/test/unit/test_nsDefaultURIFixup_info.js
+++ b/docshell/test/unit/test_nsDefaultURIFixup_info.js
@@ -125,55 +125,55 @@ var testcases = [ {
     fixedURI: "http://192.168.10.110:1234/123",
     protocolChange: true,
   }, {
     input: "192.168.10.110:1234/123foo",
     fixedURI: "http://192.168.10.110:1234/123foo",
     protocolChange: true,
   }, {
     input: "1.2.3",
-    fixedURI: "http://1.2.3/",
+    fixedURI: "http://1.2.0.3/",
     protocolChange: true,
   }, {
     input: "1.2.3/",
-    fixedURI: "http://1.2.3/",
+    fixedURI: "http://1.2.0.3/",
     protocolChange: true,
   }, {
     input: "1.2.3/foo",
-    fixedURI: "http://1.2.3/foo",
+    fixedURI: "http://1.2.0.3/foo",
     protocolChange: true,
   }, {
     input: "1.2.3/123",
-    fixedURI: "http://1.2.3/123",
+    fixedURI: "http://1.2.0.3/123",
     protocolChange: true,
   }, {
     input: "1.2.3:8000",
-    fixedURI: "http://1.2.3:8000/",
+    fixedURI: "http://1.2.0.3:8000/",
     protocolChange: true,
   }, {
     input: "1.2.3:8000/",
-    fixedURI: "http://1.2.3:8000/",
+    fixedURI: "http://1.2.0.3:8000/",
     protocolChange: true,
   }, {
     input: "1.2.3:8000/foo",
-    fixedURI: "http://1.2.3:8000/foo",
+    fixedURI: "http://1.2.0.3:8000/foo",
     protocolChange: true,
   }, {
     input: "1.2.3:8000/123",
-    fixedURI: "http://1.2.3:8000/123",
+    fixedURI: "http://1.2.0.3:8000/123",
     protocolChange: true,
   }, {
     input: "http://1.2.3",
-    fixedURI: "http://1.2.3/",
+    fixedURI: "http://1.2.0.3/",
   }, {
     input: "http://1.2.3/",
-    fixedURI: "http://1.2.3/",
+    fixedURI: "http://1.2.0.3/",
   }, {
     input: "http://1.2.3/foo",
-    fixedURI: "http://1.2.3/foo",
+    fixedURI: "http://1.2.0.3/foo",
   }, {
     input: "[::1]",
     fixedURI: "http://[::1]/",
     alternateURI: "http://[::1]/",
     protocolChange: true,
   }, {
     input: "[::1]/",
     fixedURI: "http://[::1]/",
@@ -217,18 +217,17 @@ var testcases = [ {
     keywordLookup: true,
     protocolChange: true
   }, {
     input: "[::1]]",
     keywordLookup: true,
     protocolChange: true
   }, {
     input: "1234",
-    fixedURI: "http://1234/",
-    alternateURI: "http://www.1234.com/",
+    fixedURI: "http://0.0.4.210/",
     keywordLookup: true,
     protocolChange: true,
     affectedByDNSForSingleHosts: true,
   }, {
     input: "host/foo.txt",
     fixedURI: "http://host/foo.txt",
     alternateURI: "http://www.host.com/foo.txt",
     protocolChange: true,
@@ -357,32 +356,30 @@ var testcases = [ {
   }, {
     input: "-22.14,23.51-",
     fixedURI: "http://-22.14,23.51-/",
     keywordLookup: true,
     protocolChange: true,
     affectedByDNSForSingleHosts: true,
   }, {
     input: "32.7",
-    fixedURI: "http://32.7/",
-    alternateURI: "http://www.32.7/",
+    fixedURI: "http://32.0.0.7/",
     keywordLookup: true,
     protocolChange: true,
     affectedByDNSForSingleHosts: true,
   }, {
     input: "5+2",
     fixedURI: "http://5+2/",
     alternateURI: "http://www.5+2.com/",
     keywordLookup: true,
     protocolChange: true,
     affectedByDNSForSingleHosts: true,
   }, {
     input: "5/2",
-    fixedURI: "http://5/2",
-    alternateURI: "http://www.5.com/2",
+    fixedURI: "http://0.0.0.5/2",
     keywordLookup: true,
     protocolChange: true,
     affectedByDNSForSingleHosts: true,
   }, {
     input: "moz ?.::%27",
     keywordLookup: true,
     protocolChange: true
   }, {
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1691,17 +1691,17 @@ Navigator::GetFeature(const nsAString& a
 #endif
 
   // Mirror the dom.apps.developer_mode pref to let apps get it read-only.
   if (aName.EqualsLiteral("dom.apps.developer_mode")) {
     p->MaybeResolve(Preferences::GetBool("dom.apps.developer_mode", false));
     return p.forget();
   }
 
-  p->MaybeResolve(JS::UndefinedHandleValue);
+  p->MaybeResolveWithUndefined();
   return p.forget();
 }
 
 already_AddRefed<Promise>
 Navigator::HasFeature(const nsAString& aName, ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
   RefPtr<Promise> p = Promise::Create(go, aRv);
@@ -1790,22 +1790,22 @@ Navigator::HasFeature(const nsAString& a
     }
 #endif
 
     if (featureName.EqualsLiteral("XMLHttpRequest.mozSystem")) {
       p->MaybeResolve(true);
       return p.forget();
     }
 
-    p->MaybeResolve(JS::UndefinedHandleValue);
+    p->MaybeResolveWithUndefined();
     return p.forget();
   }
 
   // resolve with <undefined> because the feature name is not supported
-  p->MaybeResolve(JS::UndefinedHandleValue);
+  p->MaybeResolveWithUndefined();
 
   return p.forget();
 }
 
 PowerManager*
 Navigator::GetMozPower(ErrorResult& aRv)
 {
   if (!mPowerManager) {
--- a/dom/base/ScreenOrientation.cpp
+++ b/dom/base/ScreenOrientation.cpp
@@ -167,24 +167,24 @@ ScreenOrientation::LockOrientationTask::
     // to lock orientation. There has since been another request to
     // lock orientation, thus we don't need to do anything. Old promise
     // should be been rejected.
     return NS_OK;
   }
 
   if (mDocument->Hidden()) {
     // Active orientation lock is not the document's orientation lock.
-    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    mPromise->MaybeResolveWithUndefined();
     mDocument->SetOrientationPendingPromise(nullptr);
     return NS_OK;
   }
 
   if (mOrientationLock == eScreenOrientation_None) {
     mScreenOrientation->UnlockDeviceOrientation();
-    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    mPromise->MaybeResolveWithUndefined();
     mDocument->SetOrientationPendingPromise(nullptr);
     return NS_OK;
   }
 
   ErrorResult rv;
   bool result = mScreenOrientation->LockDeviceOrientation(mOrientationLock,
                                                           mIsFullScreen, rv);
   if (NS_WARN_IF(rv.Failed())) {
@@ -196,17 +196,17 @@ ScreenOrientation::LockOrientationTask::
     mDocument->SetOrientationPendingPromise(nullptr);
     return NS_OK;
   }
 
   if (OrientationLockContains(mDocument->CurrentOrientationType()) ||
       (mOrientationLock == eScreenOrientation_Default &&
        mDocument->CurrentOrientationAngle() == 0)) {
     // Orientation lock will not cause an orientation change.
-    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    mPromise->MaybeResolveWithUndefined();
     mDocument->SetOrientationPendingPromise(nullptr);
   }
 
   return NS_OK;
 }
 
 already_AddRefed<Promise>
 ScreenOrientation::Lock(OrientationLockType aOrientation, ErrorResult& aRv)
@@ -545,17 +545,17 @@ ScreenOrientation::Notify(const hal::Scr
     return;
   }
 
   if (mType != doc->CurrentOrientationType()) {
     doc->SetCurrentOrientation(mType, mAngle);
 
     Promise* pendingPromise = doc->GetOrientationPendingPromise();
     if (pendingPromise) {
-      pendingPromise->MaybeResolve(JS::UndefinedHandleValue);
+      pendingPromise->MaybeResolveWithUndefined();
       doc->SetOrientationPendingPromise(nullptr);
     }
 
     nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(this,
       &ScreenOrientation::DispatchChangeEvent);
     rv = NS_DispatchToMainThread(runnable);
     NS_WARN_IF(NS_FAILED(rv));
   }
@@ -631,17 +631,17 @@ ScreenOrientation::VisibleEventListener:
     return rv.StealNSResult();
   }
 
   if (doc->CurrentOrientationType() != orientation->DeviceType()) {
     doc->SetCurrentOrientation(orientation->DeviceType(), orientation->DeviceAngle());
 
     Promise* pendingPromise = doc->GetOrientationPendingPromise();
     if (pendingPromise) {
-      pendingPromise->MaybeResolve(JS::UndefinedHandleValue);
+      pendingPromise->MaybeResolveWithUndefined();
       doc->SetOrientationPendingPromise(nullptr);
     }
 
     nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(orientation,
       &ScreenOrientation::DispatchChangeEvent);
     rv = NS_DispatchToMainThread(runnable);
     if (NS_WARN_IF(rv.Failed())) {
       return rv.StealNSResult();
--- a/dom/base/domerr.msg
+++ b/dom/base/domerr.msg
@@ -26,16 +26,17 @@ DOM4_MSG_DEF(SecurityError, "The operati
 DOM4_MSG_DEF(NetworkError, "A network error occurred.", NS_ERROR_DOM_NETWORK_ERR)
 DOM4_MSG_DEF(AbortError, "The operation was aborted. ", NS_ERROR_DOM_ABORT_ERR)
 DOM4_MSG_DEF(URLMismatchError, "The given URL does not match another URL.", NS_ERROR_DOM_URL_MISMATCH_ERR)
 DOM4_MSG_DEF(QuotaExceededError, "The quota has been exceeded.", NS_ERROR_DOM_QUOTA_EXCEEDED_ERR)
 DOM4_MSG_DEF(TimeoutError, "The operation timed out.", NS_ERROR_DOM_TIMEOUT_ERR)
 DOM4_MSG_DEF(InvalidNodeTypeError, "The supplied node is incorrect or has an incorrect ancestor for this operation.", NS_ERROR_DOM_INVALID_NODE_TYPE_ERR)
 DOM4_MSG_DEF(DataCloneError, "The object could not be cloned.", NS_ERROR_DOM_DATA_CLONE_ERR)
 DOM4_MSG_DEF(InvalidPointerId, "Invalid pointer id.", NS_ERROR_DOM_INVALID_POINTER_ERR)
+DOM4_MSG_DEF(NotAllowedError, "The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.", NS_ERROR_DOM_NOT_ALLOWED_ERR)
 
 /* XXX Should be JavaScript native TypeError */
 DOM4_MSG_DEF(TypeError, "The method parameter is missing or invalid.", NS_ERROR_TYPE_ERR)
 DOM4_MSG_DEF(RangeError, "The method parameter is out of valid range.", NS_ERROR_RANGE_ERR)
 
 /* StringEncoding API errors from http://wiki.whatwg.org/wiki/StringEncoding */
 DOM4_MSG_DEF(EncodingError, "The given encoding is not supported.", NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR)
 
@@ -156,8 +157,13 @@ DOM4_MSG_DEF(InvalidStateError, "Invalid
 DOM4_MSG_DEF(NotAllowedError, "User denied permission to use the Push API.", NS_ERROR_DOM_PUSH_DENIED_ERR)
 DOM4_MSG_DEF(AbortError, "Error retrieving push subscription.", NS_ERROR_DOM_PUSH_ABORT_ERR)
 DOM4_MSG_DEF(NetworkError, "Push service unreachable.", NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE)
 DOM4_MSG_DEF(InvalidAccessError, "Invalid raw ECDSA P-256 public key.", NS_ERROR_DOM_PUSH_INVALID_KEY_ERR)
 DOM4_MSG_DEF(InvalidStateError, "A subscription with a different application server key already exists.", NS_ERROR_DOM_PUSH_MISMATCHED_KEY_ERR)
 
 DOM_MSG_DEF(NS_ERROR_DOM_JS_EXCEPTION, "A callback threw an exception")
 DOM_MSG_DEF(NS_ERROR_DOM_DOMEXCEPTION, "A DOMException was thrown")
+
+/* Media errors */
+DOM4_MSG_DEF(AbortError,        "The fetching process for the media resource was aborted by the user agent at the user's request.", NS_ERROR_DOM_MEDIA_ABORT_ERR)
+DOM4_MSG_DEF(NotAllowedError,   "The play method is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.", NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR)
+DOM4_MSG_DEF(NotSupportedError, "The media resource indicated by the src attribute or assigned media provider object was not suitable.", NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR)
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -274,16 +274,17 @@ bool nsContentUtils::sIsFrameTimingPrefE
 bool nsContentUtils::sIsPerformanceTimingEnabled = false;
 bool nsContentUtils::sIsResourceTimingEnabled = false;
 bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
 bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
 bool nsContentUtils::sEncodeDecodeURLHash = false;
 bool nsContentUtils::sGettersDecodeURLHash = false;
 bool nsContentUtils::sPrivacyResistFingerprinting = false;
 bool nsContentUtils::sSendPerformanceTimingNotifications = false;
+bool nsContentUtils::sAppendLFInSerialization = false;
 
 uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
 
 uint32_t nsContentUtils::sCookiesLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
 uint32_t nsContentUtils::sCookiesBehavior = nsICookieService::BEHAVIOR_ACCEPT;
 
 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
 nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
@@ -588,16 +589,18 @@ nsContentUtils::Init()
   Preferences::AddUintVarCache(&sCookiesLifetimePolicy,
                                "network.cookie.lifetimePolicy",
                                nsICookieService::ACCEPT_NORMALLY);
 
   Preferences::AddUintVarCache(&sCookiesBehavior,
                                "network.cookie.cookieBehavior",
                                nsICookieService::BEHAVIOR_ACCEPT);
 
+  Preferences::AddBoolVarCache(&sAppendLFInSerialization,
+                               "dom.html_fragment_serialisation.appendLF");
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
   Preferences::AddBoolVarCache(&sDOMWindowDumpEnabled,
                                "browser.dom.window.dump.enabled");
 #endif
 
   Preferences::AddBoolVarCache(&sDoNotTrackEnabled,
                                "privacy.donottrackheader.enabled", false);
 
@@ -8974,35 +8977,35 @@ StartElement(Element* aContent, StringBu
     aBuilder.Append(attName);
     aBuilder.Append("=\"");
     AppendEncodedAttributeValue(attValue, aBuilder);
     aBuilder.Append("\"");
   }
 
   aBuilder.Append(">");
 
-  /*
+
   // Per HTML spec we should append one \n if the first child of
   // pre/textarea/listing is a textnode and starts with a \n.
   // But because browsers haven't traditionally had that behavior,
   // we're not changing our behavior either - yet.
-  if (aContent->IsHTMLElement()) {
+  if (nsContentUtils::AppendLFInSerialization() && aContent->IsHTMLElement()) {
     if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
         localName == nsGkAtoms::listing) {
       nsIContent* fc = aContent->GetFirstChild();
       if (fc &&
           (fc->NodeType() == nsIDOMNode::TEXT_NODE ||
            fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
         const nsTextFragment* text = fc->GetText();
         if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
           aBuilder.Append("\n");
         }
       }
     }
-  }*/
+  }
 }
 
 static inline bool
 ShouldEscape(nsIContent* aParent)
 {
   if (!aParent || !aParent->IsHTMLElement()) {
     return true;
   }
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1949,16 +1949,20 @@ public:
    *
    * @param aDoc the document for which to return a layer manager.
    * @param aAllowRetaining an outparam that states whether the returned
    * layer manager should be used for retained layers
    */
   static already_AddRefed<mozilla::layers::LayerManager>
   PersistentLayerManagerForDocument(nsIDocument *aDoc);
 
+  /* static */
+  static bool AppendLFInSerialization()
+    { return sAppendLFInSerialization; }
+
   /**
    * Determine whether a content node is focused or not,
    *
    * @param aContent the content node to check
    * @return true if the content node is focused, false otherwise.
    */
   static bool IsFocusedContent(const nsIContent *aContent);
 
@@ -2766,16 +2770,17 @@ private:
   static bool sIsResourceTimingEnabled;
   static bool sIsUserTimingLoggingEnabled;
   static bool sIsFrameTimingPrefEnabled;
   static bool sIsExperimentalAutocompleteEnabled;
   static bool sEncodeDecodeURLHash;
   static bool sGettersDecodeURLHash;
   static bool sPrivacyResistFingerprinting;
   static bool sSendPerformanceTimingNotifications;
+  static bool sAppendLFInSerialization;
   static uint32_t sCookiesLifetimePolicy;
   static uint32_t sCookiesBehavior;
 
   static nsHtml5StringParser* sHTMLFragmentParser;
   static nsIParser* sXMLFragmentParser;
   static nsIFragmentContentSink* sXMLFragmentSink;
 
   /**
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -10536,17 +10536,16 @@ nsGlobalWindow::GetSessionStorage(ErrorR
     nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell, &rv);
     if (NS_FAILED(rv)) {
       aError.Throw(rv);
       return nullptr;
     }
 
     nsCOMPtr<nsIDOMStorage> storage;
     aError = storageManager->CreateStorage(AsInner(), principal, documentURI,
-                                           IsPrivateBrowsing(),
                                            getter_AddRefs(storage));
     if (aError.Failed()) {
       return nullptr;
     }
 
     mSessionStorage = static_cast<DOMStorage*>(storage.get());
     MOZ_ASSERT(mSessionStorage);
 
@@ -10597,17 +10596,16 @@ nsGlobalWindow::GetLocalStorage(ErrorRes
 
     nsString documentURI;
     if (mDoc) {
       mDoc->GetDocumentURI(documentURI);
     }
 
     nsCOMPtr<nsIDOMStorage> storage;
     aError = storageManager->CreateStorage(AsInner(), principal, documentURI,
-                                           IsPrivateBrowsing(),
                                            getter_AddRefs(storage));
     if (aError.Failed()) {
       return nullptr;
     }
 
     mLocalStorage = static_cast<DOMStorage*>(storage.get());
     MOZ_ASSERT(mLocalStorage);
   }
@@ -11450,17 +11448,24 @@ nsGlobalWindow::Observe(nsISupports* aSu
     bool fireMozStorageChanged = false;
     nsAutoString eventType;
     eventType.AssignLiteral("storage");
     principal = GetPrincipal();
     if (!principal) {
       return NS_OK;
     }
 
-    if (changingStorage->IsPrivate() != IsPrivateBrowsing()) {
+    uint32_t privateBrowsingId = 0;
+    nsIPrincipal *storagePrincipal = changingStorage->GetPrincipal();
+    rv = storagePrincipal->GetPrivateBrowsingId(&privateBrowsingId);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    if ((privateBrowsingId > 0) != IsPrivateBrowsing()) {
       return NS_OK;
     }
 
     switch (changingStorage->GetType())
     {
     case DOMStorage::SessionStorage:
     {
       bool check = false;
--- a/dom/base/nsStyledElement.cpp
+++ b/dom/base/nsStyledElement.cpp
@@ -149,32 +149,44 @@ nsStyledElement::ReparseStyleAttribute(b
     // want to fire off mutation events or document notifications anyway
     nsresult rv = mAttrsAndChildren.SetAndSwapAttr(nsGkAtoms::style, attrValue);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   
   return NS_OK;
 }
 
+nsICSSDeclaration*
+nsStyledElement::GetExistingStyle()
+{
+  Element::nsDOMSlots* slots = GetExistingDOMSlots();
+  if (!slots) {
+    return nullptr;
+  }
+
+  return slots->mStyle;
+}
+
 void
 nsStyledElement::ParseStyleAttribute(const nsAString& aValue,
                                      nsAttrValue& aResult,
                                      bool aForceInDataDoc)
 {
   nsIDocument* doc = OwnerDoc();
   bool isNativeAnon = IsInNativeAnonymousSubtree();
 
   if (!isNativeAnon &&
       !nsStyleUtil::CSPAllowsInlineStyle(nullptr, NodePrincipal(),
                                          doc->GetDocumentURI(), 0, aValue,
                                          nullptr))
     return;
 
   if (aForceInDataDoc ||
       !doc->IsLoadedAsData() ||
+      GetExistingStyle() ||
       doc->IsStaticDocument()) {
     bool isCSS = true; // assume CSS until proven otherwise
 
     if (!isNativeAnon) {  // native anonymous content always assumes CSS
       nsAutoString styleType;
       doc->GetHeaderData(nsGkAtoms::headerContentStyleType, styleType);
       if (!styleType.IsEmpty()) {
         static const char textCssStr[] = "text/css";
--- a/dom/base/nsStyledElement.h
+++ b/dom/base/nsStyledElement.h
@@ -52,16 +52,18 @@ public:
                                              bool aNotify) override;
 
   nsICSSDeclaration* Style();
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_STYLED_ELEMENT_IID)
 
 protected:
 
+  nsICSSDeclaration* GetExistingStyle();
+
   /**
    * Parse a style attr value into a CSS rulestruct (or, if there is no
    * document, leave it as a string) and return as nsAttrValue.
    *
    * @param aValue the value to parse
    * @param aResult the resulting HTMLValue [OUT]
    */
   void ParseStyleAttribute(const nsAString& aValue,
--- a/dom/base/test/test_bug744830.html
+++ b/dom/base/test/test_bug744830.html
@@ -8,43 +8,43 @@ https://bugzilla.mozilla.org/show_bug.cg
   <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=166235">Mozilla Bug 166235</a>
 <div id="testnodes"><span>hi</span> there <!-- mon ami --></div>
 <pre id="test">
 <script type="application/javascript">
   var t = document.getElementById('testnodes');
-  is(t.innerHTML, 
+  is(t.innerHTML,
      "<span>hi</span> there <!-- mon ami -->",
      "comment nodes should be included");
 
   var PI = document.createProcessingInstruction('foo', 'bar="1.0"');
   t.appendChild(PI);
   is(t.innerHTML, '<span>hi</span> there <!-- mon ami --><?foo bar="1.0">',
     "pi nodes should be included");
 
   t.innerHTML = null;
   t.appendChild(document.createElement("textarea"));
   t.firstChild.appendChild(document.createTextNode("\nhello"));
   // This is the old behavior. Spec requires something else.
-  is(t.innerHTML, "<textarea>\nhello</textarea>",
+  is(t.innerHTML, "<textarea>\n\nhello</textarea>",
      "No extra newlines should be inserted to the textarea!");
 
   t.innerHTML = null;
   t.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "svg:svg"));
   t.firstChild.textContent = "<foo>";
   is(t.innerHTML, "<svg>&lt;foo&gt;</svg>");
 
   t.innerHTML = null;
   t.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", "math:math"));
   t.firstChild.textContent = "<foo>";
   is(t.innerHTML, "<math>&lt;foo&gt;</math>");
 
-  // Prefix is serialized if element isn't HTML/SVG/MathML  
+  // Prefix is serialized if element isn't HTML/SVG/MathML
   t.innerHTML = null;
   t.appendChild(document.createElementNS("http://www.example.org", "ex:example"));
   t.firstChild.textContent = "<foo>";
   is(t.innerHTML, "<ex:example>&lt;foo&gt;</ex:example>");
 
   t.innerHTML = null;
   t.appendChild(document.createElementNS("http://www.example.org", "example"));
   t.firstChild.textContent = "<foo>";
@@ -56,17 +56,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   t.firstChild.setAttributeNS("http://www.w3.org/1999/xlink", "href", "foo");
   is(t.innerHTML, '<example xml:lang="us-en" xlink:href="foo">&lt;foo&gt;</example>');
 
   t.firstChild.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", "http://foo");
   is(t.innerHTML, '<example xml:lang="us-en" xlink:href="foo" xmlns="http://foo">&lt;foo&gt;</example>');
 
   t.firstChild.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:bar", "http://bar");
   is(t.innerHTML, '<example xml:lang="us-en" xlink:href="foo" xmlns="http://foo" xmlns:bar="http://bar">&lt;foo&gt;</example>');
-  
+
   t.firstChild.setAttributeNS("http://www.helloworldns.org", "hello:world", "!");
   is(t.innerHTML, '<example xml:lang="us-en" xlink:href="foo" xmlns="http://foo" xmlns:bar="http://bar" hello:world="!">&lt;foo&gt;</example>');
 
   t.firstChild.setAttribute("foo", '-"&\xA0-');
   is(t.innerHTML, '<example xml:lang="us-en" xlink:href="foo" xmlns="http://foo" xmlns:bar="http://bar" hello:world="!" foo="-&quot;&amp;&nbsp;-">&lt;foo&gt;</example>');
 
   t.innerHTML = null;
   t.appendChild(document.createElement("div"));
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -228,18 +228,17 @@ DOMInterfaces = {
     'headerFile': 'DOMCameraCapabilities.h'
 },
 
 'CanvasRenderingContext2D': {
     'implicitJSContext': [
         'createImageData', 'getImageData'
     ],
     'binaryNames': {
-        'mozImageSmoothingEnabled': 'imageSmoothingEnabled',
-        'mozFillRule': 'fillRule'
+        'mozImageSmoothingEnabled': 'imageSmoothingEnabled'
     }
 },
 
 'CaretPosition' : {
     'nativeType': 'nsDOMCaretPosition',
 },
 
 'CharacterData': {
--- a/dom/bluetooth/common/webapi/BluetoothGattCharacteristic.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothGattCharacteristic.cpp
@@ -401,17 +401,17 @@ BluetoothGattCharacteristic::WriteValue(
   NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
 
   aValue.ComputeLengthAndData();
 
   if (mAttRole == ATT_SERVER_ROLE) {
     mValue.Clear();
     mValue.AppendElements(aValue.Data(), aValue.Length());
 
-    promise->MaybeResolve(JS::UndefinedHandleValue);
+    promise->MaybeResolveWithUndefined();
     return promise.forget();
   }
 
   BT_ENSURE_TRUE_REJECT(mProperties &
                           (GATT_CHAR_PROP_BIT_WRITE_NO_RESPONSE |
                            GATT_CHAR_PROP_BIT_WRITE |
                            GATT_CHAR_PROP_BIT_SIGNED_WRITE),
                         promise,
--- a/dom/bluetooth/common/webapi/BluetoothGattDescriptor.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothGattDescriptor.cpp
@@ -276,17 +276,17 @@ BluetoothGattDescriptor::WriteValue(
                         NS_ERROR_DOM_OPERATION_ERR);
 
   aValue.ComputeLengthAndData();
 
   if (mAttRole == ATT_SERVER_ROLE) {
     mValue.Clear();
     mValue.AppendElements(aValue.Data(), aValue.Length());
 
-    promise->MaybeResolve(JS::UndefinedHandleValue);
+    promise->MaybeResolveWithUndefined();
     return promise.forget();
   }
 
   nsTArray<uint8_t> value;
   value.AppendElements(aValue.Data(), aValue.Length());
 
   BluetoothService* bs = BluetoothService::Get();
   BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
--- a/dom/bluetooth/common/webapi/BluetoothGattServer.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothGattServer.cpp
@@ -545,17 +545,17 @@ BluetoothGattServer::StopAdvertising(Err
   }
 
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
 
   BT_ENSURE_TRUE_REJECT(mValid, promise, NS_ERROR_NOT_AVAILABLE);
 
   if (mAdvertisingAppUuid.IsCleared()) {
-    promise->MaybeResolve(JS::UndefinedHandleValue);
+    promise->MaybeResolveWithUndefined();
     return promise.forget();
   }
 
   BluetoothService* bs = BluetoothService::Get();
   BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
 
   bs->StopAdvertisingInternal(mAdvertisingAppUuid,
                               new StopAdvertisingTask(this, promise));
@@ -835,17 +835,17 @@ protected:
   virtual ~AddServiceTaskQueue()
   { }
 
 private:
   void OnSuccessFired() override
   {
     mServer->mPendingService = nullptr;
     mServer->mServices.AppendElement(mService);
-    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    mPromise->MaybeResolveWithUndefined();
   }
 
   void OnErrorFired() override
   {
     BluetoothService* bs = BluetoothService::Get();
     BT_ENSURE_TRUE_REJECT_VOID(bs, mPromise, NS_ERROR_NOT_AVAILABLE);
 
     BluetoothUuid appUuid;
--- a/dom/bluetooth/common/webapi/BluetoothGattService.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothGattService.cpp
@@ -248,12 +248,12 @@ BluetoothGattService::AddIncludedService
 
   /* The included service itself should be actively acting with the Bluetooth
    * backend. Otherwise, that service cannot be included by any services. */
   BT_ENSURE_TRUE_REJECT(aIncludedService.mActive,
                         promise,
                         NS_ERROR_UNEXPECTED);
 
   mIncludedServices.AppendElement(&aIncludedService);
-  promise->MaybeResolve(JS::UndefinedHandleValue);
+  promise->MaybeResolveWithUndefined();
 
   return promise.forget();
 }
--- a/dom/cache/Cache.cpp
+++ b/dom/cache/Cache.cpp
@@ -581,17 +581,17 @@ Cache::AddAll(const GlobalObject& aGloba
 
   // If there is no work to do, then resolve immediately
   if (aRequestList.IsEmpty()) {
     RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
     if (NS_WARN_IF(!promise)) {
       return nullptr;
     }
 
-    promise->MaybeResolve(JS::UndefinedHandleValue);
+    promise->MaybeResolveWithUndefined();
     return promise.forget();
   }
 
   AutoTArray<RefPtr<Promise>, 256> fetchList;
   fetchList.SetCapacity(aRequestList.Length());
 
   // Begin fetching each request in parallel.  For now, if an error occurs just
   // abandon our previous fetch calls.  In theory we could cancel them in the
--- a/dom/cache/CacheOpChild.cpp
+++ b/dom/cache/CacheOpChild.cpp
@@ -123,17 +123,17 @@ CacheOpChild::Recv__delete__(const Error
     }
     case CacheOpResult::TCacheMatchAllResult:
     {
       HandleResponseList(aResult.get_CacheMatchAllResult().responseList());
       break;
     }
     case CacheOpResult::TCachePutAllResult:
     {
-      mPromise->MaybeResolve(JS::UndefinedHandleValue);
+      mPromise->MaybeResolveWithUndefined();
       break;
     }
     case CacheOpResult::TCacheDeleteResult:
     {
       mPromise->MaybeResolve(aResult.get_CacheDeleteResult().success());
       break;
     }
     case CacheOpResult::TCacheKeysResult:
@@ -207,17 +207,17 @@ CacheOpChild::GetIPCManager()
 {
   MOZ_CRASH("CacheOpChild does not implement TypeUtils::GetIPCManager()");
 }
 
 void
 CacheOpChild::HandleResponse(const CacheResponseOrVoid& aResponseOrVoid)
 {
   if (aResponseOrVoid.type() == CacheResponseOrVoid::Tvoid_t) {
-    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    mPromise->MaybeResolveWithUndefined();
     return;
   }
 
   const CacheResponse& cacheResponse = aResponseOrVoid.get_CacheResponse();
 
   AddWorkerHolderToStreamChild(cacheResponse, GetWorkerHolder());
   RefPtr<Response> response = ToResponse(cacheResponse);
 
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -1095,17 +1095,17 @@ nsDOMCameraControl::ReleaseHardware(Erro
 
   RefPtr<Promise> promise = CreatePromise(aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (!mCameraControl) {
     // Always succeed if the camera instance is already closed.
-    promise->MaybeResolve(JS::UndefinedHandleValue);
+    promise->MaybeResolveWithUndefined();
     return promise.forget();
   }
 
   aRv = mCameraControl->Stop();
   if (aRv.Failed()) {
     return nullptr;
   }
 
@@ -1286,17 +1286,17 @@ nsDOMCameraControl::OnHardwareStateChang
       }
       break;
 
     case CameraControlListener::kHardwareClosed:
       DOM_CAMERA_LOGI("DOM OnHardwareStateChange: closed\n");
       if (!mSetInitialConfig) {
         RefPtr<Promise> promise = mReleasePromise.forget();
         if (promise) {
-          promise->MaybeResolve(JS::UndefinedHandleValue);
+          promise->MaybeResolveWithUndefined();
         }
 
         CameraClosedEventInit eventInit;
         switch (aReason) {
           case NS_OK:
             eventInit.mReason = NS_LITERAL_STRING("HardwareReleased");
             break;
 
@@ -1404,17 +1404,17 @@ nsDOMCameraControl::OnRecorderStateChang
 
   nsString state;
 
   switch (aState) {
     case CameraControlListener::kRecorderStarted:
       {
         RefPtr<Promise> promise = mStartRecordingPromise.forget();
         if (promise) {
-          promise->MaybeResolve(JS::UndefinedHandleValue);
+          promise->MaybeResolveWithUndefined();
         }
 
         state = NS_LITERAL_STRING("Started");
       }
       break;
 
     case CameraControlListener::kRecorderStopped:
       if (mOptions.mCreatePoster) {
@@ -1635,17 +1635,17 @@ nsDOMCameraControl::OnUserError(CameraCo
       break;
 
     case CameraControlListener::kInStopCamera:
       promise = mReleasePromise.forget();
       if (aError == NS_ERROR_NOT_INITIALIZED) {
         // This value indicates that the hardware is already closed; which for
         // kInStopCamera, is not actually an error.
         if (promise) {
-          promise->MaybeResolve(JS::UndefinedHandleValue);
+          promise->MaybeResolveWithUndefined();
         }
 
         return;
       }
       break;
 
     case CameraControlListener::kInSetConfiguration:
       if (mSetInitialConfig && mCameraControl) {
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1516,29 +1516,46 @@ CanvasRenderingContext2D::EnsureTarget(c
   MOZ_ASSERT(mRenderingMode != RenderingMode::DefaultBackendMode);
 
   RenderingMode mode = (aRenderingMode == RenderingMode::DefaultBackendMode) ? mRenderingMode : aRenderingMode;
 
   if (mTarget && mode == mRenderingMode) {
     return mRenderingMode;
   }
 
+  // If the next drawing command covers the entire canvas, we can skip copying
+  // from the previous frame and/or clearing the canvas.
+  gfx::Rect canvasRect(0, 0, mWidth, mHeight);
+  bool canDiscardContent = aCoveredRect &&
+    CurrentState().transform.TransformBounds(*aCoveredRect).Contains(canvasRect);
+
+  // If a clip is active we don't know for sure that the next drawing command
+  // will really cover the entire canvas.
+  for (const auto& style : mStyleStack) {
+    if (!canDiscardContent) {
+      break;
+    }
+    for (const auto& clipOrTransform : style.clipsAndTransforms) {
+      if (clipOrTransform.IsClip()) {
+        canDiscardContent = false;
+        break;
+      }
+    }
+  }
+
   ScheduleStableStateCallback();
 
   // we'll do a few extra things at the end of this method if we changed the
   // buffer provider.
   RefPtr<PersistentBufferProvider> oldBufferProvider = mBufferProvider;
 
   if (mBufferProvider && mode == mRenderingMode) {
-    gfx::Rect rect(0, 0, mWidth, mHeight);
-    if (aCoveredRect && CurrentState().transform.TransformBounds(*aCoveredRect).Contains(rect)) {
-      mTarget = mBufferProvider->BorrowDrawTarget(IntRect());
-    } else {
-      mTarget = mBufferProvider->BorrowDrawTarget(IntRect(0, 0, mWidth, mHeight));
-    }
+    auto persistedRect = canDiscardContent ? IntRect()
+                                           : IntRect(0, 0, mWidth, mHeight);
+    mTarget = mBufferProvider->BorrowDrawTarget(persistedRect);
 
     mode = mRenderingMode;
   }
 
   mIsSkiaGL = false;
 
    // Check that the dimensions are sane
   IntSize size(mWidth, mHeight);
@@ -1609,43 +1626,48 @@ CanvasRenderingContext2D::EnsureTarget(c
       }
 
       gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
       JSContext* context = nsContentUtils::GetCurrentJSContext();
       if (context) {
         JS_updateMallocCounter(context, mWidth * mHeight * 4);
       }
 
-      mTarget->ClearRect(gfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
+      mTarget->ClearRect(canvasRect);
 
       // Force a full layer transaction since we didn't have a layer before
       // and now we might need one.
       if (mCanvasElement) {
         mCanvasElement->InvalidateCanvas();
       }
       // Calling Redraw() tells our invalidation machinery that the entire
       // canvas is already invalid, which can speed up future drawing.
       Redraw();
     }
 
+    // Restore clips and transform.
+    mTarget->SetTransform(Matrix());
+
     if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
       // Cairo doesn't play well with huge clips. When given a very big clip it
       // will try to allocate big mask surface without taking the target
       // size into account which can cause OOM. See bug 1034593.
       // This limits the clip extents to the size of the canvas.
       // A fix in Cairo would probably be preferable, but requires somewhat
       // invasive changes.
-      mTarget->PushClipRect(gfx::Rect(Point(0, 0), Size(mWidth, mHeight)));
+      mTarget->PushClipRect(canvasRect);
     }
 
-    // Restore clip and transform.
-    for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
-      mTarget->SetTransform(mStyleStack[i].transform);
-      for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
-        mTarget->PushClip(mStyleStack[i].clipsPushed[c]);
+    for (const auto& style : mStyleStack) {
+      for (const auto& clipOrTransform : style.clipsAndTransforms) {
+        if (clipOrTransform.IsClip()) {
+          mTarget->PushClip(clipOrTransform.clip);
+        } else {
+          mTarget->SetTransform(clipOrTransform.transform);
+        }
       }
     }
   } else {
     EnsureErrorTarget();
     mTarget = sErrorTarget;
     mBufferProvider = nullptr;
   }
 
@@ -1747,19 +1769,21 @@ CanvasRenderingContext2D::ClearTarget(bo
   }
 }
 
 void
 CanvasRenderingContext2D::ReturnTarget()
 {
   if (mTarget && mBufferProvider && mTarget != sErrorTarget) {
     CurrentState().transform = mTarget->GetTransform();
-    for (uint32_t i = 0; i < mStyleStack.Length(); i++) {
-      for (uint32_t c = 0; c < mStyleStack[i].clipsPushed.Length(); c++) {
-        mTarget->PopClip();
+    for (const auto& style : mStyleStack) {
+      for (const auto& clipOrTransform : style.clipsAndTransforms) {
+        if (clipOrTransform.IsClip()) {
+          mTarget->PopClip();
+        }
       }
     }
 
     if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
       // With the cairo backend we pushed an extra clip rect which we have to
       // balance out here. See the comment in EnsureDrawTarget.
       mTarget->PopClip();
     }
@@ -1929,18 +1953,20 @@ CanvasRenderingContext2D::Save()
 void
 CanvasRenderingContext2D::Restore()
 {
   if (mStyleStack.Length() - 1 == 0)
     return;
 
   TransformWillUpdate();
 
-  for (uint32_t i = 0; i < CurrentState().clipsPushed.Length(); i++) {
-    mTarget->PopClip();
+  for (const auto& clipOrTransform : CurrentState().clipsAndTransforms) {
+    if (clipOrTransform.IsClip()) {
+      mTarget->PopClip();
+    }
   }
 
   mStyleStack.RemoveElementAt(mStyleStack.Length() - 1);
 
   mTarget->SetTransform(CurrentState().transform);
 }
 
 //
@@ -1953,91 +1979,98 @@ CanvasRenderingContext2D::Scale(double a
   TransformWillUpdate();
   if (!IsTargetValid()) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   Matrix newMatrix = mTarget->GetTransform();
   newMatrix.PreScale(aX, aY);
-  if (!newMatrix.IsFinite()) {
-    return;
-  }
-  mTarget->SetTransform(newMatrix);
+
+  SetTransformInternal(newMatrix);
 }
 
 void
 CanvasRenderingContext2D::Rotate(double aAngle, ErrorResult& aError)
 {
   TransformWillUpdate();
   if (!IsTargetValid()) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   Matrix newMatrix = Matrix::Rotation(aAngle) * mTarget->GetTransform();
-  if (!newMatrix.IsFinite()) {
-    return;
-  }
-  mTarget->SetTransform(newMatrix);
+
+  SetTransformInternal(newMatrix);
 }
 
 void
 CanvasRenderingContext2D::Translate(double aX, double aY, ErrorResult& aError)
 {
   TransformWillUpdate();
   if (!IsTargetValid()) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   Matrix newMatrix = mTarget->GetTransform();
   newMatrix.PreTranslate(aX, aY);
-  if (!newMatrix.IsFinite()) {
-    return;
-  }
-  mTarget->SetTransform(newMatrix);
+
+  SetTransformInternal(newMatrix);
 }
 
 void
 CanvasRenderingContext2D::Transform(double aM11, double aM12, double aM21,
                                     double aM22, double aDx, double aDy,
                                     ErrorResult& aError)
 {
   TransformWillUpdate();
   if (!IsTargetValid()) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   Matrix newMatrix(aM11, aM12, aM21, aM22, aDx, aDy);
   newMatrix *= mTarget->GetTransform();
-  if (!newMatrix.IsFinite()) {
-    return;
-  }
-  mTarget->SetTransform(newMatrix);
+
+  SetTransformInternal(newMatrix);
 }
 
 void
 CanvasRenderingContext2D::SetTransform(double aM11, double aM12,
                                        double aM21, double aM22,
                                        double aDx, double aDy,
                                        ErrorResult& aError)
 {
   TransformWillUpdate();
   if (!IsTargetValid()) {
     aError.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  Matrix matrix(aM11, aM12, aM21, aM22, aDx, aDy);
-  if (!matrix.IsFinite()) {
+  SetTransformInternal(Matrix(aM11, aM12, aM21, aM22, aDx, aDy));
+}
+
+void
+CanvasRenderingContext2D::SetTransformInternal(const Matrix& aTransform)
+{
+  if (!aTransform.IsFinite()) {
     return;
   }
-  mTarget->SetTransform(matrix);
+
+  // Save the transform in the clip stack to be able to replay clips properly.
+  auto& clipsAndTransforms = CurrentState().clipsAndTransforms;
+  if (clipsAndTransforms.IsEmpty() || clipsAndTransforms.LastElement().IsClip()) {
+    clipsAndTransforms.AppendElement(ClipState(aTransform));
+  } else {
+    // If the last item is a transform we can replace it instead of appending
+    // a new item.
+    clipsAndTransforms.LastElement().transform = aTransform;
+  }
+  mTarget->SetTransform(aTransform);
 }
 
 void
 CanvasRenderingContext2D::ResetTransform(ErrorResult& aError)
 {
   SetTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, aError);
 }
 
@@ -2803,17 +2836,17 @@ CanvasRenderingContext2D::FillRect(doubl
           aH = 0;
         }
       }
     }
   }
 
   CompositionOp op = UsedOperation();
   bool discardContent = PatternIsOpaque(Style::FILL)
-    && (op == CompositionOp::OP_OVER || op == CompositionOp::OP_DEST_OUT);
+    && (op == CompositionOp::OP_OVER || op == CompositionOp::OP_SOURCE);
 
   const gfx::Rect fillRect(aX, aY, aW, aH);
   EnsureTarget(discardContent ? &fillRect : nullptr);
 
   gfx::Rect bounds;
   if (NeedToCalculateBounds()) {
     bounds = mTarget->GetTransform().TransformBounds(fillRect);
   }
@@ -3097,32 +3130,32 @@ CanvasRenderingContext2D::Clip(const Can
 {
   EnsureUserSpacePath(aWinding);
 
   if (!mPath) {
     return;
   }
 
   mTarget->PushClip(mPath);
-  CurrentState().clipsPushed.AppendElement(mPath);
+  CurrentState().clipsAndTransforms.AppendElement(ClipState(mPath));
 }
 
 void
 CanvasRenderingContext2D::Clip(const CanvasPath& aPath, const CanvasWindingRule& aWinding)
 {
   EnsureTarget();
 
   RefPtr<gfx::Path> gfxpath = aPath.GetPath(aWinding, mTarget);
 
   if (!gfxpath) {
     return;
   }
 
   mTarget->PushClip(gfxpath);
-  CurrentState().clipsPushed.AppendElement(gfxpath);
+  CurrentState().clipsAndTransforms.AppendElement(ClipState(gfxpath));
 }
 
 void
 CanvasRenderingContext2D::ArcTo(double aX1, double aY1, double aX2,
                                 double aY2, double aRadius,
                                 ErrorResult& aError)
 {
   if (aRadius < 0) {
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -569,16 +569,18 @@ protected:
   /**
     * The number of living nsCanvasRenderingContexts.  When this goes down to
     * 0, we free the premultiply and unpremultiply tables, if they exist.
     */
   static uint32_t sNumLivingContexts;
 
   static mozilla::gfx::DrawTarget* sErrorTarget;
 
+  void SetTransformInternal(const mozilla::gfx::Matrix& aTransform);
+
   // Some helpers.  Doesn't modify a color on failure.
   void SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& aValue,
                          Style aWhichStyle);
   void SetStyleFromString(const nsAString& aStr, Style aWhichStyle);
 
   void SetStyleFromGradient(CanvasGradient& aGradient, Style aWhichStyle)
   {
     CurrentState().SetGradientStyle(aWhichStyle, &aGradient);
@@ -912,16 +914,32 @@ protected:
                              float aX,
                              float aY,
                              const Optional<double>& aMaxWidth,
                              TextDrawOperation aOp,
                              float* aWidth);
 
   bool CheckSizeForSkiaGL(mozilla::gfx::IntSize aSize);
 
+  // A clip or a transform, recorded and restored in order.
+  struct ClipState {
+    explicit ClipState(mozilla::gfx::Path* aClip)
+      : clip(aClip)
+    {}
+
+    explicit ClipState(const mozilla::gfx::Matrix& aTransform)
+      : transform(aTransform)
+    {}
+
+    bool IsClip() const { return !!clip; }
+
+    RefPtr<mozilla::gfx::Path> clip;
+    mozilla::gfx::Matrix transform;
+  };
+
   // state stack handling
   class ContextState {
   public:
     ContextState() : textAlign(TextAlign::START),
                      textBaseline(TextBaseline::ALPHABETIC),
                      shadowColor(0),
                      lineWidth(1.0f),
                      miterLimit(10.0f),
@@ -1004,17 +1022,17 @@ protected:
       return (int32_t)floor(ShadowBlurSigma() * GAUSSIAN_SCALE_FACTOR + 0.5);
     }
 
     mozilla::gfx::Float ShadowBlurSigma() const
     {
       return std::min(SIGMA_MAX, shadowBlur / 2.0f);
     }
 
-    nsTArray<RefPtr<mozilla::gfx::Path> > clipsPushed;
+    nsTArray<ClipState> clipsAndTransforms;
 
     RefPtr<gfxFontGroup> fontGroup;
     nsCOMPtr<nsIAtom> fontLanguage;
     nsFont fontFont;
 
     EnumeratedArray<Style, Style::MAX, RefPtr<CanvasGradient>> gradientStyles;
     EnumeratedArray<Style, Style::MAX, RefPtr<CanvasPattern>> patternStyles;
     EnumeratedArray<Style, Style::MAX, nscolor> colorStyles;
--- a/dom/canvas/crashtests/crashtests.list
+++ b/dom/canvas/crashtests/crashtests.list
@@ -11,17 +11,16 @@ load 746813-1.html
 load 743499-negative-size.html
 skip-if(Android||B2G) load 745818-large-source.html # Bug XXX - Crashes Android/B2G mid-run w/o a stack
 load 767337-1.html
 skip-if(Android||B2G) load 780392-1.html # bug 833371 for B2G
 skip-if(Android||B2G) skip-if(gtkWidget&&isDebugBuild) load 789933-1.html # bug 833371 for B2G, bug 1155252 for linux
 load 794463-1.html
 load 802926-1.html
 load 896047-1.html
-load 896047-2.html
 load 916128-1.html
 load 934939-1.html
 load 1099143-1.html
 load 1161277-1.html
 load 1183363.html
 load 1190705.html
 load 1223740-1.html
 load 1225381-1.html
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -198,17 +198,16 @@ skip-if = toolkit != 'cocoa'
 skip-if = toolkit != 'cocoa'
 # This test is bogus according to the spec; see bug 407107
 [test_2d.path.rect.zero.6.html]
 disabled = bug 407107
 [test_2d.strokeRect.zero.5.html]
 [test_bitmaprenderer.html]
 [test_bug232227.html]
 [test_bug613794.html]
-[test_bug753758.html]
 [test_bug764125.html]
 [test_bug856472.html]
 [test_bug866575.html]
 skip-if = (toolkit == 'gonk' && debug) #bug 1045153
 [test_bug902651.html]
 [test_bug1215072.html]
 subsuite = gpu
 [test_canvas.html]
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -114,16 +114,18 @@ HTMLFormElement::HTMLFormElement(already
     mDefaultSubmitElement(nullptr),
     mFirstSubmitInElements(nullptr),
     mFirstSubmitNotInElements(nullptr),
     mImageNameLookupTable(FORM_CONTROL_LIST_HASHTABLE_LENGTH),
     mPastNameLookupTable(FORM_CONTROL_LIST_HASHTABLE_LENGTH),
     mInvalidElementsCount(0),
     mEverTriedInvalidSubmit(false)
 {
+  // We start out valid.
+  AddStatesSilently(NS_EVENT_STATE_VALID);
 }
 
 HTMLFormElement::~HTMLFormElement()
 {
   if (mControls) {
     mControls->DropFormReference();
   }
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -1463,17 +1463,17 @@ HTMLInputElement::AfterSetAttr(int32_t a
       UpdateValueMissingValidityState();
 
       // This *has* to be called *after* validity has changed.
       if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
         UpdateBarredFromConstraintValidation();
       }
     } else if (MaxLengthApplies() && aName == nsGkAtoms::maxlength) {
       UpdateTooLongValidityState();
-    } else if (aName == nsGkAtoms::pattern) {
+    } else if (aName == nsGkAtoms::pattern && !mParserCreating) {
       UpdatePatternMismatchValidityState();
     } else if (aName == nsGkAtoms::multiple) {
       UpdateTypeMismatchValidityState();
     } else if (aName == nsGkAtoms::max) {
       UpdateHasRange();
       if (mType == NS_FORM_INPUT_RANGE) {
         // The value may need to change when @max changes since the value may
         // have been invalid and can now change to a valid value, or vice
@@ -5607,56 +5607,64 @@ HTMLInputElement::GetTextLength(int32_t*
 }
 
 void
 HTMLInputElement::SetSelectionRange(int32_t aSelectionStart,
                                     int32_t aSelectionEnd,
                                     const Optional<nsAString>& aDirection,
                                     ErrorResult& aRv)
 {
+  if (!SupportsTextSelection()) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  nsresult rv = SetSelectionRange(aSelectionStart, aSelectionEnd,
+    aDirection.WasPassed() ? aDirection.Value() : NullString());
+
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
+}
+
+NS_IMETHODIMP
+HTMLInputElement::SetSelectionRange(int32_t aSelectionStart,
+                                    int32_t aSelectionEnd,
+                                    const nsAString& aDirection)
+{
+  nsresult rv = NS_OK;
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
   nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
   if (textControlFrame) {
     // Default to forward, even if not specified.
     // Note that we don't currently support directionless selections, so
     // "none" is treated like "forward".
     nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eForward;
-    if (aDirection.WasPassed() && aDirection.Value().EqualsLiteral("backward")) {
+    if (!aDirection.IsEmpty() && aDirection.EqualsLiteral("backward")) {
       dir = nsITextControlFrame::eBackward;
     }
 
-    aRv = textControlFrame->SetSelectionRange(aSelectionStart, aSelectionEnd, dir);
-    if (!aRv.Failed()) {
-      aRv = textControlFrame->ScrollSelectionIntoView();
+    rv = textControlFrame->SetSelectionRange(aSelectionStart, aSelectionEnd, dir);
+    if (NS_SUCCEEDED(rv)) {
+      rv = textControlFrame->ScrollSelectionIntoView();
       RefPtr<AsyncEventDispatcher> asyncDispatcher =
         new AsyncEventDispatcher(this, NS_LITERAL_STRING("select"),
                                  true, false);
       asyncDispatcher->PostDOMEvent();
     }
   }
-}
-
-NS_IMETHODIMP
-HTMLInputElement::SetSelectionRange(int32_t aSelectionStart,
-                                    int32_t aSelectionEnd,
-                                    const nsAString& aDirection)
-{
-  ErrorResult rv;
-  Optional<nsAString> direction;
-  direction = &aDirection;
-
-  SetSelectionRange(aSelectionStart, aSelectionEnd, direction, rv);
-  return rv.StealNSResult();
+
+  return rv;
 }
 
 void
 HTMLInputElement::SetRangeText(const nsAString& aReplacement, ErrorResult& aRv)
 {
-  if (!SupportsSetRangeText()) {
-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+  if (!SupportsTextSelection()) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   int32_t start, end;
   aRv = GetSelectionRange(&start, &end);
   if (aRv.Failed()) {
     nsTextEditorState* state = GetEditorState();
     if (state && state->IsSelectionCached()) {
@@ -5671,18 +5679,18 @@ HTMLInputElement::SetRangeText(const nsA
 }
 
 void
 HTMLInputElement::SetRangeText(const nsAString& aReplacement, uint32_t aStart,
                                uint32_t aEnd, const SelectionMode& aSelectMode,
                                ErrorResult& aRv, int32_t aSelectionStart,
                                int32_t aSelectionEnd)
 {
-  if (!SupportsSetRangeText()) {
-    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+  if (!SupportsTextSelection()) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   if (aStart > aEnd) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
@@ -5758,141 +5766,185 @@ HTMLInputElement::SetRangeText(const nsA
     default:
       MOZ_CRASH("Unknown mode!");
   }
 
   Optional<nsAString> direction;
   SetSelectionRange(aSelectionStart, aSelectionEnd, direction, aRv);
 }
 
-int32_t
+Nullable<int32_t>
 HTMLInputElement::GetSelectionStart(ErrorResult& aRv)
 {
-  int32_t selEnd, selStart;
-  aRv = GetSelectionRange(&selStart, &selEnd);
-
-  if (aRv.Failed()) {
-    nsTextEditorState* state = GetEditorState();
-    if (state && state->IsSelectionCached()) {
-      aRv = NS_OK;
-      return state->GetSelectionProperties().GetStart();
-    }
-  }
-
-  return selStart;
+  if (!SupportsTextSelection()) {
+    return Nullable<int32_t>();
+  }
+
+  int32_t selStart;
+  nsresult rv = GetSelectionStart(&selStart);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
+
+  return Nullable<int32_t>(selStart);
 }
 
 NS_IMETHODIMP
 HTMLInputElement::GetSelectionStart(int32_t* aSelectionStart)
 {
   NS_ENSURE_ARG_POINTER(aSelectionStart);
 
-  ErrorResult rv;
-  *aSelectionStart = GetSelectionStart(rv);
-  return rv.StealNSResult();
+  int32_t selEnd, selStart;
+  nsresult rv = GetSelectionRange(&selStart, &selEnd);
+
+  if (NS_FAILED(rv)) {
+    nsTextEditorState* state = GetEditorState();
+    if (state && state->IsSelectionCached()) {
+      *aSelectionStart = state->GetSelectionProperties().GetStart();
+      return NS_OK;
+    }
+    return rv;
+  }
+
+  *aSelectionStart = selStart;
+  return NS_OK;
 }
 
 void
-HTMLInputElement::SetSelectionStart(int32_t aSelectionStart, ErrorResult& aRv)
-{
+HTMLInputElement::SetSelectionStart(const Nullable<int32_t>& aSelectionStart,
+                                    ErrorResult& aRv)
+{
+  if (!SupportsTextSelection()) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  int32_t selStart = 0;
+  if (!aSelectionStart.IsNull()) {
+    selStart = aSelectionStart.Value();
+  }
+
   nsTextEditorState* state = GetEditorState();
   if (state && state->IsSelectionCached()) {
-    state->GetSelectionProperties().SetStart(aSelectionStart);
+    state->GetSelectionProperties().SetStart(selStart);
     return;
   }
 
   nsAutoString direction;
   aRv = GetSelectionDirection(direction);
   if (aRv.Failed()) {
     return;
   }
 
   int32_t start, end;
   aRv = GetSelectionRange(&start, &end);
   if (aRv.Failed()) {
     return;
   }
 
-  start = aSelectionStart;
+  start = selStart;
   if (end < start) {
     end = start;
   }
 
   aRv = SetSelectionRange(start, end, direction);
 }
 
 NS_IMETHODIMP
 HTMLInputElement::SetSelectionStart(int32_t aSelectionStart)
 {
   ErrorResult rv;
-  SetSelectionStart(aSelectionStart, rv);
+  Nullable<int32_t> selStart(aSelectionStart);
+  SetSelectionStart(selStart, rv);
   return rv.StealNSResult();
 }
 
-int32_t
+Nullable<int32_t>
 HTMLInputElement::GetSelectionEnd(ErrorResult& aRv)
 {
-  int32_t selStart, selEnd;
-  aRv = GetSelectionRange(&selStart, &selEnd);
-
-  if (aRv.Failed()) {
-    nsTextEditorState* state = GetEditorState();
-    if (state && state->IsSelectionCached()) {
-      aRv = NS_OK;
-      return state->GetSelectionProperties().GetEnd();
-    }
-  }
-
-  return selEnd;
+  if (!SupportsTextSelection()) {
+    return Nullable<int32_t>();
+  }
+
+  int32_t selEnd;
+  nsresult rv = GetSelectionEnd(&selEnd);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
+
+  return Nullable<int32_t>(selEnd);
 }
 
 NS_IMETHODIMP
 HTMLInputElement::GetSelectionEnd(int32_t* aSelectionEnd)
 {
   NS_ENSURE_ARG_POINTER(aSelectionEnd);
 
-  ErrorResult rv;
-  *aSelectionEnd = GetSelectionEnd(rv);
-  return rv.StealNSResult();
+  int32_t selEnd, selStart;
+  nsresult rv = GetSelectionRange(&selStart, &selEnd);
+
+  if (NS_FAILED(rv)) {
+    nsTextEditorState* state = GetEditorState();
+    if (state && state->IsSelectionCached()) {
+      *aSelectionEnd = state->GetSelectionProperties().GetEnd();
+      return NS_OK;
+    }
+    return rv;
+  }
+
+  *aSelectionEnd = selEnd;
+  return NS_OK;
 }
 
 void
-HTMLInputElement::SetSelectionEnd(int32_t aSelectionEnd, ErrorResult& aRv)
-{
+HTMLInputElement::SetSelectionEnd(const Nullable<int32_t>& aSelectionEnd,
+                                  ErrorResult& aRv)
+{
+  if (!SupportsTextSelection()) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  int32_t selEnd = 0;
+  if (!aSelectionEnd.IsNull()) {
+    selEnd = aSelectionEnd.Value();
+  }
+
   nsTextEditorState* state = GetEditorState();
   if (state && state->IsSelectionCached()) {
-    state->GetSelectionProperties().SetEnd(aSelectionEnd);
+    state->GetSelectionProperties().SetEnd(selEnd);
     return;
   }
 
   nsAutoString direction;
   aRv = GetSelectionDirection(direction);
   if (aRv.Failed()) {
     return;
   }
 
   int32_t start, end;
   aRv = GetSelectionRange(&start, &end);
   if (aRv.Failed()) {
     return;
   }
 
-  end = aSelectionEnd;
+  end = selEnd;
   if (start > end) {
     start = end;
   }
 
   aRv = SetSelectionRange(start, end, direction);
 }
 
 NS_IMETHODIMP
 HTMLInputElement::SetSelectionEnd(int32_t aSelectionEnd)
 {
   ErrorResult rv;
-  SetSelectionEnd(aSelectionEnd, rv);
+  Nullable<int32_t> selEnd(aSelectionEnd);
+  SetSelectionEnd(selEnd, rv);
   return rv.StealNSResult();
 }
 
 NS_IMETHODIMP
 HTMLInputElement::GetFiles(nsIDOMFileList** aFileList)
 {
   RefPtr<FileList> list = GetFiles();
   list.forget(aFileList);
@@ -5924,16 +5976,21 @@ DirectionToName(nsITextControlFrame::Sel
   } else {
     NS_NOTREACHED("Invalid SelectionDirection value");
   }
 }
 
 void
 HTMLInputElement::GetSelectionDirection(nsAString& aDirection, ErrorResult& aRv)
 {
+  if (!SupportsTextSelection()) {
+    aDirection.SetIsVoid(true);
+    return;
+  }
+
   nsresult rv = NS_ERROR_FAILURE;
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
   nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
   if (textControlFrame) {
     nsITextControlFrame::SelectionDirection dir;
     rv = textControlFrame->GetSelectionRange(nullptr, nullptr, &dir);
     if (NS_SUCCEEDED(rv)) {
       DirectionToName(dir, aDirection);
@@ -5959,16 +6016,21 @@ HTMLInputElement::GetSelectionDirection(
   ErrorResult rv;
   GetSelectionDirection(aDirection, rv);
   return rv.StealNSResult();
 }
 
 void
 HTMLInputElement::SetSelectionDirection(const nsAString& aDirection, ErrorResult& aRv)
 {
+  if (!SupportsTextSelection()) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
   nsTextEditorState* state = GetEditorState();
   if (state && state->IsSelectionCached()) {
     nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eNone;
     if (aDirection.EqualsLiteral("forward")) {
       dir = nsITextControlFrame::eForward;
     } else if (aDirection.EqualsLiteral("backward")) {
       dir = nsITextControlFrame::eBackward;
     }
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -683,21 +683,21 @@ public:
   Decimal GetStep() const;
 
   void GetValidationMessage(nsAString& aValidationMessage, ErrorResult& aRv);
 
   // XPCOM GetCustomVisibility() is OK
 
   // XPCOM Select() is OK
 
-  int32_t GetSelectionStart(ErrorResult& aRv);
-  void SetSelectionStart(int32_t aValue, ErrorResult& aRv);
+  Nullable<int32_t> GetSelectionStart(ErrorResult& aRv);
+  void SetSelectionStart(const Nullable<int32_t>& aValue, ErrorResult& aRv);
 
-  int32_t GetSelectionEnd(ErrorResult& aRv);
-  void SetSelectionEnd(int32_t aValue, ErrorResult& aRv);
+  Nullable<int32_t> GetSelectionEnd(ErrorResult& aRv);
+  void SetSelectionEnd(const Nullable<int32_t>& aValue, ErrorResult& aRv);
 
   void GetSelectionDirection(nsAString& aValue, ErrorResult& aRv);
   void SetSelectionDirection(const nsAString& aValue, ErrorResult& aRv);
 
   void SetSelectionRange(int32_t aStart, int32_t aEnd,
                          const Optional< nsAString >& direction,
                          ErrorResult& aRv);
 
@@ -1476,19 +1476,19 @@ private:
    * Returns true if this input's type will fire a DOM "change" event when it
    * loses focus if its value has changed since it gained focus.
    */
   bool MayFireChangeOnBlur() const {
     return MayFireChangeOnBlur(mType);
   }
 
   /**
-   * Returns true if setRangeText can be called on element
+   * Returns true if selection methods can be called on element
    */
-  bool SupportsSetRangeText() const {
+  bool SupportsTextSelection() const {
     return mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_SEARCH ||
            mType == NS_FORM_INPUT_URL || mType == NS_FORM_INPUT_TEL ||
            mType == NS_FORM_INPUT_PASSWORD || mType == NS_FORM_INPUT_NUMBER;
   }
 
   static bool MayFireChangeOnBlur(uint8_t aType) {
     return IsSingleLineTextControl(false, aType) ||
            aType == NS_FORM_INPUT_RANGE ||
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -5731,17 +5731,17 @@ HTMLMediaElement::SetMediaKeys(mozilla::
     promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
                          NS_LITERAL_CSTRING("EME not supported on non-MSE streams"));
     return promise.forget();
   }
 
   // 1. If mediaKeys and the mediaKeys attribute are the same object,
   // return a resolved promise.
   if (mMediaKeys == aMediaKeys) {
-    promise->MaybeResolve(JS::UndefinedHandleValue);
+    promise->MaybeResolveWithUndefined();
     return promise.forget();
   }
 
   // Note: Our attaching code is synchronous, so we can skip the following steps.
 
   // 2. If this object's attaching media keys value is true, return a
   // promise rejected with a new DOMException whose name is InvalidStateError.
   // 3. Let this object's attaching media keys value be true.
@@ -5817,17 +5817,17 @@ HTMLMediaElement::SetMediaKeys(mozilla::
   }
 
   // 5.4 Set the mediaKeys attribute to mediaKeys.
   mMediaKeys = aMediaKeys;
 
   // 5.5 Let this object's attaching media keys value be false.
 
   // 5.6 Resolve promise.
-  promise->MaybeResolve(JS::UndefinedHandleValue);
+  promise->MaybeResolveWithUndefined();
 
   // 6. Return promise.
   return promise.forget();
 }
 
 EventHandlerNonNull*
 HTMLMediaElement::GetOnencrypted()
 {
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -656,127 +656,151 @@ HTMLTextAreaElement::GetTextLength(int32
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::GetSelectionStart(int32_t *aSelectionStart)
 {
   NS_ENSURE_ARG_POINTER(aSelectionStart);
 
   ErrorResult error;
-  *aSelectionStart = GetSelectionStart(error);
+  Nullable<uint32_t> selStart(GetSelectionStart(error));
+  if (error.Failed()) {
+    return error.StealNSResult();
+  }
+
+  *aSelectionStart = int32_t(selStart.Value());
   return error.StealNSResult();
 }
 
-uint32_t
+Nullable<uint32_t>
 HTMLTextAreaElement::GetSelectionStart(ErrorResult& aError)
 {
   int32_t selStart, selEnd;
   nsresult rv = GetSelectionRange(&selStart, &selEnd);
 
   if (NS_FAILED(rv) && mState.IsSelectionCached()) {
-    return mState.GetSelectionProperties().GetStart();
+    return Nullable<uint32_t>(mState.GetSelectionProperties().GetStart());
   }
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
   }
-  return selStart;
+  return Nullable<uint32_t>(selStart);
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::SetSelectionStart(int32_t aSelectionStart)
 {
   ErrorResult error;
-  SetSelectionStart(aSelectionStart, error);
+  Nullable<uint32_t> selStart(aSelectionStart);
+  SetSelectionStart(selStart, error);
   return error.StealNSResult();
 }
 
 void
-HTMLTextAreaElement::SetSelectionStart(uint32_t aSelectionStart, ErrorResult& aError)
+HTMLTextAreaElement::SetSelectionStart(const Nullable<uint32_t>& aSelectionStart,
+                                       ErrorResult& aError)
 {
+  int32_t selStart = 0;
+  if (!aSelectionStart.IsNull()) {
+    selStart = aSelectionStart.Value();
+  }
+
   if (mState.IsSelectionCached()) {
-    mState.GetSelectionProperties().SetStart(aSelectionStart);
+    mState.GetSelectionProperties().SetStart(selStart);
     return;
   }
 
   nsAutoString direction;
   nsresult rv = GetSelectionDirection(direction);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
     return;
   }
   int32_t start, end;
   rv = GetSelectionRange(&start, &end);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
     return;
   }
-  start = aSelectionStart;
+  start = selStart;
   if (end < start) {
     end = start;
   }
   rv = SetSelectionRange(start, end, direction);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
   }
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::GetSelectionEnd(int32_t *aSelectionEnd)
 {
   NS_ENSURE_ARG_POINTER(aSelectionEnd);
 
   ErrorResult error;
-  *aSelectionEnd = GetSelectionEnd(error);
-  return error.StealNSResult();
+  Nullable<uint32_t> selEnd(GetSelectionEnd(error));
+  if (error.Failed()) {
+    return error.StealNSResult();
+  }
+
+  *aSelectionEnd = int32_t(selEnd.Value());
+  return NS_OK;
 }
 
-uint32_t
+Nullable<uint32_t>
 HTMLTextAreaElement::GetSelectionEnd(ErrorResult& aError)
 {
   int32_t selStart, selEnd;
   nsresult rv = GetSelectionRange(&selStart, &selEnd);
 
   if (NS_FAILED(rv) && mState.IsSelectionCached()) {
-    return mState.GetSelectionProperties().GetEnd();
+    return Nullable<uint32_t>(mState.GetSelectionProperties().GetEnd());
   }
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
   }
-  return selEnd;
+  return Nullable<uint32_t>(selEnd);
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::SetSelectionEnd(int32_t aSelectionEnd)
 {
   ErrorResult error;
-  SetSelectionEnd(aSelectionEnd, error);
+  Nullable<uint32_t> selEnd(aSelectionEnd);
+  SetSelectionEnd(selEnd, error);
   return error.StealNSResult();
 }
 
 void
-HTMLTextAreaElement::SetSelectionEnd(uint32_t aSelectionEnd, ErrorResult& aError)
+HTMLTextAreaElement::SetSelectionEnd(const Nullable<uint32_t>& aSelectionEnd,
+                                     ErrorResult& aError)
 {
+  int32_t selEnd = 0;
+  if (!aSelectionEnd.IsNull()) {
+    selEnd = aSelectionEnd.Value();
+  }
+
   if (mState.IsSelectionCached()) {
-    mState.GetSelectionProperties().SetEnd(aSelectionEnd);
+    mState.GetSelectionProperties().SetEnd(selEnd);
     return;
   }
 
   nsAutoString direction;
   nsresult rv = GetSelectionDirection(direction);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
     return;
   }
   int32_t start, end;
   rv = GetSelectionRange(&start, &end);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
     return;
   }
-  end = aSelectionEnd;
+  end = selEnd;
   if (start > end) {
     start = end;
   }
   rv = SetSelectionRange(start, end, direction);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
   }
 }
@@ -843,17 +867,18 @@ NS_IMETHODIMP
 HTMLTextAreaElement::SetSelectionDirection(const nsAString& aDirection)
 {
   ErrorResult error;
   SetSelectionDirection(aDirection, error);
   return error.StealNSResult();
 }
 
 void
-HTMLTextAreaElement::SetSelectionDirection(const nsAString& aDirection, ErrorResult& aError)
+HTMLTextAreaElement::SetSelectionDirection(const nsAString& aDirection,
+                                           ErrorResult& aError)
 {
   if (mState.IsSelectionCached()) {
     nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eNone;
     if (aDirection.EqualsLiteral("forward")) {
       dir = nsITextControlFrame::eForward;
     } else if (aDirection.EqualsLiteral("backward")) {
       dir = nsITextControlFrame::eBackward;
     }
--- a/dom/html/HTMLTextAreaElement.h
+++ b/dom/html/HTMLTextAreaElement.h
@@ -264,20 +264,20 @@ public:
   // nsIConstraintValidation::WillValidate is fine.
   // nsIConstraintValidation::Validity() is fine.
   // nsIConstraintValidation::GetValidationMessage() is fine.
   // nsIConstraintValidation::CheckValidity() is fine.
   using nsIConstraintValidation::CheckValidity;
   using nsIConstraintValidation::ReportValidity;
   // nsIConstraintValidation::SetCustomValidity() is fine.
   // XPCOM Select is fine
-  uint32_t GetSelectionStart(ErrorResult& aError);
-  void SetSelectionStart(uint32_t aSelectionStart, ErrorResult& aError);
-  uint32_t GetSelectionEnd(ErrorResult& aError);
-  void SetSelectionEnd(uint32_t aSelectionEnd, ErrorResult& aError);
+  Nullable<uint32_t> GetSelectionStart(ErrorResult& aError);
+  void SetSelectionStart(const Nullable<uint32_t>& aSelectionStart, ErrorResult& aError);
+  Nullable<uint32_t> GetSelectionEnd(ErrorResult& aError);
+  void SetSelectionEnd(const Nullable<uint32_t>& aSelectionEnd, ErrorResult& aError);
   void GetSelectionDirection(nsAString& aDirection, ErrorResult& aError);
   void SetSelectionDirection(const nsAString& aDirection, ErrorResult& aError);
   void SetSelectionRange(uint32_t aSelectionStart, uint32_t aSelectionEnd, const Optional<nsAString>& aDirecton, ErrorResult& aError);
   nsIControllers* GetControllers(ErrorResult& aError);
   nsIEditor* GetEditor()
   {
     return mState.GetEditor();
   }
--- a/dom/html/test/forms/test_set_range_text.html
+++ b/dom/html/test/forms/test_set_range_text.html
@@ -56,33 +56,33 @@ https://bugzilla.mozilla.org/show_bug.cg
       msg = "input_" + NonSupportedTypes[i];
       elem = document.getElementById(msg);
       elem.focus();
       try {
         elem.setRangeText("abc");
       } catch (ex) {
        opThrows = true;
       }
-      ok(opThrows, msg + " should throw NotSupportedError");
+      ok(opThrows, msg + " should throw InvalidStateError");
     }
 
     var numOfSelectCalls = 0, expectedNumOfSelectCalls = 0;
     //Supported types should not throw
     for (i = 0; i < SupportedTypes.length; ++i) {
       opThrows = false;
       msg = "input_" + SupportedTypes[i];
       elem = document.getElementById(msg);
       elem.focus();
       try {
         elem.setRangeText("abc");
         expectedNumOfSelectCalls += 1;
       } catch (ex) {
         opThrows = true;
       }
-      is(opThrows, false, msg + " should not throw NotSupportedError");
+      is(opThrows, false, msg + " should not throw InvalidStateError");
 
       elem.addEventListener("select", function (aEvent) {
         ok(true, "select event should be fired for " + aEvent.target.id);
         if (++numOfSelectCalls == expectedNumOfSelectCalls) {
           SimpleTest.finish();
         } else if (numOfSelectCalls > expectedNumOfSelectCalls) {
           ok(false, "Too many select events were fired");
         }
--- a/dom/interfaces/storage/nsIDOMStorageManager.idl
+++ b/dom/interfaces/storage/nsIDOMStorageManager.idl
@@ -34,34 +34,32 @@ interface nsIDOMStorageManager : nsISupp
    *    Principal to bound storage to.
    * @param aDocumentURI
    *    URL of the demanding document, used for DOM storage event only.
    * @param aPrivate
    *    Whether the demanding document is running in Private Browsing mode or not.
    */
   nsIDOMStorage createStorage(in mozIDOMWindow aWindow,
                               in nsIPrincipal aPrincipal,
-                              in DOMString aDocumentURI,
-                              [optional] in bool aPrivate);
+                              in DOMString aDocumentURI);
   /**
    * Returns instance of DOM storage object for given principal.
    * If there is no storage managed for the scope, then null is returned and
    * no object is created.  Otherwise, an object (new) for the existing storage
    * scope is returned.
    *
    * @param aWindow
    *    The parent window.
    * @param aPrincipal
    *    Principal to bound storage to.
    * @param aPrivate
    *    Whether the demanding document is running in Private Browsing mode or not.
    */
   nsIDOMStorage getStorage(in mozIDOMWindow aWindow,
-                           in nsIPrincipal aPrincipal,
-                           [optional] in bool aPrivate);
+                           in nsIPrincipal aPrincipal);
 
   /**
    * Clones given storage into this storage manager.
    *
    * @param aStorageToCloneFrom
    *    The storage to copy all items from into this manager.  Manager will then
    *    return a new and independent object that contains snapshot of data from
    *    the moment this method was called.  Modification to this new object will
@@ -95,11 +93,10 @@ interface nsIDOMStorageManager : nsISupp
    *
    * Currently just forwards to the createStorage method of this
    * interface.
    *
    * Extension developers are strongly encouraged to use getStorage
    * or createStorage method instead.
    */
   nsIDOMStorage getLocalStorageForPrincipal(in nsIPrincipal aPrincipal,
-                                            in DOMString aDocumentURI,
-                                            [optional] in bool aPrivate);
+                                            in DOMString aDocumentURI);
 };
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -839,17 +839,17 @@ MediaDecoder::Seek(double aTime, SeekTar
 
 void
 MediaDecoder::AsyncResolveSeekDOMPromiseIfExists()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mSeekDOMPromise) {
     RefPtr<dom::Promise> promise = mSeekDOMPromise;
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
-      promise->MaybeResolve(JS::UndefinedHandleValue);
+      promise->MaybeResolveWithUndefined();
     });
     AbstractThread::MainThread()->Dispatch(r.forget());
     mSeekDOMPromise = nullptr;
   }
 }
 
 void
 MediaDecoder::AsyncRejectSeekDOMPromiseIfExists()
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -34,16 +34,21 @@ using mozilla::CheckedUint32;
 // dependent on other changes which we don't want to wait for. We plan to
 // remove this file in the near future.
 
 
 // This belongs in xpcom/monitor/Monitor.h, once we've made
 // mozilla::Monitor non-reentrant.
 namespace mozilla {
 
+// EME Key System String.
+static const char* const kEMEKeySystemClearkey = "org.w3.clearkey";
+static const char* const kEMEKeySystemWidevine = "com.widevine.alpha";
+static const char* const kEMEKeySystemPrimetime = "com.adobe.primetime";
+
 /**
  * ReentrantMonitorConditionallyEnter
  *
  * Enters the supplied monitor only if the conditional value |aEnter| is true.
  * E.g. Used to allow unmonitored read access on the decode thread,
  * and monitored access on all other threads.
  */
 class MOZ_STACK_CLASS ReentrantMonitorConditionallyEnter
--- a/dom/media/eme/EMEUtils.h
+++ b/dom/media/eme/EMEUtils.h
@@ -2,26 +2,23 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef EME_LOG_H_
 #define EME_LOG_H_
 
+#include "VideoUtils.h"
 #include "mozilla/Logging.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 
-static const char* const kEMEKeySystemClearkey = "org.w3.clearkey";
-static const char* const kEMEKeySystemWidevine = "com.widevine.alpha";
-static const char* const kEMEKeySystemPrimetime = "com.adobe.primetime";
-
 namespace dom {
 class ArrayBufferViewOrArrayBuffer;
 }
 
 #ifndef EME_LOG
   LogModule* GetEMELog();
   #define EME_LOG(...) MOZ_LOG(GetEMELog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
   #define EME_LOG_ENABLED() MOZ_LOG_TEST(GetEMELog(), mozilla::LogLevel::Debug)
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -333,17 +333,17 @@ MediaKeySession::Close(ErrorResult& aRv)
     EME_LOG("MediaKeySession[%p,''] Close() called before sessionId set by CDM", this);
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
       NS_LITERAL_CSTRING("MediaKeySession.Close() called before sessionId set by CDM"));
     return promise.forget();
   }
   if (IsClosed() || !mKeys->GetCDMProxy()) {
     EME_LOG("MediaKeySession[%p,'%s'] Close() already closed",
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
-    promise->MaybeResolve(JS::UndefinedHandleValue);
+    promise->MaybeResolveWithUndefined();
     return promise.forget();
   }
   PromiseId pid = mKeys->StorePromise(promise);
   mKeys->GetCDMProxy()->CloseSession(mSessionId, pid);
 
   EME_LOG("MediaKeySession[%p,'%s'] Close() sent to CDM, promiseId=%d",
           this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);
 
@@ -356,17 +356,17 @@ MediaKeySession::OnClosed()
   if (IsClosed()) {
     return;
   }
   EME_LOG("MediaKeySession[%p,'%s'] session close operation complete.",
           this, NS_ConvertUTF16toUTF8(mSessionId).get());
   mIsClosed = true;
   mKeys->OnSessionClosed(this);
   mKeys = nullptr;
-  mClosed->MaybeResolve(JS::UndefinedHandleValue);
+  mClosed->MaybeResolveWithUndefined();
 }
 
 bool
 MediaKeySession::IsClosed() const
 {
   return mIsClosed;
 }
 
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -278,17 +278,17 @@ MediaKeys::ResolvePromise(PromiseId aId)
                            NS_LITERAL_CSTRING("CDM LoadSession() returned a different session ID than requested"));
       mPendingSessions.Remove(aId);
       return;
     }
     mPendingSessions.Remove(aId);
     mKeySessions.Put(session->GetSessionId(), session);
     promise->MaybeResolve(session);
   } else {
-    promise->MaybeResolve(JS::UndefinedHandleValue);
+    promise->MaybeResolveWithUndefined();
   }
   MOZ_ASSERT(!mPromises.Contains(aId));
 }
 
 class MediaKeysGMPCrashHelper : public GMPCrashHelper
 {
 public:
   explicit MediaKeysGMPCrashHelper(MediaKeys* aMediaKeys)
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -18,16 +18,17 @@
 #include "nsIObserverService.h"
 #include "GMPTimerParent.h"
 #include "runnable_utils.h"
 #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
 #include "mozilla/SandboxInfo.h"
 #endif
 #include "GMPContentParent.h"
 #include "MediaPrefs.h"
+#include "VideoUtils.h"
 
 #include "mozilla/dom/CrashReporterParent.h"
 using mozilla::dom::CrashReporterParent;
 using mozilla::ipc::GeckoChildProcessHost;
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsPrintfCString.h"
 using CrashReporter::AnnotationTable;
--- a/dom/media/gmp/moz.build
+++ b/dom/media/gmp/moz.build
@@ -30,18 +30,16 @@ EXPORTS += [
     'gmp-api/gmp-video-frame.h',
     'gmp-api/gmp-video-host.h',
     'gmp-api/gmp-video-plane.h',
     'GMPAudioDecoderChild.h',
     'GMPAudioDecoderParent.h',
     'GMPAudioDecoderProxy.h',
     'GMPAudioHost.h',
     'GMPCallbackBase.h',
-    'GMPCDMCallbackProxy.h',
-    'GMPCDMProxy.h',
     'GMPChild.h',
     'GMPContentChild.h',
     'GMPContentParent.h',
     'GMPCrashHelperHolder.h',
     'GMPDecryptorChild.h',
     'GMPDecryptorParent.h',
     'GMPDecryptorProxy.h',
     'GMPEncryptedBufferDataImpl.h',
@@ -68,32 +66,36 @@ EXPORTS += [
     'GMPVideoEncoderChild.h',
     'GMPVideoEncoderParent.h',
     'GMPVideoEncoderProxy.h',
     'GMPVideoHost.h',
     'GMPVideoi420FrameImpl.h',
     'GMPVideoPlaneImpl.h',
 ]
 
+if CONFIG['MOZ_EME']:
+    EXPORTS += [
+        'GMPCDMCallbackProxy.h',
+        'GMPCDMProxy.h',
+    ]
+
 # We link GMPLoader into xul on B2G/Fennec as its code does not need to be
 # covered by a DRM vendor's voucher.
 if CONFIG['OS_TARGET'] == 'Android':
     SOURCES += [
       'GMPLoader.cpp',
     ]
     USE_LIBS += [
         'rlz',
     ]
 
 UNIFIED_SOURCES += [
     'GMPAudioDecoderChild.cpp',
     'GMPAudioDecoderParent.cpp',
     'GMPAudioHost.cpp',
-    'GMPCDMCallbackProxy.cpp',
-    'GMPCDMProxy.cpp',
     'GMPChild.cpp',
     'GMPContentChild.cpp',
     'GMPContentParent.cpp',
     'GMPDecryptorChild.cpp',
     'GMPDecryptorParent.cpp',
     'GMPDiskStorage.cpp',
     'GMPEncryptedBufferDataImpl.cpp',
     'GMPMemoryStorage.cpp',
@@ -115,16 +117,22 @@ UNIFIED_SOURCES += [
     'GMPVideoEncodedFrameImpl.cpp',
     'GMPVideoEncoderChild.cpp',
     'GMPVideoEncoderParent.cpp',
     'GMPVideoHost.cpp',
     'GMPVideoi420FrameImpl.cpp',
     'GMPVideoPlaneImpl.cpp',
 ]
 
+if CONFIG['MOZ_EME']:
+    UNIFIED_SOURCES += [
+        'GMPCDMCallbackProxy.cpp',
+        'GMPCDMProxy.cpp',
+    ]
+
 DIRS += [
     'rlz',
     'widevine-adapter',
 ]
 
 IPDL_SOURCES += [
   'GMPTypes.ipdlh',
   'PGMP.ipdl',
--- a/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
@@ -1,23 +1,23 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WidevineAdapter.h"
 #include "content_decryption_module.h"
+#include "VideoUtils.h"
 #include "WidevineDecryptor.h"
 #include "WidevineUtils.h"
 #include "WidevineVideoDecoder.h"
 #include "gmp-api/gmp-entrypoints.h"
 #include "gmp-api/gmp-decryption.h"
 #include "gmp-api/gmp-video-codec.h"
 #include "gmp-api/gmp-platform.h"
-#include "mozilla/EMEUtils.h"
 #include "mozilla/StaticPtr.h"
 
 static const GMPPlatformAPI* sPlatform = nullptr;
 
 namespace mozilla {
 
 StaticRefPtr<CDMWrapper> sCDMWrapper;
 
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -7,16 +7,17 @@
 #include "nsError.h"
 #include "MediaDecoderStateMachine.h"
 #include "AbstractMediaDecoder.h"
 #include "OggDemuxer.h"
 #include "OggCodecState.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SharedThreadPool.h"
+#include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "MediaDataDemuxer.h"
 #include "nsAutoRef.h"
 #include "XiphExtradata.h"
 
 #include <algorithm>
 
 extern mozilla::LazyLogModule gMediaDemuxerLog;
--- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp
@@ -5,19 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GMPDecoderModule.h"
 #include "DecoderDoctorDiagnostics.h"
 #include "GMPAudioDecoder.h"
 #include "GMPVideoDecoder.h"
 #include "MediaDataDecoderProxy.h"
 #include "MediaPrefs.h"
+#include "VideoUtils.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "nsServiceManagerUtils.h"
-#include "mozilla/EMEUtils.h"
 #include "mozilla/StaticMutex.h"
 #include "gmp-audio-decode.h"
 #include "gmp-video-decode.h"
 #include "MP4Decoder.h"
 #include "VPXDecoder.h"
 #ifdef XP_WIN
 #include "WMFDecoderModule.h"
 #endif
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -866,17 +866,17 @@ AudioContext::OnStateChanged(void* aProm
 
   MOZ_ASSERT(
     mIsOffline || aPromise || aNewState == AudioContextState::Running,
     "We should have a promise here if this is a real-time AudioContext."
     "Or this is the first time we switch to \"running\".");
 
   if (aPromise) {
     Promise* promise = reinterpret_cast<Promise*>(aPromise);
-    promise->MaybeResolve(JS::UndefinedHandleValue);
+    promise->MaybeResolveWithUndefined();
     DebugOnly<bool> rv = mPromiseGripArray.RemoveElement(promise);
     MOZ_ASSERT(rv, "Promise wasn't in the grip array?");
   }
 
   if (mAudioContextState != aNewState) {
     RefPtr<OnStateChangeTask> onStateChangeTask =
       new OnStateChangeTask(this);
     NS_DispatchToMainThread(onStateChangeTask);
--- a/dom/mobilemessage/MobileMessageCallback.cpp
+++ b/dom/mobilemessage/MobileMessageCallback.cpp
@@ -359,17 +359,17 @@ MobileMessageCallback::NotifyGetSmscAddr
   const nsAString& errorStr = ConvertErrorCodeToErrorString(aError);
   mPromise->MaybeRejectBrokenly(errorStr);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MobileMessageCallback::NotifySetSmscAddress()
 {
-  mPromise->MaybeResolve(JS::UndefinedHandleValue);
+  mPromise->MaybeResolveWithUndefined();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MobileMessageCallback::NotifySetSmscAddressFailed(int32_t aError)
 {
   const nsAString& errorStr = ConvertErrorCodeToErrorString(aError);
   mPromise->MaybeRejectBrokenly(errorStr);
--- a/dom/network/UDPSocket.cpp
+++ b/dom/network/UDPSocket.cpp
@@ -193,17 +193,17 @@ UDPSocket::CloseWithReason(nsresult aRea
 
   if (mSocketChild) {
     mSocketChild->Close();
     mSocketChild = nullptr;
   }
 
   if (mClosed) {
     if (NS_SUCCEEDED(aReason)) {
-      mClosed->MaybeResolve(JS::UndefinedHandleValue);
+      mClosed->MaybeResolveWithUndefined();
     } else {
       mClosed->MaybeReject(aReason);
     }
   }
 
   mPendingMcastCommands.Clear();
 }
 
@@ -464,17 +464,17 @@ UDPSocket::InitLocal(const nsAString& aL
   }
 
   mReadyState = SocketReadyState::Open;
   rv = DoPendingMcastCommand();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  mOpened->MaybeResolve(JS::UndefinedHandleValue);
+  mOpened->MaybeResolveWithUndefined();
 
   return NS_OK;
 }
 
 nsresult
 UDPSocket::InitRemote(const nsAString& aLocalAddress,
                       const uint16_t& aLocalPort)
 {
@@ -731,17 +731,17 @@ UDPSocket::CallListenerOpened()
   mReadyState = SocketReadyState::Open;
   nsresult rv = DoPendingMcastCommand();
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     CloseWithReason(rv);
     return NS_OK;
   }
 
-  mOpened->MaybeResolve(JS::UndefinedHandleValue);
+  mOpened->MaybeResolveWithUndefined();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UDPSocket::CallListenerConnected()
 {
   // This shouldn't be called here.
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -2705,17 +2705,17 @@ Notification::ShowPersistentNotification
     result.ThrowTypeError<MSG_NOTIFICATION_PERMISSION_DENIED>();
     p->MaybeReject(result);
     return p.forget();
   }
 
   // "Otherwise, resolve promise with undefined."
   // The Notification may still not be shown due to other errors, but the spec
   // is not concerned with those.
-  p->MaybeResolve(JS::UndefinedHandleValue);
+  p->MaybeResolveWithUndefined();
 
   RefPtr<Notification> notification =
     CreateAndShow(aCx, aGlobal, aTitle, aOptions, aScope, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return p.forget();
--- a/dom/presentation/PresentationService.cpp
+++ b/dom/presentation/PresentationService.cpp
@@ -168,19 +168,19 @@ PresentationDeviceRequest::CreateSession
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return info->ReplyError(NS_ERROR_DOM_OPERATION_ERR);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PresentationDeviceRequest::Cancel()
+PresentationDeviceRequest::Cancel(nsresult aReason)
 {
-  return mCallback->NotifyError(NS_ERROR_DOM_ABORT_ERR);
+  return mCallback->NotifyError(aReason);
 }
 
 /*
  * Implementation of PresentationService
  */
 
 NS_IMPL_ISUPPORTS_INHERITED(PresentationService,
                             PresentationServiceBase,
--- a/dom/presentation/interfaces/nsIPresentationDevicePrompt.idl
+++ b/dom/presentation/interfaces/nsIPresentationDevicePrompt.idl
@@ -24,19 +24,20 @@ interface nsIPresentationDeviceRequest :
 
   /*
    * Callback after selecting a device
    * @param device The selected device.
    */
   void select(in nsIPresentationDevice device);
 
   /*
-    Callback after selection failed or canceled by user.
+   * Callback after selection failed or canceled by user.
+   * @param reason The error cause for canceling this request.
    */
-  void cancel();
+  void cancel(in nsresult reason);
 };
 
 /*
  * UI prompt for device selection.
  */
 [scriptable, uuid(ac1a7e44-de86-454f-a9f1-276de2539831)]
 interface nsIPresentationDevicePrompt : nsISupports
 {
--- a/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
+++ b/dom/presentation/tests/mochitest/PresentationSessionChromeScript.js
@@ -204,18 +204,18 @@ const mockedDevicePrompt = {
   },
   promptDeviceSelection: function(request) {
     this._request = request;
     sendAsyncMessage('device-prompt');
   },
   simulateSelect: function() {
     this._request.select(mockedDevice);
   },
-  simulateCancel: function() {
-    this._request.cancel();
+  simulateCancel: function(result) {
+    this._request.cancel(result);
   }
 };
 
 const mockedSessionTransport = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransport,
                                          Ci.nsIPresentationTCPSessionTransportBuilder,
                                          Ci.nsIPresentationDataChannelSessionTransportBuilder,
                                          Ci.nsIPresentationControlChannelListener,
@@ -388,18 +388,18 @@ addMessageListener('trigger-device-add',
                       .getService(Ci.nsIPresentationDeviceManager);
   deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener).addDevice(mockedDevice);
 });
 
 addMessageListener('trigger-device-prompt-select', function() {
   mockedDevicePrompt.simulateSelect();
 });
 
-addMessageListener('trigger-device-prompt-cancel', function() {
-  mockedDevicePrompt.simulateCancel();
+addMessageListener('trigger-device-prompt-cancel', function(result) {
+  mockedDevicePrompt.simulateCancel(result);
 });
 
 addMessageListener('trigger-incoming-session-request', function(url) {
   var deviceManager = Cc['@mozilla.org/presentation-device/manager;1']
                       .getService(Ci.nsIPresentationDeviceManager);
   deviceManager.QueryInterface(Ci.nsIPresentationDeviceListener)
 	       .onSessionRequest(mockedDevice, url, sessionId, mockedControlChannel);
 });
--- a/dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html
@@ -51,26 +51,47 @@ function testCreateRequestWithEmptyURL()
   });
 }
 
 function testStartConnectionCancelPrompt() {
   return new Promise(function(aResolve, aReject) {
     gScript.addMessageListener('device-prompt', function devicePromptHandler() {
       gScript.removeMessageListener('device-prompt', devicePromptHandler);
       info("Device prompt is triggered.");
-      gScript.sendAsyncMessage('trigger-device-prompt-cancel');
+      gScript.sendAsyncMessage('trigger-device-prompt-cancel', SpecialPowers.Cr.NS_ERROR_DOM_NOT_ALLOWED_ERR);
     });
 
     request.start().then(
       function(aConnection) {
         ok(false, "|start| shouldn't succeed in this case.");
         aReject();
       },
       function(aError) {
-        is(aError.name, "AbortError", "AbortError is expected when the prompt is canceled.");
+        is(aError.name, "NotAllowedError", "NotAllowedError is expected when the prompt is canceled.");
+        aResolve();
+      }
+    );
+  });
+}
+
+function testStartConnectionNoDevice() {
+  return new Promise(function(aResolve, aReject) {
+    gScript.addMessageListener('device-prompt', function devicePromptHandler() {
+      gScript.removeMessageListener('device-prompt', devicePromptHandler);
+      info("Device prompt is triggered.");
+      gScript.sendAsyncMessage('trigger-device-prompt-cancel', SpecialPowers.Cr.NS_ERROR_DOM_NOT_FOUND_ERR);
+    });
+
+    request.start().then(
+      function(aConnection) {
+        ok(false, "|start| shouldn't succeed in this case.");
+        aReject();
+      },
+      function(aError) {
+        is(aError.name, "NotFoundError", "NotFoundError is expected when no available device.");
         aResolve();
       }
     );
   });
 }
 
 function testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit() {
   return new Promise(function(aResolve, aReject) {
@@ -356,16 +377,17 @@ function teardown() {
 }
 
 function runTests() {
   ok(window.PresentationRequest, "PresentationRequest should be available.");
 
   testCreateRequestWithEmptyURL().
   then(setup).
   then(testStartConnectionCancelPrompt).
+  then(testStartConnectionNoDevice).
   then(testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportInit).
   then(testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportInit).
   then(testStartConnectionUnexpectedControlChannelCloseBeforeDataTransportReady).
   then(testStartConnectionUnexpectedControlChannelCloseNoReasonBeforeDataTransportReady).
   then(testStartConnectionUnexpectedDataTransportClose).
   then(teardown);
 }
 
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -921,16 +921,24 @@ Promise::MaybeReject(JSContext* aCx,
   NS_ASSERT_OWNINGTHREAD(Promise);
 
   MaybeRejectInternal(aCx, aValue);
 }
 
 #endif // SPIDERMONKEY_PROMISE
 
 void
+Promise::MaybeResolveWithUndefined()
+{
+  NS_ASSERT_OWNINGTHREAD(Promise);
+
+  MaybeResolve(JS::UndefinedHandleValue);
+}
+
+void
 Promise::MaybeReject(const RefPtr<MediaStreamError>& aArg) {
   NS_ASSERT_OWNINGTHREAD(Promise);
 
   MaybeSomething(aArg, &Promise::MaybeReject);
 }
 
 void
 Promise::MaybeRejectWithNull()
@@ -943,17 +951,16 @@ Promise::MaybeRejectWithNull()
 void
 Promise::MaybeRejectWithUndefined()
 {
   NS_ASSERT_OWNINGTHREAD(Promise);
 
   MaybeSomething(JS::UndefinedHandleValue, &Promise::MaybeReject);
 }
 
-
 #ifdef SPIDERMONKEY_PROMISE
 void
 Promise::ReportRejectedPromise(JSContext* aCx, JS::HandleObject aPromise)
 {
   MOZ_ASSERT(!js::IsWrapper(aPromise));
 
   MOZ_ASSERT(JS::GetPromiseState(aPromise) == JS::PromiseState::Rejected);
 
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -135,16 +135,18 @@ public:
   // Most DOM objects are handled already.  To add a new type T, add a
   // ToJSValue overload in ToJSValue.h.
   // aArg is a const reference so we can pass rvalues like integer constants
   template <typename T>
   void MaybeResolve(const T& aArg) {
     MaybeSomething(aArg, &Promise::MaybeResolve);
   }
 
+  void MaybeResolveWithUndefined();
+
   inline void MaybeReject(nsresult aArg) {
     MOZ_ASSERT(NS_FAILED(aArg));
     MaybeSomething(aArg, &Promise::MaybeReject);
   }
 
   inline void MaybeReject(ErrorResult& aArg) {
     MOZ_ASSERT(aArg.Failed());
     MaybeSomething(aArg, &Promise::MaybeReject);
--- a/dom/storage/DOMStorage.cpp
+++ b/dom/storage/DOMStorage.cpp
@@ -39,24 +39,22 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIDOMStorage)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END
 
 DOMStorage::DOMStorage(nsPIDOMWindowInner* aWindow,
                        DOMStorageManager* aManager,
                        DOMStorageCache* aCache,
                        const nsAString& aDocumentURI,
-                       nsIPrincipal* aPrincipal,
-                       bool aIsPrivate)
+                       nsIPrincipal* aPrincipal)
 : mWindow(aWindow)
 , mManager(aManager)
 , mCache(aCache)
 , mDocumentURI(aDocumentURI)
 , mPrincipal(aPrincipal)
-, mIsPrivate(aIsPrivate)
 , mIsSessionOnly(false)
 {
   mCache->Preload();
 }
 
 DOMStorage::~DOMStorage()
 {
   mCache->KeepAlive();
@@ -226,18 +224,16 @@ DOMStorage::BroadcastChangeNotification(
 static const char kPermissionType[] = "cookie";
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 // static, public
 bool
 DOMStorage::CanUseStorage(nsPIDOMWindowInner* aWindow, DOMStorage* aStorage)
 {
   // This method is responsible for correct setting of mIsSessionOnly.
-  // It doesn't work with mIsPrivate flag at all, since it is checked
-  // regardless mIsSessionOnly flag in DOMStorageCache code.
 
   if (!mozilla::Preferences::GetBool(kStorageEnabled)) {
     return false;
   }
 
   nsContentUtils::StorageAccess access = nsContentUtils::StorageAccess::eDeny;
   if (aWindow) {
     access = nsContentUtils::StorageAllowedForWindow(aWindow);
@@ -278,16 +274,27 @@ PrincipalsEqual(nsIPrincipal* aObjectPri
 
 bool
 DOMStorage::PrincipalEquals(nsIPrincipal* aPrincipal)
 {
   return PrincipalsEqual(mPrincipal, aPrincipal);
 }
 
 bool
+DOMStorage::IsPrivate() const
+{
+  uint32_t privateBrowsingId = 0;
+  nsresult rv = mPrincipal->GetPrivateBrowsingId(&privateBrowsingId);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+  return privateBrowsingId > 0;
+}
+
+bool
 DOMStorage::CanAccess(nsIPrincipal* aPrincipal)
 {
   return !aPrincipal || aPrincipal->Subsumes(mPrincipal);
 }
 
 void
 DOMStorage::GetSupportedNames(nsTArray<nsString>& aKeys)
 {
--- a/dom/storage/DOMStorage.h
+++ b/dom/storage/DOMStorage.h
@@ -49,27 +49,22 @@ public:
   DOMStorageCache const* GetCache() const
   {
     return mCache;
   }
 
   nsIPrincipal* GetPrincipal();
   bool PrincipalEquals(nsIPrincipal* aPrincipal);
   bool CanAccess(nsIPrincipal* aPrincipal);
-  bool IsPrivate()
-  {
-    return mIsPrivate;
-  }
 
   DOMStorage(nsPIDOMWindowInner* aWindow,
              DOMStorageManager* aManager,
              DOMStorageCache* aCache,
              const nsAString& aDocumentURI,
-             nsIPrincipal* aPrincipal,
-             bool aIsPrivate);
+             nsIPrincipal* aPrincipal);
 
   // WebIDL
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   nsPIDOMWindowInner* GetParentObject() const
   {
     return mWindow;
   }
@@ -114,17 +109,17 @@ public:
   // on a storage is about to happen and ensures that the storage's
   // session-only flag is properly set according the current settings.
   // It is an optimization since the privileges check and session only
   // state determination are complex and share the code (comes hand in
   // hand together).
   static bool CanUseStorage(nsPIDOMWindowInner* aWindow,
                             DOMStorage* aStorage = nullptr);
 
-  bool IsPrivate() const { return mIsPrivate; }
+  bool IsPrivate() const;
   bool IsSessionOnly() const { return mIsSessionOnly; }
 
   bool IsForkOf(const DOMStorage* aOther) const
   {
     MOZ_ASSERT(aOther);
     return mCache == aOther->mCache;
   }
 
@@ -138,19 +133,16 @@ private:
   RefPtr<DOMStorageManager> mManager;
   RefPtr<DOMStorageCache> mCache;
   nsString mDocumentURI;
 
   // Principal this DOMStorage (i.e. localStorage or sessionStorage) has
   // been created for
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
-  // Whether this storage is running in private-browsing window.
-  bool mIsPrivate : 1;
-
   // Whether storage is set to persist data only per session, may change
   // dynamically and is set by CanUseStorage function that is called
   // before any operation on the storage.
   bool mIsSessionOnly : 1;
 
   void BroadcastChangeNotification(const nsSubstring& aKey,
                                    const nsSubstring& aOldValue,
                                    const nsSubstring& aNewValue);
--- a/dom/storage/DOMStorageCache.h
+++ b/dom/storage/DOMStorageCache.h
@@ -96,17 +96,17 @@ public:
   // Starts async preload of this cache if it persistent and not loaded.
   void Preload();
 
   // Keeps the cache alive (i.e. present in the manager's hash table) for a time.
   void KeepAlive();
 
   // The set of methods that are invoked by DOM storage web API.
   // We are passing the DOMStorage object just to let the cache
-  // read properties like mPrivate and mSessionOnly.
+  // read properties like mPrincipal and mSessionOnly.
   // Get* methods return error when load from the database has failed.
   nsresult GetLength(const DOMStorage* aStorage, uint32_t* aRetval);
   nsresult GetKey(const DOMStorage* aStorage, uint32_t index, nsAString& aRetval);
   nsresult GetItem(const DOMStorage* aStorage, const nsAString& aKey, nsAString& aRetval);
   nsresult SetItem(const DOMStorage* aStorage, const nsAString& aKey, const nsString& aValue, nsString& aOld);
   nsresult RemoveItem(const DOMStorage* aStorage, const nsAString& aKey, nsString& aOld);
   nsresult Clear(const DOMStorage* aStorage);
 
--- a/dom/storage/DOMStorageManager.cpp
+++ b/dom/storage/DOMStorageManager.cpp
@@ -305,17 +305,16 @@ DOMStorageManager::DropCache(DOMStorageC
   table->RemoveEntry(aCache->OriginNoSuffix());
 }
 
 nsresult
 DOMStorageManager::GetStorageInternal(bool aCreate,
                                       mozIDOMWindow* aWindow,
                                       nsIPrincipal* aPrincipal,
                                       const nsAString& aDocumentURI,
-                                      bool aPrivate,
                                       nsIDOMStorage** aRetval)
 {
   nsresult rv;
 
   nsAutoCString originAttrSuffix;
   BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(originAttrSuffix);
 
   nsAutoCString originKey;
@@ -356,49 +355,44 @@ DOMStorageManager::GetStorageInternal(bo
       return NS_ERROR_DOM_SECURITY_ERR;
     }
   }
 
   if (aRetval) {
     nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
 
     nsCOMPtr<nsIDOMStorage> storage = new DOMStorage(
-      inner, this, cache, aDocumentURI, aPrincipal, aPrivate);
+      inner, this, cache, aDocumentURI, aPrincipal);
     storage.forget(aRetval);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal)
 {
-  return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(), false,
-                            nullptr);
+  return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(), nullptr);
 }
 
 NS_IMETHODIMP
 DOMStorageManager::CreateStorage(mozIDOMWindow* aWindow,
                                  nsIPrincipal* aPrincipal,
                                  const nsAString& aDocumentURI,
-                                 bool aPrivate,
                                  nsIDOMStorage** aRetval)
 {
-  return GetStorageInternal(true, aWindow, aPrincipal, aDocumentURI, aPrivate,
-                            aRetval);
+  return GetStorageInternal(true, aWindow, aPrincipal, aDocumentURI, aRetval);
 }
 
 NS_IMETHODIMP
 DOMStorageManager::GetStorage(mozIDOMWindow* aWindow,
                               nsIPrincipal* aPrincipal,
-                              bool aPrivate,
                               nsIDOMStorage** aRetval)
 {
-  return GetStorageInternal(false, aWindow, aPrincipal, EmptyString(), aPrivate,
-                            aRetval);
+  return GetStorageInternal(false, aWindow, aPrincipal, EmptyString(), aRetval);
 }
 
 NS_IMETHODIMP
 DOMStorageManager::CloneStorage(nsIDOMStorage* aStorage)
 {
   if (mType != SessionStorage) {
     // Cloning is supported only for sessionStorage
     return NS_ERROR_NOT_IMPLEMENTED;
@@ -468,24 +462,23 @@ DOMStorageManager::CheckStorage(nsIPrinc
   return NS_OK;
 }
 
 // Obsolete nsIDOMStorageManager methods
 
 NS_IMETHODIMP
 DOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal* aPrincipal,
                                                const nsAString& aDocumentURI,
-                                               bool aPrivate,
                                                nsIDOMStorage** aRetval)
 {
   if (mType != LocalStorage) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  return CreateStorage(nullptr, aPrincipal, aDocumentURI, aPrivate, aRetval);
+  return CreateStorage(nullptr, aPrincipal, aDocumentURI, aRetval);
 }
 
 void
 DOMStorageManager::ClearCaches(uint32_t aUnloadFlags,
                                const OriginAttributesPattern& aPattern,
                                const nsACString& aOriginScope)
 {
   for (auto iter1 = mCaches.Iter(); !iter1.Done(); iter1.Next()) {
--- a/dom/storage/DOMStorageManager.h
+++ b/dom/storage/DOMStorageManager.h
@@ -88,17 +88,16 @@ private:
                                              const nsACString& aOriginNoSuffix,
                                              nsIPrincipal* aPrincipal);
 
   // Helper for creation of DOM storage objects
   nsresult GetStorageInternal(bool aCreate,
                               mozIDOMWindow* aWindow,
                               nsIPrincipal* aPrincipal,
                               const nsAString& aDocumentURI,
-                              bool aPrivate,
                               nsIDOMStorage** aRetval);
 
   // Suffix->origin->cache map
   typedef nsTHashtable<DOMStorageCacheHashKey> CacheOriginHashtable;
   nsClassHashtable<nsCStringHashKey, CacheOriginHashtable> mCaches;
 
   const DOMStorage::StorageType mType;
 
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -768,17 +768,17 @@ Telephony::EnumerateCallState(nsITelepho
 NS_IMETHODIMP
 Telephony::EnumerateCallStateComplete()
 {
   // Set conference state.
   mGroup->ChangeState();
 
   HandleAudioAgentState();
   if (mReadyPromise) {
-    mReadyPromise->MaybeResolve(JS::UndefinedHandleValue);
+    mReadyPromise->MaybeResolveWithUndefined();
   }
 
   if (NS_FAILED(mService->RegisterListener(mListener))) {
     NS_WARNING("Failed to register listener!");
   }
   return NS_OK;
 }
 
--- a/dom/telephony/TelephonyCallback.cpp
+++ b/dom/telephony/TelephonyCallback.cpp
@@ -19,17 +19,17 @@ TelephonyCallback::TelephonyCallback(Pro
 {
 }
 
 // nsITelephonyCallback
 
 NS_IMETHODIMP
 TelephonyCallback::NotifySuccess()
 {
-  mPromise->MaybeResolve(JS::UndefinedHandleValue);
+  mPromise->MaybeResolveWithUndefined();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TelephonyCallback::NotifyError(const nsAString& aError)
 {
   mPromise->MaybeRejectBrokenly(aError);
   return NS_OK;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..db812bd7d5367647d68fa2c2dbfa5bef3cdb084b
GIT binary patch
literal 91
zc%17D@N?(olHy`uVBq!ia0vp^DIm<q3?#jD*u{YqbAV5X>wg9Y$w!>#K(4T-i(^Oy
mWAXxR3D(64B0Wuk3=FIl42*JTHO>O189ZJ6T-G@yGywq3R1$9h
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -1,23 +1,25 @@
 [DEFAULT]
 support-files =
   497633.html
+  fail.png
   file_bug628069.html
   file_clonewrapper.html
   file_domWindowUtils_scrollbarSize.html
   file_frameElementWrapping.html
   file_interfaces.xml
   file_moving_nodeList.html
   file_moving_xhr.html
   file_showModalDialog.html
   historyframes.html
   image_50.png
   image_100.png
   image_200.png
+  pass.apng
   performance_timeline_main_test.html
   resource_timing_iframe.html
   resource_timing_main_test.html
   resource_timing_cross_origin.html
   res0.resource
   res1.resource
   res1.resource^headers^
   res2.resource
@@ -134,8 +136,9 @@ skip-if = buildapp == 'b2g' # Bug 118442
 [test_storagePermissionsLimitForeign.html]
 skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g
 [test_selectevents.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' # Mouse doesn't select in the same way
 # Disabled on Android, see bug 1230232
 [test_WebKitCSSMatrix.html]
 [test_resource_timing_frameset.html]
 [test_bug1208217.html]
+[test_picture_apng.html]
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6e78a9eef4dae888be61ed4f8872b0743810b791
GIT binary patch
literal 188
zc%17D@N?(olHy`uVBq!ia0vp^DIm<j3?$ucQqzDGTYyi9D<dPL?|%k{#^Z575yp}r
zzhDN3XE#97ww^AIAsP4Ho@L}@VBle1u(tl?RtuRe4KEu#RD?QRlqTY)KG{n?Gg23P
z(BJwHXpnS?YeY$Kep*R+Vo@rCBhY0Ej>W~Pxk)*xMG8iGmTJ-e`hm(AJYD@<);T3K
F0RXuSHVFU#
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/test_picture_apng.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+  <meta charset="utf-8">
+  <title>Image srcset mutations</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<script type="application/javascript">
+"use strict";
+window.onload = function() {
+  // Smoke test, check picture working as expected
+  const t0 = document.querySelector("#test0 img");
+  ok(t0.currentSrc.endsWith("apng"), `t0: expected pass.apng, got '${t0.currentSrc}'`);
+
+  // Test that the apng is selected over bogus types.
+  const t1 = document.querySelector("#test1 img");
+  ok(t1.currentSrc.endsWith("apng"), `t1: expected pass.apng, got '${t1.currentSrc}'`);
+
+  // Test that tree order precedence applies
+  const t2 = document.querySelector("#test2 img");
+  ok(t2.currentSrc.endsWith("apng"), `t2: expected pass.apng, got '${t2.currentSrc}'`);
+
+  // Test that apng doesn't alway win
+  const t3 = document.querySelector("#test3 img");
+  ok(t3.currentSrc.endsWith("apng"), `t3: expected pass.apng, got '${t3.currentSrc}'`);
+
+  // Test dynamically constructed picture, where apng is selected over a bogus
+  // source or the img src attribute
+  const pic = document.createElement("picture");
+  pic.id = "test4";
+  const t4 = document.createElement("img");
+  const bogusSource = document.createElement("source");
+  bogusSource.type = "bogus/bogus";
+  bogusSource.srcset = "fail.png";
+  const legitSource = document.createElement("source");
+  legitSource.type = "image/apng";
+  legitSource.srcset = "pass.apng";
+  pic.appendChild(bogusSource);
+  pic.appendChild(legitSource);
+  pic.appendChild(t4);
+  t4.src = "fail.png";
+  document.body.appendChild(pic);
+  t4.onload = ()=>{
+    ok(t4.currentSrc.endsWith("apng"), `t4: Expected pass.apng, got '${t4.currentSrc}'`);
+    SimpleTest.finish();
+  }
+};
+SimpleTest.waitForExplicitFinish();
+</script>
+
+<body>
+  <picture id="test0">
+    <srouce>
+    <img src="pass.apng">
+  </picture>
+  <picture id="test1">
+    <source type="bogus/type" srcset="fail.png">
+    <source type="image/apng" srcset="pass.apng">
+    <source type="image/jpeg" srcset="fail.png">
+    <img src="fail-fallback">
+  </picture>
+  <picture id="test2">
+    <source type="image/png" srcset="pass.apng">
+    <source srcset="fail.png">
+    <source type="bogus/type" srcset="fail.png">
+    <img src="fail-fallback">
+  </picture>
+  <picture id="test3">
+    <source type="image/jpeg" srcset="pass.apng">
+    <source type="image/apng" srcset="fail.png">
+    <img src="fail-fallback">
+  </picture>
+</body>
+
+</html>
--- a/dom/tv/TVServiceCallbacks.cpp
+++ b/dom/tv/TVServiceCallbacks.cpp
@@ -54,17 +54,17 @@ TVServiceSourceSetterCallback::NotifySuc
   }
 
   nsresult rv = mTuner->SetCurrentSource(mSourceType);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     mPromise->MaybeReject(rv);
     return rv;
   }
 
-  mPromise->MaybeResolve(JS::UndefinedHandleValue);
+  mPromise->MaybeResolveWithUndefined();
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
 TVServiceSourceSetterCallback::NotifyError(uint16_t aErrorCode)
 {
   switch (aErrorCode) {
   case nsITVServiceCallback::TV_ERROR_FAILURE:
@@ -119,17 +119,17 @@ TVServiceChannelScanCallback::NotifySucc
   // |aDataList| is expected to be null for setter callbacks.
   if (aDataList) {
     mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     return NS_ERROR_INVALID_ARG;
   }
 
   mSource->SetIsScanning(mIsScanning);
 
-  mPromise->MaybeResolve(JS::UndefinedHandleValue);
+  mPromise->MaybeResolveWithUndefined();
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
 TVServiceChannelScanCallback::NotifyError(uint16_t aErrorCode)
 {
   switch (aErrorCode) {
   case nsITVServiceCallback::TV_ERROR_FAILURE:
@@ -206,17 +206,17 @@ TVServiceChannelSetterCallback::NotifySu
   }
 
   rv = mSource->SetCurrentChannel(channelData);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     return rv;
   }
 
-  mPromise->MaybeResolve(JS::UndefinedHandleValue);
+  mPromise->MaybeResolveWithUndefined();
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
 TVServiceChannelSetterCallback::NotifyError(uint16_t aErrorCode)
 {
   switch (aErrorCode) {
   case nsITVServiceCallback::TV_ERROR_FAILURE:
@@ -429,17 +429,17 @@ TVServiceProgramGetterCallback::NotifySu
   uint32_t length;
   nsresult rv = aDataList->GetLength(&length);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     return rv;
   }
 
   if (mIsSingular && length == 0) {
-    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    mPromise->MaybeResolveWithUndefined();
     return NS_OK;
   }
 
   if (mIsSingular) {
     nsCOMPtr<nsITVProgramData> programData = do_QueryElementAt(aDataList, 0);
     if (NS_WARN_IF(!programData)) {
       mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
       return NS_ERROR_DOM_ABORT_ERR;
--- a/dom/webidl/CanvasRenderingContext2D.webidl
+++ b/dom/webidl/CanvasRenderingContext2D.webidl
@@ -148,18 +148,16 @@ interface CanvasRenderingContext2D {
 
   // Mozilla-specific stuff
   // FIXME Bug 768048 mozCurrentTransform/mozCurrentTransformInverse should return a WebIDL array.
   [Throws]
   attribute object mozCurrentTransform; // [ m11, m12, m21, m22, dx, dy ], i.e. row major
   [Throws]
   attribute object mozCurrentTransformInverse;
 
-  attribute DOMString mozFillRule; /* "evenodd", "nonzero" (default) */
-
   [Throws]
   attribute any mozDash; /* default |null| */
 
   [LenientFloat]
   attribute double mozDashOffset; /* default 0.0 */
 
   [SetterThrows]
   attribute DOMString mozTextStyle;
--- a/dom/webidl/HTMLInputElement.webidl
+++ b/dom/webidl/HTMLInputElement.webidl
@@ -110,21 +110,21 @@ interface HTMLInputElement : HTMLElement
   void setCustomValidity(DOMString error);
 
   // Bug 850365 readonly attribute NodeList labels;
 
   void select();
 
   [Throws]
            // TODO: unsigned vs signed
-           attribute long selectionStart;
+           attribute long? selectionStart;
   [Throws]
-           attribute long selectionEnd;
+           attribute long? selectionEnd;
   [Throws]
-           attribute DOMString selectionDirection;
+           attribute DOMString? selectionDirection;
   [Throws]
   void setRangeText(DOMString replacement);
   [Throws]
   void setRangeText(DOMString replacement, unsigned long start,
     unsigned long end, optional SelectionMode selectionMode = "preserve");
 
   // also has obsolete members
 };
--- a/dom/webidl/HTMLTextAreaElement.webidl
+++ b/dom/webidl/HTMLTextAreaElement.webidl
@@ -54,21 +54,21 @@ interface HTMLTextAreaElement : HTMLElem
   boolean checkValidity();
   boolean reportValidity();
   void setCustomValidity(DOMString error);
 
   // readonly attribute NodeList labels;
 
   void select();
   [Throws]
-           attribute unsigned long selectionStart;
+           attribute unsigned long? selectionStart;
   [Throws]
-           attribute unsigned long selectionEnd;
+           attribute unsigned long? selectionEnd;
   [Throws]
-           attribute DOMString selectionDirection;
+           attribute DOMString? selectionDirection;
   [Throws]
   void setRangeText(DOMString replacement);
   [Throws]
   void setRangeText(DOMString replacement, unsigned long start,
     unsigned long end, optional SelectionMode selectionMode = "preserve");
   [Throws]
   void setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction);
 };
--- a/dom/workers/ServiceWorkerClients.cpp
+++ b/dom/workers/ServiceWorkerClients.cpp
@@ -92,17 +92,17 @@ class GetRunnable final : public Runnabl
 
       if (NS_FAILED(mRv)) {
         promise->MaybeReject(mRv);
       } else if (mValue) {
         RefPtr<ServiceWorkerWindowClient> windowClient =
           new ServiceWorkerWindowClient(promise->GetParentObject(), *mValue);
         promise->MaybeResolve(windowClient.get());
       } else {
-        promise->MaybeResolve(JS::UndefinedHandleValue);
+        promise->MaybeResolveWithUndefined();
       }
       mPromiseProxy->CleanUp();
       return true;
     }
   };
 
   RefPtr<PromiseWorkerProxy> mPromiseProxy;
   nsString mClientId;
@@ -241,17 +241,17 @@ public:
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
     MOZ_ASSERT(promise);
 
     if (NS_SUCCEEDED(mResult)) {
-      promise->MaybeResolve(JS::UndefinedHandleValue);
+      promise->MaybeResolveWithUndefined();
     } else {
       promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     }
 
     mPromiseProxy->CleanUp();
     return true;
   }
 };
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -808,17 +808,17 @@ public:
       mPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
       return NS_OK;
     }
 
     RefPtr<ServiceWorkerRegistrationInfo> registration =
       swm->GetServiceWorkerRegistrationInfo(principal, uri);
 
     if (!registration) {
-      mPromise->MaybeResolve(JS::UndefinedHandleValue);
+      mPromise->MaybeResolveWithUndefined();
       return NS_OK;
     }
 
     NS_ConvertUTF8toUTF16 scope(registration->mScope);
     RefPtr<ServiceWorkerRegistration> swr =
       mWindow->GetServiceWorkerRegistration(scope);
     mPromise->MaybeResolve(swr);
 
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -371,17 +371,17 @@ public:
     : mPromise(aPromise)
   {
     AssertIsOnMainThread();
   }
 
   void
   UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override
   {
-    mPromise->MaybeResolve(JS::UndefinedHandleValue);
+    mPromise->MaybeResolveWithUndefined();
   }
 
   void
   UpdateFailed(ErrorResult& aStatus) override
   {
     mPromise->MaybeReject(aStatus);
   }
 };
@@ -413,17 +413,17 @@ public:
     ErrorResult status;
     PickleIterator iter = PickleIterator(mSerializedErrorResult);
     Unused << IPC::ReadParam(&mSerializedErrorResult, &iter, &status);
 
     Promise* promise = mPromiseProxy->WorkerPromise();
     if (status.Failed()) {
       promise->MaybeReject(status);
     } else {
-      promise->MaybeResolve(JS::UndefinedHandleValue);
+      promise->MaybeResolveWithUndefined();
     }
     status.SuppressException();
     mPromiseProxy->CleanUp();
     return true;
   }
 };
 
 class WorkerThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
@@ -1114,17 +1114,17 @@ ServiceWorkerRegistrationWorkerThread::U
   if (aRv.Failed()) {
     return nullptr;
   }
 
   // Avoid infinite update loops by ignoring update() calls during top
   // level script evaluation.  See:
   // https://github.com/slightlyoff/ServiceWorker/issues/800
   if (worker->LoadScriptAsPartOfLoadingServiceWorkerScript()) {
-    promise->MaybeResolve(JS::UndefinedHandleValue);
+    promise->MaybeResolveWithUndefined();
     return promise.forget();
   }
 
   RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
   if (!proxy) {
     aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
     return nullptr;
   }
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -593,17 +593,17 @@ public:
 
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
-    promise->MaybeResolve(JS::UndefinedHandleValue);
+    promise->MaybeResolveWithUndefined();
 
     // Release the reference on the worker thread.
     mPromiseProxy->CleanUp();
 
     return true;
   }
 };
 
@@ -658,17 +658,17 @@ ServiceWorkerGlobalScope::SkipWaiting(Er
   RefPtr<Promise> promise = Promise::Create(this, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   RefPtr<PromiseWorkerProxy> promiseProxy =
     PromiseWorkerProxy::Create(mWorkerPrivate, promise);
   if (!promiseProxy) {
-    promise->MaybeResolve(JS::UndefinedHandleValue);
+    promise->MaybeResolveWithUndefined();
     return promise.forget();
   }
 
   RefPtr<WorkerScopeSkipWaitingRunnable> runnable =
     new WorkerScopeSkipWaitingRunnable(promiseProxy,
                                        NS_ConvertUTF16toUTF8(mScope));
 
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
--- a/editor/reftests/reftest.list
+++ b/editor/reftests/reftest.list
@@ -77,17 +77,17 @@ skip-if(B2G||Mulet) fails-if(Android) ne
 needs-focus == spellcheck-non-latin-arabic.html spellcheck-non-latin-arabic-ref.html
 needs-focus == spellcheck-non-latin-chinese-simplified.html spellcheck-non-latin-chinese-simplified-ref.html
 needs-focus == spellcheck-non-latin-chinese-traditional.html spellcheck-non-latin-chinese-traditional-ref.html
 needs-focus == spellcheck-non-latin-hebrew.html spellcheck-non-latin-hebrew-ref.html
 needs-focus == spellcheck-non-latin-japanese.html spellcheck-non-latin-japanese-ref.html
 needs-focus == spellcheck-non-latin-korean.html spellcheck-non-latin-korean-ref.html
 == unneeded_scroll.html unneeded_scroll-ref.html
 skip-if(B2G||Mulet) == caret_on_presshell_reinit.html caret_on_presshell_reinit-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
-fuzzy-if(browserIsRemote,255,3) asserts-if(browserIsRemote,0-1) skip-if(B2G||Mulet) == caret_on_presshell_reinit-2.html caret_on_presshell_reinit-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+fuzzy-if(browserIsRemote,255,3) asserts-if(browserIsRemote,0-1) skip-if(B2G||Mulet) fuzzy-if(skiaContent,1,5) == caret_on_presshell_reinit-2.html caret_on_presshell_reinit-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,2824) == 642800.html 642800-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == selection_visibility_after_reframe.html selection_visibility_after_reframe-ref.html
 != selection_visibility_after_reframe-2.html selection_visibility_after_reframe-ref.html
 != selection_visibility_after_reframe-3.html selection_visibility_after_reframe-ref.html
 == 672709.html 672709-ref.html
 == 338427-1.html 338427-1-ref.html
 skip-if(Android||B2G||Mulet) needs-focus == 674212-spellcheck.html 674212-spellcheck-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(Android||B2G||Mulet) needs-focus == 338427-2.html 338427-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
--- a/embedding/components/windowwatcher/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp
@@ -1239,17 +1239,16 @@ nsWindowWatcher::OpenWindowInternal(mozI
     nsCOMPtr<nsIDOMStorageManager> newStorageManager =
       do_QueryInterface(newDocShell);
 
     if (parentStorageManager && newStorageManager) {
       nsCOMPtr<nsIDOMStorage> storage;
       nsCOMPtr<nsPIDOMWindowInner> pInnerWin = parentWindow->GetCurrentInnerWindow();
 
       parentStorageManager->GetStorage(pInnerWin, subjectPrincipal,
-                                       isPrivateBrowsingWindow,
                                        getter_AddRefs(storage));
       if (storage) {
         newStorageManager->CloneStorage(storage);
       }
     }
   }
 
   if (isNewToplevelWindow) {
--- a/extensions/cookie/test/unit/test_bug526789.js
+++ b/extensions/cookie/test/unit/test_bug526789.js
@@ -171,17 +171,16 @@ function run_test() {
   // aliases such as 'localhost', and eTLD's such as 'co.uk'; but that the
   // resulting cookie is for the exact host only.
   testDomainCookie("http://192.168.0.1/", "192.168.0.1");
   testDomainCookie("http://localhost/", "localhost");
   testDomainCookie("http://co.uk/", "co.uk");
 
   // Test that trailing dots are treated differently for purposes of the
   // 'domain' attribute when using setCookieString.
-  testTrailingDotCookie("http://192.168.0.1", "192.168.0.1");
   testTrailingDotCookie("http://localhost", "localhost");
   testTrailingDotCookie("http://foo.com", "foo.com");
 
   cm.removeAll();
 }
 
 function getCookieCount() {
   var count = 0;
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -439,16 +439,19 @@ TextureClient::IsReadLocked() const
   return mReadLock && mReadLock->GetReadCount() > 1;
 }
 
 bool
 TextureClient::Lock(OpenMode aMode)
 {
   MOZ_ASSERT(IsValid());
   MOZ_ASSERT(!mIsLocked);
+  if (!IsValid()) {
+    return false;
+  }
   if (mIsLocked) {
     return mOpenMode == aMode;
   }
 
   if (!!mFenceHandleWaiter && (aMode & OpenMode::OPEN_WRITE)) {
     mFenceHandleWaiter->WaitComplete();
   }
 
@@ -488,17 +491,17 @@ TextureClient::Lock(OpenMode aMode)
   return mIsLocked;
 }
 
 void
 TextureClient::Unlock()
 {
   MOZ_ASSERT(IsValid());
   MOZ_ASSERT(mIsLocked);
-  if (!mIsLocked) {
+  if (!IsValid() || !mIsLocked) {
     return;
   }
 
   if (mBorrowedDrawTarget) {
     if (mOpenMode & OpenMode::OPEN_WRITE) {
       mBorrowedDrawTarget->Flush();
       if (mReadbackSink && !mData->ReadBack(mReadbackSink)) {
         // Fallback implementation for reading back, because mData does not
@@ -617,17 +620,17 @@ TextureClient::BorrowDrawTarget()
 {
   MOZ_ASSERT(IsValid());
   MOZ_ASSERT(mIsLocked);
   // TODO- We can't really assert that at the moment because there is code that Borrows
   // the DrawTarget, just to get a snapshot, which is legit in term of OpenMode
   // but we should have a way to get a SourceSurface directly instead.
   //MOZ_ASSERT(mOpenMode & OpenMode::OPEN_WRITE);
 
-  if (!mIsLocked) {
+  if (!IsValid() || !mIsLocked) {
     return nullptr;
   }
 
   if (!NS_IsMainThread()) {
     return nullptr;
   }
 
   if (!mBorrowedDrawTarget) {
--- a/image/DecodedSurfaceProvider.cpp
+++ b/image/DecodedSurfaceProvider.cpp
@@ -15,16 +15,17 @@ using namespace mozilla::gfx;
 namespace mozilla {
 namespace image {
 
 DecodedSurfaceProvider::DecodedSurfaceProvider(NotNull<RasterImage*> aImage,
                                                NotNull<Decoder*> aDecoder,
                                                const SurfaceKey& aSurfaceKey)
   : ISurfaceProvider(AvailabilityState::StartAsPlaceholder())
   , mImage(aImage.get())
+  , mMutex("mozilla::image::DecodedSurfaceProvider")
   , mDecoder(aDecoder.get())
   , mSurfaceKey(aSurfaceKey)
 {
   MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
              "Use MetadataDecodingTask for metadata decodes");
   MOZ_ASSERT(mDecoder->IsFirstFrameDecode(),
              "Use AnimationDecodingTask for animation decodes");
 }
@@ -119,16 +120,18 @@ DecodedSurfaceProvider::LogicalSizeInByt
   // Single frame images are always 32bpp.
   IntSize size = mSurfaceKey.Size();
   return size.width * size.height * sizeof(uint32_t);
 }
 
 void
 DecodedSurfaceProvider::Run()
 {
+  MutexAutoLock lock(mMutex);
+
   if (!mDecoder || !mImage) {
     MOZ_ASSERT_UNREACHABLE("Running after decoding finished?");
     return;
   }
 
   // Run the decoder.
   LexerResult result = mDecoder->Decode(WrapNotNull(this));
 
@@ -157,16 +160,19 @@ DecodedSurfaceProvider::Run()
   MOZ_ASSERT_UNREACHABLE("Unexpected yield for single-frame image");
   mDecoder->TerminateFailure();
   FinishDecoding();
 }
 
 void
 DecodedSurfaceProvider::CheckForNewSurface()
 {
+  mMutex.AssertCurrentThreadOwns();
+  MOZ_ASSERT(mDecoder);
+
   if (mSurface) {
     // Single-frame images should produce no more than one surface, so if we
     // have one, it should be the same one the decoder is working on.
     MOZ_ASSERT(mSurface.get() == mDecoder->GetCurrentFrameRef().get(),
                "DecodedSurfaceProvider and Decoder have different surfaces?");
     return;
   }
 
@@ -181,16 +187,17 @@ DecodedSurfaceProvider::CheckForNewSurfa
   SurfaceCache::SurfaceAvailable(WrapNotNull(this),
                                  ImageKey(mImage.get()),
                                  mSurfaceKey);
 }
 
 void
 DecodedSurfaceProvider::FinishDecoding()
 {
+  mMutex.AssertCurrentThreadOwns();
   MOZ_ASSERT(mImage);
   MOZ_ASSERT(mDecoder);
 
   // Send notifications.
   NotifyDecodeComplete(WrapNotNull(mImage), WrapNotNull(mDecoder));
 
   // Destroy our decoder; we don't need it anymore. (And if we don't destroy it,
   // our surface can never be optimized, because the decoder has a
--- a/image/DecodedSurfaceProvider.h
+++ b/image/DecodedSurfaceProvider.h
@@ -65,16 +65,19 @@ private:
 
   void DropImageReference();
   void CheckForNewSurface();
   void FinishDecoding();
 
   /// The image associated with our decoder. Dropped after decoding.
   RefPtr<RasterImage> mImage;
 
+  /// Mutex protecting access to mDecoder.
+  Mutex mMutex;
+
   /// The decoder that will generate our surface. Dropped after decoding.
   RefPtr<Decoder> mDecoder;
 
   /// Our surface. Initially null until it's generated by the decoder.
   RefPtr<imgFrame> mSurface;
 
   /// A drawable reference to our service; used for locking.
   DrawableFrameRef mLockRef;
--- a/image/DecoderFactory.cpp
+++ b/image/DecoderFactory.cpp
@@ -29,16 +29,18 @@ DecoderFactory::GetDecoderType(const cha
   // By default we don't know.
   DecoderType type = DecoderType::UNKNOWN;
 
   // PNG
   if (!strcmp(aMimeType, IMAGE_PNG)) {
     type = DecoderType::PNG;
   } else if (!strcmp(aMimeType, IMAGE_X_PNG)) {
     type = DecoderType::PNG;
+  } else if (!strcmp(aMimeType, IMAGE_APNG)) {
+    type = DecoderType::PNG;
 
   // GIF
   } else if (!strcmp(aMimeType, IMAGE_GIF)) {
     type = DecoderType::GIF;
 
   // JPEG
   } else if (!strcmp(aMimeType, IMAGE_JPEG)) {
     type = DecoderType::JPEG;
--- a/image/IDecodingTask.cpp
+++ b/image/IDecodingTask.cpp
@@ -100,27 +100,30 @@ IDecodingTask::Resume()
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // AnimationDecodingTask implementation.
 ///////////////////////////////////////////////////////////////////////////////
 
 AnimationDecodingTask::AnimationDecodingTask(NotNull<Decoder*> aDecoder)
-  : mDecoder(aDecoder)
+  : mMutex("mozilla::image::AnimationDecodingTask")
+  , mDecoder(aDecoder)
 {
   MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
              "Use MetadataDecodingTask for metadata decodes");
   MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(),
              "Use DecodingTask for single-frame image decodes");
 }
 
 void
 AnimationDecodingTask::Run()
 {
+  MutexAutoLock lock(mMutex);
+
   while (true) {
     LexerResult result = mDecoder->Decode(WrapNotNull(this));
 
     if (result.is<TerminalState>()) {
       NotifyDecodeComplete(mDecoder->GetImage(), mDecoder);
       return;  // We're done.
     }
 
--- a/image/IDecodingTask.h
+++ b/image/IDecodingTask.h
@@ -77,16 +77,19 @@ public:
 
   // Full decodes are low priority compared to metadata decodes because they
   // don't block layout or page load.
   TaskPriority Priority() const override { return TaskPriority::eLow; }
 
 private:
   virtual ~AnimationDecodingTask() { }
 
+  /// Mutex protecting access to mDecoder.
+  Mutex mMutex;
+
   NotNull<RefPtr<Decoder>> mDecoder;
 };
 
 
 /**
  * An IDecodingTask implementation for metadata decodes of images.
  */
 class MetadataDecodingTask final : public IDecodingTask
--- a/image/build/nsImageModule.cpp
+++ b/image/build/nsImageModule.cpp
@@ -75,16 +75,17 @@ static const mozilla::Module::CategoryEn
   { "Gecko-Content-Viewers", IMAGE_PJPEG, "@mozilla.org/content/document-loader-factory;1" },
   { "Gecko-Content-Viewers", IMAGE_JPG, "@mozilla.org/content/document-loader-factory;1" },
   { "Gecko-Content-Viewers", IMAGE_ICO, "@mozilla.org/content/document-loader-factory;1" },
   { "Gecko-Content-Viewers", IMAGE_ICO_MS, "@mozilla.org/content/document-loader-factory;1" },
   { "Gecko-Content-Viewers", IMAGE_BMP, "@mozilla.org/content/document-loader-factory;1" },
   { "Gecko-Content-Viewers", IMAGE_BMP_MS, "@mozilla.org/content/document-loader-factory;1" },
   { "Gecko-Content-Viewers", IMAGE_ICON_MS, "@mozilla.org/content/document-loader-factory;1" },
   { "Gecko-Content-Viewers", IMAGE_PNG, "@mozilla.org/content/document-loader-factory;1" },
+  { "Gecko-Content-Viewers", IMAGE_APNG, "@mozilla.org/content/document-loader-factory;1" },
   { "Gecko-Content-Viewers", IMAGE_X_PNG, "@mozilla.org/content/document-loader-factory;1" },
   { "content-sniffing-services", "@mozilla.org/image/loader;1", "@mozilla.org/image/loader;1" },
   { nullptr }
 };
 
 static bool sInitialized = false;
 nsresult
 mozilla::image::EnsureModuleInitialized()
--- a/image/imgRequestProxy.cpp
+++ b/image/imgRequestProxy.cpp
@@ -663,17 +663,18 @@ imgRequestProxy::PerformClone(imgINotifi
 
 NS_IMETHODIMP
 imgRequestProxy::GetImagePrincipal(nsIPrincipal** aPrincipal)
 {
   if (!GetOwner()) {
     return NS_ERROR_FAILURE;
   }
 
-  NS_ADDREF(*aPrincipal = GetOwner()->GetPrincipal());
+  nsCOMPtr<nsIPrincipal> principal = GetOwner()->GetPrincipal();
+  principal.forget(aPrincipal);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 imgRequestProxy::GetMultipart(bool* aMultipart)
 {
   if (!GetOwner()) {
     return NS_ERROR_FAILURE;
--- a/js/public/Initialization.h
+++ b/js/public/Initialization.h
@@ -81,16 +81,32 @@ JS_InitWithFailureDiagnostic(void)
 {
 #ifdef DEBUG
     return JS::detail::InitWithFailureDiagnostic(true);
 #else
     return JS::detail::InitWithFailureDiagnostic(false);
 #endif
 }
 
+/*
+ * Returns true if SpiderMonkey has been initialized successfully, even if it has
+ * possibly been shut down.
+ *
+ * Note that it is the responsibility of the embedder to call JS_Init() and
+ * JS_ShutDown() at the correct times, and therefore this API should ideally not
+ * be necessary to use.  This is only intended to be used in cases where the
+ * embedder isn't in full control of deciding whether to initialize SpiderMonkey
+ * or hand off the task to another consumer.
+ */
+inline bool
+JS_IsInitialized(void)
+{
+  return JS::detail::libraryInitState != JS::detail::InitState::Uninitialized;
+}
+
 /**
  * Destroy free-standing resources allocated by SpiderMonkey, not associated
  * with any runtime, context, or other structure.
  *
  * This method should be called after all other JSAPI data has been properly
  * cleaned up: every new runtime must have been destroyed, every new context
  * must have been destroyed, and so on.  Calling this method before all other
  * resources have been destroyed has undefined behavior.
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -452,16 +452,22 @@ BytecodeEmitter::emitDupAt(unsigned slot
     if (!emitN(JSOP_DUPAT, 3, &off))
         return false;
 
     jsbytecode* pc = code(off);
     SET_UINT24(pc, slotFromTop);
     return true;
 }
 
+bool
+BytecodeEmitter::emitCheckIsObj(CheckIsObjectKind kind)
+{
+    return emit2(JSOP_CHECKISOBJ, uint8_t(kind));
+}
+
 static const char*
 StatementName(StmtInfoBCE* stmt)
 {
     if (!stmt)
         return js_script_str;
 
     /* XXX too many "... statement" L10N gaffes -- fix via js.msg! */
     static const char* const statementName[] = {
@@ -4142,16 +4148,18 @@ BytecodeEmitter::emitIteratorNext(ParseN
     if (!emit1(JSOP_DUP))                                 // ... ITER ITER
         return false;
     if (!emitAtomOp(cx->names().next, JSOP_CALLPROP))     // ... ITER NEXT
         return false;
     if (!emit1(JSOP_SWAP))                                // ... NEXT ITER
         return false;
     if (!emitCall(JSOP_CALL, 0, pn))                      // ... RESULT
         return false;
+    if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) // ... RESULT
+        return false;
     checkTypeSet(JSOP_CALL);
     return true;
 }
 
 bool
 BytecodeEmitter::emitDefault(ParseNode* defaultExpr)
 {
     if (!emit1(JSOP_DUP))                                 // VALUE VALUE
@@ -7166,16 +7174,18 @@ BytecodeEmitter::emitYieldStar(ParseNode
     if (!emitAtomOp(cx->names().next, JSOP_CALLPROP))            // RECEIVED ITER ITER NEXT
         return false;
     if (!emit1(JSOP_SWAP))                                       // RECEIVED ITER NEXT ITER
         return false;
     if (!emit2(JSOP_PICK, 3))                                    // ITER NEXT ITER RECEIVED
         return false;
     if (!emitCall(JSOP_CALL, 1, iter))                           // ITER RESULT
         return false;
+    if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext))        // ITER RESULT
+        return false;
     checkTypeSet(JSOP_CALL);
     MOZ_ASSERT(this->stackDepth == depth);
 
     if (!emitJumpTargetAndPatch(checkResult))                    // checkResult:
         return false;
 
     // if (!result.done) goto tryStart;                          // ITER RESULT
     if (!emit1(JSOP_DUP))                                        // ITER RESULT RESULT
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -14,16 +14,17 @@
 #include "jscntxt.h"
 #include "jsopcode.h"
 #include "jsscript.h"
 
 #include "frontend/ParseMaps.h"
 #include "frontend/Parser.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SourceNotes.h"
+#include "vm/Interpreter.h"
 
 namespace js {
 
 class ScopeObject;
 
 namespace frontend {
 
 class FullParseHandler;
@@ -499,16 +500,19 @@ struct BytecodeEmitter
 
     // Emit three bytecodes, an opcode with two bytes of immediate operands.
     MOZ_MUST_USE bool emit3(JSOp op, jsbytecode op1, jsbytecode op2);
 
     // Helper to emit JSOP_DUPAT. The argument is the value's depth on the
     // JS stack, as measured from the top.
     MOZ_MUST_USE bool emitDupAt(unsigned slotFromTop);
 
+    // Helper to emit JSOP_CHECKISOBJ.
+    MOZ_MUST_USE bool emitCheckIsObj(CheckIsObjectKind kind);
+
     // Emit a bytecode followed by an uint16 immediate operand stored in
     // big-endian order.
     MOZ_MUST_USE bool emitUint16Operand(JSOp op, uint32_t operand);
 
     // Emit a bytecode followed by an uint32 immediate operand.
     MOZ_MUST_USE bool emitUint32Operand(JSOp op, uint32_t operand);
 
     // Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -18,16 +18,17 @@
 #include "asmjs/WasmJS.h"
 #include "builtin/ModuleObject.h"
 #include "gc/GCInternals.h"
 #include "gc/Policy.h"
 #include "jit/IonCode.h"
 #include "js/SliceBudget.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/ArrayObject.h"
+#include "vm/Debugger.h"
 #include "vm/ScopeObject.h"
 #include "vm/Shape.h"
 #include "vm/Symbol.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
--- a/js/src/gc/Policy.h
+++ b/js/src/gc/Policy.h
@@ -25,16 +25,17 @@ class AccessorShape;
 class ArgumentsObject;
 class ArrayBufferObject;
 class ArrayBufferObjectMaybeShared;
 class ArrayBufferViewObject;
 class ArrayObject;
 class BaseShape;
 class ClonedBlockObject;
 class DebugScopeObject;
+class DebuggerFrame;
 class ExportEntryObject;
 class GlobalObject;
 class ImportEntryObject;
 class LazyScript;
 class ModuleEnvironmentObject;
 class ModuleNamespaceObject;
 class ModuleObject;
 class NativeObject;
@@ -81,16 +82,17 @@ class JitCode;
     D(js::ArgumentsObject*) \
     D(js::ArrayBufferObject*) \
     D(js::ArrayBufferObjectMaybeShared*) \
     D(js::ArrayBufferViewObject*) \
     D(js::ArrayObject*) \
     D(js::BaseShape*) \
     D(js::ClonedBlockObject*) \
     D(js::DebugScopeObject*) \
+    D(js::DebuggerFrame*) \
     D(js::ExportEntryObject*) \
     D(js::GlobalObject*) \
     D(js::ImportEntryObject*) \
     D(js::LazyScript*) \
     D(js::ModuleEnvironmentObject*) \
     D(js::ModuleNamespaceObject*) \
     D(js::ModuleObject*) \
     D(js::NativeObject*) \
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -19,47 +19,59 @@ class PropertyName;
 class NativeObject;
 class ArrayObject;
 class GlobalObject;
 class PlainObject;
 class ScriptSourceObject;
 class SavedFrame;
 class Shape;
 class ObjectGroup;
+class DebuggerEnvironment;
+class DebuggerFrame;
+class DebuggerObject;
 
 // These are internal counterparts to the public types such as HandleObject.
 
-typedef JS::Handle<NativeObject*>      HandleNativeObject;
-typedef JS::Handle<Shape*>             HandleShape;
-typedef JS::Handle<ObjectGroup*>       HandleObjectGroup;
-typedef JS::Handle<JSAtom*>            HandleAtom;
-typedef JS::Handle<JSLinearString*>    HandleLinearString;
-typedef JS::Handle<PropertyName*>      HandlePropertyName;
-typedef JS::Handle<ArrayObject*>       HandleArrayObject;
-typedef JS::Handle<PlainObject*>       HandlePlainObject;
-typedef JS::Handle<SavedFrame*>        HandleSavedFrame;
-typedef JS::Handle<ScriptSourceObject*> HandleScriptSource;
+typedef JS::Handle<NativeObject*>           HandleNativeObject;
+typedef JS::Handle<Shape*>                  HandleShape;
+typedef JS::Handle<ObjectGroup*>            HandleObjectGroup;
+typedef JS::Handle<JSAtom*>                 HandleAtom;
+typedef JS::Handle<JSLinearString*>         HandleLinearString;
+typedef JS::Handle<PropertyName*>           HandlePropertyName;
+typedef JS::Handle<ArrayObject*>            HandleArrayObject;
+typedef JS::Handle<PlainObject*>            HandlePlainObject;
+typedef JS::Handle<SavedFrame*>             HandleSavedFrame;
+typedef JS::Handle<ScriptSourceObject*>     HandleScriptSource;
+typedef JS::Handle<DebuggerEnvironment*>    HandleDebuggerEnvironment;
+typedef JS::Handle<DebuggerFrame*>          HandleDebuggerFrame;
+typedef JS::Handle<DebuggerObject*>         HandleDebuggerObject;
 
-typedef JS::MutableHandle<Shape*>      MutableHandleShape;
-typedef JS::MutableHandle<JSAtom*>     MutableHandleAtom;
-typedef JS::MutableHandle<NativeObject*> MutableHandleNativeObject;
-typedef JS::MutableHandle<PlainObject*> MutableHandlePlainObject;
-typedef JS::MutableHandle<SavedFrame*> MutableHandleSavedFrame;
+typedef JS::MutableHandle<Shape*>               MutableHandleShape;
+typedef JS::MutableHandle<JSAtom*>              MutableHandleAtom;
+typedef JS::MutableHandle<NativeObject*>        MutableHandleNativeObject;
+typedef JS::MutableHandle<PlainObject*>         MutableHandlePlainObject;
+typedef JS::MutableHandle<SavedFrame*>          MutableHandleSavedFrame;
+typedef JS::MutableHandle<DebuggerEnvironment*> MutableHandleDebuggerEnvironment;
+typedef JS::MutableHandle<DebuggerFrame*>       MutableHandleDebuggerFrame;
+typedef JS::MutableHandle<DebuggerObject*>      MutableHandleDebuggerObject;
 
-typedef JS::Rooted<NativeObject*>      RootedNativeObject;
-typedef JS::Rooted<Shape*>             RootedShape;
-typedef JS::Rooted<ObjectGroup*>       RootedObjectGroup;
-typedef JS::Rooted<JSAtom*>            RootedAtom;
-typedef JS::Rooted<JSLinearString*>    RootedLinearString;
-typedef JS::Rooted<PropertyName*>      RootedPropertyName;
-typedef JS::Rooted<ArrayObject*>       RootedArrayObject;
-typedef JS::Rooted<GlobalObject*>      RootedGlobalObject;
-typedef JS::Rooted<PlainObject*>       RootedPlainObject;
-typedef JS::Rooted<SavedFrame*>        RootedSavedFrame;
-typedef JS::Rooted<ScriptSourceObject*> RootedScriptSource;
+typedef JS::Rooted<NativeObject*>           RootedNativeObject;
+typedef JS::Rooted<Shape*>                  RootedShape;
+typedef JS::Rooted<ObjectGroup*>            RootedObjectGroup;
+typedef JS::Rooted<JSAtom*>                 RootedAtom;
+typedef JS::Rooted<JSLinearString*>         RootedLinearString;
+typedef JS::Rooted<PropertyName*>           RootedPropertyName;
+typedef JS::Rooted<ArrayObject*>            RootedArrayObject;
+typedef JS::Rooted<GlobalObject*>           RootedGlobalObject;
+typedef JS::Rooted<PlainObject*>            RootedPlainObject;
+typedef JS::Rooted<SavedFrame*>             RootedSavedFrame;
+typedef JS::Rooted<ScriptSourceObject*>     RootedScriptSource;
+typedef JS::Rooted<DebuggerEnvironment*>    RootedDebuggerEnvironment;
+typedef JS::Rooted<DebuggerFrame*>          RootedDebuggerFrame;
+typedef JS::Rooted<DebuggerObject*>         RootedDebuggerObject;
 
 typedef JS::GCVector<JSFunction*>   FunctionVector;
 typedef JS::GCVector<PropertyName*> PropertyNameVector;
 typedef JS::GCVector<Shape*>        ShapeVector;
 typedef JS::GCVector<JSString*>     StringVector;
 
 } /* namespace js */
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1292858.js
@@ -0,0 +1,47 @@
+var caughtInvalidArguments = false;
+var a = -1
+try {
+    var buf = new Uint8ClampedArray(a);
+    throw new Error("didn't throw");
+} catch (e) {
+    assertEq(e instanceof TypeError, true,
+             "expected TypeError, instead threw: " + e);
+    caughtInvalidArguments = true;
+}
+assertEq(caughtInvalidArguments, true);
+
+var caughtInvalidArguments = false;
+var i = 0;
+while (true) {
+    i = (i + 1) | 0;
+    var a = inIon() ? -1 : 300;
+    try {
+        var buf = new Uint8ClampedArray(a);
+        assertEq(buf.length, 300);
+    } catch (e) {
+        assertEq(a, -1);
+        assertEq(e instanceof TypeError, true,
+                "expected TypeError, instead threw: " + e);
+        caughtInvalidArguments = true;
+        break;
+    }
+}
+assertEq(caughtInvalidArguments, true);
+
+var caughtInvalidArguments = false;
+var i = 0;
+while (true) {
+    i = (i + 1) | 0;
+    var a = inIon() ? -1 : 0;
+    try {
+        var buf = new Uint8ClampedArray(a);
+        assertEq(buf.length, 0);
+    } catch (e) {
+        assertEq(a, -1);
+        assertEq(e instanceof TypeError, true,
+                "expected TypeError, instead threw: " + e);
+        caughtInvalidArguments = true;
+        break;
+    }
+}
+assertEq(caughtInvalidArguments, true);
--- a/js/src/jit-test/tests/generators/bug931414.js
+++ b/js/src/jit-test/tests/generators/bug931414.js
@@ -1,9 +1,9 @@
-// |jit-test| error: is undefined
+// |jit-test| error: TypeError
 
 load(libdir + "iteration.js");
 
 function iterable() {
   var iterable = {};
   iterable[Symbol.iterator] = () => ({next: () => void 0});
   return iterable;
 }
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1291,16 +1291,39 @@ BaselineCompiler::emit_JSOP_HOLE()
 
 bool
 BaselineCompiler::emit_JSOP_NULL()
 {
     frame.push(NullValue());
     return true;
 }
 
+typedef bool (*ThrowCheckIsObjectFn)(JSContext*, CheckIsObjectKind);
+static const VMFunction ThrowCheckIsObjectInfo =
+    FunctionInfo<ThrowCheckIsObjectFn>(ThrowCheckIsObject);
+
+bool
+BaselineCompiler::emit_JSOP_CHECKISOBJ()
+{
+    frame.syncStack(0);
+    masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
+
+    Label ok;
+    masm.branchTestObject(Assembler::Equal, R0, &ok);
+
+    prepareVMCall();
+
+    pushArg(Imm32(GET_UINT8(pc)));
+    if (!callVM(ThrowCheckIsObjectInfo))
+        return false;
+
+    masm.bind(&ok);
+    return true;
+}
+
 typedef bool (*ThrowUninitializedThisFn)(JSContext*, BaselineFrame* frame);
 static const VMFunction ThrowUninitializedThisInfo =
     FunctionInfo<ThrowUninitializedThisFn>(BaselineThrowUninitializedThis);
 
 bool
 BaselineCompiler::emit_JSOP_CHECKTHIS()
 {
     frame.syncStack(0);
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -205,16 +205,17 @@ namespace jit {
     _(JSOP_RESUME)             \
     _(JSOP_CALLEE)             \
     _(JSOP_GETRVAL)            \
     _(JSOP_SETRVAL)            \
     _(JSOP_RETRVAL)            \
     _(JSOP_RETURN)             \
     _(JSOP_FUNCTIONTHIS)       \
     _(JSOP_GLOBALTHIS)         \
+    _(JSOP_CHECKISOBJ)         \
     _(JSOP_CHECKTHIS)          \
     _(JSOP_CHECKRETURN)        \
     _(JSOP_NEWTARGET)          \
     _(JSOP_SUPERCALL)          \
     _(JSOP_SPREADSUPERCALL)    \
     _(JSOP_THROWSETCONST)      \
     _(JSOP_THROWSETALIASEDCONST) \
     _(JSOP_INITHIDDENPROP_GETTER) \
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -394,22 +394,27 @@ jit::CanEnterBaselineMethod(JSContext* c
         }
     }
 
     RootedScript script(cx, state.script());
     return CanEnterBaselineJIT(cx, script, /* osrFrame = */ nullptr);
 };
 
 BaselineScript*
-BaselineScript::New(JSScript* jsscript, uint32_t prologueOffset, uint32_t epilogueOffset,
-                    uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset,
-                    uint32_t traceLoggerEnter