Merge mozilla-central to inbound. a=merge on a CLOSED TREE
authorCosmin Sabou <csabou@mozilla.com>
Wed, 04 Apr 2018 21:07:07 +0300
changeset 411707 3c240f56a113f9cf0173d6d425806b572882522b
parent 411667 bbee7ebc2fce7e19391f09a6eaf2fd1430c00133 (current diff)
parent 411706 c23c7481957f7b982cffc0ce1d25979c69ca2c2f (diff)
child 411708 309a1389f9108c4adb81e3d8375a6a4ea4d2b038
push id101729
push usercsabou@mozilla.com
push dateWed, 04 Apr 2018 18:07:35 +0000
treeherdermozilla-inbound@3c240f56a113 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.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 mozilla-central to inbound. a=merge on a CLOSED TREE
browser/locales/en-US/chrome/browser/preferences/preferences.dtd
dom/media/MediaStreamGraph.cpp
dom/media/MediaStreamGraphImpl.h
other-licenses/nsis/Contrib/CityHash/CityHash.h
other-licenses/nsis/Contrib/CityHash/cityhash/city.cpp
toolkit/content/tests/widgets/test_bug1319301.html
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -49,17 +49,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "audioipc"
 version = "0.2.3"
 dependencies = [
  "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "cubeb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -85,17 +85,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "audioipc-server"
 version = "0.2.2"
 dependencies = [
  "audioipc 0.2.3",
  "bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "cubeb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -402,52 +402,52 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb"
-version = "0.5.0"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cubeb-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb-core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb-backend"
 version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "cubeb-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb-core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb-core"
-version = "0.5.0"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cubeb-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb-pulse"
 version = "0.2.0"
 dependencies = [
  "cubeb-backend 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "pulse 0.2.0",
  "pulse-ffi 0.1.0",
  "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cubeb-sys"
-version = "0.5.0"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
  "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)",
  "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -735,17 +735,17 @@ dependencies = [
 [[package]]
 name = "gkrust-shared"
 version = "0.1.0"
 dependencies = [
  "audioipc-client 0.4.0",
  "audioipc-server 0.2.2",
  "cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "cubeb-pulse 0.2.0",
- "cubeb-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cubeb-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding_c 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding_glue 0.1.0",
  "geckoservo 0.0.1",
  "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "mp4parse_capi 0.10.0",
  "netwerk_helper 0.0.1",
  "nserror 0.1.0",
  "nsstring 0.1.0",
@@ -2259,20 +2259,20 @@ dependencies = [
 "checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7"
 "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
 "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
 "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
 "checksum cssparser 0.23.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b906ac3f6108d8d0bfd4158469abe5909df1497116c8400346b5e08944579edd"
 "checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df"
 "checksum cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b6557bdb1dc9647eae1cf7f5601b14cd45fc3c7ccf2df618387416fe542da6ea"
 "checksum cstr-macros 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f9f316203d1ea36f4f18316822806f6999aa3dc5ed1adf51e35b77e3b3933d78"
-"checksum cubeb 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7923bed2d5a1a64ba0c3e8b6badc360508ba488d1f2f59f16a688802e755bb85"
+"checksum cubeb 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8a3502aafa1bf95c524f65d2ba46d8741700c6a8a9543ea52c6da3d8b69a2896"
 "checksum cubeb-backend 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcac95519416d9ec814db2dc40e6293e7da25b906023d93f48b87f0587ab138"
-"checksum cubeb-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "169d9a36f5daa60f9c4597905132aef056cf62693addd4a3421492853ccad565"
-"checksum cubeb-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e405ad4dff2c1a7cbfa6998e5925e8ceafe900feeacfcad35a3e3790bea0f2aa"
+"checksum cubeb-core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37f7b20f757a4e4b6aa28863236551bff77682dc6db192eba15af615492b5445"
+"checksum cubeb-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "653b9e245d35dbe2a2da7c4586275cee75ff656ddeb02d4a73b4afdfa6d67502"
 "checksum darling 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d3effd06d4057f275cb7858889f4952920bab78dd8ff0f6e7dfe0c8d2e67ed89"
 "checksum darling_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "167dd3e235c2f1da16a635c282630452cdf49191eb05711de1bcd1d3d5068c00"
 "checksum darling_macro 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c53edaba455f6073a10c27c72440860eb3f60444f8c8660a391032eeae744d82"
 "checksum debug_unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9a032eac705ca39214d169f83e3d3da290af06d8d1d344d1baad2fd002dca4b3"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
 "checksum dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "068d4026697c1a18f0b0bb8cfcad1b0c151b90d8edb9bf4c235ad68128920d1d"
 "checksum dwrote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b26e30aaa6bf31ec830db15fec14ed04f0f2ecfcc486ecfce88c55d3389b237f"
 "checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a"
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -13,18 +13,16 @@
   href="chrome://browser/content/preferences/handlers.css"?>
 <?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
 <?xml-stylesheet href="chrome://browser/skin/preferences/in-content/search.css"?>
 <?xml-stylesheet href="chrome://browser/skin/preferences/in-content/containers.css"?>
 <?xml-stylesheet href="chrome://browser/skin/preferences/in-content/privacy.css"?>
 
 <!DOCTYPE page [
 <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
-<!ENTITY % preferencesDTD SYSTEM
-  "chrome://browser/locale/preferences/preferences.dtd">
 <!ENTITY % selectBookmarkDTD SYSTEM
   "chrome://browser/locale/preferences/selectBookmark.dtd">
 <!ENTITY % languagesDTD SYSTEM "chrome://browser/locale/preferences/languages.dtd">
 <!ENTITY % fontDTD SYSTEM "chrome://browser/locale/preferences/fonts.dtd">
 <!ENTITY % colorsDTD SYSTEM "chrome://browser/locale/preferences/colors.dtd">
 <!ENTITY % permissionsDTD SYSTEM "chrome://browser/locale/preferences/permissions.dtd">
 <!ENTITY % passwordManagerDTD SYSTEM "chrome://passwordmgr/locale/passwordManager.dtd">
 <!ENTITY % historyDTD SYSTEM "chrome://mozapps/locale/update/history.dtd">
@@ -40,17 +38,16 @@
 <!ENTITY % syncDTD SYSTEM "chrome://browser/locale/preferences/sync.dtd">
 <!ENTITY % sanitizeDTD SYSTEM "chrome://browser/locale/sanitize.dtd">
 <!ENTITY % mainDTD SYSTEM "chrome://browser/locale/preferences/main.dtd">
 <!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
 <!ENTITY % contentDTD SYSTEM "chrome://browser/locale/preferences/content.dtd">
 <!ENTITY % aboutDialogDTD SYSTEM "chrome://browser/locale/aboutDialog.dtd" >
 %aboutDialogDTD;
 %brandDTD;
-%preferencesDTD;
 %selectBookmarkDTD;
 %languagesDTD;
 %fontDTD;
 %colorsDTD;
 %permissionsDTD;
 %passwordManagerDTD;
 %historyDTD;
 %certManagerDTD;
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -113,17 +113,17 @@ var gSyncPane = {
     let username = Services.prefs.getCharPref("services.sync.username", "");
     if (!username) {
       this.page = FXA_PAGE_LOGGED_OUT;
       return;
     }
 
     // Use cached values while we wait for the up-to-date values
     let cachedComputerName = Services.prefs.getCharPref("services.sync.client.name", "");
-    document.querySelector(".fxaEmailAddress").value = username;
+    document.getElementById("fxaEmailAddress").textContent = username;
     this._populateComputerName(cachedComputerName);
     this.page = FXA_PAGE_LOGGED_IN;
   },
 
   _init() {
     // Add the observers now and remove them on unload
     // XXXzpao This should use Services.obs.* but Weave's Obs does nice handling
     //        of `this`. Fix in a followup. (bug 583347)
@@ -254,17 +254,17 @@ var gSyncPane = {
   },
 
   updateWeavePrefs() {
     let service = Cc["@mozilla.org/weave/service;1"]
       .getService(Ci.nsISupports)
       .wrappedJSObject;
 
     let displayNameLabel = document.getElementById("fxaDisplayName");
-    let fxaEmailAddressLabels = document.querySelectorAll(".fxaEmailAddress");
+    let fxaEmailAddressLabels = document.querySelectorAll(".l10nArgsEmailAddress");
     displayNameLabel.hidden = true;
 
     // determine the fxa status...
     this._showLoadPage(service);
 
     let state = UIState.get();
     if (state.status == UIState.STATUS_NOT_CONFIGURED) {
       this.page = FXA_PAGE_LOGGED_OUT;
@@ -283,18 +283,21 @@ var gSyncPane = {
       fxaLoginStatus.selectedIndex = FXA_LOGIN_UNVERIFIED;
     } else {
       // We must be golden (or in an error state we expect to magically
       // resolve itself)
       fxaLoginStatus.selectedIndex = FXA_LOGIN_VERIFIED;
       syncReady = true;
     }
     fxaEmailAddressLabels.forEach((label) => {
-      label.value = state.email;
+      let l10nAttrs = document.l10n.getAttributes(label);
+      document.l10n.setAttributes(l10nAttrs.id, {email: state.email});
     });
+    document.getElementById("fxaEmailAddress").textContent = state.email;
+
     this._populateComputerName(Weave.Service.clientsEngine.localName);
     let engines = document.getElementById("fxaSyncEngines");
     for (let checkbox of engines.querySelectorAll("checkbox")) {
       checkbox.disabled = !syncReady;
     }
     document.getElementById("fxaChangeDeviceName").disabled = !syncReady;
 
     // Clear the profile image (if any) of the previously logged in account.
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -6,49 +6,48 @@
 
 <script type="application/javascript"
         src="chrome://browser/content/preferences/in-content/sync.js"/>
 
 <hbox id="firefoxAccountCategory"
       class="subcategory"
       hidden="true"
       data-category="paneSync">
-  <label class="header-name" flex="1">&paneSync1.title;</label>
+  <label class="header-name" flex="1" data-l10n-id="pane-sync-title" />
 </hbox>
 
 <deck id="weavePrefsDeck" data-category="paneSync" hidden="true"
       data-hidden-from-search="true">
   <groupbox id="noFxaAccount">
     <hbox>
       <vbox flex="1">
-        <caption><label id="noFxaCaption">&signedOut.caption;</label></caption>
-        <description id="noFxaDescription" flex="1">&signedOut.description;</description>
+        <caption><label id="noFxaCaption" data-l10n-id="sync-signedout-caption"/></caption>
+        <description id="noFxaDescription" flex="1" data-l10n-id="sync-signedout-description"/>
       </vbox>
       <vbox>
         <image class="fxaSyncIllustration"/>
       </vbox>
     </hbox>
     <hbox id="fxaNoLoginStatus" align="center" flex="1">
       <vbox>
         <image class="fxaProfileImage"/>
       </vbox>
       <vbox flex="1">
         <hbox align="center" flex="1">
           <hbox align="center" flex="1">
-            <caption><label id="signedOutAccountBoxTitle">&signedOut.accountBox.title;</label></caption>
+            <caption><label id="signedOutAccountBoxTitle" data-l10n-id="sync-signedout-account-title"/></caption>
           </hbox>
           <button id="noFxaSignIn"
                   class="accessory-button"
-                  label="&signedOut.accountBox.signin2;"
-                  accesskey="&signedOut.accountBox.signin2.accesskey;"/>
+                  data-l10n-id="sync-signedout-account-signin"/>
         </hbox>
         <hbox align="center" flex="1">
           <html:a id="noFxaSignUp"
                   class="openLink"
-                  accesskey="&signedOut.accountBox.create2.accesskey;">&signedOut.accountBox.create2;</html:a>
+                  data-l10n-id="sync-signedout-account-create" />
         </hbox>
       </vbox>
     </hbox>
     <label class="fxaMobilePromo">
         &mobilePromo3.start;<!-- We put these comments to avoid inserting white spaces
         --><image class="androidLink"></image><label id="fxaMobilePromo-android"
                   class="text-link"><!--
         -->&mobilePromo3.androidLink;</label><!--
@@ -59,166 +58,134 @@
         -->&mobilePromo3.end;
     </label>
   </groupbox>
 
   <vbox id="hasFxaAccount">
     <hbox>
       <vbox id="fxaContentWrapper" flex="1">
         <groupbox id="fxaGroup">
-          <caption class="search-header" hidden="true"><label>&paneSync1.title;</label></caption>
+          <caption class="search-header" hidden="true"><label data-l10n-id="pane-sync-title"/></caption>
 
           <deck id="fxaLoginStatus" flex="1">
 
             <!-- logged in and verified and all is good -->
             <hbox id="fxaLoginVerified" align="center" flex="1">
               <image class="fxaProfileImage actionable"
                      role="button"
                      onclick="gSyncPane.openChangeProfileImage(event);"
                      onkeypress="gSyncPane.openChangeProfileImage(event);"
-                     tooltiptext="&profilePicture.tooltip;"/>
+                     data-l10n-id="sync-profile-picture"/>
               <vbox flex="1" pack="center">
                 <hbox flex="1" align="baseline">
                   <caption><label id="fxaDisplayName" hidden="true"/></caption>
-                  <label class="fxaEmailAddress" flex="1" crop="end"/>
+                  <label id="fxaEmailAddress" flex="1" crop="end"/>
                   <button id="fxaUnlinkButton"
                           class="accessory-button"
-                          label="&disconnect3.label;"
-                          accesskey="&disconnect3.accesskey;"/>
+                          data-l10n-id="sync-disconnect"/>
                 </hbox>
                 <hbox>
                   <html:a id="verifiedManage" class="openLink"
-                          accesskey="&verifiedManage.accesskey;"
-                          onkeypress="gSyncPane.openManageFirefoxAccount(event);">&verifiedManage.label;</html:a>
+                          data-l10n-id="sync-manage-account"
+                          onkeypress="gSyncPane.openManageFirefoxAccount(event);"/>
                 </hbox>
               </vbox>
             </hbox>
 
             <!-- logged in to an unverified account -->
             <hbox id="fxaLoginUnverified">
               <vbox>
                 <image class="fxaProfileImage"/>
               </vbox>
               <vbox flex="1" pack="center">
                 <hbox>
                   <image class="fxaLoginRejectedWarning"/>
-                  <description flex="1">
-                    &signedInUnverified.beforename.label;
-                    <label class="fxaEmailAddress"/>
-                    &signedInUnverified.aftername.label;
-                  </description>
+                  <description flex="1"
+                    class="l10nArgsEmailAddress"
+                    data-l10n-id="sync-signedin-unverified"
+                    data-l10n-args='{"email": ""}'/>
                 </hbox>
                 <hbox class="fxaAccountBoxButtons">
-                  <button id="verifyFxaAccount" label="&resendVerification.label;" accesskey="&resendVerification.accesskey;"></button>
-                  <button id="unverifiedUnlinkFxaAccount" label="&removeAccount.label;" accesskey="&removeAccount.accesskey;"></button>
+                  <button id="verifyFxaAccount" data-l10n-id="sync-resend-verification"/>
+                  <button id="unverifiedUnlinkFxaAccount" data-l10n-id="sync-remove-account"/>
                 </hbox>
               </vbox>
             </hbox>
 
             <!-- logged in locally but server rejected credentials -->
             <hbox id="fxaLoginRejected">
               <vbox>
                 <image class="fxaProfileImage"/>
               </vbox>
               <vbox flex="1" pack="center">
                 <hbox>
                   <image class="fxaLoginRejectedWarning"/>
-                  <description flex="1">
-                    &signedInLoginFailure.beforename.label;
-                    <label class="fxaEmailAddress"/>
-                    &signedInLoginFailure.aftername.label;
-                  </description>
+                  <description flex="1"
+                    class="l10nArgsEmailAddress"
+                    data-l10n-id="sync-signedin-login-failure"
+                    data-l10n-args='{"email": ""}'/>
                 </hbox>
                 <hbox class="fxaAccountBoxButtons">
-                  <button id="rejectReSignIn" label="&signIn.label;" accesskey="&signIn.accesskey;"></button>
-                  <button id="rejectUnlinkFxaAccount" label="&removeAccount.label;" accesskey="&removeAccount.accesskey;"></button>
+                  <button id="rejectReSignIn" data-l10n-id="sync-sign-in"/>
+                  <button id="rejectUnlinkFxaAccount" data-l10n-id="sync-remove-account"/>
                 </hbox>
               </vbox>
             </hbox>
           </deck>
         </groupbox>
         <groupbox id="syncOptions">
-          <caption><label>&signedIn.settings.label;</label></caption>
-          <description>&signedIn.settings.description;</description>
+          <caption><label data-l10n-id="sync-signedin-settings-header"/></caption>
+          <description data-l10n-id="sync-signedin-settings-desc"/>
           <hbox id="fxaSyncEngines">
             <vbox flex="1">
               <!-- by design, no tooltip for bookmarks or history -->
-              <checkbox label="&engine.bookmarks.label;"
-                        accesskey="&engine.bookmarks.accesskey;"
+              <checkbox data-l10n-id="sync-engine-bookmarks"
                         preference="engine.bookmarks"/>
-              <checkbox label="&engine.history.label;"
-                        accesskey="&engine.history.accesskey;"
+              <checkbox data-l10n-id="sync-engine-history"
                         preference="engine.history"/>
-              <checkbox label="&engine.tabs.label2;"
-                        tooltiptext="&engine.tabs.title;"
-                        accesskey="&engine.tabs.accesskey;"
+              <checkbox data-l10n-id="sync-engine-tabs"
                         preference="engine.tabs"/>
-              <checkbox label="&engine.logins.label;"
-                        tooltiptext="&engine.logins.title;"
-                        accesskey="&engine.logins.accesskey;"
+              <checkbox data-l10n-id="sync-engine-logins"
                         preference="engine.passwords"/>
             </vbox>
             <vbox flex="1">
-              <checkbox label="&engine.addresses.label;"
-                        accesskey="&engine.addresses.accesskey;"
-                        tooltiptext="&engine.addresses.title;"
+              <checkbox data-l10n-id="sync-engine-addresses"
                         preference="engine.addresses"/>
-              <checkbox label="&engine.creditcards.label;"
-                        tooltiptext="&engine.creditcards.title;"
-                        accesskey="&engine.creditcards.accesskey;"
+              <checkbox data-l10n-id="sync-engine-creditcards"
                         preference="engine.creditcards"/>
-              <checkbox label="&engine.addons.label;"
-                        tooltiptext="&engine.addons.title;"
-                        accesskey="&engine.addons.accesskey;"
+              <checkbox data-l10n-id="sync-engine-addons"
                         preference="engine.addons"/>
-              <checkbox
-#ifdef XP_WIN
-                        label="&engine.prefsWin.label;"
-                        accesskey="&engine.prefsWin.accesskey;"
-#else
-                        label="&engine.prefs.label;"
-                        accesskey="&engine.prefs.accesskey;"
-#endif
-                        tooltiptext="&engine.prefs.title;"
+              <checkbox data-l10n-id="sync-engine-prefs"
                         preference="engine.prefs"/>
             </vbox>
             <spacer/>
           </hbox>
         </groupbox>
       </vbox>
     </hbox>
     <groupbox>
       <caption>
-        <label control="fxaSyncComputerName">
-          &fxaSyncDeviceName.label;
-        </label>
+        <label control="fxaSyncComputerName" data-l10n-id="sync-device-name-header"/>
       </caption>
       <hbox id="fxaDeviceName">
         <textbox id="fxaSyncComputerName" flex="1" disabled="true"/>
         <button id="fxaChangeDeviceName"
-                label="&changeSyncDeviceName2.label;"
-                accesskey="&changeSyncDeviceName2.accesskey;"/>
+                data-l10n-id="sync-device-name-change"/>
         <button id="fxaCancelChangeDeviceName"
-                label="&cancelChangeSyncDeviceName.label;"
-                accesskey="&cancelChangeSyncDeviceName.accesskey;"
+                data-l10n-id="sync-device-name-cancel"
                 hidden="true"/>
         <button id="fxaSaveChangeDeviceName"
-                label="&saveChangeSyncDeviceName.label;"
-                accesskey="&saveChangeSyncDeviceName.accesskey;"
+                data-l10n-id="sync-device-name-save"
                 hidden="true"/>
       </hbox>
     </groupbox>
     <vbox align="start">
       <label id="mobilePromo-singledevice"
-             class="text-link fxaMobilePromo">&mobilepromo.singledevice;</label>
+             class="text-link fxaMobilePromo" data-l10n-id="sync-mobilepromo-single"/>
       <label id="mobilePromo-multidevice"
-             class="text-link fxaMobilePromo">&mobilepromo.multidevice;</label>
+             class="text-link fxaMobilePromo" data-l10n-id="sync-mobilepromo-multi"/>
     </vbox>
     <vbox id="tosPP-small" align="start">
-      <label id="tosPP-small-ToS" class="text-link">
-        &prefs.tosLink.label;
-      </label>
-      <label id="tosPP-small-PP" class="text-link">
-        &fxaPrivacyNotice.link.label;
-      </label>
+      <label id="tosPP-small-ToS" class="text-link" data-l10n-id="sync-tos-link"/>
+      <label id="tosPP-small-PP" class="text-link" data-l10n-id="sync-fxa-privacy-notice"/>
     </vbox>
   </vbox>
 </deck>
--- a/browser/locales/en-US/browser/branding/sync-brand.ftl
+++ b/browser/locales/en-US/browser/branding/sync-brand.ftl
@@ -2,8 +2,12 @@
 # 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/.
 
 -sync-brand-short-name = Sync
 
 # “Sync” can be localized, “Firefox” must be treated as a brand,
 # and kept in English.
 -sync-brand-name = Firefox Sync
+
+# “Account” can be localized, “Firefox” must be treated as a brand,
+# and kept in English.
+-fxaccount-brand-name = Firefox Account
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -466,16 +466,123 @@ containers-add-button =
     .label = Add New Container
     .accesskey = A
 
 containers-preferences-button =
     .label = Preferences
 containers-remove-button =
     .label = Remove
 
+## Sync Section - Signed out
+
+sync-signedout-caption = Take Your Web With You
+sync-signedout-description = Synchronize your bookmarks, history, tabs, passwords, add-ons, and preferences across all your devices.
+
+sync-signedout-account-title = Connect with a { -fxaccount-brand-name }
+sync-signedout-account-create = Don’t have an account? Get started
+    .accesskey = c
+
+sync-signedout-account-signin =
+    .label = Sign In…
+    .accesskey = I
+
+## Sync Section - Signed in
+
+sync-profile-picture =
+    .tooltiptext = Change profile picture
+
+sync-disconnect =
+    .label = Disconnect…
+    .accesskey = D
+
+sync-manage-account = Manage account
+    .accesskey = o
+
+sync-signedin-unverified = { $email } is not verified.
+sync-signedin-login-failure = Please sign in to reconnect { $email }
+
+sync-resend-verification =
+    .label = Resend Verification
+    .accesskey = d
+
+sync-remove-account =
+    .label = Remove Account
+    .accesskey = R
+
+sync-sign-in =
+    .label = Sign in
+    .accesskey = g
+
+sync-signedin-settings-header = Sync Settings
+sync-signedin-settings-desc = Choose what to synchronize on your devices using { -brand-short-name }
+
+sync-engine-bookmarks =
+    .label = Bookmarks
+    .accesskey = m
+
+sync-engine-history =
+    .label = History
+    .accesskey = r
+
+sync-engine-tabs =
+    .label = Open tabs
+    .tooltiptext = A list of what’s open on all synced devices
+    .accesskey = t
+
+sync-engine-logins =
+    .label = Logins
+    .tooltiptext = Usernames and passwords you’ve saved
+    .accesskey = L
+
+sync-engine-addresses =
+    .label = Addresses
+    .tooltiptext = Postal addresses you’ve saved (desktop only)
+    .accesskey = e
+
+sync-engine-creditcards =
+    .label = Credit cards
+    .tooltiptext = Names, numbers and expiry dates (desktop only)
+    .accesskey = C
+
+sync-engine-addons =
+    .label = Add-ons
+    .tooltiptext = Extensions and themes for Firefox desktop
+    .accesskey = A
+
+sync-engine-prefs =
+    .label =
+        { PLATFORM() ->
+            [windows] Options
+           *[other] Preferences
+        }
+    .tooltiptext = General, Privacy, and Security settings you’ve changed
+    .accesskey = s
+
+sync-device-name-header = Device Name
+
+sync-device-name-change =
+    .label = Change Device Name…
+    .accesskey = h
+
+sync-device-name-cancel =
+    .label = Cancel
+    .accesskey = n
+
+sync-device-name-save =
+    .label = Save
+    .accesskey = v
+
+sync-mobilepromo-single = Connect another device
+
+sync-mobilepromo-multi = Manage devices
+
+sync-tos-link = Terms of Service
+
+sync-fxa-privacy-notice = Privacy Notice
+
 ## Privacy Section
 
 privacy-header = Browser Privacy
 
 ## Privacy Section - Forms
 
 forms-header = Forms & Passwords
 forms-remember-logins =
deleted file mode 100644
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.dtd
+++ /dev/null
@@ -1,6 +0,0 @@
-<!-- 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/. -->
-
-<!-- LOCALIZATION NOTE (paneSync1.title): This should match syncBrand.fxAccount.label in ../syncBrand.dtd -->
-<!ENTITY  paneSync1.title          "Firefox Account">
--- a/browser/locales/en-US/chrome/browser/preferences/sync.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/sync.dtd
@@ -1,106 +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/. -->
 
-<!-- The page shown when logged in... -->
-
-<!ENTITY engine.bookmarks.label     "Bookmarks">
-<!ENTITY engine.bookmarks.accesskey "m">
-<!ENTITY engine.tabs.label2         "Open tabs">
-<!ENTITY engine.tabs.title          "A list of what’s open on all synced devices">
-<!ENTITY engine.tabs.accesskey      "T">
-<!ENTITY engine.history.label       "History">
-<!ENTITY engine.history.accesskey   "r">
-<!ENTITY engine.logins.label        "Logins">
-<!ENTITY engine.logins.title        "Usernames and passwords you’ve saved">
-<!ENTITY engine.logins.accesskey    "L">
-<!-- On Windows we use the term "Options" to describe settings, but
-     on Linux and Mac OS X we use "Preferences" - carry that distinction
-     over into this string, used as the checkbox which indicates if prefs are synced
--->
-<!ENTITY engine.prefsWin.label      "Options">
-<!ENTITY engine.prefsWin.accesskey  "S">
-<!ENTITY engine.prefs.label         "Preferences">
-<!ENTITY engine.prefs.accesskey     "S">
-<!ENTITY engine.prefs.title         "General, Privacy, and Security settings you’ve changed">
-<!ENTITY engine.addons.label        "Add-ons">
-<!ENTITY engine.addons.title        "Extensions and themes for Firefox desktop">
-<!ENTITY engine.addons.accesskey    "A">
-<!ENTITY engine.addresses.label     "Addresses">
-<!ENTITY engine.addresses.title     "Postal addresses you’ve saved (desktop only)">
-<!ENTITY engine.addresses.accesskey "e">
-<!ENTITY engine.creditcards.label   "Credit cards">
-<!ENTITY engine.creditcards.title   "Names, numbers and expiry dates (desktop only)">
-<!ENTITY engine.creditcards.accesskey "C">
-
-<!-- Device Settings -->
-<!ENTITY fxaSyncDeviceName.label       "Device Name">
-<!ENTITY changeSyncDeviceName2.label "Change Device Name…">
-<!ENTITY changeSyncDeviceName2.accesskey "h">
-<!ENTITY cancelChangeSyncDeviceName.label "Cancel">
-<!ENTITY cancelChangeSyncDeviceName.accesskey "n">
-<!ENTITY saveChangeSyncDeviceName.label "Save">
-<!ENTITY saveChangeSyncDeviceName.accesskey "v">
-
-<!-- Footer stuff -->
-<!ENTITY prefs.tosLink.label        "Terms of Service">
-<!ENTITY fxaPrivacyNotice.link.label "Privacy Notice">
-
-<!-- LOCALIZATION NOTE (signedInUnverified.beforename.label,
-signedInUnverified.aftername.label): these two string are used respectively
-before and after the account email address. Localizers can use one of them, or
-both, to better adapt this sentence to their language.
--->
-<!ENTITY signedInUnverified.beforename.label "">
-<!ENTITY signedInUnverified.aftername.label "is not verified.">
-
-<!-- LOCALIZATION NOTE (signedInLoginFailure.beforename.label,
-signedInLoginFailure.aftername.label): these two string are used respectively
-before and after the account email address. Localizers can use one of them, or
-both, to better adapt this sentence to their language.
--->
-<!ENTITY signedInLoginFailure.beforename.label "Please sign in to reconnect">
-<!ENTITY signedInLoginFailure.aftername.label "">
-
-<!ENTITY notSignedIn.label            "You are not signed in.">
-<!ENTITY signIn.label                 "Sign in">
-<!ENTITY signIn.accesskey             "g">
-<!ENTITY profilePicture.tooltip       "Change profile picture">
-<!ENTITY verifiedManage.label         "Manage account">
-<!ENTITY verifiedManage.accesskey     "o">
-<!ENTITY disconnect3.label            "Disconnect…">
-<!ENTITY disconnect3.accesskey        "D">
-<!ENTITY resendVerification.label     "Resend Verification">
-<!ENTITY resendVerification.accesskey "d">
-<!ENTITY removeAccount.label          "Remove Account">
-<!ENTITY removeAccount.accesskey      "R">
-
-<!ENTITY signedOut.caption            "Take Your Web With You">
-<!ENTITY signedOut.description        "Synchronize your bookmarks, history, tabs, passwords, add-ons, and preferences across all your devices.">
-<!ENTITY signedOut.accountBox.title   "Connect with a &syncBrand.fxAccount.label;">
-<!ENTITY signedOut.accountBox.create2 "Don’t have an account? Get started">
-<!ENTITY signedOut.accountBox.create2.accesskey "C">
-<!ENTITY signedOut.accountBox.signin2 "Sign In…">
-<!ENTITY signedOut.accountBox.signin2.accesskey "I">
-
-<!ENTITY signedIn.settings.label       "Sync Settings">
-<!ENTITY signedIn.settings.description "Choose what to synchronize on your devices using &brandShortName;.">
-
 <!-- LOCALIZATION NOTE (mobilePromo3.*): the following strings will be used to
      create a single sentence with active links.
      The resulting sentence in English is: "Download Firefox for
      Android or iOS to sync with your mobile device." -->
 
 <!ENTITY mobilePromo3.start            "Download Firefox for ">
 <!-- LOCALIZATION NOTE (mobilePromo3.androidLink): This is a link title that links to https://www.mozilla.org/firefox/android/ -->
 <!ENTITY mobilePromo3.androidLink      "Android">
 
 <!-- LOCALIZATION NOTE (mobilePromo3.iOSBefore): This is text displayed between mobilePromo3.androidLink and mobilePromo3.iosLink -->
 <!ENTITY mobilePromo3.iOSBefore         " or ">
 <!-- LOCALIZATION NOTE (mobilePromo3.iOSLink): This is a link title that links to https://www.mozilla.org/firefox/ios/ -->
 <!ENTITY mobilePromo3.iOSLink          "iOS">
 
 <!ENTITY mobilePromo3.end              " to sync with your mobile device.">
-
-<!ENTITY mobilepromo.singledevice      "Connect another device">
-<!ENTITY mobilepromo.multidevice       "Manage devices">
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -65,17 +65,16 @@
     locale/browser/preferences/colors.dtd             (%chrome/browser/preferences/colors.dtd)
     locale/browser/preferences/connection.dtd         (%chrome/browser/preferences/connection.dtd)
     locale/browser/preferences/content.dtd            (%chrome/browser/preferences/content.dtd)
     locale/browser/preferences/cookies.dtd            (%chrome/browser/preferences/cookies.dtd)
     locale/browser/preferences/fonts.dtd              (%chrome/browser/preferences/fonts.dtd)
     locale/browser/preferences/languages.dtd          (%chrome/browser/preferences/languages.dtd)
     locale/browser/preferences/main.dtd               (%chrome/browser/preferences/main.dtd)
     locale/browser/preferences/permissions.dtd        (%chrome/browser/preferences/permissions.dtd)
-    locale/browser/preferences/preferences.dtd        (%chrome/browser/preferences/preferences.dtd)
     locale/browser/preferences/preferences.properties     (%chrome/browser/preferences/preferences.properties)
     locale/browser/preferences/privacy.dtd            (%chrome/browser/preferences/privacy.dtd)
     locale/browser/preferences/security.dtd           (%chrome/browser/preferences/security.dtd)
     locale/browser/preferences/selectBookmark.dtd     (%chrome/browser/preferences/selectBookmark.dtd)
     locale/browser/preferences/siteDataSettings.dtd     (%chrome/browser/preferences/siteDataSettings.dtd)
     locale/browser/preferences/sync.dtd               (%chrome/browser/preferences/sync.dtd)
     locale/browser/preferences/translation.dtd        (%chrome/browser/preferences/translation.dtd)
     locale/browser/syncBrand.dtd                (%chrome/browser/syncBrand.dtd)
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -599,20 +599,16 @@ button > hbox > label {
 .openLink:visited {
   color: var(--in-content-link-color);
 }
 
 #fxaDisplayName {
   margin-inline-end: 10px !important;
 }
 
-.fxaEmailAddress {
-  margin-inline-end: 8px !important;
-}
-
 .fxaLoginRejectedWarning {
   list-style-image: url(chrome://browser/skin/warning.svg);
   margin-inline-start: 4px;
   margin-inline-end: 8px;
 }
 
 #fxaSyncEngines > vbox > checkbox {
   max-width: 224px;
--- a/build/clang-plugin/mozsearch-plugin/MozsearchIndexer.cpp
+++ b/build/clang-plugin/mozsearch-plugin/MozsearchIndexer.cpp
@@ -1078,22 +1078,19 @@ public:
     }
 
     return Range;
   }
 
   bool VisitNamedDecl(NamedDecl *D) {
     SourceLocation Loc = D->getLocation();
 
-    if (isa<EnumConstantDecl>(D) && SM.isMacroBodyExpansion(Loc)) {
-      // for enum constants generated by macro expansion, update location
-      // to point to the expansion location as that is more useful. We might
-      // want to do this for more token types but until we have good regression
-      // testing for the Indexer it's best to be as conservative and explicit
-      // as possible with the changes.
+    // If the token is from a macro expansion and the expansion location
+    // is interesting, use that instead as it tends to be more useful.
+    if (SM.isMacroBodyExpansion(Loc)) {
       Loc = SM.getFileLoc(Loc);
     }
 
     normalizeLocation(&Loc);
     if (!isInterestingLocation(Loc)) {
       return true;
     }
 
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -125,16 +125,17 @@ support-files =
   examples/pause-points.js
   examples/script-mutate.js
   examples/script-switching-02.js
   examples/script-switching-01.js
   examples/times2.js
 
 [browser_dbg-asm.js]
 [browser_dbg-async-stepping.js]
+[browser_dbg-babel-breakpoint-console.js]
 [browser_dbg-babel-scopes.js]
 [browser_dbg-babel-stepping.js]
 [browser_dbg-babel-preview.js]
 [browser_dbg-breaking.js]
 [browser_dbg-breaking-from-console.js]
 [browser_dbg-breakpoints.js]
 [browser_dbg-breakpoints-toggle.js]
 [browser_dbg-breakpoints-reloading.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-breakpoint-console.js
@@ -0,0 +1,95 @@
+
+async function evalInConsoleAtPoint(dbg, fixture, { line, column }, statements) {
+  const { selectors: { getBreakpoint, getBreakpoints }, getState } = dbg;
+
+  const filename = `fixtures/${fixture}/input.js`;
+  await waitForSources(dbg, filename);
+
+  ok(true, "Original sources exist");
+  const source = findSource(dbg, filename);
+
+  await selectSource(dbg, source);
+
+  // Test that breakpoint is not off by a line.
+  await addBreakpoint(dbg, source, line);
+
+  is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
+  ok(
+    getBreakpoint(getState(), { sourceId: source.id, line, column }),
+    "Breakpoint has correct line"
+  );
+
+  const fnName = fixture.replace(/-([a-z])/g, (s, c) => c.toUpperCase());
+
+  const invokeResult = invokeInTab(fnName);
+
+  let invokeFailed = await Promise.race([
+    waitForPaused(dbg),
+    invokeResult.then(() => new Promise(() => {}), () => true)
+  ]);
+
+  if (invokeFailed) {
+    return invokeResult;
+  }
+
+  assertPausedLocation(dbg);
+
+  await assertConsoleEval(dbg, statements);
+
+  await removeBreakpoint(dbg, source.id, line, column);
+
+  is(getBreakpoints(getState()).size, 0, "Breakpoint reverted");
+
+  await resume(dbg);
+
+  // If the invoke errored later somehow, capture here so the error is reported nicely.
+  await invokeResult;
+
+  ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`);
+}
+
+async function assertConsoleEval(dbg, statements) {
+  const jsterm = (await dbg.toolbox.selectTool("webconsole")).hud.jsterm;
+
+  for (const [index, statement] of statements.entries()) {
+    await dbg.client.evaluate(`
+      window.TEST_RESULT = false;
+    `);
+    await jsterm.execute(`
+      TEST_RESULT = ${statement};
+    `);
+
+    const result = await dbg.client.evaluate(`window.TEST_RESULT`);
+    is(result.result, true, `'${statement}' evaluates to true`);
+  }
+}
+
+add_task(async function() {
+  await pushPref("devtools.debugger.features.map-scopes", true);
+
+  const dbg = await initDebugger("doc-babel.html");
+
+  await evalInConsoleAtPoint(dbg, "eval-source-maps", { line: 14, column: 4 }, [
+    "one === 1",
+    "two === 4",
+    "three === 5",
+  ]);
+
+  await evalInConsoleAtPoint(dbg, "imported-bindings", { line: 20, column: 2 }, [
+    `aDefault === "a-default"`,
+    `anAliased === "an-original"`,
+    `aNamed === "a-named"`,
+    `aDefault2 === "a-default2"`,
+    `anAliased2 === "an-original2"`,
+    `aNamed2 === "a-named2"`,
+    `aDefault3 === "a-default3"`,
+    `anAliased3 === "an-original3"`,
+    `aNamed3 === "a-named3"`,
+  ]);
+
+  await evalInConsoleAtPoint(dbg, "shadowed-vars", { line: 18, column: 6 }, [
+    `aVar === "var3"`,
+    `aLet === "let3"`,
+    `aConst === "const3"`,
+  ]);
+});
--- a/devtools/client/debugger/panel.js
+++ b/devtools/client/debugger/panel.js
@@ -126,16 +126,22 @@ DebuggerPanel.prototype = {
 
     return this._destroyer = this._controller.shutdownDebugger().then(() => {
       this.emit("destroyed");
     });
   },
 
   // DebuggerPanel API
 
+  getMappedExpression(expression) {
+    // No-op implementation since this feature doesn't exist in the older
+    // debugger implementation.
+    return expression;
+  },
+
   isPaused() {
     let framesController = this.panelWin.DebuggerController.StackFrames;
     let thread = framesController.activeThread;
     return thread && thread.paused;
   },
 
   getFrames() {
     let framesController = this.panelWin.DebuggerController.StackFrames;
--- a/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_02.js
+++ b/devtools/client/inspector/markup/test/browser_markup_css_completion_style_attribute_02.js
@@ -78,20 +78,20 @@ const TEST_DATA_INNER = [
   ["\"", "style=\"", 7, 7, false],
   ["b", "style=\"border", 8, 13, true],
   ["a", "style=\"background", 9, 17, true],
   ["VK_RIGHT", "style=\"background", 17, 17, false],
   [":", "style=\"background:aliceblue", 18, 27, true],
   ["u", "style=\"background:unset", 19, 23, true],
   ["r", "style=\"background:url", 20, 21, false],
   ["l", "style=\"background:url", 21, 21, false],
-  ["(", "style=\"background:url(", 22, 22, false],
-  ["'", "style=\"background:url('", 23, 23, false],
-  ["1", "style=\"background:url('1", 24, 24, false],
-  ["'", "style=\"background:url('1'", 25, 25, false],
+  ["(", "style=\"background:url()", 22, 22, false],
+  ["'", "style=\"background:url(')", 23, 23, false],
+  ["1", "style=\"background:url('1)", 24, 24, false],
+  ["'", "style=\"background:url('1')", 25, 25, false],
   [")", "style=\"background:url('1')", 26, 26, false],
   [";", "style=\"background:url('1');", 27, 27, false],
   [" ", "style=\"background:url('1'); ", 28, 28, false],
   ["c", "style=\"background:url('1'); color", 29, 33, true],
   ["VK_RIGHT", "style=\"background:url('1'); color", 33, 33, false],
   [":", "style=\"background:url('1'); color:aliceblue", 34, 43, true],
   ["b", "style=\"background:url('1'); color:beige", 35, 39, true],
   ["VK_RETURN", "style=\"background:url('1'); color:beige\"", -1, -1, false]
--- a/devtools/client/memory/components/tree-map/canvas-utils.js
+++ b/devtools/client/memory/components/tree-map/canvas-utils.js
@@ -11,17 +11,17 @@
  * canvas. The main canvas dimensions match the parent div, but the CSS can be
  * transformed to be zoomed and dragged around (potentially creating a blurry
  * canvas once zoomed in). The zoom canvas is a zoomed in section that matches
  * the parent div's dimensions and is kept in place through CSS. A zoomed in
  * view of the visualization is drawn onto this canvas, providing a crisp zoomed
  * in view of the tree map.
  */
 const { debounce } = require("devtools/shared/debounce");
-const EventEmitter = require("devtools/shared/old-event-emitter");
+const EventEmitter = require("devtools/shared/event-emitter");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const FULLSCREEN_STYLE = {
   width: "100%",
   height: "100%",
   position: "absolute",
 };
 
--- a/devtools/client/memory/components/tree-map/drag-zoom.js
+++ b/devtools/client/memory/components/tree-map/drag-zoom.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 { debounce } = require("devtools/shared/debounce");
 const { lerp } = require("devtools/client/memory/utils");
-const EventEmitter = require("devtools/shared/old-event-emitter");
+const EventEmitter = require("devtools/shared/event-emitter");
 
 const LERP_SPEED = 0.5;
 const ZOOM_SPEED = 0.01;
 const TRANSLATE_EPSILON = 1;
 const ZOOM_EPSILON = 0.001;
 const LINE_SCROLL_MODE = 1;
 const SCROLL_LINE_SIZE = 15;
 
--- a/devtools/client/memory/panel.js
+++ b/devtools/client/memory/panel.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 EventEmitter = require("devtools/shared/old-event-emitter");
+const EventEmitter = require("devtools/shared/event-emitter");
 const { MemoryFront } = require("devtools/shared/fronts/memory");
 const HeapAnalysesClient = require("devtools/shared/heapsnapshot/HeapAnalysesClient");
 
 function MemoryPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
 
   EventEmitter.decorate(this);
--- a/devtools/client/shared/inplace-editor.js
+++ b/devtools/client/shared/inplace-editor.js
@@ -21,16 +21,18 @@
  * See editableField() for more options.
  */
 
 "use strict";
 
 const Services = require("Services");
 const focusManager = Services.focus;
 const {KeyCodes} = require("devtools/client/shared/keycodes");
+const EventEmitter = require("devtools/shared/event-emitter");
+const { findMostRelevantCssPropertyIndex } = require("./suggestion-picker");
 
 loader.lazyRequireGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm", true);
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const CONTENT_TYPES = {
   PLAIN_TEXT: 0,
   CSS_VALUE: 1,
   CSS_MIXED: 2,
@@ -39,18 +41,20 @@ const CONTENT_TYPES = {
 
 // The limit of 500 autocomplete suggestions should not be reached but is kept
 // for safety.
 const MAX_POPUP_ENTRIES = 500;
 
 const FOCUS_FORWARD = focusManager.MOVEFOCUS_FORWARD;
 const FOCUS_BACKWARD = focusManager.MOVEFOCUS_BACKWARD;
 
-const EventEmitter = require("devtools/shared/event-emitter");
-const { findMostRelevantCssPropertyIndex } = require("./suggestion-picker");
+const WORD_REGEXP = /\w/;
+const isWordChar = function(str) {
+  return str && WORD_REGEXP.test(str);
+};
 
 /**
  * Helper to check if the provided key matches one of the expected keys.
  * Keys will be prefixed with DOM_VK_ and should match a key in KeyCodes.
  *
  * @param {String} key
  *        the key to check (can be a keyCode).
  * @param {...String} keys
@@ -1046,16 +1050,20 @@ InplaceEditor.prototype = {
    * Handle the input field's keypress event.
    */
   _onKeyPress: function(event) {
     let prevent = false;
 
     let key = event.keyCode;
     let input = this.input;
 
+    // We want to autoclose some characters, remember the pressed key in order to process
+    // it later on in maybeSuggestionCompletion().
+    this._pressedKey = event.key;
+
     let multilineNavigation = !this._isSingleLine() &&
       isKeyIn(key, "UP", "DOWN", "LEFT", "RIGHT");
     let isPlainText = this.contentType == CONTENT_TYPES.PLAIN_TEXT;
     let isPopupOpen = this.popup && this.popup.isOpen;
 
     let increment = 0;
     if (!isPlainText && !multilineNavigation) {
       increment = this._getIncrement(event);
@@ -1288,16 +1296,17 @@ InplaceEditor.prototype = {
    * @param {Boolean} autoInsert
    *        Pass true to automatically insert the most relevant suggestion.
    */
   _maybeSuggestCompletion: function(autoInsert) {
     // Input can be null in cases when you intantaneously switch out of it.
     if (!this.input) {
       return;
     }
+
     let preTimeoutQuery = this.input.value;
 
     // Since we are calling this method from a keypress event handler, the
     // |input.value| does not include currently typed character. Thus we perform
     // this method async.
     this._openPopupTimeout = this.doc.defaultView.setTimeout(() => {
       if (this._preventSuggestions) {
         this._preventSuggestions = false;
@@ -1472,23 +1481,69 @@ InplaceEditor.prototype = {
         let selectedIndex = autoInsert ? index : -1;
 
         // Open the suggestions popup.
         this.popup.setItems(finalList);
         this._openAutocompletePopup(offset, selectedIndex);
       } else {
         this._hideAutocompletePopup();
       }
+
+      this._autocloseParenthesis();
+
       // This emit is mainly for the purpose of making the test flow simpler.
       this.emit("after-suggest");
       this._doValidation();
     }, 0);
   },
 
   /**
+   * Automatically add closing parenthesis and skip closing parenthesis when needed.
+   */
+  _autocloseParenthesis: function() {
+    // Split the current value at the cursor index to rebuild the string.
+    let parts = this._splitStringAt(this.input.value, this.input.selectionStart);
+
+    // Lookup the character following the caret to know if the string should be modified.
+    let nextChar = parts[1][0];
+
+    // Autocomplete closing parenthesis if the last key pressed was "(" and the next
+    // character is not a "word" character.
+    if (this._pressedKey == "(" && !isWordChar(nextChar)) {
+      this._updateValue(parts[0] + ")" + parts[1]);
+    }
+
+    // Skip inserting ")" if the next character is already a ")" (note that we actually
+    // insert and remove the extra ")" here, as the input has already been modified).
+    if (this._pressedKey == ")" && nextChar == ")") {
+      this._updateValue(parts[0] + parts[1].substring(1));
+    }
+
+    this._pressedKey = null;
+  },
+
+  /**
+   * Update the current value of the input while preserving the caret position.
+   */
+  _updateValue: function(str) {
+    let start = this.input.selectionStart;
+    this.input.value = str;
+    this.input.setSelectionRange(start, start);
+    this._updateSize();
+  },
+
+  /**
+   * Split the provided string at the provided index. Returns an array of two strings.
+   * _splitStringAt("1234567", 3) will return ["123", "4567"]
+   */
+  _splitStringAt: function(str, index) {
+    return [str.substring(0, index), str.substring(index, str.length)];
+  },
+
+  /**
    * Check if the current input is displaying more than one line of text.
    *
    * @return {Boolean} true if the input has a single line of text
    */
   _isSingleLine: function() {
     let inputRect = this.input.getBoundingClientRect();
     return inputRect.height < 2 * this.inputCharDimensions.height;
   },
--- a/devtools/client/shared/test/browser.ini
+++ b/devtools/client/shared/test/browser.ini
@@ -142,16 +142,17 @@ skip-if = e10s # Bug 1221911, bug 122228
 [browser_html_tooltip_hover.js]
 [browser_html_tooltip_offset.js]
 [browser_html_tooltip_rtl.js]
 [browser_html_tooltip_variable-height.js]
 [browser_html_tooltip_width-auto.js]
 [browser_html_tooltip_xul-wrapper.js]
 [browser_inplace-editor-01.js]
 [browser_inplace-editor-02.js]
+[browser_inplace-editor_autoclose_parentheses.js]
 [browser_inplace-editor_autocomplete_01.js]
 [browser_inplace-editor_autocomplete_02.js]
 [browser_inplace-editor_autocomplete_offset.js]
 [browser_inplace-editor_autocomplete_css_variable.js]
 [browser_inplace-editor_maxwidth.js]
 [browser_keycodes.js]
 [browser_key_shortcuts.js]
 [browser_layoutHelpers.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/test/browser_inplace-editor_autoclose_parentheses.js
@@ -0,0 +1,73 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_inplace_editor.js */
+
+"use strict";
+
+const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
+const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
+loadHelperScript("helper_inplace_editor.js");
+
+// Test the inplace-editor closes parentheses automatically.
+
+// format :
+//  [
+//    what key to press,
+//    expected input box value after keypress,
+//    selected suggestion index (-1 if popup is hidden),
+//    number of suggestions in the popup (0 if popup is hidden),
+//  ]
+const testData = [
+  ["u", "u", -1, 0],
+  ["r", "ur", -1, 0],
+  ["l", "url", -1, 0],
+  ["(", "url()", -1, 0],
+  ["v", "url(v)", -1, 0],
+  ["a", "url(va)", -1, 0],
+  ["r", "url(var)", -1, 0],
+  ["(", "url(var())", -1, 0],
+  ["-", "url(var(-))", -1, 0],
+  ["-", "url(var(--))", -1, 0],
+  ["a", "url(var(--a))", -1, 0],
+  [")", "url(var(--a))", -1, 0],
+  [")", "url(var(--a))", -1, 0],
+];
+
+add_task(async function() {
+  await addTab("data:text/html;charset=utf-8," +
+    "inplace editor parentheses autoclose");
+  let [host, win, doc] = await createHost();
+
+  let xulDocument = win.top.document;
+  let popup = new AutocompletePopup(xulDocument, { autoSelect: true });
+  await new Promise(resolve => {
+    createInplaceEditorAndClick({
+      start: runPropertyAutocompletionTest,
+      contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
+      property: {
+        name: "background-image"
+      },
+      cssVariables: new Map(),
+      done: resolve,
+      popup: popup
+    }, doc);
+  });
+
+  popup.destroy();
+  host.destroy();
+  gBrowser.removeCurrentTab();
+});
+
+let runPropertyAutocompletionTest = async function(editor) {
+  info("Starting to test for css property completion");
+
+  // No need to test autocompletion here, return an empty array.
+  editor._getCSSValuesForPropertyName = () => [];
+
+  for (let data of testData) {
+    await testCompletion(data, editor);
+  }
+
+  EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
+};
--- a/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js
+++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js
@@ -22,26 +22,26 @@ loadHelperScript("helper_inplace_editor.
 //    selected suggestion index (-1 if popup is hidden),
 //    number of suggestions in the popup (0 if popup is hidden),
 //    expected post label corresponding with the input box value,
 //  ]
 const testData = [
   ["v", "v", -1, 0, null],
   ["a", "va", -1, 0, null],
   ["r", "var", -1, 0, null],
-  ["(", "var(", -1, 0, null],
-  ["-", "var(--abc", 0, 4, "blue"],
-  ["VK_BACK_SPACE", "var(-", -1, 0, null],
-  ["-", "var(--abc", 0, 4, "blue"],
-  ["VK_DOWN", "var(--def", 1, 4, "red"],
-  ["VK_DOWN", "var(--ghi", 2, 4, "green"],
-  ["VK_DOWN", "var(--jkl", 3, 4, "yellow"],
-  ["VK_DOWN", "var(--abc", 0, 4, "blue"],
-  ["VK_DOWN", "var(--def", 1, 4, "red"],
-  ["VK_LEFT", "var(--def", -1, 0, null],
+  ["(", "var()", -1, 0, null],
+  ["-", "var(--abc)", 0, 4, "blue"],
+  ["VK_BACK_SPACE", "var(-)", -1, 0, null],
+  ["-", "var(--abc)", 0, 4, "blue"],
+  ["VK_DOWN", "var(--def)", 1, 4, "red"],
+  ["VK_DOWN", "var(--ghi)", 2, 4, "green"],
+  ["VK_DOWN", "var(--jkl)", 3, 4, "yellow"],
+  ["VK_DOWN", "var(--abc)", 0, 4, "blue"],
+  ["VK_DOWN", "var(--def)", 1, 4, "red"],
+  ["VK_LEFT", "var(--def)", -1, 0, null],
 ];
 
 const CSS_VARIABLES = [
   ["--abc", "blue"],
   ["--def", "red"],
   ["--ghi", "green"],
   ["--jkl", "yellow"]
 ];
--- a/devtools/client/webconsole/hudservice.js
+++ b/devtools/client/webconsole/hudservice.js
@@ -477,16 +477,30 @@ WebConsole.prototype = {
 
     if (!panel) {
       return null;
     }
 
     return panel.getFrames();
   },
 
+  async getMappedExpression(expression) {
+    let toolbox = gDevTools.getToolbox(this.target);
+    if (!toolbox) {
+      return expression;
+    }
+    let panel = toolbox.getPanel("jsdebugger");
+
+    if (!panel) {
+      return expression;
+    }
+
+    return panel.getMappedExpression(expression);
+  },
+
   /**
    * Retrieves the current selection from the Inspector, if such a selection
    * exists. This is used to pass the ID of the selected actor to the Web
    * Console server for the $0 helper.
    *
    * @return object|null
    *         A Selection referring to the currently selected node in the
    *         Inspector.
--- a/devtools/client/webconsole/jsterm.js
+++ b/devtools/client/webconsole/jsterm.js
@@ -431,17 +431,17 @@ JSTerm.prototype = {
    *        The string you want to execute. If this is not provided, the current
    *        user input is used - taken from |this.getInputValue()|.
    * @param function [callback]
    *        Optional function to invoke when the result is displayed.
    *        This is deprecated - please use the promise return value instead.
    * @returns Promise
    *          Resolves with the message once the result is displayed.
    */
-  execute: function(executeString, callback) {
+  execute: async function(executeString, callback) {
     let deferred = defer();
     let resultCallback;
     if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
       resultCallback = (msg) => deferred.resolve(msg);
     } else {
       resultCallback = (msg) => {
         deferred.resolve(msg);
         if (callback) {
@@ -451,16 +451,31 @@ JSTerm.prototype = {
     }
 
     // attempt to execute the content of the inputNode
     executeString = executeString || this.getInputValue();
     if (!executeString) {
       return null;
     }
 
+    // Append a new value in the history of executed code, or overwrite the most
+    // recent entry. The most recent entry may contain the last edited input
+    // value that was not evaluated yet.
+    this.history[this.historyIndex++] = executeString;
+    this.historyPlaceHolder = this.history.length;
+
+    if (this.history.length > this.inputHistoryCount) {
+      this.history.splice(0, this.history.length - this.inputHistoryCount);
+      this.historyIndex = this.historyPlaceHolder = this.history.length;
+    }
+    this.storeHistory();
+    WebConsoleUtils.usageCount++;
+    this.setInputValue("");
+    this.clearCompletion();
+
     let selectedNodeActor = null;
     let inspectorSelection = this.hud.owner.getInspectorSelection();
     if (inspectorSelection && inspectorSelection.nodeFront) {
       selectedNodeActor = inspectorSelection.nodeFront.actorID;
     }
 
     if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
       const { ConsoleCommand } = require("devtools/client/webconsole/new-console-output/types");
@@ -477,32 +492,19 @@ JSTerm.prototype = {
     }
     let onResult = this._executeResultCallback.bind(this, resultCallback);
 
     let options = {
       frame: this.SELECTED_FRAME,
       selectedNodeActor: selectedNodeActor,
     };
 
-    this.requestEvaluation(executeString, options).then(onResult, onResult);
-
-    // Append a new value in the history of executed code, or overwrite the most
-    // recent entry. The most recent entry may contain the last edited input
-    // value that was not evaluated yet.
-    this.history[this.historyIndex++] = executeString;
-    this.historyPlaceHolder = this.history.length;
+    const mappedString = await this.hud.owner.getMappedExpression(executeString);
+    this.requestEvaluation(mappedString, options).then(onResult, onResult);
 
-    if (this.history.length > this.inputHistoryCount) {
-      this.history.splice(0, this.history.length - this.inputHistoryCount);
-      this.historyIndex = this.historyPlaceHolder = this.history.length;
-    }
-    this.storeHistory();
-    WebConsoleUtils.usageCount++;
-    this.setInputValue("");
-    this.clearCompletion();
     return deferred.promise;
   },
 
   /**
    * Request a JavaScript string evaluation from the server.
    *
    * @param string str
    *        String to execute.
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_add_edited_input_to_history.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_add_edited_input_to_history.js
@@ -8,45 +8,45 @@
 // See https://bugzilla.mozilla.org/show_bug.cgi?id=817834
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 817834";
 
 add_task(async function() {
   let hud = await openNewTabAndConsole(TEST_URI);
-  testEditedInputHistory(hud);
+  await testEditedInputHistory(hud);
 });
 
-function testEditedInputHistory(hud) {
+async function testEditedInputHistory(hud) {
   let jsterm = hud.jsterm;
   let inputNode = jsterm.inputNode;
 
   ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
   is(inputNode.selectionStart, 0);
   is(inputNode.selectionEnd, 0);
 
   jsterm.setInputValue('"first item"');
   EventUtils.synthesizeKey("KEY_ArrowUp");
   is(jsterm.getInputValue(), '"first item"', "null test history up");
   EventUtils.synthesizeKey("KEY_ArrowDown");
   is(jsterm.getInputValue(), '"first item"', "null test history down");
 
-  jsterm.execute();
+  await jsterm.execute();
   is(jsterm.getInputValue(), "", "cleared input line after submit");
 
   jsterm.setInputValue('"editing input 1"');
   EventUtils.synthesizeKey("KEY_ArrowUp");
   is(jsterm.getInputValue(), '"first item"', "test history up");
   EventUtils.synthesizeKey("KEY_ArrowDown");
   is(jsterm.getInputValue(), '"editing input 1"',
     "test history down restores in-progress input");
 
   jsterm.setInputValue('"second item"');
-  jsterm.execute();
+  await jsterm.execute();
   jsterm.setInputValue('"editing input 2"');
   EventUtils.synthesizeKey("KEY_ArrowUp");
   is(jsterm.getInputValue(), '"second item"', "test history up");
   EventUtils.synthesizeKey("KEY_ArrowUp");
   is(jsterm.getInputValue(), '"first item"', "test history up");
   EventUtils.synthesizeKey("KEY_ArrowDown");
   is(jsterm.getInputValue(), '"second item"', "test history down");
   EventUtils.synthesizeKey("KEY_ArrowDown");
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_ctrl_key_nav.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_ctrl_key_nav.js
@@ -18,17 +18,17 @@ add_task(async function() {
   const {jsterm} = await openNewTabAndConsole(TEST_URI);
 
   ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
   is(jsterm.inputNode.selectionStart, 0);
   is(jsterm.inputNode.selectionEnd, 0);
 
   testSingleLineInputNavNoHistory(jsterm);
   testMultiLineInputNavNoHistory(jsterm);
-  testNavWithHistory(jsterm);
+  await testNavWithHistory(jsterm);
 });
 
 function testSingleLineInputNavNoHistory(jsterm) {
   let inputNode = jsterm.inputNode;
   // Single char input
   EventUtils.sendString("1");
   is(inputNode.selectionStart, 1, "caret location after single char input");
 
@@ -146,31 +146,31 @@ function testMultiLineInputNavNoHistory(
     synthesizeLineEndKey();
     caretPos = inputNode.selectionStart;
     expectedStringBeforeCarat += lineValues[i];
     is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat,
        "ctrl-e to end of line " + (i + 1) + "in multiline input");
   }
 }
 
-function testNavWithHistory(jsterm) {
+async function testNavWithHistory(jsterm) {
   let inputNode = jsterm.inputNode;
 
   // NOTE: Tests does NOT currently define behaviour for ctrl-p/ctrl-n with
   // caret placed _within_ single line input
   let values = [
     '"single line input"',
     '"a longer single-line input to check caret repositioning"',
     '"multi-line"\n"input"\n"here!"',
   ];
 
   // submit to history
   for (let i = 0; i < values.length; i++) {
     jsterm.setInputValue(values[i]);
-    jsterm.execute();
+    await jsterm.execute();
   }
   is(inputNode.selectionStart, 0, "caret location at start of empty line");
 
   synthesizeLineUpKey();
   is(inputNode.selectionStart, values[values.length - 1].length,
      "caret location correct at end of last history input");
 
   // Navigate backwards history with ctrl-p
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_history_nav.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_history_nav.js
@@ -15,32 +15,35 @@ add_task(async function() {
   let { jsterm } = await openNewTabAndConsole(TEST_URI);
   let popup = jsterm.autocompletePopup;
 
   // The autocomplete popup should never be displayed during the test.
   let onShown = function() {
     ok(false, "popup shown");
   };
 
-  jsterm.execute(`window.foobarBug660806 = {
+  await jsterm.execute(`window.foobarBug660806 = {
     'location': 'value0',
     'locationbar': 'value1'
   }`);
 
   popup.on("popup-opened", onShown);
 
   ok(!popup.isOpen, "popup is not open");
 
   ok(!jsterm.lastInputValue, "no lastInputValue");
   jsterm.setInputValue("window.foobarBug660806.location");
   is(jsterm.lastInputValue, "window.foobarBug660806.location",
      "lastInputValue is correct");
 
   EventUtils.synthesizeKey("KEY_Enter");
 
+  // Wait for the execution to complete and clear the value.
+  await waitFor(() => !jsterm.lastInputValue);
+
   let onSetInputValue = jsterm.once("set-input-value");
   EventUtils.synthesizeKey("KEY_ArrowUp");
   await onSetInputValue;
 
   // We don't have an explicit event to wait for here, so we just wait for the next tick
   // before checking the popup status.
   await new Promise(executeSoon);
 
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_history_persist.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_history_persist.js
@@ -44,17 +44,17 @@ add_task(async function() {
   let hud3 = await openNewTabAndConsole(TEST_URI, false);
   is(JSON.stringify(hud3.jsterm.history),
      '["0","1","2","3","4","5","6","7","8","9"]',
      "Third tab has populated history");
 
   // Set input value separately from execute so UP arrow accurately navigates
   // history.
   hud3.jsterm.setInputValue('"hello from third tab"');
-  hud3.jsterm.execute();
+  await hud3.jsterm.execute();
 
   is(JSON.stringify(hud1.jsterm.history),
      '["0","1","2","3","4","5","6","7","8","9"]',
      "First tab history hasn't changed due to command in third tab");
   is(JSON.stringify(hud2.jsterm.history),
      '["0","1","2","3","4","5","6","7","8","9",""]',
      "Second tab history hasn't changed due to command in third tab");
   is(JSON.stringify(hud3.jsterm.history),
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_multiline.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_multiline.js
@@ -56,14 +56,16 @@ add_task(async function() {
     let inputWithNewline = input + "\n";
     is(inputValue, inputWithNewline, "Input value is correct");
   }
 
   for (let {input, shiftKey} of SHOULD_EXECUTE) {
     hud.jsterm.setInputValue(input);
     EventUtils.synthesizeKey("VK_RETURN", { shiftKey });
 
+    await waitFor(() => !hud.jsterm.getInputValue());
+
     let inputValue = hud.jsterm.getInputValue();
     is(inputNode.selectionStart, 0, "selection starts/ends at 0");
     is(inputNode.selectionEnd, 0, "selection starts/ends at 0");
     is(inputValue, "", "Input value is cleared");
   }
 });
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_history_arrow_keys.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_history_arrow_keys.js
@@ -23,17 +23,17 @@ add_task(async function() {
   let { jsterm } = hud;
 
   jsterm.focus();
   ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
 
   info("Execute each test value in the console");
   for (let value of TEST_VALUES) {
     jsterm.setInputValue(value);
-    jsterm.execute();
+    await jsterm.execute();
   }
 
   performTests(jsterm);
 });
 
 function performTests(jsterm) {
   let { inputNode } = jsterm;
   let values = TEST_VALUES;
--- a/devtools/client/webconsole/test/browser_console_history_persist.js
+++ b/devtools/client/webconsole/test/browser_console_history_persist.js
@@ -48,17 +48,17 @@ add_task(function* () {
   let hud3 = yield openConsole();
   is(JSON.stringify(hud3.jsterm.history),
      '["0","1","2","3","4","5","6","7","8","9"]',
      "Third tab has populated history");
 
   // Set input value separately from execute so UP arrow accurately navigates
   // history.
   hud3.jsterm.setInputValue('"hello from third tab"');
-  hud3.jsterm.execute();
+  yield hud3.jsterm.execute();
 
   is(JSON.stringify(hud1.jsterm.history),
      '["0","1","2","3","4","5","6","7","8","9"]',
      "First tab history hasn't changed due to command in third tab");
   is(JSON.stringify(hud2.jsterm.history),
      '["0","1","2","3","4","5","6","7","8","9",""]',
      "Second tab history hasn't changed due to command in third tab");
   is(JSON.stringify(hud3.jsterm.history),
@@ -93,17 +93,17 @@ add_task(function* () {
  */
 function* populateInputHistory(hud) {
   let jsterm = hud.jsterm;
 
   for (let i = 0; i < INPUT_HISTORY_COUNT; i++) {
     // Set input value separately from execute so UP arrow accurately navigates
     // history.
     jsterm.setInputValue(i);
-    jsterm.execute();
+    yield jsterm.execute();
   }
 }
 
 /**
  * Check pressing up results in history traversal like:
  *  [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
  */
 function* testNaviatingHistoryInUI(hud) {
--- a/devtools/client/webconsole/test/browser_console_optimized_out_vars.js
+++ b/devtools/client/webconsole/test/browser_console_optimized_out_vars.js
@@ -35,17 +35,17 @@ function test() {
     });
 
     yield fetchedScopes;
     ok(true, "Scopes were fetched");
 
     yield toolbox.selectTool("webconsole");
 
     // This is the meat of the test: evaluate the optimized out variable.
-    hud.jsterm.execute("upvar");
+    yield hud.jsterm.execute("upvar");
     yield waitForMessages({
       webconsole: hud,
       messages: [{
         text: "optimized out",
         category: CATEGORY_OUTPUT,
       }]
     });
 
--- a/devtools/client/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js
@@ -10,37 +10,37 @@ var jsterm, inputNode, values;
 var TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
                "bug 594497 and bug 619598";
 
 add_task(function* () {
   yield loadTab(TEST_URI);
 
   let hud = yield openConsole();
 
-  setup(hud);
+  yield setup(hud);
   performTests();
 
   jsterm = inputNode = values = null;
 });
 
-function setup(HUD) {
+function* setup(HUD) {
   jsterm = HUD.jsterm;
   inputNode = jsterm.inputNode;
 
   jsterm.focus();
 
   ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
 
   values = ["document", "window", "document.body"];
   values.push(values.join(";\n"), "document.location");
 
   // Execute each of the values;
   for (let i = 0; i < values.length; i++) {
     jsterm.setInputValue(values[i]);
-    jsterm.execute();
+    yield jsterm.execute();
   }
 }
 
 function performTests() {
   EventUtils.synthesizeKey("KEY_ArrowUp");
 
 
   is(jsterm.getInputValue(), values[4],
--- a/devtools/client/webconsole/test/browser_webconsole_bug_804845_ctrl_key_nav.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_804845_ctrl_key_nav.js
@@ -12,31 +12,31 @@ const TEST_URI = "data:text/html;charset
 
 var jsterm, inputNode;
 
 add_task(function* () {
   yield loadTab(TEST_URI);
 
   let hud = yield openConsole();
 
-  doTests(hud);
+  yield doTests(hud);
 
   jsterm = inputNode = null;
 });
 
-function doTests(HUD) {
+function* doTests(HUD) {
   jsterm = HUD.jsterm;
   inputNode = jsterm.inputNode;
   ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
   is(jsterm.inputNode.selectionStart, 0);
   is(jsterm.inputNode.selectionEnd, 0);
 
   testSingleLineInputNavNoHistory();
   testMultiLineInputNavNoHistory();
-  testNavWithHistory();
+  yield testNavWithHistory();
 }
 
 function testSingleLineInputNavNoHistory() {
   // Single char input
   EventUtils.sendString("1");
   is(inputNode.selectionStart, 1, "caret location after single char input");
 
   // nav to start/end with ctrl-a and ctrl-e;
@@ -152,27 +152,27 @@ function testMultiLineInputNavNoHistory(
     EventUtils.synthesizeKey("e", {ctrlKey: true});
     caretPos = inputNode.selectionStart;
     expectedStringBeforeCarat += lineValues[i];
     is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat,
        "ctrl-e to end of line " + (i + 1) + "in multiline input");
   }
 }
 
-function testNavWithHistory() {
+function* testNavWithHistory() {
   // NOTE: Tests does NOT currently define behaviour for ctrl-p/ctrl-n with
   // caret placed _within_ single line input
   let values = ['"single line input"',
                 '"a longer single-line input to check caret repositioning"',
                 ['"multi-line"', '"input"', '"here!"'].join("\n"),
                ];
   // submit to history
   for (let i = 0; i < values.length; i++) {
     jsterm.setInputValue(values[i]);
-    jsterm.execute();
+    yield jsterm.execute();
   }
   is(inputNode.selectionStart, 0, "caret location at start of empty line");
 
   EventUtils.synthesizeKey("p", {ctrlKey: true});
   is(inputNode.selectionStart, values[values.length - 1].length,
      "caret location correct at end of last history input");
 
   // Navigate backwards history with ctrl-p
--- a/devtools/client/webconsole/test/browser_webconsole_bug_817834_add_edited_input_to_history.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_817834_add_edited_input_to_history.js
@@ -6,49 +6,49 @@
 // Test that user input that is not submitted in the command line input is not
 // lost after navigating in history.
 // See https://bugzilla.mozilla.org/show_bug.cgi?id=817834
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 817834";
 
-add_task(function* () {
-  yield loadTab(TEST_URI);
+add_task(async function () {
+  await loadTab(TEST_URI);
 
-  let hud = yield openConsole();
+  let hud = await openConsole();
 
-  testEditedInputHistory(hud);
+  await testEditedInputHistory(hud);
 });
 
-function testEditedInputHistory(HUD) {
+async function testEditedInputHistory(HUD) {
   let jsterm = HUD.jsterm;
   let inputNode = jsterm.inputNode;
   ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
   is(inputNode.selectionStart, 0);
   is(inputNode.selectionEnd, 0);
 
   jsterm.setInputValue('"first item"');
   EventUtils.synthesizeKey("KEY_ArrowUp");
   is(jsterm.getInputValue(), '"first item"', "null test history up");
   EventUtils.synthesizeKey("KEY_ArrowDown");
   is(jsterm.getInputValue(), '"first item"', "null test history down");
 
-  jsterm.execute();
+  await jsterm.execute();
   is(jsterm.getInputValue(), "", "cleared input line after submit");
 
   jsterm.setInputValue('"editing input 1"');
   EventUtils.synthesizeKey("KEY_ArrowUp");
   is(jsterm.getInputValue(), '"first item"', "test history up");
   EventUtils.synthesizeKey("KEY_ArrowDown");
   is(jsterm.getInputValue(), '"editing input 1"',
     "test history down restores in-progress input");
 
   jsterm.setInputValue('"second item"');
-  jsterm.execute();
+  await jsterm.execute();
   jsterm.setInputValue('"editing input 2"');
   EventUtils.synthesizeKey("KEY_ArrowUp");
   is(jsterm.getInputValue(), '"second item"', "test history up");
   EventUtils.synthesizeKey("KEY_ArrowUp");
   is(jsterm.getInputValue(), '"first item"', "test history up");
   EventUtils.synthesizeKey("KEY_ArrowDown");
   is(jsterm.getInputValue(), '"second item"', "test history down");
   EventUtils.synthesizeKey("KEY_ArrowDown");
--- a/devtools/client/webconsole/test/browser_webconsole_multiline_input.js
+++ b/devtools/client/webconsole/test/browser_webconsole_multiline_input.js
@@ -56,15 +56,18 @@ add_task(function* () {
        "caret at end of multiline input");
     let inputWithNewline = test.input + "\n";
     is(inputValue, inputWithNewline, "Input value is correct");
   }
 
   for (let test of SHOULD_EXECUTE) {
     hud.jsterm.setInputValue(test.input);
     EventUtils.synthesizeKey("VK_RETURN", { shiftKey: test.shiftKey });
+
+    yield waitFor(() => !hud.jsterm.getInputValue());
+
     let inputValue = hud.jsterm.getInputValue();
     is(inputNode.selectionStart, 0, "selection starts/ends at 0");
     is(inputNode.selectionEnd, 0, "selection starts/ends at 0");
     is(inputValue, "", "Input value is cleared");
   }
 
 });
--- a/devtools/client/webconsole/test/head.js
+++ b/devtools/client/webconsole/test/head.js
@@ -125,16 +125,35 @@ function afterAllTabsLoaded(callback, wi
   }
 
   if (!stillToLoad) {
     callback();
   }
 }
 
 /**
+ * Wait for a predicate to return a result.
+ *
+ * @param function condition
+ *        Invoked once in a while until it returns a truthy value. This should be an
+ *        idempotent function, since we have to run it a second time after it returns
+ *        true in order to return the value.
+ * @param string message [optional]
+ *        A message to output if the condition fails.
+ * @param number interval [optional]
+ *        How often the predicate is invoked, in milliseconds.
+ * @return object
+ *         A promise that is resolved with the result of the condition.
+ */
+async function waitFor(condition, message = "waitFor", interval = 10, maxTries = 500) {
+  await BrowserTestUtils.waitForCondition(condition, message, interval, maxTries);
+  return condition();
+}
+
+/**
  * Check if a log entry exists in the HUD output node.
  *
  * @param {Element} outputNode
  *        the HUD output node.
  * @param {string} matchString
  *        the string you want to check if it exists in the output node.
  * @param {string} msg
  *        the message describing the test
copy from devtools/server/actors/object.js
copy to devtools/server/actors/array-buffer.js
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/array-buffer.js
@@ -1,2417 +1,16 @@
 /* -*- 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 { Cu, Ci } = require("chrome");
-const { GeneratedLocation } = require("devtools/server/actors/common");
-const { DebuggerServer } = require("devtools/server/main");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const { assert } = DevToolsUtils;
-
-loader.lazyRequireGetter(this, "ChromeUtils");
-
-// Number of items to preview in objects, arrays, maps, sets, lists,
-// collections, etc.
-const OBJECT_PREVIEW_MAX_ITEMS = 10;
-
-/**
- * Creates an actor for the specified object.
- *
- * @param obj Debugger.Object
- *        The debuggee object.
- * @param hooks Object
- *        A collection of abstract methods that are implemented by the caller.
- *        ObjectActor requires the following functions to be implemented by
- *        the caller:
- *          - createValueGrip
- *              Creates a value grip for the given object
- *          - sources
- *              TabSources getter that manages the sources of a thread
- *          - createEnvironmentActor
- *              Creates and return an environment actor
- *          - getGripDepth
- *              An actor's grip depth getter
- *          - incrementGripDepth
- *              Increment the actor's grip depth
- *          - decrementGripDepth
- *              Decrement the actor's grip depth
- *          - globalDebugObject
- *              The Debuggee Global Object as given by the ThreadActor
- */
-function ObjectActor(obj, {
-  createValueGrip: createValueGripHook,
-  sources,
-  createEnvironmentActor,
-  getGripDepth,
-  incrementGripDepth,
-  decrementGripDepth,
-  getGlobalDebugObject
-}) {
-  assert(!obj.optimizedOut,
-         "Should not create object actors for optimized out values!");
-  this.obj = obj;
-  this.hooks = {
-    createValueGrip: createValueGripHook,
-    sources,
-    createEnvironmentActor,
-    getGripDepth,
-    incrementGripDepth,
-    decrementGripDepth,
-    getGlobalDebugObject
-  };
-  this.iterators = new Set();
-}
-
-ObjectActor.prototype = {
-  actorPrefix: "obj",
-
-  rawValue: function() {
-    return this.obj.unsafeDereference();
-  },
-
-  /**
-   * Returns a grip for this actor for returning in a protocol message.
-   */
-  grip: function() {
-    let g = {
-      "type": "object",
-      "actor": this.actorID,
-      "class": this.obj.class,
-    };
-
-    let unwrapped = DevToolsUtils.unwrap(this.obj);
-
-    // Unsafe objects must be treated carefully.
-    if (!DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      if (DevToolsUtils.isCPOW(this.obj)) {
-        // Cross-process object wrappers can't be accessed.
-        g.class = "CPOW: " + g.class;
-      } else if (unwrapped === undefined) {
-        // Objects belonging to an invisible-to-debugger compartment might be proxies,
-        // so just in case they shouldn't be accessed.
-        g.class = "InvisibleToDebugger: " + g.class;
-      } else if (unwrapped.isProxy) {
-        // Proxy objects can run traps when accessed, so just create a preview with
-        // the target and the handler.
-        g.class = "Proxy";
-        this.hooks.incrementGripDepth();
-        DebuggerServer.ObjectActorPreviewers.Proxy[0](this, g, null);
-        this.hooks.decrementGripDepth();
-      }
-      return g;
-    }
-
-    // If the debuggee does not subsume the object's compartment, most properties won't
-    // be accessible. Cross-orgin Window and Location objects might expose some, though.
-    // Change the displayed class, but when creating the preview use the original one.
-    if (unwrapped === null) {
-      g.class = "Restricted";
-    }
-
-    this.hooks.incrementGripDepth();
-
-    g.extensible = this.obj.isExtensible();
-    g.frozen = this.obj.isFrozen();
-    g.sealed = this.obj.isSealed();
-
-    if (g.class == "Promise") {
-      g.promiseState = this._createPromiseState();
-    }
-
-    // FF40+: Allow to know how many properties an object has to lazily display them
-    // when there is a bunch.
-    if (isTypedArray(g)) {
-      // Bug 1348761: getOwnPropertyNames is unnecessary slow on TypedArrays
-      g.ownPropertyLength = getArrayLength(this.obj);
-    } else {
-      try {
-        g.ownPropertyLength = this.obj.getOwnPropertyNames().length;
-      } catch (err) {
-        // The above can throw when the debuggee does not subsume the object's
-        // compartment, or for some WrappedNatives like Cu.Sandbox.
-      }
-    }
-
-    let raw = this.obj.unsafeDereference();
-
-    // If Cu is not defined, we are running on a worker thread, where xrays
-    // don't exist.
-    if (Cu) {
-      raw = Cu.unwaiveXrays(raw);
-    }
-
-    if (!DevToolsUtils.isSafeJSObject(raw)) {
-      raw = null;
-    }
-
-    let previewers = DebuggerServer.ObjectActorPreviewers[this.obj.class] ||
-                     DebuggerServer.ObjectActorPreviewers.Object;
-    for (let fn of previewers) {
-      try {
-        if (fn(this, g, raw)) {
-          break;
-        }
-      } catch (e) {
-        let msg = "ObjectActor.prototype.grip previewer function";
-        DevToolsUtils.reportException(msg, e);
-      }
-    }
-
-    this.hooks.decrementGripDepth();
-    return g;
-  },
-
-  /**
-   * Returns an object exposing the internal Promise state.
-   */
-  _createPromiseState: function() {
-    const { state, value, reason } = getPromiseState(this.obj);
-    let promiseState = { state };
-
-    if (state == "fulfilled") {
-      promiseState.value = this.hooks.createValueGrip(value);
-    } else if (state == "rejected") {
-      promiseState.reason = this.hooks.createValueGrip(reason);
-    }
-
-    promiseState.creationTimestamp = Date.now() - this.obj.promiseLifetime;
-
-    // Only add the timeToSettle property if the Promise isn't pending.
-    if (state !== "pending") {
-      promiseState.timeToSettle = this.obj.promiseTimeToResolution;
-    }
-
-    return promiseState;
-  },
-
-  /**
-   * Releases this actor from the pool.
-   */
-  release: function() {
-    if (this.registeredPool.objectActors) {
-      this.registeredPool.objectActors.delete(this.obj);
-    }
-    this.iterators.forEach(actor => this.registeredPool.removeActor(actor));
-    this.iterators.clear();
-    this.registeredPool.removeActor(this);
-  },
-
-  /**
-   * Handle a protocol request to provide the definition site of this function
-   * object.
-   */
-  onDefinitionSite: function() {
-    if (this.obj.class != "Function") {
-      return {
-        from: this.actorID,
-        error: "objectNotFunction",
-        message: this.actorID + " is not a function."
-      };
-    }
-
-    if (!this.obj.script) {
-      return {
-        from: this.actorID,
-        error: "noScript",
-        message: this.actorID + " has no Debugger.Script"
-      };
-    }
-
-    return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
-      this.hooks.sources().createNonSourceMappedActor(this.obj.script.source),
-      this.obj.script.startLine,
-      0 // TODO bug 901138: use Debugger.Script.prototype.startColumn
-    )).then((originalLocation) => {
-      return {
-        source: originalLocation.originalSourceActor.form(),
-        line: originalLocation.originalLine,
-        column: originalLocation.originalColumn
-      };
-    });
-  },
-
-  /**
-   * Handle a protocol request to provide the names of the properties defined on
-   * the object and not its prototype.
-   */
-  onOwnPropertyNames: function() {
-    let props = [];
-    if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      try {
-        props = this.obj.getOwnPropertyNames();
-      } catch (err) {
-        // The above can throw when the debuggee does not subsume the object's
-        // compartment, or for some WrappedNatives like Cu.Sandbox.
-      }
-    }
-    return { from: this.actorID, ownPropertyNames: props };
-  },
-
-  /**
-   * Creates an actor to iterate over an object property names and values.
-   * See PropertyIteratorActor constructor for more info about options param.
-   *
-   * @param request object
-   *        The protocol request object.
-   */
-  onEnumProperties: function(request) {
-    let actor = new PropertyIteratorActor(this, request.options);
-    this.registeredPool.addActor(actor);
-    this.iterators.add(actor);
-    return { iterator: actor.grip() };
-  },
-
-  /**
-   * Creates an actor to iterate over entries of a Map/Set-like object.
-   */
-  onEnumEntries: function() {
-    let actor = new PropertyIteratorActor(this, { enumEntries: true });
-    this.registeredPool.addActor(actor);
-    this.iterators.add(actor);
-    return { iterator: actor.grip() };
-  },
-
-  /**
-   * Creates an actor to iterate over an object symbols properties.
-   */
-  onEnumSymbols: function() {
-    let actor = new SymbolIteratorActor(this);
-    this.registeredPool.addActor(actor);
-    this.iterators.add(actor);
-    return { iterator: actor.grip() };
-  },
-
-  /**
-   * Handle a protocol request to provide the prototype and own properties of
-   * the object.
-   *
-   * @returns {Object} An object containing the data of this.obj, of the following form:
-   *          - {string} from: this.obj's actorID.
-   *          - {Object} prototype: The descriptor of this.obj's prototype.
-   *          - {Object} ownProperties: an object where the keys are the names of the
-   *                     this.obj's ownProperties, and the values the descriptors of
-   *                     the properties.
-   *          - {Array} ownSymbols: An array containing all descriptors of this.obj's
-   *                    ownSymbols. Here we have an array, and not an object like for
-   *                    ownProperties, because we can have multiple symbols with the same
-   *                    name in this.obj, e.g. `{[Symbol()]: "a", [Symbol()]: "b"}`.
-   *          - {Object} safeGetterValues: an object that maps this.obj's property names
-   *                     with safe getters descriptors.
-   */
-  onPrototypeAndProperties: function() {
-    let proto = null;
-    let names = [];
-    let symbols = [];
-    if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      try {
-        proto = this.obj.proto;
-        names = this.obj.getOwnPropertyNames();
-        symbols = this.obj.getOwnPropertySymbols();
-      } catch (err) {
-        // The above can throw when the debuggee does not subsume the object's
-        // compartment, or for some WrappedNatives like Cu.Sandbox.
-      }
-    }
-
-    let ownProperties = Object.create(null);
-    let ownSymbols = [];
-
-    for (let name of names) {
-      ownProperties[name] = this._propertyDescriptor(name);
-    }
-
-    for (let sym of symbols) {
-      ownSymbols.push({
-        name: sym.toString(),
-        descriptor: this._propertyDescriptor(sym)
-      });
-    }
-
-    return { from: this.actorID,
-             prototype: this.hooks.createValueGrip(proto),
-             ownProperties,
-             ownSymbols,
-             safeGetterValues: this._findSafeGetterValues(names) };
-  },
-
-  /**
-   * Find the safe getter values for the current Debugger.Object, |this.obj|.
-   *
-   * @private
-   * @param array ownProperties
-   *        The array that holds the list of known ownProperties names for
-   *        |this.obj|.
-   * @param number [limit=0]
-   *        Optional limit of getter values to find.
-   * @return object
-   *         An object that maps property names to safe getter descriptors as
-   *         defined by the remote debugging protocol.
-   */
-  _findSafeGetterValues: function(ownProperties, limit = 0) {
-    let safeGetterValues = Object.create(null);
-    let obj = this.obj;
-    let level = 0, i = 0;
-
-    // Do not search safe getters in unsafe objects.
-    if (!DevToolsUtils.isSafeDebuggerObject(obj)) {
-      return safeGetterValues;
-    }
-
-    // Most objects don't have any safe getters but inherit some from their
-    // prototype. Avoid calling getOwnPropertyNames on objects that may have
-    // many properties like Array, strings or js objects. That to avoid
-    // freezing firefox when doing so.
-    if (isArray(this.obj) || ["Object", "String"].includes(this.obj.class)) {
-      obj = obj.proto;
-      level++;
-    }
-
-    while (obj && DevToolsUtils.isSafeDebuggerObject(obj)) {
-      let getters = this._findSafeGetters(obj);
-      for (let name of getters) {
-        // Avoid overwriting properties from prototypes closer to this.obj. Also
-        // avoid providing safeGetterValues from prototypes if property |name|
-        // is already defined as an own property.
-        if (name in safeGetterValues ||
-            (obj != this.obj && ownProperties.includes(name))) {
-          continue;
-        }
-
-        // Ignore __proto__ on Object.prototye.
-        if (!obj.proto && name == "__proto__") {
-          continue;
-        }
-
-        let desc = null, getter = null;
-        try {
-          desc = obj.getOwnPropertyDescriptor(name);
-          getter = desc.get;
-        } catch (ex) {
-          // The above can throw if the cache becomes stale.
-        }
-        if (!getter) {
-          obj._safeGetters = null;
-          continue;
-        }
-
-        let result = getter.call(this.obj);
-        if (result && !("throw" in result)) {
-          let getterValue = undefined;
-          if ("return" in result) {
-            getterValue = result.return;
-          } else if ("yield" in result) {
-            getterValue = result.yield;
-          }
-          // WebIDL attributes specified with the LenientThis extended attribute
-          // return undefined and should be ignored.
-          if (getterValue !== undefined) {
-            safeGetterValues[name] = {
-              getterValue: this.hooks.createValueGrip(getterValue),
-              getterPrototypeLevel: level,
-              enumerable: desc.enumerable,
-              writable: level == 0 ? desc.writable : true,
-            };
-            if (limit && ++i == limit) {
-              break;
-            }
-          }
-        }
-      }
-      if (limit && i == limit) {
-        break;
-      }
-
-      obj = obj.proto;
-      level++;
-    }
-
-    return safeGetterValues;
-  },
-
-  /**
-   * Find the safe getters for a given Debugger.Object. Safe getters are native
-   * getters which are safe to execute.
-   *
-   * @private
-   * @param Debugger.Object object
-   *        The Debugger.Object where you want to find safe getters.
-   * @return Set
-   *         A Set of names of safe getters. This result is cached for each
-   *         Debugger.Object.
-   */
-  _findSafeGetters: function(object) {
-    if (object._safeGetters) {
-      return object._safeGetters;
-    }
-
-    let getters = new Set();
-
-    if (!DevToolsUtils.isSafeDebuggerObject(object)) {
-      object._safeGetters = getters;
-      return getters;
-    }
-
-    let names = [];
-    try {
-      names = object.getOwnPropertyNames();
-    } catch (ex) {
-      // Calling getOwnPropertyNames() on some wrapped native prototypes is not
-      // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
-    }
-
-    for (let name of names) {
-      let desc = null;
-      try {
-        desc = object.getOwnPropertyDescriptor(name);
-      } catch (e) {
-        // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
-        // allowed (bug 560072).
-      }
-      if (!desc || desc.value !== undefined || !("get" in desc)) {
-        continue;
-      }
-
-      if (DevToolsUtils.hasSafeGetter(desc)) {
-        getters.add(name);
-      }
-    }
-
-    object._safeGetters = getters;
-    return getters;
-  },
-
-  /**
-   * Handle a protocol request to provide the prototype of the object.
-   */
-  onPrototype: function() {
-    let proto = null;
-    if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      proto = this.obj.proto;
-    }
-    return { from: this.actorID,
-             prototype: this.hooks.createValueGrip(proto) };
-  },
-
-  /**
-   * Handle a protocol request to provide the property descriptor of the
-   * object's specified property.
-   *
-   * @param request object
-   *        The protocol request object.
-   */
-  onProperty: function(request) {
-    if (!request.name) {
-      return { error: "missingParameter",
-               message: "no property name was specified" };
-    }
-
-    return { from: this.actorID,
-             descriptor: this._propertyDescriptor(request.name) };
-  },
-
-  /**
-   * Handle a protocol request to provide the display string for the object.
-   */
-  onDisplayString: function() {
-    const string = stringify(this.obj);
-    return { from: this.actorID,
-             displayString: this.hooks.createValueGrip(string) };
-  },
-
-  /**
-   * A helper method that creates a property descriptor for the provided object,
-   * properly formatted for sending in a protocol response.
-   *
-   * @private
-   * @param string name
-   *        The property that the descriptor is generated for.
-   * @param boolean [onlyEnumerable]
-   *        Optional: true if you want a descriptor only for an enumerable
-   *        property, false otherwise.
-   * @return object|undefined
-   *         The property descriptor, or undefined if this is not an enumerable
-   *         property and onlyEnumerable=true.
-   */
-  _propertyDescriptor: function(name, onlyEnumerable) {
-    if (!DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      return undefined;
-    }
-
-    let desc;
-    try {
-      desc = this.obj.getOwnPropertyDescriptor(name);
-    } catch (e) {
-      // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
-      // allowed (bug 560072). Inform the user with a bogus, but hopefully
-      // explanatory, descriptor.
-      return {
-        configurable: false,
-        writable: false,
-        enumerable: false,
-        value: e.name
-      };
-    }
-
-    if (!desc || onlyEnumerable && !desc.enumerable) {
-      return undefined;
-    }
-
-    let retval = {
-      configurable: desc.configurable,
-      enumerable: desc.enumerable
-    };
-
-    if ("value" in desc) {
-      retval.writable = desc.writable;
-      retval.value = this.hooks.createValueGrip(desc.value);
-    } else {
-      if ("get" in desc) {
-        retval.get = this.hooks.createValueGrip(desc.get);
-      }
-      if ("set" in desc) {
-        retval.set = this.hooks.createValueGrip(desc.set);
-      }
-    }
-    return retval;
-  },
-
-  /**
-   * Handle a protocol request to provide the source code of a function.
-   *
-   * @param request object
-   *        The protocol request object.
-   */
-  onDecompile: function(request) {
-    if (this.obj.class !== "Function") {
-      return { error: "objectNotFunction",
-               message: "decompile request is only valid for object grips " +
-                        "with a 'Function' class." };
-    }
-
-    return { from: this.actorID,
-             decompiledCode: this.obj.decompile(!!request.pretty) };
-  },
-
-  /**
-   * Handle a protocol request to provide the parameters of a function.
-   */
-  onParameterNames: function() {
-    if (this.obj.class !== "Function") {
-      return { error: "objectNotFunction",
-               message: "'parameterNames' request is only valid for object " +
-                        "grips with a 'Function' class." };
-    }
-
-    return { parameterNames: this.obj.parameterNames };
-  },
-
-  /**
-   * Handle a protocol request to release a thread-lifetime grip.
-   */
-  onRelease: function() {
-    this.release();
-    return {};
-  },
-
-  /**
-   * Handle a protocol request to provide the lexical scope of a function.
-   */
-  onScope: function() {
-    if (this.obj.class !== "Function") {
-      return { error: "objectNotFunction",
-               message: "scope request is only valid for object grips with a" +
-                        " 'Function' class." };
-    }
-
-    let envActor = this.hooks.createEnvironmentActor(this.obj.environment,
-                                                     this.registeredPool);
-    if (!envActor) {
-      return { error: "notDebuggee",
-               message: "cannot access the environment of this function." };
-    }
-
-    return { from: this.actorID, scope: envActor.form() };
-  },
-
-  /**
-   * Handle a protocol request to get the list of dependent promises of a
-   * promise.
-   *
-   * @return object
-   *         Returns an object containing an array of object grips of the
-   *         dependent promises
-   */
-  onDependentPromises: function() {
-    if (this.obj.class != "Promise") {
-      return { error: "objectNotPromise",
-               message: "'dependentPromises' request is only valid for " +
-                        "object grips with a 'Promise' class." };
-    }
-
-    let promises = this.obj.promiseDependentPromises
-                           .map(p => this.hooks.createValueGrip(p));
-
-    return { promises };
-  },
-
-  /**
-   * Handle a protocol request to get the allocation stack of a promise.
-   */
-  onAllocationStack: function() {
-    if (this.obj.class != "Promise") {
-      return { error: "objectNotPromise",
-               message: "'allocationStack' request is only valid for " +
-                        "object grips with a 'Promise' class." };
-    }
-
-    let stack = this.obj.promiseAllocationSite;
-    let allocationStacks = [];
-
-    while (stack) {
-      if (stack.source) {
-        let source = this._getSourceOriginalLocation(stack);
-
-        if (source) {
-          allocationStacks.push(source);
-        }
-      }
-      stack = stack.parent;
-    }
-
-    return Promise.all(allocationStacks).then(stacks => {
-      return { allocationStack: stacks };
-    });
-  },
-
-  /**
-   * Handle a protocol request to get the fulfillment stack of a promise.
-   */
-  onFulfillmentStack: function() {
-    if (this.obj.class != "Promise") {
-      return { error: "objectNotPromise",
-               message: "'fulfillmentStack' request is only valid for " +
-                        "object grips with a 'Promise' class." };
-    }
-
-    let stack = this.obj.promiseResolutionSite;
-    let fulfillmentStacks = [];
-
-    while (stack) {
-      if (stack.source) {
-        let source = this._getSourceOriginalLocation(stack);
-
-        if (source) {
-          fulfillmentStacks.push(source);
-        }
-      }
-      stack = stack.parent;
-    }
-
-    return Promise.all(fulfillmentStacks).then(stacks => {
-      return { fulfillmentStack: stacks };
-    });
-  },
-
-  /**
-   * Handle a protocol request to get the rejection stack of a promise.
-   */
-  onRejectionStack: function() {
-    if (this.obj.class != "Promise") {
-      return { error: "objectNotPromise",
-               message: "'rejectionStack' request is only valid for " +
-                        "object grips with a 'Promise' class." };
-    }
-
-    let stack = this.obj.promiseResolutionSite;
-    let rejectionStacks = [];
-
-    while (stack) {
-      if (stack.source) {
-        let source = this._getSourceOriginalLocation(stack);
-
-        if (source) {
-          rejectionStacks.push(source);
-        }
-      }
-      stack = stack.parent;
-    }
-
-    return Promise.all(rejectionStacks).then(stacks => {
-      return { rejectionStack: stacks };
-    });
-  },
-
-  /**
-   * Helper function for fetching the source location of a SavedFrame stack.
-   *
-   * @param SavedFrame stack
-   *        The promise allocation stack frame
-   * @return object
-   *         Returns an object containing the source location of the SavedFrame
-   *         stack.
-   */
-  _getSourceOriginalLocation: function(stack) {
-    let source;
-
-    // Catch any errors if the source actor cannot be found
-    try {
-      source = this.hooks.sources().getSourceActorByURL(stack.source);
-    } catch (e) {
-      // ignored
-    }
-
-    if (!source) {
-      return null;
-    }
-
-    return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
-      source,
-      stack.line,
-      stack.column
-    )).then((originalLocation) => {
-      return {
-        source: originalLocation.originalSourceActor.form(),
-        line: originalLocation.originalLine,
-        column: originalLocation.originalColumn,
-        functionDisplayName: stack.functionDisplayName
-      };
-    });
-  }
-};
-
-ObjectActor.prototype.requestTypes = {
-  "definitionSite": ObjectActor.prototype.onDefinitionSite,
-  "parameterNames": ObjectActor.prototype.onParameterNames,
-  "prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties,
-  "enumProperties": ObjectActor.prototype.onEnumProperties,
-  "prototype": ObjectActor.prototype.onPrototype,
-  "property": ObjectActor.prototype.onProperty,
-  "displayString": ObjectActor.prototype.onDisplayString,
-  "ownPropertyNames": ObjectActor.prototype.onOwnPropertyNames,
-  "decompile": ObjectActor.prototype.onDecompile,
-  "release": ObjectActor.prototype.onRelease,
-  "scope": ObjectActor.prototype.onScope,
-  "dependentPromises": ObjectActor.prototype.onDependentPromises,
-  "allocationStack": ObjectActor.prototype.onAllocationStack,
-  "fulfillmentStack": ObjectActor.prototype.onFulfillmentStack,
-  "rejectionStack": ObjectActor.prototype.onRejectionStack,
-  "enumEntries": ObjectActor.prototype.onEnumEntries,
-  "enumSymbols": ObjectActor.prototype.onEnumSymbols,
-};
-
-/**
- * Creates an actor to iterate over an object's property names and values.
- *
- * @param objectActor ObjectActor
- *        The object actor.
- * @param options Object
- *        A dictionary object with various boolean attributes:
- *        - enumEntries Boolean
- *          If true, enumerates the entries of a Map or Set object
- *          instead of enumerating properties.
- *        - ignoreIndexedProperties Boolean
- *          If true, filters out Array items.
- *          e.g. properties names between `0` and `object.length`.
- *        - ignoreNonIndexedProperties Boolean
- *          If true, filters out items that aren't array items
- *          e.g. properties names that are not a number between `0`
- *          and `object.length`.
- *        - sort Boolean
- *          If true, the iterator will sort the properties by name
- *          before dispatching them.
- *        - query String
- *          If non-empty, will filter the properties by names and values
- *          containing this query string. The match is not case-sensitive.
- *          Regarding value filtering it just compare to the stringification
- *          of the property value.
- */
-function PropertyIteratorActor(objectActor, options) {
-  if (!DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
-    this.iterator = {
-      size: 0,
-      propertyName: index => undefined,
-      propertyDescription: index => undefined,
-    };
-  } else if (options.enumEntries) {
-    let cls = objectActor.obj.class;
-    if (cls == "Map") {
-      this.iterator = enumMapEntries(objectActor);
-    } else if (cls == "WeakMap") {
-      this.iterator = enumWeakMapEntries(objectActor);
-    } else if (cls == "Set") {
-      this.iterator = enumSetEntries(objectActor);
-    } else if (cls == "WeakSet") {
-      this.iterator = enumWeakSetEntries(objectActor);
-    } else {
-      throw new Error("Unsupported class to enumerate entries from: " + cls);
-    }
-  } else if (
-    isArray(objectActor.obj)
-    && options.ignoreNonIndexedProperties
-    && !options.query
-  ) {
-    this.iterator = enumArrayProperties(objectActor, options);
-  } else {
-    this.iterator = enumObjectProperties(objectActor, options);
-  }
-}
-
-PropertyIteratorActor.prototype = {
-  actorPrefix: "propertyIterator",
-
-  grip() {
-    return {
-      type: this.actorPrefix,
-      actor: this.actorID,
-      count: this.iterator.size
-    };
-  },
-
-  names({ indexes }) {
-    let list = [];
-    for (let idx of indexes) {
-      list.push(this.iterator.propertyName(idx));
-    }
-    return {
-      names: indexes
-    };
-  },
-
-  slice({ start, count }) {
-    let ownProperties = Object.create(null);
-    for (let i = start, m = start + count; i < m; i++) {
-      let name = this.iterator.propertyName(i);
-      ownProperties[name] = this.iterator.propertyDescription(i);
-    }
-    return {
-      ownProperties
-    };
-  },
-
-  all() {
-    return this.slice({ start: 0, count: this.iterator.size });
-  }
-};
-
-PropertyIteratorActor.prototype.requestTypes = {
-  "names": PropertyIteratorActor.prototype.names,
-  "slice": PropertyIteratorActor.prototype.slice,
-  "all": PropertyIteratorActor.prototype.all,
-};
-
-function enumArrayProperties(objectActor, options) {
-  return {
-    size: getArrayLength(objectActor.obj),
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      return objectActor._propertyDescriptor(index);
-    }
-  };
-}
-
-function enumObjectProperties(objectActor, options) {
-  let names = [];
-  try {
-    names = objectActor.obj.getOwnPropertyNames();
-  } catch (ex) {
-    // Calling getOwnPropertyNames() on some wrapped native prototypes is not
-    // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
-  }
-
-  if (options.ignoreNonIndexedProperties || options.ignoreIndexedProperties) {
-    let length = DevToolsUtils.getProperty(objectActor.obj, "length");
-    let sliceIndex;
-
-    const isLengthTrustworthy =
-      isUint32(length)
-      && (!length || isArrayIndex(names[length - 1]))
-      && !isArrayIndex(names[length]);
-
-    if (!isLengthTrustworthy) {
-      // The length property may not reflect what the object looks like, let's find
-      // where indexed properties end.
-
-      if (!isArrayIndex(names[0])) {
-        // If the first item is not a number, this means there is no indexed properties
-        // in this object.
-        sliceIndex = 0;
-      } else {
-        sliceIndex = names.length;
-        while (sliceIndex > 0) {
-          if (isArrayIndex(names[sliceIndex - 1])) {
-            break;
-          }
-          sliceIndex--;
-        }
-      }
-    } else {
-      sliceIndex = length;
-    }
-
-    // It appears that getOwnPropertyNames always returns indexed properties
-    // first, so we can safely slice `names` for/against indexed properties.
-    // We do such clever operation to optimize very large array inspection,
-    // like webaudio buffers.
-    if (options.ignoreIndexedProperties) {
-      // Keep items after `sliceIndex` index
-      names = names.slice(sliceIndex);
-    } else if (options.ignoreNonIndexedProperties) {
-      // Keep `sliceIndex` first items
-      names.length = sliceIndex;
-    }
-  }
-
-  let safeGetterValues = objectActor._findSafeGetterValues(names, 0);
-  let safeGetterNames = Object.keys(safeGetterValues);
-  // Merge the safe getter values into the existing properties list.
-  for (let name of safeGetterNames) {
-    if (!names.includes(name)) {
-      names.push(name);
-    }
-  }
-
-  if (options.query) {
-    let { query } = options;
-    query = query.toLowerCase();
-    names = names.filter(name => {
-      // Filter on attribute names
-      if (name.toLowerCase().includes(query)) {
-        return true;
-      }
-      // and then on attribute values
-      let desc;
-      try {
-        desc = objectActor.obj.getOwnPropertyDescriptor(name);
-      } catch (e) {
-        // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
-        // allowed (bug 560072).
-      }
-      if (desc && desc.value &&
-          String(desc.value).includes(query)) {
-        return true;
-      }
-      return false;
-    });
-  }
-
-  if (options.sort) {
-    names.sort();
-  }
-
-  return {
-    size: names.length,
-    propertyName(index) {
-      return names[index];
-    },
-    propertyDescription(index) {
-      let name = names[index];
-      let desc = objectActor._propertyDescriptor(name);
-      if (!desc) {
-        desc = safeGetterValues[name];
-      } else if (name in safeGetterValues) {
-        // Merge the safe getter values into the existing properties list.
-        let { getterValue, getterPrototypeLevel } = safeGetterValues[name];
-        desc.getterValue = getterValue;
-        desc.getterPrototypeLevel = getterPrototypeLevel;
-      }
-      return desc;
-    }
-  };
-}
-
-/**
- * Helper function to create a grip from a Map/Set entry
- */
-function gripFromEntry({ obj, hooks }, entry) {
-  return hooks.createValueGrip(
-    makeDebuggeeValueIfNeeded(obj, Cu.unwaiveXrays(entry)));
-}
-
-function enumMapEntries(objectActor) {
-  // Iterating over a Map via .entries goes through various intermediate
-  // objects - an Iterator object, then a 2-element Array object, then the
-  // actual values we care about. We don't have Xrays to Iterator objects,
-  // so we get Opaque wrappers for them. And even though we have Xrays to
-  // Arrays, the semantics often deny access to the entires based on the
-  // nature of the values. So we need waive Xrays for the iterator object
-  // and the tupes, and then re-apply them on the underlying values until
-  // we fix bug 1023984.
-  //
-  // Even then though, we might want to continue waiving Xrays here for the
-  // same reason we do so for Arrays above - this filtering behavior is likely
-  // to be more confusing than beneficial in the case of Object previews.
-  let raw = objectActor.obj.unsafeDereference();
-
-  let keys = [...Cu.waiveXrays(Map.prototype.keys.call(raw))];
-  return {
-    [Symbol.iterator]: function* () {
-      for (let key of keys) {
-        let value = Map.prototype.get.call(raw, key);
-        yield [ key, value ].map(val => gripFromEntry(objectActor, val));
-      }
-    },
-    size: keys.length,
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      let key = keys[index];
-      let val = Map.prototype.get.call(raw, key);
-      return {
-        enumerable: true,
-        value: {
-          type: "mapEntry",
-          preview: {
-            key: gripFromEntry(objectActor, key),
-            value: gripFromEntry(objectActor, val)
-          }
-        }
-      };
-    }
-  };
-}
-
-function enumWeakMapEntries(objectActor) {
-  // We currently lack XrayWrappers for WeakMap, so when we iterate over
-  // the values, the temporary iterator objects get created in the target
-  // compartment. However, we _do_ have Xrays to Object now, so we end up
-  // Xraying those temporary objects, and filtering access to |it.value|
-  // based on whether or not it's Xrayable and/or callable, which breaks
-  // the for/of iteration.
-  //
-  // This code is designed to handle untrusted objects, so we can safely
-  // waive Xrays on the iterable, and relying on the Debugger machinery to
-  // make sure we handle the resulting objects carefully.
-  let raw = objectActor.obj.unsafeDereference();
-  let keys = Cu.waiveXrays(
-    ChromeUtils.nondeterministicGetWeakMapKeys(raw));
-
-  return {
-    [Symbol.iterator]: function* () {
-      for (let key of keys) {
-        let value = WeakMap.prototype.get.call(raw, key);
-        yield [ key, value ].map(val => gripFromEntry(objectActor, val));
-      }
-    },
-    size: keys.length,
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      let key = keys[index];
-      let val = WeakMap.prototype.get.call(raw, key);
-      return {
-        enumerable: true,
-        value: {
-          type: "mapEntry",
-          preview: {
-            key: gripFromEntry(objectActor, key),
-            value: gripFromEntry(objectActor, val)
-          }
-        }
-      };
-    }
-  };
-}
-
-function enumSetEntries(objectActor) {
-  // We currently lack XrayWrappers for Set, so when we iterate over
-  // the values, the temporary iterator objects get created in the target
-  // compartment. However, we _do_ have Xrays to Object now, so we end up
-  // Xraying those temporary objects, and filtering access to |it.value|
-  // based on whether or not it's Xrayable and/or callable, which breaks
-  // the for/of iteration.
-  //
-  // This code is designed to handle untrusted objects, so we can safely
-  // waive Xrays on the iterable, and relying on the Debugger machinery to
-  // make sure we handle the resulting objects carefully.
-  let raw = objectActor.obj.unsafeDereference();
-  let values = [...Cu.waiveXrays(Set.prototype.values.call(raw))];
-
-  return {
-    [Symbol.iterator]: function* () {
-      for (let item of values) {
-        yield gripFromEntry(objectActor, item);
-      }
-    },
-    size: values.length,
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      let val = values[index];
-      return {
-        enumerable: true,
-        value: gripFromEntry(objectActor, val)
-      };
-    }
-  };
-}
-
-function enumWeakSetEntries(objectActor) {
-  // We currently lack XrayWrappers for WeakSet, so when we iterate over
-  // the values, the temporary iterator objects get created in the target
-  // compartment. However, we _do_ have Xrays to Object now, so we end up
-  // Xraying those temporary objects, and filtering access to |it.value|
-  // based on whether or not it's Xrayable and/or callable, which breaks
-  // the for/of iteration.
-  //
-  // This code is designed to handle untrusted objects, so we can safely
-  // waive Xrays on the iterable, and relying on the Debugger machinery to
-  // make sure we handle the resulting objects carefully.
-  let raw = objectActor.obj.unsafeDereference();
-  let keys = Cu.waiveXrays(
-    ChromeUtils.nondeterministicGetWeakSetKeys(raw));
-
-  return {
-    [Symbol.iterator]: function* () {
-      for (let item of keys) {
-        yield gripFromEntry(objectActor, item);
-      }
-    },
-    size: keys.length,
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      let val = keys[index];
-      return {
-        enumerable: true,
-        value: gripFromEntry(objectActor, val)
-      };
-    }
-  };
-}
-
-/**
- * Creates an actor to iterate over an object's symbols.
- *
- * @param objectActor ObjectActor
- *        The object actor.
- */
-function SymbolIteratorActor(objectActor) {
-  let symbols = [];
-  if (DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
-    try {
-      symbols = objectActor.obj.getOwnPropertySymbols();
-    } catch (err) {
-      // The above can throw when the debuggee does not subsume the object's
-      // compartment, or for some WrappedNatives like Cu.Sandbox.
-    }
-  }
-
-  this.iterator = {
-    size: symbols.length,
-    symbolDescription(index) {
-      const symbol = symbols[index];
-      return {
-        name: symbol.toString(),
-        descriptor: objectActor._propertyDescriptor(symbol)
-      };
-    }
-  };
-}
-
-SymbolIteratorActor.prototype = {
-  actorPrefix: "symbolIterator",
-
-  grip() {
-    return {
-      type: this.actorPrefix,
-      actor: this.actorID,
-      count: this.iterator.size
-    };
-  },
-
-  slice({ start, count }) {
-    let ownSymbols = [];
-    for (let i = start, m = start + count; i < m; i++) {
-      ownSymbols.push(this.iterator.symbolDescription(i));
-    }
-    return {
-      ownSymbols
-    };
-  },
-
-  all() {
-    return this.slice({ start: 0, count: this.iterator.size });
-  }
-};
-
-SymbolIteratorActor.prototype.requestTypes = {
-  "slice": SymbolIteratorActor.prototype.slice,
-  "all": SymbolIteratorActor.prototype.all,
-};
-
-/**
- * Functions for adding information to ObjectActor grips for the purpose of
- * having customized output. This object holds arrays mapped by
- * Debugger.Object.prototype.class.
- *
- * In each array you can add functions that take three
- * arguments:
- *   - the ObjectActor instance and its hooks to make a preview for,
- *   - the grip object being prepared for the client,
- *   - the raw JS object after calling Debugger.Object.unsafeDereference(). This
- *   argument is only provided if the object is safe for reading properties and
- *   executing methods. See DevToolsUtils.isSafeJSObject().
- *
- * Functions must return false if they cannot provide preview
- * information for the debugger object, or true otherwise.
- */
-DebuggerServer.ObjectActorPreviewers = {
-  String: [function(objectActor, grip, rawObj) {
-    return wrappedPrimitivePreviewer("String", String, objectActor, grip, rawObj);
-  }],
-
-  Boolean: [function(objectActor, grip, rawObj) {
-    return wrappedPrimitivePreviewer("Boolean", Boolean, objectActor, grip, rawObj);
-  }],
-
-  Number: [function(objectActor, grip, rawObj) {
-    return wrappedPrimitivePreviewer("Number", Number, objectActor, grip, rawObj);
-  }],
-
-  Symbol: [function(objectActor, grip, rawObj) {
-    return wrappedPrimitivePreviewer("Symbol", Symbol, objectActor, grip, rawObj);
-  }],
-
-  Function: [function({obj, hooks}, grip) {
-    if (obj.name) {
-      grip.name = obj.name;
-    }
-
-    if (obj.displayName) {
-      grip.displayName = obj.displayName.substr(0, 500);
-    }
-
-    if (obj.parameterNames) {
-      grip.parameterNames = obj.parameterNames;
-    }
-
-    // Check if the developer has added a de-facto standard displayName
-    // property for us to use.
-    let userDisplayName;
-    try {
-      userDisplayName = obj.getOwnPropertyDescriptor("displayName");
-    } catch (e) {
-      // The above can throw "permission denied" errors when the debuggee
-      // does not subsume the function's compartment.
-    }
-
-    if (userDisplayName && typeof userDisplayName.value == "string" &&
-        userDisplayName.value) {
-      grip.userDisplayName = hooks.createValueGrip(userDisplayName.value);
-    }
-
-    let dbgGlobal = hooks.getGlobalDebugObject();
-    if (dbgGlobal) {
-      let script = dbgGlobal.makeDebuggeeValue(obj.unsafeDereference()).script;
-      if (script) {
-        grip.location = {
-          url: script.url,
-          line: script.startLine
-        };
-      }
-    }
-
-    return true;
-  }],
-
-  RegExp: [function({obj, hooks}, grip) {
-    let str = DevToolsUtils.callPropertyOnObject(obj, "toString");
-    if (typeof str != "string") {
-      return false;
-    }
-
-    grip.displayString = hooks.createValueGrip(str);
-    return true;
-  }],
-
-  Date: [function({obj, hooks}, grip) {
-    let time = DevToolsUtils.callPropertyOnObject(obj, "getTime");
-    if (typeof time != "number") {
-      return false;
-    }
-
-    grip.preview = {
-      timestamp: hooks.createValueGrip(time),
-    };
-    return true;
-  }],
-
-  Array: [function({obj, hooks}, grip) {
-    let length = getArrayLength(obj);
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: length,
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let raw = obj.unsafeDereference();
-    let items = grip.preview.items = [];
-
-    for (let i = 0; i < length; ++i) {
-      // Array Xrays filter out various possibly-unsafe properties (like
-      // functions, and claim that the value is undefined instead. This
-      // is generally the right thing for privileged code accessing untrusted
-      // objects, but quite confusing for Object previews. So we manually
-      // override this protection by waiving Xrays on the array, and re-applying
-      // Xrays on any indexed value props that we pull off of it.
-      let desc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(raw), i);
-      if (desc && !desc.get && !desc.set) {
-        let value = Cu.unwaiveXrays(desc.value);
-        value = makeDebuggeeValueIfNeeded(obj, value);
-        items.push(hooks.createValueGrip(value));
-      } else {
-        items.push(null);
-      }
-
-      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  Set: [function(objectActor, grip) {
-    let size = DevToolsUtils.getProperty(objectActor.obj, "size");
-    if (typeof size != "number") {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: size,
-    };
-
-    // Avoid recursive object grips.
-    if (objectActor.hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let items = grip.preview.items = [];
-    for (let item of enumSetEntries(objectActor)) {
-      items.push(item);
-      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  WeakSet: [function(objectActor, grip) {
-    let enumEntries = enumWeakSetEntries(objectActor);
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: enumEntries.size
-    };
-
-    // Avoid recursive object grips.
-    if (objectActor.hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let items = grip.preview.items = [];
-    for (let item of enumEntries) {
-      items.push(item);
-      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  Map: [function(objectActor, grip) {
-    let size = DevToolsUtils.getProperty(objectActor.obj, "size");
-    if (typeof size != "number") {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "MapLike",
-      size: size,
-    };
-
-    if (objectActor.hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let entries = grip.preview.entries = [];
-    for (let entry of enumMapEntries(objectActor)) {
-      entries.push(entry);
-      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  WeakMap: [function(objectActor, grip) {
-    let enumEntries = enumWeakMapEntries(objectActor);
-
-    grip.preview = {
-      kind: "MapLike",
-      size: enumEntries.size
-    };
-
-    if (objectActor.hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let entries = grip.preview.entries = [];
-    for (let entry of enumEntries) {
-      entries.push(entry);
-      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  DOMStringMap: [function({obj, hooks}, grip, rawObj) {
-    if (!rawObj) {
-      return false;
-    }
-
-    let keys = obj.getOwnPropertyNames();
-    grip.preview = {
-      kind: "MapLike",
-      size: keys.length,
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let entries = grip.preview.entries = [];
-    for (let key of keys) {
-      let value = makeDebuggeeValueIfNeeded(obj, rawObj[key]);
-      entries.push([key, hooks.createValueGrip(value)]);
-      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  Proxy: [function({obj, hooks}, grip, rawObj) {
-    // The `isProxy` getter of the debuggee object only detects proxies without
-    // security wrappers. If false, the target and handler are not available.
-    let hasTargetAndHandler = obj.isProxy;
-    if (hasTargetAndHandler) {
-      grip.proxyTarget = hooks.createValueGrip(obj.proxyTarget);
-      grip.proxyHandler = hooks.createValueGrip(obj.proxyHandler);
-    }
-
-    grip.preview = {
-      kind: "Object",
-      ownProperties: Object.create(null),
-      ownPropertiesLength: 2 * hasTargetAndHandler
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    if (hasTargetAndHandler) {
-      grip.preview.ownProperties["<target>"] = {value: grip.proxyTarget};
-      grip.preview.ownProperties["<handler>"] = {value: grip.proxyHandler};
-    }
-
-    return true;
-  }],
-};
-
-/**
- * Generic previewer for classes wrapping primitives, like String,
- * Number and Boolean.
- *
- * @param string className
- *        Class name to expect.
- * @param object classObj
- *        The class to expect, eg. String. The valueOf() method of the class is
- *        invoked on the given object.
- * @param ObjectActor objectActor
- *        The object actor
- * @param Object grip
- *        The result grip to fill in
- * @return Booolean true if the object was handled, false otherwise
- */
-function wrappedPrimitivePreviewer(className, classObj, objectActor, grip, rawObj) {
-  let {obj, hooks} = objectActor;
-
-  let v = null;
-  try {
-    v = classObj.prototype.valueOf.call(rawObj);
-  } catch (ex) {
-    // valueOf() can throw if the raw JS object is "misbehaved".
-    return false;
-  }
-
-  if (v === null) {
-    return false;
-  }
-
-  let canHandle = GenericObject(objectActor, grip, rawObj, className === "String");
-  if (!canHandle) {
-    return false;
-  }
-
-  grip.preview.wrappedValue =
-    hooks.createValueGrip(makeDebuggeeValueIfNeeded(obj, v));
-  return true;
-}
-
-function GenericObject(objectActor, grip, rawObj, specialStringBehavior = false) {
-  let {obj, hooks} = objectActor;
-  if (grip.preview || grip.displayString || hooks.getGripDepth() > 1) {
-    return false;
-  }
-
-  let i = 0, names = [], symbols = [];
-  let preview = grip.preview = {
-    kind: "Object",
-    ownProperties: Object.create(null),
-    ownSymbols: [],
-  };
-
-  try {
-    names = obj.getOwnPropertyNames();
-    symbols = obj.getOwnPropertySymbols();
-  } catch (ex) {
-    // Calling getOwnPropertyNames() on some wrapped native prototypes is not
-    // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
-  }
-  preview.ownPropertiesLength = names.length;
-  preview.ownSymbolsLength = symbols.length;
-
-  let length;
-  if (specialStringBehavior) {
-    length = DevToolsUtils.getProperty(obj, "length");
-    if (typeof length != "number") {
-      specialStringBehavior = false;
-    }
-  }
-
-  for (let name of names) {
-    if (specialStringBehavior && /^[0-9]+$/.test(name)) {
-      let num = parseInt(name, 10);
-      if (num.toString() === name && num >= 0 && num < length) {
-        continue;
-      }
-    }
-
-    let desc = objectActor._propertyDescriptor(name, true);
-    if (!desc) {
-      continue;
-    }
-
-    preview.ownProperties[name] = desc;
-    if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
-      break;
-    }
-  }
-
-  for (let symbol of symbols) {
-    let descriptor = objectActor._propertyDescriptor(symbol, true);
-    if (!descriptor) {
-      continue;
-    }
-
-    preview.ownSymbols.push(Object.assign({
-      descriptor
-    }, hooks.createValueGrip(symbol)));
-
-    if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
-      break;
-    }
-  }
-
-  if (i < OBJECT_PREVIEW_MAX_ITEMS) {
-    preview.safeGetterValues = objectActor._findSafeGetterValues(
-      Object.keys(preview.ownProperties),
-      OBJECT_PREVIEW_MAX_ITEMS - i);
-  }
-
-  return true;
-}
-
-// Preview functions that do not rely on the object class.
-DebuggerServer.ObjectActorPreviewers.Object = [
-  function TypedArray({obj, hooks}, grip) {
-    if (!isTypedArray(obj)) {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: getArrayLength(obj),
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let raw = obj.unsafeDereference();
-    let global = Cu.getGlobalForObject(DebuggerServer);
-    let classProto = global[obj.class].prototype;
-    // The Xray machinery for TypedArrays denies indexed access on the grounds
-    // that it's slow, and advises callers to do a structured clone instead.
-    let safeView = Cu.cloneInto(classProto.subarray.call(raw, 0,
-      OBJECT_PREVIEW_MAX_ITEMS), global);
-    let items = grip.preview.items = [];
-    for (let i = 0; i < safeView.length; i++) {
-      items.push(safeView[i]);
-    }
-
-    return true;
-  },
-
-  function Error({obj, hooks}, grip) {
-    switch (obj.class) {
-      case "Error":
-      case "EvalError":
-      case "RangeError":
-      case "ReferenceError":
-      case "SyntaxError":
-      case "TypeError":
-      case "URIError":
-        let name = DevToolsUtils.getProperty(obj, "name");
-        let msg = DevToolsUtils.getProperty(obj, "message");
-        let stack = DevToolsUtils.getProperty(obj, "stack");
-        let fileName = DevToolsUtils.getProperty(obj, "fileName");
-        let lineNumber = DevToolsUtils.getProperty(obj, "lineNumber");
-        let columnNumber = DevToolsUtils.getProperty(obj, "columnNumber");
-        grip.preview = {
-          kind: "Error",
-          name: hooks.createValueGrip(name),
-          message: hooks.createValueGrip(msg),
-          stack: hooks.createValueGrip(stack),
-          fileName: hooks.createValueGrip(fileName),
-          lineNumber: hooks.createValueGrip(lineNumber),
-          columnNumber: hooks.createValueGrip(columnNumber),
-        };
-        return true;
-      default:
-        return false;
-    }
-  },
-
-  function CSSMediaRule({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || obj.class != "CSSMediaRule") {
-      return false;
-    }
-    grip.preview = {
-      kind: "ObjectWithText",
-      text: hooks.createValueGrip(rawObj.conditionText),
-    };
-    return true;
-  },
-
-  function CSSStyleRule({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || obj.class != "CSSStyleRule") {
-      return false;
-    }
-    grip.preview = {
-      kind: "ObjectWithText",
-      text: hooks.createValueGrip(rawObj.selectorText),
-    };
-    return true;
-  },
-
-  function ObjectWithURL({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || !(obj.class == "CSSImportRule" ||
-                                 obj.class == "CSSStyleSheet" ||
-                                 obj.class == "Location" ||
-                                 rawObj instanceof Ci.nsIDOMWindow)) {
-      return false;
-    }
-
-    let url;
-    if (rawObj instanceof Ci.nsIDOMWindow && rawObj.location) {
-      url = rawObj.location.href;
-    } else if (rawObj.href) {
-      url = rawObj.href;
-    } else {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ObjectWithURL",
-      url: hooks.createValueGrip(url),
-    };
-
-    return true;
-  },
-
-  function ArrayLike({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj ||
-        obj.class != "DOMStringList" &&
-        obj.class != "DOMTokenList" &&
-        obj.class != "CSSRuleList" &&
-        obj.class != "MediaList" &&
-        obj.class != "StyleSheetList" &&
-        obj.class != "CSSValueList" &&
-        obj.class != "NamedNodeMap" &&
-        obj.class != "FileList" &&
-        obj.class != "NodeList") {
-      return false;
-    }
-
-    if (typeof rawObj.length != "number") {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: rawObj.length,
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let items = grip.preview.items = [];
-
-    for (let i = 0; i < rawObj.length &&
-                    items.length < OBJECT_PREVIEW_MAX_ITEMS; i++) {
-      let value = makeDebuggeeValueIfNeeded(obj, rawObj[i]);
-      items.push(hooks.createValueGrip(value));
-    }
-
-    return true;
-  },
-
-  function CSSStyleDeclaration({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj ||
-        (obj.class != "CSSStyleDeclaration" &&
-         obj.class != "CSS2Properties")) {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "MapLike",
-      size: rawObj.length,
-    };
-
-    let entries = grip.preview.entries = [];
-
-    for (let i = 0; i < OBJECT_PREVIEW_MAX_ITEMS &&
-                    i < rawObj.length; i++) {
-      let prop = rawObj[i];
-      let value = rawObj.getPropertyValue(prop);
-      entries.push([prop, hooks.createValueGrip(value)]);
-    }
-
-    return true;
-  },
-
-  function DOMNode({obj, hooks}, grip, rawObj) {
-    if (isWorker || obj.class == "Object" || !rawObj ||
-        !(rawObj instanceof Ci.nsIDOMNode)) {
-      return false;
-    }
-
-    let preview = grip.preview = {
-      kind: "DOMNode",
-      nodeType: rawObj.nodeType,
-      nodeName: rawObj.nodeName,
-      isConnected: rawObj.isConnected === true,
-    };
-
-    if (rawObj instanceof Ci.nsIDOMDocument && rawObj.location) {
-      preview.location = hooks.createValueGrip(rawObj.location.href);
-    } else if (rawObj instanceof Ci.nsIDOMDocumentFragment) {
-      preview.childNodesLength = rawObj.childNodes.length;
-
-      if (hooks.getGripDepth() < 2) {
-        preview.childNodes = [];
-        for (let node of rawObj.childNodes) {
-          let actor = hooks.createValueGrip(obj.makeDebuggeeValue(node));
-          preview.childNodes.push(actor);
-          if (preview.childNodes.length == OBJECT_PREVIEW_MAX_ITEMS) {
-            break;
-          }
-        }
-      }
-    } else if (rawObj instanceof Ci.nsIDOMElement) {
-      // For HTML elements (in an HTML document, at least), the nodeName is an
-      // uppercased version of the actual element name.  Check for HTML
-      // elements, that is elements in the HTML namespace, and lowercase the
-      // nodeName in that case.
-      if (rawObj.namespaceURI == "http://www.w3.org/1999/xhtml") {
-        preview.nodeName = preview.nodeName.toLowerCase();
-      }
-
-      // Add preview for DOM element attributes.
-      preview.attributes = {};
-      preview.attributesLength = rawObj.attributes.length;
-      for (let attr of rawObj.attributes) {
-        preview.attributes[attr.nodeName] = hooks.createValueGrip(attr.value);
-      }
-    } else if (obj.class == "Attr") {
-      preview.value = hooks.createValueGrip(rawObj.value);
-    } else if (obj.class == "Text" ||
-               obj.class == "CDATASection" ||
-               obj.class == "Comment") {
-      preview.textContent = hooks.createValueGrip(rawObj.textContent);
-    }
-
-    return true;
-  },
-
-  function DOMEvent({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMEvent)) {
-      return false;
-    }
-
-    let preview = grip.preview = {
-      kind: "DOMEvent",
-      type: rawObj.type,
-      properties: Object.create(null),
-    };
-
-    if (hooks.getGripDepth() < 2) {
-      let target = obj.makeDebuggeeValue(rawObj.target);
-      preview.target = hooks.createValueGrip(target);
-    }
-
-    let props = [];
-    if (obj.class == "MouseEvent" ||
-        obj.class == "DragEvent" ||
-        obj.class == "PointerEvent" ||
-        obj.class == "SimpleGestureEvent" ||
-        obj.class == "WheelEvent") {
-      props.push("buttons", "clientX", "clientY", "layerX", "layerY");
-    } else if (obj.class == "KeyboardEvent") {
-      let modifiers = [];
-      if (rawObj.altKey) {
-        modifiers.push("Alt");
-      }
-      if (rawObj.ctrlKey) {
-        modifiers.push("Control");
-      }
-      if (rawObj.metaKey) {
-        modifiers.push("Meta");
-      }
-      if (rawObj.shiftKey) {
-        modifiers.push("Shift");
-      }
-      preview.eventKind = "key";
-      preview.modifiers = modifiers;
-
-      props.push("key", "charCode", "keyCode");
-    } else if (obj.class == "TransitionEvent") {
-      props.push("propertyName", "pseudoElement");
-    } else if (obj.class == "AnimationEvent") {
-      props.push("animationName", "pseudoElement");
-    } else if (obj.class == "ClipboardEvent") {
-      props.push("clipboardData");
-    }
-
-    // Add event-specific properties.
-    for (let prop of props) {
-      let value = rawObj[prop];
-      if (value && (typeof value == "object" || typeof value == "function")) {
-        // Skip properties pointing to objects.
-        if (hooks.getGripDepth() > 1) {
-          continue;
-        }
-        value = obj.makeDebuggeeValue(value);
-      }
-      preview.properties[prop] = hooks.createValueGrip(value);
-    }
-
-    // Add any properties we find on the event object.
-    if (!props.length) {
-      let i = 0;
-      for (let prop in rawObj) {
-        let value = rawObj[prop];
-        if (prop == "target" || prop == "type" || value === null ||
-            typeof value == "function") {
-          continue;
-        }
-        if (value && typeof value == "object") {
-          if (hooks.getGripDepth() > 1) {
-            continue;
-          }
-          value = obj.makeDebuggeeValue(value);
-        }
-        preview.properties[prop] = hooks.createValueGrip(value);
-        if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
-          break;
-        }
-      }
-    }
-
-    return true;
-  },
-
-  function DOMException({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMDOMException)) {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "DOMException",
-      name: hooks.createValueGrip(rawObj.name),
-      message: hooks.createValueGrip(rawObj.message),
-      code: hooks.createValueGrip(rawObj.code),
-      result: hooks.createValueGrip(rawObj.result),
-      filename: hooks.createValueGrip(rawObj.filename),
-      lineNumber: hooks.createValueGrip(rawObj.lineNumber),
-      columnNumber: hooks.createValueGrip(rawObj.columnNumber),
-    };
-
-    return true;
-  },
-
-  function PseudoArray({obj, hooks}, grip, rawObj) {
-    // An object is considered a pseudo-array if all the following apply:
-    // - All its properties are array indices except, optionally, a "length" property.
-    // - At least it has the "0" array index.
-    // - The array indices are consecutive.
-    // - The value of "length", if present, is the number of array indices.
-
-    let keys;
-    try {
-      keys = obj.getOwnPropertyNames();
-    } catch (err) {
-      // The above can throw when the debuggee does not subsume the object's
-      // compartment, or for some WrappedNatives like Cu.Sandbox.
-      return false;
-    }
-    let {length} = keys;
-    if (length === 0) {
-      return false;
-    }
-
-    // Array indices should be sorted at the beginning, from smallest to largest.
-    // Other properties should be at the end, so check if the last one is "length".
-    if (keys[length - 1] === "length") {
-      --length;
-      if (length === 0 || length !== DevToolsUtils.getProperty(obj, "length")) {
-        return false;
-      }
-    }
-
-    // Check that the last key is the array index expected at that position.
-    let lastKey = keys[length - 1];
-    if (!isArrayIndex(lastKey) || +lastKey !== length - 1) {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: length,
-    };
-
-    // Avoid recursive object grips.
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let items = grip.preview.items = [];
-    let numItems = Math.min(OBJECT_PREVIEW_MAX_ITEMS, length);
-
-    for (let i = 0; i < numItems; ++i) {
-      let desc = obj.getOwnPropertyDescriptor(i);
-      if (desc && "value" in desc) {
-        items.push(hooks.createValueGrip(desc.value));
-      } else {
-        items.push(null);
-      }
-    }
-
-    return true;
-  },
-
-  function Object(objectActor, grip, rawObj) {
-    return GenericObject(objectActor, grip, rawObj, /* specialStringBehavior = */ false);
-  },
-];
-
-/**
- * Get thisDebugger.Object referent's `promiseState`.
- *
- * @returns Object
- *          An object of one of the following forms:
- *          - { state: "pending" }
- *          - { state: "fulfilled", value }
- *          - { state: "rejected", reason }
- */
-function getPromiseState(obj) {
-  if (obj.class != "Promise") {
-    throw new Error(
-      "Can't call `getPromiseState` on `Debugger.Object`s that don't " +
-      "refer to Promise objects.");
-  }
-
-  let state = { state: obj.promiseState };
-  if (state.state === "fulfilled") {
-    state.value = obj.promiseValue;
-  } else if (state.state === "rejected") {
-    state.reason = obj.promiseReason;
-  }
-  return state;
-}
-
-/**
- * Determine if a given value is non-primitive.
- *
- * @param Any value
- *        The value to test.
- * @return Boolean
- *         Whether the value is non-primitive.
- */
-function isObject(value) {
-  const type = typeof value;
-  return type == "object" ? value !== null : type == "function";
-}
-
-/**
- * Create a function that can safely stringify Debugger.Objects of a given
- * builtin type.
- *
- * @param Function ctor
- *        The builtin class constructor.
- * @return Function
- *         The stringifier for the class.
- */
-function createBuiltinStringifier(ctor) {
-  return obj => {
-    try {
-      return ctor.prototype.toString.call(obj.unsafeDereference());
-    } catch (err) {
-      // The debuggee will see a "Function" class if the object is callable and
-      // its compartment is not subsumed. The above will throw if it's not really
-      // a function, e.g. if it's a callable proxy.
-      return "[object " + obj.class + "]";
-    }
-  };
-}
-
-/**
- * Stringify a Debugger.Object-wrapped Error instance.
- *
- * @param Debugger.Object obj
- *        The object to stringify.
- * @return String
- *         The stringification of the object.
- */
-function errorStringify(obj) {
-  let name = DevToolsUtils.getProperty(obj, "name");
-  if (name === "" || name === undefined) {
-    name = obj.class;
-  } else if (isObject(name)) {
-    name = stringify(name);
-  }
-
-  let message = DevToolsUtils.getProperty(obj, "message");
-  if (isObject(message)) {
-    message = stringify(message);
-  }
-
-  if (message === "" || message === undefined) {
-    return name;
-  }
-  return name + ": " + message;
-}
-
-/**
- * Stringify a Debugger.Object based on its class.
- *
- * @param Debugger.Object obj
- *        The object to stringify.
- * @return String
- *         The stringification for the object.
- */
-function stringify(obj) {
-  if (!DevToolsUtils.isSafeDebuggerObject(obj)) {
-    if (DevToolsUtils.isCPOW(obj)) {
-      return "<cpow>";
-    }
-    let unwrapped = DevToolsUtils.unwrap(obj);
-    if (unwrapped === undefined) {
-      return "<invisibleToDebugger>";
-    } else if (unwrapped.isProxy) {
-      return "<proxy>";
-    }
-    // The following line should not be reached. It's there just in case somebody
-    // modifies isSafeDebuggerObject to return false for additional kinds of objects.
-    return "[object " + obj.class + "]";
-  } else if (obj.class == "DeadObject") {
-    return "<dead object>";
-  }
-
-  const stringifier = stringifiers[obj.class] || stringifiers.Object;
-
-  try {
-    return stringifier(obj);
-  } catch (e) {
-    DevToolsUtils.reportException("stringify", e);
-    return "<failed to stringify object>";
-  }
-}
-
-// Used to prevent infinite recursion when an array is found inside itself.
-var seen = null;
-
-var stringifiers = {
-  Error: errorStringify,
-  EvalError: errorStringify,
-  RangeError: errorStringify,
-  ReferenceError: errorStringify,
-  SyntaxError: errorStringify,
-  TypeError: errorStringify,
-  URIError: errorStringify,
-  Boolean: createBuiltinStringifier(Boolean),
-  Function: createBuiltinStringifier(Function),
-  Number: createBuiltinStringifier(Number),
-  RegExp: createBuiltinStringifier(RegExp),
-  String: createBuiltinStringifier(String),
-  Object: obj => "[object " + obj.class + "]",
-  Array: obj => {
-    // If we're at the top level then we need to create the Set for tracking
-    // previously stringified arrays.
-    const topLevel = !seen;
-    if (topLevel) {
-      seen = new Set();
-    } else if (seen.has(obj)) {
-      return "";
-    }
-
-    seen.add(obj);
-
-    const len = getArrayLength(obj);
-    let string = "";
-
-    // Array.length is always a non-negative safe integer.
-    for (let i = 0; i < len; i++) {
-      const desc = obj.getOwnPropertyDescriptor(i);
-      if (desc) {
-        const { value } = desc;
-        if (value != null) {
-          string += isObject(value) ? stringify(value) : value;
-        }
-      }
-
-      if (i < len - 1) {
-        string += ",";
-      }
-    }
-
-    if (topLevel) {
-      seen = null;
-    }
-
-    return string;
-  },
-  DOMException: obj => {
-    const message = DevToolsUtils.getProperty(obj, "message") || "<no message>";
-    const result = (+DevToolsUtils.getProperty(obj, "result")).toString(16);
-    const code = DevToolsUtils.getProperty(obj, "code");
-    const name = DevToolsUtils.getProperty(obj, "name") || "<unknown>";
-
-    return '[Exception... "' + message + '" ' +
-           'code: "' + code + '" ' +
-           'nsresult: "0x' + result + " (" + name + ')"]';
-  },
-  Promise: obj => {
-    const { state, value, reason } = getPromiseState(obj);
-    let statePreview = state;
-    if (state != "pending") {
-      const settledValue = state === "fulfilled" ? value : reason;
-      statePreview += ": " + (typeof settledValue === "object" && settledValue !== null
-                                ? stringify(settledValue)
-                                : settledValue);
-    }
-    return "Promise (" + statePreview + ")";
-  },
-};
-
-/**
- * Make a debuggee value for the given object, if needed. Primitive values
- * are left the same.
- *
- * Use case: you have a raw JS object (after unsafe dereference) and you want to
- * send it to the client. In that case you need to use an ObjectActor which
- * requires a debuggee value. The Debugger.Object.prototype.makeDebuggeeValue()
- * method works only for JS objects and functions.
- *
- * @param Debugger.Object obj
- * @param any value
- * @return object
- */
-function makeDebuggeeValueIfNeeded(obj, value) {
-  if (value && (typeof value == "object" || typeof value == "function")) {
-    return obj.makeDebuggeeValue(value);
-  }
-  return value;
-}
-
-/**
- * Creates an actor for the specified "very long" string. "Very long" is specified
- * at the server's discretion.
- *
- * @param string String
- *        The string.
- */
-function LongStringActor(string) {
-  this.string = string;
-  this.stringLength = string.length;
-}
-
-LongStringActor.prototype = {
-  actorPrefix: "longString",
-
-  rawValue: function() {
-    return this.string;
-  },
-
-  destroy: function() {
-    // Because longStringActors is not a weak map, we won't automatically leave
-    // it so we need to manually leave on destroy so that we don't leak
-    // memory.
-    this._releaseActor();
-  },
-
-  /**
-   * Returns a grip for this actor for returning in a protocol message.
-   */
-  grip: function() {
-    return {
-      "type": "longString",
-      "initial": this.string.substring(
-        0, DebuggerServer.LONG_STRING_INITIAL_LENGTH),
-      "length": this.stringLength,
-      "actor": this.actorID
-    };
-  },
-
-  /**
-   * Handle a request to extract part of this actor's string.
-   *
-   * @param request object
-   *        The protocol request object.
-   */
-  onSubstring: function(request) {
-    return {
-      "from": this.actorID,
-      "substring": this.string.substring(request.start, request.end)
-    };
-  },
-
-  /**
-   * Handle a request to release this LongStringActor instance.
-   */
-  onRelease: function() {
-    // TODO: also check if registeredPool === threadActor.threadLifetimePool
-    // when the web console moves away from manually releasing pause-scoped
-    // actors.
-    this._releaseActor();
-    this.registeredPool.removeActor(this);
-    return {};
-  },
-
-  _releaseActor: function() {
-    if (this.registeredPool && this.registeredPool.longStringActors) {
-      delete this.registeredPool.longStringActors[this.string];
-    }
-  }
-};
-
-LongStringActor.prototype.requestTypes = {
-  "substring": LongStringActor.prototype.onSubstring,
-  "release": LongStringActor.prototype.onRelease
-};
-
-/**
- * Creates an actor for the specified symbol.
- *
- * @param symbol Symbol
- *        The symbol.
- */
-function SymbolActor(symbol) {
-  this.symbol = symbol;
-}
-
-SymbolActor.prototype = {
-  actorPrefix: "symbol",
-
-  rawValue: function() {
-    return this.symbol;
-  },
-
-  destroy: function() {
-    // Because symbolActors is not a weak map, we won't automatically leave
-    // it so we need to manually leave on destroy so that we don't leak
-    // memory.
-    this._releaseActor();
-  },
-
-  /**
-   * Returns a grip for this actor for returning in a protocol message.
-   */
-  grip: function() {
-    let form = {
-      type: "symbol",
-      actor: this.actorID,
-    };
-    let name = getSymbolName(this.symbol);
-    if (name !== undefined) {
-      // Create a grip for the name because it might be a longString.
-      form.name = createValueGrip(name, this.registeredPool);
-    }
-    return form;
-  },
-
-  /**
-   * Handle a request to release this SymbolActor instance.
-   */
-  onRelease: function() {
-    // TODO: also check if registeredPool === threadActor.threadLifetimePool
-    // when the web console moves away from manually releasing pause-scoped
-    // actors.
-    this._releaseActor();
-    this.registeredPool.removeActor(this);
-    return {};
-  },
-
-  _releaseActor: function() {
-    if (this.registeredPool && this.registeredPool.symbolActors) {
-      delete this.registeredPool.symbolActors[this.symbol];
-    }
-  }
-};
-
-SymbolActor.prototype.requestTypes = {
-  "release": SymbolActor.prototype.onRelease
-};
-
 /**
  * Creates an actor for the specified ArrayBuffer.
  *
  * @param buffer ArrayBuffer
  *        The buffer.
  */
 function ArrayBufferActor(buffer) {
   this.buffer = buffer;
@@ -2453,136 +52,16 @@ ArrayBufferActor.prototype = {
   }
 };
 
 ArrayBufferActor.prototype.requestTypes = {
   "slice": ArrayBufferActor.prototype.onSlice,
 };
 
 /**
- * Create a grip for the given debuggee value.  If the value is an
- * object, will create an actor with the given lifetime.
- */
-function createValueGrip(value, pool, makeObjectGrip) {
-  switch (typeof value) {
-    case "boolean":
-      return value;
-
-    case "string":
-      if (stringIsLong(value)) {
-        return longStringGrip(value, pool);
-      }
-      return value;
-
-    case "number":
-      if (value === Infinity) {
-        return { type: "Infinity" };
-      } else if (value === -Infinity) {
-        return { type: "-Infinity" };
-      } else if (Number.isNaN(value)) {
-        return { type: "NaN" };
-      } else if (!value && 1 / value === -Infinity) {
-        return { type: "-0" };
-      }
-      return value;
-
-    case "undefined":
-      return { type: "undefined" };
-
-    case "object":
-      if (value === null) {
-        return { type: "null" };
-      } else if (value.optimizedOut ||
-             value.uninitialized ||
-             value.missingArguments) {
-        // The slot is optimized out, an uninitialized binding, or
-        // arguments on a dead scope
-        return {
-          type: "null",
-          optimizedOut: value.optimizedOut,
-          uninitialized: value.uninitialized,
-          missingArguments: value.missingArguments
-        };
-      }
-      return makeObjectGrip(value, pool);
-
-    case "symbol":
-      return symbolGrip(value, pool);
-
-    default:
-      assert(false, "Failed to provide a grip for: " + value);
-      return null;
-  }
-}
-
-const symbolProtoToString = Symbol.prototype.toString;
-
-function getSymbolName(symbol) {
-  const name = symbolProtoToString.call(symbol).slice("Symbol(".length, -1);
-  return name || undefined;
-}
-
-/**
- * Returns true if the string is long enough to use a LongStringActor instead
- * of passing the value directly over the protocol.
- *
- * @param str String
- *        The string we are checking the length of.
- */
-function stringIsLong(str) {
-  return str.length >= DebuggerServer.LONG_STRING_LENGTH;
-}
-
-/**
- * Create a grip for the given string.
- *
- * @param str String
- *        The string we are creating a grip for.
- * @param pool ActorPool
- *        The actor pool where the new actor will be added.
- */
-function longStringGrip(str, pool) {
-  if (!pool.longStringActors) {
-    pool.longStringActors = {};
-  }
-
-  if (pool.longStringActors.hasOwnProperty(str)) {
-    return pool.longStringActors[str].grip();
-  }
-
-  let actor = new LongStringActor(str);
-  pool.addActor(actor);
-  pool.longStringActors[str] = actor;
-  return actor.grip();
-}
-
-/**
- * Create a grip for the given symbol.
- *
- * @param sym Symbol
- *        The symbol we are creating a grip for.
- * @param pool ActorPool
- *        The actor pool where the new actor will be added.
- */
-function symbolGrip(sym, pool) {
-  if (!pool.symbolActors) {
-    pool.symbolActors = Object.create(null);
-  }
-
-  if (sym in pool.symbolActors) {
-    return pool.symbolActors[sym].grip();
-  }
-
-  let actor = new SymbolActor(sym);
-  pool.addActor(actor);
-  pool.symbolActors[sym] = actor;
-  return actor.grip();
-}
-
-/**
  * Create a grip for the given ArrayBuffer.
  *
  * @param buffer ArrayBuffer
  *        The ArrayBuffer we are creating a grip for.
  * @param pool ActorPool
  *        The actor pool where the new actor will be added.
  */
 function arrayBufferGrip(buffer, pool) {
@@ -2595,95 +74,12 @@ function arrayBufferGrip(buffer, pool) {
   }
 
   let actor = new ArrayBufferActor(buffer);
   pool.addActor(actor);
   pool.arrayBufferActors.set(buffer, actor);
   return actor.grip();
 }
 
-const TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
-                             "Uint32Array", "Int8Array", "Int16Array", "Int32Array",
-                             "Float32Array", "Float64Array"];
-
-/**
- * Returns true if a debuggee object is a typed array.
- *
- * @param obj Debugger.Object
- *        The debuggee object to test.
- * @return Boolean
- */
-function isTypedArray(object) {
-  return TYPED_ARRAY_CLASSES.includes(object.class);
-}
-
-/**
- * Returns true if a debuggee object is an array, including a typed array.
- *
- * @param obj Debugger.Object
- *        The debuggee object to test.
- * @return Boolean
- */
-function isArray(object) {
-  return isTypedArray(object) || object.class === "Array";
-}
-
-/**
- * Returns the length of an array (or typed array).
- *
- * @param obj Debugger.Object
- *        The debuggee object of the array.
- * @return Number
- * @throws if the object is not an array.
- */
-function getArrayLength(object) {
-  if (!isArray(object)) {
-    throw new Error("Expected an array, got a " + object.class);
-  }
-
-  // Real arrays have a reliable `length` own property.
-  if (object.class === "Array") {
-    return DevToolsUtils.getProperty(object, "length");
-  }
-
-  // For typed arrays, `DevToolsUtils.getProperty` is not reliable because the `length`
-  // getter could be shadowed by an own property, and `getOwnPropertyNames` is
-  // unnecessarily slow. Obtain the `length` getter safely and call it manually.
-  let typedProto = Object.getPrototypeOf(Uint8Array.prototype);
-  let getter = Object.getOwnPropertyDescriptor(typedProto, "length").get;
-  return getter.call(object.unsafeDereference());
-}
-
-/**
- * Returns true if the parameter can be stored as a 32-bit unsigned integer.
- * If so, it will be suitable for use as the length of an array object.
- *
- * @param num Number
- *        The number to test.
- * @return Boolean
- */
-function isUint32(num) {
-  return num >>> 0 === num;
-}
-
-/**
- * Returns true if the parameter is suitable to be an array index.
- *
- * @param str String
- * @return Boolean
- */
-function isArrayIndex(str) {
-  // Transform the parameter to a 32-bit unsigned integer.
-  let num = str >>> 0;
-  // Check that the parameter is a canonical Uint32 index.
-  return num + "" === str &&
-    // Array indices cannot attain the maximum Uint32 value.
-    num != -1 >>> 0;
-}
-
-exports.ObjectActor = ObjectActor;
-exports.PropertyIteratorActor = PropertyIteratorActor;
-exports.LongStringActor = LongStringActor;
-exports.SymbolActor = SymbolActor;
-exports.createValueGrip = createValueGrip;
-exports.stringIsLong = stringIsLong;
-exports.longStringGrip = longStringGrip;
-exports.arrayBufferGrip = arrayBufferGrip;
+module.exports = {
+  ArrayBufferActor,
+  arrayBufferGrip,
+};
--- a/devtools/server/actors/environment.js
+++ b/devtools/server/actors/environment.js
@@ -3,17 +3,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";
 
 /* global Debugger */
 
 const { ActorClassWithSpec } = require("devtools/shared/protocol");
-const { createValueGrip } = require("devtools/server/actors/object");
+const { createValueGrip } = require("devtools/server/actors/object/utils");
 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
--- a/devtools/server/actors/frame.js
+++ b/devtools/server/actors/frame.js
@@ -2,17 +2,17 @@
 /* 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 { ActorPool } = require("devtools/server/actors/common");
-const { createValueGrip } = require("devtools/server/actors/object");
+const { createValueGrip } = require("devtools/server/actors/object/utils");
 const { ActorClassWithSpec } = require("devtools/shared/protocol");
 const { frameSpec } = require("devtools/shared/specs/frame");
 
 /**
  * An actor for a specified stack frame.
  */
 let FrameActor = ActorClassWithSpec(frameSpec, {
   /**
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -4,28 +4,30 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
     'canvas',
     'emulation',
     'highlighters',
     'inspector',
+    'object',
     'utils',
     'webconsole',
     'worker',
 ]
 
 DevToolsModules(
     'accessibility-parent.js',
     'accessibility.js',
     'actor-registry.js',
     'addon.js',
     'addons.js',
     'animation.js',
+    'array-buffer.js',
     'breakpoint.js',
     'call-watcher.js',
     'canvas.js',
     'child-process.js',
     'chrome.js',
     'common.js',
     'content.js',
     'css-properties.js',
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -1,28 +1,32 @@
 /* -*- 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 { Cu, Ci } = require("chrome");
+const { Cu } = require("chrome");
 const { GeneratedLocation } = require("devtools/server/actors/common");
-const { DebuggerServer } = require("devtools/server/main");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { assert } = DevToolsUtils;
 
-loader.lazyRequireGetter(this, "ChromeUtils");
+loader.lazyRequireGetter(this, "PropertyIteratorActor", "devtools/server/actors/object/property-iterator", true);
+loader.lazyRequireGetter(this, "SymbolIteratorActor", "devtools/server/actors/object/symbol-iterator", true);
+loader.lazyRequireGetter(this, "previewers", "devtools/server/actors/object/previewers");
+loader.lazyRequireGetter(this, "stringify", "devtools/server/actors/object/stringifiers");
 
-// Number of items to preview in objects, arrays, maps, sets, lists,
-// collections, etc.
-const OBJECT_PREVIEW_MAX_ITEMS = 10;
-
+const {
+  getArrayLength,
+  getPromiseState,
+  isArray,
+  isTypedArray,
+} = require("devtools/server/actors/object/utils");
 /**
  * Creates an actor for the specified object.
  *
  * @param obj Debugger.Object
  *        The debuggee object.
  * @param hooks Object
  *        A collection of abstract methods that are implemented by the caller.
  *        ObjectActor requires the following functions to be implemented by
@@ -94,17 +98,17 @@ ObjectActor.prototype = {
         // Objects belonging to an invisible-to-debugger compartment might be proxies,
         // so just in case they shouldn't be accessed.
         g.class = "InvisibleToDebugger: " + g.class;
       } else if (unwrapped.isProxy) {
         // Proxy objects can run traps when accessed, so just create a preview with
         // the target and the handler.
         g.class = "Proxy";
         this.hooks.incrementGripDepth();
-        DebuggerServer.ObjectActorPreviewers.Proxy[0](this, g, null);
+        previewers.Proxy[0](this, g, null);
         this.hooks.decrementGripDepth();
       }
       return g;
     }
 
     // If the debuggee does not subsume the object's compartment, most properties won't
     // be accessible. Cross-orgin Window and Location objects might expose some, though.
     // Change the displayed class, but when creating the preview use the original one.
@@ -143,19 +147,17 @@ ObjectActor.prototype = {
     if (Cu) {
       raw = Cu.unwaiveXrays(raw);
     }
 
     if (!DevToolsUtils.isSafeJSObject(raw)) {
       raw = null;
     }
 
-    let previewers = DebuggerServer.ObjectActorPreviewers[this.obj.class] ||
-                     DebuggerServer.ObjectActorPreviewers.Object;
-    for (let fn of previewers) {
+    for (let fn of previewers[this.obj.class] || previewers.Object) {
       try {
         if (fn(this, g, raw)) {
           break;
         }
       } catch (e) {
         let msg = "ObjectActor.prototype.grip previewer function";
         DevToolsUtils.reportException(msg, e);
       }
@@ -798,1892 +800,9 @@ ObjectActor.prototype.requestTypes = {
   "dependentPromises": ObjectActor.prototype.onDependentPromises,
   "allocationStack": ObjectActor.prototype.onAllocationStack,
   "fulfillmentStack": ObjectActor.prototype.onFulfillmentStack,
   "rejectionStack": ObjectActor.prototype.onRejectionStack,
   "enumEntries": ObjectActor.prototype.onEnumEntries,
   "enumSymbols": ObjectActor.prototype.onEnumSymbols,
 };
 
-/**
- * Creates an actor to iterate over an object's property names and values.
- *
- * @param objectActor ObjectActor
- *        The object actor.
- * @param options Object
- *        A dictionary object with various boolean attributes:
- *        - enumEntries Boolean
- *          If true, enumerates the entries of a Map or Set object
- *          instead of enumerating properties.
- *        - ignoreIndexedProperties Boolean
- *          If true, filters out Array items.
- *          e.g. properties names between `0` and `object.length`.
- *        - ignoreNonIndexedProperties Boolean
- *          If true, filters out items that aren't array items
- *          e.g. properties names that are not a number between `0`
- *          and `object.length`.
- *        - sort Boolean
- *          If true, the iterator will sort the properties by name
- *          before dispatching them.
- *        - query String
- *          If non-empty, will filter the properties by names and values
- *          containing this query string. The match is not case-sensitive.
- *          Regarding value filtering it just compare to the stringification
- *          of the property value.
- */
-function PropertyIteratorActor(objectActor, options) {
-  if (!DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
-    this.iterator = {
-      size: 0,
-      propertyName: index => undefined,
-      propertyDescription: index => undefined,
-    };
-  } else if (options.enumEntries) {
-    let cls = objectActor.obj.class;
-    if (cls == "Map") {
-      this.iterator = enumMapEntries(objectActor);
-    } else if (cls == "WeakMap") {
-      this.iterator = enumWeakMapEntries(objectActor);
-    } else if (cls == "Set") {
-      this.iterator = enumSetEntries(objectActor);
-    } else if (cls == "WeakSet") {
-      this.iterator = enumWeakSetEntries(objectActor);
-    } else {
-      throw new Error("Unsupported class to enumerate entries from: " + cls);
-    }
-  } else if (
-    isArray(objectActor.obj)
-    && options.ignoreNonIndexedProperties
-    && !options.query
-  ) {
-    this.iterator = enumArrayProperties(objectActor, options);
-  } else {
-    this.iterator = enumObjectProperties(objectActor, options);
-  }
-}
-
-PropertyIteratorActor.prototype = {
-  actorPrefix: "propertyIterator",
-
-  grip() {
-    return {
-      type: this.actorPrefix,
-      actor: this.actorID,
-      count: this.iterator.size
-    };
-  },
-
-  names({ indexes }) {
-    let list = [];
-    for (let idx of indexes) {
-      list.push(this.iterator.propertyName(idx));
-    }
-    return {
-      names: indexes
-    };
-  },
-
-  slice({ start, count }) {
-    let ownProperties = Object.create(null);
-    for (let i = start, m = start + count; i < m; i++) {
-      let name = this.iterator.propertyName(i);
-      ownProperties[name] = this.iterator.propertyDescription(i);
-    }
-    return {
-      ownProperties
-    };
-  },
-
-  all() {
-    return this.slice({ start: 0, count: this.iterator.size });
-  }
-};
-
-PropertyIteratorActor.prototype.requestTypes = {
-  "names": PropertyIteratorActor.prototype.names,
-  "slice": PropertyIteratorActor.prototype.slice,
-  "all": PropertyIteratorActor.prototype.all,
-};
-
-function enumArrayProperties(objectActor, options) {
-  return {
-    size: getArrayLength(objectActor.obj),
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      return objectActor._propertyDescriptor(index);
-    }
-  };
-}
-
-function enumObjectProperties(objectActor, options) {
-  let names = [];
-  try {
-    names = objectActor.obj.getOwnPropertyNames();
-  } catch (ex) {
-    // Calling getOwnPropertyNames() on some wrapped native prototypes is not
-    // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
-  }
-
-  if (options.ignoreNonIndexedProperties || options.ignoreIndexedProperties) {
-    let length = DevToolsUtils.getProperty(objectActor.obj, "length");
-    let sliceIndex;
-
-    const isLengthTrustworthy =
-      isUint32(length)
-      && (!length || isArrayIndex(names[length - 1]))
-      && !isArrayIndex(names[length]);
-
-    if (!isLengthTrustworthy) {
-      // The length property may not reflect what the object looks like, let's find
-      // where indexed properties end.
-
-      if (!isArrayIndex(names[0])) {
-        // If the first item is not a number, this means there is no indexed properties
-        // in this object.
-        sliceIndex = 0;
-      } else {
-        sliceIndex = names.length;
-        while (sliceIndex > 0) {
-          if (isArrayIndex(names[sliceIndex - 1])) {
-            break;
-          }
-          sliceIndex--;
-        }
-      }
-    } else {
-      sliceIndex = length;
-    }
-
-    // It appears that getOwnPropertyNames always returns indexed properties
-    // first, so we can safely slice `names` for/against indexed properties.
-    // We do such clever operation to optimize very large array inspection,
-    // like webaudio buffers.
-    if (options.ignoreIndexedProperties) {
-      // Keep items after `sliceIndex` index
-      names = names.slice(sliceIndex);
-    } else if (options.ignoreNonIndexedProperties) {
-      // Keep `sliceIndex` first items
-      names.length = sliceIndex;
-    }
-  }
-
-  let safeGetterValues = objectActor._findSafeGetterValues(names, 0);
-  let safeGetterNames = Object.keys(safeGetterValues);
-  // Merge the safe getter values into the existing properties list.
-  for (let name of safeGetterNames) {
-    if (!names.includes(name)) {
-      names.push(name);
-    }
-  }
-
-  if (options.query) {
-    let { query } = options;
-    query = query.toLowerCase();
-    names = names.filter(name => {
-      // Filter on attribute names
-      if (name.toLowerCase().includes(query)) {
-        return true;
-      }
-      // and then on attribute values
-      let desc;
-      try {
-        desc = objectActor.obj.getOwnPropertyDescriptor(name);
-      } catch (e) {
-        // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
-        // allowed (bug 560072).
-      }
-      if (desc && desc.value &&
-          String(desc.value).includes(query)) {
-        return true;
-      }
-      return false;
-    });
-  }
-
-  if (options.sort) {
-    names.sort();
-  }
-
-  return {
-    size: names.length,
-    propertyName(index) {
-      return names[index];
-    },
-    propertyDescription(index) {
-      let name = names[index];
-      let desc = objectActor._propertyDescriptor(name);
-      if (!desc) {
-        desc = safeGetterValues[name];
-      } else if (name in safeGetterValues) {
-        // Merge the safe getter values into the existing properties list.
-        let { getterValue, getterPrototypeLevel } = safeGetterValues[name];
-        desc.getterValue = getterValue;
-        desc.getterPrototypeLevel = getterPrototypeLevel;
-      }
-      return desc;
-    }
-  };
-}
-
-/**
- * Helper function to create a grip from a Map/Set entry
- */
-function gripFromEntry({ obj, hooks }, entry) {
-  return hooks.createValueGrip(
-    makeDebuggeeValueIfNeeded(obj, Cu.unwaiveXrays(entry)));
-}
-
-function enumMapEntries(objectActor) {
-  // Iterating over a Map via .entries goes through various intermediate
-  // objects - an Iterator object, then a 2-element Array object, then the
-  // actual values we care about. We don't have Xrays to Iterator objects,
-  // so we get Opaque wrappers for them. And even though we have Xrays to
-  // Arrays, the semantics often deny access to the entires based on the
-  // nature of the values. So we need waive Xrays for the iterator object
-  // and the tupes, and then re-apply them on the underlying values until
-  // we fix bug 1023984.
-  //
-  // Even then though, we might want to continue waiving Xrays here for the
-  // same reason we do so for Arrays above - this filtering behavior is likely
-  // to be more confusing than beneficial in the case of Object previews.
-  let raw = objectActor.obj.unsafeDereference();
-
-  let keys = [...Cu.waiveXrays(Map.prototype.keys.call(raw))];
-  return {
-    [Symbol.iterator]: function* () {
-      for (let key of keys) {
-        let value = Map.prototype.get.call(raw, key);
-        yield [ key, value ].map(val => gripFromEntry(objectActor, val));
-      }
-    },
-    size: keys.length,
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      let key = keys[index];
-      let val = Map.prototype.get.call(raw, key);
-      return {
-        enumerable: true,
-        value: {
-          type: "mapEntry",
-          preview: {
-            key: gripFromEntry(objectActor, key),
-            value: gripFromEntry(objectActor, val)
-          }
-        }
-      };
-    }
-  };
-}
-
-function enumWeakMapEntries(objectActor) {
-  // We currently lack XrayWrappers for WeakMap, so when we iterate over
-  // the values, the temporary iterator objects get created in the target
-  // compartment. However, we _do_ have Xrays to Object now, so we end up
-  // Xraying those temporary objects, and filtering access to |it.value|
-  // based on whether or not it's Xrayable and/or callable, which breaks
-  // the for/of iteration.
-  //
-  // This code is designed to handle untrusted objects, so we can safely
-  // waive Xrays on the iterable, and relying on the Debugger machinery to
-  // make sure we handle the resulting objects carefully.
-  let raw = objectActor.obj.unsafeDereference();
-  let keys = Cu.waiveXrays(
-    ChromeUtils.nondeterministicGetWeakMapKeys(raw));
-
-  return {
-    [Symbol.iterator]: function* () {
-      for (let key of keys) {
-        let value = WeakMap.prototype.get.call(raw, key);
-        yield [ key, value ].map(val => gripFromEntry(objectActor, val));
-      }
-    },
-    size: keys.length,
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      let key = keys[index];
-      let val = WeakMap.prototype.get.call(raw, key);
-      return {
-        enumerable: true,
-        value: {
-          type: "mapEntry",
-          preview: {
-            key: gripFromEntry(objectActor, key),
-            value: gripFromEntry(objectActor, val)
-          }
-        }
-      };
-    }
-  };
-}
-
-function enumSetEntries(objectActor) {
-  // We currently lack XrayWrappers for Set, so when we iterate over
-  // the values, the temporary iterator objects get created in the target
-  // compartment. However, we _do_ have Xrays to Object now, so we end up
-  // Xraying those temporary objects, and filtering access to |it.value|
-  // based on whether or not it's Xrayable and/or callable, which breaks
-  // the for/of iteration.
-  //
-  // This code is designed to handle untrusted objects, so we can safely
-  // waive Xrays on the iterable, and relying on the Debugger machinery to
-  // make sure we handle the resulting objects carefully.
-  let raw = objectActor.obj.unsafeDereference();
-  let values = [...Cu.waiveXrays(Set.prototype.values.call(raw))];
-
-  return {
-    [Symbol.iterator]: function* () {
-      for (let item of values) {
-        yield gripFromEntry(objectActor, item);
-      }
-    },
-    size: values.length,
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      let val = values[index];
-      return {
-        enumerable: true,
-        value: gripFromEntry(objectActor, val)
-      };
-    }
-  };
-}
-
-function enumWeakSetEntries(objectActor) {
-  // We currently lack XrayWrappers for WeakSet, so when we iterate over
-  // the values, the temporary iterator objects get created in the target
-  // compartment. However, we _do_ have Xrays to Object now, so we end up
-  // Xraying those temporary objects, and filtering access to |it.value|
-  // based on whether or not it's Xrayable and/or callable, which breaks
-  // the for/of iteration.
-  //
-  // This code is designed to handle untrusted objects, so we can safely
-  // waive Xrays on the iterable, and relying on the Debugger machinery to
-  // make sure we handle the resulting objects carefully.
-  let raw = objectActor.obj.unsafeDereference();
-  let keys = Cu.waiveXrays(
-    ChromeUtils.nondeterministicGetWeakSetKeys(raw));
-
-  return {
-    [Symbol.iterator]: function* () {
-      for (let item of keys) {
-        yield gripFromEntry(objectActor, item);
-      }
-    },
-    size: keys.length,
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      let val = keys[index];
-      return {
-        enumerable: true,
-        value: gripFromEntry(objectActor, val)
-      };
-    }
-  };
-}
-
-/**
- * Creates an actor to iterate over an object's symbols.
- *
- * @param objectActor ObjectActor
- *        The object actor.
- */
-function SymbolIteratorActor(objectActor) {
-  let symbols = [];
-  if (DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
-    try {
-      symbols = objectActor.obj.getOwnPropertySymbols();
-    } catch (err) {
-      // The above can throw when the debuggee does not subsume the object's
-      // compartment, or for some WrappedNatives like Cu.Sandbox.
-    }
-  }
-
-  this.iterator = {
-    size: symbols.length,
-    symbolDescription(index) {
-      const symbol = symbols[index];
-      return {
-        name: symbol.toString(),
-        descriptor: objectActor._propertyDescriptor(symbol)
-      };
-    }
-  };
-}
-
-SymbolIteratorActor.prototype = {
-  actorPrefix: "symbolIterator",
-
-  grip() {
-    return {
-      type: this.actorPrefix,
-      actor: this.actorID,
-      count: this.iterator.size
-    };
-  },
-
-  slice({ start, count }) {
-    let ownSymbols = [];
-    for (let i = start, m = start + count; i < m; i++) {
-      ownSymbols.push(this.iterator.symbolDescription(i));
-    }
-    return {
-      ownSymbols
-    };
-  },
-
-  all() {
-    return this.slice({ start: 0, count: this.iterator.size });
-  }
-};
-
-SymbolIteratorActor.prototype.requestTypes = {
-  "slice": SymbolIteratorActor.prototype.slice,
-  "all": SymbolIteratorActor.prototype.all,
-};
-
-/**
- * Functions for adding information to ObjectActor grips for the purpose of
- * having customized output. This object holds arrays mapped by
- * Debugger.Object.prototype.class.
- *
- * In each array you can add functions that take three
- * arguments:
- *   - the ObjectActor instance and its hooks to make a preview for,
- *   - the grip object being prepared for the client,
- *   - the raw JS object after calling Debugger.Object.unsafeDereference(). This
- *   argument is only provided if the object is safe for reading properties and
- *   executing methods. See DevToolsUtils.isSafeJSObject().
- *
- * Functions must return false if they cannot provide preview
- * information for the debugger object, or true otherwise.
- */
-DebuggerServer.ObjectActorPreviewers = {
-  String: [function(objectActor, grip, rawObj) {
-    return wrappedPrimitivePreviewer("String", String, objectActor, grip, rawObj);
-  }],
-
-  Boolean: [function(objectActor, grip, rawObj) {
-    return wrappedPrimitivePreviewer("Boolean", Boolean, objectActor, grip, rawObj);
-  }],
-
-  Number: [function(objectActor, grip, rawObj) {
-    return wrappedPrimitivePreviewer("Number", Number, objectActor, grip, rawObj);
-  }],
-
-  Symbol: [function(objectActor, grip, rawObj) {
-    return wrappedPrimitivePreviewer("Symbol", Symbol, objectActor, grip, rawObj);
-  }],
-
-  Function: [function({obj, hooks}, grip) {
-    if (obj.name) {
-      grip.name = obj.name;
-    }
-
-    if (obj.displayName) {
-      grip.displayName = obj.displayName.substr(0, 500);
-    }
-
-    if (obj.parameterNames) {
-      grip.parameterNames = obj.parameterNames;
-    }
-
-    // Check if the developer has added a de-facto standard displayName
-    // property for us to use.
-    let userDisplayName;
-    try {
-      userDisplayName = obj.getOwnPropertyDescriptor("displayName");
-    } catch (e) {
-      // The above can throw "permission denied" errors when the debuggee
-      // does not subsume the function's compartment.
-    }
-
-    if (userDisplayName && typeof userDisplayName.value == "string" &&
-        userDisplayName.value) {
-      grip.userDisplayName = hooks.createValueGrip(userDisplayName.value);
-    }
-
-    let dbgGlobal = hooks.getGlobalDebugObject();
-    if (dbgGlobal) {
-      let script = dbgGlobal.makeDebuggeeValue(obj.unsafeDereference()).script;
-      if (script) {
-        grip.location = {
-          url: script.url,
-          line: script.startLine
-        };
-      }
-    }
-
-    return true;
-  }],
-
-  RegExp: [function({obj, hooks}, grip) {
-    let str = DevToolsUtils.callPropertyOnObject(obj, "toString");
-    if (typeof str != "string") {
-      return false;
-    }
-
-    grip.displayString = hooks.createValueGrip(str);
-    return true;
-  }],
-
-  Date: [function({obj, hooks}, grip) {
-    let time = DevToolsUtils.callPropertyOnObject(obj, "getTime");
-    if (typeof time != "number") {
-      return false;
-    }
-
-    grip.preview = {
-      timestamp: hooks.createValueGrip(time),
-    };
-    return true;
-  }],
-
-  Array: [function({obj, hooks}, grip) {
-    let length = getArrayLength(obj);
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: length,
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let raw = obj.unsafeDereference();
-    let items = grip.preview.items = [];
-
-    for (let i = 0; i < length; ++i) {
-      // Array Xrays filter out various possibly-unsafe properties (like
-      // functions, and claim that the value is undefined instead. This
-      // is generally the right thing for privileged code accessing untrusted
-      // objects, but quite confusing for Object previews. So we manually
-      // override this protection by waiving Xrays on the array, and re-applying
-      // Xrays on any indexed value props that we pull off of it.
-      let desc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(raw), i);
-      if (desc && !desc.get && !desc.set) {
-        let value = Cu.unwaiveXrays(desc.value);
-        value = makeDebuggeeValueIfNeeded(obj, value);
-        items.push(hooks.createValueGrip(value));
-      } else {
-        items.push(null);
-      }
-
-      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  Set: [function(objectActor, grip) {
-    let size = DevToolsUtils.getProperty(objectActor.obj, "size");
-    if (typeof size != "number") {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: size,
-    };
-
-    // Avoid recursive object grips.
-    if (objectActor.hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let items = grip.preview.items = [];
-    for (let item of enumSetEntries(objectActor)) {
-      items.push(item);
-      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  WeakSet: [function(objectActor, grip) {
-    let enumEntries = enumWeakSetEntries(objectActor);
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: enumEntries.size
-    };
-
-    // Avoid recursive object grips.
-    if (objectActor.hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let items = grip.preview.items = [];
-    for (let item of enumEntries) {
-      items.push(item);
-      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  Map: [function(objectActor, grip) {
-    let size = DevToolsUtils.getProperty(objectActor.obj, "size");
-    if (typeof size != "number") {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "MapLike",
-      size: size,
-    };
-
-    if (objectActor.hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let entries = grip.preview.entries = [];
-    for (let entry of enumMapEntries(objectActor)) {
-      entries.push(entry);
-      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  WeakMap: [function(objectActor, grip) {
-    let enumEntries = enumWeakMapEntries(objectActor);
-
-    grip.preview = {
-      kind: "MapLike",
-      size: enumEntries.size
-    };
-
-    if (objectActor.hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let entries = grip.preview.entries = [];
-    for (let entry of enumEntries) {
-      entries.push(entry);
-      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  DOMStringMap: [function({obj, hooks}, grip, rawObj) {
-    if (!rawObj) {
-      return false;
-    }
-
-    let keys = obj.getOwnPropertyNames();
-    grip.preview = {
-      kind: "MapLike",
-      size: keys.length,
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let entries = grip.preview.entries = [];
-    for (let key of keys) {
-      let value = makeDebuggeeValueIfNeeded(obj, rawObj[key]);
-      entries.push([key, hooks.createValueGrip(value)]);
-      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  Proxy: [function({obj, hooks}, grip, rawObj) {
-    // The `isProxy` getter of the debuggee object only detects proxies without
-    // security wrappers. If false, the target and handler are not available.
-    let hasTargetAndHandler = obj.isProxy;
-    if (hasTargetAndHandler) {
-      grip.proxyTarget = hooks.createValueGrip(obj.proxyTarget);
-      grip.proxyHandler = hooks.createValueGrip(obj.proxyHandler);
-    }
-
-    grip.preview = {
-      kind: "Object",
-      ownProperties: Object.create(null),
-      ownPropertiesLength: 2 * hasTargetAndHandler
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    if (hasTargetAndHandler) {
-      grip.preview.ownProperties["<target>"] = {value: grip.proxyTarget};
-      grip.preview.ownProperties["<handler>"] = {value: grip.proxyHandler};
-    }
-
-    return true;
-  }],
-};
-
-/**
- * Generic previewer for classes wrapping primitives, like String,
- * Number and Boolean.
- *
- * @param string className
- *        Class name to expect.
- * @param object classObj
- *        The class to expect, eg. String. The valueOf() method of the class is
- *        invoked on the given object.
- * @param ObjectActor objectActor
- *        The object actor
- * @param Object grip
- *        The result grip to fill in
- * @return Booolean true if the object was handled, false otherwise
- */
-function wrappedPrimitivePreviewer(className, classObj, objectActor, grip, rawObj) {
-  let {obj, hooks} = objectActor;
-
-  let v = null;
-  try {
-    v = classObj.prototype.valueOf.call(rawObj);
-  } catch (ex) {
-    // valueOf() can throw if the raw JS object is "misbehaved".
-    return false;
-  }
-
-  if (v === null) {
-    return false;
-  }
-
-  let canHandle = GenericObject(objectActor, grip, rawObj, className === "String");
-  if (!canHandle) {
-    return false;
-  }
-
-  grip.preview.wrappedValue =
-    hooks.createValueGrip(makeDebuggeeValueIfNeeded(obj, v));
-  return true;
-}
-
-function GenericObject(objectActor, grip, rawObj, specialStringBehavior = false) {
-  let {obj, hooks} = objectActor;
-  if (grip.preview || grip.displayString || hooks.getGripDepth() > 1) {
-    return false;
-  }
-
-  let i = 0, names = [], symbols = [];
-  let preview = grip.preview = {
-    kind: "Object",
-    ownProperties: Object.create(null),
-    ownSymbols: [],
-  };
-
-  try {
-    names = obj.getOwnPropertyNames();
-    symbols = obj.getOwnPropertySymbols();
-  } catch (ex) {
-    // Calling getOwnPropertyNames() on some wrapped native prototypes is not
-    // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
-  }
-  preview.ownPropertiesLength = names.length;
-  preview.ownSymbolsLength = symbols.length;
-
-  let length;
-  if (specialStringBehavior) {
-    length = DevToolsUtils.getProperty(obj, "length");
-    if (typeof length != "number") {
-      specialStringBehavior = false;
-    }
-  }
-
-  for (let name of names) {
-    if (specialStringBehavior && /^[0-9]+$/.test(name)) {
-      let num = parseInt(name, 10);
-      if (num.toString() === name && num >= 0 && num < length) {
-        continue;
-      }
-    }
-
-    let desc = objectActor._propertyDescriptor(name, true);
-    if (!desc) {
-      continue;
-    }
-
-    preview.ownProperties[name] = desc;
-    if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
-      break;
-    }
-  }
-
-  for (let symbol of symbols) {
-    let descriptor = objectActor._propertyDescriptor(symbol, true);
-    if (!descriptor) {
-      continue;
-    }
-
-    preview.ownSymbols.push(Object.assign({
-      descriptor
-    }, hooks.createValueGrip(symbol)));
-
-    if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
-      break;
-    }
-  }
-
-  if (i < OBJECT_PREVIEW_MAX_ITEMS) {
-    preview.safeGetterValues = objectActor._findSafeGetterValues(
-      Object.keys(preview.ownProperties),
-      OBJECT_PREVIEW_MAX_ITEMS - i);
-  }
-
-  return true;
-}
-
-// Preview functions that do not rely on the object class.
-DebuggerServer.ObjectActorPreviewers.Object = [
-  function TypedArray({obj, hooks}, grip) {
-    if (!isTypedArray(obj)) {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: getArrayLength(obj),
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let raw = obj.unsafeDereference();
-    let global = Cu.getGlobalForObject(DebuggerServer);
-    let classProto = global[obj.class].prototype;
-    // The Xray machinery for TypedArrays denies indexed access on the grounds
-    // that it's slow, and advises callers to do a structured clone instead.
-    let safeView = Cu.cloneInto(classProto.subarray.call(raw, 0,
-      OBJECT_PREVIEW_MAX_ITEMS), global);
-    let items = grip.preview.items = [];
-    for (let i = 0; i < safeView.length; i++) {
-      items.push(safeView[i]);
-    }
-
-    return true;
-  },
-
-  function Error({obj, hooks}, grip) {
-    switch (obj.class) {
-      case "Error":
-      case "EvalError":
-      case "RangeError":
-      case "ReferenceError":
-      case "SyntaxError":
-      case "TypeError":
-      case "URIError":
-        let name = DevToolsUtils.getProperty(obj, "name");
-        let msg = DevToolsUtils.getProperty(obj, "message");
-        let stack = DevToolsUtils.getProperty(obj, "stack");
-        let fileName = DevToolsUtils.getProperty(obj, "fileName");
-        let lineNumber = DevToolsUtils.getProperty(obj, "lineNumber");
-        let columnNumber = DevToolsUtils.getProperty(obj, "columnNumber");
-        grip.preview = {
-          kind: "Error",
-          name: hooks.createValueGrip(name),
-          message: hooks.createValueGrip(msg),
-          stack: hooks.createValueGrip(stack),
-          fileName: hooks.createValueGrip(fileName),
-          lineNumber: hooks.createValueGrip(lineNumber),
-          columnNumber: hooks.createValueGrip(columnNumber),
-        };
-        return true;
-      default:
-        return false;
-    }
-  },
-
-  function CSSMediaRule({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || obj.class != "CSSMediaRule") {
-      return false;
-    }
-    grip.preview = {
-      kind: "ObjectWithText",
-      text: hooks.createValueGrip(rawObj.conditionText),
-    };
-    return true;
-  },
-
-  function CSSStyleRule({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || obj.class != "CSSStyleRule") {
-      return false;
-    }
-    grip.preview = {
-      kind: "ObjectWithText",
-      text: hooks.createValueGrip(rawObj.selectorText),
-    };
-    return true;
-  },
-
-  function ObjectWithURL({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || !(obj.class == "CSSImportRule" ||
-                                 obj.class == "CSSStyleSheet" ||
-                                 obj.class == "Location" ||
-                                 rawObj instanceof Ci.nsIDOMWindow)) {
-      return false;
-    }
-
-    let url;
-    if (rawObj instanceof Ci.nsIDOMWindow && rawObj.location) {
-      url = rawObj.location.href;
-    } else if (rawObj.href) {
-      url = rawObj.href;
-    } else {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ObjectWithURL",
-      url: hooks.createValueGrip(url),
-    };
-
-    return true;
-  },
-
-  function ArrayLike({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj ||
-        obj.class != "DOMStringList" &&
-        obj.class != "DOMTokenList" &&
-        obj.class != "CSSRuleList" &&
-        obj.class != "MediaList" &&
-        obj.class != "StyleSheetList" &&
-        obj.class != "CSSValueList" &&
-        obj.class != "NamedNodeMap" &&
-        obj.class != "FileList" &&
-        obj.class != "NodeList") {
-      return false;
-    }
-
-    if (typeof rawObj.length != "number") {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: rawObj.length,
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let items = grip.preview.items = [];
-
-    for (let i = 0; i < rawObj.length &&
-                    items.length < OBJECT_PREVIEW_MAX_ITEMS; i++) {
-      let value = makeDebuggeeValueIfNeeded(obj, rawObj[i]);
-      items.push(hooks.createValueGrip(value));
-    }
-
-    return true;
-  },
-
-  function CSSStyleDeclaration({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj ||
-        (obj.class != "CSSStyleDeclaration" &&
-         obj.class != "CSS2Properties")) {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "MapLike",
-      size: rawObj.length,
-    };
-
-    let entries = grip.preview.entries = [];
-
-    for (let i = 0; i < OBJECT_PREVIEW_MAX_ITEMS &&
-                    i < rawObj.length; i++) {
-      let prop = rawObj[i];
-      let value = rawObj.getPropertyValue(prop);
-      entries.push([prop, hooks.createValueGrip(value)]);
-    }
-
-    return true;
-  },
-
-  function DOMNode({obj, hooks}, grip, rawObj) {
-    if (isWorker || obj.class == "Object" || !rawObj ||
-        !(rawObj instanceof Ci.nsIDOMNode)) {
-      return false;
-    }
-
-    let preview = grip.preview = {
-      kind: "DOMNode",
-      nodeType: rawObj.nodeType,
-      nodeName: rawObj.nodeName,
-      isConnected: rawObj.isConnected === true,
-    };
-
-    if (rawObj instanceof Ci.nsIDOMDocument && rawObj.location) {
-      preview.location = hooks.createValueGrip(rawObj.location.href);
-    } else if (rawObj instanceof Ci.nsIDOMDocumentFragment) {
-      preview.childNodesLength = rawObj.childNodes.length;
-
-      if (hooks.getGripDepth() < 2) {
-        preview.childNodes = [];
-        for (let node of rawObj.childNodes) {
-          let actor = hooks.createValueGrip(obj.makeDebuggeeValue(node));
-          preview.childNodes.push(actor);
-          if (preview.childNodes.length == OBJECT_PREVIEW_MAX_ITEMS) {
-            break;
-          }
-        }
-      }
-    } else if (rawObj instanceof Ci.nsIDOMElement) {
-      // For HTML elements (in an HTML document, at least), the nodeName is an
-      // uppercased version of the actual element name.  Check for HTML
-      // elements, that is elements in the HTML namespace, and lowercase the
-      // nodeName in that case.
-      if (rawObj.namespaceURI == "http://www.w3.org/1999/xhtml") {
-        preview.nodeName = preview.nodeName.toLowerCase();
-      }
-
-      // Add preview for DOM element attributes.
-      preview.attributes = {};
-      preview.attributesLength = rawObj.attributes.length;
-      for (let attr of rawObj.attributes) {
-        preview.attributes[attr.nodeName] = hooks.createValueGrip(attr.value);
-      }
-    } else if (obj.class == "Attr") {
-      preview.value = hooks.createValueGrip(rawObj.value);
-    } else if (obj.class == "Text" ||
-               obj.class == "CDATASection" ||
-               obj.class == "Comment") {
-      preview.textContent = hooks.createValueGrip(rawObj.textContent);
-    }
-
-    return true;
-  },
-
-  function DOMEvent({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMEvent)) {
-      return false;
-    }
-
-    let preview = grip.preview = {
-      kind: "DOMEvent",
-      type: rawObj.type,
-      properties: Object.create(null),
-    };
-
-    if (hooks.getGripDepth() < 2) {
-      let target = obj.makeDebuggeeValue(rawObj.target);
-      preview.target = hooks.createValueGrip(target);
-    }
-
-    let props = [];
-    if (obj.class == "MouseEvent" ||
-        obj.class == "DragEvent" ||
-        obj.class == "PointerEvent" ||
-        obj.class == "SimpleGestureEvent" ||
-        obj.class == "WheelEvent") {
-      props.push("buttons", "clientX", "clientY", "layerX", "layerY");
-    } else if (obj.class == "KeyboardEvent") {
-      let modifiers = [];
-      if (rawObj.altKey) {
-        modifiers.push("Alt");
-      }
-      if (rawObj.ctrlKey) {
-        modifiers.push("Control");
-      }
-      if (rawObj.metaKey) {
-        modifiers.push("Meta");
-      }
-      if (rawObj.shiftKey) {
-        modifiers.push("Shift");
-      }
-      preview.eventKind = "key";
-      preview.modifiers = modifiers;
-
-      props.push("key", "charCode", "keyCode");
-    } else if (obj.class == "TransitionEvent") {
-      props.push("propertyName", "pseudoElement");
-    } else if (obj.class == "AnimationEvent") {
-      props.push("animationName", "pseudoElement");
-    } else if (obj.class == "ClipboardEvent") {
-      props.push("clipboardData");
-    }
-
-    // Add event-specific properties.
-    for (let prop of props) {
-      let value = rawObj[prop];
-      if (value && (typeof value == "object" || typeof value == "function")) {
-        // Skip properties pointing to objects.
-        if (hooks.getGripDepth() > 1) {
-          continue;
-        }
-        value = obj.makeDebuggeeValue(value);
-      }
-      preview.properties[prop] = hooks.createValueGrip(value);
-    }
-
-    // Add any properties we find on the event object.
-    if (!props.length) {
-      let i = 0;
-      for (let prop in rawObj) {
-        let value = rawObj[prop];
-        if (prop == "target" || prop == "type" || value === null ||
-            typeof value == "function") {
-          continue;
-        }
-        if (value && typeof value == "object") {
-          if (hooks.getGripDepth() > 1) {
-            continue;
-          }
-          value = obj.makeDebuggeeValue(value);
-        }
-        preview.properties[prop] = hooks.createValueGrip(value);
-        if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
-          break;
-        }
-      }
-    }
-
-    return true;
-  },
-
-  function DOMException({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMDOMException)) {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "DOMException",
-      name: hooks.createValueGrip(rawObj.name),
-      message: hooks.createValueGrip(rawObj.message),
-      code: hooks.createValueGrip(rawObj.code),
-      result: hooks.createValueGrip(rawObj.result),
-      filename: hooks.createValueGrip(rawObj.filename),
-      lineNumber: hooks.createValueGrip(rawObj.lineNumber),
-      columnNumber: hooks.createValueGrip(rawObj.columnNumber),
-    };
-
-    return true;
-  },
-
-  function PseudoArray({obj, hooks}, grip, rawObj) {
-    // An object is considered a pseudo-array if all the following apply:
-    // - All its properties are array indices except, optionally, a "length" property.
-    // - At least it has the "0" array index.
-    // - The array indices are consecutive.
-    // - The value of "length", if present, is the number of array indices.
-
-    let keys;
-    try {
-      keys = obj.getOwnPropertyNames();
-    } catch (err) {
-      // The above can throw when the debuggee does not subsume the object's
-      // compartment, or for some WrappedNatives like Cu.Sandbox.
-      return false;
-    }
-    let {length} = keys;
-    if (length === 0) {
-      return false;
-    }
-
-    // Array indices should be sorted at the beginning, from smallest to largest.
-    // Other properties should be at the end, so check if the last one is "length".
-    if (keys[length - 1] === "length") {
-      --length;
-      if (length === 0 || length !== DevToolsUtils.getProperty(obj, "length")) {
-        return false;
-      }
-    }
-
-    // Check that the last key is the array index expected at that position.
-    let lastKey = keys[length - 1];
-    if (!isArrayIndex(lastKey) || +lastKey !== length - 1) {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: length,
-    };
-
-    // Avoid recursive object grips.
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let items = grip.preview.items = [];
-    let numItems = Math.min(OBJECT_PREVIEW_MAX_ITEMS, length);
-
-    for (let i = 0; i < numItems; ++i) {
-      let desc = obj.getOwnPropertyDescriptor(i);
-      if (desc && "value" in desc) {
-        items.push(hooks.createValueGrip(desc.value));
-      } else {
-        items.push(null);
-      }
-    }
-
-    return true;
-  },
-
-  function Object(objectActor, grip, rawObj) {
-    return GenericObject(objectActor, grip, rawObj, /* specialStringBehavior = */ false);
-  },
-];
-
-/**
- * Get thisDebugger.Object referent's `promiseState`.
- *
- * @returns Object
- *          An object of one of the following forms:
- *          - { state: "pending" }
- *          - { state: "fulfilled", value }
- *          - { state: "rejected", reason }
- */
-function getPromiseState(obj) {
-  if (obj.class != "Promise") {
-    throw new Error(
-      "Can't call `getPromiseState` on `Debugger.Object`s that don't " +
-      "refer to Promise objects.");
-  }
-
-  let state = { state: obj.promiseState };
-  if (state.state === "fulfilled") {
-    state.value = obj.promiseValue;
-  } else if (state.state === "rejected") {
-    state.reason = obj.promiseReason;
-  }
-  return state;
-}
-
-/**
- * Determine if a given value is non-primitive.
- *
- * @param Any value
- *        The value to test.
- * @return Boolean
- *         Whether the value is non-primitive.
- */
-function isObject(value) {
-  const type = typeof value;
-  return type == "object" ? value !== null : type == "function";
-}
-
-/**
- * Create a function that can safely stringify Debugger.Objects of a given
- * builtin type.
- *
- * @param Function ctor
- *        The builtin class constructor.
- * @return Function
- *         The stringifier for the class.
- */
-function createBuiltinStringifier(ctor) {
-  return obj => {
-    try {
-      return ctor.prototype.toString.call(obj.unsafeDereference());
-    } catch (err) {
-      // The debuggee will see a "Function" class if the object is callable and
-      // its compartment is not subsumed. The above will throw if it's not really
-      // a function, e.g. if it's a callable proxy.
-      return "[object " + obj.class + "]";
-    }
-  };
-}
-
-/**
- * Stringify a Debugger.Object-wrapped Error instance.
- *
- * @param Debugger.Object obj
- *        The object to stringify.
- * @return String
- *         The stringification of the object.
- */
-function errorStringify(obj) {
-  let name = DevToolsUtils.getProperty(obj, "name");
-  if (name === "" || name === undefined) {
-    name = obj.class;
-  } else if (isObject(name)) {
-    name = stringify(name);
-  }
-
-  let message = DevToolsUtils.getProperty(obj, "message");
-  if (isObject(message)) {
-    message = stringify(message);
-  }
-
-  if (message === "" || message === undefined) {
-    return name;
-  }
-  return name + ": " + message;
-}
-
-/**
- * Stringify a Debugger.Object based on its class.
- *
- * @param Debugger.Object obj
- *        The object to stringify.
- * @return String
- *         The stringification for the object.
- */
-function stringify(obj) {
-  if (!DevToolsUtils.isSafeDebuggerObject(obj)) {
-    if (DevToolsUtils.isCPOW(obj)) {
-      return "<cpow>";
-    }
-    let unwrapped = DevToolsUtils.unwrap(obj);
-    if (unwrapped === undefined) {
-      return "<invisibleToDebugger>";
-    } else if (unwrapped.isProxy) {
-      return "<proxy>";
-    }
-    // The following line should not be reached. It's there just in case somebody
-    // modifies isSafeDebuggerObject to return false for additional kinds of objects.
-    return "[object " + obj.class + "]";
-  } else if (obj.class == "DeadObject") {
-    return "<dead object>";
-  }
-
-  const stringifier = stringifiers[obj.class] || stringifiers.Object;
-
-  try {
-    return stringifier(obj);
-  } catch (e) {
-    DevToolsUtils.reportException("stringify", e);
-    return "<failed to stringify object>";
-  }
-}
-
-// Used to prevent infinite recursion when an array is found inside itself.
-var seen = null;
-
-var stringifiers = {
-  Error: errorStringify,
-  EvalError: errorStringify,
-  RangeError: errorStringify,
-  ReferenceError: errorStringify,
-  SyntaxError: errorStringify,
-  TypeError: errorStringify,
-  URIError: errorStringify,
-  Boolean: createBuiltinStringifier(Boolean),
-  Function: createBuiltinStringifier(Function),
-  Number: createBuiltinStringifier(Number),
-  RegExp: createBuiltinStringifier(RegExp),
-  String: createBuiltinStringifier(String),
-  Object: obj => "[object " + obj.class + "]",
-  Array: obj => {
-    // If we're at the top level then we need to create the Set for tracking
-    // previously stringified arrays.
-    const topLevel = !seen;
-    if (topLevel) {
-      seen = new Set();
-    } else if (seen.has(obj)) {
-      return "";
-    }
-
-    seen.add(obj);
-
-    const len = getArrayLength(obj);
-    let string = "";
-
-    // Array.length is always a non-negative safe integer.
-    for (let i = 0; i < len; i++) {
-      const desc = obj.getOwnPropertyDescriptor(i);
-      if (desc) {
-        const { value } = desc;
-        if (value != null) {
-          string += isObject(value) ? stringify(value) : value;
-        }
-      }
-
-      if (i < len - 1) {
-        string += ",";
-      }
-    }
-
-    if (topLevel) {
-      seen = null;
-    }
-
-    return string;
-  },
-  DOMException: obj => {
-    const message = DevToolsUtils.getProperty(obj, "message") || "<no message>";
-    const result = (+DevToolsUtils.getProperty(obj, "result")).toString(16);
-    const code = DevToolsUtils.getProperty(obj, "code");
-    const name = DevToolsUtils.getProperty(obj, "name") || "<unknown>";
-
-    return '[Exception... "' + message + '" ' +
-           'code: "' + code + '" ' +
-           'nsresult: "0x' + result + " (" + name + ')"]';
-  },
-  Promise: obj => {
-    const { state, value, reason } = getPromiseState(obj);
-    let statePreview = state;
-    if (state != "pending") {
-      const settledValue = state === "fulfilled" ? value : reason;
-      statePreview += ": " + (typeof settledValue === "object" && settledValue !== null
-                                ? stringify(settledValue)
-                                : settledValue);
-    }
-    return "Promise (" + statePreview + ")";
-  },
-};
-
-/**
- * Make a debuggee value for the given object, if needed. Primitive values
- * are left the same.
- *
- * Use case: you have a raw JS object (after unsafe dereference) and you want to
- * send it to the client. In that case you need to use an ObjectActor which
- * requires a debuggee value. The Debugger.Object.prototype.makeDebuggeeValue()
- * method works only for JS objects and functions.
- *
- * @param Debugger.Object obj
- * @param any value
- * @return object
- */
-function makeDebuggeeValueIfNeeded(obj, value) {
-  if (value && (typeof value == "object" || typeof value == "function")) {
-    return obj.makeDebuggeeValue(value);
-  }
-  return value;
-}
-
-/**
- * Creates an actor for the specified "very long" string. "Very long" is specified
- * at the server's discretion.
- *
- * @param string String
- *        The string.
- */
-function LongStringActor(string) {
-  this.string = string;
-  this.stringLength = string.length;
-}
-
-LongStringActor.prototype = {
-  actorPrefix: "longString",
-
-  rawValue: function() {
-    return this.string;
-  },
-
-  destroy: function() {
-    // Because longStringActors is not a weak map, we won't automatically leave
-    // it so we need to manually leave on destroy so that we don't leak
-    // memory.
-    this._releaseActor();
-  },
-
-  /**
-   * Returns a grip for this actor for returning in a protocol message.
-   */
-  grip: function() {
-    return {
-      "type": "longString",
-      "initial": this.string.substring(
-        0, DebuggerServer.LONG_STRING_INITIAL_LENGTH),
-      "length": this.stringLength,
-      "actor": this.actorID
-    };
-  },
-
-  /**
-   * Handle a request to extract part of this actor's string.
-   *
-   * @param request object
-   *        The protocol request object.
-   */
-  onSubstring: function(request) {
-    return {
-      "from": this.actorID,
-      "substring": this.string.substring(request.start, request.end)
-    };
-  },
-
-  /**
-   * Handle a request to release this LongStringActor instance.
-   */
-  onRelease: function() {
-    // TODO: also check if registeredPool === threadActor.threadLifetimePool
-    // when the web console moves away from manually releasing pause-scoped
-    // actors.
-    this._releaseActor();
-    this.registeredPool.removeActor(this);
-    return {};
-  },
-
-  _releaseActor: function() {
-    if (this.registeredPool && this.registeredPool.longStringActors) {
-      delete this.registeredPool.longStringActors[this.string];
-    }
-  }
-};
-
-LongStringActor.prototype.requestTypes = {
-  "substring": LongStringActor.prototype.onSubstring,
-  "release": LongStringActor.prototype.onRelease
-};
-
-/**
- * Creates an actor for the specified symbol.
- *
- * @param symbol Symbol
- *        The symbol.
- */
-function SymbolActor(symbol) {
-  this.symbol = symbol;
-}
-
-SymbolActor.prototype = {
-  actorPrefix: "symbol",
-
-  rawValue: function() {
-    return this.symbol;
-  },
-
-  destroy: function() {
-    // Because symbolActors is not a weak map, we won't automatically leave
-    // it so we need to manually leave on destroy so that we don't leak
-    // memory.
-    this._releaseActor();
-  },
-
-  /**
-   * Returns a grip for this actor for returning in a protocol message.
-   */
-  grip: function() {
-    let form = {
-      type: "symbol",
-      actor: this.actorID,
-    };
-    let name = getSymbolName(this.symbol);
-    if (name !== undefined) {
-      // Create a grip for the name because it might be a longString.
-      form.name = createValueGrip(name, this.registeredPool);
-    }
-    return form;
-  },
-
-  /**
-   * Handle a request to release this SymbolActor instance.
-   */
-  onRelease: function() {
-    // TODO: also check if registeredPool === threadActor.threadLifetimePool
-    // when the web console moves away from manually releasing pause-scoped
-    // actors.
-    this._releaseActor();
-    this.registeredPool.removeActor(this);
-    return {};
-  },
-
-  _releaseActor: function() {
-    if (this.registeredPool && this.registeredPool.symbolActors) {
-      delete this.registeredPool.symbolActors[this.symbol];
-    }
-  }
-};
-
-SymbolActor.prototype.requestTypes = {
-  "release": SymbolActor.prototype.onRelease
-};
-
-/**
- * Creates an actor for the specified ArrayBuffer.
- *
- * @param buffer ArrayBuffer
- *        The buffer.
- */
-function ArrayBufferActor(buffer) {
-  this.buffer = buffer;
-  this.bufferLength = buffer.byteLength;
-}
-
-ArrayBufferActor.prototype = {
-  actorPrefix: "arrayBuffer",
-
-  rawValue: function() {
-    return this.buffer;
-  },
-
-  destroy: function() {
-  },
-
-  grip() {
-    return {
-      "type": "arrayBuffer",
-      "length": this.bufferLength,
-      "actor": this.actorID
-    };
-  },
-
-  onSlice({start, count}) {
-    let slice = new Uint8Array(this.buffer, start, count);
-    let parts = [], offset = 0;
-    const PortionSize = 0x6000; // keep it divisible by 3 for btoa() and join()
-    while (offset + PortionSize < count) {
-      parts.push(btoa(
-        String.fromCharCode.apply(null, slice.subarray(offset, offset + PortionSize))));
-      offset += PortionSize;
-    }
-    parts.push(btoa(String.fromCharCode.apply(null, slice.subarray(offset, count))));
-    return {
-      "from": this.actorID,
-      "encoded": parts.join(""),
-    };
-  }
-};
-
-ArrayBufferActor.prototype.requestTypes = {
-  "slice": ArrayBufferActor.prototype.onSlice,
-};
-
-/**
- * Create a grip for the given debuggee value.  If the value is an
- * object, will create an actor with the given lifetime.
- */
-function createValueGrip(value, pool, makeObjectGrip) {
-  switch (typeof value) {
-    case "boolean":
-      return value;
-
-    case "string":
-      if (stringIsLong(value)) {
-        return longStringGrip(value, pool);
-      }
-      return value;
-
-    case "number":
-      if (value === Infinity) {
-        return { type: "Infinity" };
-      } else if (value === -Infinity) {
-        return { type: "-Infinity" };
-      } else if (Number.isNaN(value)) {
-        return { type: "NaN" };
-      } else if (!value && 1 / value === -Infinity) {
-        return { type: "-0" };
-      }
-      return value;
-
-    case "undefined":
-      return { type: "undefined" };
-
-    case "object":
-      if (value === null) {
-        return { type: "null" };
-      } else if (value.optimizedOut ||
-             value.uninitialized ||
-             value.missingArguments) {
-        // The slot is optimized out, an uninitialized binding, or
-        // arguments on a dead scope
-        return {
-          type: "null",
-          optimizedOut: value.optimizedOut,
-          uninitialized: value.uninitialized,
-          missingArguments: value.missingArguments
-        };
-      }
-      return makeObjectGrip(value, pool);
-
-    case "symbol":
-      return symbolGrip(value, pool);
-
-    default:
-      assert(false, "Failed to provide a grip for: " + value);
-      return null;
-  }
-}
-
-const symbolProtoToString = Symbol.prototype.toString;
-
-function getSymbolName(symbol) {
-  const name = symbolProtoToString.call(symbol).slice("Symbol(".length, -1);
-  return name || undefined;
-}
-
-/**
- * Returns true if the string is long enough to use a LongStringActor instead
- * of passing the value directly over the protocol.
- *
- * @param str String
- *        The string we are checking the length of.
- */
-function stringIsLong(str) {
-  return str.length >= DebuggerServer.LONG_STRING_LENGTH;
-}
-
-/**
- * Create a grip for the given string.
- *
- * @param str String
- *        The string we are creating a grip for.
- * @param pool ActorPool
- *        The actor pool where the new actor will be added.
- */
-function longStringGrip(str, pool) {
-  if (!pool.longStringActors) {
-    pool.longStringActors = {};
-  }
-
-  if (pool.longStringActors.hasOwnProperty(str)) {
-    return pool.longStringActors[str].grip();
-  }
-
-  let actor = new LongStringActor(str);
-  pool.addActor(actor);
-  pool.longStringActors[str] = actor;
-  return actor.grip();
-}
-
-/**
- * Create a grip for the given symbol.
- *
- * @param sym Symbol
- *        The symbol we are creating a grip for.
- * @param pool ActorPool
- *        The actor pool where the new actor will be added.
- */
-function symbolGrip(sym, pool) {
-  if (!pool.symbolActors) {
-    pool.symbolActors = Object.create(null);
-  }
-
-  if (sym in pool.symbolActors) {
-    return pool.symbolActors[sym].grip();
-  }
-
-  let actor = new SymbolActor(sym);
-  pool.addActor(actor);
-  pool.symbolActors[sym] = actor;
-  return actor.grip();
-}
-
-/**
- * Create a grip for the given ArrayBuffer.
- *
- * @param buffer ArrayBuffer
- *        The ArrayBuffer we are creating a grip for.
- * @param pool ActorPool
- *        The actor pool where the new actor will be added.
- */
-function arrayBufferGrip(buffer, pool) {
-  if (!pool.arrayBufferActors) {
-    pool.arrayBufferActors = new WeakMap();
-  }
-
-  if (pool.arrayBufferActors.has(buffer)) {
-    return pool.arrayBufferActors.get(buffer).grip();
-  }
-
-  let actor = new ArrayBufferActor(buffer);
-  pool.addActor(actor);
-  pool.arrayBufferActors.set(buffer, actor);
-  return actor.grip();
-}
-
-const TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
-                             "Uint32Array", "Int8Array", "Int16Array", "Int32Array",
-                             "Float32Array", "Float64Array"];
-
-/**
- * Returns true if a debuggee object is a typed array.
- *
- * @param obj Debugger.Object
- *        The debuggee object to test.
- * @return Boolean
- */
-function isTypedArray(object) {
-  return TYPED_ARRAY_CLASSES.includes(object.class);
-}
-
-/**
- * Returns true if a debuggee object is an array, including a typed array.
- *
- * @param obj Debugger.Object
- *        The debuggee object to test.
- * @return Boolean
- */
-function isArray(object) {
-  return isTypedArray(object) || object.class === "Array";
-}
-
-/**
- * Returns the length of an array (or typed array).
- *
- * @param obj Debugger.Object
- *        The debuggee object of the array.
- * @return Number
- * @throws if the object is not an array.
- */
-function getArrayLength(object) {
-  if (!isArray(object)) {
-    throw new Error("Expected an array, got a " + object.class);
-  }
-
-  // Real arrays have a reliable `length` own property.
-  if (object.class === "Array") {
-    return DevToolsUtils.getProperty(object, "length");
-  }
-
-  // For typed arrays, `DevToolsUtils.getProperty` is not reliable because the `length`
-  // getter could be shadowed by an own property, and `getOwnPropertyNames` is
-  // unnecessarily slow. Obtain the `length` getter safely and call it manually.
-  let typedProto = Object.getPrototypeOf(Uint8Array.prototype);
-  let getter = Object.getOwnPropertyDescriptor(typedProto, "length").get;
-  return getter.call(object.unsafeDereference());
-}
-
-/**
- * Returns true if the parameter can be stored as a 32-bit unsigned integer.
- * If so, it will be suitable for use as the length of an array object.
- *
- * @param num Number
- *        The number to test.
- * @return Boolean
- */
-function isUint32(num) {
-  return num >>> 0 === num;
-}
-
-/**
- * Returns true if the parameter is suitable to be an array index.
- *
- * @param str String
- * @return Boolean
- */
-function isArrayIndex(str) {
-  // Transform the parameter to a 32-bit unsigned integer.
-  let num = str >>> 0;
-  // Check that the parameter is a canonical Uint32 index.
-  return num + "" === str &&
-    // Array indices cannot attain the maximum Uint32 value.
-    num != -1 >>> 0;
-}
-
 exports.ObjectActor = ObjectActor;
-exports.PropertyIteratorActor = PropertyIteratorActor;
-exports.LongStringActor = LongStringActor;
-exports.SymbolActor = SymbolActor;
-exports.createValueGrip = createValueGrip;
-exports.stringIsLong = stringIsLong;
-exports.longStringGrip = longStringGrip;
-exports.arrayBufferGrip = arrayBufferGrip;
copy from devtools/server/actors/object.js
copy to devtools/server/actors/object/long-string.js
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object/long-string.js
@@ -1,2281 +1,23 @@
 /* -*- 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 { Cu, Ci } = require("chrome");
-const { GeneratedLocation } = require("devtools/server/actors/common");
 const { DebuggerServer } = require("devtools/server/main");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const { assert } = DevToolsUtils;
-
-loader.lazyRequireGetter(this, "ChromeUtils");
-
-// Number of items to preview in objects, arrays, maps, sets, lists,
-// collections, etc.
-const OBJECT_PREVIEW_MAX_ITEMS = 10;
-
-/**
- * Creates an actor for the specified object.
- *
- * @param obj Debugger.Object
- *        The debuggee object.
- * @param hooks Object
- *        A collection of abstract methods that are implemented by the caller.
- *        ObjectActor requires the following functions to be implemented by
- *        the caller:
- *          - createValueGrip
- *              Creates a value grip for the given object
- *          - sources
- *              TabSources getter that manages the sources of a thread
- *          - createEnvironmentActor
- *              Creates and return an environment actor
- *          - getGripDepth
- *              An actor's grip depth getter
- *          - incrementGripDepth
- *              Increment the actor's grip depth
- *          - decrementGripDepth
- *              Decrement the actor's grip depth
- *          - globalDebugObject
- *              The Debuggee Global Object as given by the ThreadActor
- */
-function ObjectActor(obj, {
-  createValueGrip: createValueGripHook,
-  sources,
-  createEnvironmentActor,
-  getGripDepth,
-  incrementGripDepth,
-  decrementGripDepth,
-  getGlobalDebugObject
-}) {
-  assert(!obj.optimizedOut,
-         "Should not create object actors for optimized out values!");
-  this.obj = obj;
-  this.hooks = {
-    createValueGrip: createValueGripHook,
-    sources,
-    createEnvironmentActor,
-    getGripDepth,
-    incrementGripDepth,
-    decrementGripDepth,
-    getGlobalDebugObject
-  };
-  this.iterators = new Set();
-}
-
-ObjectActor.prototype = {
-  actorPrefix: "obj",
-
-  rawValue: function() {
-    return this.obj.unsafeDereference();
-  },
-
-  /**
-   * Returns a grip for this actor for returning in a protocol message.
-   */
-  grip: function() {
-    let g = {
-      "type": "object",
-      "actor": this.actorID,
-      "class": this.obj.class,
-    };
-
-    let unwrapped = DevToolsUtils.unwrap(this.obj);
-
-    // Unsafe objects must be treated carefully.
-    if (!DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      if (DevToolsUtils.isCPOW(this.obj)) {
-        // Cross-process object wrappers can't be accessed.
-        g.class = "CPOW: " + g.class;
-      } else if (unwrapped === undefined) {
-        // Objects belonging to an invisible-to-debugger compartment might be proxies,
-        // so just in case they shouldn't be accessed.
-        g.class = "InvisibleToDebugger: " + g.class;
-      } else if (unwrapped.isProxy) {
-        // Proxy objects can run traps when accessed, so just create a preview with
-        // the target and the handler.
-        g.class = "Proxy";
-        this.hooks.incrementGripDepth();
-        DebuggerServer.ObjectActorPreviewers.Proxy[0](this, g, null);
-        this.hooks.decrementGripDepth();
-      }
-      return g;
-    }
-
-    // If the debuggee does not subsume the object's compartment, most properties won't
-    // be accessible. Cross-orgin Window and Location objects might expose some, though.
-    // Change the displayed class, but when creating the preview use the original one.
-    if (unwrapped === null) {
-      g.class = "Restricted";
-    }
-
-    this.hooks.incrementGripDepth();
-
-    g.extensible = this.obj.isExtensible();
-    g.frozen = this.obj.isFrozen();
-    g.sealed = this.obj.isSealed();
-
-    if (g.class == "Promise") {
-      g.promiseState = this._createPromiseState();
-    }
-
-    // FF40+: Allow to know how many properties an object has to lazily display them
-    // when there is a bunch.
-    if (isTypedArray(g)) {
-      // Bug 1348761: getOwnPropertyNames is unnecessary slow on TypedArrays
-      g.ownPropertyLength = getArrayLength(this.obj);
-    } else {
-      try {
-        g.ownPropertyLength = this.obj.getOwnPropertyNames().length;
-      } catch (err) {
-        // The above can throw when the debuggee does not subsume the object's
-        // compartment, or for some WrappedNatives like Cu.Sandbox.
-      }
-    }
-
-    let raw = this.obj.unsafeDereference();
-
-    // If Cu is not defined, we are running on a worker thread, where xrays
-    // don't exist.
-    if (Cu) {
-      raw = Cu.unwaiveXrays(raw);
-    }
-
-    if (!DevToolsUtils.isSafeJSObject(raw)) {
-      raw = null;
-    }
-
-    let previewers = DebuggerServer.ObjectActorPreviewers[this.obj.class] ||
-                     DebuggerServer.ObjectActorPreviewers.Object;
-    for (let fn of previewers) {
-      try {
-        if (fn(this, g, raw)) {
-          break;
-        }
-      } catch (e) {
-        let msg = "ObjectActor.prototype.grip previewer function";
-        DevToolsUtils.reportException(msg, e);
-      }
-    }
-
-    this.hooks.decrementGripDepth();
-    return g;
-  },
-
-  /**
-   * Returns an object exposing the internal Promise state.
-   */
-  _createPromiseState: function() {
-    const { state, value, reason } = getPromiseState(this.obj);
-    let promiseState = { state };
-
-    if (state == "fulfilled") {
-      promiseState.value = this.hooks.createValueGrip(value);
-    } else if (state == "rejected") {
-      promiseState.reason = this.hooks.createValueGrip(reason);
-    }
-
-    promiseState.creationTimestamp = Date.now() - this.obj.promiseLifetime;
-
-    // Only add the timeToSettle property if the Promise isn't pending.
-    if (state !== "pending") {
-      promiseState.timeToSettle = this.obj.promiseTimeToResolution;
-    }
-
-    return promiseState;
-  },
-
-  /**
-   * Releases this actor from the pool.
-   */
-  release: function() {
-    if (this.registeredPool.objectActors) {
-      this.registeredPool.objectActors.delete(this.obj);
-    }
-    this.iterators.forEach(actor => this.registeredPool.removeActor(actor));
-    this.iterators.clear();
-    this.registeredPool.removeActor(this);
-  },
-
-  /**
-   * Handle a protocol request to provide the definition site of this function
-   * object.
-   */
-  onDefinitionSite: function() {
-    if (this.obj.class != "Function") {
-      return {
-        from: this.actorID,
-        error: "objectNotFunction",
-        message: this.actorID + " is not a function."
-      };
-    }
-
-    if (!this.obj.script) {
-      return {
-        from: this.actorID,
-        error: "noScript",
-        message: this.actorID + " has no Debugger.Script"
-      };
-    }
-
-    return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
-      this.hooks.sources().createNonSourceMappedActor(this.obj.script.source),
-      this.obj.script.startLine,
-      0 // TODO bug 901138: use Debugger.Script.prototype.startColumn
-    )).then((originalLocation) => {
-      return {
-        source: originalLocation.originalSourceActor.form(),
-        line: originalLocation.originalLine,
-        column: originalLocation.originalColumn
-      };
-    });
-  },
-
-  /**
-   * Handle a protocol request to provide the names of the properties defined on
-   * the object and not its prototype.
-   */
-  onOwnPropertyNames: function() {
-    let props = [];
-    if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      try {
-        props = this.obj.getOwnPropertyNames();
-      } catch (err) {
-        // The above can throw when the debuggee does not subsume the object's
-        // compartment, or for some WrappedNatives like Cu.Sandbox.
-      }
-    }
-    return { from: this.actorID, ownPropertyNames: props };
-  },
-
-  /**
-   * Creates an actor to iterate over an object property names and values.
-   * See PropertyIteratorActor constructor for more info about options param.
-   *
-   * @param request object
-   *        The protocol request object.
-   */
-  onEnumProperties: function(request) {
-    let actor = new PropertyIteratorActor(this, request.options);
-    this.registeredPool.addActor(actor);
-    this.iterators.add(actor);
-    return { iterator: actor.grip() };
-  },
-
-  /**
-   * Creates an actor to iterate over entries of a Map/Set-like object.
-   */
-  onEnumEntries: function() {
-    let actor = new PropertyIteratorActor(this, { enumEntries: true });
-    this.registeredPool.addActor(actor);
-    this.iterators.add(actor);
-    return { iterator: actor.grip() };
-  },
-
-  /**
-   * Creates an actor to iterate over an object symbols properties.
-   */
-  onEnumSymbols: function() {
-    let actor = new SymbolIteratorActor(this);
-    this.registeredPool.addActor(actor);
-    this.iterators.add(actor);
-    return { iterator: actor.grip() };
-  },
-
-  /**
-   * Handle a protocol request to provide the prototype and own properties of
-   * the object.
-   *
-   * @returns {Object} An object containing the data of this.obj, of the following form:
-   *          - {string} from: this.obj's actorID.
-   *          - {Object} prototype: The descriptor of this.obj's prototype.
-   *          - {Object} ownProperties: an object where the keys are the names of the
-   *                     this.obj's ownProperties, and the values the descriptors of
-   *                     the properties.
-   *          - {Array} ownSymbols: An array containing all descriptors of this.obj's
-   *                    ownSymbols. Here we have an array, and not an object like for
-   *                    ownProperties, because we can have multiple symbols with the same
-   *                    name in this.obj, e.g. `{[Symbol()]: "a", [Symbol()]: "b"}`.
-   *          - {Object} safeGetterValues: an object that maps this.obj's property names
-   *                     with safe getters descriptors.
-   */
-  onPrototypeAndProperties: function() {
-    let proto = null;
-    let names = [];
-    let symbols = [];
-    if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      try {
-        proto = this.obj.proto;
-        names = this.obj.getOwnPropertyNames();
-        symbols = this.obj.getOwnPropertySymbols();
-      } catch (err) {
-        // The above can throw when the debuggee does not subsume the object's
-        // compartment, or for some WrappedNatives like Cu.Sandbox.
-      }
-    }
-
-    let ownProperties = Object.create(null);
-    let ownSymbols = [];
-
-    for (let name of names) {
-      ownProperties[name] = this._propertyDescriptor(name);
-    }
-
-    for (let sym of symbols) {
-      ownSymbols.push({
-        name: sym.toString(),
-        descriptor: this._propertyDescriptor(sym)
-      });
-    }
-
-    return { from: this.actorID,
-             prototype: this.hooks.createValueGrip(proto),
-             ownProperties,
-             ownSymbols,
-             safeGetterValues: this._findSafeGetterValues(names) };
-  },
-
-  /**
-   * Find the safe getter values for the current Debugger.Object, |this.obj|.
-   *
-   * @private
-   * @param array ownProperties
-   *        The array that holds the list of known ownProperties names for
-   *        |this.obj|.
-   * @param number [limit=0]
-   *        Optional limit of getter values to find.
-   * @return object
-   *         An object that maps property names to safe getter descriptors as
-   *         defined by the remote debugging protocol.
-   */
-  _findSafeGetterValues: function(ownProperties, limit = 0) {
-    let safeGetterValues = Object.create(null);
-    let obj = this.obj;
-    let level = 0, i = 0;
-
-    // Do not search safe getters in unsafe objects.
-    if (!DevToolsUtils.isSafeDebuggerObject(obj)) {
-      return safeGetterValues;
-    }
-
-    // Most objects don't have any safe getters but inherit some from their
-    // prototype. Avoid calling getOwnPropertyNames on objects that may have
-    // many properties like Array, strings or js objects. That to avoid
-    // freezing firefox when doing so.
-    if (isArray(this.obj) || ["Object", "String"].includes(this.obj.class)) {
-      obj = obj.proto;
-      level++;
-    }
-
-    while (obj && DevToolsUtils.isSafeDebuggerObject(obj)) {
-      let getters = this._findSafeGetters(obj);
-      for (let name of getters) {
-        // Avoid overwriting properties from prototypes closer to this.obj. Also
-        // avoid providing safeGetterValues from prototypes if property |name|
-        // is already defined as an own property.
-        if (name in safeGetterValues ||
-            (obj != this.obj && ownProperties.includes(name))) {
-          continue;
-        }
-
-        // Ignore __proto__ on Object.prototye.
-        if (!obj.proto && name == "__proto__") {
-          continue;
-        }
-
-        let desc = null, getter = null;
-        try {
-          desc = obj.getOwnPropertyDescriptor(name);
-          getter = desc.get;
-        } catch (ex) {
-          // The above can throw if the cache becomes stale.
-        }
-        if (!getter) {
-          obj._safeGetters = null;
-          continue;
-        }
-
-        let result = getter.call(this.obj);
-        if (result && !("throw" in result)) {
-          let getterValue = undefined;
-          if ("return" in result) {
-            getterValue = result.return;
-          } else if ("yield" in result) {
-            getterValue = result.yield;
-          }
-          // WebIDL attributes specified with the LenientThis extended attribute
-          // return undefined and should be ignored.
-          if (getterValue !== undefined) {
-            safeGetterValues[name] = {
-              getterValue: this.hooks.createValueGrip(getterValue),
-              getterPrototypeLevel: level,
-              enumerable: desc.enumerable,
-              writable: level == 0 ? desc.writable : true,
-            };
-            if (limit && ++i == limit) {
-              break;
-            }
-          }
-        }
-      }
-      if (limit && i == limit) {
-        break;
-      }
-
-      obj = obj.proto;
-      level++;
-    }
-
-    return safeGetterValues;
-  },
-
-  /**
-   * Find the safe getters for a given Debugger.Object. Safe getters are native
-   * getters which are safe to execute.
-   *
-   * @private
-   * @param Debugger.Object object
-   *        The Debugger.Object where you want to find safe getters.
-   * @return Set
-   *         A Set of names of safe getters. This result is cached for each
-   *         Debugger.Object.
-   */
-  _findSafeGetters: function(object) {
-    if (object._safeGetters) {
-      return object._safeGetters;
-    }
-
-    let getters = new Set();
-
-    if (!DevToolsUtils.isSafeDebuggerObject(object)) {
-      object._safeGetters = getters;
-      return getters;
-    }
-
-    let names = [];
-    try {
-      names = object.getOwnPropertyNames();
-    } catch (ex) {
-      // Calling getOwnPropertyNames() on some wrapped native prototypes is not
-      // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
-    }
-
-    for (let name of names) {
-      let desc = null;
-      try {
-        desc = object.getOwnPropertyDescriptor(name);
-      } catch (e) {
-        // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
-        // allowed (bug 560072).
-      }
-      if (!desc || desc.value !== undefined || !("get" in desc)) {
-        continue;
-      }
-
-      if (DevToolsUtils.hasSafeGetter(desc)) {
-        getters.add(name);
-      }
-    }
-
-    object._safeGetters = getters;
-    return getters;
-  },
-
-  /**
-   * Handle a protocol request to provide the prototype of the object.
-   */
-  onPrototype: function() {
-    let proto = null;
-    if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      proto = this.obj.proto;
-    }
-    return { from: this.actorID,
-             prototype: this.hooks.createValueGrip(proto) };
-  },
-
-  /**
-   * Handle a protocol request to provide the property descriptor of the
-   * object's specified property.
-   *
-   * @param request object
-   *        The protocol request object.
-   */
-  onProperty: function(request) {
-    if (!request.name) {
-      return { error: "missingParameter",
-               message: "no property name was specified" };
-    }
-
-    return { from: this.actorID,
-             descriptor: this._propertyDescriptor(request.name) };
-  },
-
-  /**
-   * Handle a protocol request to provide the display string for the object.
-   */
-  onDisplayString: function() {
-    const string = stringify(this.obj);
-    return { from: this.actorID,
-             displayString: this.hooks.createValueGrip(string) };
-  },
-
-  /**
-   * A helper method that creates a property descriptor for the provided object,
-   * properly formatted for sending in a protocol response.
-   *
-   * @private
-   * @param string name
-   *        The property that the descriptor is generated for.
-   * @param boolean [onlyEnumerable]
-   *        Optional: true if you want a descriptor only for an enumerable
-   *        property, false otherwise.
-   * @return object|undefined
-   *         The property descriptor, or undefined if this is not an enumerable
-   *         property and onlyEnumerable=true.
-   */
-  _propertyDescriptor: function(name, onlyEnumerable) {
-    if (!DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      return undefined;
-    }
-
-    let desc;
-    try {
-      desc = this.obj.getOwnPropertyDescriptor(name);
-    } catch (e) {
-      // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
-      // allowed (bug 560072). Inform the user with a bogus, but hopefully
-      // explanatory, descriptor.
-      return {
-        configurable: false,
-        writable: false,
-        enumerable: false,
-        value: e.name
-      };
-    }
-
-    if (!desc || onlyEnumerable && !desc.enumerable) {
-      return undefined;
-    }
-
-    let retval = {
-      configurable: desc.configurable,
-      enumerable: desc.enumerable
-    };
-
-    if ("value" in desc) {
-      retval.writable = desc.writable;
-      retval.value = this.hooks.createValueGrip(desc.value);
-    } else {
-      if ("get" in desc) {
-        retval.get = this.hooks.createValueGrip(desc.get);
-      }
-      if ("set" in desc) {
-        retval.set = this.hooks.createValueGrip(desc.set);
-      }
-    }
-    return retval;
-  },
-
-  /**
-   * Handle a protocol request to provide the source code of a function.
-   *
-   * @param request object
-   *        The protocol request object.
-   */
-  onDecompile: function(request) {
-    if (this.obj.class !== "Function") {
-      return { error: "objectNotFunction",
-               message: "decompile request is only valid for object grips " +
-                        "with a 'Function' class." };
-    }
-
-    return { from: this.actorID,
-             decompiledCode: this.obj.decompile(!!request.pretty) };
-  },
-
-  /**
-   * Handle a protocol request to provide the parameters of a function.
-   */
-  onParameterNames: function() {
-    if (this.obj.class !== "Function") {
-      return { error: "objectNotFunction",
-               message: "'parameterNames' request is only valid for object " +
-                        "grips with a 'Function' class." };
-    }
-
-    return { parameterNames: this.obj.parameterNames };
-  },
-
-  /**
-   * Handle a protocol request to release a thread-lifetime grip.
-   */
-  onRelease: function() {
-    this.release();
-    return {};
-  },
-
-  /**
-   * Handle a protocol request to provide the lexical scope of a function.
-   */
-  onScope: function() {
-    if (this.obj.class !== "Function") {
-      return { error: "objectNotFunction",
-               message: "scope request is only valid for object grips with a" +
-                        " 'Function' class." };
-    }
-
-    let envActor = this.hooks.createEnvironmentActor(this.obj.environment,
-                                                     this.registeredPool);
-    if (!envActor) {
-      return { error: "notDebuggee",
-               message: "cannot access the environment of this function." };
-    }
-
-    return { from: this.actorID, scope: envActor.form() };
-  },
-
-  /**
-   * Handle a protocol request to get the list of dependent promises of a
-   * promise.
-   *
-   * @return object
-   *         Returns an object containing an array of object grips of the
-   *         dependent promises
-   */
-  onDependentPromises: function() {
-    if (this.obj.class != "Promise") {
-      return { error: "objectNotPromise",
-               message: "'dependentPromises' request is only valid for " +
-                        "object grips with a 'Promise' class." };
-    }
-
-    let promises = this.obj.promiseDependentPromises
-                           .map(p => this.hooks.createValueGrip(p));
-
-    return { promises };
-  },
-
-  /**
-   * Handle a protocol request to get the allocation stack of a promise.
-   */
-  onAllocationStack: function() {
-    if (this.obj.class != "Promise") {
-      return { error: "objectNotPromise",
-               message: "'allocationStack' request is only valid for " +
-                        "object grips with a 'Promise' class." };
-    }
-
-    let stack = this.obj.promiseAllocationSite;
-    let allocationStacks = [];
-
-    while (stack) {
-      if (stack.source) {
-        let source = this._getSourceOriginalLocation(stack);
-
-        if (source) {
-          allocationStacks.push(source);
-        }
-      }
-      stack = stack.parent;
-    }
-
-    return Promise.all(allocationStacks).then(stacks => {
-      return { allocationStack: stacks };
-    });
-  },
-
-  /**
-   * Handle a protocol request to get the fulfillment stack of a promise.
-   */
-  onFulfillmentStack: function() {
-    if (this.obj.class != "Promise") {
-      return { error: "objectNotPromise",
-               message: "'fulfillmentStack' request is only valid for " +
-                        "object grips with a 'Promise' class." };
-    }
-
-    let stack = this.obj.promiseResolutionSite;
-    let fulfillmentStacks = [];
-
-    while (stack) {
-      if (stack.source) {
-        let source = this._getSourceOriginalLocation(stack);
-
-        if (source) {
-          fulfillmentStacks.push(source);
-        }
-      }
-      stack = stack.parent;
-    }
-
-    return Promise.all(fulfillmentStacks).then(stacks => {
-      return { fulfillmentStack: stacks };
-    });
-  },
-
-  /**
-   * Handle a protocol request to get the rejection stack of a promise.
-   */
-  onRejectionStack: function() {
-    if (this.obj.class != "Promise") {
-      return { error: "objectNotPromise",
-               message: "'rejectionStack' request is only valid for " +
-                        "object grips with a 'Promise' class." };
-    }
-
-    let stack = this.obj.promiseResolutionSite;
-    let rejectionStacks = [];
-
-    while (stack) {
-      if (stack.source) {
-        let source = this._getSourceOriginalLocation(stack);
-
-        if (source) {
-          rejectionStacks.push(source);
-        }
-      }
-      stack = stack.parent;
-    }
-
-    return Promise.all(rejectionStacks).then(stacks => {
-      return { rejectionStack: stacks };
-    });
-  },
-
-  /**
-   * Helper function for fetching the source location of a SavedFrame stack.
-   *
-   * @param SavedFrame stack
-   *        The promise allocation stack frame
-   * @return object
-   *         Returns an object containing the source location of the SavedFrame
-   *         stack.
-   */
-  _getSourceOriginalLocation: function(stack) {
-    let source;
-
-    // Catch any errors if the source actor cannot be found
-    try {
-      source = this.hooks.sources().getSourceActorByURL(stack.source);
-    } catch (e) {
-      // ignored
-    }
-
-    if (!source) {
-      return null;
-    }
-
-    return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
-      source,
-      stack.line,
-      stack.column
-    )).then((originalLocation) => {
-      return {
-        source: originalLocation.originalSourceActor.form(),
-        line: originalLocation.originalLine,
-        column: originalLocation.originalColumn,
-        functionDisplayName: stack.functionDisplayName
-      };
-    });
-  }
-};
-
-ObjectActor.prototype.requestTypes = {
-  "definitionSite": ObjectActor.prototype.onDefinitionSite,
-  "parameterNames": ObjectActor.prototype.onParameterNames,
-  "prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties,
-  "enumProperties": ObjectActor.prototype.onEnumProperties,
-  "prototype": ObjectActor.prototype.onPrototype,
-  "property": ObjectActor.prototype.onProperty,
-  "displayString": ObjectActor.prototype.onDisplayString,
-  "ownPropertyNames": ObjectActor.prototype.onOwnPropertyNames,
-  "decompile": ObjectActor.prototype.onDecompile,
-  "release": ObjectActor.prototype.onRelease,
-  "scope": ObjectActor.prototype.onScope,
-  "dependentPromises": ObjectActor.prototype.onDependentPromises,
-  "allocationStack": ObjectActor.prototype.onAllocationStack,
-  "fulfillmentStack": ObjectActor.prototype.onFulfillmentStack,
-  "rejectionStack": ObjectActor.prototype.onRejectionStack,
-  "enumEntries": ObjectActor.prototype.onEnumEntries,
-  "enumSymbols": ObjectActor.prototype.onEnumSymbols,
-};
-
-/**
- * Creates an actor to iterate over an object's property names and values.
- *
- * @param objectActor ObjectActor
- *        The object actor.
- * @param options Object
- *        A dictionary object with various boolean attributes:
- *        - enumEntries Boolean
- *          If true, enumerates the entries of a Map or Set object
- *          instead of enumerating properties.
- *        - ignoreIndexedProperties Boolean
- *          If true, filters out Array items.
- *          e.g. properties names between `0` and `object.length`.
- *        - ignoreNonIndexedProperties Boolean
- *          If true, filters out items that aren't array items
- *          e.g. properties names that are not a number between `0`
- *          and `object.length`.
- *        - sort Boolean
- *          If true, the iterator will sort the properties by name
- *          before dispatching them.
- *        - query String
- *          If non-empty, will filter the properties by names and values
- *          containing this query string. The match is not case-sensitive.
- *          Regarding value filtering it just compare to the stringification
- *          of the property value.
- */
-function PropertyIteratorActor(objectActor, options) {
-  if (!DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
-    this.iterator = {
-      size: 0,
-      propertyName: index => undefined,
-      propertyDescription: index => undefined,
-    };
-  } else if (options.enumEntries) {
-    let cls = objectActor.obj.class;
-    if (cls == "Map") {
-      this.iterator = enumMapEntries(objectActor);
-    } else if (cls == "WeakMap") {
-      this.iterator = enumWeakMapEntries(objectActor);
-    } else if (cls == "Set") {
-      this.iterator = enumSetEntries(objectActor);
-    } else if (cls == "WeakSet") {
-      this.iterator = enumWeakSetEntries(objectActor);
-    } else {
-      throw new Error("Unsupported class to enumerate entries from: " + cls);
-    }
-  } else if (
-    isArray(objectActor.obj)
-    && options.ignoreNonIndexedProperties
-    && !options.query
-  ) {
-    this.iterator = enumArrayProperties(objectActor, options);
-  } else {
-    this.iterator = enumObjectProperties(objectActor, options);
-  }
-}
-
-PropertyIteratorActor.prototype = {
-  actorPrefix: "propertyIterator",
-
-  grip() {
-    return {
-      type: this.actorPrefix,
-      actor: this.actorID,
-      count: this.iterator.size
-    };
-  },
-
-  names({ indexes }) {
-    let list = [];
-    for (let idx of indexes) {
-      list.push(this.iterator.propertyName(idx));
-    }
-    return {
-      names: indexes
-    };
-  },
-
-  slice({ start, count }) {
-    let ownProperties = Object.create(null);
-    for (let i = start, m = start + count; i < m; i++) {
-      let name = this.iterator.propertyName(i);
-      ownProperties[name] = this.iterator.propertyDescription(i);
-    }
-    return {
-      ownProperties
-    };
-  },
-
-  all() {
-    return this.slice({ start: 0, count: this.iterator.size });
-  }
-};
-
-PropertyIteratorActor.prototype.requestTypes = {
-  "names": PropertyIteratorActor.prototype.names,
-  "slice": PropertyIteratorActor.prototype.slice,
-  "all": PropertyIteratorActor.prototype.all,
-};
-
-function enumArrayProperties(objectActor, options) {
-  return {
-    size: getArrayLength(objectActor.obj),
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      return objectActor._propertyDescriptor(index);
-    }
-  };
-}
-
-function enumObjectProperties(objectActor, options) {
-  let names = [];
-  try {
-    names = objectActor.obj.getOwnPropertyNames();
-  } catch (ex) {
-    // Calling getOwnPropertyNames() on some wrapped native prototypes is not
-    // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
-  }
-
-  if (options.ignoreNonIndexedProperties || options.ignoreIndexedProperties) {
-    let length = DevToolsUtils.getProperty(objectActor.obj, "length");
-    let sliceIndex;
-
-    const isLengthTrustworthy =
-      isUint32(length)
-      && (!length || isArrayIndex(names[length - 1]))
-      && !isArrayIndex(names[length]);
-
-    if (!isLengthTrustworthy) {
-      // The length property may not reflect what the object looks like, let's find
-      // where indexed properties end.
-
-      if (!isArrayIndex(names[0])) {
-        // If the first item is not a number, this means there is no indexed properties
-        // in this object.
-        sliceIndex = 0;
-      } else {
-        sliceIndex = names.length;
-        while (sliceIndex > 0) {
-          if (isArrayIndex(names[sliceIndex - 1])) {
-            break;
-          }
-          sliceIndex--;
-        }
-      }
-    } else {
-      sliceIndex = length;
-    }
-
-    // It appears that getOwnPropertyNames always returns indexed properties
-    // first, so we can safely slice `names` for/against indexed properties.
-    // We do such clever operation to optimize very large array inspection,
-    // like webaudio buffers.
-    if (options.ignoreIndexedProperties) {
-      // Keep items after `sliceIndex` index
-      names = names.slice(sliceIndex);
-    } else if (options.ignoreNonIndexedProperties) {
-      // Keep `sliceIndex` first items
-      names.length = sliceIndex;
-    }
-  }
-
-  let safeGetterValues = objectActor._findSafeGetterValues(names, 0);
-  let safeGetterNames = Object.keys(safeGetterValues);
-  // Merge the safe getter values into the existing properties list.
-  for (let name of safeGetterNames) {
-    if (!names.includes(name)) {
-      names.push(name);
-    }
-  }
-
-  if (options.query) {
-    let { query } = options;
-    query = query.toLowerCase();
-    names = names.filter(name => {
-      // Filter on attribute names
-      if (name.toLowerCase().includes(query)) {
-        return true;
-      }
-      // and then on attribute values
-      let desc;
-      try {
-        desc = objectActor.obj.getOwnPropertyDescriptor(name);
-      } catch (e) {
-        // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
-        // allowed (bug 560072).
-      }
-      if (desc && desc.value &&
-          String(desc.value).includes(query)) {
-        return true;
-      }
-      return false;
-    });
-  }
-
-  if (options.sort) {
-    names.sort();
-  }
-
-  return {
-    size: names.length,
-    propertyName(index) {
-      return names[index];
-    },
-    propertyDescription(index) {
-      let name = names[index];
-      let desc = objectActor._propertyDescriptor(name);
-      if (!desc) {
-        desc = safeGetterValues[name];
-      } else if (name in safeGetterValues) {
-        // Merge the safe getter values into the existing properties list.
-        let { getterValue, getterPrototypeLevel } = safeGetterValues[name];
-        desc.getterValue = getterValue;
-        desc.getterPrototypeLevel = getterPrototypeLevel;
-      }
-      return desc;
-    }
-  };
-}
-
-/**
- * Helper function to create a grip from a Map/Set entry
- */
-function gripFromEntry({ obj, hooks }, entry) {
-  return hooks.createValueGrip(
-    makeDebuggeeValueIfNeeded(obj, Cu.unwaiveXrays(entry)));
-}
-
-function enumMapEntries(objectActor) {
-  // Iterating over a Map via .entries goes through various intermediate
-  // objects - an Iterator object, then a 2-element Array object, then the
-  // actual values we care about. We don't have Xrays to Iterator objects,
-  // so we get Opaque wrappers for them. And even though we have Xrays to
-  // Arrays, the semantics often deny access to the entires based on the
-  // nature of the values. So we need waive Xrays for the iterator object
-  // and the tupes, and then re-apply them on the underlying values until
-  // we fix bug 1023984.
-  //
-  // Even then though, we might want to continue waiving Xrays here for the
-  // same reason we do so for Arrays above - this filtering behavior is likely
-  // to be more confusing than beneficial in the case of Object previews.
-  let raw = objectActor.obj.unsafeDereference();
-
-  let keys = [...Cu.waiveXrays(Map.prototype.keys.call(raw))];
-  return {
-    [Symbol.iterator]: function* () {
-      for (let key of keys) {
-        let value = Map.prototype.get.call(raw, key);
-        yield [ key, value ].map(val => gripFromEntry(objectActor, val));
-      }
-    },
-    size: keys.length,
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      let key = keys[index];
-      let val = Map.prototype.get.call(raw, key);
-      return {
-        enumerable: true,
-        value: {
-          type: "mapEntry",
-          preview: {
-            key: gripFromEntry(objectActor, key),
-            value: gripFromEntry(objectActor, val)
-          }
-        }
-      };
-    }
-  };
-}
-
-function enumWeakMapEntries(objectActor) {
-  // We currently lack XrayWrappers for WeakMap, so when we iterate over
-  // the values, the temporary iterator objects get created in the target
-  // compartment. However, we _do_ have Xrays to Object now, so we end up
-  // Xraying those temporary objects, and filtering access to |it.value|
-  // based on whether or not it's Xrayable and/or callable, which breaks
-  // the for/of iteration.
-  //
-  // This code is designed to handle untrusted objects, so we can safely
-  // waive Xrays on the iterable, and relying on the Debugger machinery to
-  // make sure we handle the resulting objects carefully.
-  let raw = objectActor.obj.unsafeDereference();
-  let keys = Cu.waiveXrays(
-    ChromeUtils.nondeterministicGetWeakMapKeys(raw));
-
-  return {
-    [Symbol.iterator]: function* () {
-      for (let key of keys) {
-        let value = WeakMap.prototype.get.call(raw, key);
-        yield [ key, value ].map(val => gripFromEntry(objectActor, val));
-      }
-    },
-    size: keys.length,
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      let key = keys[index];
-      let val = WeakMap.prototype.get.call(raw, key);
-      return {
-        enumerable: true,
-        value: {
-          type: "mapEntry",
-          preview: {
-            key: gripFromEntry(objectActor, key),
-            value: gripFromEntry(objectActor, val)
-          }
-        }
-      };
-    }
-  };
-}
-
-function enumSetEntries(objectActor) {
-  // We currently lack XrayWrappers for Set, so when we iterate over
-  // the values, the temporary iterator objects get created in the target
-  // compartment. However, we _do_ have Xrays to Object now, so we end up
-  // Xraying those temporary objects, and filtering access to |it.value|
-  // based on whether or not it's Xrayable and/or callable, which breaks
-  // the for/of iteration.
-  //
-  // This code is designed to handle untrusted objects, so we can safely
-  // waive Xrays on the iterable, and relying on the Debugger machinery to
-  // make sure we handle the resulting objects carefully.
-  let raw = objectActor.obj.unsafeDereference();
-  let values = [...Cu.waiveXrays(Set.prototype.values.call(raw))];
-
-  return {
-    [Symbol.iterator]: function* () {
-      for (let item of values) {
-        yield gripFromEntry(objectActor, item);
-      }
-    },
-    size: values.length,
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      let val = values[index];
-      return {
-        enumerable: true,
-        value: gripFromEntry(objectActor, val)
-      };
-    }
-  };
-}
-
-function enumWeakSetEntries(objectActor) {
-  // We currently lack XrayWrappers for WeakSet, so when we iterate over
-  // the values, the temporary iterator objects get created in the target
-  // compartment. However, we _do_ have Xrays to Object now, so we end up
-  // Xraying those temporary objects, and filtering access to |it.value|
-  // based on whether or not it's Xrayable and/or callable, which breaks
-  // the for/of iteration.
-  //
-  // This code is designed to handle untrusted objects, so we can safely
-  // waive Xrays on the iterable, and relying on the Debugger machinery to
-  // make sure we handle the resulting objects carefully.
-  let raw = objectActor.obj.unsafeDereference();
-  let keys = Cu.waiveXrays(
-    ChromeUtils.nondeterministicGetWeakSetKeys(raw));
-
-  return {
-    [Symbol.iterator]: function* () {
-      for (let item of keys) {
-        yield gripFromEntry(objectActor, item);
-      }
-    },
-    size: keys.length,
-    propertyName(index) {
-      return index;
-    },
-    propertyDescription(index) {
-      let val = keys[index];
-      return {
-        enumerable: true,
-        value: gripFromEntry(objectActor, val)
-      };
-    }
-  };
-}
-
-/**
- * Creates an actor to iterate over an object's symbols.
- *
- * @param objectActor ObjectActor
- *        The object actor.
- */
-function SymbolIteratorActor(objectActor) {
-  let symbols = [];
-  if (DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
-    try {
-      symbols = objectActor.obj.getOwnPropertySymbols();
-    } catch (err) {
-      // The above can throw when the debuggee does not subsume the object's
-      // compartment, or for some WrappedNatives like Cu.Sandbox.
-    }
-  }
-
-  this.iterator = {
-    size: symbols.length,
-    symbolDescription(index) {
-      const symbol = symbols[index];
-      return {
-        name: symbol.toString(),
-        descriptor: objectActor._propertyDescriptor(symbol)
-      };
-    }
-  };
-}
-
-SymbolIteratorActor.prototype = {
-  actorPrefix: "symbolIterator",
-
-  grip() {
-    return {
-      type: this.actorPrefix,
-      actor: this.actorID,
-      count: this.iterator.size
-    };
-  },
-
-  slice({ start, count }) {
-    let ownSymbols = [];
-    for (let i = start, m = start + count; i < m; i++) {
-      ownSymbols.push(this.iterator.symbolDescription(i));
-    }
-    return {
-      ownSymbols
-    };
-  },
-
-  all() {
-    return this.slice({ start: 0, count: this.iterator.size });
-  }
-};
-
-SymbolIteratorActor.prototype.requestTypes = {
-  "slice": SymbolIteratorActor.prototype.slice,
-  "all": SymbolIteratorActor.prototype.all,
-};
-
-/**
- * Functions for adding information to ObjectActor grips for the purpose of
- * having customized output. This object holds arrays mapped by
- * Debugger.Object.prototype.class.
- *
- * In each array you can add functions that take three
- * arguments:
- *   - the ObjectActor instance and its hooks to make a preview for,
- *   - the grip object being prepared for the client,
- *   - the raw JS object after calling Debugger.Object.unsafeDereference(). This
- *   argument is only provided if the object is safe for reading properties and
- *   executing methods. See DevToolsUtils.isSafeJSObject().
- *
- * Functions must return false if they cannot provide preview
- * information for the debugger object, or true otherwise.
- */
-DebuggerServer.ObjectActorPreviewers = {
-  String: [function(objectActor, grip, rawObj) {
-    return wrappedPrimitivePreviewer("String", String, objectActor, grip, rawObj);
-  }],
-
-  Boolean: [function(objectActor, grip, rawObj) {
-    return wrappedPrimitivePreviewer("Boolean", Boolean, objectActor, grip, rawObj);
-  }],
-
-  Number: [function(objectActor, grip, rawObj) {
-    return wrappedPrimitivePreviewer("Number", Number, objectActor, grip, rawObj);
-  }],
-
-  Symbol: [function(objectActor, grip, rawObj) {
-    return wrappedPrimitivePreviewer("Symbol", Symbol, objectActor, grip, rawObj);
-  }],
-
-  Function: [function({obj, hooks}, grip) {
-    if (obj.name) {
-      grip.name = obj.name;
-    }
-
-    if (obj.displayName) {
-      grip.displayName = obj.displayName.substr(0, 500);
-    }
-
-    if (obj.parameterNames) {
-      grip.parameterNames = obj.parameterNames;
-    }
-
-    // Check if the developer has added a de-facto standard displayName
-    // property for us to use.
-    let userDisplayName;
-    try {
-      userDisplayName = obj.getOwnPropertyDescriptor("displayName");
-    } catch (e) {
-      // The above can throw "permission denied" errors when the debuggee
-      // does not subsume the function's compartment.
-    }
-
-    if (userDisplayName && typeof userDisplayName.value == "string" &&
-        userDisplayName.value) {
-      grip.userDisplayName = hooks.createValueGrip(userDisplayName.value);
-    }
-
-    let dbgGlobal = hooks.getGlobalDebugObject();
-    if (dbgGlobal) {
-      let script = dbgGlobal.makeDebuggeeValue(obj.unsafeDereference()).script;
-      if (script) {
-        grip.location = {
-          url: script.url,
-          line: script.startLine
-        };
-      }
-    }
-
-    return true;
-  }],
-
-  RegExp: [function({obj, hooks}, grip) {
-    let str = DevToolsUtils.callPropertyOnObject(obj, "toString");
-    if (typeof str != "string") {
-      return false;
-    }
-
-    grip.displayString = hooks.createValueGrip(str);
-    return true;
-  }],
-
-  Date: [function({obj, hooks}, grip) {
-    let time = DevToolsUtils.callPropertyOnObject(obj, "getTime");
-    if (typeof time != "number") {
-      return false;
-    }
-
-    grip.preview = {
-      timestamp: hooks.createValueGrip(time),
-    };
-    return true;
-  }],
-
-  Array: [function({obj, hooks}, grip) {
-    let length = getArrayLength(obj);
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: length,
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let raw = obj.unsafeDereference();
-    let items = grip.preview.items = [];
-
-    for (let i = 0; i < length; ++i) {
-      // Array Xrays filter out various possibly-unsafe properties (like
-      // functions, and claim that the value is undefined instead. This
-      // is generally the right thing for privileged code accessing untrusted
-      // objects, but quite confusing for Object previews. So we manually
-      // override this protection by waiving Xrays on the array, and re-applying
-      // Xrays on any indexed value props that we pull off of it.
-      let desc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(raw), i);
-      if (desc && !desc.get && !desc.set) {
-        let value = Cu.unwaiveXrays(desc.value);
-        value = makeDebuggeeValueIfNeeded(obj, value);
-        items.push(hooks.createValueGrip(value));
-      } else {
-        items.push(null);
-      }
-
-      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  Set: [function(objectActor, grip) {
-    let size = DevToolsUtils.getProperty(objectActor.obj, "size");
-    if (typeof size != "number") {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: size,
-    };
-
-    // Avoid recursive object grips.
-    if (objectActor.hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let items = grip.preview.items = [];
-    for (let item of enumSetEntries(objectActor)) {
-      items.push(item);
-      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  WeakSet: [function(objectActor, grip) {
-    let enumEntries = enumWeakSetEntries(objectActor);
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: enumEntries.size
-    };
-
-    // Avoid recursive object grips.
-    if (objectActor.hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let items = grip.preview.items = [];
-    for (let item of enumEntries) {
-      items.push(item);
-      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  Map: [function(objectActor, grip) {
-    let size = DevToolsUtils.getProperty(objectActor.obj, "size");
-    if (typeof size != "number") {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "MapLike",
-      size: size,
-    };
-
-    if (objectActor.hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let entries = grip.preview.entries = [];
-    for (let entry of enumMapEntries(objectActor)) {
-      entries.push(entry);
-      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  WeakMap: [function(objectActor, grip) {
-    let enumEntries = enumWeakMapEntries(objectActor);
-
-    grip.preview = {
-      kind: "MapLike",
-      size: enumEntries.size
-    };
-
-    if (objectActor.hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let entries = grip.preview.entries = [];
-    for (let entry of enumEntries) {
-      entries.push(entry);
-      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  DOMStringMap: [function({obj, hooks}, grip, rawObj) {
-    if (!rawObj) {
-      return false;
-    }
-
-    let keys = obj.getOwnPropertyNames();
-    grip.preview = {
-      kind: "MapLike",
-      size: keys.length,
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let entries = grip.preview.entries = [];
-    for (let key of keys) {
-      let value = makeDebuggeeValueIfNeeded(obj, rawObj[key]);
-      entries.push([key, hooks.createValueGrip(value)]);
-      if (entries.length == OBJECT_PREVIEW_MAX_ITEMS) {
-        break;
-      }
-    }
-
-    return true;
-  }],
-
-  Proxy: [function({obj, hooks}, grip, rawObj) {
-    // The `isProxy` getter of the debuggee object only detects proxies without
-    // security wrappers. If false, the target and handler are not available.
-    let hasTargetAndHandler = obj.isProxy;
-    if (hasTargetAndHandler) {
-      grip.proxyTarget = hooks.createValueGrip(obj.proxyTarget);
-      grip.proxyHandler = hooks.createValueGrip(obj.proxyHandler);
-    }
-
-    grip.preview = {
-      kind: "Object",
-      ownProperties: Object.create(null),
-      ownPropertiesLength: 2 * hasTargetAndHandler
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    if (hasTargetAndHandler) {
-      grip.preview.ownProperties["<target>"] = {value: grip.proxyTarget};
-      grip.preview.ownProperties["<handler>"] = {value: grip.proxyHandler};
-    }
-
-    return true;
-  }],
-};
-
-/**
- * Generic previewer for classes wrapping primitives, like String,
- * Number and Boolean.
- *
- * @param string className
- *        Class name to expect.
- * @param object classObj
- *        The class to expect, eg. String. The valueOf() method of the class is
- *        invoked on the given object.
- * @param ObjectActor objectActor
- *        The object actor
- * @param Object grip
- *        The result grip to fill in
- * @return Booolean true if the object was handled, false otherwise
- */
-function wrappedPrimitivePreviewer(className, classObj, objectActor, grip, rawObj) {
-  let {obj, hooks} = objectActor;
-
-  let v = null;
-  try {
-    v = classObj.prototype.valueOf.call(rawObj);
-  } catch (ex) {
-    // valueOf() can throw if the raw JS object is "misbehaved".
-    return false;
-  }
-
-  if (v === null) {
-    return false;
-  }
-
-  let canHandle = GenericObject(objectActor, grip, rawObj, className === "String");
-  if (!canHandle) {
-    return false;
-  }
-
-  grip.preview.wrappedValue =
-    hooks.createValueGrip(makeDebuggeeValueIfNeeded(obj, v));
-  return true;
-}
-
-function GenericObject(objectActor, grip, rawObj, specialStringBehavior = false) {
-  let {obj, hooks} = objectActor;
-  if (grip.preview || grip.displayString || hooks.getGripDepth() > 1) {
-    return false;
-  }
-
-  let i = 0, names = [], symbols = [];
-  let preview = grip.preview = {
-    kind: "Object",
-    ownProperties: Object.create(null),
-    ownSymbols: [],
-  };
-
-  try {
-    names = obj.getOwnPropertyNames();
-    symbols = obj.getOwnPropertySymbols();
-  } catch (ex) {
-    // Calling getOwnPropertyNames() on some wrapped native prototypes is not
-    // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
-  }
-  preview.ownPropertiesLength = names.length;
-  preview.ownSymbolsLength = symbols.length;
-
-  let length;
-  if (specialStringBehavior) {
-    length = DevToolsUtils.getProperty(obj, "length");
-    if (typeof length != "number") {
-      specialStringBehavior = false;
-    }
-  }
-
-  for (let name of names) {
-    if (specialStringBehavior && /^[0-9]+$/.test(name)) {
-      let num = parseInt(name, 10);
-      if (num.toString() === name && num >= 0 && num < length) {
-        continue;
-      }
-    }
-
-    let desc = objectActor._propertyDescriptor(name, true);
-    if (!desc) {
-      continue;
-    }
-
-    preview.ownProperties[name] = desc;
-    if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
-      break;
-    }
-  }
-
-  for (let symbol of symbols) {
-    let descriptor = objectActor._propertyDescriptor(symbol, true);
-    if (!descriptor) {
-      continue;
-    }
-
-    preview.ownSymbols.push(Object.assign({
-      descriptor
-    }, hooks.createValueGrip(symbol)));
-
-    if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
-      break;
-    }
-  }
-
-  if (i < OBJECT_PREVIEW_MAX_ITEMS) {
-    preview.safeGetterValues = objectActor._findSafeGetterValues(
-      Object.keys(preview.ownProperties),
-      OBJECT_PREVIEW_MAX_ITEMS - i);
-  }
-
-  return true;
-}
-
-// Preview functions that do not rely on the object class.
-DebuggerServer.ObjectActorPreviewers.Object = [
-  function TypedArray({obj, hooks}, grip) {
-    if (!isTypedArray(obj)) {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: getArrayLength(obj),
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let raw = obj.unsafeDereference();
-    let global = Cu.getGlobalForObject(DebuggerServer);
-    let classProto = global[obj.class].prototype;
-    // The Xray machinery for TypedArrays denies indexed access on the grounds
-    // that it's slow, and advises callers to do a structured clone instead.
-    let safeView = Cu.cloneInto(classProto.subarray.call(raw, 0,
-      OBJECT_PREVIEW_MAX_ITEMS), global);
-    let items = grip.preview.items = [];
-    for (let i = 0; i < safeView.length; i++) {
-      items.push(safeView[i]);
-    }
-
-    return true;
-  },
-
-  function Error({obj, hooks}, grip) {
-    switch (obj.class) {
-      case "Error":
-      case "EvalError":
-      case "RangeError":
-      case "ReferenceError":
-      case "SyntaxError":
-      case "TypeError":
-      case "URIError":
-        let name = DevToolsUtils.getProperty(obj, "name");
-        let msg = DevToolsUtils.getProperty(obj, "message");
-        let stack = DevToolsUtils.getProperty(obj, "stack");
-        let fileName = DevToolsUtils.getProperty(obj, "fileName");
-        let lineNumber = DevToolsUtils.getProperty(obj, "lineNumber");
-        let columnNumber = DevToolsUtils.getProperty(obj, "columnNumber");
-        grip.preview = {
-          kind: "Error",
-          name: hooks.createValueGrip(name),
-          message: hooks.createValueGrip(msg),
-          stack: hooks.createValueGrip(stack),
-          fileName: hooks.createValueGrip(fileName),
-          lineNumber: hooks.createValueGrip(lineNumber),
-          columnNumber: hooks.createValueGrip(columnNumber),
-        };
-        return true;
-      default:
-        return false;
-    }
-  },
-
-  function CSSMediaRule({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || obj.class != "CSSMediaRule") {
-      return false;
-    }
-    grip.preview = {
-      kind: "ObjectWithText",
-      text: hooks.createValueGrip(rawObj.conditionText),
-    };
-    return true;
-  },
-
-  function CSSStyleRule({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || obj.class != "CSSStyleRule") {
-      return false;
-    }
-    grip.preview = {
-      kind: "ObjectWithText",
-      text: hooks.createValueGrip(rawObj.selectorText),
-    };
-    return true;
-  },
-
-  function ObjectWithURL({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || !(obj.class == "CSSImportRule" ||
-                                 obj.class == "CSSStyleSheet" ||
-                                 obj.class == "Location" ||
-                                 rawObj instanceof Ci.nsIDOMWindow)) {
-      return false;
-    }
-
-    let url;
-    if (rawObj instanceof Ci.nsIDOMWindow && rawObj.location) {
-      url = rawObj.location.href;
-    } else if (rawObj.href) {
-      url = rawObj.href;
-    } else {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ObjectWithURL",
-      url: hooks.createValueGrip(url),
-    };
-
-    return true;
-  },
-
-  function ArrayLike({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj ||
-        obj.class != "DOMStringList" &&
-        obj.class != "DOMTokenList" &&
-        obj.class != "CSSRuleList" &&
-        obj.class != "MediaList" &&
-        obj.class != "StyleSheetList" &&
-        obj.class != "CSSValueList" &&
-        obj.class != "NamedNodeMap" &&
-        obj.class != "FileList" &&
-        obj.class != "NodeList") {
-      return false;
-    }
-
-    if (typeof rawObj.length != "number") {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: rawObj.length,
-    };
-
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let items = grip.preview.items = [];
-
-    for (let i = 0; i < rawObj.length &&
-                    items.length < OBJECT_PREVIEW_MAX_ITEMS; i++) {
-      let value = makeDebuggeeValueIfNeeded(obj, rawObj[i]);
-      items.push(hooks.createValueGrip(value));
-    }
-
-    return true;
-  },
-
-  function CSSStyleDeclaration({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj ||
-        (obj.class != "CSSStyleDeclaration" &&
-         obj.class != "CSS2Properties")) {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "MapLike",
-      size: rawObj.length,
-    };
-
-    let entries = grip.preview.entries = [];
-
-    for (let i = 0; i < OBJECT_PREVIEW_MAX_ITEMS &&
-                    i < rawObj.length; i++) {
-      let prop = rawObj[i];
-      let value = rawObj.getPropertyValue(prop);
-      entries.push([prop, hooks.createValueGrip(value)]);
-    }
-
-    return true;
-  },
-
-  function DOMNode({obj, hooks}, grip, rawObj) {
-    if (isWorker || obj.class == "Object" || !rawObj ||
-        !(rawObj instanceof Ci.nsIDOMNode)) {
-      return false;
-    }
-
-    let preview = grip.preview = {
-      kind: "DOMNode",
-      nodeType: rawObj.nodeType,
-      nodeName: rawObj.nodeName,
-      isConnected: rawObj.isConnected === true,
-    };
-
-    if (rawObj instanceof Ci.nsIDOMDocument && rawObj.location) {
-      preview.location = hooks.createValueGrip(rawObj.location.href);
-    } else if (rawObj instanceof Ci.nsIDOMDocumentFragment) {
-      preview.childNodesLength = rawObj.childNodes.length;
-
-      if (hooks.getGripDepth() < 2) {
-        preview.childNodes = [];
-        for (let node of rawObj.childNodes) {
-          let actor = hooks.createValueGrip(obj.makeDebuggeeValue(node));
-          preview.childNodes.push(actor);
-          if (preview.childNodes.length == OBJECT_PREVIEW_MAX_ITEMS) {
-            break;
-          }
-        }
-      }
-    } else if (rawObj instanceof Ci.nsIDOMElement) {
-      // For HTML elements (in an HTML document, at least), the nodeName is an
-      // uppercased version of the actual element name.  Check for HTML
-      // elements, that is elements in the HTML namespace, and lowercase the
-      // nodeName in that case.
-      if (rawObj.namespaceURI == "http://www.w3.org/1999/xhtml") {
-        preview.nodeName = preview.nodeName.toLowerCase();
-      }
-
-      // Add preview for DOM element attributes.
-      preview.attributes = {};
-      preview.attributesLength = rawObj.attributes.length;
-      for (let attr of rawObj.attributes) {
-        preview.attributes[attr.nodeName] = hooks.createValueGrip(attr.value);
-      }
-    } else if (obj.class == "Attr") {
-      preview.value = hooks.createValueGrip(rawObj.value);
-    } else if (obj.class == "Text" ||
-               obj.class == "CDATASection" ||
-               obj.class == "Comment") {
-      preview.textContent = hooks.createValueGrip(rawObj.textContent);
-    }
-
-    return true;
-  },
-
-  function DOMEvent({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMEvent)) {
-      return false;
-    }
-
-    let preview = grip.preview = {
-      kind: "DOMEvent",
-      type: rawObj.type,
-      properties: Object.create(null),
-    };
-
-    if (hooks.getGripDepth() < 2) {
-      let target = obj.makeDebuggeeValue(rawObj.target);
-      preview.target = hooks.createValueGrip(target);
-    }
-
-    let props = [];
-    if (obj.class == "MouseEvent" ||
-        obj.class == "DragEvent" ||
-        obj.class == "PointerEvent" ||
-        obj.class == "SimpleGestureEvent" ||
-        obj.class == "WheelEvent") {
-      props.push("buttons", "clientX", "clientY", "layerX", "layerY");
-    } else if (obj.class == "KeyboardEvent") {
-      let modifiers = [];
-      if (rawObj.altKey) {
-        modifiers.push("Alt");
-      }
-      if (rawObj.ctrlKey) {
-        modifiers.push("Control");
-      }
-      if (rawObj.metaKey) {
-        modifiers.push("Meta");
-      }
-      if (rawObj.shiftKey) {
-        modifiers.push("Shift");
-      }
-      preview.eventKind = "key";
-      preview.modifiers = modifiers;
-
-      props.push("key", "charCode", "keyCode");
-    } else if (obj.class == "TransitionEvent") {
-      props.push("propertyName", "pseudoElement");
-    } else if (obj.class == "AnimationEvent") {
-      props.push("animationName", "pseudoElement");
-    } else if (obj.class == "ClipboardEvent") {
-      props.push("clipboardData");
-    }
-
-    // Add event-specific properties.
-    for (let prop of props) {
-      let value = rawObj[prop];
-      if (value && (typeof value == "object" || typeof value == "function")) {
-        // Skip properties pointing to objects.
-        if (hooks.getGripDepth() > 1) {
-          continue;
-        }
-        value = obj.makeDebuggeeValue(value);
-      }
-      preview.properties[prop] = hooks.createValueGrip(value);
-    }
-
-    // Add any properties we find on the event object.
-    if (!props.length) {
-      let i = 0;
-      for (let prop in rawObj) {
-        let value = rawObj[prop];
-        if (prop == "target" || prop == "type" || value === null ||
-            typeof value == "function") {
-          continue;
-        }
-        if (value && typeof value == "object") {
-          if (hooks.getGripDepth() > 1) {
-            continue;
-          }
-          value = obj.makeDebuggeeValue(value);
-        }
-        preview.properties[prop] = hooks.createValueGrip(value);
-        if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
-          break;
-        }
-      }
-    }
-
-    return true;
-  },
-
-  function DOMException({obj, hooks}, grip, rawObj) {
-    if (isWorker || !rawObj || !(rawObj instanceof Ci.nsIDOMDOMException)) {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "DOMException",
-      name: hooks.createValueGrip(rawObj.name),
-      message: hooks.createValueGrip(rawObj.message),
-      code: hooks.createValueGrip(rawObj.code),
-      result: hooks.createValueGrip(rawObj.result),
-      filename: hooks.createValueGrip(rawObj.filename),
-      lineNumber: hooks.createValueGrip(rawObj.lineNumber),
-      columnNumber: hooks.createValueGrip(rawObj.columnNumber),
-    };
-
-    return true;
-  },
-
-  function PseudoArray({obj, hooks}, grip, rawObj) {
-    // An object is considered a pseudo-array if all the following apply:
-    // - All its properties are array indices except, optionally, a "length" property.
-    // - At least it has the "0" array index.
-    // - The array indices are consecutive.
-    // - The value of "length", if present, is the number of array indices.
-
-    let keys;
-    try {
-      keys = obj.getOwnPropertyNames();
-    } catch (err) {
-      // The above can throw when the debuggee does not subsume the object's
-      // compartment, or for some WrappedNatives like Cu.Sandbox.
-      return false;
-    }
-    let {length} = keys;
-    if (length === 0) {
-      return false;
-    }
-
-    // Array indices should be sorted at the beginning, from smallest to largest.
-    // Other properties should be at the end, so check if the last one is "length".
-    if (keys[length - 1] === "length") {
-      --length;
-      if (length === 0 || length !== DevToolsUtils.getProperty(obj, "length")) {
-        return false;
-      }
-    }
-
-    // Check that the last key is the array index expected at that position.
-    let lastKey = keys[length - 1];
-    if (!isArrayIndex(lastKey) || +lastKey !== length - 1) {
-      return false;
-    }
-
-    grip.preview = {
-      kind: "ArrayLike",
-      length: length,
-    };
-
-    // Avoid recursive object grips.
-    if (hooks.getGripDepth() > 1) {
-      return true;
-    }
-
-    let items = grip.preview.items = [];
-    let numItems = Math.min(OBJECT_PREVIEW_MAX_ITEMS, length);
-
-    for (let i = 0; i < numItems; ++i) {
-      let desc = obj.getOwnPropertyDescriptor(i);
-      if (desc && "value" in desc) {
-        items.push(hooks.createValueGrip(desc.value));
-      } else {
-        items.push(null);
-      }
-    }
-
-    return true;
-  },
-
-  function Object(objectActor, grip, rawObj) {
-    return GenericObject(objectActor, grip, rawObj, /* specialStringBehavior = */ false);
-  },
-];
-
-/**
- * Get thisDebugger.Object referent's `promiseState`.
- *
- * @returns Object
- *          An object of one of the following forms:
- *          - { state: "pending" }
- *          - { state: "fulfilled", value }
- *          - { state: "rejected", reason }
- */
-function getPromiseState(obj) {
-  if (obj.class != "Promise") {
-    throw new Error(
-      "Can't call `getPromiseState` on `Debugger.Object`s that don't " +
-      "refer to Promise objects.");
-  }
-
-  let state = { state: obj.promiseState };
-  if (state.state === "fulfilled") {
-    state.value = obj.promiseValue;
-  } else if (state.state === "rejected") {
-    state.reason = obj.promiseReason;
-  }
-  return state;
-}
-
-/**
- * Determine if a given value is non-primitive.
- *
- * @param Any value
- *        The value to test.
- * @return Boolean
- *         Whether the value is non-primitive.
- */
-function isObject(value) {
-  const type = typeof value;
-  return type == "object" ? value !== null : type == "function";
-}
-
-/**
- * Create a function that can safely stringify Debugger.Objects of a given
- * builtin type.
- *
- * @param Function ctor
- *        The builtin class constructor.
- * @return Function
- *         The stringifier for the class.
- */
-function createBuiltinStringifier(ctor) {
-  return obj => {
-    try {
-      return ctor.prototype.toString.call(obj.unsafeDereference());
-    } catch (err) {
-      // The debuggee will see a "Function" class if the object is callable and
-      // its compartment is not subsumed. The above will throw if it's not really
-      // a function, e.g. if it's a callable proxy.
-      return "[object " + obj.class + "]";
-    }
-  };
-}
-
-/**
- * Stringify a Debugger.Object-wrapped Error instance.
- *
- * @param Debugger.Object obj
- *        The object to stringify.
- * @return String
- *         The stringification of the object.
- */
-function errorStringify(obj) {
-  let name = DevToolsUtils.getProperty(obj, "name");
-  if (name === "" || name === undefined) {
-    name = obj.class;
-  } else if (isObject(name)) {
-    name = stringify(name);
-  }
-
-  let message = DevToolsUtils.getProperty(obj, "message");
-  if (isObject(message)) {
-    message = stringify(message);
-  }
-
-  if (message === "" || message === undefined) {
-    return name;
-  }
-  return name + ": " + message;
-}
-
-/**
- * Stringify a Debugger.Object based on its class.
- *
- * @param Debugger.Object obj
- *        The object to stringify.
- * @return String
- *         The stringification for the object.
- */
-function stringify(obj) {
-  if (!DevToolsUtils.isSafeDebuggerObject(obj)) {
-    if (DevToolsUtils.isCPOW(obj)) {
-      return "<cpow>";
-    }
-    let unwrapped = DevToolsUtils.unwrap(obj);
-    if (unwrapped === undefined) {
-      return "<invisibleToDebugger>";
-    } else if (unwrapped.isProxy) {
-      return "<proxy>";
-    }
-    // The following line should not be reached. It's there just in case somebody
-    // modifies isSafeDebuggerObject to return false for additional kinds of objects.
-    return "[object " + obj.class + "]";
-  } else if (obj.class == "DeadObject") {
-    return "<dead object>";
-  }
-
-  const stringifier = stringifiers[obj.class] || stringifiers.Object;
-
-  try {
-    return stringifier(obj);
-  } catch (e) {
-    DevToolsUtils.reportException("stringify", e);
-    return "<failed to stringify object>";
-  }
-}
-
-// Used to prevent infinite recursion when an array is found inside itself.
-var seen = null;
-
-var stringifiers = {
-  Error: errorStringify,
-  EvalError: errorStringify,
-  RangeError: errorStringify,
-  ReferenceError: errorStringify,
-  SyntaxError: errorStringify,
-  TypeError: errorStringify,
-  URIError: errorStringify,
-  Boolean: createBuiltinStringifier(Boolean),
-  Function: createBuiltinStringifier(Function),
-  Number: createBuiltinStringifier(Number),
-  RegExp: createBuiltinStringifier(RegExp),
-  String: createBuiltinStringifier(String),
-  Object: obj => "[object " + obj.class + "]",
-  Array: obj => {
-    // If we're at the top level then we need to create the Set for tracking
-    // previously stringified arrays.
-    const topLevel = !seen;
-    if (topLevel) {
-      seen = new Set();
-    } else if (seen.has(obj)) {
-      return "";
-    }
-
-    seen.add(obj);
-
-    const len = getArrayLength(obj);
-    let string = "";
-
-    // Array.length is always a non-negative safe integer.
-    for (let i = 0; i < len; i++) {
-      const desc = obj.getOwnPropertyDescriptor(i);
-      if (desc) {
-        const { value } = desc;
-        if (value != null) {
-          string += isObject(value) ? stringify(value) : value;
-        }
-      }
-
-      if (i < len - 1) {
-        string += ",";
-      }
-    }
-
-    if (topLevel) {
-      seen = null;
-    }
-
-    return string;
-  },
-  DOMException: obj => {
-    const message = DevToolsUtils.getProperty(obj, "message") || "<no message>";
-    const result = (+DevToolsUtils.getProperty(obj, "result")).toString(16);
-    const code = DevToolsUtils.getProperty(obj, "code");
-    const name = DevToolsUtils.getProperty(obj, "name") || "<unknown>";
-
-    return '[Exception... "' + message + '" ' +
-           'code: "' + code + '" ' +
-           'nsresult: "0x' + result + " (" + name + ')"]';
-  },
-  Promise: obj => {
-    const { state, value, reason } = getPromiseState(obj);
-    let statePreview = state;
-    if (state != "pending") {
-      const settledValue = state === "fulfilled" ? value : reason;
-      statePreview += ": " + (typeof settledValue === "object" && settledValue !== null
-                                ? stringify(settledValue)
-                                : settledValue);
-    }
-    return "Promise (" + statePreview + ")";
-  },
-};
-
-/**
- * Make a debuggee value for the given object, if needed. Primitive values
- * are left the same.
- *
- * Use case: you have a raw JS object (after unsafe dereference) and you want to
- * send it to the client. In that case you need to use an ObjectActor which
- * requires a debuggee value. The Debugger.Object.prototype.makeDebuggeeValue()
- * method works only for JS objects and functions.
- *
- * @param Debugger.Object obj
- * @param any value
- * @return object
- */
-function makeDebuggeeValueIfNeeded(obj, value) {
-  if (value && (typeof value == "object" || typeof value == "function")) {
-    return obj.makeDebuggeeValue(value);
-  }
-  return value;
-}
 
 /**
  * Creates an actor for the specified "very long" string. "Very long" is specified
  * at the server's discretion.
+ * There is a newer, protocol.js based LongString actor in
+ * devtools/server/actors/string.js and if you can you should use this one.
  *
  * @param string String
  *        The string.
  */
 function LongStringActor(string) {
   this.string = string;
   this.stringLength = string.length;
 }
@@ -2340,203 +82,16 @@ LongStringActor.prototype = {
 };
 
 LongStringActor.prototype.requestTypes = {
   "substring": LongStringActor.prototype.onSubstring,
   "release": LongStringActor.prototype.onRelease
 };
 
 /**
- * Creates an actor for the specified symbol.
- *
- * @param symbol Symbol
- *        The symbol.
- */
-function SymbolActor(symbol) {
-  this.symbol = symbol;
-}
-
-SymbolActor.prototype = {
-  actorPrefix: "symbol",
-
-  rawValue: function() {
-    return this.symbol;
-  },
-
-  destroy: function() {
-    // Because symbolActors is not a weak map, we won't automatically leave
-    // it so we need to manually leave on destroy so that we don't leak
-    // memory.
-    this._releaseActor();
-  },
-
-  /**
-   * Returns a grip for this actor for returning in a protocol message.
-   */
-  grip: function() {
-    let form = {
-      type: "symbol",
-      actor: this.actorID,
-    };
-    let name = getSymbolName(this.symbol);
-    if (name !== undefined) {
-      // Create a grip for the name because it might be a longString.
-      form.name = createValueGrip(name, this.registeredPool);
-    }
-    return form;
-  },
-
-  /**
-   * Handle a request to release this SymbolActor instance.
-   */
-  onRelease: function() {
-    // TODO: also check if registeredPool === threadActor.threadLifetimePool
-    // when the web console moves away from manually releasing pause-scoped
-    // actors.
-    this._releaseActor();
-    this.registeredPool.removeActor(this);
-    return {};
-  },
-
-  _releaseActor: function() {
-    if (this.registeredPool && this.registeredPool.symbolActors) {
-      delete this.registeredPool.symbolActors[this.symbol];
-    }
-  }
-};
-
-SymbolActor.prototype.requestTypes = {
-  "release": SymbolActor.prototype.onRelease
-};
-
-/**
- * Creates an actor for the specified ArrayBuffer.
- *
- * @param buffer ArrayBuffer
- *        The buffer.
- */
-function ArrayBufferActor(buffer) {
-  this.buffer = buffer;
-  this.bufferLength = buffer.byteLength;
-}
-
-ArrayBufferActor.prototype = {
-  actorPrefix: "arrayBuffer",
-
-  rawValue: function() {
-    return this.buffer;
-  },
-
-  destroy: function() {
-  },
-
-  grip() {
-    return {
-      "type": "arrayBuffer",
-      "length": this.bufferLength,
-      "actor": this.actorID
-    };
-  },
-
-  onSlice({start, count}) {
-    let slice = new Uint8Array(this.buffer, start, count);
-    let parts = [], offset = 0;
-    const PortionSize = 0x6000; // keep it divisible by 3 for btoa() and join()
-    while (offset + PortionSize < count) {
-      parts.push(btoa(
-        String.fromCharCode.apply(null, slice.subarray(offset, offset + PortionSize))));
-      offset += PortionSize;
-    }
-    parts.push(btoa(String.fromCharCode.apply(null, slice.subarray(offset, count))));
-    return {
-      "from": this.actorID,
-      "encoded": parts.join(""),
-    };
-  }
-};
-
-ArrayBufferActor.prototype.requestTypes = {
-  "slice": ArrayBufferActor.prototype.onSlice,
-};
-
-/**
- * Create a grip for the given debuggee value.  If the value is an
- * object, will create an actor with the given lifetime.
- */
-function createValueGrip(value, pool, makeObjectGrip) {
-  switch (typeof value) {
-    case "boolean":
-      return value;
-
-    case "string":
-      if (stringIsLong(value)) {
-        return longStringGrip(value, pool);
-      }
-      return value;
-
-    case "number":
-      if (value === Infinity) {
-        return { type: "Infinity" };
-      } else if (value === -Infinity) {
-        return { type: "-Infinity" };
-      } else if (Number.isNaN(value)) {
-        return { type: "NaN" };
-      } else if (!value && 1 / value === -Infinity) {
-        return { type: "-0" };
-      }
-      return value;
-
-    case "undefined":
-      return { type: "undefined" };
-
-    case "object":
-      if (value === null) {
-        return { type: "null" };
-      } else if (value.optimizedOut ||
-             value.uninitialized ||
-             value.missingArguments) {
-        // The slot is optimized out, an uninitialized binding, or
-        // arguments on a dead scope
-        return {
-          type: "null",
-          optimizedOut: value.optimizedOut,
-          uninitialized: value.uninitialized,
-          missingArguments: value.missingArguments
-        };
-      }
-      return makeObjectGrip(value, pool);
-
-    case "symbol":
-      return symbolGrip(value, pool);
-
-    default:
-      assert(false, "Failed to provide a grip for: " + value);
-      return null;
-  }
-}
-
-const symbolProtoToString = Symbol.prototype.toString;
-
-function getSymbolName(symbol) {
-  const name = symbolProtoToString.call(symbol).slice("Symbol(".length, -1);
-  return name || undefined;
-}
-
-/**
- * Returns true if the string is long enough to use a LongStringActor instead
- * of passing the value directly over the protocol.
- *
- * @param str String
- *        The string we are checking the length of.
- */
-function stringIsLong(str) {
-  return str.length >= DebuggerServer.LONG_STRING_LENGTH;
-}
-
-/**
  * Create a grip for the given string.
  *
  * @param str String
  *        The string we are creating a grip for.
  * @param pool ActorPool
  *        The actor pool where the new actor will be added.
  */
 function longStringGrip(str, pool) {
@@ -2549,141 +104,12 @@ function longStringGrip(str, pool) {
   }
 
   let actor = new LongStringActor(str);
   pool.addActor(actor);
   pool.longStringActors[str] = actor;
   return actor.grip();
 }
 
-/**
- * Create a grip for the given symbol.
- *
- * @param sym Symbol
- *        The symbol we are creating a grip for.
- * @param pool ActorPool
- *        The actor pool where the new actor will be added.
- */
-function symbolGrip(sym, pool) {
-  if (!pool.symbolActors) {
-    pool.symbolActors = Object.create(null);
-  }
-
-  if (sym in pool.symbolActors) {
-    return pool.symbolActors[sym].grip();
-  }
-
-  let actor = new SymbolActor(sym);
-  pool.addActor(actor);
-  pool.symbolActors[sym] = actor;
-  return actor.grip();
-}
-
-/**
- * Create a grip for the given ArrayBuffer.
- *
- * @param buffer ArrayBuffer
- *        The ArrayBuffer we are creating a grip for.
- * @param pool ActorPool
- *        The actor pool where the new actor will be added.
- */
-function arrayBufferGrip(buffer, pool) {
-  if (!pool.arrayBufferActors) {
-    pool.arrayBufferActors = new WeakMap();
-  }
-
-  if (pool.arrayBufferActors.has(buffer)) {
-    return pool.arrayBufferActors.get(buffer).grip();
-  }
-
-  let actor = new ArrayBufferActor(buffer);
-  pool.addActor(actor);
-  pool.arrayBufferActors.set(buffer, actor);
-  return actor.grip();
-}
-
-const TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
-                             "Uint32Array", "Int8Array", "Int16Array", "Int32Array",
-                             "Float32Array", "Float64Array"];
-
-/**
- * Returns true if a debuggee object is a typed array.
- *
- * @param obj Debugger.Object
- *        The debuggee object to test.
- * @return Boolean
- */
-function isTypedArray(object) {
-  return TYPED_ARRAY_CLASSES.includes(object.class);
-}
-
-/**
- * Returns true if a debuggee object is an array, including a typed array.
- *
- * @param obj Debugger.Object
- *        The debuggee object to test.
- * @return Boolean
- */
-function isArray(object) {
-  return isTypedArray(object) || object.class === "Array";
-}
-
-/**
- * Returns the length of an array (or typed array).
- *
- * @param obj Debugger.Object
- *        The debuggee object of the array.
- * @return Number
- * @throws if the object is not an array.
- */
-function getArrayLength(object) {
-  if (!isArray(object)) {
-    throw new Error("Expected an array, got a " + object.class);
-  }
-
-  // Real arrays have a reliable `length` own property.
-  if (object.class === "Array") {
-    return DevToolsUtils.getProperty(object, "length");
-  }
-
-  // For typed arrays, `DevToolsUtils.getProperty` is not reliable because the `length`
-  // getter could be shadowed by an own property, and `getOwnPropertyNames` is
-  // unnecessarily slow. Obtain the `length` getter safely and call it manually.
-  let typedProto = Object.getPrototypeOf(Uint8Array.prototype);
-  let getter = Object.getOwnPropertyDescriptor(typedProto, "length").get;
-  return getter.call(object.unsafeDereference());
-}
-
-/**
- * Returns true if the parameter can be stored as a 32-bit unsigned integer.
- * If so, it will be suitable for use as the length of an array object.
- *
- * @param num Number
- *        The number to test.
- * @return Boolean
- */
-function isUint32(num) {
-  return num >>> 0 === num;
-}
-
-/**
- * Returns true if the parameter is suitable to be an array index.
- *
- * @param str String
- * @return Boolean
- */
-function isArrayIndex(str) {
-  // Transform the parameter to a 32-bit unsigned integer.
-  let num = str >>> 0;
-  // Check that the parameter is a canonical Uint32 index.
-  return num + "" === str &&
-    // Array indices cannot attain the maximum Uint32 value.
-    num != -1 >>> 0;
-}
-
-exports.ObjectActor = ObjectActor;
-exports.PropertyIteratorActor = PropertyIteratorActor;
-exports.LongStringActor = LongStringActor;
-exports.SymbolActor = SymbolActor;
-exports.createValueGrip = createValueGrip;
-exports.stringIsLong = stringIsLong;
-exports.longStringGrip = longStringGrip;
-exports.arrayBufferGrip = arrayBufferGrip;
+module.exports = {
+  LongStringActor,
+  longStringGrip,
+};
new file mode 100644
--- /dev/null
+++ b/devtools/server/actors/object/moz.build
@@ -0,0 +1,13 @@
+# 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(
+    'long-string.js',
+    'previewers.js',
+    'property-iterator.js',
+    'stringifiers.js',
+    'symbol-iterator.js',
+    'symbol.js',
+    'utils.js',
+)
copy from devtools/server/actors/object.js
copy to devtools/server/actors/object/previewers.js
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object/previewers.js
@@ -2,1275 +2,42 @@
 /* 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 { Cu, Ci } = require("chrome");
-const { GeneratedLocation } = require("devtools/server/actors/common");
 const { DebuggerServer } = require("devtools/server/main");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const { assert } = DevToolsUtils;
-
-loader.lazyRequireGetter(this, "ChromeUtils");
+loader.lazyRequireGetter(this, "ObjectUtils", "devtools/server/actors/object/utils");
+loader.lazyRequireGetter(this, "PropertyIterators", "devtools/server/actors/object/property-iterator");
 
 // Number of items to preview in objects, arrays, maps, sets, lists,
 // collections, etc.
 const OBJECT_PREVIEW_MAX_ITEMS = 10;
 
 /**
- * Creates an actor for the specified object.
- *
- * @param obj Debugger.Object
- *        The debuggee object.
- * @param hooks Object
- *        A collection of abstract methods that are implemented by the caller.
- *        ObjectActor requires the following functions to be implemented by
- *        the caller:
- *          - createValueGrip
- *              Creates a value grip for the given object
- *          - sources
- *              TabSources getter that manages the sources of a thread
- *          - createEnvironmentActor
- *              Creates and return an environment actor
- *          - getGripDepth
- *              An actor's grip depth getter
- *          - incrementGripDepth
- *              Increment the actor's grip depth
- *          - decrementGripDepth
- *              Decrement the actor's grip depth
- *          - globalDebugObject
- *              The Debuggee Global Object as given by the ThreadActor
- */
-function ObjectActor(obj, {
-  createValueGrip: createValueGripHook,
-  sources,
-  createEnvironmentActor,
-  getGripDepth,
-  incrementGripDepth,
-  decrementGripDepth,
-  getGlobalDebugObject
-}) {
-  assert(!obj.optimizedOut,
-         "Should not create object actors for optimized out values!");
-  this.obj = obj;
-  this.hooks = {
-    createValueGrip: createValueGripHook,
-    sources,
-    createEnvironmentActor,
-    getGripDepth,
-    incrementGripDepth,
-    decrementGripDepth,
-    getGlobalDebugObject
-  };
-  this.iterators = new Set();
-}
-
-ObjectActor.prototype = {
-  actorPrefix: "obj",
-
-  rawValue: function() {
-    return this.obj.unsafeDereference();
-  },
-
-  /**
-   * Returns a grip for this actor for returning in a protocol message.
-   */
-  grip: function() {
-    let g = {
-      "type": "object",
-      "actor": this.actorID,
-      "class": this.obj.class,
-    };
-
-    let unwrapped = DevToolsUtils.unwrap(this.obj);
-
-    // Unsafe objects must be treated carefully.
-    if (!DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      if (DevToolsUtils.isCPOW(this.obj)) {
-        // Cross-process object wrappers can't be accessed.
-        g.class = "CPOW: " + g.class;
-      } else if (unwrapped === undefined) {
-        // Objects belonging to an invisible-to-debugger compartment might be proxies,
-        // so just in case they shouldn't be accessed.
-        g.class = "InvisibleToDebugger: " + g.class;
-      } else if (unwrapped.isProxy) {
-        // Proxy objects can run traps when accessed, so just create a preview with
-        // the target and the handler.
-        g.class = "Proxy";
-        this.hooks.incrementGripDepth();
-        DebuggerServer.ObjectActorPreviewers.Proxy[0](this, g, null);
-        this.hooks.decrementGripDepth();
-      }
-      return g;
-    }
-
-    // If the debuggee does not subsume the object's compartment, most properties won't
-    // be accessible. Cross-orgin Window and Location objects might expose some, though.
-    // Change the displayed class, but when creating the preview use the original one.
-    if (unwrapped === null) {
-      g.class = "Restricted";
-    }
-
-    this.hooks.incrementGripDepth();
-
-    g.extensible = this.obj.isExtensible();
-    g.frozen = this.obj.isFrozen();
-    g.sealed = this.obj.isSealed();
-
-    if (g.class == "Promise") {
-      g.promiseState = this._createPromiseState();
-    }
-
-    // FF40+: Allow to know how many properties an object has to lazily display them
-    // when there is a bunch.
-    if (isTypedArray(g)) {
-      // Bug 1348761: getOwnPropertyNames is unnecessary slow on TypedArrays
-      g.ownPropertyLength = getArrayLength(this.obj);
-    } else {
-      try {
-        g.ownPropertyLength = this.obj.getOwnPropertyNames().length;
-      } catch (err) {
-        // The above can throw when the debuggee does not subsume the object's
-        // compartment, or for some WrappedNatives like Cu.Sandbox.
-      }
-    }
-
-    let raw = this.obj.unsafeDereference();
-
-    // If Cu is not defined, we are running on a worker thread, where xrays
-    // don't exist.
-    if (Cu) {
-      raw = Cu.unwaiveXrays(raw);
-    }
-
-    if (!DevToolsUtils.isSafeJSObject(raw)) {
-      raw = null;
-    }
-
-    let previewers = DebuggerServer.ObjectActorPreviewers[this.obj.class] ||
-                     DebuggerServer.ObjectActorPreviewers.Object;
-    for (let fn of previewers) {
-      try {
-        if (fn(this, g, raw)) {
-          break;
-        }
-      } catch (e) {
-        let msg = "ObjectActor.prototype.grip previewer function";
-        DevToolsUtils.reportException(msg, e);
-      }
-    }
-
-    this.hooks.decrementGripDepth();
-    return g;
-  },
-
-  /**
-   * Returns an object exposing the internal Promise state.
-   */
-  _createPromiseState: function() {
-    const { state, value, reason } = getPromiseState(this.obj);
-    let promiseState = { state };
-
-    if (state == "fulfilled") {
-      promiseState.value = this.hooks.createValueGrip(value);
-    } else if (state == "rejected") {
-      promiseState.reason = this.hooks.createValueGrip(reason);
-    }
-
-    promiseState.creationTimestamp = Date.now() - this.obj.promiseLifetime;
-
-    // Only add the timeToSettle property if the Promise isn't pending.
-    if (state !== "pending") {
-      promiseState.timeToSettle = this.obj.promiseTimeToResolution;
-    }
-
-    return promiseState;
-  },
-
-  /**
-   * Releases this actor from the pool.
-   */
-  release: function() {
-    if (this.registeredPool.objectActors) {
-      this.registeredPool.objectActors.delete(this.obj);
-    }
-    this.iterators.forEach(actor => this.registeredPool.removeActor(actor));
-    this.iterators.clear();
-    this.registeredPool.removeActor(this);
-  },
-
-  /**
-   * Handle a protocol request to provide the definition site of this function
-   * object.
-   */
-  onDefinitionSite: function() {
-    if (this.obj.class != "Function") {
-      return {
-        from: this.actorID,
-        error: "objectNotFunction",
-        message: this.actorID + " is not a function."
-      };
-    }
-
-    if (!this.obj.script) {
-      return {
-        from: this.actorID,
-        error: "noScript",
-        message: this.actorID + " has no Debugger.Script"
-      };
-    }
-
-    return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
-      this.hooks.sources().createNonSourceMappedActor(this.obj.script.source),
-      this.obj.script.startLine,
-      0 // TODO bug 901138: use Debugger.Script.prototype.startColumn
-    )).then((originalLocation) => {
-      return {
-        source: originalLocation.originalSourceActor.form(),
-        line: originalLocation.originalLine,
-        column: originalLocation.originalColumn
-      };
-    });
-  },
-
-  /**
-   * Handle a protocol request to provide the names of the properties defined on
-   * the object and not its prototype.
-   */
-  onOwnPropertyNames: function() {
-    let props = [];
-    if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      try {
-        props = this.obj.getOwnPropertyNames();
-      } catch (err) {
-        // The above can throw when the debuggee does not subsume the object's
-        // compartment, or for some WrappedNatives like Cu.Sandbox.
-      }
-    }
-    return { from: this.actorID, ownPropertyNames: props };
-  },
-
-  /**
-   * Creates an actor to iterate over an object property names and values.
-   * See PropertyIteratorActor constructor for more info about options param.
-   *
-   * @param request object
-   *        The protocol request object.
-   */
-  onEnumProperties: function(request) {
-    let actor = new PropertyIteratorActor(this, request.options);
-    this.registeredPool.addActor(actor);
-    this.iterators.add(actor);
-    return { iterator: actor.grip() };
-  },
-
-  /**
-   * Creates an actor to iterate over entries of a Map/Set-like object.
-   */
-  onEnumEntries: function() {
-    let actor = new PropertyIteratorActor(this, { enumEntries: true });
-    this.registeredPool.addActor(actor);
-    this.iterators.add(actor);
-    return { iterator: actor.grip() };
-  },
-
-  /**
-   * Creates an actor to iterate over an object symbols properties.
-   */
-  onEnumSymbols: function() {
-    let actor = new SymbolIteratorActor(this);
-    this.registeredPool.addActor(actor);
-    this.iterators.add(actor);
-    return { iterator: actor.grip() };
-  },
-
-  /**
-   * Handle a protocol request to provide the prototype and own properties of
-   * the object.
-   *
-   * @returns {Object} An object containing the data of this.obj, of the following form:
-   *          - {string} from: this.obj's actorID.
-   *          - {Object} prototype: The descriptor of this.obj's prototype.
-   *          - {Object} ownProperties: an object where the keys are the names of the
-   *                     this.obj's ownProperties, and the values the descriptors of
-   *                     the properties.
-   *          - {Array} ownSymbols: An array containing all descriptors of this.obj's
-   *                    ownSymbols. Here we have an array, and not an object like for
-   *                    ownProperties, because we can have multiple symbols with the same
-   *                    name in this.obj, e.g. `{[Symbol()]: "a", [Symbol()]: "b"}`.
-   *          - {Object} safeGetterValues: an object that maps this.obj's property names
-   *                     with safe getters descriptors.
-   */
-  onPrototypeAndProperties: function() {
-    let proto = null;
-    let names = [];
-    let symbols = [];
-    if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      try {
-        proto = this.obj.proto;
-        names = this.obj.getOwnPropertyNames();
-        symbols = this.obj.getOwnPropertySymbols();
-      } catch (err) {
-        // The above can throw when the debuggee does not subsume the object's
-        // compartment, or for some WrappedNatives like Cu.Sandbox.
-      }
-    }
-
-    let ownProperties = Object.create(null);
-    let ownSymbols = [];
-
-    for (let name of names) {
-      ownProperties[name] = this._propertyDescriptor(name);
-    }
-
-    for (let sym of symbols) {
-      ownSymbols.push({
-        name: sym.toString(),
-        descriptor: this._propertyDescriptor(sym)
-      });
-    }
-
-    return { from: this.actorID,
-             prototype: this.hooks.createValueGrip(proto),
-             ownProperties,
-             ownSymbols,
-             safeGetterValues: this._findSafeGetterValues(names) };
-  },
-
-  /**
-   * Find the safe getter values for the current Debugger.Object, |this.obj|.
-   *
-   * @private
-   * @param array ownProperties
-   *        The array that holds the list of known ownProperties names for
-   *        |this.obj|.
-   * @param number [limit=0]
-   *        Optional limit of getter values to find.
-   * @return object
-   *         An object that maps property names to safe getter descriptors as
-   *         defined by the remote debugging protocol.
-   */
-  _findSafeGetterValues: function(ownProperties, limit = 0) {
-    let safeGetterValues = Object.create(null);
-    let obj = this.obj;
-    let level = 0, i = 0;
-
-    // Do not search safe getters in unsafe objects.
-    if (!DevToolsUtils.isSafeDebuggerObject(obj)) {
-      return safeGetterValues;
-    }
-
-    // Most objects don't have any safe getters but inherit some from their
-    // prototype. Avoid calling getOwnPropertyNames on objects that may have
-    // many properties like Array, strings or js objects. That to avoid
-    // freezing firefox when doing so.
-    if (isArray(this.obj) || ["Object", "String"].includes(this.obj.class)) {
-      obj = obj.proto;
-      level++;
-    }
-
-    while (obj && DevToolsUtils.isSafeDebuggerObject(obj)) {
-      let getters = this._findSafeGetters(obj);
-      for (let name of getters) {
-        // Avoid overwriting properties from prototypes closer to this.obj. Also
-        // avoid providing safeGetterValues from prototypes if property |name|
-        // is already defined as an own property.
-        if (name in safeGetterValues ||
-            (obj != this.obj && ownProperties.includes(name))) {
-          continue;
-        }
-
-        // Ignore __proto__ on Object.prototye.
-        if (!obj.proto && name == "__proto__") {
-          continue;
-        }
-
-        let desc = null, getter = null;
-        try {
-          desc = obj.getOwnPropertyDescriptor(name);
-          getter = desc.get;
-        } catch (ex) {
-          // The above can throw if the cache becomes stale.
-        }
-        if (!getter) {
-          obj._safeGetters = null;
-          continue;
-        }
-
-        let result = getter.call(this.obj);
-        if (result && !("throw" in result)) {
-          let getterValue = undefined;
-          if ("return" in result) {
-            getterValue = result.return;
-          } else if ("yield" in result) {
-            getterValue = result.yield;
-          }
-          // WebIDL attributes specified with the LenientThis extended attribute
-          // return undefined and should be ignored.
-          if (getterValue !== undefined) {
-            safeGetterValues[name] = {
-              getterValue: this.hooks.createValueGrip(getterValue),
-              getterPrototypeLevel: level,
-              enumerable: desc.enumerable,
-              writable: level == 0 ? desc.writable : true,
-            };
-            if (limit && ++i == limit) {
-              break;
-            }
-          }
-        }
-      }
-      if (limit && i == limit) {
-        break;
-      }
-
-      obj = obj.proto;
-      level++;
-    }
-
-    return safeGetterValues;
-  },
-
-  /**
-   * Find the safe getters for a given Debugger.Object. Safe getters are native
-   * getters which are safe to execute.
-   *
-   * @private
-   * @param Debugger.Object object
-   *        The Debugger.Object where you want to find safe getters.
-   * @return Set
-   *         A Set of names of safe getters. This result is cached for each
-   *         Debugger.Object.
-   */
-  _findSafeGetters: function(object) {
-    if (object._safeGetters) {
-      return object._safeGetters;
-    }
-
-    let getters = new Set();
-
-    if (!DevToolsUtils.isSafeDebuggerObject(object)) {
-      object._safeGetters = getters;
-      return getters;
-    }
-
-    let names = [];
-    try {
-      names = object.getOwnPropertyNames();
-    } catch (ex) {
-      // Calling getOwnPropertyNames() on some wrapped native prototypes is not
-      // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
-    }
-
-    for (let name of names) {
-      let desc = null;
-      try {
-        desc = object.getOwnPropertyDescriptor(name);
-      } catch (e) {
-        // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
-        // allowed (bug 560072).
-      }
-      if (!desc || desc.value !== undefined || !("get" in desc)) {
-        continue;
-      }
-
-      if (DevToolsUtils.hasSafeGetter(desc)) {
-        getters.add(name);
-      }
-    }
-
-    object._safeGetters = getters;
-    return getters;
-  },
-
-  /**
-   * Handle a protocol request to provide the prototype of the object.
-   */
-  onPrototype: function() {
-    let proto = null;
-    if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      proto = this.obj.proto;
-    }
-    return { from: this.actorID,
-             prototype: this.hooks.createValueGrip(proto) };
-  },
-
-  /**
-   * Handle a protocol request to provide the property descriptor of the
-   * object's specified property.
-   *
-   * @param request object
-   *        The protocol request object.
-   */
-  onProperty: function(request) {
-    if (!request.name) {
-      return { error: "missingParameter",
-               message: "no property name was specified" };
-    }
-
-    return { from: this.actorID,
-             descriptor: this._propertyDescriptor(request.name) };
-  },
-
-  /**
-   * Handle a protocol request to provide the display string for the object.
-   */
-  onDisplayString: function() {
-    const string = stringify(this.obj);
-    return { from: this.actorID,
-             displayString: this.hooks.createValueGrip(string) };
-  },
-
-  /**
-   * A helper method that creates a property descriptor for the provided object,
-   * properly formatted for sending in a protocol response.
-   *
-   * @private
-   * @param string name
-   *        The property that the descriptor is generated for.
-   * @param boolean [onlyEnumerable]
-   *        Optional: true if you want a descriptor only for an enumerable
-   *        property, false otherwise.
-   * @return object|undefined
-   *         The property descriptor, or undefined if this is not an enumerable
-   *         property and onlyEnumerable=true.
-   */
-  _propertyDescriptor: function(name, onlyEnumerable) {
-    if (!DevToolsUtils.isSafeDebuggerObject(this.obj)) {
-      return undefined;
-    }
-
-    let desc;
-    try {
-      desc = this.obj.getOwnPropertyDescriptor(name);
-    } catch (e) {
-      // Calling getOwnPropertyDescriptor on wrapped native prototypes is not
-      // allowed (bug 560072). Inform the user with a bogus, but hopefully
-      // explanatory, descriptor.
-      return {
-        configurable: false,
-        writable: false,
-        enumerable: false,
-        value: e.name
-      };
-    }
-
-    if (!desc || onlyEnumerable && !desc.enumerable) {
-      return undefined;
-    }
-
-    let retval = {
-      configurable: desc.configurable,
-      enumerable: desc.enumerable
-    };
-
-    if ("value" in desc) {
-      retval.writable = desc.writable;
-      retval.value = this.hooks.createValueGrip(desc.value);
-    } else {
-      if ("get" in desc) {
-        retval.get = this.hooks.createValueGrip(desc.get);
-      }
-      if ("set" in desc) {
-        retval.set = this.hooks.createValueGrip(desc.set);
-      }
-    }
-    return retval;
-  },
-
-  /**
-   * Handle a protocol request to provide the source code of a function.
-   *
-   * @param request object
-   *        The protocol request object.
-   */
-  onDecompile: function(request) {
-    if (this.obj.class !== "Function") {
-      return { error: "objectNotFunction",
-               message: "decompile request is only valid for object grips " +
-                        "with a 'Function' class." };
-    }
-
-    return { from: this.actorID,
-             decompiledCode: this.obj.decompile(!!request.pretty) };
-  },
-
-  /**
-   * Handle a protocol request to provide the parameters of a function.
-   */
-  onParameterNames: function() {
-    if (this.obj.class !== "Function") {
-      return { error: "objectNotFunction",
-               message: "'parameterNames' request is only valid for object " +
-                        "grips with a 'Function' class." };
-    }
-
-    return { parameterNames: this.obj.parameterNames };
-  },
-
-  /**
-   * Handle a protocol request to release a thread-lifetime grip.
-   */
-  onRelease: function() {
-    this.release();
-    return {};
-  },
-
-  /**
-   * Handle a protocol request to provide the lexical scope of a function.
-   */
-  onScope: function() {
-    if (this.obj.class !== "Function") {
-      return { error: "objectNotFunction",
-               message: "scope request is only valid for object grips with a" +
-                        " 'Function' class." };
-    }
-
-    let envActor = this.hooks.createEnvironmentActor(this.obj.environment,
-                                                     this.registeredPool);
-    if (!envActor) {
-      return { error: "notDebuggee",
-               message: "cannot access the environment of this function." };
-    }
-
-    return { from: this.actorID, scope: envActor.form() };
-  },
-
-  /**
-   * Handle a protocol request to get the list of dependent promises of a
-   * promise.
-   *
-   * @return object
-   *         Returns an object containing an array of object grips of the
-   *         dependent promises
-   */
-  onDependentPromises: function() {
-    if (this.obj.class != "Promise") {
-      return { error: "objectNotPromise",
-               message: "'dependentPromises' request is only valid for " +
-                        "object grips with a 'Promise' class." };
-    }
-
-    let promises = this.obj.promiseDependentPromises
-                           .map(p => this.hooks.createValueGrip(p));
-
-    return { promises };
-  },
-
-  /**
-   * Handle a protocol request to get the allocation stack of a promise.
-   */
-  onAllocationStack: function() {
-    if (this.obj.class != "Promise") {
-      return { error: "objectNotPromise",