Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 09 Aug 2017 12:27:38 +0200
changeset 643326 ee77b27b55e4d743b801514ea7d76a652f161873
parent 643325 ec16e8357305e1b1737bfe195ff22e23b5b48de2 (current diff)
parent 643173 4c5fbf49376351679dcc49f4cff26c3c2e055ccc (diff)
child 643327 a886c2a428f6f9093e0ec2bd979be4cc29df74f5
push id73068
push usergpascutto@mozilla.com
push dateWed, 09 Aug 2017 16:52:24 +0000
milestone57.0a1
Merge mozilla-central to mozilla-inbound
browser/themes/shared/incontentprefs/icons.svg
servo/tests/unit/style/parsing/basic_shape.rs
servo/tests/unit/style/parsing/containment.rs
servo/tests/unit/style/parsing/inherited_box.rs
servo/tests/unit/style/parsing/mask.rs
servo/tests/unit/style/parsing/text.rs
servo/tests/unit/style/parsing/ui.rs
testing/web-platform/meta/dom/events/EventTarget-dispatchEvent.html.ini
--- a/.eslintignore
+++ b/.eslintignore
@@ -169,16 +169,17 @@ devtools/client/debugger/test/mochitest/
 devtools/client/debugger/test/mochitest/code_binary_search_absolute.js
 devtools/client/debugger/test/mochitest/code_math.min.js
 devtools/client/debugger/test/mochitest/code_math_bogus_map.js
 devtools/client/debugger/test/mochitest/code_ugly*
 devtools/client/debugger/test/mochitest/code_worker-source-map.js
 devtools/client/framework/test/code_ugly*
 devtools/client/inspector/markup/test/events_bundle.js
 devtools/client/netmonitor/test/xhr_bundle.js
+devtools/client/webconsole/new-console-output/test/mochitest/code_bundle_nosource.js
 devtools/server/tests/unit/babel_and_browserify_script_with_source_map.js
 devtools/server/tests/unit/setBreakpoint*
 devtools/server/tests/unit/sourcemapped.js
 
 # dom/ exclusions
 dom/animation/**
 dom/archivereader/**
 dom/asmjscache/**
@@ -334,19 +335,16 @@ services/sync/tps/extensions/mozmill
 toolkit/components/help/**
 
 # Intentionally invalid JS
 toolkit/components/workerloader/tests/moduleF-syntax-error.js
 
 # Tests old non-star function generators
 toolkit/modules/tests/xpcshell/test_task.js
 
-# Not yet updated
-toolkit/components/url-classifier/**
-
 # External code:
 toolkit/components/microformats/test/**
 toolkit/components/microformats/microformat-shiv.js
 toolkit/components/reader/Readability.js
 toolkit/components/reader/JSDOMParser.js
 
 # Uses preprocessing
 toolkit/content/widgets/wizard.xml
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-development-helpers.js
@@ -0,0 +1,48 @@
+/* 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/. */
+
+/**
+ * Extra features for local development. This file isn't loaded in
+ * non-local builds.
+ */
+
+var DevelopmentHelpers = {
+  init() {
+    this.quickRestart = this.quickRestart.bind(this);
+    this.addRestartShortcut();
+  },
+
+  quickRestart() {
+    Services.obs.notifyObservers(null, "startupcache-invalidate");
+
+    let env = Cc["@mozilla.org/process/environment;1"].
+              getService(Components.interfaces.nsIEnvironment);
+    env.set("MOZ_DISABLE_SAFE_MODE_KEY", "1");
+
+    Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
+  },
+
+  addRestartShortcut() {
+    let command = document.createElement("command");
+    command.setAttribute("id", "cmd_quickRestart");
+    command.addEventListener("command", this.quickRestart, true);
+    command.setAttribute("oncommand", "void 0;"); // Needed - bug 371900
+    document.getElementById("mainCommandSet").prepend(command);
+
+    let key = document.createElement("key");
+    key.setAttribute("id", "key_quickRestart");
+    key.setAttribute("key", "r");
+    key.setAttribute("modifiers", "accel,alt");
+    key.setAttribute("command", "cmd_quickRestart");
+    key.setAttribute("oncommand", "void 0;"); // Needed - bug 371900
+    document.getElementById("mainKeyset").prepend(key);
+
+    let menuitem = document.createElement("menuitem");
+    menuitem.setAttribute("id", "menu_FileRestartItem");
+    menuitem.setAttribute("key", "key_quickRestart");
+    menuitem.setAttribute("label", "Restart (Developer)");
+    menuitem.addEventListener("command", this.quickRestart, true);
+    document.getElementById("menu_FilePopup").appendChild(menuitem);
+  },
+};
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1683,16 +1683,19 @@ var gBrowserInit = {
     // initialize the sync UI
     requestIdleCallback(() => {
       gSync.init();
     }, {timeout: 1000 * 5});
 
     if (AppConstants.MOZ_DATA_REPORTING)
       gDataNotificationInfoBar.init();
 
+    if (!AppConstants.MOZILLA_OFFICIAL)
+      DevelopmentHelpers.init();
+
     requestIdleCallback(() => {
       // setup simple gestures support
       gGestureSupport.init(true);
 
       // setup history swipe animation
       gHistorySwipeAnimation.init();
     });
 
--- a/browser/base/content/global-scripts.inc
+++ b/browser/base/content/global-scripts.inc
@@ -18,8 +18,12 @@
 <script type="application/javascript" src="chrome://browser/content/browser-plugins.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-sidebar.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-tabsintitlebar.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-trackingprotection.js"/>
 
 #ifdef MOZ_DATA_REPORTING
 <script type="application/javascript" src="chrome://browser/content/browser-data-submission-info-bar.js"/>
 #endif
+
+#ifndef MOZILLA_OFFICIAL
+<script type="application/javascript" src="chrome://browser/content/browser-development-helpers.js"/>
+#endif
--- a/browser/base/content/newtab/page.js
+++ b/browser/base/content/newtab/page.js
@@ -14,18 +14,24 @@ const SCHEDULE_UPDATE_TIMEOUT_MS = 1000;
 var gPage = {
   /**
    * Initializes the page.
    */
   init: function Page_init() {
     // Add ourselves to the list of pages to receive notifications.
     gAllPages.register(this);
 
-    // Listen for 'unload' to unregister this page.
-    addEventListener("unload", this, false);
+    // Listen for 'unload' to unregister this page. Save a promise that can be
+    // passed to others to know when to clean up, e.g., background thumbnails.
+    this.unloadingPromise = new Promise(resolve => {
+      addEventListener("unload", () => {
+        resolve();
+        this._handleUnloadEvent();
+      });
+    });
 
     // XXX bug 991111 - Not all click events are correctly triggered when
     // listening from xhtml nodes -- in particular middle clicks on sites, so
     // listen from the xul window and filter then delegate
     addEventListener("click", this, false);
 
     // Check if the new tab feature is enabled.
     let enabled = gAllPages.enabled;
@@ -184,19 +190,16 @@ var gPage = {
   /**
    * Handles all page events.
    */
   handleEvent: function Page_handleEvent(aEvent) {
     switch (aEvent.type) {
       case "load":
         this.onPageVisibleAndLoaded();
         break;
-      case "unload":
-        this._handleUnloadEvent();
-        break;
       case "click":
         let {button, target} = aEvent;
         // Go up ancestors until we find a Site or not
         while (target) {
           if (target.hasOwnProperty("_newtabSite")) {
             target._newtabSite.onClick(aEvent);
             break;
           }
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -163,17 +163,18 @@ Site.prototype = {
   },
 
   /**
    * Captures the site's thumbnail in the background, but only if there's no
    * existing thumbnail and the page allows background captures.
    */
   captureIfMissing: function Site_captureIfMissing() {
     if (!document.hidden && !this.link.imageURI) {
-      BackgroundPageThumbs.captureIfMissing(this.url);
+      const {unloadingPromise} = gPage;
+      BackgroundPageThumbs.captureIfMissing(this.url, {unloadingPromise});
     }
   },
 
   /**
    * Refreshes the thumbnail for the site.
    */
   refreshThumbnail: function Site_refreshThumbnail() {
     // Only enhance tiles if that feature is turned on
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -116,16 +116,19 @@ var whitelist = [
    platforms: ["linux", "macosx"]},
 
   // browser/extensions/pdfjs/content/web/viewer.js#7450
   {file: "resource://pdf.js/web/debugger.js"},
 
   // Needed by Normandy
   {file: "resource://gre/modules/IndexedDB.jsm"},
 
+  // New L10n API that is not yet used in production
+  {file: "resource://gre/modules/Localization.jsm"},
+
   // Starting from here, files in the whitelist are bugs that need fixing.
   // Bug 1339420
   {file: "chrome://branding/content/icon128.png"},
   // Bug 1339424 (wontfix?)
   {file: "chrome://browser/locale/taskbar.properties",
    platforms: ["linux", "macosx"]},
   // Bug 1316187
   {file: "chrome://global/content/customizeToolbar.xul"},
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -64,16 +64,19 @@ browser.jar:
         content/browser/browser.js                    (content/browser.js)
 *       content/browser/browser.xul                   (content/browser.xul)
         content/browser/browser-addons.js             (content/browser-addons.js)
         content/browser/browser-captivePortal.js      (content/browser-captivePortal.js)
         content/browser/browser-ctrlTab.js            (content/browser-ctrlTab.js)
         content/browser/browser-customization.js      (content/browser-customization.js)
         content/browser/browser-data-submission-info-bar.js (content/browser-data-submission-info-bar.js)
         content/browser/browser-compacttheme.js       (content/browser-compacttheme.js)
+#ifndef MOZILLA_OFFICIAL
+        content/browser/browser-development-helpers.js (content/browser-development-helpers.js)
+#endif
         content/browser/browser-feeds.js              (content/browser-feeds.js)
         content/browser/browser-fullScreenAndPointerLock.js  (content/browser-fullScreenAndPointerLock.js)
         content/browser/browser-fullZoom.js           (content/browser-fullZoom.js)
         content/browser/browser-gestureSupport.js     (content/browser-gestureSupport.js)
         content/browser/browser-media.js              (content/browser-media.js)
         content/browser/browser-pageActions.js        (content/browser-pageActions.js)
         content/browser/browser-places.js             (content/browser-places.js)
         content/browser/browser-plugins.js            (content/browser-plugins.js)
--- a/browser/branding/nightly/content/aboutDialog.css
+++ b/browser/branding/nightly/content/aboutDialog.css
@@ -6,16 +6,17 @@
   background-color: #000f40;
   color: #fff;
 }
 
 #leftBox {
   background-image: url("chrome://branding/content/about-logo.png");
   background-repeat: no-repeat;
   background-size: 192px auto;
+  background-position: center 20%;
   /* min-width and min-height create room for the logo */
   min-width: 210px;
   min-height: 210px;
   margin-top: 20px;
   margin-inline-start: 30px;
 }
 
 @media (min-resolution: 2dppx) {
--- a/browser/components/preferences/in-content-new/main.xul
+++ b/browser/components/preferences/in-content-new/main.xul
@@ -304,23 +304,25 @@
 #endif
 
 #ifdef HAVE_SHELL_SERVICE
   <vbox id="defaultBrowserBox">
     <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
               label="&alwaysCheckDefault2.label;" accesskey="&alwaysCheckDefault2.accesskey;"/>
     <deck id="setDefaultPane">
       <hbox align="center" class="indent">
+        <image class="face-sad"/>
         <label id="isNotDefaultLabel" flex="1">&isNotDefault.label;</label>
         <button id="setDefaultButton"
                 class="accessory-button"
                 label="&setAsMyDefaultBrowser3.label;" accesskey="&setAsMyDefaultBrowser3.accesskey;"
                 preference="pref.general.disable_button.default_browser"/>
       </hbox>
       <hbox align="center" class="indent">
+        <image class="face-smile"/>
         <label id="isDefaultLabel" flex="1">&isDefault.label;</label>
       </hbox>
     </deck>
     <separator class="thin"/>
   </vbox>
 #endif
 
   <html:table id="startupTable">
@@ -823,30 +825,32 @@
       <hbox id="adminDisabled" align="center">
         <label>&update.adminDisabled;</label>
         <spacer flex="1"/>
         <button label="&update.checkForUpdatesButton.label;"
                 accesskey="&update.checkForUpdatesButton.accesskey;"
                 disabled="true"/>
       </hbox>
       <hbox id="noUpdatesFound" align="center">
+        <image class="face-smile"/>
         <label>&update.noUpdatesFound;</label>
         <spacer flex="1"/>
         <button label="&update.checkForUpdatesButton.label;"
                 accesskey="&update.checkForUpdatesButton.accesskey;"
                 oncommand="gAppUpdater.checkForUpdates();"/>
       </hbox>
       <hbox id="otherInstanceHandlingUpdates" align="center">
         <label>&update.otherInstanceHandlingUpdates;</label>
         <spacer flex="1"/>
         <button label="&update.checkForUpdatesButton.label;"
                 accesskey="&update.checkForUpdatesButton.accesskey;"
                 disabled="true"/>
       </hbox>
       <hbox id="manualUpdate" align="center">
+        <image class="face-sad"/>
         <label>&update.manual.start;</label><label id="manualLink" class="text-link"/><label>&update.manual.end;</label>
         <spacer flex="1"/>
         <button label="&update.checkForUpdatesButton.label;"
                 accesskey="&update.checkForUpdatesButton.accesskey;"
                 disabled="true"/>
       </hbox>
       <hbox id="unsupportedSystem" align="center">
         <description flex="1">
--- a/browser/extensions/activity-stream/test/functional/mochitest/browser.ini
+++ b/browser/extensions/activity-stream/test/functional/mochitest/browser.ini
@@ -1,9 +1,8 @@
 [DEFAULT]
 support-files =
   blue_page.html
   head.js
 
 [browser_as_load_location.js]
 [browser_as_render.js]
 [browser_getScreenshots.js]
-skip-if=true # issue 2851
--- a/browser/extensions/shield-recipe-client/skin/shared/Heartbeat.css
+++ b/browser/extensions/shield-recipe-client/skin/shared/Heartbeat.css
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Notification overrides for Heartbeat UI */
 
 notification.heartbeat {
   background-color: #F1F1F1 !important;
   border-bottom: 1px solid #C1C1C1 !important;
   height: 40px;
+  color: #333 !important;
 }
 
 @keyframes pulse-onshow {
   0% {
     opacity: 0;
     transform: scale(1);
   }
 
@@ -44,17 +45,16 @@ notification.heartbeat {
   }
 
   100% {
     transform: scale(1);
   }
 }
 
 .messageText.heartbeat {
-  color: #333 !important;
   margin-inline-end: 12px !important; /* The !important is required to override OSX default style. */
   margin-inline-start: 0;
   text-shadow: none;
 }
 
 .messageImage.heartbeat {
   height: 24px !important;
   margin-inline-end: 8px !important;
--- a/browser/themes/linux/preferences/in-content-new/preferences.css
+++ b/browser/themes/linux/preferences/in-content-new/preferences.css
@@ -20,29 +20,17 @@
   margin-inline-start: 1px;
   margin-inline-end: 6px;
 }
 
 .actionsMenu > .menulist-label-box > .menulist-label {
   margin-top: 2px !important;
 }
 
-#fxaProfileImage {
-  -moz-user-focus: normal;
-}
-
 menulist.actionsMenu > .menulist-dropmarker {
   margin-top: 11px;
   margin-bottom: 11px;
 }
 
 textbox + button,
 filefield + button {
   margin-inline-start: -4px;
 }
-
-/**
- * Dialog
- */
-
-#dialogTitle {
-  font-size: 1em;
-}
--- a/browser/themes/osx/preferences/in-content-new/preferences.css
+++ b/browser/themes/osx/preferences/in-content-new/preferences.css
@@ -14,40 +14,23 @@ prefpane .groupbox-title {
   margin-inline-start: 2px;
   margin-inline-end: 8px !important;
 }
 
 #downloadFolder > .fileFieldContentBox {
   padding-inline-start: 3px;
 }
 
-#fxaProfileImage {
-  -moz-user-focus: normal;
-}
-
 textbox + button {
   margin-inline-start: -4px;
 }
 
 filefield + button {
   margin-inline-start: -8px;
 }
 
 #popupPolicyRow {
   /* Override styles from
      browser/themes/osx/preferences/preferences.css */
   margin-bottom: 0 !important;
   padding-bottom: 0 !important;
   border-bottom: none;
 }
-
-#advancedPrefs {
-  margin-right: 0; /*override margin from normal preferences.css */
-  margin-left: 0;
-}
-
-/**
- * Dialog
- */
-
-#dialogTitle {
-  font-size: 1.1em;
-}
new file mode 100755
--- /dev/null
+++ b/browser/themes/shared/incontentprefs/face-sad.svg
@@ -0,0 +1,9 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+  <path fill="#00D3E7" d="M9.94.8a9.01 9.01 0 0 0-9 9.42 1.07 1.07 0 0 0 .13 2.12h.21A9.01 9.01 0 1 0 9.94.8"/>
+  <path fill="#00A1C1" d="M18.93 10.21v-.4c0-2.4-.93-4.6-2.47-6.2A9.26 9.26 0 0 1 3.72 16.34a9 9 0 0 0 14.85-4l.21-.01c.6 0 1.07-.48 1.07-1.07.01-.54-.4-.99-.92-1.06"/>
+  <path fill="#323B59" d="M5.47 6.55a.99.99 0 0 0-.99.99v2.22a.99.99 0 1 0 1.98 0V7.54a.99.99 0 0 0-1-.99m8.98 0a.99.99 0 0 0-1 .99v2.22a.99.99 0 1 0 1.98 0V7.54a.99.99 0 0 0-.98-.99m-4.5 5.47a2.27 2.27 0 0 0-2.34 2.13c-.02.23.09.45.27.58.19.14.43.17.64.09 0 0 .47-.17 1.34-.17.91 0 1.55.19 1.56.19a.69.69 0 0 0 .61-.11.67.67 0 0 0 .26-.58 2.27 2.27 0 0 0-2.34-2.13"/>
+  <path fill="#00A1C1" d="M4.59 7.02c-.36 0-.72-.11-1.03-.32a.318.318 0 1 1 .35-.53 1.25 1.25 0 0 0 1.8-.47.313.313 0 1 1 .56.28 1.88 1.88 0 0 1-1.68 1.04m10.47 0a1.88 1.88 0 0 1-1.68-1.04.313.313 0 1 1 .56-.28 1.24 1.24 0 0 0 1.8.48.32.32 0 0 1 .35.53c-.3.2-.66.3-1.03.3"/>
+</svg>
new file mode 100755
--- /dev/null
+++ b/browser/themes/shared/incontentprefs/face-smile.svg
@@ -0,0 +1,14 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+  <path fill="#FFBF49" d="M9.95.8A9.03 9.03 0 0 0 .92 9.83l.01.4a1.07 1.07 0 0 0 .14 2.13h.21A9.04 9.04 0 1 0 9.95.8"/>
+  <path fill="#323B59" d="M14.44 6.7c.55 0 1 .44 1 .99V9.9c0 .55-.45.12-1 .12s-.99.42-.99-.12V7.69c0-.55.45-1 1-1"/>
+  <path fill="#FFA500" d="M13.54 10.46a.31.31 0 0 1-.14-.59 2.36 2.36 0 0 1 2.08-.03.3.3 0 0 1 .14.41.3.3 0 0 1-.41.15 1.75 1.75 0 0 0-1.53.02.28.28 0 0 1-.14.04"/>
+  <path fill="#323B59" d="M5.46 6.7c.55 0 .99.44.99.99V9.9c0 .55-.44.12-.99.12s-.99.42-.99-.12V7.69c0-.55.45-1 .99-1"/>
+  <path fill="#FFA500" d="M4.56 10.46a.31.31 0 0 1-.14-.59 2.36 2.36 0 0 1 2.07-.03.3.3 0 0 1 .15.41.3.3 0 0 1-.41.15 1.75 1.75 0 0 0-1.53.02.3.3 0 0 1-.14.04"/>
+  <path fill="#323B59" d="M16.06 12.42l-.62-.05c-.06.02-.21.06-.45.1-.3.04-.73.15-1.25.2-.51.04-1.12.14-1.76.17-.65.04-1.33.05-2.03.06-.68-.01-1.37-.02-2.02-.07-.65-.03-1.25-.12-1.76-.17-.52-.04-.95-.15-1.25-.2l-.45-.09a30.35 30.35 0 0 0-.7.07.17.17 0 0 0-.04.23l.1.17a7.67 7.67 0 0 0 1.54 1.75c.5.45 1.01.78 1.53 1.06l.41.19c.78-.55 1.68.2 2.64.2.97 0 1.87-.74 2.64-.2.14-.05.28-.1.42-.18a8.34 8.34 0 0 0 3.06-2.82l.1-.18c.02-.02.03-.04.03-.07a.16.16 0 0 0-.14-.17"/>
+  <path fill="#FFF" d="M6.17 12.66c.51.05 1.12.14 1.76.17.65.05 1.33.06 2.02.07.7-.01 1.38-.02 2.03-.06.65-.04 1.25-.13 1.76-.17.52-.05.95-.15 1.25-.2.24-.04.4-.08.45-.1l-.9-.05-1.53-.06c-1.02-.03-2.04-.03-3.06-.05-1.02.02-2.04.02-3.05.05l-1.53.06-.9.05c.05.02.21.06.45.1.3.04.73.15 1.25.2"/>
+  <path fill="#FFA500" d="M18.96 10.23v-.4c0-2.42-.94-4.6-2.48-6.22A9.27 9.27 0 0 1 3.72 16.37a9.02 9.02 0 0 0 14.87-4l.22-.01c.6 0 1.08-.48 1.08-1.07 0-.55-.4-1-.93-1.06"/>
+  <path fill="#FF7664" d="M12.58 15.85h.01c-.77-.55-1.68-.37-2.64-.25-.96-.12-1.87-.3-2.64.25.87.39 1.73.57 2.6.58H10c.86 0 1.73-.2 2.6-.58h-.02"/>
+</svg>
new file mode 100755
--- /dev/null
+++ b/browser/themes/shared/incontentprefs/fxa-avatar.svg
@@ -0,0 +1,8 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80">
+  <circle r="40" cx="40" cy="40" fill="#E1E1E6"/>
+  <path fill="#F5F5F7" d="M70.5 41.2l.02-1.36A30.5 30.5 0 0 0 39.97 9.41 30.5 30.5 0 0 0 9.41 39.84l.02 1.36a3.62 3.62 0 0 0-3.15 3.58c0 2 1.62 3.6 3.63 3.6h.72a30.46 30.46 0 0 0 8.3 13.51l-.04-.02a30.54 30.54 0 0 0 19.72 8.35l.24.01.43.01.51.01h.86l.41-.02h.27c.16-.02.33-.02.5-.03l.12-.01A30.55 30.55 0 0 0 69.3 48.37h.68a3.61 3.61 0 0 0 .51-7.18"/>
+  <path fill="#C8C8CC" d="M58.53 39.81v-7.18a3.35 3.35 0 1 0-6.7 0v7.27l-.17.08a1.05 1.05 0 0 0 .47 1.98c.17 0 .32-.04.47-.12a5.94 5.94 0 0 1 5.18-.07c.53.25 1.15.03 1.4-.5.25-.52.03-1.14-.5-1.39l-.15-.07zm-30.41 0v-7.18a3.35 3.35 0 1 0-6.7 0v7.27l-.17.08a1.05 1.05 0 0 0 .47 1.98 1 1 0 0 0 .47-.12 5.94 5.94 0 0 1 5.18-.07c.53.25 1.15.03 1.4-.5.25-.52.03-1.14-.5-1.39l-.15-.07zM48.85 60.1l.07.04c.47-.2.93-.38 1.4-.62A28.29 28.29 0 0 0 60.68 50l.37-.58a.58.58 0 0 0 .08-.26.53.53 0 0 0-.47-.57c-.7-.07-1.4-.11-2.1-.15-.2.05-.72.18-1.53.33-1.02.14-2.47.5-4.22.66-1.74.15-3.8.47-5.97.57-2.19.15-4.51.18-6.85.2-2.33-.04-4.67-.07-6.86-.22-2.19-.1-4.22-.41-5.97-.56-1.74-.17-3.21-.51-4.22-.66-.8-.16-1.35-.28-1.53-.33-.72.04-1.42.1-2.14.15a.47.47 0 0 0-.25.1.56.56 0 0 0-.13.75l.38.57a30.15 30.15 0 0 0 5.18 5.91 27.28 27.28 0 0 0 5.18 3.57c.47.25.94.43 1.41.64a3.84 3.84 0 0 1-.01.02l.02.02-.02.01a21.8 21.8 0 0 0 8.79 1.93l.14.02h.03l.13-.02a21.8 21.8 0 0 0 8.8-1.93l-.03-.01c.02 0 .02 0 .03-.02a6.87 6.87 0 0 0-.07-.04z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/incontentprefs/general.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M7 11.5a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0M21.48 10h-2.65a7.45 7.45 0 0 0-1.1-2.64l1.88-1.87a1.5 1.5 0 1 0-2.12-2.12l-1.87 1.87c-.8-.52-1.69-.9-2.65-1.1l.01-.15V1.5a1.5 1.5 0 1 0-3 0V4l.02.14c-.96.2-1.86.58-2.65 1.1L5.5 3.37a1.5 1.5 0 1 0-2.12 2.12l1.87 1.87c-.52.79-.9 1.68-1.1 2.63H1.5a1.5 1.5 0 1 0 0 3h2.64c.2.96.58 1.85 1.1 2.64L3.37 17.5a1.5 1.5 0 1 0 2.12 2.12l1.87-1.87c.79.52 1.68.9 2.63 1.1v2.64a1.5 1.5 0 1 0 3 0v-2.65c.96-.2 1.85-.57 2.64-1.1l1.87 1.88a1.5 1.5 0 0 0 2.12 0 1.5 1.5 0 0 0 0-2.12l-1.87-1.87c.52-.8.9-1.68 1.1-2.64h2.64a1.5 1.5 0 1 0 0-3"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/incontentprefs/help.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
+  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8 2a6 6 0 1 0 .01 12.01A6 6 0 0 0 8 2m0 13A7 7 0 0 1 8 1a7 7 0 0 1 0 14"/>
+  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8 10.75a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5m2.82-5.6A2.84 2.84 0 0 0 8 3.13C6.25 3.13 5.12 4.25 5.12 6a.88.88 0 0 0 1.75 0c0-1 .6-1.13 1.13-1.13.64 0 1.03.38 1.13.75.1.39-.08.75-.53 1.01C7.35 7.38 7.09 8.52 7.13 9v.34a.88.88 0 0 0 1.75 0v-.37c0-.05.01-.48.6-.83 1.13-.66 1.65-1.83 1.34-2.98"/>
+</svg>
deleted file mode 100644
--- a/browser/themes/shared/incontentprefs/icons.svg
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
-  <style>
-    use:not(:target) {
-      display: none;
-    }
-    use {
-      fill: #fbfbfb;
-      stroke: rgba(0,0,0,0.4);
-      stroke-width: .5px;
-    }
-    use[id$="-native"] {
-      fill: ThreeDHighlight;
-    }
-  </style>
-  <defs>
-    <g id="general-shape">
-      <path d="M18.97,3H5.03C3.914,3,3,3.914,3,5.03v13.94C3,20.086,3.914,21,5.03,21H18.97c1.117,0,2.03-0.914,2.03-2.03 V5.03C21,3.914,20.086,3,18.97,3z M5.35,19.326c-0.404,0-0.731-0.327-0.731-0.731c0-0.404,0.327-0.731,0.731-0.731 c0.404,0,0.731,0.327,0.731,0.731C6.081,19,5.754,19.326,5.35,19.326z M5.35,6.168c-0.403,0-0.731-0.328-0.731-0.731 c0-0.404,0.328-0.731,0.731-0.731c0.403,0,0.731,0.327,0.731,0.731C6.081,5.84,5.753,6.168,5.35,6.168z M15.243,14.035 c0,0.229-0.186,0.416-0.414,0.416c-0.229,0-0.415,0.186-0.415,0.414v3.347c0,0.228-0.185,0.384-0.414,0.384l-4.141,0.03 c-0.227,0-0.414-0.186-0.414-0.414v-3.347c0-0.228-0.185-0.414-0.414-0.414c-0.227,0-0.414-0.187-0.414-0.416V6.582 c0-0.229,0.187-0.414,0.414-0.414h5.798c0.228,0,0.414,0.185,0.414,0.414V14.035z M18.509,19.326c-0.404,0-0.731-0.327-0.731-0.731 c0-0.404,0.327-0.731,0.731-0.731c0.404,0,0.731,0.327,0.731,0.731C19.24,19,18.913,19.326,18.509,19.326z M18.509,6.168 c-0.404,0-0.731-0.328-0.731-0.731c0-0.404,0.327-0.731,0.731-0.731c0.404,0,0.731,0.327,0.731,0.731 C19.24,5.84,18.913,6.168,18.509,6.168z"/>
-      <path d="M12.757,7.824h-1.657c-0.456,0-0.828,0.373-0.828,0.828v8.282c0,0.456,0.373,0.828,0.828,0.828h1.657 c0.456,0,0.828-0.373,0.828-0.828V8.652C13.586,8.196,13.213,7.824,12.757,7.824z"/>
-    </g>
-    <g id="security-shape">
-      <path d="M18.909,9.783h-0.863V8.086C18.046,4.725,15.339,2,12,2 C8.661,2,5.954,4.725,5.954,8.086v1.697H5.091c-0.955,0-1.728,0.779-1.728,1.739v8.738c0,0.961,0.773,1.74,1.728,1.74h13.818 c0.954,0,1.728-0.779,1.728-1.74v-8.738C20.637,10.562,19.863,9.783,18.909,9.783z M8.545,8.086c0-1.92,1.547-3.478,3.455-3.478 c1.908,0,3.455,1.557,3.455,3.478v1.697h-6.91V8.086z M5.181,16.092l-0.909-1.2v-2.284l2.728,3.483H5.181z M8.818,16.092 l-2.773-3.657h1.727l2.864,3.657H8.818z M12,16.092l-2.773-3.657h1.727l2.864,3.657H12z M15.637,16.092l-2.773-3.657h1.727 l2.864,3.657H15.637z M19.728,16.092h-0.455l-2.773-3.657h1.727l1.501,1.916V16.092z"/>
-    </g>
-    <g id="sync-shape">
-      <path style="fill:#F1F1F1;" d="M3.2,22h3.3h10.8h3.3c0.5,0,0.9-0.4,0.9-0.9V20c0-1-0.5-1.9-1.2-2.5c-2.3-1.8-4.6-2.9-5.1-3.1
-        c-0.1,0-0.1-0.1-0.1-0.2v-1.6c0.3-0.5,0.4-1,0.5-1.5c0.2,0.1,0.6,0.1,1-1.3c0.3-1.1,0.1-1.5-0.2-1.6c0.9-4.4-1.1-4.5-1.1-4.5
-        S15,3.1,14.1,2.6c-0.5-0.3-1.3-0.6-2.3-0.5c-0.4,0-0.7,0.1-1,0.2c-0.4,0.1-0.7,0.3-1,0.5C9.4,3.1,9.1,3.3,8.7,3.7
-        c-0.5,0.5-1,1.2-1.1,2C7.4,6.4,7.4,7.1,7.7,7.9C7.3,7.8,6.9,8,7.3,9.5c0.3,1.1,0.6,1.4,0.8,1.4c0.1,0.6,0.3,1.3,0.7,1.9v1.4
-        c0,0.1,0,0.1-0.1,0.2c-0.5,0.2-2.8,1.4-5.1,3.1C2.8,18.1,2.3,19,2.3,20v1.1C2.3,21.6,2.7,22,3.2,22"/>
-    </g>
-    <g id="search-shape">
-      <path d="M20.6,19.6l-4.4-4.5c2.4-2.9,2.4-7.1,0.2-10c-2.3-3-6.3-3.9-9.6-2.3c-3.3,1.6-5.1,5.3-4.3,9 c0.8,3.7,4,6.3,7.7,6.3c1.5,0,3-0.4,4.3-1.3l4.5,4.6c0.3,0.3,0.8,0.4,1.2,0.3c0.4-0.1,0.7-0.4,0.9-0.9S20.9,19.9,20.6,19.6z M10.1,16c-3.3,0-6-2.7-6-6.1c0-3.4,2.7-6.1,6-6.1c3.3,0,6,2.7,6,6.1C16.1,13.3,13.4,16,10.1,16z"/>
-      <path d="M10.1,5.3c-2.5,0-4.6,2.1-4.6,4.7c0,1.2,0.5,2.4,1.4,3.3c0.9,0.9,2,1.4,3.3,1.4c2.5,0,4.6-2.1,4.6-4.7 C14.7,7.4,12.7,5.3,10.1,5.3z M10,7.9c-1,0-1.8,0.8-1.8,1.8c0,0.4-0.3,0.8-0.8,0.8s-0.8-0.4-0.8-0.8c0-1.9,1.5-3.4,3.3-3.4h0 c0.4,0,0.8,0.4,0.8,0.8S10.4,7.9,10,7.9z"/>
-    </g>
-  </defs>
-  <use id="general" href="#general-shape"/>
-  <use id="general-native" href="#general-shape"/>
-  <use id="security" href="#security-shape"/>
-  <use id="security-native" href="#security-shape"/>
-  <use id="sync" href="#sync-shape"/>
-  <use id="sync-native" href="#sync-shape"/>
-  <use id="search" href="#search-shape"/>
-  <use id="search-native" href="#search-shape"/>
-</svg>
new file mode 100755
--- /dev/null
+++ b/browser/themes/shared/incontentprefs/logo-android.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+  <path fill="#0C0C0D" d="M9.24 16.03v2.73c0 .7-.56 1.24-1.25 1.24-.7 0-1.26-.55-1.26-1.24v-2.73h-.9c-.75 0-1.35-.59-1.35-1.33v-8h11.2v8c0 .74-.6 1.33-1.36 1.33h-.9v2.73c0 .7-.55 1.24-1.25 1.24s-1.26-.55-1.26-1.24v-2.73H9.24zM2.76 6.5c.7 0 1.25.55 1.25 1.24v5.15c0 .7-.56 1.24-1.25 1.24-.7 0-1.26-.55-1.26-1.24V7.73c0-.69.56-1.24 1.26-1.24zm14.64 0c.7 0 1.25.55 1.25 1.24v5.15c0 .7-.55 1.24-1.25 1.24s-1.26-.55-1.26-1.24V7.73c0-.69.56-1.24 1.26-1.24zM6.6 0c.06 0 .12.03.16.1l.9 1.58a6.04 6.04 0 0 1 4.84 0L13.4.1a.18.18 0 0 1 .24-.07c.09.05.12.15.07.24l-.89 1.58a5.04 5.04 0 0 1 2.86 4.43H4.48c0-1.9 1.15-3.56 2.85-4.43L6.45.26a.17.17 0 0 1 .07-.24A.18.18 0 0 1 6.6 0zm.9 3.33c-.26 0-.47.2-.47.46a.47.47 0 0 0 .93 0 .47.47 0 0 0-.47-.46zm5.16 0c-.25 0-.47.2-.47.46a.47.47 0 0 0 .94 0 .47.47 0 0 0-.47-.46z"/>
+</svg>
new file mode 100755
--- /dev/null
+++ b/browser/themes/shared/incontentprefs/logo-ios.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+  <path fill="#0C0C0D" fill-rule="evenodd" d="M16.84 15.03c-.27.63-.59 1.2-.96 1.74-.51.72-.93 1.23-1.25 1.5-.5.46-1.03.7-1.6.71-.41 0-.9-.12-1.48-.35a4.25 4.25 0 0 0-1.6-.36c-.5 0-1.05.12-1.63.36A4.4 4.4 0 0 1 6.9 19c-.55.02-1.1-.22-1.64-.73-.35-.3-.78-.82-1.3-1.56-.56-.78-1.02-1.7-1.38-2.73A10.03 10.03 0 0 1 2 10.7c0-1.2.26-2.25.78-3.12a4.6 4.6 0 0 1 3.86-2.28c.43 0 1 .13 1.71.4.71.26 1.16.4 1.36.4.15 0 .66-.16 1.51-.47.8-.3 1.5-.41 2.05-.37 1.51.13 2.65.72 3.4 1.8a3.8 3.8 0 0 0-2 3.44 4.1 4.1 0 0 0 2.5 3.68c-.1.3-.21.57-.33.84zM13.37 1.36c0 .9-.33 1.74-.98 2.52-.8.92-1.75 1.46-2.79 1.37a3.95 3.95 0 0 1 1.02-2.89c.34-.38.76-.7 1.28-.95.51-.25 1-.39 1.45-.41.02.12.02.24.02.36z"/>
+</svg>
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -14,17 +14,17 @@
   display: block;
   max-width: 800px;
 }
 
 #mainPrefPane {
   width: 100%;
   padding: 0;
   font: message-box;
