Merge m-c to autoland, a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 10 Aug 2016 16:56:26 -0700
changeset 309144 a2a3d88d28d0875938104b4352817abeaf7974c9
parent 309143 548a0e44482a6835ca7e870ecd40fa5ad5410de8 (current diff)
parent 308987 0502bd9e025edde29777ba1de4280f9b52af4663 (diff)
child 309145 6c2bac3a1afce83def2133e58c30ca1cf49887ff
push id80544
push userkwierso@gmail.com
push dateFri, 12 Aug 2016 23:29:38 +0000
treeherdermozilla-inbound@204eae54f875 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to 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);
+