-  color: #0c0c0d;
+  color: currentColor;
 }
 
 #mainPrefPane groupbox,
 #mainPrefPane deck,
 #mainPrefPane description {
   font-size: 1.36rem;
 }
 
@@ -119,55 +119,50 @@ caption > label {
 }
 
 #categories > scrollbox {
   overflow-x: hidden !important;
 }
 
 .category-name {
   font-size: 1.45rem;
-  color: #0c0c0d;
 }
 
 .category,
 .category:hover,
 .category[selected] {
   background-color: transparent;
   border-inline-start: initial;
   padding-inline-start: 44px;
 }
 
-richlistitem[selected='true'] .category-name {
-  color: #0a84ff;
-}
-
 /**
  * We want the last category to always have non-0 getBoundingClientRect().bottom
  * so we can use the value to figure out the max-height of the list in
  * preferences.js, so use collapse instead of display: none; if it's hidden
  */
 #categories > .category[hidden="true"] {
   display: -moz-box;
   visibility: collapse;
 }
 
 #category-general > .category-icon {
-  list-style-image: url("chrome://browser/skin/preferences/in-content-new/icons.svg#general");
+  list-style-image: url("chrome://browser/skin/preferences/in-content-new/general.svg");
 }
 
 #category-search > .category-icon {
-  list-style-image: url("chrome://browser/skin/preferences/in-content-new/icons.svg#search");
+  list-style-image: url("chrome://browser/skin/preferences/in-content-new/search.svg");
 }
 
 #category-privacy > .category-icon {
-  list-style-image: url("chrome://browser/skin/preferences/in-content-new/icons.svg#security");
+  list-style-image: url("chrome://browser/skin/preferences/in-content-new/privacy-security.svg");
 }
 
 #category-sync > .category-icon {
-  list-style-image: url("chrome://browser/skin/preferences/in-content-new/icons.svg#sync");
+  list-style-image: url("chrome://browser/skin/preferences/in-content-new/sync.svg");
 }
 
 @media (max-width: 800px) {
   .category-name {
     display: none;
   }
   .help-button {
     font-size: 0 !important;
@@ -447,20 +442,21 @@ groupbox {
 /**
  * Sync
  */
 
 #fxaProfileImage {
   width: 60px;
   height: 60px;
   border-radius: 50%;
-  list-style-image: url(chrome://browser/skin/fxa/default-avatar.svg);
+  list-style-image: url("chrome://browser/skin/preferences/in-content-new/fxa-avatar.svg");
   margin-inline-end: 15px;
   image-rendering: auto;
   border: 1px solid transparent;
+  -moz-user-focus: normal;
 }
 
 #fxaLoginStatus[hasName] #fxaProfileImage {
   width: 80px;
   height: 80px;
 }
 
 #fxaProfileImage.actionable {
@@ -576,17 +572,17 @@ groupbox {
 
 #fxaEmailAddress1,
 #fxaEmailAddress2,
 #fxaEmailAddress3 {
   word-break: break-all;
 }
 
 .fxaFirefoxLogo {
-  list-style-image: url(chrome://browser/skin/fxa/logo.png);
+  list-style-image: url("chrome://browser/skin/preferences/in-content-new/fxa-avatar.svg");
   width: 64px;
   height: 64px;
   margin-inline-end: 14px;
 }
 
 .fxaMobilePromo {
   line-height: 28px;
   margin-bottom: 20px;
@@ -599,47 +595,35 @@ groupbox {
   margin: 4px 8px 0px 0px;
 }
 
 #syncOptions {
   margin-bottom: 27.5px;
 }
 
 .androidLink {
-  list-style-image: url("chrome://browser/skin/fxa/android.png");
+  list-style-image: url("chrome://browser/skin/preferences/in-content-new/logo-android.svg");
 }
 
 .iOSLink {
-  list-style-image: url("chrome://browser/skin/fxa/ios.png");
+  list-style-image: url("chrome://browser/skin/preferences/in-content-new/logo-ios.svg");
 }
 
 .androidLink,
 .iOSLink {
-  vertical-align: bottom;
-  padding: 0 5px;
-  height: 28px;
+  width: 20px;
+  height: 20px;
+  vertical-align: text-bottom;
 }
 
 #tosPP-small {
   margin-top: 20px;
   margin-bottom: 20px;
 }
 
-@media (min-resolution: 1.1dppx) {
-  .androidLink {
-    list-style-image: url("chrome://browser/skin/fxa/android@2x.png");
-  }
-  .iOSLink {
-    list-style-image: url("chrome://browser/skin/fxa/ios@2x.png");
-  }
-  .fxaFirefoxLogo {
-    list-style-image: url(chrome://browser/skin/fxa/logo@2x.png);
-  }
-}
-
 #updateDeck > hbox > label {
   margin-inline-end: 5px ! important;
 }
 
 .update-throbber {
   width: 16px;
   min-height: 16px;
   margin-inline-end: 3px;
@@ -653,45 +637,56 @@ groupbox {
 }
 
 .help-button {
   position: fixed;
   left: 0;
   /* Needs to have enough gap from the bottom to not
      get behind the status panel (bug 1357841). */
   bottom: 2rem;
+  background-image: url("chrome://browser/skin/preferences/in-content-new/help.svg");
+  -moz-context-properties: fill, fill-opacity;
+  fill: currentColor;
+  fill-opacity: 0.8;
   font-size: 13px;
-  line-height: 13px;
-  height: 14px;
+  line-height: 16px;
   background-position: 15px;
   padding-inline-start: 35px;
   white-space: nowrap;
-  fill: #0c0c0d;
-  stroke: #0c0c0d;
 }
 
 .help-button:-moz-locale-dir(rtl) {
   left: auto;
   right: 0;
   background-position: right 15px top 0;
 }
 
+.help-button:hover {
+  fill: currentColor;
+  fill-opacity: 0.9;
+}
+
 .help-button:link,
 .help-button:visited {
-  color: #0c0c0d !important;
-  opacity: 0.8;
+  color: var(--in-content-category-text);
   text-decoration: none;
 }
 
-.help-button:hover {
-  color: #0c0c0d !important;
-  fill: #0c0c0d !important;
-  stroke: #0c0c0d !important;
-  stroke-opacity: 0!important;
-  opacity: 0.9;
+.face-sad {
+  list-style-image: url("chrome://browser/skin/preferences/in-content-new/face-sad.svg");
+  width: 20px;
+  height: 20px;
+  margin-inline-end: 8px;
+}
+
+.face-smile {
+  list-style-image: url("chrome://browser/skin/preferences/in-content-new/face-smile.svg");
+  width: 20px;
+  height: 20px;
+  margin-inline-end: 8px;
 }
 
 .search-container {
   position: sticky;
   background-color: var(--in-content-page-background);
   width: 100%;
   top: 0;
   z-index: 1;
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/incontentprefs/privacy-security.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M8 6a3 3 0 0 1 6 0v4H8V6zm9 4V6A6 6 0 0 0 5 6v4h-.75C3.01 10 2 11 2 12.25v7.52c0 1.24 1 2.25 2.25 2.25h13.5c1.24 0 2.25-1 2.25-2.25v-7.52C20 11 19 10 17.75 10H17z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/incontentprefs/search.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M10,15.1c-3.3,0-6-2.7-6-6c0-3.3,2.7-6,6-6c3.3,0,6,2.7,6,6S13.3,15.1,10,15.1L10,15.1 M17.3,14.3 c2.9-4,2-9.7-2.1-12.6s-9.7-2-12.6,2.1C1.6,5.3,1,7.2,1,9.1c0,5,4.1,9,9,9c1.8,0,3.6-0.6,5.2-1.6l6.2,6.2c0.6,0.6,1.5,0.6,2.1,0	c0.6-0.6,0.6-1.5,0-2.1L17.3,14.3L17.3,14.3z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/incontentprefs/sync.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M20.5 0c-.83 0-1.5.67-1.5 1.5v1.72A10.47 10.47 0 0 0 11.5 0C6.69 0 2.51 3.27 1.34 7.96a1.5 1.5 0 1 0 2.91.73A7.47 7.47 0 0 1 11.5 3c2.37 0 4.54 1.1 5.95 3H14.5a1.5 1.5 0 1 0 0 3h6c.83 0 1.5-.67 1.5-1.5v-6c0-.83-.67-1.5-1.5-1.5m.07 11.22a1.5 1.5 0 0 0-1.82 1.1A7.48 7.48 0 0 1 11.5 18a7.38 7.38 0 0 1-5.95-3H8.5a1.5 1.5 0 1 0 0-3h-6c-.83 0-1.5.67-1.5 1.5v6a1.5 1.5 0 0 0 3 0v-1.72A10.47 10.47 0 0 0 11.5 21c4.81 0 8.99-3.27 10.16-7.96a1.5 1.5 0 0 0-1.1-1.82"/>
+</svg>
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -67,21 +67,30 @@
   skin/classic/browser/panel-icon-arrow-right.svg              (../shared/panel-icon-arrow-right.svg)
   skin/classic/browser/panel-icon-cancel.svg                   (../shared/panel-icon-cancel.svg)
 #ifndef XP_MACOSX
   skin/classic/browser/panel-icon-folder.svg                   (../shared/panel-icon-folder.svg)
 #else
   skin/classic/browser/panel-icon-magnifier.svg                (../shared/panel-icon-magnifier.svg)
 #endif
   skin/classic/browser/panel-icon-retry.svg                    (../shared/panel-icon-retry.svg)
-  skin/classic/browser/preferences/in-content-new/icons.svg        (../shared/incontentprefs/icons.svg)
-  skin/classic/browser/preferences/in-content-new/no-search-results.svg     (../shared/incontentprefs/no-search-results.svg)
+  skin/classic/browser/preferences/in-content-new/face-sad.svg     (../shared/incontentprefs/face-sad.svg)
+  skin/classic/browser/preferences/in-content-new/face-smile.svg   (../shared/incontentprefs/face-smile.svg)
+  skin/classic/browser/preferences/in-content-new/fxa-avatar.svg   (../shared/incontentprefs/fxa-avatar.svg)
+  skin/classic/browser/preferences/in-content-new/general.svg      (../shared/incontentprefs/general.svg)
+  skin/classic/browser/preferences/in-content-new/help.svg         (../shared/incontentprefs/help.svg)
+  skin/classic/browser/preferences/in-content-new/logo-android.svg (../shared/incontentprefs/logo-android.svg)
+  skin/classic/browser/preferences/in-content-new/logo-ios.svg     (../shared/incontentprefs/logo-ios.svg)
+  skin/classic/browser/preferences/in-content-new/no-search-results.svg       (../shared/incontentprefs/no-search-results.svg)
+  skin/classic/browser/preferences/in-content-new/privacy-security.svg        (../shared/incontentprefs/privacy-security.svg)
   skin/classic/browser/preferences/in-content-new/search-arrow-indicator.svg  (../shared/incontentprefs/search-arrow-indicator.svg)
   skin/classic/browser/preferences/in-content-new/search.css       (../shared/incontentprefs/search.css)
+  skin/classic/browser/preferences/in-content-new/search.svg       (../shared/incontentprefs/search.svg)
   skin/classic/browser/preferences/in-content-new/siteDataSettings.css (../shared/incontentprefs/siteDataSettings.css)
+  skin/classic/browser/preferences/in-content-new/sync.svg         (../shared/incontentprefs/sync.svg)
 * skin/classic/browser/preferences/in-content-new/containers.css   (../shared/incontentprefs/containers.css)
   skin/classic/browser/preferences/in-content/icons.svg        (../shared/incontentprefs-old/icons.svg)
   skin/classic/browser/preferences/in-content/search.css       (../shared/incontentprefs-old/search.css)
 * skin/classic/browser/preferences/in-content/containers.css   (../shared/incontentprefs-old/containers.css)
 * skin/classic/browser/preferences/containers.css              (../shared/preferences/containers.css)
   skin/classic/browser/fxa/default-avatar.svg                  (../shared/fxa/default-avatar.svg)
   skin/classic/browser/fxa/logo.png                            (../shared/fxa/logo.png)
   skin/classic/browser/fxa/logo@2x.png                         (../shared/fxa/logo@2x.png)
--- a/browser/themes/windows/preferences/in-content-new/preferences.css
+++ b/browser/themes/windows/preferences/in-content-new/preferences.css
@@ -1,48 +1,14 @@
 /* - This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this file,
    - You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 %include ../../../shared/incontentprefs/preferences.inc.css
 
-@media (-moz-windows-default-theme: 0) {
-  #category-general > .category-icon {
-    list-style-image: url("chrome://browser/skin/preferences/in-content-new/icons.svg#general-native");
-  }
-
-  #category-privacy > .category-icon {
-    list-style-image: url("chrome://browser/skin/preferences/in-content-new/icons.svg#security-native");
-  }
-
-  #category-sync > .category-icon {
-    list-style-image: url("chrome://browser/skin/preferences/in-content-new/icons.svg#sync-native");
-  }
-
-  #category-search > .category-icon {
-    list-style-image: url("chrome://browser/skin/preferences/in-content-new/icons.svg#search-native");
-  }
-}
-
 .actionsMenu > .menulist-label-box > .menulist-icon {
   margin-inline-end: 9px;
 }
 
 textbox + button,
 filefield + button {
   margin-inline-start: -4px;
 }
-
-#advancedPrefs {
-  padding-bottom: 0; /* override padding from normal preferences.css */
-}
-
-#fxaProfileImage {
-  -moz-user-focus: normal;
-}
-
-/**
- * Dialog
- */
-
-#dialogTitle {
-  font-size: 1em;
-}
--- a/build/sanitizers/lsan_suppressions.txt
+++ b/build/sanitizers/lsan_suppressions.txt
@@ -21,16 +21,19 @@ leak:pixman_implementation_lookup_compos
 # Bug 987918 - Font shutdown leaks when CLEANUP_MEMORY is not enabled.
 leak:libfontconfig.so
 leak:GI___strdup
 # The symbol is really __GI___strdup, but if you have the leading _, it doesn't suppress it.
 
 # Bug 1078015 - If the process terminates during a PR_Sleep, LSAN  detects a leak
 leak:PR_Sleep
 
+# Bug 1363976 - Stylo holds some global data alive forever.
+leak:style::gecko::global_style_data
+
 ###
 ### Many leaks only affect some test suites.  The suite annotations are not checked.
 ###
 
 # Bug 979928 - WebRTC leaks in different mochitest suites.
 leak:NR_reg_init
 # nr_reg_local_init should be redundant with NR_reg_init, but on Aurora
 # we get fewer stack frames for some reason.
--- a/config/config.mk
+++ b/config/config.mk
@@ -323,18 +323,18 @@ endif # CLANG_CL
 
 # Use warnings-as-errors if ALLOW_COMPILER_WARNINGS is not set to 1 (which
 # includes the case where it's undefined).
 ifneq (1,$(ALLOW_COMPILER_WARNINGS))
 CXXFLAGS += $(WARNINGS_AS_ERRORS)
 CFLAGS   += $(WARNINGS_AS_ERRORS)
 endif # ALLOW_COMPILER_WARNINGS
 
-COMPILE_CFLAGS	= $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(OS_INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CFLAGS) $(_DEPEND_CFLAGS) $(CFLAGS) $(MOZBUILD_CFLAGS)
-COMPILE_CXXFLAGS = $(if $(DISABLE_STL_WRAPPING),,$(STL_FLAGS)) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(OS_INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CXXFLAGS) $(_DEPEND_CFLAGS) $(CXXFLAGS) $(MOZBUILD_CXXFLAGS)
+COMPILE_CFLAGS	= $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(OS_INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CFLAGS) $(_DEPEND_CFLAGS) $(CFLAGS) $(MOZBUILD_CFLAGS) $(MK_COMPILE_DEFINES)
+COMPILE_CXXFLAGS = $(if $(DISABLE_STL_WRAPPING),,$(STL_FLAGS)) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(OS_INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_COMPILE_CXXFLAGS) $(_DEPEND_CFLAGS) $(CXXFLAGS) $(MOZBUILD_CXXFLAGS) $(MK_COMPILE_DEFINES)
 COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS)
 COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS)
 ASFLAGS += $(MOZBUILD_ASFLAGS)
 
 ifndef CROSS_COMPILE
 HOST_CFLAGS += $(RTL_FLAGS)
 endif
 
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -920,17 +920,17 @@ define RUN_CARGO
 $(if $(findstring n,$(filter-out --%, $(MAKEFLAGS))),,+)env $(environment_cleaner) $(rust_unlock_unstable) $(rustflags_override) $(sccache_wrap) \
 	CARGO_TARGET_DIR=$(CARGO_TARGET_DIR) \
 	RUSTC=$(RUSTC) \
 	MOZ_SRC=$(topsrcdir) \
 	MOZ_DIST=$(ABS_DIST) \
 	LIBCLANG_PATH="$(MOZ_LIBCLANG_PATH)" \
 	CLANG_PATH="$(MOZ_CLANG_PATH)" \
 	PKG_CONFIG_ALLOW_CROSS=1 \
-	RUST_BACKTRACE=1 \
+	RUST_BACKTRACE=full \
 	MOZ_TOPOBJDIR=$(topobjdir) \
 	$(2) \
 	$(CARGO) $(1) $(cargo_build_flags)
 endef
 
 # This function is intended to be called by:
 #
 #   $(call CARGO_BUILD,EXTRA_ENV_VAR1=X EXTRA_ENV_VAR2=Y ...)
--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js
+++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js
@@ -74,17 +74,17 @@ function* ifTestingSupported() {
   yield once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
   ok(true, "The full-sized screenshot was displayed for the item at index 0.");
 
   yield teardown(panel);
   finish();
 }
 
 function waitForMozSetImageElement(panel) {
-  let deferred = promise.defer();
+  let deferred = defer();
   panel._onMozSetImageElement = deferred.resolve;
   return deferred.promise;
 }
 
 function sameArray(a, b) {
   if (a.length != b.length) {
     return false;
   }
--- a/devtools/client/canvasdebugger/test/head.js
+++ b/devtools/client/canvasdebugger/test/head.js
@@ -4,16 +4,17 @@
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 var { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
 var Services = require("Services");
 var promise = require("promise");
+const defer = require("devtools/shared/defer");
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { DebuggerClient } = require("devtools/shared/client/main");
 var { DebuggerServer } = require("devtools/server/main");
 var { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");
 var { CanvasFront } = require("devtools/shared/fronts/canvas");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var flags = require("devtools/shared/flags");
 var { TargetFactory } = require("devtools/client/framework/target");
@@ -67,17 +68,17 @@ registerCleanupFunction(() => {
 function loadFrameScripts() {
   mm = gBrowser.selectedBrowser.messageManager;
   mm.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
 }
 
 function addTab(aUrl, aWindow) {
   info("Adding tab: " + aUrl);
 
-  let deferred = promise.defer();
+  let deferred = defer();
   let targetWindow = aWindow || window;
   let targetBrowser = targetWindow.gBrowser;
 
   targetWindow.focus();
   let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
   let linkedBrowser = tab.linkedBrowser;
 
   BrowserTestUtils.browserLoaded(linkedBrowser)
@@ -87,17 +88,17 @@ function addTab(aUrl, aWindow) {
     });
 
   return deferred.promise;
 }
 
 function removeTab(aTab, aWindow) {
   info("Removing tab.");
 
-  let deferred = promise.defer();
+  let deferred = defer();
   let targetWindow = aWindow || window;
   let targetBrowser = targetWindow.gBrowser;
   let tabContainer = targetBrowser.tabContainer;
 
   tabContainer.addEventListener("TabClose", function (aEvent) {
     info("Tab removed and finished closing.");
     deferred.resolve();
   }, {once: true});
@@ -143,17 +144,17 @@ function isTestingSupported() {
   info("This test requires WebGL support.");
   info("Apparently, WebGL is" + (supported ? "" : " not") + " supported.");
   return supported;
 }
 
 function once(aTarget, aEventName, aUseCapture = false) {
   info("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
 
-  let deferred = promise.defer();
+  let deferred = defer();
 
   for (let [add, remove] of [
     ["on", "off"], // Use event emitter before DOM events for consistency
     ["addEventListener", "removeEventListener"],
     ["addListener", "removeListener"]
   ]) {
     if ((add in aTarget) && (remove in aTarget)) {
       aTarget[add](aEventName, function onEvent(...aArgs) {
@@ -164,17 +165,17 @@ function once(aTarget, aEventName, aUseC
       break;
     }
   }
 
   return deferred.promise;
 }
 
 function waitForTick() {
-  let deferred = promise.defer();
+  let deferred = defer();
   executeSoon(deferred.resolve);
   return deferred.promise;
 }
 
 function navigateInHistory(aTarget, aDirection, aWaitForTargetEvent = "navigate") {
   executeSoon(() => content.history[aDirection]());
   return once(aTarget, aWaitForTargetEvent);
 }
@@ -251,17 +252,17 @@ function teardown({target}) {
   });
 }
 
 /**
  * Takes a string `script` and evaluates it directly in the content
  * in potentially a different process.
  */
 function evalInDebuggee(script) {
-  let deferred = promise.defer();
+  let deferred = defer();
 
   if (!mm) {
     throw new Error("`loadFrameScripts()` must be called when using MessageManager.");
   }
 
   let id = generateUUID().toString();
   mm.sendAsyncMessage("devtools:test:eval", { script: script, id: id });
   mm.addMessageListener("devtools:test:eval:response", handler);
@@ -290,14 +291,14 @@ function getSourceActor(aSources, aURL) 
  *        Invoked once in a while until it returns true.
  * @param number interval [optional]
  *        How often the predicate is invoked, in milliseconds.
  */
 function* waitUntil(predicate, interval = 10) {
   if (yield predicate()) {
     return Promise.resolve(true);
   }
-  let deferred = Promise.defer();
+  let deferred = defer();
   setTimeout(function () {
     waitUntil(predicate).then(() => deferred.resolve(true));
   }, interval);
   return deferred.promise;
 }
--- a/devtools/client/debugger/test/mochitest/addon-source/browser_dbg_addon3/lib/main.js
+++ b/devtools/client/debugger/test/mochitest/addon-source/browser_dbg_addon3/lib/main.js
@@ -1,13 +1,20 @@
 var { Cc, Ci } = require("chrome");
-var { once } = require("sdk/system/events");
+var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
 
-var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
 var observer = {
   observe: function () {
     debugger;
   }
 };
 
-once("sdk:loader:destroy", () => observerService.removeObserver(observer, "debuggerAttached"));
+observerService.addObserver(observer, "debuggerAttached");
 
-observerService.addObserver(observer, "debuggerAttached");
+var sdkLoaderDestroyObserver = {
+  observe: function () {
+    // Remove all observers on sdk:loader:destroy
+    observerService.removeObserver(observer, "debuggerAttached");
+    observerService.removeObserver(sdkLoaderDestroyObserver, "sdk:loader:destroy");
+  }
+};
+
+observerService.addObserver(sdkLoaderDestroyObserver, "sdk:loader:destroy");
--- a/devtools/client/dom/dom-panel.js
+++ b/devtools/client/dom/dom-panel.js
@@ -1,20 +1,19 @@
 /* -*- indent-tabs-mode: nil; 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 } = require("chrome");
-const defer = require("devtools/shared/defer");
 const { ObjectClient } = require("devtools/shared/client/main");
 
-const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { Task } = require("devtools/shared/task");
 
 /**
  * This object represents DOM panel. It's responsibility is to
  * render Document Object Model of the current debugger target.
  */
 function DomPanel(iframeWindow, toolbox) {
@@ -37,17 +36,17 @@ DomPanel.prototype = {
    * @return object
    *         A promise that is resolved when the DOM panel completes opening.
    */
   open: Task.async(function* () {
     if (this._opening) {
       return this._opening;
     }
 
-    let deferred = promise.defer();
+    let deferred = defer();
     this._opening = deferred.promise;
 
     // Local monitoring needs to make the target remote.
     if (!this.target.isRemote) {
       yield this.target.makeRemote();
     }
 
     this.initialize();
@@ -77,17 +76,17 @@ DomPanel.prototype = {
     this.shouldRefresh = true;
   },
 
   destroy: Task.async(function* () {
     if (this._destroying) {
       return this._destroying;
     }
 
-    let deferred = promise.defer();
+    let deferred = defer();
     this._destroying = deferred.promise;
 
     this.target.off("navigate", this.onTabNavigated);
     this._toolbox.off("select", this.onPanelVisibilityChange);
 
     this.emit("destroyed");
 
     deferred.resolve();
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -544,27 +544,52 @@ Toolbox.prototype = {
       return this._sourceMapService;
     }
     // Uses browser loader to access the `Worker` global.
     let service = this.browserRequire("devtools/client/shared/source-map/index");
 
     // Provide a wrapper for the service that reports errors more nicely.
     this._sourceMapService = new Proxy(service, {
       get: (target, name) => {
-        if (name === "getOriginalURLs") {
-          return (urlInfo) => {
-            return target.getOriginalURLs(urlInfo)
-              .catch(text => {
-                let message = L10N.getFormatStr("toolbox.sourceMapFailure",
-                                                text, urlInfo.url, urlInfo.sourceMapURL);
-                this.target.logErrorInPage(message, "source map");
-              });
-          };
+        switch (name) {
+          case "getOriginalURLs":
+            return (urlInfo) => {
+              return target.getOriginalURLs(urlInfo)
+                .catch(text => {
+                  let message = L10N.getFormatStr("toolbox.sourceMapFailure",
+                                                  text, urlInfo.url,
+                                                  urlInfo.sourceMapURL);
+                  this.target.logErrorInPage(message, "source map");
+                  // It's ok to swallow errors here, because a null
+                  // result just means that no source map was found.
+                  return null;
+                });
+            };
+
+          case "getOriginalSourceText":
+            return (originalSource) => {
+              return target.getOriginalSourceText(originalSource)
+                .catch(text => {
+                  let message = L10N.getFormatStr("toolbox.sourceMapSourceFailure",
+                                                  text, originalSource.url);
+                  this.target.logErrorInPage(message, "source map");
+                  // Also replace the result with the error text.
+                  // Note that this result has to have the same form
+                  // as whatever the upstream getOriginalSourceText
+                  // returns.
+                  return {
+                    text: message,
+                    contentType: "text/plain",
+                  };
+                });
+            };
+
+          default:
+            return target[name];
         }
-        return target[name];
       },
     });
 
     this._sourceMapService.startSourceMapWorker(SOURCE_MAP_WORKER);
     return this._sourceMapService;
   },
 
   /**
--- a/devtools/client/jsonview/test/head.js
+++ b/devtools/client/jsonview/test/head.js
@@ -30,17 +30,17 @@ registerCleanupFunction(() => {
  *   The maximum number of milliseconds allowed before the initialization of the
  *   JSON Viewer once the tab has been loaded. If exceeded, the initialization
  *   will be considered to have failed, and the returned promise will be rejected.
  *   If this parameter is not passed or is negative, it will be ignored.
  */
 function addJsonViewTab(url, timeout = -1) {
   info("Adding a new JSON tab with URL: '" + url + "'");
 
-  let deferred = promise.defer();
+  let deferred = defer();
   addTab(url).then(tab => {
     let browser = tab.linkedBrowser;
 
     // Load devtools/shared/frame-script-utils.js
     getFrameScript();
 
     // Load frame script with helpers for JSON View tests.
     let rootDir = getRootDirectory(gTestPath);
@@ -146,17 +146,17 @@ function sendString(str, selector) {
     selector: selector,
     str: str
   };
 
   return executeInContent("Test:JsonView:SendString", data);
 }
 
 function waitForTime(delay) {
-  let deferred = promise.defer();
+  let deferred = defer();
   setTimeout(deferred.resolve, delay);
   return deferred.promise;
 }
 
 function waitForFilter() {
   return executeInContent("Test:JsonView:WaitForFilter");
 }
 
--- a/devtools/client/locales/en-US/toolbox.properties
+++ b/devtools/client/locales/en-US/toolbox.properties
@@ -179,8 +179,15 @@ toolbox.closebutton.tooltip=Close Develo
 toolbox.allToolsButton.tooltip=Select another tool
 
 # LOCALIZATION NOTE (toolbox.sourceMapFailure): This is shown in the web console
 # when there is a failure to fetch or parse a source map.
 # The text of the error: %1$S
 # The URL that caused DevTools to try to fetch a source map: %2$S
 # The URL of the source map itself: %3$S
 toolbox.sourceMapFailure=Source map error: %1$S\nResource URL: %2$S\nSource Map URL: %3$S
+
+# LOCALIZATION NOTE (toolbox.sourceMapSourceFailure): This is shown in
+# the web console when there is a failure to fetch or parse an
+# original source that was mentioned in a source map.
+# The text of the error: %1$S
+# The URL of the source: %2$S
+toolbox.sourceMapSourceFailure=Error while fetching an original source: %1$S\nSource URL: %2$S
--- a/devtools/client/netmonitor/test/browser_net_brotli.js
+++ b/devtools/client/netmonitor/test/browser_net_brotli.js
@@ -35,17 +35,17 @@ add_task(function* () {
     document,
     getDisplayedRequests(store.getState()),
     getSortedRequests(store.getState()).get(0),
     "GET", HTTPS_CONTENT_TYPE_SJS + "?fmt=br", {
       status: 200,
       statusText: "Connected",
       type: "plain",
       fullMimeType: "text/plain",
-      transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 10),
+      transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 60),
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 64),
       time: true
     });
 
   wait = waitForDOM(document, ".CodeMirror-code");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".network-details-panel-toggle"));
   EventUtils.sendMouseEvent({ type: "click" },
--- a/devtools/client/netmonitor/test/browser_net_content-type.js
+++ b/devtools/client/netmonitor/test/browser_net_content-type.js
@@ -124,17 +124,17 @@ add_task(function* () {
     getSortedRequests(store.getState()).get(6),
     "GET",
     CONTENT_TYPE_SJS + "?fmt=gzip",
     {
       status: 200,
       statusText: "OK",
       type: "plain",
       fullMimeType: "text/plain",
-      transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 73),
+      transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 324),
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 10.73),
       time: true
     }
   );
 
   yield selectIndexAndWaitForSourceEditor(0);
   yield testResponseTab("xml");
 
--- a/devtools/client/netmonitor/test/browser_net_filter-flags.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-flags.js
@@ -251,24 +251,24 @@ add_task(function* () {
   setFreetextFilter("size:10989");
   testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
 
   // Testing the upper bound
   setFreetextFilter("size:11.804k");
   testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
 
   // Test transferred
-  setFreetextFilter("transferred:0");
-  testContents([0, 0, 0, 0, 1, 1, 1, 1, 0, 1]);
+  setFreetextFilter("transferred:200");
+  testContents([0, 0, 0, 0, 1, 1, 1, 1, 0, 0]);
 
-  setFreetextFilter("transferred:1");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+  setFreetextFilter("transferred:234");
+  testContents([1, 0, 1, 0, 0, 0, 0, 0, 0, 1]);
 
-  setFreetextFilter("transferred:34");
-  testContents([0, 0, 1, 1, 0, 0, 0, 0, 0, 0]);
+  setFreetextFilter("transferred:248");
+  testContents([0, 0, 1, 1, 0, 0, 0, 0, 0, 1]);
 
   // Test larger-than
   setFreetextFilter("larger-than:-1");
   testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
   setFreetextFilter("larger-than:0");
   testContents([1, 1, 1, 1, 0, 0, 0, 0, 1, 0]);
 
@@ -283,24 +283,24 @@ add_task(function* () {
 
   setFreetextFilter("larger-than:10.732k");
   testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test transferred-larger-than
   setFreetextFilter("transferred-larger-than:-1");
   testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
-  setFreetextFilter("transferred-larger-than:0");
-  testContents([1, 1, 1, 1, 0, 0, 0, 0, 1, 0]);
+  setFreetextFilter("transferred-larger-than:214");
+  testContents([1, 1, 1, 1, 0, 0, 0, 0, 1, 1]);
 
-  setFreetextFilter("transferred-larger-than:33");
-  testContents([0, 0, 1, 1, 0, 0, 0, 0, 1, 0]);
+  setFreetextFilter("transferred-larger-than:247");
+  testContents([0, 1, 1, 1, 0, 0, 0, 0, 1, 0]);
 
-  setFreetextFilter("transferred-larger-than:34");
-  testContents([0, 0, 0, 0, 0, 0, 0, 0, 1, 0]);
+  setFreetextFilter("transferred-larger-than:248");
+  testContents([0, 1, 0, 1, 0, 0, 0, 0, 1, 0]);
 
   setFreetextFilter("transferred-larger-than:10.73k");
   testContents([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
 
   // Test cause
   setFreetextFilter("cause:xhr");
   testContents([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
--- a/devtools/client/netmonitor/test/browser_net_simple-request-data.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request-data.js
@@ -251,17 +251,17 @@ function test() {
                requestItem.transferredSize &&
                requestItem.contentSize &&
                requestItem.mimeType &&
                requestItem.responseContent;
       });
 
       let requestItem = getSortedRequests(store.getState()).get(0);
 
-      is(requestItem.transferredSize, "12",
+      is(requestItem.transferredSize, "342",
         "The transferredSize data has an incorrect value.");
       is(requestItem.contentSize, "12",
         "The contentSize data has an incorrect value.");
       is(requestItem.mimeType, "text/plain; charset=utf-8",
         "The mimeType data has an incorrect value.");
 
       ok(requestItem.responseContent,
         "There should be a responseContent data available.");
@@ -282,17 +282,17 @@ function test() {
         document,
         getDisplayedRequests(store.getState()),
         requestItem,
         "GET",
         SIMPLE_SJS,
         {
           type: "plain",
           fullMimeType: "text/plain; charset=utf-8",
-          transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
+          transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 342),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
         }
       );
     });
 
     expectEvent(EVENTS.UPDATING_EVENT_TIMINGS, async () => {
       await waitUntil(() => {
         let requestItem = getSortedRequests(store.getState()).get(0);
--- a/devtools/client/netmonitor/test/browser_net_sort-01.js
+++ b/devtools/client/netmonitor/test/browser_net_sort-01.js
@@ -162,79 +162,79 @@ add_task(function* () {
         getDisplayedRequests(store.getState()),
         getSortedRequests(store.getState()).get(order[i]),
         "GET1", SORTING_SJS + "?index=1", {
           fuzzyUrl: true,
           status: 101,
           statusText: "Meh",
           type: "1",
           fullMimeType: "text/1",
-          transferred: L10N.getStr("networkMenu.sizeUnavailable"),
+          transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 198),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0),
           time: true
         });
     }
     for (let i = 0, len = order.length / 5; i < len; i++) {
       verifyRequestItemTarget(
         document,
         getDisplayedRequests(store.getState()),
         getSortedRequests(store.getState()).get(order[i + len]),
         "GET2", SORTING_SJS + "?index=2", {
           fuzzyUrl: true,
           status: 200,
           statusText: "Meh",
           type: "2",
           fullMimeType: "text/2",
-          transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19),
+          transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 217),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19),
           time: true
         });
     }
     for (let i = 0, len = order.length / 5; i < len; i++) {
       verifyRequestItemTarget(
         document,
         getDisplayedRequests(store.getState()),
         getSortedRequests(store.getState()).get(order[i + len * 2]),
         "GET3", SORTING_SJS + "?index=3", {
           fuzzyUrl: true,
           status: 300,
           statusText: "Meh",
           type: "3",
           fullMimeType: "text/3",
-          transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
+          transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 227),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
           time: true
         });
     }
     for (let i = 0, len = order.length / 5; i < len; i++) {
       verifyRequestItemTarget(
         document,
         getDisplayedRequests(store.getState()),
         getSortedRequests(store.getState()).get(order[i + len * 3]),
         "GET4", SORTING_SJS + "?index=4", {
           fuzzyUrl: true,
           status: 400,
           statusText: "Meh",
           type: "4",
           fullMimeType: "text/4",
-          transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39),
+          transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 237),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39),
           time: true
         });
     }
     for (let i = 0, len = order.length / 5; i < len; i++) {
       verifyRequestItemTarget(
         document,
         getDisplayedRequests(store.getState()),
         getSortedRequests(store.getState()).get(order[i + len * 4]),
         "GET5", SORTING_SJS + "?index=5", {
           fuzzyUrl: true,
           status: 500,
           statusText: "Meh",
           type: "5",
           fullMimeType: "text/5",
-          transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49),
+          transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 247),
           size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49),
           time: true
         });
     }
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_sort-02.js
+++ b/devtools/client/netmonitor/test/browser_net_sort-02.js
@@ -245,70 +245,70 @@ add_task(function* () {
       getDisplayedRequests(store.getState()),
       getSortedRequests(store.getState()).get(a),
       "GET1", SORTING_SJS + "?index=1", {
         fuzzyUrl: true,
         status: 101,
         statusText: "Meh",
         type: "1",
         fullMimeType: "text/1",
-        transferred: L10N.getStr("networkMenu.sizeUnavailable"),
+        transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 198),
         size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0),
         time: true
       });
     verifyRequestItemTarget(
       document,
       getDisplayedRequests(store.getState()),
       getSortedRequests(store.getState()).get(b),
       "GET2", SORTING_SJS + "?index=2", {
         fuzzyUrl: true,
         status: 200,
         statusText: "Meh",
         type: "2",
         fullMimeType: "text/2",
-        transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19),
+        transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 217),
         size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 19),
         time: true
       });
     verifyRequestItemTarget(
       document,
       getDisplayedRequests(store.getState()),
       getSortedRequests(store.getState()).get(c),
       "GET3", SORTING_SJS + "?index=3", {
         fuzzyUrl: true,
         status: 300,
         statusText: "Meh",
         type: "3",
         fullMimeType: "text/3",
-        transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
+        transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 227),
         size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
         time: true
       });
     verifyRequestItemTarget(
       document,
       getDisplayedRequests(store.getState()),
       getSortedRequests(store.getState()).get(d),
       "GET4", SORTING_SJS + "?index=4", {
         fuzzyUrl: true,
         status: 400,
         statusText: "Meh",
         type: "4",
         fullMimeType: "text/4",
-        transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39),
+        transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 237),
         size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 39),
         time: true
       });
     verifyRequestItemTarget(
       document,
       getDisplayedRequests(store.getState()),
       getSortedRequests(store.getState()).get(e),
       "GET5", SORTING_SJS + "?index=5", {
         fuzzyUrl: true,
         status: 500,
         statusText: "Meh",
         type: "5",
         fullMimeType: "text/5",
-        transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49),
+        transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 247),
         size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 49),
         time: true
       });
   }
 });
--- a/devtools/client/performance/modules/widgets/graphs.js
+++ b/devtools/client/performance/modules/widgets/graphs.js
@@ -8,17 +8,17 @@
  */
 
 const { Task } = require("devtools/shared/task");
 const { Heritage } = require("devtools/client/shared/widgets/view-helpers");
 const LineGraphWidget = require("devtools/client/shared/widgets/LineGraphWidget");
 const MountainGraphWidget = require("devtools/client/shared/widgets/MountainGraphWidget");
 const { CanvasGraphUtils } = require("devtools/client/shared/widgets/Graphs");
 
-const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const EventEmitter = require("devtools/shared/event-emitter");
 
 const { colorUtils } = require("devtools/shared/css/color");
 const { getColor } = require("devtools/client/shared/theme");
 const ProfilerGlobal = require("devtools/client/performance/modules/global");
 const { MarkersOverview } = require("devtools/client/performance/modules/widgets/markers-overview");
 const { createTierGraphDataFromFrameNode } = require("devtools/client/performance/modules/logic/jit");
 
@@ -214,17 +214,17 @@ GraphsController.prototype = {
     yield (this._rendering && this._rendering.promise);
 
     // Check after yielding to ensure we're not tearing down,
     // as this can create a race condition in tests
     if (this._destroyed) {
       return;
     }
 
-    this._rendering = promise.defer();
+    this._rendering = defer();
     for (let graph of (yield this._getEnabled())) {
       yield graph.setPerformanceData(recordingData, resolution);
       this.emit("rendered", graph.graphName);
     }
     this._rendering.resolve();
   }),
 
   /**
--- a/devtools/client/performance/panel.js
+++ b/devtools/client/performance/panel.js
@@ -1,18 +1,18 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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 { Task } = require("devtools/shared/task");
+const defer = require("devtools/shared/defer");
 
-loader.lazyRequireGetter(this, "promise");
 loader.lazyRequireGetter(this, "EventEmitter",
   "devtools/shared/event-emitter");
 
 function PerformancePanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this.toolbox = toolbox;
 
   EventEmitter.decorate(this);
@@ -27,17 +27,17 @@ PerformancePanel.prototype = {
    * @return object
    *         A promise that is resolved when the Performance tool
    *         completes opening.
    */
   open: Task.async(function* () {
     if (this._opening) {
       return this._opening;
     }
-    let deferred = promise.defer();
+    let deferred = defer();
     this._opening = deferred.promise;
 
     this.panelWin.gToolbox = this.toolbox;
     this.panelWin.gTarget = this.target;
     this._checkRecordingStatus = this._checkRecordingStatus.bind(this);
 
     // Actor is already created in the toolbox; reuse
     // the same front, and the toolbox will also initialize the front,
--- a/devtools/client/performance/performance-controller.js
+++ b/devtools/client/performance/performance-controller.js
@@ -35,16 +35,17 @@ var Waterfall = React.createFactory(requ
 var JITOptimizationsView = React.createFactory(require("devtools/client/performance/components/jit-optimizations"));
 var RecordingControls = React.createFactory(require("devtools/client/performance/components/recording-controls"));
 var RecordingButton = React.createFactory(require("devtools/client/performance/components/recording-button"));
 var RecordingList = React.createFactory(require("devtools/client/performance/components/recording-list"));
 var RecordingListItem = React.createFactory(require("devtools/client/performance/components/recording-list-item"));
 
 var Services = require("Services");
 var promise = require("promise");
+const defer = require("devtools/shared/defer");
 var EventEmitter = require("devtools/shared/event-emitter");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var flags = require("devtools/shared/flags");
 var system = require("devtools/shared/system");
 
 // Logic modules
 /* exported L10N, PerformanceTelemetry, TIMELINE_BLUEPRINT, RecordingUtils,
    PerformanceUtils, OptimizationsGraph, GraphsController,
@@ -530,17 +531,17 @@ var PerformanceController = {
    * Takes a PerformanceRecording and a state, and waits for
    * the event to be emitted from the front for that recording.
    *
    * @param {PerformanceRecordingFront} recording
    * @param {string} expectedState
    * @return {Promise}
    */
   waitForStateChangeOnRecording: Task.async(function* (recording, expectedState) {
-    let deferred = promise.defer();
+    let deferred = defer();
     this.on(EVENTS.RECORDING_STATE_CHANGE, function handler(state, model) {
       if (state === expectedState && model === recording) {
         this.off(EVENTS.RECORDING_STATE_CHANGE, handler);
         deferred.resolve();
       }
     });
     yield deferred.promise;
   }),
--- a/devtools/client/performance/test/browser_perf-recordings-io-03.js
+++ b/devtools/client/performance/test/browser_perf-recordings-io-03.js
@@ -33,17 +33,17 @@ var test = Task.async(function* () {
 function getUnicodeConverter() {
   let className = "@mozilla.org/intl/scriptableunicodeconverter";
   let converter = Cc[className].createInstance(Ci.nsIScriptableUnicodeConverter);
   converter.charset = "UTF-8";
   return converter;
 }
 
 function asyncCopy(data, file) {
-  let deferred = Promise.defer();
+  let deferred = defer();
 
   let string = JSON.stringify(data);
   let inputStream = getUnicodeConverter().convertToInputStream(string);
   let outputStream = FileUtils.openSafeFileOutputStream(file);
 
   NetUtil.asyncCopy(inputStream, outputStream, status => {
     if (!Components.isSuccessCode(status)) {
       deferred.reject(new Error("Could not save data to file."));
--- a/devtools/client/performance/test/browser_perf-recordings-io-04.js
+++ b/devtools/client/performance/test/browser_perf-recordings-io-04.js
@@ -155,17 +155,17 @@ var test = Task.async(function* () {
 function getUnicodeConverter() {
   let className = "@mozilla.org/intl/scriptableunicodeconverter";
   let converter = Cc[className].createInstance(Ci.nsIScriptableUnicodeConverter);
   converter.charset = "UTF-8";
   return converter;
 }
 
 function asyncCopy(data, file) {
-  let deferred = Promise.defer();
+  let deferred = defer();
 
   let string = JSON.stringify(data);
   let inputStream = getUnicodeConverter().convertToInputStream(string);
   let outputStream = FileUtils.openSafeFileOutputStream(file);
 
   NetUtil.asyncCopy(inputStream, outputStream, status => {
     if (!Components.isSuccessCode(status)) {
       deferred.reject(new Error("Could not save data to file."));
--- a/devtools/client/performance/test/browser_perf-recordings-io-06.js
+++ b/devtools/client/performance/test/browser_perf-recordings-io-06.js
@@ -119,17 +119,17 @@ var test = Task.async(function* () {
 function getUnicodeConverter() {
   let className = "@mozilla.org/intl/scriptableunicodeconverter";
   let converter = Cc[className].createInstance(Ci.nsIScriptableUnicodeConverter);
   converter.charset = "UTF-8";
   return converter;
 }
 
 function asyncCopy(data, file) {
-  let deferred = Promise.defer();
+  let deferred = defer();
 
   let string = JSON.stringify(data);
   let inputStream = getUnicodeConverter().convertToInputStream(string);
   let outputStream = FileUtils.openSafeFileOutputStream(file);
 
   NetUtil.asyncCopy(inputStream, outputStream, status => {
     if (!Components.isSuccessCode(status)) {
       deferred.reject(new Error("Could not save data to file."));
--- a/devtools/client/performance/test/browser_timeline-waterfall-workers.js
+++ b/devtools/client/performance/test/browser_timeline-waterfall-workers.js
@@ -68,17 +68,17 @@ function testWorkerMarkerUI(node) {
 }
 
 /**
  * Takes a string `script` and evaluates it directly in the content
  * in potentially a different process.
  */
 function evalInDebuggee(script) {
   let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
-  let deferred = Promise.defer();
+  let deferred = defer();
 
   if (!mm) {
     throw new Error("`loadFrameScripts()` must be called when using MessageManager.");
   }
 
   let id = generateUUID().toString();
   mm.sendAsyncMessage("devtools:test:eval", { script: script, id: id });
   mm.addMessageListener("devtools:test:eval:response", handler);
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -13,17 +13,16 @@ pref("devtools.devedition.promo.url", "h
   pref("devtools.devedition.promo.enabled", false);
 #endif
 
 // DevTools development workflow
 pref("devtools.loader.hotreload", false);
 
 // Developer toolbar preferences
 pref("devtools.toolbar.enabled", true);
-pref("devtools.toolbar.visible", false);
 
 // Enable DevTools WebIDE by default
 pref("devtools.webide.enabled", true);
 
 // Toolbox preferences
 pref("devtools.toolbox.footer.height", 250);
 pref("devtools.toolbox.sidebar.width", 500);
 pref("devtools.toolbox.host", "bottom");
--- a/devtools/client/scratchpad/scratchpad-panel.js
+++ b/devtools/client/scratchpad/scratchpad-panel.js
@@ -3,28 +3,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {Cu} = require("chrome");
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 
 
 function ScratchpadPanel(iframeWindow, toolbox) {
   let { Scratchpad } = iframeWindow;
   this._toolbox = toolbox;
   this.panelWin = iframeWindow;
   this.scratchpad = Scratchpad;
 
   Scratchpad.target = this.target;
   Scratchpad.hideMenu();
 
-  let deferred = promise.defer();
+  let deferred = defer();
   this._readyObserver = deferred.promise;
   Scratchpad.addObserver({
     onReady: function () {
       Scratchpad.removeObserver(this);
       deferred.resolve();
     }
   });
 
--- a/devtools/client/scratchpad/scratchpad.js
+++ b/devtools/client/scratchpad/scratchpad.js
@@ -47,16 +47,17 @@ const {require, loader} = Cu.import("res
 
 const Editor = require("devtools/client/sourceeditor/editor");
 const TargetFactory = require("devtools/client/framework/target").TargetFactory;
 const EventEmitter = require("devtools/shared/event-emitter");
 const {DevToolsWorker} = require("devtools/shared/worker/worker");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const flags = require("devtools/shared/flags");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const Services = require("Services");
 const {gDevTools} = require("devtools/client/framework/devtools");
 const {Heritage} = require("devtools/client/shared/widgets/view-helpers");
 
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
 const {ScratchpadManager} = require("resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 const {addDebuggerToGlobal} = require("resource://gre/modules/jsdebugger.jsm");
@@ -501,17 +502,17 @@ var Scratchpad = {
     }
     else {
       connection = ScratchpadWindow.consoleFor(this.browserWindow);
     }
 
     let evalOptions = { url: this.uniqueName };
 
     return connection.then(({ debuggerClient, webConsoleClient }) => {
-      let deferred = promise.defer();
+      let deferred = defer();
 
       webConsoleClient.evaluateJSAsync(aString, aResponse => {
         this.debuggerClient = debuggerClient;
         this.webConsoleClient = webConsoleClient;
         if (aResponse.error) {
           deferred.reject(aResponse);
         }
         else if (aResponse.exception !== null) {
@@ -544,17 +545,17 @@ var Scratchpad = {
    * Execute the selected text (if any) or the entire editor content in the
    * current context.
    *
    * @return Promise
    *         The promise for the script evaluation result.
    */
   run: function SP_run()
   {
-    let deferred = promise.defer();
+    let deferred = defer();
     let reject = aReason => deferred.reject(aReason);
 
     this.execute().then(([aString, aError, aResult]) => {
       let resolve = () => deferred.resolve([aString, aError, aResult]);
 
       if (aError) {
         this.writeAsErrorComment(aError).then(resolve, reject);
       }
@@ -571,17 +572,17 @@ var Scratchpad = {
    * Execute the selected text (if any) or the entire editor content in the
    * current context. The resulting object is inspected up in the sidebar.
    *
    * @return Promise
    *         The promise for the script evaluation result.
    */
   inspect: function SP_inspect()
   {
-    let deferred = promise.defer();
+    let deferred = defer();
     let reject = aReason => deferred.reject(aReason);
 
     this.execute().then(([aString, aError, aResult]) => {
       let resolve = () => deferred.resolve([aString, aError, aResult]);
 
       if (aError) {
         this.writeAsErrorComment(aError).then(resolve, reject);
       }
@@ -599,17 +600,17 @@ var Scratchpad = {
    * the page finishes loading. Note that this operation should be available
    * only in the content context.
    *
    * @return Promise
    *         The promise for the script evaluation result.
    */
   reloadAndRun: function SP_reloadAndRun()
   {
-    let deferred = promise.defer();
+    let deferred = defer();
 
     if (this.executionContext !== SCRATCHPAD_CONTEXT_CONTENT) {
       console.error(this.strings.
                     GetStringFromName("scratchpadContext.invalid"));
       return;
     }
 
     let target = TargetFactory.forTab(this.gBrowser.selectedTab);
@@ -627,17 +628,17 @@ var Scratchpad = {
    * the selected text, or at the end of the editor content if there is no
    * selected text.
    *
    * @return Promise
    *         The promise for the script evaluation result.
    */
   display: function SP_display()
   {
-    let deferred = promise.defer();
+    let deferred = defer();
     let reject = aReason => deferred.reject(aReason);
 
     this.execute().then(([aString, aError, aResult]) => {
       let resolve = () => deferred.resolve([aString, aError, aResult]);
 
       if (aError) {
         this.writeAsErrorComment(aError).then(resolve, reject);
       }
@@ -870,17 +871,17 @@ var Scratchpad = {
    *
    * @param any aValue
    *        The value to print.
    * @return Promise
    *         The promise that resolves after the value has been printed.
    */
   _writePrimitiveAsComment: function SP__writePrimitiveAsComment(aValue)
   {
-    let deferred = promise.defer();
+    let deferred = defer();
 
     if (aValue.type == "longString") {
       let client = this.webConsoleClient;
       client.longString(aValue).substring(0, aValue.length, aResponse => {
         if (aResponse.error) {
           reportError("display", aResponse);
           deferred.reject(aResponse);
         }
@@ -929,17 +930,17 @@ var Scratchpad = {
    *        The error object to write out the message and stack trace. It must
    *        contain an |exception| property with the actual error thrown, but it
    *        will often be the entire response of an evaluateJS request.
    * @return Promise
    *         The promise that indicates when writing the comment completes.
    */
   writeAsErrorComment: function SP_writeAsErrorComment(aError)
   {
-    let deferred = promise.defer();
+    let deferred = defer();
 
     if (VariablesView.isPrimitive({ value: aError.exception })) {
       let error = aError.exception;
       let type = error.type;
       if (type == "undefined" ||
           type == "null" ||
           type == "Infinity" ||
           type == "-Infinity" ||
@@ -2106,17 +2107,17 @@ ScratchpadTab.prototype = {
    *         The promise for the result of connecting to this tab or window.
    */
   connect: function ST_connect(aSubject)
   {
     if (this._connector) {
       return this._connector;
     }
 
-    let deferred = promise.defer();
+    let deferred = defer();
     this._connector = deferred.promise;
 
     let connectTimer = setTimeout(() => {
       deferred.reject({
         error: "timeout",
         message: Scratchpad.strings.GetStringFromName("connectionTimeout"),
       });
     }, REMOTE_TIMEOUT);
@@ -2257,17 +2258,17 @@ ScratchpadSidebar.prototype = {
    *        The object to inspect, which is the aEvalString evaluation result.
    * @return Promise
    *         A promise that will resolve once the sidebar is open.
    */
   open: function SS_open(aEvalString, aObject)
   {
     this.show();
 
-    let deferred = promise.defer();
+    let deferred = defer();
 
     let onTabReady = () => {
       if (this.variablesView) {
         this.variablesView.controller.releaseActors();
       }
       else {
         let window = this._sidebar.getWindowForTab("variablesview");
         let container = window.document.querySelector("#variables");
--- a/devtools/client/scratchpad/test/browser_scratchpad_reload_and_run.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_reload_and_run.js
@@ -48,17 +48,17 @@ function runTests()
   // body to make sure that the page has been reloaded and Scratchpad
   // code has been executed.
 
   sp.setContentContext();
   sp.setText(EDITOR_TEXT);
 
   let browser = gBrowser.selectedBrowser;
 
-  let deferred = promise.defer();
+  let deferred = defer();
   browser.addEventListener("DOMWindowCreated", function () {
     browser.contentWindow.addEventListener("foo", function () {
       is(browser.contentWindow.document.body.innerHTML, "Modified text",
         "After reloading, HTML is different.");
 
       Services.prefs.clearUserPref(DEVTOOLS_CHROME_ENABLED);
       deferred.resolve();
     }, {capture: true, once: true});
--- a/devtools/client/scratchpad/test/head.js
+++ b/devtools/client/scratchpad/test/head.js
@@ -8,16 +8,17 @@ const {NetUtil} = Cu.import("resource://
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
 const {ScratchpadManager} = Cu.import("resource://devtools/client/scratchpad/scratchpad-manager.jsm", {});
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const Services = require("Services");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const flags = require("devtools/shared/flags");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 
 
 var gScratchpadWindow; // Reference to the Scratchpad chrome window object
 
 flags.testing = true;
 registerCleanupFunction(() => {
   flags.testing = false;
 });
@@ -148,17 +149,17 @@ function createTempFile(aName, aContent,
  *          Expected code that will be in the scratchpad upon completion.
  *        - label
  *          The tests label which will be logged in the test runner output.
  * @return Promise
  *         The promise that will be resolved when all tests are finished.
  */
 function runAsyncTests(aScratchpad, aTests)
 {
-  let deferred = promise.defer();
+  let deferred = defer();
 
   (function runTest() {
     if (aTests.length) {
       let test = aTests.shift();
       aScratchpad.setText(test.code);
       aScratchpad[test.method]().then(function success() {
         is(aScratchpad.getText(), test.result, test.label);
         runTest();
--- a/devtools/client/shadereditor/shadereditor.js
+++ b/devtools/client/shadereditor/shadereditor.js
@@ -4,16 +4,17 @@
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 const {SideMenuWidget} = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
 const promise = require("promise");
+const defer = require("devtools/shared/defer");
 const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 const Tooltip = require("devtools/client/shared/widgets/tooltip/Tooltip");
 const Editor = require("devtools/client/sourceeditor/editor");
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const {Heritage, WidgetMethods, setNamedTimeout} =
   require("devtools/client/shared/widgets/view-helpers");
 const {Task} = require("devtools/shared/task");
@@ -420,17 +421,17 @@ var ShadersEditorsView = {
    * @return object
    *        Returns a promise that resolves to an editor instance
    */
   _getEditor: function (type) {
     if (this._editorPromises.has(type)) {
       return this._editorPromises.get(type);
     }
 
-    let deferred = promise.defer();
+    let deferred = defer();
     this._editorPromises.set(type, deferred.promise);
 
     // Initialize the source editor and store the newly created instance
     // in the ether of a resolved promise's value.
     let parent = $("#" + type + "-editor");
     let editor = new Editor(DEFAULT_EDITOR_CONFIG);
     editor.config.mode = Editor.modes[type];
 
--- a/devtools/client/shadereditor/test/browser_se_programs-blackbox-01.js
+++ b/devtools/client/shadereditor/test/browser_se_programs-blackbox-01.js
@@ -158,12 +158,12 @@ function getItemLabel(aPanel, aIndex) {
 }
 
 function getBlackBoxCheckbox(aPanel, aIndex) {
   return aPanel.panelWin.document.querySelectorAll(
     ".side-menu-widget-item-checkbox")[aIndex];
 }
 
 function once(aTarget, aEvent) {
-  let deferred = promise.defer();
+  let deferred = defer();
   aTarget.once(aEvent, deferred.resolve);
   return deferred.promise;
 }
--- a/devtools/client/shadereditor/test/head.js
+++ b/devtools/client/shadereditor/test/head.js
@@ -4,16 +4,17 @@
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 var { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 var { Task } = require("devtools/shared/task");
 
 var Services = require("Services");
 var promise = require("promise");
+const defer = require("devtools/shared/defer");
 var { gDevTools } = require("devtools/client/framework/devtools");
 var { DebuggerClient } = require("devtools/shared/client/main");
 var { DebuggerServer } = require("devtools/server/main");
 var { WebGLFront } = require("devtools/shared/fronts/webgl");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var flags = require("devtools/shared/flags");
 var { TargetFactory } = require("devtools/client/framework/target");
 var { Toolbox } = require("devtools/client/framework/toolbox");
@@ -63,17 +64,17 @@ function loadFrameScripts() {
     mm = gBrowser.selectedBrowser.messageManager;
     mm.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
   }
 }
 
 function addTab(aUrl, aWindow) {
   info("Adding tab: " + aUrl);
 
-  let deferred = promise.defer();
+  let deferred = defer();
   let targetWindow = aWindow || window;
   let targetBrowser = targetWindow.gBrowser;
 
   targetWindow.focus();
   let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
   let linkedBrowser = tab.linkedBrowser;
 
   BrowserTestUtils.browserLoaded(linkedBrowser).then(function () {
@@ -82,17 +83,17 @@ function addTab(aUrl, aWindow) {
   });
 
   return deferred.promise;
 }
 
 function removeTab(aTab, aWindow) {
   info("Removing tab.");
 
-  let deferred = promise.defer();
+  let deferred = defer();
   let targetWindow = aWindow || window;
   let targetBrowser = targetWindow.gBrowser;
   let tabContainer = targetBrowser.tabContainer;
 
   tabContainer.addEventListener("TabClose", function (aEvent) {
     info("Tab removed and finished closing.");
     deferred.resolve();
   }, {once: true});
@@ -123,17 +124,17 @@ function test() {
 
 function createCanvas() {
   return document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
 }
 
 function once(aTarget, aEventName, aUseCapture = false) {
   info("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
 
-  let deferred = promise.defer();
+  let deferred = defer();
 
   for (let [add, remove] of [
     ["on", "off"], // Use event emitter before DOM events for consistency
     ["addEventListener", "removeEventListener"],
     ["addListener", "removeListener"]
   ]) {
     if ((add in aTarget) && (remove in aTarget)) {
       aTarget[add](aEventName, function onEvent(...aArgs) {
@@ -147,25 +148,25 @@ function once(aTarget, aEventName, aUseC
   return deferred.promise;
 }
 
 // Hack around `once`, as that only resolves to a single (first) argument
 // and discards the rest. `onceSpread` is similar, except resolves to an
 // array of all of the arguments in the handler. These should be consolidated
 // into the same function, but many tests will need to be changed.
 function onceSpread(aTarget, aEvent) {
-  let deferred = promise.defer();
+  let deferred = defer();
   aTarget.once(aEvent, (...args) => deferred.resolve(args));
   return deferred.promise;
 }
 
 function observe(aNotificationName, aOwnsWeak = false) {
   info("Waiting for observer notification: '" + aNotificationName + ".");
 
-  let deferred = promise.defer();
+  let deferred = defer();
 
   Services.obs.addObserver(function onNotification(...aArgs) {
     Services.obs.removeObserver(onNotification, aNotificationName);
     deferred.resolve.apply(deferred, aArgs);
   }, aNotificationName, aOwnsWeak);
 
   return deferred.promise;
 }
@@ -271,17 +272,17 @@ function teardown(aPanel) {
 // first event since they're fired consecutively. This allows us to capture
 // all actors and returns an array containing them.
 //
 // Takes a `front` object that is an event emitter, the number of
 // programs that should be listened to and waited on, and an optional
 // `onAdd` function that calls with the entire actors array on program link
 function getPrograms(front, count, onAdd) {
   let actors = [];
-  let deferred = promise.defer();
+  let deferred = defer();
   front.on("program-linked", function onLink(actor) {
     if (actors.length !== count) {
       actors.push(actor);
       if (typeof onAdd === "function") onAdd(actors);
     }
     if (actors.length === count) {
       front.off("program-linked", onLink);
       deferred.resolve(actors);
--- a/devtools/client/shared/components/reps/reps.css
+++ b/devtools/client/shared/components/reps/reps.css
@@ -58,16 +58,20 @@
 }
 
 .objectBox-textNode,
 .objectBox-string,
 .objectBox-symbol {
   color: var(--string-color);
 }
 
+.objectBox-string a, .objectBox-string a:visited {
+  color: currentColor;
+}
+
 .objectBox-function,
 .objectBox-stackTrace,
 .objectBox-profile {
   color: var(--object-color);
 }
 
 .objectBox-Location {
   font-style: italic;
@@ -246,16 +250,20 @@ html[dir="rtl"] .arrow svg,
 .arrow svg:-moz-locale-dir(rtl) {
   transform: rotate(90deg);
 }
 
 .arrow.expanded.expanded svg {
   transform: rotate(0deg);
 }
 
-.object-label {
+.object-label, .object-label * {
   color: var(--theme-highlight-blue);
 }
 
+.tree .node .unavailable {
+  color: var(--theme-content-color3);
+}
+
 .lessen {
   opacity: 0.6;
 }
 
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -2,6864 +2,7089 @@
 	if(typeof exports === 'object' && typeof module === 'object')
 		module.exports = factory(require("devtools/client/shared/vendor/react"));
 	else if(typeof define === 'function' && define.amd)
 		define(["devtools/client/shared/vendor/react"], factory);
 	else {
 		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react")) : factory(root["devtools/client/shared/vendor/react"]);
 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
 	}
-})(this, function(__WEBPACK_EXTERNAL_MODULE_8__) {
+})(this, function(__WEBPACK_EXTERNAL_MODULE_0__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
-
+/******/
 /******/ 	// The require function
 /******/ 	function __webpack_require__(moduleId) {
-
+/******/
 /******/ 		// Check if module is in cache
-/******/ 		if(installedModules[moduleId])
+/******/ 		if(installedModules[moduleId]) {
 /******/ 			return installedModules[moduleId].exports;
-
+/******/ 		}
 /******/ 		// Create a new module (and put it into the cache)
 /******/ 		var module = installedModules[moduleId] = {
-/******/ 			exports: {},
-/******/ 			id: moduleId,
-/******/ 			loaded: false
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
 /******/ 		};
-
+/******/
 /******/ 		// Execute the module function
 /******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
-
+/******/
 /******/ 		// Flag the module as loaded
-/******/ 		module.loaded = true;
-
+/******/ 		module.l = true;
+/******/
 /******/ 		// Return the exports of the module
 /******/ 		return module.exports;
 /******/ 	}
-
-
+/******/
+/******/
 /******/ 	// expose the modules object (__webpack_modules__)
 /******/ 	__webpack_require__.m = modules;
-
+/******/
 /******/ 	// expose the module cache
 /******/ 	__webpack_require__.c = installedModules;
-
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, {
+/******/ 				configurable: false,
+/******/ 				enumerable: true,
+/******/ 				get: getter
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
 /******/ 	// __webpack_public_path__
 /******/ 	__webpack_require__.p = "/assets/build";
-
+/******/
 /******/ 	// Load entry module and return exports
-/******/ 	return __webpack_require__(0);
+/******/ 	return __webpack_require__(__webpack_require__.s = 26);
 /******/ })
 /************************************************************************/
 /******/ ([
 /* 0 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	const { MODE } = __webpack_require__(1);
-	const { REPS, getRep } = __webpack_require__(2);
-	const ObjectInspector = __webpack_require__(45);
-
-	const {
-	  parseURLEncodedText,
-	  parseURLParams,
-	  maybeEscapePropertyName,
-	  getGripPreviewItems
-	} = __webpack_require__(7);
-
-	module.exports = {
-	  REPS,
-	  getRep,
-	  MODE,
-	  maybeEscapePropertyName,
-	  parseURLEncodedText,
-	  parseURLParams,
-	  getGripPreviewItems,
-	  ObjectInspector
-	};
-
-/***/ },
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE_0__;
+
+/***/ }),
 /* 1 */
-/***/ function(module, exports) {
-
-	/* 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/. */
-
-	module.exports = {
-	  MODE: {
-	    TINY: Symbol("TINY"),
-	    SHORT: Symbol("SHORT"),
-	    LONG: Symbol("LONG")
-	  }
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+const validProtocols = /^(http|https|ftp|data|javascript|resource|chrome):/i;
+const tokenSplitRegex = /(\s|\'|\"|\\)+/;
+/**
+ * Returns true if the given object is a grip (see RDP protocol)
+ */
+function isGrip(object) {
+  return object && object.actor;
+}
+
+function escapeNewLines(value) {
+  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
+}
+
+// Map from character code to the corresponding escape sequence.  \0
+// isn't here because it would require special treatment in some
+// situations.  \b, \f, and \v aren't here because they aren't very
+// common.  \' isn't here because there's no need, we only
+// double-quote strings.
+const escapeMap = {
+  // Tab.
+  9: "\\t",
+  // Newline.
+  0xa: "\\n",
+  // Carriage return.
+  0xd: "\\r",
+  // Quote.
+  0x22: "\\\"",
+  // Backslash.
+  0x5c: "\\\\"
+};
+
+// Regexp that matches any character we might possibly want to escape.
+// Note that we over-match here, because it's difficult to, say, match
+// an unpaired surrogate with a regexp.  The details are worked out by
+// the replacement function; see |escapeString|.
+const escapeRegexp = new RegExp("[" +
+// Quote and backslash.
+"\"\\\\" +
+// Controls.
+"\x00-\x1f" +
+// More controls.
+"\x7f-\x9f" +
+// BOM
+"\ufeff" +
+// Replacement characters and non-characters.
+"\ufffc-\uffff" +
+// Surrogates.
+"\ud800-\udfff" +
+// Mathematical invisibles.
+"\u2061-\u2064" +
+// Line and paragraph separators.
+"\u2028-\u2029" +
+// Private use area.
+"\ue000-\uf8ff" + "]", "g");
+
+/**
+ * Escape a string so that the result is viewable and valid JS.
+ * Control characters, other invisibles, invalid characters,
+ * backslash, and double quotes are escaped.  The resulting string is
+ * surrounded by double quotes.
+ *
+ * @param {String} str
+ *        the input
+ * @param {Boolean} escapeWhitespace
+ *        if true, TAB, CR, and NL characters will be escaped
+ * @return {String} the escaped string
+ */
+function escapeString(str, escapeWhitespace) {
+  return "\"" + str.replace(escapeRegexp, (match, offset) => {
+    let c = match.charCodeAt(0);
+    if (c in escapeMap) {
+      if (!escapeWhitespace && (c === 9 || c === 0xa || c === 0xd)) {
+        return match[0];
+      }
+      return escapeMap[c];
+    }
+    if (c >= 0xd800 && c <= 0xdfff) {
+      // Find the full code point containing the surrogate, with a
+      // special case for a trailing surrogate at the start of the
+      // string.
+      if (c >= 0xdc00 && offset > 0) {
+        --offset;
+      }
+      let codePoint = str.codePointAt(offset);
+      if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
+        // Unpaired surrogate.
+        return "\\u" + codePoint.toString(16);
+      } else if (codePoint >= 0xf0000 && codePoint <= 0x10fffd) {
+        // Private use area.  Because we visit each pair of a such a
+        // character, return the empty string for one half and the
+        // real result for the other, to avoid duplication.
+        if (c <= 0xdbff) {
+          return "\\u{" + codePoint.toString(16) + "}";
+        }
+        return "";
+      }
+      // Other surrogate characters are passed through.
+      return match;
+    }
+    return "\\u" + ("0000" + c.toString(16)).substr(-4);
+  }) + "\"";
+}
+
+/**
+ * Escape a property name, if needed.  "Escaping" in this context
+ * means surrounding the property name with quotes.
+ *
+ * @param {String}
+ *        name the property name
+ * @return {String} either the input, or the input surrounded by
+ *                  quotes, properly quoted in JS syntax.
+ */
+function maybeEscapePropertyName(name) {
+  // Quote the property name if it needs quoting.  This particular
+  // test is an approximation; see
+  // https://mathiasbynens.be/notes/javascript-properties.  However,
+  // the full solution requires a fair amount of Unicode data, and so
+  // let's defer that until either it's important, or the \p regexp
+  // syntax lands, see
+  // https://github.com/tc39/proposal-regexp-unicode-property-escapes.
+  if (!/^\w+$/.test(name)) {
+    name = escapeString(name);
+  }
+  return name;
+}
+
+function cropMultipleLines(text, limit) {
+  return escapeNewLines(cropString(text, limit));
+}
+
+function rawCropString(text, limit, alternativeText) {
+  if (!alternativeText) {
+    alternativeText = "\u2026";
+  }
+
+  // Crop the string only if a limit is actually specified.
+  if (!limit || limit <= 0) {
+    return text;
+  }
+
+  // Set the limit at least to the length of the alternative text
+  // plus one character of the original text.
+  if (limit <= alternativeText.length) {
+    limit = alternativeText.length + 1;
+  }
+
+  let halfLimit = (limit - alternativeText.length) / 2;
+
+  if (text.length > limit) {
+    return text.substr(0, Math.ceil(halfLimit)) + alternativeText + text.substr(text.length - Math.floor(halfLimit));
+  }
+
+  return text;
+}
+
+function cropString(text, limit, alternativeText) {
+  return rawCropString(sanitizeString(text + ""), limit, alternativeText);
+}
+
+function sanitizeString(text) {
+  // Replace all non-printable characters, except of
+  // (horizontal) tab (HT: \x09) and newline (LF: \x0A, CR: \x0D),
+  // with unicode replacement character (u+fffd).
+  // eslint-disable-next-line no-control-regex
+  let re = new RegExp("[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]", "g");
+  return text.replace(re, "\ufffd");
+}
+
+function parseURLParams(url) {
+  url = new URL(url);
+  return parseURLEncodedText(url.searchParams);
+}
+
+function parseURLEncodedText(text) {
+  let params = [];
+
+  // In case the text is empty just return the empty parameters
+  if (text == "") {
+    return params;
+  }
+
+  let searchParams = new URLSearchParams(text);
+  let entries = [...searchParams.entries()];
+  return entries.map(entry => {
+    return {
+      name: entry[0],
+      value: entry[1]
+    };
+  });
+}
+
+function getFileName(url) {
+  let split = splitURLBase(url);
+  return split.name;
+}
+
+function splitURLBase(url) {
+  if (!isDataURL(url)) {
+    return splitURLTrue(url);
+  }
+  return {};
+}
+
+function getURLDisplayString(url) {
+  return cropString(url);
+}
+
+function isDataURL(url) {
+  return url && url.substr(0, 5) == "data:";
+}
+
+function splitURLTrue(url) {
+  const reSplitFile = /(.*?):\/{2,3}([^\/]*)(.*?)([^\/]*?)($|\?.*)/;
+  let m = reSplitFile.exec(url);
+
+  if (!m) {
+    return {
+      name: url,
+      path: url
+    };
+  } else if (m[4] == "" && m[5] == "") {
+    return {
+      protocol: m[1],
+      domain: m[2],
+      path: m[3],
+      name: m[3] != "/" ? m[3] : m[2]
+    };
+  }
+
+  return {
+    protocol: m[1],
+    domain: m[2],
+    path: m[2] + m[3],
+    name: m[4] + m[5]
+  };
+}
+
+/**
+ * Wrap the provided render() method of a rep in a try/catch block that will render a
+ * fallback rep if the render fails.
+ */
+function wrapRender(renderMethod) {
+  const wrappedFunction = function (props) {
+    try {
+      return renderMethod.call(this, props);
+    } catch (e) {
+      console.error(e);
+      return React.DOM.span({
+        className: "objectBox objectBox-failure",
+        title: "This object could not be rendered, " + "please file a bug on bugzilla.mozilla.org"
+      },
+      /* Labels have to be hardcoded for reps, see Bug 1317038. */
+      "Invalid object");
+    }
+  };
+  wrappedFunction.propTypes = renderMethod.propTypes;
+  return wrappedFunction;
+}
+
+/**
+ * Get preview items from a Grip.
+ *
+ * @param {Object} Grip from which we want the preview items
+ * @return {Array} Array of the preview items of the grip, or an empty array
+ *                 if the grip does not have preview items
+ */
+function getGripPreviewItems(grip) {
+  if (!grip) {
+    return [];
+  }
+
+  // Promise resolved value Grip
+  if (grip.promiseState && grip.promiseState.value) {
+    return [grip.promiseState.value];
+  }
+
+  // Array Grip
+  if (grip.preview && grip.preview.items) {
+    return grip.preview.items;
+  }
+
+  // Node Grip
+  if (grip.preview && grip.preview.childNodes) {
+    return grip.preview.childNodes;
+  }
+
+  // Set or Map Grip
+  if (grip.preview && grip.preview.entries) {
+    return grip.preview.entries.reduce((res, entry) => res.concat(entry), []);
+  }
+
+  // Event Grip
+  if (grip.preview && grip.preview.target) {
+    let keys = Object.keys(grip.preview.properties);
+    let values = Object.values(grip.preview.properties);
+    return [grip.preview.target, ...keys, ...values];
+  }
+
+  // RegEx Grip
+  if (grip.displayString) {
+    return [grip.displayString];
+  }
+
+  // Generic Grip
+  if (grip.preview && grip.preview.ownProperties) {
+    let propertiesValues = Object.values(grip.preview.ownProperties).map(property => property.value || property);
+
+    let propertyKeys = Object.keys(grip.preview.ownProperties);
+    propertiesValues = propertiesValues.concat(propertyKeys);
+
+    // ArrayBuffer Grip
+    if (grip.preview.safeGetterValues) {
+      propertiesValues = propertiesValues.concat(Object.values(grip.preview.safeGetterValues).map(property => property.getterValue || property));
+    }
+
+    return propertiesValues;
+  }
+
+  return [];
+}
+
+/**
+ * Get the type of an object.
+ *
+ * @param {Object} Grip from which we want the type.
+ * @param {boolean} noGrip true if the object is not a grip.
+ * @return {boolean}
+ */
+function getGripType(object, noGrip) {
+  let type = typeof object;
+  if (type == "object" && object instanceof String) {
+    type = "string";
+  } else if (object && type == "object" && object.type && noGrip !== true) {
+    type = object.type;
+  }
+
+  if (isGrip(object)) {
+    type = object.class;
+  }
+
+  return type;
+}
+
+/**
+ * Determines whether a grip is a string containing a URL.
+ *
+ * @param string grip
+ *        The grip, which may contain a URL.
+ * @return boolean
+ *         Whether the grip is a string containing a URL.
+ */
+function containsURL(grip) {
+  if (typeof grip !== "string") {
+    return false;
+  }
+
+  let tokens = grip.split(tokenSplitRegex);
+  return tokens.some(isURL);
+}
+
+/**
+ * Determines whether a string token is a valid URL.
+ *
+ * @param string token
+ *        The token.
+ * @return boolean
+ *         Whenther the token is a URL.
+ */
+function isURL(token) {
+  try {
+    if (!validProtocols.test(token)) {
+      return false;
+    }
+    new URL(token);
+    return true;
+  } catch (e) {
+    return false;
+  }
+}
+
+module.exports = {
+  isGrip,
+  isURL,
+  cropString,
+  containsURL,
+  rawCropString,
+  sanitizeString,
+  escapeString,
+  wrapRender,
+  cropMultipleLines,
+  parseURLParams,
+  parseURLEncodedText,
+  getFileName,
+  getURLDisplayString,
+  maybeEscapePropertyName,
+  getGripPreviewItems,
+  getGripType,
+  tokenSplitRegex
+};
+
+/***/ }),
 /* 2 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	__webpack_require__(3);
-	const { isGrip } = __webpack_require__(7);
-
-	// Load all existing rep templates
-	const Undefined = __webpack_require__(9);
-	const Null = __webpack_require__(10);
-	const StringRep = __webpack_require__(11);
-	const LongStringRep = __webpack_require__(12);
-	const Number = __webpack_require__(13);
-	const ArrayRep = __webpack_require__(14);
-	const Obj = __webpack_require__(15);
-	const SymbolRep = __webpack_require__(18);
-	const InfinityRep = __webpack_require__(19);
-	const NaNRep = __webpack_require__(20);
-	const Accessor = __webpack_require__(21);
-
-	// DOM types (grips)
-	const Attribute = __webpack_require__(22);
-	const DateTime = __webpack_require__(23);
-	const Document = __webpack_require__(24);
-	const Event = __webpack_require__(25);
-	const Func = __webpack_require__(26);
-	const PromiseRep = __webpack_require__(27);
-	const RegExp = __webpack_require__(28);
-	const StyleSheet = __webpack_require__(29);
-	const CommentNode = __webpack_require__(30);
-	const ElementNode = __webpack_require__(32);
-	const TextNode = __webpack_require__(37);
-	const ErrorRep = __webpack_require__(38);
-	const Window = __webpack_require__(39);
-	const ObjectWithText = __webpack_require__(40);
-	const ObjectWithURL = __webpack_require__(41);
-	const GripArray = __webpack_require__(42);
-	const GripMap = __webpack_require__(43);
-	const GripMapEntry = __webpack_require__(44);
-	const Grip = __webpack_require__(17);
-
-	// List of all registered template.
-	// XXX there should be a way for extensions to register a new
-	// or modify an existing rep.
-	let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, LongStringRep, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
-
-	/**
-	 * Generic rep that is using for rendering native JS types or an object.
-	 * The right template used for rendering is picked automatically according
-	 * to the current value type. The value must be passed is as 'object'
-	 * property.
-	 */
-	const Rep = function (props) {
-	  let {
-	    object,
-	    defaultRep
-	  } = props;
-	  let rep = getRep(object, defaultRep, props.noGrip);
-	  return rep(props);
-	};
-
-	// Helpers
-
-	/**
-	 * Return a rep object that is responsible for rendering given
-	 * object.
-	 *
-	 * @param object {Object} Object to be rendered in the UI. This
-	 * can be generic JS object as well as a grip (handle to a remote
-	 * debuggee object).
-	 *
-	 * @param defaultObject {React.Component} The default template
-	 * that should be used to render given object if none is found.
-	 *
-	 * @param noGrip {Boolean} If true, will only check reps not made for remote objects.
-	 */
-	function getRep(object, defaultRep = Obj, noGrip = false) {
-	  let type = typeof object;
-	  if (type == "object" && object instanceof String) {
-	    type = "string";
-	  } else if (object && type == "object" && object.type && noGrip !== true) {
-	    type = object.type;
-	  }
-
-	  if (isGrip(object)) {
-	    type = object.class;
-	  }
-
-	  for (let i = 0; i < reps.length; i++) {
-	    let rep = reps[i];
-	    try {
-	      // supportsObject could return weight (not only true/false
-	      // but a number), which would allow to priorities templates and
-	      // support better extensibility.
-	      if (rep.supportsObject(object, type, noGrip)) {
-	        return rep.rep;
-	      }
-	    } catch (err) {
-	      console.error(err);
-	    }
-	  }
-
-	  return defaultRep.rep;
-	}
-
-	module.exports = {
-	  Rep,
-	  REPS: {
-	    Accessor,
-	    ArrayRep,
-	    Attribute,
-	    CommentNode,
-	    DateTime,
-	    Document,
-	    ElementNode,
-	    ErrorRep,
-	    Event,
-	    Func,
-	    Grip,
-	    GripArray,
-	    GripMap,
-	    GripMapEntry,
-	    InfinityRep,
-	    LongStringRep,
-	    NaNRep,
-	    Null,
-	    Number,
-	    Obj,
-	    ObjectWithText,
-	    ObjectWithURL,
-	    PromiseRep,
-	    RegExp,
-	    Rep,
-	    StringRep,
-	    StyleSheet,
-	    SymbolRep,
-	    TextNode,
-	    Undefined,
-	    Window
-	  },
-	  // Exporting for tests
-	  getRep
-	};
-
-/***/ },
+/***/ (function(module, exports) {
+
+/* 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/. */
+
+module.exports = {
+  MODE: {
+    TINY: Symbol("TINY"),
+    SHORT: Symbol("SHORT"),
+    LONG: Symbol("LONG")
+  }
+};
+
+/***/ }),
 /* 3 */
-/***/ function(module, exports) {
-
-	// removed by extract-text-webpack-plugin
-
-/***/ },
-/* 4 */,
-/* 5 */,
-/* 6 */,
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+__webpack_require__(27);
+
+// Load all existing rep templates
+const Undefined = __webpack_require__(28);
+const Null = __webpack_require__(29);
+const StringRep = __webpack_require__(16);
+const LongStringRep = __webpack_require__(30);
+const Number = __webpack_require__(31);
+const ArrayRep = __webpack_require__(17);
+const Obj = __webpack_require__(32);
+const SymbolRep = __webpack_require__(33);
+const InfinityRep = __webpack_require__(34);
+const NaNRep = __webpack_require__(35);
+const Accessor = __webpack_require__(36);
+
+// DOM types (grips)
+const Attribute = __webpack_require__(37);
+const DateTime = __webpack_require__(38);
+const Document = __webpack_require__(39);
+const Event = __webpack_require__(40);
+const Func = __webpack_require__(41);
+const PromiseRep = __webpack_require__(42);
+const RegExp = __webpack_require__(43);
+const StyleSheet = __webpack_require__(44);
+const CommentNode = __webpack_require__(45);
+const ElementNode = __webpack_require__(46);
+const TextNode = __webpack_require__(50);
+const ErrorRep = __webpack_require__(51);
+const Window = __webpack_require__(52);
+const ObjectWithText = __webpack_require__(53);
+const ObjectWithURL = __webpack_require__(54);
+const GripArray = __webpack_require__(19);
+const GripMap = __webpack_require__(55);
+const GripMapEntry = __webpack_require__(20);
+const Grip = __webpack_require__(9);
+
+// List of all registered template.
+// XXX there should be a way for extensions to register a new
+// or modify an existing rep.
+let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, LongStringRep, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
+
+/**
+ * Generic rep that is using for rendering native JS types or an object.
+ * The right template used for rendering is picked automatically according
+ * to the current value type. The value must be passed is as 'object'
+ * property.
+ */
+const Rep = function (props) {
+  let {
+    object,
+    defaultRep
+  } = props;
+  let rep = getRep(object, defaultRep, props.noGrip);
+  return rep(props);
+};
+
+// Helpers
+
+/**
+ * Return a rep object that is responsible for rendering given
+ * object.
+ *
+ * @param object {Object} Object to be rendered in the UI. This
+ * can be generic JS object as well as a grip (handle to a remote
+ * debuggee object).
+ *
+ * @param defaultObject {React.Component} The default template
+ * that should be used to render given object if none is found.
+ *
+ * @param noGrip {Boolean} If true, will only check reps not made for remote objects.
+ */
+function getRep(object, defaultRep = Obj, noGrip = false) {
+  for (let i = 0; i < reps.length; i++) {
+    let rep = reps[i];
+    try {
+      // supportsObject could return weight (not only true/false
+      // but a number), which would allow to priorities templates and
+      // support better extensibility.
+      if (rep.supportsObject(object, noGrip)) {
+        return rep.rep;
+      }
+    } catch (err) {
+      console.error(err);
+    }
+  }
+
+  return defaultRep.rep;
+}
+
+module.exports = {
+  Rep,
+  REPS: {
+    Accessor,
+    ArrayRep,
+    Attribute,
+    CommentNode,
+    DateTime,
+    Document,
+    ElementNode,
+    ErrorRep,
+    Event,
+    Func,
+    Grip,
+    GripArray,
+    GripMap,
+    GripMapEntry,
+    InfinityRep,
+    LongStringRep,
+    NaNRep,
+    Null,
+    Number,
+    Obj,
+    ObjectWithText,
+    ObjectWithURL,
+    PromiseRep,
+    RegExp,
+    Rep,
+    StringRep,
+    StyleSheet,
+    SymbolRep,
+    TextNode,
+    Undefined,
+    Window
+  },
+  // Exporting for tests
+  getRep
+};
+
+/***/ }),
+/* 4 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+const {
+  maybeEscapePropertyName,
+  wrapRender
+} = __webpack_require__(1);
+const { MODE } = __webpack_require__(2);
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Property for Obj (local JS objects), Grip (remote JS objects)
+ * and GripMap (remote JS maps and weakmaps) reps.
+ * It's used to render object properties.
+ */
+PropRep.propTypes = {
+  // Property name.
+  name: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.object]).isRequired,
+  // Equal character rendered between property name and value.
+  equal: React.PropTypes.string,
+  // @TODO Change this to Object.values once it's supported in Node's version of V8
+  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  onDOMNodeMouseOver: React.PropTypes.func,
+  onDOMNodeMouseOut: React.PropTypes.func,
+  onInspectIconClick: React.PropTypes.func,
+  // Normally a PropRep will quote a property name that isn't valid
+  // when unquoted; but this flag can be used to suppress the
+  // quoting.
+  suppressQuotes: React.PropTypes.bool
+};
+
+/**
+ * Function that given a name, a delimiter and an object returns an array
+ * of React elements representing an object property (e.g. `name: value`)
+ *
+ * @param {Object} props
+ * @return {Array} Array of React elements.
+ */
+function PropRep(props) {
+  const Grip = __webpack_require__(9);
+  const { Rep } = __webpack_require__(3);
+
+  let {
+    name,
+    mode,
+    equal,
+    suppressQuotes
+  } = props;
+
+  let key;
+  // The key can be a simple string, for plain objects,
+  // or another object for maps and weakmaps.
+  if (typeof name === "string") {
+    if (!suppressQuotes) {
+      name = maybeEscapePropertyName(name);
+    }
+    key = span({ "className": "nodeName" }, name);
+  } else {
+    key = Rep(Object.assign({}, props, {
+      className: "nodeName",
+      object: name,
+      mode: mode || MODE.TINY,
+      defaultRep: Grip
+    }));
+  }
+
+  return [key, span({
+    "className": "objectEqual"
+  }, equal), Rep(Object.assign({}, props))];
+}
+
+// Exports from this module
+module.exports = wrapRender(PropRep);
+
+/***/ }),
+/* 5 */
+/***/ (function(module, exports) {
+
+/**
+ * Checks if `value` is classified as an `Array` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an array, else `false`.
+ * @example
+ *
+ * _.isArray([1, 2, 3]);
+ * // => true
+ *
+ * _.isArray(document.body.children);
+ * // => false
+ *
+ * _.isArray('abc');
+ * // => false
+ *
+ * _.isArray(_.noop);
+ * // => false
+ */
+var isArray = Array.isArray;
+
+module.exports = isArray;
+
+
+/***/ }),
+/* 6 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var getNative = __webpack_require__(23);
+
+/* Built-in method references that are verified to be native. */
+var nativeCreate = getNative(Object, 'create');
+
+module.exports = nativeCreate;
+
+
+/***/ }),
 /* 7 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-
-	/**
-	 * Returns true if the given object is a grip (see RDP protocol)
-	 */
-	function isGrip(object) {
-	  return object && object.actor;
-	}
-
-	function escapeNewLines(value) {
-	  return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
-	}
-
-	// Map from character code to the corresponding escape sequence.  \0
-	// isn't here because it would require special treatment in some
-	// situations.  \b, \f, and \v aren't here because they aren't very
-	// common.  \' isn't here because there's no need, we only
-	// double-quote strings.
-	const escapeMap = {
-	  // Tab.
-	  9: "\\t",
-	  // Newline.
-	  0xa: "\\n",
-	  // Carriage return.
-	  0xd: "\\r",
-	  // Quote.
-	  0x22: "\\\"",
-	  // Backslash.
-	  0x5c: "\\\\"
-	};
-
-	// Regexp that matches any character we might possibly want to escape.
-	// Note that we over-match here, because it's difficult to, say, match
-	// an unpaired surrogate with a regexp.  The details are worked out by
-	// the replacement function; see |escapeString|.
-	const escapeRegexp = new RegExp("[" +
-	// Quote and backslash.
-	"\"\\\\" +
-	// Controls.
-	"\x00-\x1f" +
-	// More controls.
-	"\x7f-\x9f" +
-	// BOM
-	"\ufeff" +
-	// Replacement characters and non-characters.
-	"\ufffc-\uffff" +
-	// Surrogates.
-	"\ud800-\udfff" +
-	// Mathematical invisibles.
-	"\u2061-\u2064" +
-	// Line and paragraph separators.
-	"\u2028-\u2029" +
-	// Private use area.
-	"\ue000-\uf8ff" + "]", "g");
-
-	/**
-	 * Escape a string so that the result is viewable and valid JS.
-	 * Control characters, other invisibles, invalid characters,
-	 * backslash, and double quotes are escaped.  The resulting string is
-	 * surrounded by double quotes.
-	 *
-	 * @param {String} str
-	 *        the input
-	 * @param {Boolean} escapeWhitespace
-	 *        if true, TAB, CR, and NL characters will be escaped
-	 * @return {String} the escaped string
-	 */
-	function escapeString(str, escapeWhitespace) {
-	  return "\"" + str.replace(escapeRegexp, (match, offset) => {
-	    let c = match.charCodeAt(0);
-	    if (c in escapeMap) {
-	      if (!escapeWhitespace && (c === 9 || c === 0xa || c === 0xd)) {
-	        return match[0];
-	      }
-	      return escapeMap[c];
-	    }
-	    if (c >= 0xd800 && c <= 0xdfff) {
-	      // Find the full code point containing the surrogate, with a
-	      // special case for a trailing surrogate at the start of the
-	      // string.
-	      if (c >= 0xdc00 && offset > 0) {
-	        --offset;
-	      }
-	      let codePoint = str.codePointAt(offset);
-	      if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
-	        // Unpaired surrogate.
-	        return "\\u" + codePoint.toString(16);
-	      } else if (codePoint >= 0xf0000 && codePoint <= 0x10fffd) {
-	        // Private use area.  Because we visit each pair of a such a
-	        // character, return the empty string for one half and the
-	        // real result for the other, to avoid duplication.
-	        if (c <= 0xdbff) {
-	          return "\\u{" + codePoint.toString(16) + "}";
-	        }
-	        return "";
-	      }
-	      // Other surrogate characters are passed through.
-	      return match;
-	    }
-	    return "\\u" + ("0000" + c.toString(16)).substr(-4);
-	  }) + "\"";
-	}
-
-	/**
-	 * Escape a property name, if needed.  "Escaping" in this context
-	 * means surrounding the property name with quotes.
-	 *
-	 * @param {String}
-	 *        name the property name
-	 * @return {String} either the input, or the input surrounded by
-	 *                  quotes, properly quoted in JS syntax.
-	 */
-	function maybeEscapePropertyName(name) {
-	  // Quote the property name if it needs quoting.  This particular
-	  // test is an approximation; see
-	  // https://mathiasbynens.be/notes/javascript-properties.  However,
-	  // the full solution requires a fair amount of Unicode data, and so
-	  // let's defer that until either it's important, or the \p regexp
-	  // syntax lands, see
-	  // https://github.com/tc39/proposal-regexp-unicode-property-escapes.
-	  if (!/^\w+$/.test(name)) {
-	    name = escapeString(name);
-	  }
-	  return name;
-	}
-
-	function cropMultipleLines(text, limit) {
-	  return escapeNewLines(cropString(text, limit));
-	}
-
-	function rawCropString(text, limit, alternativeText) {
-	  if (!alternativeText) {
-	    alternativeText = "\u2026";
-	  }
-
-	  // Crop the string only if a limit is actually specified.
-	  if (!limit || limit <= 0) {
-	    return text;
-	  }
-
-	  // Set the limit at least to the length of the alternative text
-	  // plus one character of the original text.
-	  if (limit <= alternativeText.length) {
-	    limit = alternativeText.length + 1;
-	  }
-
-	  let halfLimit = (limit - alternativeText.length) / 2;
-
-	  if (text.length > limit) {
-	    return text.substr(0, Math.ceil(halfLimit)) + alternativeText + text.substr(text.length - Math.floor(halfLimit));
-	  }
-
-	  return text;
-	}
-
-	function cropString(text, limit, alternativeText) {
-	  return rawCropString(sanitizeString(text + ""), limit, alternativeText);
-	}
-
-	function sanitizeString(text) {
-	  // Replace all non-printable characters, except of
-	  // (horizontal) tab (HT: \x09) and newline (LF: \x0A, CR: \x0D),
-	  // with unicode replacement character (u+fffd).
-	  // eslint-disable-next-line no-control-regex
-	  let re = new RegExp("[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]", "g");
-	  return text.replace(re, "\ufffd");
-	}
-
-	function parseURLParams(url) {
-	  url = new URL(url);
-	  return parseURLEncodedText(url.searchParams);
-	}
-
-	function parseURLEncodedText(text) {
-	  let params = [];
-
-	  // In case the text is empty just return the empty parameters
-	  if (text == "") {
-	    return params;
-	  }
-
-	  let searchParams = new URLSearchParams(text);
-	  let entries = [...searchParams.entries()];
-	  return entries.map(entry => {
-	    return {
-	      name: entry[0],
-	      value: entry[1]
-	    };
-	  });
-	}
-
-	function getFileName(url) {
-	  let split = splitURLBase(url);
-	  return split.name;
-	}
-
-	function splitURLBase(url) {
-	  if (!isDataURL(url)) {
-	    return splitURLTrue(url);
-	  }
-	  return {};
-	}
-
-	function getURLDisplayString(url) {
-	  return cropString(url);
-	}
-
-	function isDataURL(url) {
-	  return url && url.substr(0, 5) == "data:";
-	}
-
-	function splitURLTrue(url) {
-	  const reSplitFile = /(.*?):\/{2,3}([^\/]*)(.*?)([^\/]*?)($|\?.*)/;
-	  let m = reSplitFile.exec(url);
-
-	  if (!m) {
-	    return {
-	      name: url,
-	      path: url
-	    };
-	  } else if (m[4] == "" && m[5] == "") {
-	    return {
-	      protocol: m[1],
-	      domain: m[2],
-	      path: m[3],
-	      name: m[3] != "/" ? m[3] : m[2]
-	    };
-	  }
-
-	  return {
-	    protocol: m[1],
-	    domain: m[2],
-	    path: m[2] + m[3],
-	    name: m[4] + m[5]
-	  };
-	}
-
-	/**
-	 * Wrap the provided render() method of a rep in a try/catch block that will render a
-	 * fallback rep if the render fails.
-	 */
-	function wrapRender(renderMethod) {
-	  const wrappedFunction = function (props) {
-	    try {
-	      return renderMethod.call(this, props);
-	    } catch (e) {
-	      console.error(e);
-	      return React.DOM.span({
-	        className: "objectBox objectBox-failure",
-	        title: "This object could not be rendered, " + "please file a bug on bugzilla.mozilla.org"
-	      },
-	      /* Labels have to be hardcoded for reps, see Bug 1317038. */
-	      "Invalid object");
-	    }
-	  };
-	  wrappedFunction.propTypes = renderMethod.propTypes;
-	  return wrappedFunction;
-	}
-
-	/**
-	 * Get preview items from a Grip.
-	 *
-	 * @param {Object} Grip from which we want the preview items
-	 * @return {Array} Array of the preview items of the grip, or an empty array
-	 *                 if the grip does not have preview items
-	 */
-	function getGripPreviewItems(grip) {
-	  if (!grip) {
-	    return [];
-	  }
-
-	  // Promise resolved value Grip
-	  if (grip.promiseState && grip.promiseState.value) {
-	    return [grip.promiseState.value];
-	  }
-
-	  // Array Grip
-	  if (grip.preview && grip.preview.items) {
-	    return grip.preview.items;
-	  }
-
-	  // Node Grip
-	  if (grip.preview && grip.preview.childNodes) {
-	    return grip.preview.childNodes;
-	  }
-
-	  // Set or Map Grip
-	  if (grip.preview && grip.preview.entries) {
-	    return grip.preview.entries.reduce((res, entry) => res.concat(entry), []);
-	  }
-
-	  // Event Grip
-	  if (grip.preview && grip.preview.target) {
-	    let keys = Object.keys(grip.preview.properties);
-	    let values = Object.values(grip.preview.properties);
-	    return [grip.preview.target, ...keys, ...values];
-	  }
-
-	  // RegEx Grip
-	  if (grip.displayString) {
-	    return [grip.displayString];
-	  }
-
-	  // Generic Grip
-	  if (grip.preview && grip.preview.ownProperties) {
-	    let propertiesValues = Object.values(grip.preview.ownProperties).map(property => property.value || property);
-
-	    let propertyKeys = Object.keys(grip.preview.ownProperties);
-	    propertiesValues = propertiesValues.concat(propertyKeys);
-
-	    // ArrayBuffer Grip
-	    if (grip.preview.safeGetterValues) {
-	      propertiesValues = propertiesValues.concat(Object.values(grip.preview.safeGetterValues).map(property => property.getterValue || property));
-	    }
-
-	    return propertiesValues;
-	  }
-
-	  return [];
-	}
-
-	module.exports = {
-	  isGrip,
-	  cropString,
-	  rawCropString,
-	  sanitizeString,
-	  escapeString,
-	  wrapRender,
-	  cropMultipleLines,
-	  parseURLParams,
-	  parseURLEncodedText,
-	  getFileName,
-	  getURLDisplayString,
-	  maybeEscapePropertyName,
-	  getGripPreviewItems
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+var eq = __webpack_require__(88);
+
+/**
+ * Gets the index at which the `key` is found in `array` of key-value pairs.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} key The key to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+function assocIndexOf(array, key) {
+  var length = array.length;
+  while (length--) {
+    if (eq(array[length][0], key)) {
+      return length;
+    }
+  }
+  return -1;
+}
+
+module.exports = assocIndexOf;
+
+
+/***/ }),
 /* 8 */
-/***/ function(module, exports) {
-
-	module.exports = __WEBPACK_EXTERNAL_MODULE_8__;
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+var isKeyable = __webpack_require__(94);
+
+/**
+ * Gets the data for `map`.
+ *
+ * @private
+ * @param {Object} map The map to query.
+ * @param {string} key The reference key.
+ * @returns {*} Returns the map data.
+ */
+function getMapData(map, key) {
+  var data = map.__data__;
+  return isKeyable(key)
+    ? data[typeof key == 'string' ? 'string' : 'hash']
+    : data.map;
+}
+
+module.exports = getMapData;
+
+
+/***/ }),
 /* 9 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-
-	const { wrapRender } = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders undefined value
-	 */
-	const Undefined = function () {
-	  return span({ className: "objectBox objectBox-undefined" }, "undefined");
-	};
-
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip === true) {
-	    return false;
-	  }
-
-	  return object && object.type && object.type == "undefined" || type == "undefined";
-	}
-
-	// Exports from this module
-
-	module.exports = {
-	  rep: wrapRender(Undefined),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// ReactJS
+const React = __webpack_require__(0);
+// Dependencies
+const {
+  isGrip,
+  wrapRender
+} = __webpack_require__(1);
+const PropRep = __webpack_require__(4);
+const { MODE } = __webpack_require__(2);
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders generic grip. Grip is client representation
+ * of remote JS object and is used as an input object
+ * for this rep component.
+ */
+GripRep.propTypes = {
+  object: React.PropTypes.object.isRequired,
+  // @TODO Change this to Object.values once it's supported in Node's version of V8
+  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  isInterestingProp: React.PropTypes.func,
+  title: React.PropTypes.string,
+  onDOMNodeMouseOver: React.PropTypes.func,
+  onDOMNodeMouseOut: React.PropTypes.func,
+  onInspectIconClick: React.PropTypes.func,
+  noGrip: React.PropTypes.bool
+};
+
+const DEFAULT_TITLE = "Object";
+
+function GripRep(props) {
+  let {
+    mode = MODE.SHORT,
+    object
+  } = props;
+
+  const config = {
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-object"
+  };
+
+  if (mode === MODE.TINY) {
+    let propertiesLength = getPropertiesLength(object);
+
+    const tinyModeItems = [];
+    if (getTitle(props, object) !== DEFAULT_TITLE) {
+      tinyModeItems.push(getTitleElement(props, object));
+    } else {
+      tinyModeItems.push(span({
+        className: "objectLeftBrace"
+      }, "{"), propertiesLength > 0 ? span({
+        key: "more",
+        className: "more-ellipsis",
+        title: "more…"
+      }, "…") : null, span({
+        className: "objectRightBrace"
+      }, "}"));
+    }
+
+    return span(config, ...tinyModeItems);
+  }
+
+  let propsArray = safePropIterator(props, object, maxLengthMap.get(mode));
+
+  return span(config, getTitleElement(props, object), span({
+    className: "objectLeftBrace"
+  }, " { "), ...propsArray, span({
+    className: "objectRightBrace"
+  }, " }"));
+}
+
+function getTitleElement(props, object) {
+  return span({
+    className: "objectTitle"
+  }, getTitle(props, object));
+}
+
+function getTitle(props, object) {
+  return props.title || object.class || DEFAULT_TITLE;
+}
+
+function getPropertiesLength(object) {
+  let propertiesLength = object.preview && object.preview.ownPropertiesLength ? object.preview.ownPropertiesLength : object.ownPropertyLength;
+
+  if (object.preview && object.preview.safeGetterValues) {
+    propertiesLength += Object.keys(object.preview.safeGetterValues).length;
+  }
+
+  if (object.preview && object.preview.ownSymbols) {
+    propertiesLength += object.preview.ownSymbolsLength;
+  }
+
+  return propertiesLength;
+}
+
+function safePropIterator(props, object, max) {
+  max = typeof max === "undefined" ? maxLengthMap.get(MODE.SHORT) : max;
+  try {
+    return propIterator(props, object, max);
+  } catch (err) {
+    console.error(err);
+  }
+  return [];
+}
+
+function propIterator(props, object, max) {
+  if (object.preview && Object.keys(object.preview).includes("wrappedValue")) {
+    const { Rep } = __webpack_require__(3);
+
+    return [Rep({
+      object: object.preview.wrappedValue,
+      mode: props.mode || MODE.TINY,
+      defaultRep: Grip
+    })];
+  }
+
+  // Property filter. Show only interesting properties to the user.
+  let isInterestingProp = props.isInterestingProp || ((type, value) => {
+    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
+  });
+
+  let properties = object.preview ? object.preview.ownProperties : {};
+  let propertiesLength = getPropertiesLength(object);
+
+  if (object.preview && object.preview.safeGetterValues) {
+    properties = Object.assign({}, properties, object.preview.safeGetterValues);
+  }
+
+  let indexes = getPropIndexes(properties, max, isInterestingProp);
+  if (indexes.length < max && indexes.length < propertiesLength) {
+    // There are not enough props yet. Then add uninteresting props to display them.
+    indexes = indexes.concat(getPropIndexes(properties, max - indexes.length, (t, value, name) => {
+      return !isInterestingProp(t, value, name);
+    }));
+  }
+
+  // The server synthesizes some property names for a Proxy, like
+  // <target> and <handler>; we don't want to quote these because,
+  // as synthetic properties, they appear more natural when
+  // unquoted.
+  const suppressQuotes = object.class === "Proxy";
+  let propsArray = getProps(props, properties, indexes, suppressQuotes);
+
+  // Show symbols.
+  if (object.preview && object.preview.ownSymbols) {
+    const { ownSymbols } = object.preview;
+    const length = max - indexes.length;
+
+    const symbolsProps = ownSymbols.slice(0, length).map(symbolItem => {
+      return PropRep(Object.assign({}, props, {
+        mode: MODE.TINY,
+        name: symbolItem,
+        object: symbolItem.descriptor.value,
+        equal: ": ",
+        defaultRep: Grip,
+        title: null,
+        suppressQuotes
+      }));
+    });
+
+    propsArray.push(...symbolsProps);
+  }
+
+  if (Object.keys(properties).length > max || propertiesLength > max
+  // When the object has non-enumerable properties, we don't have them in the packet,
+  // but we might want to show there's something in the object.
+  || propertiesLength > propsArray.length) {
+    // There are some undisplayed props. Then display "more...".
+    propsArray.push(span({
+      key: "more",
+      className: "more-ellipsis",
+      title: "more…"
+    }, "…"));
+  }
+
+  return unfoldProps(propsArray);
+}
+
+function unfoldProps(items) {
+  return items.reduce((res, item, index) => {
+    if (Array.isArray(item)) {
+      res = res.concat(item);
+    } else {
+      res.push(item);
+    }
+
+    // Interleave commas between elements
+    if (index !== items.length - 1) {
+      res.push(", ");
+    }
+    return res;
+  }, []);
+}
+
+/**
+ * Get props ordered by index.
+ *
+ * @param {Object} componentProps Grip Component props.
+ * @param {Object} properties Properties of the object the Grip describes.
+ * @param {Array} indexes Indexes of properties.
+ * @param {Boolean} suppressQuotes true if we should suppress quotes
+ *                  on property names.
+ * @return {Array} Props.
+ */
+function getProps(componentProps, properties, indexes, suppressQuotes) {
+  // Make indexes ordered by ascending.
+  indexes.sort(function (a, b) {
+    return a - b;
+  });
+
+  const propertiesKeys = Object.keys(properties);
+  return indexes.map(i => {
+    let name = propertiesKeys[i];
+    let value = getPropValue(properties[name]);
+
+    return PropRep(Object.assign({}, componentProps, {
+      mode: MODE.TINY,
+      name,
+      object: value,
+      equal: ": ",
+      defaultRep: Grip,
+      title: null,
+      suppressQuotes
+    }));
+  });
+}
+
+/**
+ * Get the indexes of props in the object.
+ *
+ * @param {Object} properties Props object.
+ * @param {Number} max The maximum length of indexes array.
+ * @param {Function} filter Filter the props you want.
+ * @return {Array} Indexes of interesting props in the object.
+ */
+function getPropIndexes(properties, max, filter) {
+  let indexes = [];
+
+  try {
+    let i = 0;
+    for (let name in properties) {
+      if (indexes.length >= max) {
+        return indexes;
+      }
+
+      // Type is specified in grip's "class" field and for primitive
+      // values use typeof.
+      let value = getPropValue(properties[name]);
+      let type = value.class || typeof value;
+      type = type.toLowerCase();
+
+      if (filter(type, value, name)) {
+        indexes.push(i);
+      }
+      i++;
+    }
+  } catch (err) {
+    console.error(err);
+  }
+  return indexes;
+}
+
+/**
+ * Get the actual value of a property.
+ *
+ * @param {Object} property
+ * @return {Object} Value of the property.
+ */
+function getPropValue(property) {
+  let value = property;
+  if (typeof property === "object") {
+    let keys = Object.keys(property);
+    if (keys.includes("value")) {
+      value = property.value;
+    } else if (keys.includes("getterValue")) {
+      value = property.getterValue;
+    }
+  }
+  return value;
+}
+
+// Registration
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true || !isGrip(object)) {
+    return false;
+  }
+
+  return object.preview ? typeof object.preview.ownProperties !== "undefined" : typeof object.ownPropertyLength !== "undefined";
+}
+
+const maxLengthMap = new Map();
+maxLengthMap.set(MODE.SHORT, 3);
+maxLengthMap.set(MODE.LONG, 10);
+
+// Grip is used in propIterator and has to be defined here.
+let Grip = {
+  rep: wrapRender(GripRep),
+  supportsObject,
+  maxLengthMap
+};
+
+// Exports from this module
+module.exports = Grip;
+
+/***/ }),
 /* 10 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-
-	const { wrapRender } = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders null value
-	 */
-	function Null(props) {
-	  return span({ className: "objectBox objectBox-null" }, "null");
-	}
-
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip === true) {
-	    return false;
-	  }
-
-	  if (object && object.type && object.type == "null") {
-	    return true;
-	  }
-
-	  return object == null;
-	}
-
-	// Exports from this module
-
-	module.exports = {
-	  rep: wrapRender(Null),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+const React = __webpack_require__(0);
+const InlineSVG = __webpack_require__(47);
+
+const svg = {
+  "arrow": __webpack_require__(48),
+  "open-inspector": __webpack_require__(49)
+};
+
+Svg.propTypes = {
+  className: React.PropTypes.string
+};
+
+function Svg(name, props) {
+  if (!svg[name]) {
+    throw new Error("Unknown SVG: " + name);
+  }
+  let className = name;
+  if (props && props.className) {
+    className = `${name} ${props.className}`;
+  }
+  if (name === "subSettings") {
+    className = "";
+  }
+  props = Object.assign({}, props, { className, src: svg[name] });
+  return React.createElement(InlineSVG, props);
+}
+
+module.exports = Svg;
+
+/***/ }),
 /* 11 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-
-	const {
-	  escapeString,
-	  rawCropString,
-	  sanitizeString,
-	  wrapRender
-	} = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders a string. String value is enclosed within quotes.
-	 */
-	StringRep.propTypes = {
-	  useQuotes: React.PropTypes.bool,
-	  escapeWhitespace: React.PropTypes.bool,
-	  style: React.PropTypes.object,
-	  object: React.PropTypes.string.isRequired,
-	  member: React.PropTypes.any,
-	  cropLimit: React.PropTypes.number
-	};
-
-	function StringRep(props) {
-	  let {
-	    cropLimit,
-	    object: text,
-	    member,
-	    style,
-	    useQuotes = true,
-	    escapeWhitespace = true
-	  } = props;
-
-	  let config = { className: "objectBox objectBox-string" };
-	  if (style) {
-	    config.style = style;
-	  }
-
-	  if (useQuotes) {
-	    text = escapeString(text, escapeWhitespace);
-	  } else {
-	    text = sanitizeString(text);
-	  }
-
-	  if ((!member || !member.open) && cropLimit) {
-	    text = rawCropString(text, cropLimit);
-	  }
-
-	  return span(config, text);
-	}
-
-	function supportsObject(object, type) {
-	  return type == "string";
-	}
-
-	// Exports from this module
-
-	module.exports = {
-	  rep: wrapRender(StringRep),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+var baseGetTag = __webpack_require__(12),
+    isObjectLike = __webpack_require__(15);
+
+/** `Object#toString` result references. */
+var symbolTag = '[object Symbol]';
+
+/**
+ * Checks if `value` is classified as a `Symbol` primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
+ * @example
+ *
+ * _.isSymbol(Symbol.iterator);
+ * // => true
+ *
+ * _.isSymbol('abc');
+ * // => false
+ */
+function isSymbol(value) {
+  return typeof value == 'symbol' ||
+    (isObjectLike(value) && baseGetTag(value) == symbolTag);
+}
+
+module.exports = isSymbol;
+
+
+/***/ }),
 /* 12 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-	const {
-	  escapeString,
-	  sanitizeString,
-	  isGrip,
-	  wrapRender
-	} = __webpack_require__(7);
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders a long string grip.
-	 */
-	LongStringRep.propTypes = {
-	  useQuotes: React.PropTypes.bool,
-	  escapeWhitespace: React.PropTypes.bool,
-	  style: React.PropTypes.object,
-	  cropLimit: React.PropTypes.number.isRequired,
-	  member: React.PropTypes.string,
-	  object: React.PropTypes.object.isRequired
-	};
-
-	function LongStringRep(props) {
-	  let {
-	    cropLimit,
-	    member,
-	    object,
-	    style,
-	    useQuotes = true,
-	    escapeWhitespace = true
-	  } = props;
-	  let { fullText, initial, length } = object;
-
-	  let config = {
-	    "data-link-actor-id": object.actor,
-	    className: "objectBox objectBox-string"
-	  };
-
-	  if (style) {
-	    config.style = style;
-	  }
-
-	  let string = member && member.open ? fullText || initial : initial.substring(0, cropLimit);
-
-	  if (string.length < length) {
-	    string += "\u2026";
-	  }
-	  let formattedString = useQuotes ? escapeString(string, escapeWhitespace) : sanitizeString(string);
-	  return span(config, formattedString);
-	}
-
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(object)) {
-	    return false;
-	  }
-	  return object.type === "longString";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(LongStringRep),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+var Symbol = __webpack_require__(13),
+    getRawTag = __webpack_require__(66),
+    objectToString = __webpack_require__(67);
+
+/** `Object#toString` result references. */
+var nullTag = '[object Null]',
+    undefinedTag = '[object Undefined]';
+
+/** Built-in value references. */
+var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
+
+/**
+ * The base implementation of `getTag` without fallbacks for buggy environments.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the `toStringTag`.
+ */
+function baseGetTag(value) {
+  if (value == null) {
+    return value === undefined ? undefinedTag : nullTag;
+  }
+  return (symToStringTag && symToStringTag in Object(value))
+    ? getRawTag(value)
+    : objectToString(value);
+}
+
+module.exports = baseGetTag;
+
+
+/***/ }),
 /* 13 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-
-	const { wrapRender } = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders a number
-	 */
-	Number.propTypes = {
-	  object: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.number, React.PropTypes.bool]).isRequired
-	};
-
-	function Number(props) {
-	  let value = props.object;
-
-	  return span({ className: "objectBox objectBox-number" }, stringify(value));
-	}
-
-	function stringify(object) {
-	  let isNegativeZero = Object.is(object, -0) || object.type && object.type == "-0";
-
-	  return isNegativeZero ? "-0" : String(object);
-	}
-
-	function supportsObject(object, type) {
-	  return ["boolean", "number", "-0"].includes(type);
-	}
-
-	// Exports from this module
-
-	module.exports = {
-	  rep: wrapRender(Number),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+var root = __webpack_require__(14);
+
+/** Built-in value references. */
+var Symbol = root.Symbol;
+
+module.exports = Symbol;
+
+
+/***/ }),
 /* 14 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-	const {
-	  wrapRender
-	} = __webpack_require__(7);
-	const { MODE } = __webpack_require__(1);
-
-	const ModePropType = React.PropTypes.oneOf(
-	// @TODO Change this to Object.values once it's supported in Node's version of V8
-	Object.keys(MODE).map(key => MODE[key]));
-
-	// Shortcuts
-	const DOM = React.DOM;
-
-	/**
-	 * Renders an array. The array is enclosed by left and right bracket
-	 * and the max number of rendered items depends on the current mode.
-	 */
-	ArrayRep.propTypes = {
-	  mode: ModePropType,
-	  object: React.PropTypes.array.isRequired
-	};
-
-	function ArrayRep(props) {
-	  let {
-	    object,
-	    mode = MODE.SHORT
-	  } = props;
-
-	  let items;
-	  let brackets;
-	  let needSpace = function (space) {
-	    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
-	  };
-
-	  if (mode === MODE.TINY) {
-	    let isEmpty = object.length === 0;
-	    if (isEmpty) {
-	      items = [];
-	    } else {
-	      items = [DOM.span({
-	        className: "more-ellipsis",
-	        title: "more…"
-	      }, "…")];
-	    }
-	    brackets = needSpace(false);
-	  } else {
-	    items = arrayIterator(props, object, maxLengthMap.get(mode));
-	    brackets = needSpace(items.length > 0);
-	  }
-
-	  return DOM.span({
-	    className: "objectBox objectBox-array" }, DOM.span({
-	    className: "arrayLeftBracket"
-	  }, brackets.left), ...items, DOM.span({
-	    className: "arrayRightBracket"
-	  }, brackets.right), DOM.span({
-	    className: "arrayProperties",
-	    role: "group" }));
-	}
-
-	function arrayIterator(props, array, max) {
-	  let items = [];
-	  let delim;
-
-	  for (let i = 0; i < array.length && i < max; i++) {
-	    try {
-	      let value = array[i];
-
-	      delim = i == array.length - 1 ? "" : ", ";
-
-	      items.push(ItemRep({
-	        object: value,
-	        // Hardcode tiny mode to avoid recursive handling.
-	        mode: MODE.TINY,
-	        delim: delim
-	      }));
-	    } catch (exc) {
-	      items.push(ItemRep({
-	        object: exc,
-	        mode: MODE.TINY,
-	        delim: delim
-	      }));
-	    }
-	  }
-
-	  if (array.length > max) {
-	    items.push(DOM.span({
-	      className: "more-ellipsis",
-	      title: "more…"
-	    }, "…"));
-	  }
-
-	  return items;
-	}
-
-	/**
-	 * Renders array item. Individual values are separated by a comma.
-	 */
-	ItemRep.propTypes = {
-	  object: React.PropTypes.any.isRequired,
-	  delim: React.PropTypes.string.isRequired,
-	  mode: ModePropType
-	};
-
-	function ItemRep(props) {
-	  const { Rep } = __webpack_require__(2);
-
-	  let {
-	    object,
-	    delim,
-	    mode
-	  } = props;
-	  return DOM.span({}, Rep({ object: object, mode: mode }), delim);
-	}
-
-	function supportsObject(object, type) {
-	  return Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]";
-	}
-
-	const maxLengthMap = new Map();
-	maxLengthMap.set(MODE.SHORT, 3);
-	maxLengthMap.set(MODE.LONG, 10);
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(ArrayRep),
-	  supportsObject,
-	  maxLengthMap
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+var freeGlobal = __webpack_require__(64);
+
+/** Detect free variable `self`. */
+var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
+
+/** Used as a reference to the global object. */
+var root = freeGlobal || freeSelf || Function('return this')();
+
+module.exports = root;
+
+
+/***/ }),
 /* 15 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-	const {
-	  wrapRender
-	} = __webpack_require__(7);
-	const PropRep = __webpack_require__(16);
-	const { MODE } = __webpack_require__(1);
-	// Shortcuts
-	const { span } = React.DOM;
-
-	const DEFAULT_TITLE = "Object";
-
-	/**
-	 * Renders an object. An object is represented by a list of its
-	 * properties enclosed in curly brackets.
-	 */
-	ObjectRep.propTypes = {
-	  object: React.PropTypes.object.isRequired,
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	  title: React.PropTypes.string
-	};
-
-	function ObjectRep(props) {
-	  let object = props.object;
-	  let propsArray = safePropIterator(props, object);
-
-	  if (props.mode === MODE.TINY) {
-	    const tinyModeItems = [];
-	    if (getTitle(props, object) !== DEFAULT_TITLE) {
-	      tinyModeItems.push(getTitleElement(props, object));
-	    } else {
-	      tinyModeItems.push(span({
-	        className: "objectLeftBrace"
-	      }, "{"), propsArray.length > 0 ? span({
-	        key: "more",
-	        className: "more-ellipsis",
-	        title: "more…"
-	      }, "…") : null, span({
-	        className: "objectRightBrace"
-	      }, "}"));
-	    }
-
-	    return span({ className: "objectBox objectBox-object" }, ...tinyModeItems);
-	  }
-
-	  return span({ className: "objectBox objectBox-object" }, getTitleElement(props, object), span({
-	    className: "objectLeftBrace"
-	  }, " { "), ...propsArray, span({
-	    className: "objectRightBrace"
-	  }, " }"));
-	}
-
-	function getTitleElement(props, object) {
-	  return span({ className: "objectTitle" }, getTitle(props, object));
-	}
-
-	function getTitle(props, object) {
-	  return props.title || object.class || DEFAULT_TITLE;
-	}
-
-	function safePropIterator(props, object, max) {
-	  max = typeof max === "undefined" ? 3 : max;
-	  try {
-	    return propIterator(props, object, max);
-	  } catch (err) {
-	    console.error(err);
-	  }
-	  return [];
-	}
-
-	function propIterator(props, object, max) {
-	  let isInterestingProp = (type, value) => {
-	    // Do not pick objects, it could cause recursion.
-	    return type == "boolean" || type == "number" || type == "string" && value;
-	  };
-
-	  // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=945377
-	  if (Object.prototype.toString.call(object) === "[object Generator]") {
-	    object = Object.getPrototypeOf(object);
-	  }
-
-	  // Object members with non-empty values are preferred since it gives the
-	  // user a better overview of the object.
-	  let interestingObject = getFilteredObject(object, max, isInterestingProp);
-
-	  if (Object.keys(interestingObject).length < max) {
-	    // There are not enough props yet (or at least, not enough props to
-	    // be able to know whether we should print "more…" or not).
-	    // Let's display also empty members and functions.
-	    interestingObject = Object.assign({}, interestingObject, getFilteredObject(object, max - Object.keys(interestingObject).length, (type, value) => !isInterestingProp(type, value)));
-	  }
-
-	  let propsArray = getPropsArray(interestingObject);
-	  if (Object.keys(object).length > max) {
-	    propsArray.push(span({
-	      className: "more-ellipsis",
-	      title: "more…"
-	    }, "…"));
-	  }
-
-	  return unfoldProps(propsArray);
-	}
-
-	function unfoldProps(items) {
-	  return items.reduce((res, item, index) => {
-	    if (Array.isArray(item)) {
-	      res = res.concat(item);
-	    } else {
-	      res.push(item);
-	    }
-
-	    // Interleave commas between elements
-	    if (index !== items.length - 1) {
-	      res.push(", ");
-	    }
-	    return res;
-	  }, []);
-	}
-
-	/**
-	 * Get an array of components representing the properties of the object
-	 *
-	 * @param {Object} object
-	 * @return {Array} Array of PropRep.
-	 */
-	function getPropsArray(object) {
-	  let propsArray = [];
-
-	  if (!object) {
-	    return propsArray;
-	  }
-
-	  // Hardcode tiny mode to avoid recursive handling.
-	  let mode = MODE.TINY;
-	  const objectKeys = Object.keys(object);
-	  return objectKeys.map((name, i) => PropRep({
-	    mode,
-	    name,
-	    object: object[name],
-	    equal: ": "
-	  }));
-	}
-
-	/**
-	 * Get a copy of the object filtered by a given predicate.
-	 *
-	 * @param {Object} object.
-	 * @param {Number} max The maximum length of keys array.
-	 * @param {Function} filter Filter the props you want.
-	 * @return {Object} the filtered object.
-	 */
-	function getFilteredObject(object, max, filter) {
-	  let filteredObject = {};
-
-	  try {
-	    for (let name in object) {
-	      if (Object.keys(filteredObject).length >= max) {
-	        return filteredObject;
-	      }
-
-	      let value;
-	      try {
-	        value = object[name];
-	      } catch (exc) {
-	        continue;
-	      }
-
-	      let t = typeof value;
-	      if (filter(t, value)) {
-	        filteredObject[name] = value;
-	      }
-	    }
-	  } catch (err) {
-	    console.error(err);
-	  }
-	  return filteredObject;
-	}
-
-	function supportsObject(object, type) {
-	  return true;
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(ObjectRep),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports) {
+
+/**
+ * Checks if `value` is object-like. A value is object-like if it's not `null`
+ * and has a `typeof` result of "object".
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+ * @example
+ *
+ * _.isObjectLike({});
+ * // => true
+ *
+ * _.isObjectLike([1, 2, 3]);
+ * // => true
+ *
+ * _.isObjectLike(_.noop);
+ * // => false
+ *
+ * _.isObjectLike(null);
+ * // => false
+ */
+function isObjectLike(value) {
+  return value != null && typeof value == 'object';
+}
+
+module.exports = isObjectLike;
+
+
+/***/ }),
 /* 16 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-	const {
-	  maybeEscapePropertyName,
-	  wrapRender
-	} = __webpack_require__(7);
-	const { MODE } = __webpack_require__(1);
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Property for Obj (local JS objects), Grip (remote JS objects)
-	 * and GripMap (remote JS maps and weakmaps) reps.
-	 * It's used to render object properties.
-	 */
-	PropRep.propTypes = {
-	  // Property name.
-	  name: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.object]).isRequired,
-	  // Equal character rendered between property name and value.
-	  equal: React.PropTypes.string,
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	  onDOMNodeMouseOver: React.PropTypes.func,
-	  onDOMNodeMouseOut: React.PropTypes.func,
-	  onInspectIconClick: React.PropTypes.func,
-	  // Normally a PropRep will quote a property name that isn't valid
-	  // when unquoted; but this flag can be used to suppress the
-	  // quoting.
-	  suppressQuotes: React.PropTypes.bool
-	};
-
-	/**
-	 * Function that given a name, a delimiter and an object returns an array
-	 * of React elements representing an object property (e.g. `name: value`)
-	 *
-	 * @param {Object} props
-	 * @return {Array} Array of React elements.
-	 */
-	function PropRep(props) {
-	  const Grip = __webpack_require__(17);
-	  const { Rep } = __webpack_require__(2);
-
-	  let {
-	    name,
-	    mode,
-	    equal,
-	    suppressQuotes
-	  } = props;
-
-	  let key;
-	  // The key can be a simple string, for plain objects,
-	  // or another object for maps and weakmaps.
-	  if (typeof name === "string") {
-	    if (!suppressQuotes) {
-	      name = maybeEscapePropertyName(name);
-	    }
-	    key = span({ "className": "nodeName" }, name);
-	  } else {
-	    key = Rep(Object.assign({}, props, {
-	      className: "nodeName",
-	      object: name,
-	      mode: mode || MODE.TINY,
-	      defaultRep: Grip
-	    }));
-	  }
-
-	  return [key, span({
-	    "className": "objectEqual"
-	  }, equal), Rep(Object.assign({}, props))];
-	}
-
-	// Exports from this module
-	module.exports = wrapRender(PropRep);
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+
+const {
+  containsURL,
+  isURL,
+  escapeString,
+  getGripType,
+  rawCropString,
+  sanitizeString,
+  wrapRender,
+  tokenSplitRegex
+} = __webpack_require__(1);
+
+// Shortcuts
+const { a, span } = React.DOM;
+
+/**
+ * Renders a string. String value is enclosed within quotes.
+ */
+StringRep.propTypes = {
+  useQuotes: React.PropTypes.bool,
+  escapeWhitespace: React.PropTypes.bool,
+  style: React.PropTypes.object,
+  object: React.PropTypes.string.isRequired,
+  member: React.PropTypes.any,
+  cropLimit: React.PropTypes.number,
+  openLink: React.PropTypes.func
+};
+
+function StringRep(props) {
+  let {
+    cropLimit,
+    object: text,
+    member,
+    style,
+    useQuotes = true,
+    escapeWhitespace = true,
+    openLink
+  } = props;
+
+  let config = { className: "objectBox objectBox-string" };
+  if (style) {
+    config.style = style;
+  }
+
+  if (useQuotes) {
+    text = escapeString(text, escapeWhitespace);
+  } else {
+    text = sanitizeString(text);
+  }
+
+  if ((!member || !member.open) && cropLimit) {
+    text = rawCropString(text, cropLimit);
+  }
+
+  if (!containsURL(text)) {
+    return span(config, text);
+  }
+
+  const items = [];
+
+  // As we walk through the tokens of the source string, we make sure to preserve
+  // the original whitespace that separated the tokens.
+  let tokens = text.split(tokenSplitRegex);
+  let textIndex = 0;
+  let tokenStart;
+  tokens.forEach((token, i) => {
+    tokenStart = text.indexOf(token, textIndex);
+    if (isURL(token)) {
+      items.push(text.slice(textIndex, tokenStart));
+      textIndex = tokenStart + token.length;
+
+      items.push(a({
+        className: "url",
+        title: token,
+        href: token,
+        draggable: false,
+        onClick: openLink ? () => openLink(token) : null
+      }, token));
+    }
+  });
+
+  // Clean up any non-URL text at the end of the source string.
+  items.push(text.slice(textIndex, text.length));
+  return span(config, ...items);
+}
+
+function supportsObject(object, noGrip = false) {
+  return getGripType(object, noGrip) == "string";
+}
+
+// Exports from this module
+
+module.exports = {
+  rep: wrapRender(StringRep),
+  supportsObject
+};
+
+/***/ }),
 /* 17 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-	// Dependencies
-	const {
-	  isGrip,
-	  wrapRender
-	} = __webpack_require__(7);
-	const PropRep = __webpack_require__(16);
-	const { MODE } = __webpack_require__(1);
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders generic grip. Grip is client representation
-	 * of remote JS object and is used as an input object
-	 * for this rep component.
-	 */
-	GripRep.propTypes = {
-	  object: React.PropTypes.object.isRequired,
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	  isInterestingProp: React.PropTypes.func,
-	  title: React.PropTypes.string,
-	  onDOMNodeMouseOver: React.PropTypes.func,
-	  onDOMNodeMouseOut: React.PropTypes.func,
-	  onInspectIconClick: React.PropTypes.func,
-	  noGrip: React.PropTypes.bool
-	};
-
-	const DEFAULT_TITLE = "Object";
-
-	function GripRep(props) {
-	  let {
-	    mode = MODE.SHORT,
-	    object
-	  } = props;
-
-	  const config = {
-	    "data-link-actor-id": object.actor,
-	    className: "objectBox objectBox-object"
-	  };
-
-	  if (mode === MODE.TINY) {
-	    let propertiesLength = getPropertiesLength(object);
-
-	    const tinyModeItems = [];
-	    if (getTitle(props, object) !== DEFAULT_TITLE) {
-	      tinyModeItems.push(getTitleElement(props, object));
-	    } else {
-	      tinyModeItems.push(span({
-	        className: "objectLeftBrace"
-	      }, "{"), propertiesLength > 0 ? span({
-	        key: "more",
-	        className: "more-ellipsis",
-	        title: "more…"
-	      }, "…") : null, span({
-	        className: "objectRightBrace"
-	      }, "}"));
-	    }
-
-	    return span(config, ...tinyModeItems);
-	  }
-
-	  let propsArray = safePropIterator(props, object, maxLengthMap.get(mode));
-
-	  return span(config, getTitleElement(props, object), span({
-	    className: "objectLeftBrace"
-	  }, " { "), ...propsArray, span({
-	    className: "objectRightBrace"
-	  }, " }"));
-	}
-
-	function getTitleElement(props, object) {
-	  return span({
-	    className: "objectTitle"
-	  }, getTitle(props, object));
-	}
-
-	function getTitle(props, object) {
-	  return props.title || object.class || DEFAULT_TITLE;
-	}
-
-	function getPropertiesLength(object) {
-	  let propertiesLength = object.preview && object.preview.ownPropertiesLength ? object.preview.ownPropertiesLength : object.ownPropertyLength;
-
-	  if (object.preview && object.preview.safeGetterValues) {
-	    propertiesLength += Object.keys(object.preview.safeGetterValues).length;
-	  }
-
-	  if (object.preview && object.preview.ownSymbols) {
-	    propertiesLength += object.preview.ownSymbolsLength;
-	  }
-
-	  return propertiesLength;
-	}
-
-	function safePropIterator(props, object, max) {
-	  max = typeof max === "undefined" ? maxLengthMap.get(MODE.SHORT) : max;
-	  try {
-	    return propIterator(props, object, max);
-	  } catch (err) {
-	    console.error(err);
-	  }
-	  return [];
-	}
-
-	function propIterator(props, object, max) {
-	  if (object.preview && Object.keys(object.preview).includes("wrappedValue")) {
-	    const { Rep } = __webpack_require__(2);
-
-	    return [Rep({
-	      object: object.preview.wrappedValue,
-	      mode: props.mode || MODE.TINY,
-	      defaultRep: Grip
-	    })];
-	  }
-
-	  // Property filter. Show only interesting properties to the user.
-	  let isInterestingProp = props.isInterestingProp || ((type, value) => {
-	    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
-	  });
-
-	  let properties = object.preview ? object.preview.ownProperties : {};
-	  let propertiesLength = getPropertiesLength(object);
-
-	  if (object.preview && object.preview.safeGetterValues) {
-	    properties = Object.assign({}, properties, object.preview.safeGetterValues);
-	  }
-
-	  let indexes = getPropIndexes(properties, max, isInterestingProp);
-	  if (indexes.length < max && indexes.length < propertiesLength) {
-	    // There are not enough props yet. Then add uninteresting props to display them.
-	    indexes = indexes.concat(getPropIndexes(properties, max - indexes.length, (t, value, name) => {
-	      return !isInterestingProp(t, value, name);
-	    }));
-	  }
-
-	  // The server synthesizes some property names for a Proxy, like
-	  // <target> and <handler>; we don't want to quote these because,
-	  // as synthetic properties, they appear more natural when
-	  // unquoted.
-	  const suppressQuotes = object.class === "Proxy";
-	  let propsArray = getProps(props, properties, indexes, suppressQuotes);
-
-	  // Show symbols.
-	  if (object.preview && object.preview.ownSymbols) {
-	    const { ownSymbols } = object.preview;
-	    const length = max - indexes.length;
-
-	    const symbolsProps = ownSymbols.slice(0, length).map(symbolItem => {
-	      return PropRep(Object.assign({}, props, {
-	        mode: MODE.TINY,
-	        name: symbolItem,
-	        object: symbolItem.descriptor.value,
-	        equal: ": ",
-	        defaultRep: Grip,
-	        title: null,
-	        suppressQuotes
-	      }));
-	    });
-
-	    propsArray.push(...symbolsProps);
-	  }
-
-	  if (Object.keys(properties).length > max || propertiesLength > max
-	  // When the object has non-enumerable properties, we don't have them in the packet,
-	  // but we might want to show there's something in the object.
-	  || propertiesLength > propsArray.length) {
-	    // There are some undisplayed props. Then display "more...".
-	    propsArray.push(span({
-	      key: "more",
-	      className: "more-ellipsis",
-	      title: "more…"
-	    }, "…"));
-	  }
-
-	  return unfoldProps(propsArray);
-	}
-
-	function unfoldProps(items) {
-	  return items.reduce((res, item, index) => {
-	    if (Array.isArray(item)) {
-	      res = res.concat(item);
-	    } else {
-	      res.push(item);
-	    }
-
-	    // Interleave commas between elements
-	    if (index !== items.length - 1) {
-	      res.push(", ");
-	    }
-	    return res;
-	  }, []);
-	}
-
-	/**
-	 * Get props ordered by index.
-	 *
-	 * @param {Object} componentProps Grip Component props.
-	 * @param {Object} properties Properties of the object the Grip describes.
-	 * @param {Array} indexes Indexes of properties.
-	 * @param {Boolean} suppressQuotes true if we should suppress quotes
-	 *                  on property names.
-	 * @return {Array} Props.
-	 */
-	function getProps(componentProps, properties, indexes, suppressQuotes) {
-	  // Make indexes ordered by ascending.
-	  indexes.sort(function (a, b) {
-	    return a - b;
-	  });
-
-	  const propertiesKeys = Object.keys(properties);
-	  return indexes.map(i => {
-	    let name = propertiesKeys[i];
-	    let value = getPropValue(properties[name]);
-
-	    return PropRep(Object.assign({}, componentProps, {
-	      mode: MODE.TINY,
-	      name,
-	      object: value,
-	      equal: ": ",
-	      defaultRep: Grip,
-	      title: null,
-	      suppressQuotes
-	    }));
-	  });
-	}
-
-	/**
-	 * Get the indexes of props in the object.
-	 *
-	 * @param {Object} properties Props object.
-	 * @param {Number} max The maximum length of indexes array.
-	 * @param {Function} filter Filter the props you want.
-	 * @return {Array} Indexes of interesting props in the object.
-	 */
-	function getPropIndexes(properties, max, filter) {
-	  let indexes = [];
-
-	  try {
-	    let i = 0;
-	    for (let name in properties) {
-	      if (indexes.length >= max) {
-	        return indexes;
-	      }
-
-	      // Type is specified in grip's "class" field and for primitive
-	      // values use typeof.
-	      let value = getPropValue(properties[name]);
-	      let type = value.class || typeof value;
-	      type = type.toLowerCase();
-
-	      if (filter(type, value, name)) {
-	        indexes.push(i);
-	      }
-	      i++;
-	    }
-	  } catch (err) {
-	    console.error(err);
-	  }
-	  return indexes;
-	}
-
-	/**
-	 * Get the actual value of a property.
-	 *
-	 * @param {Object} property
-	 * @return {Object} Value of the property.
-	 */
-	function getPropValue(property) {
-	  let value = property;
-	  if (typeof property === "object") {
-	    let keys = Object.keys(property);
-	    if (keys.includes("value")) {
-	      value = property.value;
-	    } else if (keys.includes("getterValue")) {
-	      value = property.getterValue;
-	    }
-	  }
-	  return value;
-	}
-
-	// Registration
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(object)) {
-	    return false;
-	  }
-
-	  return object.preview ? typeof object.preview.ownProperties !== "undefined" : typeof object.ownPropertyLength !== "undefined";
-	}
-
-	const maxLengthMap = new Map();
-	maxLengthMap.set(MODE.SHORT, 3);
-	maxLengthMap.set(MODE.LONG, 10);
-
-	// Grip is used in propIterator and has to be defined here.
-	let Grip = {
-	  rep: wrapRender(GripRep),
-	  supportsObject,
-	  maxLengthMap
-	};
-
-	// Exports from this module
-	module.exports = Grip;
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+const {
+  wrapRender
+} = __webpack_require__(1);
+const { MODE } = __webpack_require__(2);
+
+const ModePropType = React.PropTypes.oneOf(
+// @TODO Change this to Object.values once it's supported in Node's version of V8
+Object.keys(MODE).map(key => MODE[key]));
+
+// Shortcuts
+const DOM = React.DOM;
+
+/**
+ * Renders an array. The array is enclosed by left and right bracket
+ * and the max number of rendered items depends on the current mode.
+ */
+ArrayRep.propTypes = {
+  mode: ModePropType,
+  object: React.PropTypes.array.isRequired
+};
+
+function ArrayRep(props) {
+  let {
+    object,
+    mode = MODE.SHORT
+  } = props;
+
+  let items;
+  let brackets;
+  let needSpace = function (space) {
+    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
+  };
+
+  if (mode === MODE.TINY) {
+    let isEmpty = object.length === 0;
+    if (isEmpty) {
+      items = [];
+    } else {
+      items = [DOM.span({
+        className: "more-ellipsis",
+        title: "more…"
+      }, "…")];
+    }
+    brackets = needSpace(false);
+  } else {
+    items = arrayIterator(props, object, maxLengthMap.get(mode));
+    brackets = needSpace(items.length > 0);
+  }
+
+  return DOM.span({
+    className: "objectBox objectBox-array" }, DOM.span({
+    className: "arrayLeftBracket"
+  }, brackets.left), ...items, DOM.span({
+    className: "arrayRightBracket"
+  }, brackets.right), DOM.span({
+    className: "arrayProperties",
+    role: "group" }));
+}
+
+function arrayIterator(props, array, max) {
+  let items = [];
+
+  for (let i = 0; i < array.length && i < max; i++) {
+    let config = {
+      mode: MODE.TINY,
+      delim: i == array.length - 1 ? "" : ", "
+    };
+    let item;
+
+    try {
+      item = ItemRep(Object.assign({}, props, config, {
+        object: array[i]
+      }));
+    } catch (exc) {
+      item = ItemRep(Object.assign({}, props, config, {
+        object: exc
+      }));
+    }
+    items.push(item);
+  }
+
+  if (array.length > max) {
+    items.push(DOM.span({
+      className: "more-ellipsis",
+      title: "more…"
+    }, "…"));
+  }
+
+  return items;
+}
+
+/**
+ * Renders array item. Individual values are separated by a comma.
+ */
+ItemRep.propTypes = {
+  object: React.PropTypes.any.isRequired,
+  delim: React.PropTypes.string.isRequired,
+  mode: ModePropType
+};
+
+function ItemRep(props) {
+  const { Rep } = __webpack_require__(3);
+
+  let {
+    object,
+    delim,
+    mode
+  } = props;
+  return DOM.span({}, Rep(Object.assign({}, props, {
+    object: object,
+    mode: mode
+  })), delim);
+}
+
+function supportsObject(object) {
+  return Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]";
+}
+
+const maxLengthMap = new Map();
+maxLengthMap.set(MODE.SHORT, 3);
+maxLengthMap.set(MODE.LONG, 10);
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(ArrayRep),
+  supportsObject,
+  maxLengthMap
+};
+
+/***/ }),
 /* 18 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-
-	const { wrapRender } = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders a symbol.
-	 */
-	SymbolRep.propTypes = {
-	  object: React.PropTypes.object.isRequired
-	};
-
-	function SymbolRep(props) {
-	  let {
-	    className = "objectBox objectBox-symbol",
-	    object
-	  } = props;
-	  let { name } = object;
-
-	  return span({ className }, `Symbol(${name || ""})`);
-	}
-
-	function supportsObject(object, type) {
-	  return type == "symbol";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(SymbolRep),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports) {
+
+/* 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/. */
+
+module.exports = {
+  ELEMENT_NODE: 1,
+  ATTRIBUTE_NODE: 2,
+  TEXT_NODE: 3,
+  CDATA_SECTION_NODE: 4,
+  ENTITY_REFERENCE_NODE: 5,
+  ENTITY_NODE: 6,
+  PROCESSING_INSTRUCTION_NODE: 7,
+  COMMENT_NODE: 8,
+  DOCUMENT_NODE: 9,
+  DOCUMENT_TYPE_NODE: 10,
+  DOCUMENT_FRAGMENT_NODE: 11,
+  NOTATION_NODE: 12,
+
+  // DocumentPosition
+  DOCUMENT_POSITION_DISCONNECTED: 0x01,
+  DOCUMENT_POSITION_PRECEDING: 0x02,
+  DOCUMENT_POSITION_FOLLOWING: 0x04,
+  DOCUMENT_POSITION_CONTAINS: 0x08,
+  DOCUMENT_POSITION_CONTAINED_BY: 0x10,
+  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
+};
+
+/***/ }),
 /* 19 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-
-	const { wrapRender } = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders a Infinity object
-	 */
-	InfinityRep.propTypes = {
-	  object: React.PropTypes.object.isRequired
-	};
-
-	function InfinityRep(props) {
-	  const {
-	    object
-	  } = props;
-
-	  return span({ className: "objectBox objectBox-number" }, object.type);
-	}
-
-	function supportsObject(object, type) {
-	  return type == "Infinity" || type == "-Infinity";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(InfinityRep),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+const {
+  getGripType,
+  isGrip,
+  wrapRender
+} = __webpack_require__(1);
+const { MODE } = __webpack_require__(2);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders an array. The array is enclosed by left and right bracket
+ * and the max number of rendered items depends on the current mode.
+ */
+GripArray.propTypes = {
+  object: React.PropTypes.object.isRequired,
+  // @TODO Change this to Object.values once it's supported in Node's version of V8
+  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  provider: React.PropTypes.object,
+  onDOMNodeMouseOver: React.PropTypes.func,
+  onDOMNodeMouseOut: React.PropTypes.func,
+  onInspectIconClick: React.PropTypes.func
+};
+
+function GripArray(props) {
+  let {
+    object,
+    mode = MODE.SHORT
+  } = props;
+
+  let items;
+  let brackets;
+  let needSpace = function (space) {
+    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
+  };
+
+  if (mode === MODE.TINY) {
+    let objectLength = getLength(object);
+    let isEmpty = objectLength === 0;
+    if (isEmpty) {
+      items = [];
+    } else {
+      items = [span({
+        className: "more-ellipsis",
+        title: "more…"
+      }, "…")];
+    }
+    brackets = needSpace(false);
+  } else {
+    let max = maxLengthMap.get(mode);
+    items = arrayIterator(props, object, max);
+    brackets = needSpace(items.length > 0);
+  }
+
+  let title = getTitle(props, object);
+
+  return span({
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-array" }, title, span({
+    className: "arrayLeftBracket"
+  }, brackets.left), ...interleaveCommas(items), span({
+    className: "arrayRightBracket"
+  }, brackets.right), span({
+    className: "arrayProperties",
+    role: "group" }));
+}
+
+function interleaveCommas(items) {
+  return items.reduce((res, item, index) => {
+    if (index !== items.length - 1) {
+      return res.concat(item, ", ");
+    }
+    return res.concat(item);
+  }, []);
+}
+
+function getLength(grip) {
+  if (!grip.preview) {
+    return 0;
+  }
+
+  return grip.preview.length || grip.preview.childNodesLength || 0;
+}
+
+function getTitle(props, object) {
+  if (props.mode === MODE.TINY) {
+    return "";
+  }
+
+  let title = props.title || object.class || "Array";
+  return span({
+    className: "objectTitle"
+  }, title + " ");
+}
+
+function getPreviewItems(grip) {
+  if (!grip.preview) {
+    return null;
+  }
+
+  return grip.preview.items || grip.preview.childNodes || [];
+}
+
+function arrayIterator(props, grip, max) {
+  let { Rep } = __webpack_require__(3);
+
+  let items = [];
+  const gripLength = getLength(grip);
+
+  if (!gripLength) {
+    return items;
+  }
+
+  const previewItems = getPreviewItems(grip);
+  let provider = props.provider;
+
+  let emptySlots = 0;
+  let foldedEmptySlots = 0;
+  items = previewItems.reduce((res, itemGrip) => {
+    if (res.length >= max) {
+      return res;
+    }
+
+    let object;
+    try {
+      if (!provider && itemGrip === null) {
+        emptySlots++;
+        return res;
+      }
+
+      object = provider ? provider.getValue(itemGrip) : itemGrip;
+    } catch (exc) {
+      object = exc;
+    }
+
+    if (emptySlots > 0) {
+      res.push(getEmptySlotsElement(emptySlots));
+      foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
+      emptySlots = 0;
+    }
+
+    if (res.length < max) {
+      res.push(Rep(Object.assign({}, props, {
+        object,
+        mode: MODE.TINY,
+        // Do not propagate title to array items reps
+        title: undefined
+      })));
+    }
+
+    return res;
+  }, []);
+
+  // Handle trailing empty slots if there are some.
+  if (items.length < max && emptySlots > 0) {
+    items.push(getEmptySlotsElement(emptySlots));
+    foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
+  }
+
+  const itemsShown = items.length + foldedEmptySlots;
+  if (gripLength > itemsShown) {
+    items.push(span({
+      className: "more-ellipsis",
+      title: "more…"
+    }, "…"));
+  }
+
+  return items;
+}
+
+function getEmptySlotsElement(number) {
+  // TODO: Use l10N - See https://github.com/devtools-html/reps/issues/141
+  return `<${number} empty slot${number > 1 ? "s" : ""}>`;
+}
+
+function supportsObject(grip, noGrip = false) {
+  if (noGrip === true || !isGrip(grip)) {
+    return false;
+  }
+
+  return grip.preview && (grip.preview.kind == "ArrayLike" || getGripType(grip, noGrip) === "DocumentFragment");
+}
+
+const maxLengthMap = new Map();
+maxLengthMap.set(MODE.SHORT, 3);
+maxLengthMap.set(MODE.LONG, 10);
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(GripArray),
+  supportsObject,
+  maxLengthMap
+};
+
+/***/ }),
 /* 20 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-
-	const { wrapRender } = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders a NaN object
-	 */
-	function NaNRep(props) {
-	  return span({ className: "objectBox objectBox-nan" }, "NaN");
-	}
-
-	function supportsObject(object, type) {
-	  return type == "NaN";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(NaNRep),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+// Shortcuts
+const { span } = React.DOM;
+const {
+  wrapRender
+} = __webpack_require__(1);
+const PropRep = __webpack_require__(4);
+const { MODE } = __webpack_require__(2);
+/**
+ * Renders an map entry. A map entry is represented by its key, a column and its value.
+ */
+GripMapEntry.propTypes = {
+  object: React.PropTypes.object,
+  // @TODO Change this to Object.values once it's supported in Node's version of V8
+  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  onDOMNodeMouseOver: React.PropTypes.func,
+  onDOMNodeMouseOut: React.PropTypes.func,
+  onInspectIconClick: React.PropTypes.func
+};
+
+function GripMapEntry(props) {
+  const {
+    object
+  } = props;
+
+  const {
+    key,
+    value
+  } = object.preview;
+
+  return span({
+    className: "objectBox objectBox-map-entry"
+  }, ...PropRep(Object.assign({}, props, {
+    name: key,
+    object: value,
+    equal: " \u2192 ",
+    title: null,
+    suppressQuotes: false
+  })));
+}
+
+function supportsObject(grip, noGrip = false) {
+  if (noGrip === true) {
+    return false;
+  }
+  return grip && grip.type === "mapEntry" && grip.preview;
+}
+
+function createGripMapEntry(key, value) {
+  return {
+    type: "mapEntry",
+    preview: {
+      key,
+      value
+    }
+  };
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(GripMapEntry),
+  createGripMapEntry,
+  supportsObject
+};
+
+/***/ }),
 /* 21 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-	const {
-	  wrapRender
-	} = __webpack_require__(7);
-	const { MODE } = __webpack_require__(1);
-	// Shortcuts
-	const {
-	  span
-	} = React.DOM;
-	/**
-	 * Renders an object. An object is represented by a list of its
-	 * properties enclosed in curly brackets.
-	 */
-	Accessor.propTypes = {
-	  object: React.PropTypes.object.isRequired,
-	  mode: React.PropTypes.oneOf(Object.values(MODE))
-	};
-
-	function Accessor(props) {
-	  const {
-	    object
-	  } = props;
-
-	  const accessors = [];
-	  if (hasGetter(object)) {
-	    accessors.push("Getter");
-	  }
-	  if (hasSetter(object)) {
-	    accessors.push("Setter");
-	  }
-	  const title = accessors.join(" & ");
-
-	  return span({ className: "objectBox objectBox-accessor" }, span({
-	    className: "objectTitle"
-	  }, title));
-	}
-
-	function hasGetter(object) {
-	  return object && object.get && object.get.type !== "undefined";
-	}
-
-	function hasSetter(object) {
-	  return object && object.set && object.set.type !== "undefined";
-	}
-
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip !== true && (hasGetter(object) || hasSetter(object))) {
-	    return true;
-	  }
-
-	  return false;
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(Accessor),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+const get = __webpack_require__(61);
+const has = __webpack_require__(101);
+const { maybeEscapePropertyName } = __webpack_require__(1);
+const ArrayRep = __webpack_require__(17);
+const GripArrayRep = __webpack_require__(19);
+const GripMapEntryRep = __webpack_require__(20);
+
+const NODE_TYPES = {
+  BUCKET: Symbol("[n…n]"),
+  DEFAULT_PROPERTIES: Symbol("[default properties]"),
+  ENTRIES: Symbol("<entries>"),
+  GET: Symbol("<get>"),
+  GRIP: Symbol("GRIP"),
+  MAP_ENTRY_KEY: Symbol("<key>"),
+  MAP_ENTRY_VALUE: Symbol("<value>"),
+  PROMISE_REASON: Symbol("<reason>"),
+  PROMISE_STATE: Symbol("<state>"),
+  PROMISE_VALUE: Symbol("<value>"),
+  SET: Symbol("<set>"),
+  PROTOTYPE: Symbol("__proto__")
+};
+
+let WINDOW_PROPERTIES = {};
+
+if (typeof window === "object") {
+  WINDOW_PROPERTIES = Object.getOwnPropertyNames(window);
+}
+
+const SAFE_PATH_PREFIX = "##-";
+
+function getType(item) {
+  return item.type;
+}
+
+function getValue(item) {
+  if (has(item, "contents.value")) {
+    return get(item, "contents.value");
+  }
+
+  if (has(item, "contents.getterValue")) {
+    return get(item, "contents.getterValue", undefined);
+  }
+
+  if (nodeHasAccessors(item)) {
+    return item.contents;
+  }
+
+  return undefined;
+}
+
+function nodeIsBucket(item) {
+  return getType(item) === NODE_TYPES.BUCKET;
+}
+
+function nodeIsEntries(item) {
+  return getType(item) === NODE_TYPES.ENTRIES;
+}
+
+function nodeIsMapEntry(item) {
+  return GripMapEntryRep.supportsObject(getValue(item));
+}
+
+function nodeHasChildren(item) {
+  return Array.isArray(item.contents) || nodeIsBucket(item);
+}
+
+function nodeIsObject(item) {
+  const value = getValue(item);
+  return value && value.type === "object";
+}
+
+function nodeIsArrayLike(item) {
+  const value = getValue(item);
+  return GripArrayRep.supportsObject(value) || ArrayRep.supportsObject(value);
+}
+
+function nodeIsFunction(item) {
+  const value = getValue(item);
+  return value && value.class === "Function";
+}
+
+function nodeIsOptimizedOut(item) {
+  const value = getValue(item);
+  return !nodeHasChildren(item) && value && value.optimizedOut;
+}
+
+function nodeIsMissingArguments(item) {
+  const value = getValue(item);
+  return !nodeHasChildren(item) && value && value.missingArguments;
+}
+
+function nodeHasProperties(item) {
+  return !nodeHasChildren(item) && nodeIsObject(item);
+}
+
+function nodeIsPrimitive(item) {
+  return !nodeHasChildren(item) && !nodeHasProperties(item) && !nodeIsEntries(item) && !nodeIsMapEntry(item) && !nodeHasAccessors(item);
+}
+
+function nodeIsDefaultProperties(item) {
+  return getType(item) === NODE_TYPES.DEFAULT_PROPERTIES;
+}
+
+function isDefaultWindowProperty(name) {
+  return WINDOW_PROPERTIES.includes(name);
+}
+
+function nodeIsPromise(item) {
+  const value = getValue(item);
+  if (!value) {
+    return false;
+  }
+
+  return value.class == "Promise";
+}
+
+function nodeIsPrototype(item) {
+  return getType(item) === NODE_TYPES.PROTOTYPE;
+}
+
+function nodeIsWindow(item) {
+  const value = getValue(item);
+  if (!value) {
+    return false;
+  }
+
+  return value.class == "Window";
+}
+
+function nodeIsGetter(item) {
+  return getType(item) === NODE_TYPES.GET;
+}
+
+function nodeIsSetter(item) {
+  return getType(item) === NODE_TYPES.SET;
+}
+
+function nodeHasAccessors(item) {
+  return !!getNodeGetter(item) || !!getNodeSetter(item);
+}
+
+function nodeSupportsBucketing(item) {
+  return nodeIsArrayLike(item) || nodeIsEntries(item);
+}
+
+function nodeHasEntries(item) {
+  const value = getValue(item);
+  if (!value) {
+    return false;
+  }
+
+  return value.class === "Map" || value.class === "Set" || value.class === "WeakMap" || value.class === "WeakSet";
+}
+
+function nodeHasAllEntriesInPreview(item) {
+  const { preview } = getValue(item) || {};
+  if (!preview) {
+    return false;
+  }
+
+  const {
+    entries,
+    items,
+    length,
+    size
+  } = preview;
+
+  return entries ? entries.length === size : items.length === length;
+}
+
+function makeNodesForPromiseProperties(item) {
+  const { promiseState: { reason, value, state } } = getValue(item);
+
+  const properties = [];
+
+  if (state) {
+    properties.push(createNode(item, "<state>", `${item.path}/${SAFE_PATH_PREFIX}state`, { value: state }, NODE_TYPES.PROMISE_STATE));
+  }
+
+  if (reason) {
+    properties.push(createNode(item, "<reason>", `${item.path}/${SAFE_PATH_PREFIX}reason`, { value: reason }, NODE_TYPES.PROMISE_REASON));
+  }
+
+  if (value) {
+    properties.push(createNode(item, "<value>", `${item.path}/${SAFE_PATH_PREFIX}value`, { value: value }, NODE_TYPES.PROMISE_VALUE));
+  }
+
+  return properties;
+}
+
+function makeNodesForEntries(item) {
+  const { path } = item;
+  const { preview } = getValue(item);
+  const nodeName = "<entries>";
+  const entriesPath = `${path}/${SAFE_PATH_PREFIX}entries`;
+
+  if (nodeHasAllEntriesInPreview(item)) {
+    let entriesNodes = [];
+    if (preview.entries) {
+      entriesNodes = preview.entries.map(([key, value], index) => {
+        return createNode(item, index, `${entriesPath}/${index}`, {
+          value: GripMapEntryRep.createGripMapEntry(key, value)
+        });
+      });
+    } else if (preview.items) {
+      entriesNodes = preview.items.map((value, index) => {
+        return createNode(item, index, `${entriesPath}/${index}`, { value });
+      });
+    }
+    return createNode(item, nodeName, entriesPath, entriesNodes, NODE_TYPES.ENTRIES);
+  }
+  return createNode(item, nodeName, entriesPath, null, NODE_TYPES.ENTRIES);
+}
+
+function makeNodesForMapEntry(item) {
+  const nodeValue = getValue(item);
+  if (!nodeValue || !nodeValue.preview) {
+    return [];
+  }
+
+  const { key, value } = nodeValue.preview;
+  const path = item.path;
+
+  return [createNode(item, "<key>", `${path}/##key`, { value: key }, NODE_TYPES.MAP_ENTRY_KEY), createNode(item, "<value>", `${path}/##value`, { value }, NODE_TYPES.MAP_ENTRY_VALUE)];
+}
+
+function getNodeGetter(item) {
+  return get(item, "contents.get", undefined);
+}
+
+function getNodeSetter(item) {
+  return get(item, "contents.set", undefined);
+}
+
+function makeNodesForAccessors(item) {
+  const accessors = [];
+
+  const getter = getNodeGetter(item);
+  if (getter && getter.type !== "undefined") {
+    accessors.push(createNode(item, "<get>", `${item.path}/${SAFE_PATH_PREFIX}get`, { value: getter }, NODE_TYPES.GET));
+  }
+
+  const setter = getNodeSetter(item);
+  if (setter && setter.type !== "undefined") {
+    accessors.push(createNode(item, "<set>", `${item.path}/${SAFE_PATH_PREFIX}set`, { value: setter }, NODE_TYPES.SET));
+  }
+
+  return accessors;
+}
+
+function sortProperties(properties) {
+  return properties.sort((a, b) => {
+    // Sort numbers in ascending order and sort strings lexicographically
+    const aInt = parseInt(a, 10);
+    const bInt = parseInt(b, 10);
+
+    if (isNaN(aInt) || isNaN(bInt)) {
+      return a > b ? 1 : -1;
+    }
+
+    return aInt - bInt;
+  });
+}
+
+function makeNumericalBuckets(propertiesNames, parent, ownProperties, startIndex = 0) {
+  const parentPath = parent.path;
+  const numProperties = propertiesNames.length;
+
+  // We want to have at most a hundred slices.
+  const bucketSize = 10 ** Math.max(2, Math.ceil(Math.log10(numProperties)) - 2);
+  const numBuckets = Math.ceil(numProperties / bucketSize);
+
+  let buckets = [];
+  for (let i = 1; i <= numBuckets; i++) {
+    const minKey = (i - 1) * bucketSize;
+    const maxKey = Math.min(i * bucketSize - 1, numProperties - 1);
+
+    if (maxKey === minKey) {
+      const name = propertiesNames[maxKey];
+      buckets.push(createNode(parent, name, `${parentPath}/${name}`, ownProperties[name]));
+    } else {
+      const minIndex = startIndex + minKey;
+      const maxIndex = startIndex + maxKey;
+      const bucketKey = `${SAFE_PATH_PREFIX}bucket_${minIndex}-${maxIndex}`;
+      const bucketName = `[${minIndex}…${maxIndex}]`;
+
+      const bucketRoot = createNode(parent, bucketName, `${parentPath}/${bucketKey}`, [], NODE_TYPES.BUCKET);
+
+      const bucketProperties = propertiesNames.slice(minKey, maxKey + 1);
+      let bucketNodes;
+      if (bucketProperties.length <= 100) {
+        bucketNodes = bucketProperties.map(name => createNode(bucketRoot, name, `${parentPath}/${bucketKey}/${name}`, ownProperties[name]));
+      } else {
+        bucketNodes = makeNumericalBuckets(bucketProperties, bucketRoot, ownProperties, minIndex);
+      }
+      setNodeChildren(bucketRoot, bucketNodes);
+      buckets.push(bucketRoot);
+    }
+  }
+  return buckets;
+}
+
+function makeDefaultPropsBucket(propertiesNames, parent, ownProperties) {
+  const parentPath = parent.path;
+
+  const userPropertiesNames = [];
+  const defaultProperties = [];
+
+  propertiesNames.forEach(name => {
+    if (isDefaultWindowProperty(name)) {
+      defaultProperties.push(name);
+    } else {
+      userPropertiesNames.push(name);
+    }
+  });
+
+  let nodes = makeNodesForOwnProps(userPropertiesNames, parent, ownProperties);
+
+  if (defaultProperties.length > 0) {
+    const defaultPropertiesNode = createNode(parent, "[default properties]", `${parentPath}/${SAFE_PATH_PREFIX}default`, null, NODE_TYPES.DEFAULT_PROPERTIES);
+
+    const defaultNodes = defaultProperties.map((name, index) => createNode(defaultPropertiesNode, maybeEscapePropertyName(name), `${parentPath}/${SAFE_PATH_PREFIX}bucket${index}/${name}`, ownProperties[name]));
+    nodes.push(setNodeChildren(defaultPropertiesNode, defaultNodes));
+  }
+  return nodes;
+}
+
+function makeNodesForOwnProps(propertiesNames, parent, ownProperties) {
+  const parentPath = parent.path;
+  return propertiesNames.map(name => createNode(parent, maybeEscapePropertyName(name), `${parentPath}/${name}`, ownProperties[name]));
+}
+
+function makeNodesForProperties(objProps, parent) {
+  const {
+    ownProperties = {},
+    ownSymbols,
+    prototype,
+    safeGetterValues
+  } = objProps;
+
+  const parentPath = parent.path;
+  const parentValue = getValue(parent);
+
+  let allProperties = Object.assign({}, ownProperties, safeGetterValues);
+
+  // Ignore properties that are neither non-concrete nor getters/setters.
+  const propertiesNames = sortProperties(Object.keys(allProperties)).filter(name => allProperties[name].hasOwnProperty("value") || allProperties[name].hasOwnProperty("getterValue") || allProperties[name].hasOwnProperty("get") || allProperties[name].hasOwnProperty("set"));
+
+  const numProperties = propertiesNames.length;
+
+  let nodes = [];
+  if (nodeSupportsBucketing(parent) && numProperties > 100) {
+    nodes = makeNumericalBuckets(propertiesNames, parent, allProperties);
+  } else if (parentValue && parentValue.class == "Window") {
+    nodes = makeDefaultPropsBucket(propertiesNames, parent, allProperties);
+  } else {
+    nodes = makeNodesForOwnProps(propertiesNames, parent, allProperties);
+  }
+
+  if (Array.isArray(ownSymbols)) {
+    ownSymbols.forEach((ownSymbol, index) => {
+      nodes.push(createNode(parent, ownSymbol.name, `${parentPath}/${SAFE_PATH_PREFIX}symbol-${index}`, ownSymbol.descriptor));
+    }, this);
+  }
+
+  if (nodeIsPromise(parent)) {
+    nodes.push(...makeNodesForPromiseProperties(parent));
+  }
+
+  if (nodeHasEntries(parent)) {
+    nodes.push(makeNodesForEntries(parent));
+  }
+
+  // Add the prototype if it exists and is not null
+  if (prototype && prototype.type !== "null") {
+    nodes.push(createNode(parent, "__proto__", `${parentPath}/__proto__`, { value: prototype }, NODE_TYPES.PROTOTYPE));
+  }
+
+  return nodes;
+}
+
+function createNode(parent, name, path, contents, type = NODE_TYPES.GRIP) {
+  if (contents === undefined) {
+    return null;
+  }
+
+  // The path is important to uniquely identify the item in the entire
+  // tree. This helps debugging & optimizes React's rendering of large
+  // lists. The path will be separated by property name,
+  // i.e. `{ foo: { bar: { baz: 5 }}}` will have a path of `foo/bar/baz`
+  // for the inner object.
+  return {
+    parent,
+    name,
+    path,
+    contents,
+    type
+  };
+}
+
+function setNodeChildren(node, children) {
+  node.contents = children;
+  return node;
+}
+
+function getChildren(options) {
+  const {
+    actors = {},
+    getObjectEntries,
+    getObjectProperties,
+    item
+  } = options;
+  // Nodes can either have children already, or be an object with
+  // properties that we need to go and fetch.
+  if (nodeHasAccessors(item)) {
+    return makeNodesForAccessors(item);
+  }
+
+  if (nodeIsMapEntry(item)) {
+    return makeNodesForMapEntry(item);
+  }
+
+  if (nodeHasChildren(item)) {
+    return item.contents;
+  }
+
+  if (!nodeHasProperties(item) && !nodeIsEntries(item)) {
+    return [];
+  }
+
+  // Because we are dynamically creating the tree as the user
+  // expands it (not precalculated tree structure), we cache child
+  // arrays. This not only helps performance, but is necessary
+  // because the expanded state depends on instances of nodes
+  // being the same across renders. If we didn't do this, each
+  // node would be a new instance every render.
+  const key = item.path;
+  if (actors && actors[key]) {
+    return actors[key];
+  }
+
+  if (nodeIsBucket(item)) {
+    return item.contents.children;
+  }
+
+  let loadedProps;
+  if (nodeIsEntries(item)) {
+    // If `item` is an <entries> node, we need to get the entries
+    // matching the parent node actor.
+    const parent = getParent(item);
+    loadedProps = getObjectEntries(get(getValue(parent), "actor", undefined));
+  } else {
+    loadedProps = getObjectProperties(get(getValue(item), "actor", undefined));
+  }
+
+  const {
+    ownProperties,
+    ownSymbols,
+    safeGetterValues,
+    prototype
+  } = loadedProps || {};
+
+  if (!ownProperties && !ownSymbols && !safeGetterValues && !prototype) {
+    return [];
+  }
+
+  let children = makeNodesForProperties(loadedProps, item);
+  actors[key] = children;
+  return children;
+}
+
+function getParent(item) {
+  return item.parent;
+}
+
+module.exports = {
+  createNode,
+  getChildren,
+  getParent,
+  getValue,
+  makeNodesForEntries,
+  makeNodesForPromiseProperties,
+  makeNodesForProperties,
+  nodeHasAccessors,
+  nodeHasAllEntriesInPreview,
+  nodeHasChildren,
+  nodeHasEntries,
+  nodeHasProperties,
+  nodeIsDefaultProperties,
+  nodeIsEntries,
+  nodeIsFunction,
+  nodeIsGetter,
+  nodeIsMapEntry,
+  nodeIsMissingArguments,
+  nodeIsObject,
+  nodeIsOptimizedOut,
+  nodeIsPrimitive,
+  nodeIsPromise,
+  nodeIsPrototype,
+  nodeIsSetter,
+  nodeIsWindow,
+  nodeSupportsBucketing,
+  setNodeChildren,
+  sortProperties,
+  NODE_TYPES,
+  // Export for testing purpose.
+  SAFE_PATH_PREFIX
+};
+
+/***/ }),
 /* 22 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-
-	// Reps
-	const {
-	  isGrip,
-	  wrapRender
-	} = __webpack_require__(7);
-	const { rep: StringRep } = __webpack_require__(11);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders DOM attribute
-	 */
-	Attribute.propTypes = {
-	  object: React.PropTypes.object.isRequired
-	};
-
-	function Attribute(props) {
-	  let {
-	    object
-	  } = props;
-	  let value = object.preview.value;
-
-	  return span({
-	    "data-link-actor-id": object.actor,
-	    className: "objectLink-Attr"
-	  }, span({ className: "attrTitle" }, getTitle(object)), span({ className: "attrEqual" }, "="), StringRep({ object: value }));
-	}
-
-	function getTitle(grip) {
-	  return grip.preview.nodeName;
-	}
-
-	// Registration
-	function supportsObject(grip, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(grip)) {
-	    return false;
-	  }
-
-	  return type == "Attr" && grip.preview;
-	}
-
-	module.exports = {
-	  rep: wrapRender(Attribute),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+var isArray = __webpack_require__(5),
+    isKey = __webpack_require__(63),
+    stringToPath = __webpack_require__(68),
+    toString = __webpack_require__(98);
+
+/**
+ * Casts `value` to a path array if it's not one.
+ *
+ * @private
+ * @param {*} value The value to inspect.
+ * @param {Object} [object] The object to query keys on.
+ * @returns {Array} Returns the cast property path array.
+ */
+function castPath(value, object) {
+  if (isArray(value)) {
+    return value;
+  }
+  return isKey(value, object) ? [value] : stringToPath(toString(value));
+}
+
+module.exports = castPath;
+
+
+/***/ }),
 /* 23 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-
-	// Reps
-	const {
-	  isGrip,
-	  wrapRender
-	} = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Used to render JS built-in Date() object.
-	 */
-	DateTime.propTypes = {
-	  object: React.PropTypes.object.isRequired
-	};
-
-	function DateTime(props) {
-	  let grip = props.object;
-	  let date;
-	  try {
-	    date = span({
-	      "data-link-actor-id": grip.actor,
-	      className: "objectBox"
-	    }, getTitle(grip), span({ className: "Date" }, new Date(grip.preview.timestamp).toISOString()));
-	  } catch (e) {
-	    date = span({ className: "objectBox" }, "Invalid Date");
-	  }
-
-	  return date;
-	}
-
-	function getTitle(grip) {
-	  return span({
-	    className: "objectTitle"
-	  }, grip.class + " ");
-	}
-
-	// Registration
-	function supportsObject(grip, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(grip)) {
-	    return false;
-	  }
-
-	  return type == "Date" && grip.preview;
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(DateTime),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+var baseIsNative = __webpack_require__(75),
+    getValue = __webpack_require__(80);
+
+/**
+ * Gets the native function at `key` of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {string} key The key of the method to get.
+ * @returns {*} Returns the function if it's native, else `undefined`.
+ */
+function getNative(object, key) {
+  var value = getValue(object, key);
+  return baseIsNative(value) ? value : undefined;
+}
+
+module.exports = getNative;
+
+
+/***/ }),
 /* 24 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-
-	// Reps
-	const {
-	  isGrip,
-	  getURLDisplayString,
-	  wrapRender
-	} = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders DOM document object.
-	 */
-	Document.propTypes = {
-	  object: React.PropTypes.object.isRequired
-	};
-
-	function Document(props) {
-	  let grip = props.object;
-
-	  return span({
-	    "data-link-actor-id": grip.actor,
-	    className: "objectBox objectBox-object"
-	  }, getTitle(grip), span({ className: "objectPropValue" }, getLocation(grip)));
-	}
-
-	function getLocation(grip) {
-	  let location = grip.preview.location;
-	  return location ? getURLDisplayString(location) : "";
-	}
-
-	function getTitle(grip) {
-	  return span({
-	    className: "objectTitle"
-	  }, grip.class + " ");
-	}
-
-	// Registration
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(object)) {
-	    return false;
-	  }
-
-	  return object.preview && type == "HTMLDocument";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(Document),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports) {
+
+/**
+ * Checks if `value` is the
+ * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
+ * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(_.noop);
+ * // => true
+ *
+ * _.isObject(null);
+ * // => false
+ */
+function isObject(value) {
+  var type = typeof value;
+  return value != null && (type == 'object' || type == 'function');
+}
+
+module.exports = isObject;
+
+
+/***/ }),
 /* 25 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-
-	// Reps
-	const {
-	  isGrip,
-	  wrapRender
-	} = __webpack_require__(7);
-
-	const { MODE } = __webpack_require__(1);
-	const { rep } = __webpack_require__(17);
-
-	/**
-	 * Renders DOM event objects.
-	 */
-	Event.propTypes = {
-	  object: React.PropTypes.object.isRequired,
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	  onDOMNodeMouseOver: React.PropTypes.func,
-	  onDOMNodeMouseOut: React.PropTypes.func,
-	  onInspectIconClick: React.PropTypes.func
-	};
-
-	function Event(props) {
-	  // Use `Object.assign` to keep `props` without changes because:
-	  // 1. JSON.stringify/JSON.parse is slow.
-	  // 2. Immutable.js is planned for the future.
-	  let gripProps = Object.assign({}, props, {
-	    title: getTitle(props)
-	  });
-	  gripProps.object = Object.assign({}, props.object);
-	  gripProps.object.preview = Object.assign({}, props.object.preview);
-
-	  gripProps.object.preview.ownProperties = {};
-	  if (gripProps.object.preview.target) {
-	    Object.assign(gripProps.object.preview.ownProperties, {
-	      target: gripProps.object.preview.target
-	    });
-	  }
-	  Object.assign(gripProps.object.preview.ownProperties, gripProps.object.preview.properties);
-
-	  delete gripProps.object.preview.properties;
-	  gripProps.object.ownPropertyLength = Object.keys(gripProps.object.preview.ownProperties).length;
-
-	  switch (gripProps.object.class) {
-	    case "MouseEvent":
-	      gripProps.isInterestingProp = (type, value, name) => {
-	        return ["target", "clientX", "clientY", "layerX", "layerY"].includes(name);
-	      };
-	      break;
-	    case "KeyboardEvent":
-	      gripProps.isInterestingProp = (type, value, name) => {
-	        return ["target", "key", "charCode", "keyCode"].includes(name);
-	      };
-	      break;
-	    case "MessageEvent":
-	      gripProps.isInterestingProp = (type, value, name) => {
-	        return ["target", "isTrusted", "data"].includes(name);
-	      };
-	      break;
-	    default:
-	      gripProps.isInterestingProp = (type, value, name) => {
-	        // We want to show the properties in the order they are declared.
-	        return Object.keys(gripProps.object.preview.ownProperties).includes(name);
-	      };
-	  }
-
-	  return rep(gripProps);
-	}
-
-	function getTitle(props) {
-	  let preview = props.object.preview;
-	  let title = preview.type;
-
-	  if (preview.eventKind == "key" && preview.modifiers && preview.modifiers.length) {
-	    title = `${title} ${preview.modifiers.join("-")}`;
-	  }
-	  return title;
-	}
-
-	// Registration
-	function supportsObject(grip, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(grip)) {
-	    return false;
-	  }
-
-	  return grip.preview && grip.preview.kind == "DOMEvent";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(Event),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+var isSymbol = __webpack_require__(11);
+
+/** Used as references for various `Number` constants. */
+var INFINITY = 1 / 0;
+
+/**
+ * Converts `value` to a string key if it's not a string or symbol.
+ *
+ * @private
+ * @param {*} value The value to inspect.
+ * @returns {string|symbol} Returns the key.
+ */
+function toKey(value) {
+  if (typeof value == 'string' || isSymbol(value)) {
+    return value;
+  }
+  var result = (value + '');
+  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
+}
+
+module.exports = toKey;
+
+
+/***/ }),
 /* 26 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-
-	// Reps
-	const {
-	  isGrip,
-	  cropString,
-	  wrapRender
-	} = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * This component represents a template for Function objects.
-	 */
-	FunctionRep.propTypes = {
-	  object: React.PropTypes.object.isRequired,
-	  parameterNames: React.PropTypes.array
-	};
-
-	function FunctionRep(props) {
-	  let grip = props.object;
-
-	  return span({
-	    "data-link-actor-id": grip.actor,
-	    className: "objectBox objectBox-function",
-	    // Set dir="ltr" to prevent function parentheses from
-	    // appearing in the wrong direction
-	    dir: "ltr"
-	  }, getTitle(props, grip), summarizeFunction(grip), "(", ...renderParams(props), ")");
-	}
-
-	function getTitle(props, grip) {
-	  let title = "function ";
-	  if (grip.isGenerator) {
-	    title = "function* ";
-	  }
-	  if (grip.isAsync) {
-	    title = "async " + title;
-	  }
-
-	  return span({
-	    className: "objectTitle"
-	  }, title);
-	}
-
-	function summarizeFunction(grip) {
-	  let name = grip.userDisplayName || grip.displayName || grip.name || "";
-	  return cropString(name, 100);
-	}
-
-	function renderParams(props) {
-	  const {
-	    parameterNames = []
-	  } = props;
-
-	  return parameterNames.filter(param => param).reduce((res, param, index, arr) => {
-	    res.push(span({ className: "param" }, param));
-	    if (index < arr.length - 1) {
-	      res.push(span({ className: "delimiter" }, ", "));
-	    }
-	    return res;
-	  }, []);
-	}
-
-	// Registration
-	function supportsObject(grip, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(grip)) {
-	    return type == "function";
-	  }
-
-	  return type == "Function";
-	}
-
-	// Exports from this module
-
-	module.exports = {
-	  rep: wrapRender(FunctionRep),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+const { MODE } = __webpack_require__(2);
+const { REPS, getRep } = __webpack_require__(3);
+const ObjectInspector = __webpack_require__(56);
+const ObjectInspectorUtils = __webpack_require__(21);
+
+const {
+  parseURLEncodedText,
+  parseURLParams,
+  maybeEscapePropertyName,
+  getGripPreviewItems
+} = __webpack_require__(1);
+
+module.exports = {
+  REPS,
+  getRep,
+  MODE,
+  maybeEscapePropertyName,
+  parseURLEncodedText,
+  parseURLParams,
+  getGripPreviewItems,
+  ObjectInspector,
+  ObjectInspectorUtils
+};
+
+/***/ }),
 /* 27 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-	// Dependencies
-	const {
-	  isGrip,
-	  wrapRender
-	} = __webpack_require__(7);
-
-	const PropRep = __webpack_require__(16);
-	const { MODE } = __webpack_require__(1);
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders a DOM Promise object.
-	 */
-	PromiseRep.propTypes = {
-	  object: React.PropTypes.object.isRequired,
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	  onDOMNodeMouseOver: React.PropTypes.func,
-	  onDOMNodeMouseOut: React.PropTypes.func,
-	  onInspectIconClick: React.PropTypes.func
-	};
-
-	function PromiseRep(props) {
-	  const object = props.object;
-	  const { promiseState } = object;
-
-	  const config = {
-	    "data-link-actor-id": object.actor,
-	    className: "objectBox objectBox-object"
-	  };
-
-	  if (props.mode === MODE.TINY) {
-	    let { Rep } = __webpack_require__(2);
-
-	    return span(config, getTitle(object), span({
-	      className: "objectLeftBrace"
-	    }, " { "), Rep({ object: promiseState.state }), span({
-	      className: "objectRightBrace"
-	    }, " }"));
-	  }
-
-	  const propsArray = getProps(props, promiseState);
-	  return span(config, getTitle(object), span({
-	    className: "objectLeftBrace"
-	  }, " { "), ...propsArray, span({
-	    className: "objectRightBrace"
-	  }, " }"));
-	}
-
-	function getTitle(object) {
-	  return span({
-	    className: "objectTitle"
-	  }, object.class);
-	}
-
-	function getProps(props, promiseState) {
-	  const keys = ["state"];
-	  if (Object.keys(promiseState).includes("value")) {
-	    keys.push("value");
-	  }
-
-	  return keys.reduce((res, key, i) => {
-	    let object = promiseState[key];
-	    res = res.concat(PropRep(Object.assign({}, props, {
-	      mode: MODE.TINY,
-	      name: `<${key}>`,
-	      object,
-	      equal: ": ",
-	      suppressQuotes: true
-	    })));
-
-	    // Interleave commas between elements
-	    if (i !== keys.length - 1) {
-	      res.push(", ");
-	    }
-
-	    return res;
-	  }, []);
-	}
-
-	// Registration
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(object)) {
-	    return false;
-	  }
-	  return type === "Promise";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(PromiseRep),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports) {
+
+// removed by extract-text-webpack-plugin
+
+/***/ }),
 /* 28 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-
-	// Reps
-	const {
-	  isGrip,
-	  wrapRender
-	} = __webpack_require__(7);
-
-	/**
-	 * Renders a grip object with regular expression.
-	 */
-	RegExp.propTypes = {
-	  object: React.PropTypes.object.isRequired
-	};
-
-	function RegExp(props) {
-	  let { object } = props;
-
-	  return React.DOM.span({
-	    "data-link-actor-id": object.actor,
-	    className: "objectBox objectBox-regexp regexpSource"
-	  }, getSource(object));
-	}
-
-	function getSource(grip) {
-	  return grip.displayString;
-	}
-
-	// Registration
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(object)) {
-	    return false;
-	  }
-
-	  return type == "RegExp";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(RegExp),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+
+const {
+  getGripType,
+  wrapRender
+} = __webpack_require__(1);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders undefined value
+ */
+const Undefined = function () {
+  return span({ className: "objectBox objectBox-undefined" }, "undefined");
+};
+
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true) {
+    return false;
+  }
+
+  return object && object.type && object.type == "undefined" || getGripType(object, noGrip) == "undefined";
+}
+
+// Exports from this module
+
+module.exports = {
+  rep: wrapRender(Undefined),
+  supportsObject
+};
+
+/***/ }),
 /* 29 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-
-	// Reps
-	const {
-	  isGrip,
-	  getURLDisplayString,
-	  wrapRender
-	} = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders a grip representing CSSStyleSheet
-	 */
-	StyleSheet.propTypes = {
-	  object: React.PropTypes.object.isRequired
-	};
-
-	function StyleSheet(props) {
-	  let grip = props.object;
-
-	  return span({
-	    "data-link-actor-id": grip.actor,
-	    className: "objectBox objectBox-object"
-	  }, getTitle(grip), span({ className: "objectPropValue" }, getLocation(grip)));
-	}
-
-	function getTitle(grip) {
-	  let title = "StyleSheet ";
-	  return span({ className: "objectBoxTitle" }, title);
-	}
-
-	function getLocation(grip) {
-	  // Embedded stylesheets don't have URL and so, no preview.
-	  let url = grip.preview ? grip.preview.url : "";
-	  return url ? getURLDisplayString(url) : "";
-	}
-
-	// Registration
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(object)) {
-	    return false;
-	  }
-
-	  return type == "CSSStyleSheet";
-	}
-
-	// Exports from this module
-
-	module.exports = {
-	  rep: wrapRender(StyleSheet),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+
+const { wrapRender } = __webpack_require__(1);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders null value
+ */
+function Null(props) {
+  return span({ className: "objectBox objectBox-null" }, "null");
+}
+
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true) {
+    return false;
+  }
+
+  if (object && object.type && object.type == "null") {
+    return true;
+  }
+
+  return object == null;
+}
+
+// Exports from this module
+
+module.exports = {
+  rep: wrapRender(Null),
+  supportsObject
+};
+
+/***/ }),
 /* 30 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-	const {
-	  isGrip,
-	  cropString,
-	  cropMultipleLines,
-	  wrapRender
-	} = __webpack_require__(7);
-	const { MODE } = __webpack_require__(1);
-	const nodeConstants = __webpack_require__(31);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders DOM comment node.
-	 */
-	CommentNode.propTypes = {
-	  object: React.PropTypes.object.isRequired,
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key]))
-	};
-
-	function CommentNode(props) {
-	  let {
-	    object,
-	    mode = MODE.SHORT
-	  } = props;
-
-	  let { textContent } = object.preview;
-	  if (mode === MODE.TINY) {
-	    textContent = cropMultipleLines(textContent, 30);
-	  } else if (mode === MODE.SHORT) {
-	    textContent = cropString(textContent, 50);
-	  }
-
-	  return span({
-	    className: "objectBox theme-comment",
-	    "data-link-actor-id": object.actor
-	  }, `<!-- ${textContent} -->`);
-	}
-
-	// Registration
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(object)) {
-	    return false;
-	  }
-	  return object.preview && object.preview.nodeType === nodeConstants.COMMENT_NODE;
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(CommentNode),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+const {
+  escapeString,
+  sanitizeString,
+  isGrip,
+  wrapRender
+} = __webpack_require__(1);
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders a long string grip.
+ */
+LongStringRep.propTypes = {
+  useQuotes: React.PropTypes.bool,
+  escapeWhitespace: React.PropTypes.bool,
+  style: React.PropTypes.object,
+  cropLimit: React.PropTypes.number.isRequired,
+  member: React.PropTypes.string,
+  object: React.PropTypes.object.isRequired
+};
+
+function LongStringRep(props) {
+  let {
+    cropLimit,
+    member,
+    object,
+    style,
+    useQuotes = true,
+    escapeWhitespace = true
+  } = props;
+  let { fullText, initial, length } = object;
+
+  let config = {
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-string"
+  };
+
+  if (style) {
+    config.style = style;
+  }
+
+  let string = member && member.open ? fullText || initial : initial.substring(0, cropLimit);
+
+  if (string.length < length) {
+    string += "\u2026";
+  }
+  let formattedString = useQuotes ? escapeString(string, escapeWhitespace) : sanitizeString(string);
+  return span(config, formattedString);
+}
+
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true || !isGrip(object)) {
+    return false;
+  }
+  return object.type === "longString";
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(LongStringRep),
+  supportsObject
+};
+
+/***/ }),
 /* 31 */
-/***/ function(module, exports) {
-
-	/* 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/. */
-
-	module.exports = {
-	  ELEMENT_NODE: 1,
-	  ATTRIBUTE_NODE: 2,
-	  TEXT_NODE: 3,
-	  CDATA_SECTION_NODE: 4,
-	  ENTITY_REFERENCE_NODE: 5,
-	  ENTITY_NODE: 6,
-	  PROCESSING_INSTRUCTION_NODE: 7,
-	  COMMENT_NODE: 8,
-	  DOCUMENT_NODE: 9,
-	  DOCUMENT_TYPE_NODE: 10,
-	  DOCUMENT_FRAGMENT_NODE: 11,
-	  NOTATION_NODE: 12,
-
-	  // DocumentPosition
-	  DOCUMENT_POSITION_DISCONNECTED: 0x01,
-	  DOCUMENT_POSITION_PRECEDING: 0x02,
-	  DOCUMENT_POSITION_FOLLOWING: 0x04,
-	  DOCUMENT_POSITION_CONTAINS: 0x08,
-	  DOCUMENT_POSITION_CONTAINED_BY: 0x10,
-	  DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+
+const {
+  getGripType,
+  wrapRender
+} = __webpack_require__(1);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders a number
+ */
+Number.propTypes = {
+  object: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.number, React.PropTypes.bool]).isRequired
+};
+
+function Number(props) {
+  let value = props.object;
+
+  return span({ className: "objectBox objectBox-number" }, stringify(value));
+}
+
+function stringify(object) {
+  let isNegativeZero = Object.is(object, -0) || object.type && object.type == "-0";
+
+  return isNegativeZero ? "-0" : String(object);
+}
+
+function supportsObject(object, noGrip = false) {
+  return ["boolean", "number", "-0"].includes(getGripType(object, noGrip));
+}
+
+// Exports from this module
+
+module.exports = {
+  rep: wrapRender(Number),
+  supportsObject
+};
+
+/***/ }),
 /* 32 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-
-	// Utils
-	const {
-	  isGrip,
-	  wrapRender
-	} = __webpack_require__(7);
-	const { MODE } = __webpack_require__(1);
-	const nodeConstants = __webpack_require__(31);
-	const Svg = __webpack_require__(33);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders DOM element node.
-	 */
-	ElementNode.propTypes = {
-	  object: React.PropTypes.object.isRequired,
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	  onDOMNodeMouseOver: React.PropTypes.func,
-	  onDOMNodeMouseOut: React.PropTypes.func,
-	  onInspectIconClick: React.PropTypes.func
-	};
-
-	function ElementNode(props) {
-	  let {
-	    object,
-	    mode,
-	    onDOMNodeMouseOver,
-	    onDOMNodeMouseOut,
-	    onInspectIconClick
-	  } = props;
-	  let elements = getElements(object, mode);
-
-	  let isInTree = object.preview && object.preview.isConnected === true;
-
-	  let baseConfig = {
-	    "data-link-actor-id": object.actor,
-	    className: "objectBox objectBox-node"
-	  };
-	  let inspectIcon;
-	  if (isInTree) {
-	    if (onDOMNodeMouseOver) {
-	      Object.assign(baseConfig, {
-	        onMouseOver: _ => onDOMNodeMouseOver(object)
-	      });
-	    }
-
-	    if (onDOMNodeMouseOut) {
-	      Object.assign(baseConfig, {
-	        onMouseOut: onDOMNodeMouseOut
-	      });
-	    }
-
-	    if (onInspectIconClick) {
-	      inspectIcon = Svg("open-inspector", {
-	        element: "a",
-	        draggable: false,
-	        // TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
-	        title: "Click to select the node in the inspector",
-	        onClick: e => onInspectIconClick(object, e)
-	      });
-	    }
-	  }
-
-	  return span(baseConfig, ...elements, inspectIcon);
-	}
-
-	function getElements(grip, mode) {
-	  let { attributes, nodeName } = grip.preview;
-	  const nodeNameElement = span({
-	    className: "tag-name theme-fg-color3"
-	  }, nodeName);
-
-	  if (mode === MODE.TINY) {
-	    let elements = [nodeNameElement];
-	    if (attributes.id) {
-	      elements.push(span({ className: "attr-name theme-fg-color2" }, `#${attributes.id}`));
-	    }
-	    if (attributes.class) {
-	      elements.push(span({ className: "attr-name theme-fg-color2" }, attributes.class.replace(/(^\s+)|(\s+$)/g, "").split(" ").map(cls => `.${cls}`).join("")));
-	    }
-	    return elements;
-	  }
-	  let attributeKeys = Object.keys(attributes);
-	  if (attributeKeys.includes("class")) {
-	    attributeKeys.splice(attributeKeys.indexOf("class"), 1);
-	    attributeKeys.unshift("class");
-	  }
-	  if (attributeKeys.includes("id")) {
-	    attributeKeys.splice(attributeKeys.indexOf("id"), 1);
-	    attributeKeys.unshift("id");
-	  }
-	  const attributeElements = attributeKeys.reduce((arr, name, i, keys) => {
-	    let value = attributes[name];
-	    let attribute = span({}, span({ className: "attr-name theme-fg-color2" }, `${name}`), `="`, span({ className: "attr-value theme-fg-color6" }, `${value}`), `"`);
-
-	    return arr.concat([" ", attribute]);
-	  }, []);
-
-	  return ["<", nodeNameElement, ...attributeElements, ">"];
-	}
-
-	// Registration
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(object)) {
-	    return false;
-	  }
-	  return object.preview && object.preview.nodeType === nodeConstants.ELEMENT_NODE;
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(ElementNode),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+const {
+  wrapRender
+} = __webpack_require__(1);
+const PropRep = __webpack_require__(4);
+const { MODE } = __webpack_require__(2);
+// Shortcuts
+const { span } = React.DOM;
+
+const DEFAULT_TITLE = "Object";
+
+/**
+ * Renders an object. An object is represented by a list of its
+ * properties enclosed in curly brackets.
+ */
+ObjectRep.propTypes = {
+  object: React.PropTypes.object.isRequired,
+  // @TODO Change this to Object.values once it's supported in Node's version of V8
+  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  title: React.PropTypes.string
+};
+
+function ObjectRep(props) {
+  let object = props.object;
+  let propsArray = safePropIterator(props, object);
+
+  if (props.mode === MODE.TINY) {
+    const tinyModeItems = [];
+    if (getTitle(props, object) !== DEFAULT_TITLE) {
+      tinyModeItems.push(getTitleElement(props, object));
+    } else {
+      tinyModeItems.push(span({
+        className: "objectLeftBrace"
+      }, "{"), propsArray.length > 0 ? span({
+        key: "more",
+        className: "more-ellipsis",
+        title: "more…"
+      }, "…") : null, span({
+        className: "objectRightBrace"
+      }, "}"));
+    }
+
+    return span({ className: "objectBox objectBox-object" }, ...tinyModeItems);
+  }
+
+  return span({ className: "objectBox objectBox-object" }, getTitleElement(props, object), span({
+    className: "objectLeftBrace"
+  }, " { "), ...propsArray, span({
+    className: "objectRightBrace"
+  }, " }"));
+}
+
+function getTitleElement(props, object) {
+  return span({ className: "objectTitle" }, getTitle(props, object));
+}
+
+function getTitle(props, object) {
+  return props.title || object.class || DEFAULT_TITLE;
+}
+
+function safePropIterator(props, object, max) {
+  max = typeof max === "undefined" ? 3 : max;
+  try {
+    return propIterator(props, object, max);
+  } catch (err) {
+    console.error(err);
+  }
+  return [];
+}
+
+function propIterator(props, object, max) {
+  let isInterestingProp = (type, value) => {
+    // Do not pick objects, it could cause recursion.
+    return type == "boolean" || type == "number" || type == "string" && value;
+  };
+
+  // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=945377
+  if (Object.prototype.toString.call(object) === "[object Generator]") {
+    object = Object.getPrototypeOf(object);
+  }
+
+  // Object members with non-empty values are preferred since it gives the
+  // user a better overview of the object.
+  let interestingObject = getFilteredObject(object, max, isInterestingProp);
+
+  if (Object.keys(interestingObject).length < max) {
+    // There are not enough props yet (or at least, not enough props to
+    // be able to know whether we should print "more…" or not).
+    // Let's display also empty members and functions.
+    interestingObject = Object.assign({}, interestingObject, getFilteredObject(object, max - Object.keys(interestingObject).length, (type, value) => !isInterestingProp(type, value)));
+  }
+
+  let propsArray = getPropsArray(interestingObject, props);
+  if (Object.keys(object).length > max) {
+    propsArray.push(span({
+      className: "more-ellipsis",
+      title: "more…"
+    }, "…"));
+  }
+
+  return unfoldProps(propsArray);
+}
+
+function unfoldProps(items) {
+  return items.reduce((res, item, index) => {
+    if (Array.isArray(item)) {
+      res = res.concat(item);
+    } else {
+      res.push(item);
+    }
+
+    // Interleave commas between elements
+    if (index !== items.length - 1) {
+      res.push(", ");
+    }
+    return res;
+  }, []);
+}
+
+/**
+ * Get an array of components representing the properties of the object
+ *
+ * @param {Object} object
+ * @param {Object} props
+ * @return {Array} Array of PropRep.
+ */
+function getPropsArray(object, props) {
+  let propsArray = [];
+
+  if (!object) {
+    return propsArray;
+  }
+
+  // Hardcode tiny mode to avoid recursive handling.
+  let mode = MODE.TINY;
+  const objectKeys = Object.keys(object);
+  return objectKeys.map((name, i) => PropRep(Object.assign({}, props, {
+    mode,
+    name,
+    object: object[name],
+    equal: ": "
+  })));
+}
+
+/**
+ * Get a copy of the object filtered by a given predicate.
+ *
+ * @param {Object} object.
+ * @param {Number} max The maximum length of keys array.
+ * @param {Function} filter Filter the props you want.
+ * @return {Object} the filtered object.
+ */
+function getFilteredObject(object, max, filter) {
+  let filteredObject = {};
+
+  try {
+    for (let name in object) {
+      if (Object.keys(filteredObject).length >= max) {
+        return filteredObject;
+      }
+
+      let value;
+      try {
+        value = object[name];
+      } catch (exc) {
+        continue;
+      }
+
+      let t = typeof value;
+      if (filter(t, value)) {
+        filteredObject[name] = value;
+      }
+    }
+  } catch (err) {
+    console.error(err);
+  }
+  return filteredObject;
+}
+
+function supportsObject(object) {
+  return true;
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(ObjectRep),
+  supportsObject
+};
+
+/***/ }),
 /* 33 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	const React = __webpack_require__(8);
-	const InlineSVG = __webpack_require__(34);
-
-	const svg = {
-	  "arrow": __webpack_require__(35),
-	  "open-inspector": __webpack_require__(36)
-	};
-
-	Svg.propTypes = {
-	  className: React.PropTypes.string
-	};
-
-	function Svg(name, props) {
-	  if (!svg[name]) {
-	    throw new Error("Unknown SVG: " + name);
-	  }
-	  let className = name;
-	  if (props && props.className) {
-	    className = `${name} ${props.className}`;
-	  }
-	  if (name === "subSettings") {
-	    className = "";
-	  }
-	  props = Object.assign({}, props, { className, src: svg[name] });
-	  return React.createElement(InlineSVG, props);
-	}
-
-	module.exports = Svg;
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+
+const {
+  getGripType,
+  wrapRender
+} = __webpack_require__(1);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders a symbol.
+ */
+SymbolRep.propTypes = {
+  object: React.PropTypes.object.isRequired
+};
+
+function SymbolRep(props) {
+  let {
+    className = "objectBox objectBox-symbol",
+    object
+  } = props;
+  let { name } = object;
+
+  return span({ className }, `Symbol(${name || ""})`);
+}
+
+function supportsObject(object, noGrip = false) {
+  return getGripType(object, noGrip) == "symbol";
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(SymbolRep),
+  supportsObject
+};
+
+/***/ }),
 /* 34 */
-/***/ function(module, exports, __webpack_require__) {
-
-	'use strict';
-
-	Object.defineProperty(exports, '__esModule', {
-	    value: true
-	});
-
-	var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
-
-	var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
-
-	var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
-
-	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
-
-	function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
-
-	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
-
-	function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
-
-	var _react = __webpack_require__(8);
-
-	var _react2 = _interopRequireDefault(_react);
-
-	var DOMParser = typeof window !== 'undefined' && window.DOMParser;
-	var process = process || {};
-	process.env = process.env || {};
-	var parserAvailable = typeof DOMParser !== 'undefined' && DOMParser.prototype != null && DOMParser.prototype.parseFromString != null;
-
-	function isParsable(src) {
-	    // kinda naive but meh, ain't gonna use full-blown parser for this
-	    return parserAvailable && typeof src === 'string' && src.trim().substr(0, 4) === '<svg';
-	}
-
-	// parse SVG string using `DOMParser`
-	function parseFromSVGString(src) {
-	    var parser = new DOMParser();
-	    return parser.parseFromString(src, "image/svg+xml");
-	}
-
-	// Transform DOM prop/attr names applicable to `<svg>` element but react-limited
-	function switchSVGAttrToReactProp(propName) {
-	    switch (propName) {
-	        case 'class':
-	            return 'className';
-	        default:
-	            return propName;
-	    }
-	}
-
-	var InlineSVG = (function (_React$Component) {
-	    _inherits(InlineSVG, _React$Component);
-
-	    _createClass(InlineSVG, null, [{
-	        key: 'defaultProps',
-	        value: {
-	            element: 'i',
-	            raw: false,
-	            src: ''
-	        },
-	        enumerable: true
-	    }, {
-	        key: 'propTypes',
-	        value: {
-	            src: _react2['default'].PropTypes.string.isRequired,
-	            element: _react2['default'].PropTypes.string,
-	            raw: _react2['default'].PropTypes.bool
-	        },
-	        enumerable: true
-	    }]);
-
-	    function InlineSVG(props) {
-	        _classCallCheck(this, InlineSVG);
-
-	        _get(Object.getPrototypeOf(InlineSVG.prototype), 'constructor', this).call(this, props);
-	        this._extractSVGProps = this._extractSVGProps.bind(this);
-	    }
-
-	    // Serialize `Attr` objects in `NamedNodeMap`
-
-	    _createClass(InlineSVG, [{
-	        key: '_serializeAttrs',
-	        value: function _serializeAttrs(map) {
-	            var ret = {};
-	            var prop = undefined;
-	            for (var i = 0; i < map.length; i++) {
-	                prop = switchSVGAttrToReactProp(map[i].name);
-	                ret[prop] = map[i].value;
-	            }
-	            return ret;
-	        }
-
-	        // get <svg /> element props
-	    }, {
-	        key: '_extractSVGProps',
-	        value: function _extractSVGProps(src) {
-	            var map = parseFromSVGString(src).documentElement.attributes;
-	            return map.length > 0 ? this._serializeAttrs(map) : null;
-	        }
-
-	        // get content inside <svg> element.
-	    }, {
-	        key: '_stripSVG',
-	        value: function _stripSVG(src) {
-	            return parseFromSVGString(src).documentElement.innerHTML;
-	        }
-	    }, {
-	        key: 'componentWillReceiveProps',
-	        value: function componentWillReceiveProps(_ref) {
-	            var children = _ref.children;
-
-	            if ("production" !== process.env.NODE_ENV && children != null) {
-	                console.info('<InlineSVG />: `children` prop will be ignored.');
-	            }
-	        }
-	    }, {
-	        key: 'render',
-	        value: function render() {
-	            var Element = undefined,
-	                __html = undefined,
-	                svgProps = undefined;
-	            var _props = this.props;
-	            var element = _props.element;
-	            var raw = _props.raw;
-	            var src = _props.src;
-
-	            var otherProps = _objectWithoutProperties(_props, ['element', 'raw', 'src']);
-
-	            if (raw === true && isParsable(src)) {
-	                Element = 'svg';
-	                svgProps = this._extractSVGProps(src);
-	                __html = this._stripSVG(src);
-	            }
-	            __html = __html || src;
-	            Element = Element || element;
-	            svgProps = svgProps || {};
-
-	            return _react2['default'].createElement(Element, _extends({}, svgProps, otherProps, { src: null, children: null,
-	                dangerouslySetInnerHTML: { __html: __html } }));
-	        }
-	    }]);
-
-	    return InlineSVG;
-	})(_react2['default'].Component);
-
-	exports['default'] = InlineSVG;
-	module.exports = exports['default'];
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+
+const {
+  getGripType,
+  wrapRender
+} = __webpack_require__(1);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders a Infinity object
+ */
+InfinityRep.propTypes = {
+  object: React.PropTypes.object.isRequired
+};
+
+function InfinityRep(props) {
+  const {
+    object
+  } = props;
+
+  return span({ className: "objectBox objectBox-number" }, object.type);
+}
+
+function supportsObject(object, noGrip = false) {
+  const type = getGripType(object, noGrip);
+  return type == "Infinity" || type == "-Infinity";
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(InfinityRep),
+  supportsObject
+};
+
+/***/ }),
 /* 35 */
-/***/ function(module, exports) {
-
-	module.exports = "<!-- 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/. --><svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z\"></path></svg>"
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+
+const {
+  getGripType,
+  wrapRender
+} = __webpack_require__(1);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders a NaN object
+ */
+function NaNRep(props) {
+  return span({ className: "objectBox objectBox-nan" }, "NaN");
+}
+
+function supportsObject(object, noGrip = false) {
+  return getGripType(object, noGrip) == "NaN";
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(NaNRep),
+  supportsObject
+};
+
+/***/ }),
 /* 36 */
-/***/ function(module, exports) {
-
-	module.exports = "<!-- 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/. --><svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8,3L12,3L12,7L14,7L14,8L12,8L12,12L8,12L8,14L7,14L7,12L3,12L3,8L1,8L1,7L3,7L3,3L7,3L7,1L8,1L8,3ZM10,10L10,5L5,5L5,10L10,10Z\"></path></svg>"
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+const {
+  wrapRender
+} = __webpack_require__(1);
+const { MODE } = __webpack_require__(2);
+// Shortcuts
+const {
+  span
+} = React.DOM;
+/**
+ * Renders an object. An object is represented by a list of its
+ * properties enclosed in curly brackets.
+ */
+Accessor.propTypes = {
+  object: React.PropTypes.object.isRequired,
+  mode: React.PropTypes.oneOf(Object.values(MODE))
+};
+
+function Accessor(props) {
+  const {
+    object
+  } = props;
+
+  const accessors = [];
+  if (hasGetter(object)) {
+    accessors.push("Getter");
+  }
+  if (hasSetter(object)) {
+    accessors.push("Setter");
+  }
+  const title = accessors.join(" & ");
+
+  return span({ className: "objectBox objectBox-accessor" }, span({
+    className: "objectTitle"
+  }, title));
+}
+
+function hasGetter(object) {
+  return object && object.get && object.get.type !== "undefined";
+}
+
+function hasSetter(object) {
+  return object && object.set && object.set.type !== "undefined";
+}
+
+function supportsObject(object, noGrip = false) {
+  if (noGrip !== true && (hasGetter(object) || hasSetter(object))) {
+    return true;
+  }
+
+  return false;
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(Accessor),
+  supportsObject
+};
+
+/***/ }),
 /* 37 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-
-	// Reps
-	const {
-	  isGrip,
-	  cropString,
-	  wrapRender
-	} = __webpack_require__(7);
-	const { MODE } = __webpack_require__(1);
-	const Svg = __webpack_require__(33);
-
-	// Shortcuts
-	const DOM = React.DOM;
-
-	/**
-	 * Renders DOM #text node.
-	 */
-	TextNode.propTypes = {
-	  object: React.PropTypes.object.isRequired,
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	  onDOMNodeMouseOver: React.PropTypes.func,
-	  onDOMNodeMouseOut: React.PropTypes.func,
-	  onInspectIconClick: React.PropTypes.func
-	};
-
-	function TextNode(props) {
-	  let {
-	    object: grip,
-	    mode = MODE.SHORT,
-	    onDOMNodeMouseOver,
-	    onDOMNodeMouseOut,
-	    onInspectIconClick
-	  } = props;
-
-	  let baseConfig = {
-	    "data-link-actor-id": grip.actor,
-	    className: "objectBox objectBox-textNode"
-	  };
-	  let inspectIcon;
-	  let isInTree = grip.preview && grip.preview.isConnected === true;
-
-	  if (isInTree) {
-	    if (onDOMNodeMouseOver) {
-	      Object.assign(baseConfig, {
-	        onMouseOver: _ => onDOMNodeMouseOver(grip)
-	      });
-	    }
-
-	    if (onDOMNodeMouseOut) {
-	      Object.assign(baseConfig, {
-	        onMouseOut: onDOMNodeMouseOut
-	      });
-	    }
-
-	    if (onInspectIconClick) {
-	      inspectIcon = Svg("open-inspector", {
-	        element: "a",
-	        draggable: false,
-	        // TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
-	        title: "Click to select the node in the inspector",
-	        onClick: e => onInspectIconClick(grip, e)
-	      });
-	    }
-	  }
-
-	  if (mode === MODE.TINY) {
-	    return DOM.span(baseConfig, getTitle(grip), inspectIcon);
-	  }
-
-	  return DOM.span(baseConfig, getTitle(grip), DOM.span({ className: "nodeValue" }, " ", `"${getTextContent(grip)}"`), inspectIcon);
-	}
-
-	function getTextContent(grip) {
-	  return cropString(grip.preview.textContent);
-	}
-
-	function getTitle(grip) {
-	  const title = "#text";
-	  return DOM.span({}, title);
-	}
-
-	// Registration
-	function supportsObject(grip, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(grip)) {
-	    return false;
-	  }
-
-	  return grip.preview && grip.class == "Text";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(TextNode),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// ReactJS
+const React = __webpack_require__(0);
+
+// Reps
+const {
+  getGripType,
+  isGrip,
+  wrapRender
+} = __webpack_require__(1);
+const { rep: StringRep } = __webpack_require__(16);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders DOM attribute
+ */
+Attribute.propTypes = {
+  object: React.PropTypes.object.isRequired
+};
+
+function Attribute(props) {
+  let {
+    object
+  } = props;
+  let value = object.preview.value;
+
+  return span({
+    "data-link-actor-id": object.actor,
+    className: "objectLink-Attr"
+  }, span({ className: "attrTitle" }, getTitle(object)), span({ className: "attrEqual" }, "="), StringRep({ object: value }));
+}
+
+function getTitle(grip) {
+  return grip.preview.nodeName;
+}
+
+// Registration
+function supportsObject(grip, noGrip = false) {
+  if (noGrip === true || !isGrip(grip)) {
+    return false;
+  }
+
+  return getGripType(grip, noGrip) == "Attr" && grip.preview;
+}
+
+module.exports = {
+  rep: wrapRender(Attribute),
+  supportsObject
+};
+
+/***/ }),
 /* 38 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-	// Utils
-	const {
-	  isGrip,
-	  wrapRender
-	} = __webpack_require__(7);
-	const { MODE } = __webpack_require__(1);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders Error objects.
-	 */
-	ErrorRep.propTypes = {
-	  object: React.PropTypes.object.isRequired,
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key]))
-	};
-
-	function ErrorRep(props) {
-	  let object = props.object;
-	  let preview = object.preview;
-	  let name = preview && preview.name ? preview.name : "Error";
-
-	  let content = props.mode === MODE.TINY ? name : `${name}: ${preview.message}`;
-
-	  if (preview.stack && props.mode !== MODE.TINY) {
-	    /*
-	      * Since Reps are used in the JSON Viewer, we can't localize
-	      * the "Stack trace" label (defined in debugger.properties as
-	      * "variablesViewErrorStacktrace" property), until Bug 1317038 lands.
-	      */
-	    content = `${content}\nStack trace:\n${preview.stack}`;
-	  }
-
-	  return span({
-	    "data-link-actor-id": object.actor,
-	    className: "objectBox-stackTrace"
-	  }, content);
-	}
-
-	// Registration
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(object)) {
-	    return false;
-	  }
-	  return object.preview && type === "Error";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(ErrorRep),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// ReactJS
+const React = __webpack_require__(0);
+
+// Reps
+const {
+  getGripType,
+  isGrip,
+  wrapRender
+} = __webpack_require__(1);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Used to render JS built-in Date() object.
+ */
+DateTime.propTypes = {
+  object: React.PropTypes.object.isRequired
+};
+
+function DateTime(props) {
+  let grip = props.object;
+  let date;
+  try {
+    date = span({
+      "data-link-actor-id": grip.actor,
+      className: "objectBox"
+    }, getTitle(grip), span({ className: "Date" }, new Date(grip.preview.timestamp).toISOString()));
+  } catch (e) {
+    date = span({ className: "objectBox" }, "Invalid Date");
+  }
+
+  return date;
+}
+
+function getTitle(grip) {
+  return span({
+    className: "objectTitle"
+  }, grip.class + " ");
+}
+
+// Registration
+function supportsObject(grip, noGrip = false) {
+  if (noGrip === true || !isGrip(grip)) {
+    return false;
+  }
+
+  return getGripType(grip, noGrip) == "Date" && grip.preview;
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(DateTime),
+  supportsObject
+};
+
+/***/ }),
 /* 39 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-
-	// Reps
-	const {
-	  isGrip,
-	  getURLDisplayString,
-	  wrapRender
-	} = __webpack_require__(7);
-
-	const { MODE } = __webpack_require__(1);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders a grip representing a window.
-	 */
-	WindowRep.propTypes = {
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	  object: React.PropTypes.object.isRequired
-	};
-
-	function WindowRep(props) {
-	  let {
-	    mode,
-	    object
-	  } = props;
-
-	  const config = {
-	    "data-link-actor-id": object.actor,
-	    className: "objectBox objectBox-Window"
-	  };
-
-	  if (mode === MODE.TINY) {
-	    return span(config, getTitle(object));
-	  }
-
-	  return span(config, getTitle(object), " ", span({ className: "objectPropValue" }, getLocation(object)));
-	}
-
-	function getTitle(object) {
-	  let title = object.displayClass || object.class || "Window";
-	  return span({ className: "objectBoxTitle" }, title);
-	}
-
-	function getLocation(object) {
-	  return getURLDisplayString(object.preview.url);
-	}
-
-	// Registration
-	function supportsObject(object, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(object)) {
-	    return false;
-	  }
-
-	  return object.preview && type == "Window";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(WindowRep),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// ReactJS
+const React = __webpack_require__(0);
+
+// Reps
+const {
+  getGripType,
+  isGrip,
+  getURLDisplayString,
+  wrapRender
+} = __webpack_require__(1);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders DOM document object.
+ */
+Document.propTypes = {
+  object: React.PropTypes.object.isRequired
+};
+
+function Document(props) {
+  let grip = props.object;
+
+  return span({
+    "data-link-actor-id": grip.actor,
+    className: "objectBox objectBox-object"
+  }, getTitle(grip), span({ className: "objectPropValue" }, getLocation(grip)));
+}
+
+function getLocation(grip) {
+  let location = grip.preview.location;
+  return location ? getURLDisplayString(location) : "";
+}
+
+function getTitle(grip) {
+  return span({
+    className: "objectTitle"
+  }, grip.class + " ");
+}
+
+// Registration
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true || !isGrip(object)) {
+    return false;
+  }
+
+  return object.preview && getGripType(object, noGrip) == "HTMLDocument";
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(Document),
+  supportsObject
+};
+
+/***/ }),
 /* 40 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-
-	// Reps
-	const {
-	  isGrip,
-	  wrapRender
-	} = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders a grip object with textual data.
-	 */
-	ObjectWithText.propTypes = {
-	  object: React.PropTypes.object.isRequired
-	};
-
-	function ObjectWithText(props) {
-	  let grip = props.object;
-	  return span({
-	    "data-link-actor-id": grip.actor,
-	    className: "objectBox objectBox-" + getType(grip)
-	  }, span({ className: "objectPropValue" }, getDescription(grip)));
-	}
-
-	function getType(grip) {
-	  return grip.class;
-	}
-
-	function getDescription(grip) {
-	  return "\"" + grip.preview.text + "\"";
-	}
-
-	// Registration
-	function supportsObject(grip, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(grip)) {
-	    return false;
-	  }
-
-	  return grip.preview && grip.preview.kind == "ObjectWithText";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(ObjectWithText),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// ReactJS
+const React = __webpack_require__(0);
+
+// Reps
+const {
+  isGrip,
+  wrapRender
+} = __webpack_require__(1);
+
+const { MODE } = __webpack_require__(2);
+const { rep } = __webpack_require__(9);
+
+/**
+ * Renders DOM event objects.
+ */
+Event.propTypes = {
+  object: React.PropTypes.object.isRequired,
+  // @TODO Change this to Object.values once it's supported in Node's version of V8
+  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  onDOMNodeMouseOver: React.PropTypes.func,
+  onDOMNodeMouseOut: React.PropTypes.func,
+  onInspectIconClick: React.PropTypes.func
+};
+
+function Event(props) {
+  // Use `Object.assign` to keep `props` without changes because:
+  // 1. JSON.stringify/JSON.parse is slow.
+  // 2. Immutable.js is planned for the future.
+  let gripProps = Object.assign({}, props, {
+    title: getTitle(props)
+  });
+  gripProps.object = Object.assign({}, props.object);
+  gripProps.object.preview = Object.assign({}, props.object.preview);
+
+  gripProps.object.preview.ownProperties = {};
+  if (gripProps.object.preview.target) {
+    Object.assign(gripProps.object.preview.ownProperties, {
+      target: gripProps.object.preview.target
+    });
+  }
+  Object.assign(gripProps.object.preview.ownProperties, gripProps.object.preview.properties);
+
+  delete gripProps.object.preview.properties;
+  gripProps.object.ownPropertyLength = Object.keys(gripProps.object.preview.ownProperties).length;
+
+  switch (gripProps.object.class) {
+    case "MouseEvent":
+      gripProps.isInterestingProp = (type, value, name) => {
+        return ["target", "clientX", "clientY", "layerX", "layerY"].includes(name);
+      };
+      break;
+    case "KeyboardEvent":
+      gripProps.isInterestingProp = (type, value, name) => {
+        return ["target", "key", "charCode", "keyCode"].includes(name);
+      };
+      break;
+    case "MessageEvent":
+      gripProps.isInterestingProp = (type, value, name) => {
+        return ["target", "isTrusted", "data"].includes(name);
+      };
+      break;
+    default:
+      gripProps.isInterestingProp = (type, value, name) => {
+        // We want to show the properties in the order they are declared.
+        return Object.keys(gripProps.object.preview.ownProperties).includes(name);
+      };
+  }
+
+  return rep(gripProps);
+}
+
+function getTitle(props) {
+  let preview = props.object.preview;
+  let title = preview.type;
+
+  if (preview.eventKind == "key" && preview.modifiers && preview.modifiers.length) {
+    title = `${title} ${preview.modifiers.join("-")}`;
+  }
+  return title;
+}
+
+// Registration
+function supportsObject(grip, noGrip = false) {
+  if (noGrip === true || !isGrip(grip)) {
+    return false;
+  }
+
+  return grip.preview && grip.preview.kind == "DOMEvent";
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(Event),
+  supportsObject
+};
+
+/***/ }),
 /* 41 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// ReactJS
-	const React = __webpack_require__(8);
-
-	// Reps
-	const {
-	  isGrip,
-	  getURLDisplayString,
-	  wrapRender
-	} = __webpack_require__(7);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders a grip object with URL data.
-	 */
-	ObjectWithURL.propTypes = {
-	  object: React.PropTypes.object.isRequired
-	};
-
-	function ObjectWithURL(props) {
-	  let grip = props.object;
-	  return span({
-	    "data-link-actor-id": grip.actor,
-	    className: "objectBox objectBox-" + getType(grip)
-	  }, getTitle(grip), span({ className: "objectPropValue" }, getDescription(grip)));
-	}
-
-	function getTitle(grip) {
-	  return span({ className: "objectTitle" }, getType(grip) + " ");
-	}
-
-	function getType(grip) {
-	  return grip.class;
-	}
-
-	function getDescription(grip) {
-	  return getURLDisplayString(grip.preview.url);
-	}
-
-	// Registration
-	function supportsObject(grip, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(grip)) {
-	    return false;
-	  }
-
-	  return grip.preview && grip.preview.kind == "ObjectWithURL";
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(ObjectWithURL),
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// ReactJS
+const React = __webpack_require__(0);
+
+// Reps
+const {
+  getGripType,
+  isGrip,
+  cropString,
+  wrapRender
+} = __webpack_require__(1);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * This component represents a template for Function objects.
+ */
+FunctionRep.propTypes = {
+  object: React.PropTypes.object.isRequired,
+  parameterNames: React.PropTypes.array
+};
+
+function FunctionRep(props) {
+  let grip = props.object;
+
+  return span({
+    "data-link-actor-id": grip.actor,
+    className: "objectBox objectBox-function",
+    // Set dir="ltr" to prevent function parentheses from
+    // appearing in the wrong direction
+    dir: "ltr"
+  }, getTitle(props, grip), getFunctionName(grip, props), "(", ...renderParams(props), ")");
+}
+
+function getTitle(props, grip) {
+  const {
+    simplified
+  } = props;
+
+  if (simplified === true && !grip.isGenerator && !grip.isAsync) {
+    return null;
+  }
+
+  let title = simplified === true ? "" : "function ";
+
+  if (grip.isGenerator) {
+    title = simplified === true ? "* " : "function* ";
+  }
+
+  if (grip.isAsync) {
+    title = "async" + " " + title;
+  }
+
+  return span({
+    className: "objectTitle"
+  }, title);
+}
+
+function getFunctionName(grip, props) {
+  let name = grip.userDisplayName || grip.displayName || grip.name || props.functionName || "";
+  return cropString(name, 100);
+}
+
+function renderParams(props) {
+  const {
+    parameterNames = []
+  } = props;
+
+  return parameterNames.filter(param => param).reduce((res, param, index, arr) => {
+    res.push(span({ className: "param" }, param));
+    if (index < arr.length - 1) {
+      res.push(span({ className: "delimiter" }, ", "));
+    }
+    return res;
+  }, []);
+}
+
+// Registration
+function supportsObject(grip, noGrip = false) {
+  const type = getGripType(grip, noGrip);
+  if (noGrip === true || !isGrip(grip)) {
+    return type == "function";
+  }
+
+  return type == "Function";
+}
+
+// Exports from this module
+
+module.exports = {
+  rep: wrapRender(FunctionRep),
+  supportsObject
+};
+
+/***/ }),
 /* 42 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-	const {
-	  isGrip,
-	  wrapRender
-	} = __webpack_require__(7);
-	const { MODE } = __webpack_require__(1);
-
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders an array. The array is enclosed by left and right bracket
-	 * and the max number of rendered items depends on the current mode.
-	 */
-	GripArray.propTypes = {
-	  object: React.PropTypes.object.isRequired,
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	  provider: React.PropTypes.object,
-	  onDOMNodeMouseOver: React.PropTypes.func,
-	  onDOMNodeMouseOut: React.PropTypes.func,
-	  onInspectIconClick: React.PropTypes.func
-	};
-
-	function GripArray(props) {
-	  let {
-	    object,
-	    mode = MODE.SHORT
-	  } = props;
-
-	  let items;
-	  let brackets;
-	  let needSpace = function (space) {
-	    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
-	  };
-
-	  if (mode === MODE.TINY) {
-	    let objectLength = getLength(object);
-	    let isEmpty = objectLength === 0;
-	    if (isEmpty) {
-	      items = [];
-	    } else {
-	      items = [span({
-	        className: "more-ellipsis",
-	        title: "more…"
-	      }, "…")];
-	    }
-	    brackets = needSpace(false);
-	  } else {
-	    let max = maxLengthMap.get(mode);
-	    items = arrayIterator(props, object, max);
-	    brackets = needSpace(items.length > 0);
-	  }
-
-	  let title = getTitle(props, object);
-
-	  return span({
-	    "data-link-actor-id": object.actor,
-	    className: "objectBox objectBox-array" }, title, span({
-	    className: "arrayLeftBracket"
-	  }, brackets.left), ...interleaveCommas(items), span({
-	    className: "arrayRightBracket"
-	  }, brackets.right), span({
-	    className: "arrayProperties",
-	    role: "group" }));
-	}
-
-	function interleaveCommas(items) {
-	  return items.reduce((res, item, index) => {
-	    if (index !== items.length - 1) {
-	      return res.concat(item, ", ");
-	    }
-	    return res.concat(item);
-	  }, []);
-	}
-
-	function getLength(grip) {
-	  if (!grip.preview) {
-	    return 0;
-	  }
-
-	  return grip.preview.length || grip.preview.childNodesLength || 0;
-	}
-
-	function getTitle(props, object) {
-	  if (props.mode === MODE.TINY) {
-	    return "";
-	  }
-
-	  let title = props.title || object.class || "Array";
-	  return span({
-	    className: "objectTitle"
-	  }, title + " ");
-	}
-
-	function getPreviewItems(grip) {
-	  if (!grip.preview) {
-	    return null;
-	  }
-
-	  return grip.preview.items || grip.preview.childNodes || [];
-	}
-
-	function arrayIterator(props, grip, max) {
-	  let { Rep } = __webpack_require__(2);
-
-	  let items = [];
-	  const gripLength = getLength(grip);
-
-	  if (!gripLength) {
-	    return items;
-	  }
-
-	  const previewItems = getPreviewItems(grip);
-	  let provider = props.provider;
-
-	  let emptySlots = 0;
-	  let foldedEmptySlots = 0;
-	  items = previewItems.reduce((res, itemGrip) => {
-	    if (res.length >= max) {
-	      return res;
-	    }
-
-	    let object;
-	    try {
-	      if (!provider && itemGrip === null) {
-	        emptySlots++;
-	        return res;
-	      }
-
-	      object = provider ? provider.getValue(itemGrip) : itemGrip;
-	    } catch (exc) {
-	      object = exc;
-	    }
-
-	    if (emptySlots > 0) {
-	      res.push(getEmptySlotsElement(emptySlots));
-	      foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
-	      emptySlots = 0;
-	    }
-
-	    if (res.length < max) {
-	      res.push(Rep(Object.assign({}, props, {
-	        object,
-	        mode: MODE.TINY,
-	        // Do not propagate title to array items reps
-	        title: undefined
-	      })));
-	    }
-
-	    return res;
-	  }, []);
-
-	  // Handle trailing empty slots if there are some.
-	  if (items.length < max && emptySlots > 0) {
-	    items.push(getEmptySlotsElement(emptySlots));
-	    foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
-	  }
-
-	  const itemsShown = items.length + foldedEmptySlots;
-	  if (gripLength > itemsShown) {
-	    items.push(span({
-	      className: "more-ellipsis",
-	      title: "more…"
-	    }, "…"));
-	  }
-
-	  return items;
-	}
-
-	function getEmptySlotsElement(number) {
-	  // TODO: Use l10N - See https://github.com/devtools-html/reps/issues/141
-	  return `<${number} empty slot${number > 1 ? "s" : ""}>`;
-	}
-
-	function supportsObject(grip, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(grip)) {
-	    return false;
-	  }
-
-	  return grip.preview && (grip.preview.kind == "ArrayLike" || type === "DocumentFragment");
-	}
-
-	const maxLengthMap = new Map();
-	maxLengthMap.set(MODE.SHORT, 3);
-	maxLengthMap.set(MODE.LONG, 10);
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(GripArray),
-	  supportsObject,
-	  maxLengthMap
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// ReactJS
+const React = __webpack_require__(0);
+// Dependencies
+const {
+  getGripType,
+  isGrip,
+  wrapRender
+} = __webpack_require__(1);
+
+const PropRep = __webpack_require__(4);
+const { MODE } = __webpack_require__(2);
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders a DOM Promise object.
+ */
+PromiseRep.propTypes = {
+  object: React.PropTypes.object.isRequired,
+  // @TODO Change this to Object.values once it's supported in Node's version of V8
+  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  onDOMNodeMouseOver: React.PropTypes.func,
+  onDOMNodeMouseOut: React.PropTypes.func,
+  onInspectIconClick: React.PropTypes.func
+};
+
+function PromiseRep(props) {
+  const object = props.object;
+  const { promiseState } = object;
+
+  const config = {
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-object"
+  };
+
+  if (props.mode === MODE.TINY) {
+    let { Rep } = __webpack_require__(3);
+
+    return span(config, getTitle(object), span({
+      className: "objectLeftBrace"
+    }, " { "), Rep({ object: promiseState.state }), span({
+      className: "objectRightBrace"
+    }, " }"));
+  }
+
+  const propsArray = getProps(props, promiseState);
+  return span(config, getTitle(object), span({
+    className: "objectLeftBrace"
+  }, " { "), ...propsArray, span({
+    className: "objectRightBrace"
+  }, " }"));
+}
+
+function getTitle(object) {
+  return span({
+    className: "objectTitle"
+  }, object.class);
+}
+
+function getProps(props, promiseState) {
+  const keys = ["state"];
+  if (Object.keys(promiseState).includes("value")) {
+    keys.push("value");
+  }
+
+  return keys.reduce((res, key, i) => {
+    let object = promiseState[key];
+    res = res.concat(PropRep(Object.assign({}, props, {
+      mode: MODE.TINY,
+      name: `<${key}>`,
+      object,
+      equal: ": ",
+      suppressQuotes: true
+    })));
+
+    // Interleave commas between elements
+    if (i !== keys.length - 1) {
+      res.push(", ");
+    }
+
+    return res;
+  }, []);
+}
+
+// Registration
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true || !isGrip(object)) {
+    return false;
+  }
+  return getGripType(object, noGrip) == "Promise";
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(PromiseRep),
+  supportsObject
+};
+
+/***/ }),
 /* 43 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-	const {
-	  isGrip,
-	  wrapRender
-	} = __webpack_require__(7);
-	const PropRep = __webpack_require__(16);
-	const { MODE } = __webpack_require__(1);
-	// Shortcuts
-	const { span } = React.DOM;
-
-	/**
-	 * Renders an map. A map is represented by a list of its
-	 * entries enclosed in curly brackets.
-	 */
-	GripMap.propTypes = {
-	  object: React.PropTypes.object,
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	  isInterestingEntry: React.PropTypes.func,
-	  onDOMNodeMouseOver: React.PropTypes.func,
-	  onDOMNodeMouseOut: React.PropTypes.func,
-	  onInspectIconClick: React.PropTypes.func,
-	  title: React.PropTypes.string
-	};
-
-	function GripMap(props) {
-	  let {
-	    mode,
-	    object
-	  } = props;
-
-	  const config = {
-	    "data-link-actor-id": object.actor,
-	    className: "objectBox objectBox-object"
-	  };
-
-	  if (mode === MODE.TINY) {
-	    return span(config, getTitle(props, object));
-	  }
-
-	  let propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));
-
-	  return span(config, getTitle(props, object), span({
-	    className: "objectLeftBrace"
-	  }, " { "), ...propsArray, span({
-	    className: "objectRightBrace"
-	  }, " }"));
-	}
-
-	function getTitle(props, object) {
-	  let title = props.title || (object && object.class ? object.class : "Map");
-	  return span({
-	    className: "objectTitle"
-	  }, title);
-	}
-
-	function safeEntriesIterator(props, object, max) {
-	  max = typeof max === "undefined" ? 3 : max;
-	  try {
-	    return entriesIterator(props, object, max);
-	  } catch (err) {
-	    console.error(err);
-	  }
-	  return [];
-	}
-
-	function entriesIterator(props, object, max) {
-	  // Entry filter. Show only interesting entries to the user.
-	  let isInterestingEntry = props.isInterestingEntry || ((type, value) => {
-	    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
-	  });
-
-	  let mapEntries = object.preview && object.preview.entries ? object.preview.entries : [];
-
-	  let indexes = getEntriesIndexes(mapEntries, max, isInterestingEntry);
-	  if (indexes.length < max && indexes.length < mapEntries.length) {
-	    // There are not enough entries yet, so we add uninteresting entries.
-	    indexes = indexes.concat(getEntriesIndexes(mapEntries, max - indexes.length, (t, value, name) => {
-	      return !isInterestingEntry(t, value, name);
-	    }));
-	  }
-
-	  let entries = getEntries(props, mapEntries, indexes);
-	  if (entries.length < object.preview.size) {
-	    // There are some undisplayed entries. Then display "…".
-	    entries.push(span({
-	      key: "more",
-	      className: "more-ellipsis",
-	      title: "more…"
-	    }, "…"));
-	  }
-
-	  return unfoldEntries(entries);
-	}
-
-	function unfoldEntries(items) {
-	  return items.reduce((res, item, index) => {
-	    if (Array.isArray(item)) {
-	      res = res.concat(item);
-	    } else {
-	      res.push(item);
-	    }
-
-	    // Interleave commas between elements
-	    if (index !== items.length - 1) {
-	      res.push(", ");
-	    }
-	    return res;
-	  }, []);
-	}
-
-	/**
-	 * Get entries ordered by index.
-	 *
-	 * @param {Object} props Component props.
-	 * @param {Array} entries Entries array.
-	 * @param {Array} indexes Indexes of entries.
-	 * @return {Array} Array of PropRep.
-	 */
-	function getEntries(props, entries, indexes) {
-	  let {
-	    onDOMNodeMouseOver,
-	    onDOMNodeMouseOut,
-	    onInspectIconClick
-	  } = props;
-
-	  // Make indexes ordered by ascending.
-	  indexes.sort(function (a, b) {
-	    return a - b;
-	  });
-
-	  return indexes.map((index, i) => {
-	    let [key, entryValue] = entries[index];
-	    let value = entryValue.value !== undefined ? entryValue.value : entryValue;
-
-	    return PropRep({
-	      name: key,
-	      equal: " \u2192 ",
-	      object: value,
-	      mode: MODE.TINY,
-	      onDOMNodeMouseOver,
-	      onDOMNodeMouseOut,
-	      onInspectIconClick
-	    });
-	  });
-	}
-
-	/**
-	 * Get the indexes of entries in the map.
-	 *
-	 * @param {Array} entries Entries array.
-	 * @param {Number} max The maximum length of indexes array.
-	 * @param {Function} filter Filter the entry you want.
-	 * @return {Array} Indexes of filtered entries in the map.
-	 */
-	function getEntriesIndexes(entries, max, filter) {
-	  return entries.reduce((indexes, [key, entry], i) => {
-	    if (indexes.length < max) {
-	      let value = entry && entry.value !== undefined ? entry.value : entry;
-	      // Type is specified in grip's "class" field and for primitive
-	      // values use typeof.
-	      let type = (value && value.class ? value.class : typeof value).toLowerCase();
-
-	      if (filter(type, value, key)) {
-	        indexes.push(i);
-	      }
-	    }
-
-	    return indexes;
-	  }, []);
-	}
-
-	function supportsObject(grip, type, noGrip = false) {
-	  if (noGrip === true || !isGrip(grip)) {
-	    return false;
-	  }
-	  return grip.preview && grip.preview.kind == "MapLike";
-	}
-
-	const maxLengthMap = new Map();
-	maxLengthMap.set(MODE.SHORT, 3);
-	maxLengthMap.set(MODE.LONG, 10);
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(GripMap),
-	  supportsObject,
-	  maxLengthMap
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// ReactJS
+const React = __webpack_require__(0);
+
+// Reps
+const {
+  getGripType,
+  isGrip,
+  wrapRender
+} = __webpack_require__(1);
+
+/**
+ * Renders a grip object with regular expression.
+ */
+RegExp.propTypes = {
+  object: React.PropTypes.object.isRequired
+};
+
+function RegExp(props) {
+  let { object } = props;
+
+  return React.DOM.span({
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-regexp regexpSource"
+  }, getSource(object));
+}
+
+function getSource(grip) {
+  return grip.displayString;
+}
+
+// Registration
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true || !isGrip(object)) {
+    return false;
+  }
+
+  return getGripType(object, noGrip) == "RegExp";
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(RegExp),
+  supportsObject
+};
+
+/***/ }),
 /* 44 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	// Dependencies
-	const React = __webpack_require__(8);
-	// Shortcuts
-	const { span } = React.DOM;
-	const {
-	  wrapRender
-	} = __webpack_require__(7);
-	const PropRep = __webpack_require__(16);
-	const { MODE } = __webpack_require__(1);
-	/**
-	 * Renders an map entry. A map entry is represented by its key, a column and its value.
-	 */
-	GripMapEntry.propTypes = {
-	  object: React.PropTypes.object,
-	  // @TODO Change this to Object.values once it's supported in Node's version of V8
-	  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
-	  onDOMNodeMouseOver: React.PropTypes.func,
-	  onDOMNodeMouseOut: React.PropTypes.func,
-	  onInspectIconClick: React.PropTypes.func
-	};
-
-	function GripMapEntry(props) {
-	  const {
-	    object
-	  } = props;
-
-	  const {
-	    key,
-	    value
-	  } = object.preview;
-
-	  return span({
-	    className: "objectBox objectBox-map-entry"
-	  }, ...PropRep(Object.assign({}, props, {
-	    name: key,
-	    object: value,
-	    equal: " \u2192 ",
-	    title: null,
-	    suppressQuotes: false
-	  })));
-	}
-
-	function supportsObject(grip, type, noGrip = false) {
-	  if (noGrip === true) {
-	    return false;
-	  }
-	  return grip && grip.type === "mapEntry" && grip.preview;
-	}
-
-	function createGripMapEntry(key, value) {
-	  return {
-	    type: "mapEntry",
-	    preview: {
-	      key,
-	      value
-	    }
-	  };
-	}
-
-	// Exports from this module
-	module.exports = {
-	  rep: wrapRender(GripMapEntry),
-	  createGripMapEntry,
-	  supportsObject
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// ReactJS
+const React = __webpack_require__(0);
+
+// Reps
+const {
+  getGripType,
+  isGrip,
+  getURLDisplayString,
+  wrapRender
+} = __webpack_require__(1);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders a grip representing CSSStyleSheet
+ */
+StyleSheet.propTypes = {
+  object: React.PropTypes.object.isRequired
+};
+
+function StyleSheet(props) {
+  let grip = props.object;
+
+  return span({
+    "data-link-actor-id": grip.actor,
+    className: "objectBox objectBox-object"
+  }, getTitle(grip), span({ className: "objectPropValue" }, getLocation(grip)));
+}
+
+function getTitle(grip) {
+  let title = "StyleSheet ";
+  return span({ className: "objectBoxTitle" }, title);
+}
+
+function getLocation(grip) {
+  // Embedded stylesheets don't have URL and so, no preview.
+  let url = grip.preview ? grip.preview.url : "";
+  return url ? getURLDisplayString(url) : "";
+}
+
+// Registration
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true || !isGrip(object)) {
+    return false;
+  }
+
+  return getGripType(object, noGrip) == "CSSStyleSheet";
+}
+
+// Exports from this module
+
+module.exports = {
+  rep: wrapRender(StyleSheet),
+  supportsObject
+};
+
+/***/ }),
 /* 45 */
-/***/ function(module, exports, __webpack_require__) {
-
-	/* 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/. */
-
-	const {
-	  Component,
-	  createFactory,
-	  DOM: dom,
-	  PropTypes
-	} = __webpack_require__(8);
-
-	const Tree = createFactory(__webpack_require__(46).Tree);
-	__webpack_require__(48);
-
-	const classnames = __webpack_require__(50);
-	const Svg = __webpack_require__(33);
-	const {
-	  REPS: {
-	    Rep,
-	    Grip
-	  }
-	} = __webpack_require__(2);
-	const {
-	  MODE
-	} = __webpack_require__(1);
-
-	const {
-	  getChildren,
-	  getParent,
-	  getValue,
-	  nodeHasAccessors,
-	  nodeHasAllEntriesInPreview,
-	  nodeHasProperties,
-	  nodeIsDefaultProperties,
-	  nodeIsEntries,
-	  nodeIsMapEntry,
-	  nodeIsMissingArguments,
-	  nodeIsOptimizedOut,
-	  nodeIsPrimitive,
-	  nodeIsPrototype
-	} = __webpack_require__(51);
-
-	// This implements a component that renders an interactive inspector
-	// for looking at JavaScript objects. It expects descriptions of
-	// objects from the protocol, and will dynamically fetch child
-	// properties as objects are expanded.
-	//
-	// If you want to inspect a single object, pass the name and the
-	// protocol descriptor of it:
-	//
-	//  ObjectInspector({
-	//    name: "foo",
-	//    desc: { writable: true, ..., { value: { actor: "1", ... }}},
-	//    ...
-	//  })
-	//
-	// If you want multiple top-level objects (like scopes), you can pass
-	// an array of manually constructed nodes as `roots`:
-	//
-	//  ObjectInspector({
-	//    roots: [{ name: ... }, ...],
-	//    ...
-	//  });
-
-	// There are 3 types of nodes: a simple node with a children array, an
-	// object that has properties that should be children when they are
-	// fetched, and a primitive value that should be displayed with no
-	// children.
-
-	class ObjectInspector extends Component {
-	  constructor() {
-	    super();
-
-	    this.actors = {};
-	    this.state = {
-	      expandedKeys: new Set(),
-	      focusedItem: null
-	    };
-
-	    const self = this;
-
-	    self.getChildren = this.getChildren.bind(this);
-	    self.renderTreeItem = this.renderTreeItem.bind(this);
-	    self.setExpanded = this.setExpanded.bind(this);
-	    self.focusItem = this.focusItem.bind(this);
-	    self.getRoots = this.getRoots.bind(this);
-	  }
-
-	  getChildren(item) {
-	    const {
-	      getObjectEntries,
-	      getObjectProperties
-	    } = this.props;
-	    const { actors } = this;
-
-	    return getChildren({
-	      getObjectEntries,
-	      getObjectProperties,
-	      actors,
-	      item
-	    });
-	  }
-
-	  getRoots() {
-	    return this.props.roots;
-	  }
-
-	  getKey(item) {
-	    return item.path;
-	  }
-
-	  setExpanded(item, expand) {
-	    const { expandedKeys } = this.state;
-	    const key = this.getKey(item);
-
-	    if (expand === true) {
-	      expandedKeys.add(key);
-	    } else {
-	      expandedKeys.delete(key);
-	    }
-
-	    this.setState({ expandedKeys });
-
-	    if (expand === true) {
-	      const {
-	        getObjectProperties,
-	        getObjectEntries,
-	        loadObjectProperties,
-	        loadObjectEntries
-	      } = this.props;
-
-	      const value = getValue(item);
-	      const parent = getParent(item);
-	      const parentValue = getValue(parent);
-	      const parentActor = parentValue ? parentValue.actor : null;
-
-	      if (nodeHasProperties(item) && value && !getObjectProperties(value.actor)) {
-	        loadObjectProperties(value);
-	      }
-
-	      if (nodeIsEntries(item) && !nodeHasAllEntriesInPreview(parent) && parentActor && !getObjectEntries(parentActor)) {
-	        loadObjectEntries(parentValue);
-	      }
-	    }
-	  }
-
-	  focusItem(item) {
-	    if (!this.props.disabledFocus && this.state.focusedItem !== item) {
-	      this.setState({
-	        focusedItem: item
-	      });
-
-	      if (this.props.onFocus) {
-	        this.props.onFocus(item);
-	      }
-	    }
-	  }
-
-	  renderTreeItem(item, depth, focused, arrow, expanded) {
-	    let objectValue;
-	    let label = item.name;
-	    let itemValue = getValue(item);
-
-	    const isPrimitive = nodeIsPrimitive(item);
-
-	    const unavailable = isPrimitive && itemValue && itemValue.hasOwnProperty && itemValue.hasOwnProperty("unavailable");
-
-	    if (nodeIsOptimizedOut(item)) {
-	      objectValue = dom.span({ className: "unavailable" }, "(optimized away)");
-	    } else if (nodeIsMissingArguments(item) || unavailable) {
-	      objectValue = dom.span({ className: "unavailable" }, "(unavailable)");
-	    } else if (nodeHasProperties(item) || nodeHasAccessors(item) || nodeIsMapEntry(item) || isPrimitive) {
-	      let repsProp = Object.assign({}, this.props);
-	      if (depth > 0) {
-	        repsProp.mode = this.props.mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
-	      }
-
-	      objectValue = this.renderGrip(item, repsProp);
-	    }
-
-	    const hasLabel = label !== null && typeof label !== "undefined";
-	    const hasValue = typeof objectValue !== "undefined";
-
-	    const SINGLE_INDENT_WIDTH = 15;
-	    const indentWidth = (depth + (isPrimitive ? 1 : 0)) * SINGLE_INDENT_WIDTH;
-
-	    const {
-	      onDoubleClick,
-	      onLabelClick
-	    } = this.props;
-
-	    return dom.div({
-	      className: classnames("node object-node", {
-	        focused,
-	        lessen: !expanded && (nodeIsDefaultProperties(item) || nodeIsPrototype(item))
-	      }),
-	      style: {
-	        marginLeft: indentWidth
-	      },
-	      onClick: isPrimitive === false ? e => {
-	        e.stopPropagation();
-	        this.setExpanded(item, !expanded);
-	      } : null,
-	      onDoubleClick: onDoubleClick ? e => {
-	        e.stopPropagation();
-	        onDoubleClick(item, {
-	          depth,
-	          focused,
-	          expanded
-	        });
-	      } : null
-	    }, isPrimitive === false ? Svg("arrow", {
-	      className: classnames({
-	        expanded: expanded
-	      })
-	    }) : null, hasLabel ? dom.span({
-	      className: "object-label",
-	      onClick: onLabelClick ? event => {
-	        event.stopPropagation();
-	        onLabelClick(item, {
-	          depth,
-	          focused,
-	          expanded,
-	          setExpanded: this.setExpanded
-	        });
-	      } : null
-	    }, label) : null, hasLabel && hasValue ? dom.span({ className: "object-delimiter" }, " : ") : null, hasValue ? objectValue : null);
-	  }
-
-	  renderGrip(item, props) {
-	    const object = getValue(item);
-	    return Rep(Object.assign({}, props, {
-	      object,
-	      mode: props.mode || MODE.TINY,
-	      defaultRep: Grip
-	    }));
-	  }
-
-	  render() {
-	    const {
-	      autoExpandDepth = 1,
-	      autoExpandAll = true,
-	      disabledFocus,
-	      itemHeight = 20,
-	      disableWrap = false
-	    } = this.props;
-
-	    const {
-	      expandedKeys,
-	      focusedItem
-	    } = this.state;
-
-	    let roots = this.getRoots();
-	    if (roots.length === 1 && nodeIsPrimitive(roots[0])) {
-	      return this.renderGrip(roots[0], this.props);
-	    }
-
-	    return Tree({
-	      className: disableWrap ? "nowrap" : "",
-	      autoExpandAll,
-	      autoExpandDepth,
-	      disabledFocus,
-	      itemHeight,
-
-	      isExpanded: item => expandedKeys.has(this.getKey(item)),
-	      focused: focusedItem,
-
-	      getRoots: this.getRoots,
-	      getParent,
-	      getChildren: this.getChildren,
-	      getKey: this.getKey,
-
-	      onExpand: item => this.setExpanded(item, true),
-	      onCollapse: item => this.setExpanded(item, false),
-	      onFocus: this.focusItem,
-
-	      renderItem: this.renderTreeItem
-	    });
-	  }
-	}
-
-	ObjectInspector.displayName = "ObjectInspector";
-
-	ObjectInspector.propTypes = {
-	  autoExpandAll: PropTypes.bool,
-	  autoExpandDepth: PropTypes.number,
-	  disabledFocus: PropTypes.bool,
-	  disableWrap: PropTypes.bool,
-	  roots: PropTypes.array,
-	  getObjectProperties: PropTypes.func.isRequired,
-	  loadObjectProperties: PropTypes.func.isRequired,
-	  itemHeight: PropTypes.number,
-	  mode: PropTypes.oneOf(Object.values(MODE)),
-	  onFocus: PropTypes.func,
-	  onDoubleClick: PropTypes.func,
-	  onLabelClick: PropTypes.func
-	};
-
-	module.exports = ObjectInspector;
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// Dependencies
+const React = __webpack_require__(0);
+const {
+  isGrip,
+  cropString,
+  cropMultipleLines,
+  wrapRender
+} = __webpack_require__(1);
+const { MODE } = __webpack_require__(2);
+const nodeConstants = __webpack_require__(18);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders DOM comment node.
+ */
+CommentNode.propTypes = {
+  object: React.PropTypes.object.isRequired,
+  // @TODO Change this to Object.values once it's supported in Node's version of V8
+  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key]))
+};
+
+function CommentNode(props) {
+  let {
+    object,
+    mode = MODE.SHORT
+  } = props;
+
+  let { textContent } = object.preview;
+  if (mode === MODE.TINY) {
+    textContent = cropMultipleLines(textContent, 30);
+  } else if (mode === MODE.SHORT) {
+    textContent = cropString(textContent, 50);
+  }
+
+  return span({
+    className: "objectBox theme-comment",
+    "data-link-actor-id": object.actor
+  }, `<!-- ${textContent} -->`);
+}
+
+// Registration
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true || !isGrip(object)) {
+    return false;
+  }
+  return object.preview && object.preview.nodeType === nodeConstants.COMMENT_NODE;
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(CommentNode),
+  supportsObject
+};
+
+/***/ }),
 /* 46 */
-/***/ function(module, exports, __webpack_require__) {
-
-	const Tree = __webpack_require__(47);
-
-	module.exports = {
-	  Tree
-	};
-
-/***/ },
+/***/ (function(module, exports, __webpack_require__) {
+
+/* 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/. */
+
+// ReactJS
+const React = __webpack_require__(0);
+
+// Utils
+const {
+  isGrip,
+  wrapRender
+} = __webpack_require__(1);
+const { MODE } = __webpack_require__(2);
+const nodeConstants = __webpack_require__(18);
+const Svg = __webpack_require__(10);
+
+// Shortcuts
+const { span } = React.DOM;
+
+/**
+ * Renders DOM element node.
+ */
+ElementNode.propTypes = {
+  object: React.PropTypes.object.isRequired,
+  // @TODO Change this to Object.values once it's supported in Node's version of V8
+  mode: React.PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+  onDOMNodeMouseOver: React.PropTypes.func,
+  onDOMNodeMouseOut: React.PropTypes.func,
+  onInspectIconClick: React.PropTypes.func
+};
+
+function ElementNode(props) {
+  let {
+    object,
+    mode,
+    onDOMNodeMouseOver,
+    onDOMNodeMouseOut,
+    onInspectIconClick
+  } = props;
+  let elements = getElements(object, mode);
+
+  let isInTree = object.preview && object.preview.isConnected === true;
+
+  let baseConfig = {
+    "data-link-actor-id": object.actor,
+    className: "objectBox objectBox-node"
+  };
+  let inspectIcon;
+  if (isInTree) {
+    if (onDOMNodeMouseOver) {
+      Object.assign(baseConfig, {
+        onMouseOver: _ => onDOMNodeMouseOver(object)
+      });
+    }
+
+    if (onDOMNodeMouseOut) {
+      Object.assign(baseConfig, {
+        onMouseOut: onDOMNodeMouseOut
+      });
+    }
+
+    if (onInspectIconClick) {
+      inspectIcon = Svg("open-inspector", {
+        element: "a",
+        draggable: false,
+        // TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
+        title: "Click to select the node in the inspector",
+        onClick: e => onInspectIconClick(object, e)
+      });
+    }
+  }
+
+  return span(baseConfig, ...elements, inspectIcon);
+}
+
+function getElements(grip, mode) {
+  let { attributes, nodeName } = grip.preview;
+  const nodeNameElement = span({
+    className: "tag-name theme-fg-color3"
+  }, nodeName);
+
+  if (mode === MODE.TINY) {
+    let elements = [nodeNameElement];
+    if (attributes.id) {
+      elements.push(span({ className: "attr-name theme-fg-color2" }, `#${attributes.id}`));
+    }
+    if (attributes.class) {
+      elements.push(span({ className: "attr-name theme-fg-color2" }, attributes.class.replace(/(^\s+)|(\s+$)/g, "").split(" ").map(cls => `.${cls}`).join("")));
+    }
+    return elements;
+  }
+  let attributeKeys = Object.keys(attributes);
+  if (attributeKeys.includes("class")) {
+    attributeKeys.splice(attributeKeys.indexOf("class"), 1);
+    attributeKeys.unshift("class");
+  }
+  if (attributeKeys.includes("id")) {
+    attributeKeys.splice(attributeKeys.indexOf("id"), 1);
+    attributeKeys.unshift("id");
+  }
+  const attributeElements = attributeKeys.reduce((arr, name, i, keys) => {
+    let value = attributes[name];
+    let attribute = span({}, span({ className: "attr-name theme-fg-color2" }, `${name}`), `="`, span({ className: "attr-value theme-fg-color6" }, `${value}`), `"`);
+
+    return arr.concat([" ", attribute]);
+  }, []);
+
+  return ["<", nodeNameElement, ...attributeElements, ">"];
+}
+
+// Registration
+function supportsObject(object, noGrip = false) {
+  if (noGrip === true || !isGrip(object)) {
+    return false;
+  }
+  return object.preview && object.preview.nodeType === nodeConstants.ELEMENT_NODE;
+}
+
+// Exports from this module
+module.exports = {
+  rep: wrapRender(ElementNode),
+  supportsObject
+};
+
+/***/ }),
 /* 47 */
-/***/ function(module, exports, __webpack_require__) {
-
-	const { DOM: dom, createClass, createFactory, PropTypes } = __webpack_require__(8);
-
-	const AUTO_EXPAND_DEPTH = 0; // depth
-
-	/**
-	 * An arrow that displays whether its node is expanded (▼) or collapsed
-	 * (▶). When its node has no children, it is hidden.
-	 */
-	const ArrowExpander = createFactory(createClass({
-	  displayName: "ArrowExpander",
-
-	  shouldComponentUpdate(nextProps, nextState) {
-	    return this.props.item !== nextProps.item || this.props.visible !== nextProps.visible || this.props.expanded !== nextProps.expanded;
-	  },
-
-	  render() {
-	    const attrs = {
-	      className: "arrow theme-twisty",
-	      onClick: this.props.expanded ? () => this.props.onCollapse(this.props.item) : e => this.props.onExpand(this.props.item, e.altKey)
-	    };
-
-	    if (this.props.expanded) {
-	      attrs.className += " open";
-	    }
-
-	    if (!this.props.visible) {
-	      attrs.style = Object.assign({}, this.props.style || {}, {
-	        visibility: "hidden"
-	      });
-	    }
-
-	    return dom.div(attrs, this.props.children);
-	  }
-	}));
-
-	const TreeNode = createFactory(createClass({
-	  displayName: "TreeNode",
-
-	  componentDidMount() {
-	    if (this.props.focused) {
-	      this.refs.button.focus();
-	    }
-	  },
-
-	  componentDidUpdate() {
-	    if (this.props.focused) {
-	      this.refs.button.focus();
-	    }
-	  },
-
-	  shouldComponentUpdate(nextProps) {
-	    return this.props.item !== nextProps.item || this.props.focused !== nextProps.focused || this.props.expanded !== nextProps.expanded;
-	  },
-
-	  render() {
-	    const arrow = ArrowExpander({
-	      item: this.props.item,
-	      expanded: this.props.expanded,
-	      visible: this.props.hasChildren,
-	      onExpand: this.props.onExpand,
-	      onCollapse: this.props.onCollapse
-	    });
-
-	    let isOddRow = this.props.index % 2;
-	    return dom.div({
-	      className: `tree-node div ${isOddRow ? "tree-node-odd" : ""}`,
-	      onFocus: this.props.onFocus,
-	      onClick: this.props.onFocus,
-	      onBlur: this.props.onBlur,
-	      style: {
-	        padding: 0,
-	        margin: 0
-	      }
-	    }, this.props.renderItem(this.props.item, this.props.depth, this.props.focused, arrow, this.props.expanded),
-
-	    // XXX: OSX won't focus/blur regular elements even if you set tabindex
-	    // unless there is an input/button child.
-	    dom.button(this._buttonAttrs));
-	  },
-
-	  _buttonAttrs: {
-	    ref: "button",
-	    style: {
-	      opacity: 0,
-	      width: "0 !important",
-	      height: "0 !important",
-	      padding: "0 !important",
-	      outline: "none",
-	      MozAppearance: "none",
-	      // XXX: Despite resetting all of the above properties (and margin), the
-	      // button still ends up with ~79px width, so we set a large negative
-	      // margin to completely hide it.
-	      MozMarginStart: "-1000px !important"
-	    }
-	  }
-	}));
-
-	/**
-	 * Create a function that calls the given function `fn` only once per animation
-	 * frame.
-	 *
-	 * @param {Function} fn
-	 * @returns {Function}
-	 */
-	function oncePerAnimationFrame(fn) {
-	  let animationId = null;
-	  let argsToPass = null;
-	  return function (...args) {
-	    argsToPass = args;
-	    if (animationId !== null) {
-	      return;
-	    }
-
-	    animationId = requestAnimationFrame(() => {
-	      fn.call(this, ...argsToPass);
-	      animationId = null;
-	      argsToPass = null;
-	    });
-	  };
-	}
-
-	const NUMBER_OF_OFFSCREEN_ITEMS = 1;
-
-	/**
-	 * A generic tree component. See propTypes for the public API.
-	 *
-	 * @see `devtools/client/memory/components/test/mochitest/head.js` for usage
-	 * @see `devtools/client/memory/components/heap.js` for usage
-	 */
-	const Tree = module.exports = createClass({
-	  displayName: "Tree",
</