Merge inbound to mozilla-central. a=merge
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Thu, 12 Apr 2018 12:38:56 +0300
changeset 412936 246c614e160586c1eb3167cff866dd550be35e03
parent 412907 abd91e812e7e0c85b4943bdd94f0a1326320d777 (current diff)
parent 412935 1e9190687c32a0fba7c3c5d2ad2b8ec2f1da2e8e (diff)
child 412937 ee1d1bf1dc8a83eec16967ddb61dd5024c8d6058
child 412966 e4e3e42d46838df5d0702d2eab6273449902ee98
child 412991 2655ff95e4ff89b252aea31795e067e3c976e222
push id33824
push userebalazs@mozilla.com
push dateThu, 12 Apr 2018 09:39:57 +0000
treeherdermozilla-central@246c614e1605 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
246c614e1605 / 61.0a1 / 20180412100111 / files
nightly linux64
246c614e1605 / 61.0a1 / 20180412100111 / files
nightly mac
246c614e1605 / 61.0a1 / 20180412100111 / files
nightly win32
246c614e1605 / 61.0a1 / 20180412100111 / files
nightly win64
246c614e1605 / 61.0a1 / 20180412100111 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
toolkit/content/select-child.js
--- a/accessible/tests/mochitest/events/test_focus_autocomplete.xul
+++ b/accessible/tests/mochitest/events/test_focus_autocomplete.xul
@@ -254,17 +254,17 @@
       }
 
       // XUL autocomplete
       var popupNode = autocompleteNode.popup;
       if (!popupNode) {
         // HTML form autocomplete
         var controller = Cc["@mozilla.org/autocomplete/controller;1"].
           getService(Ci.nsIAutoCompleteController);
-        popupNode = controller.input.popup.QueryInterface(nsIDOMNode);
+        popupNode = controller.input.popup;
       }
 
       if (popupNode) {
         if ("richlistbox" in popupNode) {
           var list = getAccessible(popupNode.richlistbox);
           return list.getChildAt(aItemObj.index);
         }
 
--- a/accessible/tests/mochitest/relations/a11y.ini
+++ b/accessible/tests/mochitest/relations/a11y.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
 support-files =
   !/accessible/tests/mochitest/*.js
 
 [test_bindings.xhtml]
 [test_embeds.xul]
+skip-if = os == 'linux' && !debug # bug 1411145
 [test_general.html]
 [test_general.xul]
 [test_tabbrowser.xul]
 [test_tree.xul]
 [test_ui_modalprompt.html]
 [test_update.html]
--- a/browser/base/content/browser-fullZoom.js
+++ b/browser/base/content/browser-fullZoom.js
@@ -34,18 +34,17 @@ var FullZoom = {
         !Services.prefs.getBoolPref("privacy.resistFingerprinting") &&
         Services.prefs.getBoolPref("browser.zoom.siteSpecific");
     }
     return this._siteSpecificPref;
   },
 
   // nsISupports
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
-                                         Ci.nsIObserver,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsIContentPrefObserver,
                                          Ci.nsISupportsWeakReference,
                                          Ci.nsISupports]),
 
   // Initialization & Destruction
 
   init: function FullZoom_init() {
     gBrowser.addEventListener("ZoomChangeUsingMouseWheel", this);
@@ -82,17 +81,17 @@ var FullZoom = {
     Services.prefs.removeObserver("browser.zoom.", this);
     this._cps2.removeObserverForName(this.name, this);
     gBrowser.removeEventListener("ZoomChangeUsingMouseWheel", this);
   },
 
 
   // Event Handlers
 
-  // nsIDOMEventListener
+  // EventListener
 
   handleEvent: function FullZoom_handleEvent(event) {
     switch (event.type) {
       case "ZoomChangeUsingMouseWheel":
         let browser = this._getTargetedBrowser(event);
         this._ignorePendingZoomAccesses(browser);
         this._applyZoomToPref(browser);
         break;
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -95,17 +95,17 @@
                            command="cmd_newNavigatorTab"
                            onclick="checkForMiddleClick(this, event);"
                            tooltip="dynamic-shortcut-tooltip"/>
         <xul:spacer class="closing-tabs-spacer" anonid="closing-tabs-spacer"
                     style="width: 0;"/>
       </xul:arrowscrollbox>
     </content>
 
-    <implementation implements="nsIDOMEventListener, nsIObserver">
+    <implementation implements="nsIObserver">
       <constructor>
         <![CDATA[
           this._tabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth");
 
           let strId = PrivateBrowsingUtils.isWindowPrivate(window) ?
               "emptyPrivateTabTitle" : "emptyTabTitle";
           this.emptyTabTitle = gTabBrowserBundle.GetStringFromName("tabs." + strId);
 
@@ -1960,17 +1960,17 @@
         }
       ]]>
       </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-alltabs-popup"
            extends="chrome://global/content/bindings/popup.xml#popup">
-    <implementation implements="nsIDOMEventListener">
+    <implementation>
       <method name="_tabOnAttrModified">
         <parameter name="aEvent"/>
         <body><![CDATA[
           var tab = aEvent.target;
           if (tab.mCorrespondingMenuitem)
             this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab);
         ]]></body>
       </method>
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -48,17 +48,17 @@ file, You can obtain one at http://mozil
                         xbl:inherits="open,parentfocused=focused,usertyping"/>
         <children includes="hbox"/>
       </xul:hbox>
       <xul:popupset anonid="popupset"
                     class="autocomplete-result-popupset"/>
       <children includes="toolbarbutton"/>
     </content>
 
-    <implementation implements="nsIObserver, nsIDOMEventListener">
+    <implementation implements="nsIObserver">
       <field name="ExtensionSearchHandler" readonly="true">
         (ChromeUtils.import("resource://gre/modules/ExtensionSearchHandler.jsm", {})).ExtensionSearchHandler;
       </field>
 
       <constructor><![CDATA[
         this._prefs = Cc["@mozilla.org/preferences-service;1"]
                         .getService(Ci.nsIPrefService)
                         .getBranch("browser.urlbar.");
--- a/browser/components/feeds/FeedWriter.js
+++ b/browser/components/feeds/FeedWriter.js
@@ -541,17 +541,17 @@ FeedWriter.prototype = {
       } else {
         LOG("FeedWriter._setAlwaysUseLabel: Expected textNode as lastChild of alwaysUse label");
         let textNode = this._document.createTextNode(label);
         checkboxText.appendChild(textNode);
       }
     }
   },
 
-  // nsIDomEventListener
+  // EventListener
   handleEvent(event) {
     if (event.target.ownerDocument != this._document) {
       LOG("FeedWriter.handleEvent: Someone passed the feed writer as a listener to the events of another document!");
       return;
     }
 
     switch (event.type) {
       case "click":
@@ -976,13 +976,13 @@ FeedWriter.prototype = {
         }
       });
     } else {
       subscribeCallback();
     }
   },
 
   classID: FEEDWRITER_CID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener, Ci.nsIObserver,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsIDOMGlobalPropertyInitializer])
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FeedWriter]);
--- a/browser/components/places/content/bookmarkProperties.js
+++ b/browser/components/places/content/bookmarkProperties.js
@@ -344,17 +344,17 @@ var BookmarkPropertiesPanel = {
               .addEventListener("input", this);
         }
       }
     }
     // Only enable the accept button once we've finished everything.
     acceptButton.disabled = acceptButtonDisabled;
   },
 
-  // nsIDOMEventListener
+  // EventListener
   handleEvent: function BPP_handleEvent(aEvent) {
     var target = aEvent.target;
     switch (aEvent.type) {
       case "input":
         if (target.id == "editBMPanel_locationField" ||
             target.id == "editBMPanel_keywordField") {
           // Check uri fields to enable accept button if input is valid
           document.documentElement
@@ -368,18 +368,17 @@ var BookmarkPropertiesPanel = {
           elementsHeight.set(id, newHeight);
         }
         break;
     }
   },
 
   // nsISupports
   QueryInterface: function BPP_QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIDOMEventListener) ||
-        aIID.equals(Ci.nsISupports))
+    if (aIID.equals(Ci.nsISupports))
       return this;
 
     throw Cr.NS_NOINTERFACE;
   },
 
   _element: function BPP__element(aID) {
     return document.getElementById("editBMPanel_" + aID);
   },
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -1004,18 +1004,17 @@ function PlacesToolbar(aPlace) {
 
 PlacesToolbar.prototype = {
   __proto__: PlacesViewBase.prototype,
 
   _cbEvents: ["dragstart", "dragover", "dragexit", "dragend", "drop",
               "mousemove", "mouseover", "mouseout"],
 
   QueryInterface: function PT_QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIDOMEventListener) ||
-        aIID.equals(Ci.nsITimerCallback))
+    if (aIID.equals(Ci.nsITimerCallback))
       return this;
 
     return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
   },
 
   uninit: function PT_uninit() {
     this._removeEventListeners(this._viewElt, this._cbEvents, false);
     this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
@@ -1906,23 +1905,16 @@ function PlacesMenu(aPopupShowingEvent, 
 
   PlacesViewBase.call(this, aPlace, aOptions);
   this._onPopupShowing(aPopupShowingEvent);
 }
 
 PlacesMenu.prototype = {
   __proto__: PlacesViewBase.prototype,
 
-  QueryInterface: function PM_QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIDOMEventListener))
-      return this;
-
-    return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
-  },
-
   _removeChild: function PM_removeChild(aChild) {
     PlacesViewBase.prototype._removeChild.apply(this, arguments);
   },
 
   uninit: function PM_uninit() {
     this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
                                true);
     this._removeEventListeners(window, ["unload"], false);
--- a/browser/components/places/content/editBookmark.js
+++ b/browser/components/places/content/editBookmark.js
@@ -460,18 +460,17 @@ var gEditItemOverlay = {
                                       this._folderMenuList.selectedIndex);
 
     // Hide the folders-separator if no folder is annotated as recently-used
     this._element("foldersSeparator").hidden = (menupopup.childNodes.length <= 6);
     this._folderMenuList.disabled = this.readOnly;
   },
 
   QueryInterface:
-  XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
-                         Ci.nsINavBookmarkObserver]),
+  XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver]),
 
   _element(aID) {
     return document.getElementById("editBMPanel_" + aID);
   },
 
   uninitPanel(aHideCollapsibleElements) {
     if (aHideCollapsibleElements) {
       // Hide the folder tree if it was previously visible.
@@ -950,17 +949,17 @@ var gEditItemOverlay = {
     this._folderTree.focus();
     this._folderTree.selectItems([ip.itemId]);
     PlacesUtils.asContainer(this._folderTree.selectedNode).containerOpen = true;
     this._folderTree.selectItems([this._lastNewItem]);
     this._folderTree.startEditing(this._folderTree.view.selection.currentIndex,
                                   this._folderTree.columns.getFirstColumn());
   },
 
-  // nsIDOMEventListener
+  // EventListener
   handleEvent(aEvent) {
     switch (aEvent.type) {
     case "CheckboxStateChange":
       // Update the tags field when items are checked/unchecked in the listbox
       let tags = this._getTagsArrayFromTagsInputField();
       let tagCheckbox = aEvent.target;
 
       let curTagIndex = tags.indexOf(tagCheckbox.label);
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -192,18 +192,17 @@ var PlacesOrganizer = {
     if (!Services.policies.isAllowed("profileImport")) {
       document.getElementById("OrganizerCommand_browserImport").setAttribute("disabled", true);
     }
 
     ContentArea.focus();
   },
 
   QueryInterface: function PO_QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIDOMEventListener) ||
-        aIID.equals(Ci.nsISupports))
+    if (aIID.equals(Ci.nsISupports))
       return this;
 
     throw Cr.NS_NOINTERFACE;
   },
 
   handleEvent: function PO_handleEvent(aEvent) {
     if (aEvent.type != "AppCommand")
       return;
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1290,17 +1290,17 @@ var gMainPane = {
     Services.prefs.removeObserver(PREF_AUDIO_FEED_SELECTED_READER, this);
 
     Services.prefs.removeObserver(PREF_CONTAINERS_EXTENSION, this);
   },
 
 
   // nsISupports
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsIDOMEventListener]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
 
   // nsIObserver
 
   async observe(aSubject, aTopic, aData) {
     if (aTopic == "nsPref:changed") {
       if (aData == PREF_CONTAINERS_EXTENSION) {
         this.readBrowserContainersCheckbox();
         return;
@@ -1322,17 +1322,17 @@ var gMainPane = {
       }
       if (AppConstants.MOZ_UPDATER) {
         this.updateReadPrefs();
       }
     }
   },
 
 
-  // nsIDOMEventListener
+  // EventListener
 
   handleEvent(aEvent) {
     if (aEvent.type == "unload") {
       this.destroy();
     }
   },
 
 
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -1112,17 +1112,17 @@
                       label="&searchInNewTab.label;"
                       accesskey="&searchInNewTab.accesskey;"/>
         <xul:menuitem anonid="search-one-offs-context-set-default"
                       label="&searchSetAsDefault.label;"
                       accesskey="&searchSetAsDefault.accesskey;"/>
       </xul:menupopup>
     </content>
 
-    <implementation implements="nsIDOMEventListener,nsIObserver,nsIWeakReference">
+    <implementation implements="nsIObserver,nsIWeakReference">
 
       <!-- Width in pixels of the one-off buttons.  49px is the min-width of
            each search engine button, adapt this const when changing the css.
            It's actually 48px + 1px of right border. -->
       <property name="buttonWidth" readonly="true" onget="return 49;"/>
 
       <field name="_popup">null</field>
 
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -424,17 +424,16 @@ var SessionStore = {
   },
 };
 
 // Freeze the SessionStore object. We don't want anyone to modify it.
 Object.freeze(SessionStore);
 
 var SessionStoreInternal = {
   QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsIDOMEventListener,
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference
   ]),
 
   _globalState: new GlobalState(),
 
   // A counter to be used to generate a unique ID for each closed tab or window.
   _nextClosedId: 0,
@@ -994,17 +993,17 @@ var SessionStoreInternal = {
       default:
         throw new Error(`received unknown message '${aMessage.name}'`);
     }
   },
 
   /* ........ Window Event Handlers .............. */
 
   /**
-   * Implement nsIDOMEventListener for handling various window and tab events
+   * Implement EventListener for handling various window and tab events
    */
   handleEvent: function ssi_handleEvent(aEvent) {
     let win = aEvent.currentTarget.ownerGlobal;
     let target = aEvent.originalTarget;
     switch (aEvent.type) {
       case "TabOpen":
         this.onTabAdd(win);
         break;
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -46,16 +46,19 @@ const DOM_STORAGE_LIMIT_PREF = "browser.
 // or not, and should only be used for tests or debugging.
 const TIMEOUT_DISABLED_PREF = "browser.sessionstore.debug.no_auto_updates";
 
 const PREF_INTERVAL = "browser.sessionstore.interval";
 
 const kNoIndex = Number.MAX_SAFE_INTEGER;
 const kLastIndex = Number.MAX_SAFE_INTEGER - 1;
 
+// Grab our global so we can access it in functions below.
+const global = this;
+
 /**
  * A function that will recursively call |cb| to collected data for all
  * non-dynamic frames in the current frame/docShell tree.
  */
 function mapFrameTree(callback) {
   return (function map(frame, cb) {
     // Collect data for the current frame.
     let obj = cb(frame) || {};
@@ -145,17 +148,17 @@ var StateChangeNotifier = {
 
 /**
  * Listens for and handles content events that we need for the
  * session store service to be notified of state changes in content.
  */
 var EventListener = {
 
   init() {
-    addEventListener("load", ssu.createDynamicFrameEventFilter(this), true);
+    ssu.addDynamicFrameFilteredListener(global, "load", this, true);
   },
 
   handleEvent(event) {
     // Ignore load events from subframes.
     if (event.target != content.document) {
       return;
     }
 
@@ -459,17 +462,17 @@ var SessionHistoryListener = {
  * is scrolled this will return null so that we don't tack a property onto
  * the tabData object in the parent process.
  *
  * Example:
  *   {scroll: "100,100", children: [null, null, {scroll: "200,200"}]}
  */
 var ScrollPositionListener = {
   init() {
-    addEventListener("scroll", ssu.createDynamicFrameEventFilter(this));
+    ssu.addDynamicFrameFilteredListener(global, "scroll", this, false);
     StateChangeNotifier.addObserver(this);
   },
 
   handleEvent() {
     MessageQueue.push("scroll", () => this.collect());
   },
 
   onPageLoadCompleted() {
@@ -499,17 +502,17 @@ var ScrollPositionListener = {
  *     children: [
  *       null,
  *       {url: "http://sub.mozilla.org/", id: {input_id: "input value 2"}}
  *     ]
  *   }
  */
 var FormDataListener = {
   init() {
-    addEventListener("input", ssu.createDynamicFrameEventFilter(this), true);
+    ssu.addDynamicFrameFilteredListener(global, "input", this, true);
     StateChangeNotifier.addObserver(this);
   },
 
   handleEvent() {
     MessageQueue.push("formdata", () => this.collect());
   },
 
   onPageLoadStarted() {
@@ -591,23 +594,25 @@ var SessionStorageListener = {
     this._changes = undefined;
   },
 
   // The event listener waiting for MozSessionStorageChanged events.
   _listener: null,
 
   resetEventListener() {
     if (!this._listener) {
-      this._listener = ssu.createDynamicFrameEventFilter(this);
-      addEventListener("MozSessionStorageChanged", this._listener, true);
+      this._listener =
+        ssu.addDynamicFrameFilteredListener(global, "MozSessionStorageChanged",
+                                            this, true);
     }
   },
 
   removeEventListener() {
-    removeEventListener("MozSessionStorageChanged", this._listener, true);
+    ssu.removeDynamicFrameFilteredListener(global, "MozSessionStorageChanged",
+                                           this._listener, true);
     this._listener = null;
   },
 
   handleEvent(event) {
     if (!docShell) {
       return;
     }
 
--- a/browser/components/sessionstore/nsISessionStoreUtils.idl
+++ b/browser/components/sessionstore/nsISessionStoreUtils.idl
@@ -1,16 +1,16 @@
 /* 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 "nsISupports.idl"
 
 interface mozIDOMWindowProxy;
-interface nsIDOMEventListener;
+interface nsIDOMEventTarget;
 
 /**
  * A callback passed to nsISessionStoreUtils.forEachNonDynamicChildFrame().
  */
 [function, scriptable, uuid(8199ebf7-76c0-43d6-bcbe-913dd3de3ebf)]
 interface nsISessionStoreUtilsFrameCallback : nsISupports
 {
   /**
@@ -30,17 +30,40 @@ interface nsISessionStoreUtils : nsISupp
   /**
    * Calls the given |callback| once for each non-dynamic child frame of the
    * given |window|.
    */
   void forEachNonDynamicChildFrame(in mozIDOMWindowProxy window,
                                    in nsISessionStoreUtilsFrameCallback callback);
 
   /**
-   * Creates and returns an event listener that filters events from dynamic
-   * docShells. It forwards those from non-dynamic docShells to the given
-   * |listener|.
+   * Takes the given listener, wraps it in a filter that filters out events from
+   * dynamic docShells, and adds that filter as a listener for the given event
+   * type on the given event target.  The listener that was added is returned
+   * (as nsISupports) so that it can later be removed via
+   * removeDynamicFrameFilteredListener.
    *
    * This is implemented as a native filter, rather than a JS-based one, for
    * performance reasons.
+   *
+   * Once bug 1444991 is fixed, this should start taking an EventTarget.
    */
-  nsIDOMEventListener createDynamicFrameEventFilter(in nsIDOMEventListener listener);
+  [implicit_jscontext]
+  nsISupports addDynamicFrameFilteredListener(in nsIDOMEventTarget target,
+                                              in AString type,
+                                              in jsval listener,
+                                              in boolean useCapture);
+
+  /**
+   * Remove the passed-in filtered listener from the given event target, if it's
+   * currently a listener for the given event type there.  The 'listener'
+   * argument must be something that was returned by
+   * addDynamicFrameFilteredListener.
+   *
+   * This is needed, instead of the normal removeEventListener, because the
+   * caller doesn't actually have something that WebIDL considers an
+   * EventListener.
+   */
+  void removeDynamicFrameFilteredListener(in nsIDOMEventTarget target,
+                                          in AString type,
+                                          in nsISupports listener,
+                                          in boolean useCapture);
 };
--- a/browser/components/sessionstore/nsSessionStoreUtils.cpp
+++ b/browser/components/sessionstore/nsSessionStoreUtils.cpp
@@ -1,36 +1,39 @@
 /* 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 "nsSessionStoreUtils.h"
 
 #include "mozilla/dom/Event.h"
+#include "mozilla/dom/EventListenerBinding.h"
+#include "mozilla/dom/EventTarget.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDocShell.h"
 
 using namespace mozilla::dom;
 
 namespace {
 
 class DynamicFrameEventFilter final : public nsIDOMEventListener
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(DynamicFrameEventFilter)
 
-  explicit DynamicFrameEventFilter(nsIDOMEventListener* aListener)
+  explicit DynamicFrameEventFilter(EventListener* aListener)
     : mListener(aListener)
   { }
 
   NS_IMETHODIMP HandleEvent(nsIDOMEvent* aEvent) override
   {
     if (mListener && TargetInNonDynamicDocShell(aEvent)) {
-      mListener->HandleEvent(aEvent);
+      mListener->HandleEvent(*aEvent->InternalDOMEvent());
     }
 
     return NS_OK;
   }
 
 private:
   ~DynamicFrameEventFilter() { }
 
@@ -51,17 +54,17 @@ private:
       return false;
     }
 
     bool isDynamic = false;
     nsresult rv = docShell->GetCreatedDynamically(&isDynamic);
     return NS_SUCCEEDED(rv) && !isDynamic;
   }
 
-  nsCOMPtr<nsIDOMEventListener> mListener;
+  RefPtr<EventListener> mListener;
 };
 
 NS_IMPL_CYCLE_COLLECTION(DynamicFrameEventFilter, mListener)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DynamicFrameEventFilter)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
 NS_INTERFACE_MAP_END
@@ -109,18 +112,50 @@ nsSessionStoreUtils::ForEachNonDynamicCh
 
     aCallback->HandleFrame(item->GetWindow(), childOffset);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSessionStoreUtils::CreateDynamicFrameEventFilter(nsIDOMEventListener* aListener,
-                                                   nsIDOMEventListener** aResult)
+nsSessionStoreUtils::AddDynamicFrameFilteredListener(nsIDOMEventTarget* aTarget,
+                                                     const nsAString& aType,
+                                                     JS::Handle<JS::Value> aListener,
+                                                     bool aUseCapture,
+                                                     JSContext* aCx,
+                                                     nsISupports** aResult)
 {
-  NS_ENSURE_TRUE(aListener, NS_ERROR_INVALID_ARG);
+  if (NS_WARN_IF(!aListener.isObject())) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
+  NS_ENSURE_TRUE(target, NS_ERROR_NO_INTERFACE);
 
-  nsCOMPtr<nsIDOMEventListener> filter(new DynamicFrameEventFilter(aListener));
+  JS::Rooted<JSObject*> obj(aCx, &aListener.toObject());
+  RefPtr<EventListener> listener =
+    new EventListener(aCx, obj, GetIncumbentGlobal());
+
+  nsCOMPtr<nsIDOMEventListener> filter(new DynamicFrameEventFilter(listener));
+
+  nsresult rv = target->AddEventListener(aType, filter, aUseCapture);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   filter.forget(aResult);
-
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsSessionStoreUtils::RemoveDynamicFrameFilteredListener(nsIDOMEventTarget* aTarget,
+                                                        const nsAString& aType,
+                                                        nsISupports* aListener,
+                                                        bool aUseCapture)
+{
+  nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
+  NS_ENSURE_TRUE(target, NS_ERROR_NO_INTERFACE);
+
+  nsCOMPtr<nsIDOMEventListener> listener = do_QueryInterface(aListener);
+  NS_ENSURE_TRUE(listener, NS_ERROR_NO_INTERFACE);
+
+  target->RemoveEventListener(aType, listener, aUseCapture);
+  return NS_OK;
+}
--- a/browser/modules/WindowsPreviewPerTab.jsm
+++ b/browser/modules/WindowsPreviewPerTab.jsm
@@ -150,18 +150,17 @@ function PreviewController(win, tab) {
   XPCOMUtils.defineLazyGetter(this, "canvasPreview", function() {
     let canvas = PageThumbs.createCanvas();
     canvas.mozOpaque = true;
     return canvas;
   });
 }
 
 PreviewController.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsITaskbarPreviewController,
-                                         Ci.nsIDOMEventListener]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsITaskbarPreviewController]),
 
   destroy() {
     this.tab.removeEventListener("TabAttrModified", this);
 
     // Break cycles, otherwise we end up leaking the window with everything
     // attached to it.
     delete this.win;
     delete this.preview;
@@ -328,17 +327,17 @@ PreviewController.prototype = {
   onActivate() {
     this.win.tabbrowser.selectedTab = this.tab;
 
     // Accept activation - this will restore the browser window
     // if it's minimized
     return true;
   },
 
-  // nsIDOMEventListener
+  // EventListener
   handleEvent(evt) {
     switch (evt.type) {
       case "TabAttrModified":
         this.updateTitleAndTooltip();
         break;
     }
   }
 };
@@ -491,17 +490,17 @@ TabWindow.prototype = {
     // on it the sorting order of our local array.  To do so we must walk
     // the local array backwards, otherwise we would send move requests in the
     // wrong order.  See bug 522610 for details.
     for (let i = inorder.length - 1; i >= 0; i--) {
       inorder[i].move(inorder[i + 1] || null);
     }
   },
 
-  // nsIDOMEventListener
+  // EventListener
   handleEvent(evt) {
     let tab = evt.originalTarget;
     switch (evt.type) {
       case "TabOpen":
         this.newTab(tab);
         this.updateTabOrdering();
         break;
       case "TabClose":
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 33.0
+Version 34.0
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-32...release-33
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-33...release-34
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.0
 - babel-preset-react @6.24.1
 - react @16.2.0
 - react-dom @16.2.0
 - webpack @3.11.0
--- a/devtools/client/debugger/new/debugger.css
+++ b/devtools/client/debugger/new/debugger.css
@@ -321,28 +321,16 @@ menuseparator {
   --object-color: var(--theme-body-color);
   --caption-color: var(--theme-highlight-blue);
   --location-color: var(--theme-content-color1);
   --source-link-color: var(--theme-highlight-blue);
   --node-color: var(--theme-highlight-bluegrey);
   --reference-color: var(--theme-highlight-purple);
 }
 
-.theme-firebug {
-  --number-color: #000088;
-  --string-color: #ff0000;
-  --null-color: #787878;
-  --object-color: DarkGreen;
-  --caption-color: #444444;
-  --location-color: #555555;
-  --source-link-color: blue;
-  --node-color: rgb(0, 0, 136);
-  --reference-color: rgb(102, 102, 255);
-}
-
 /******************************************************************************/
 
 .objectLink:hover {
   text-decoration: underline;
 }
 
 .inline {
   display: inline;
@@ -1073,32 +1061,24 @@ html[dir="rtl"] .tree-node img.arrow {
 .project-text-search .matches-summary {
   margin-left: 5px;
 }
 
 .project-text-search .result {
   font-family: Menlo, monospace;
 }
 
-.project-text-search .result.focused {
-  background-color: var(--search-overlays-semitransparent);
-}
-
 .project-text-search .result .query-match {
   background-color: var(--theme-selection-background);
   color: white;
   padding: 1px 4px;
   margin: 0 2px 0 2px;
   border-radius: 2px;
 }
 
-.project-text-search .result.focused .line-number {
-  font-weight: bolder;
-}
-
 .project-text-search .result .line-number {
   margin-right: 1em;
   width: 2em;
   margin-left: 0.8em;
 }
 
 .project-text-search .no-result-msg {
   color: var(--theme-body-color-inactive);
@@ -1116,20 +1096,16 @@ html[dir="rtl"] .tree-node img.arrow {
   padding: 2px 0 2px 5px;
   font-size: 12px;
 }
 
 .project-text-search .file-result .arrow {
   margin: 2px 0 2px 0;
 }
 
-.project-text-search .file-result.focused {
-  background-color: var(--search-overlays-semitransparent);
-}
-
 .project-text-search .line-match {
   display: "flex";
   flex-grow: 1;
 }
 
 .project-text-search .search-field {
   display: flex;
   align-self: stretch;
@@ -1143,16 +1119,32 @@ html[dir="rtl"] .tree-node img.arrow {
 .project-text-search .managed-tree {
   overflow-y: auto;
   height: 100%;
 }
 
 .project-text-search .managed-tree .tree {
   height: 100%;
 }
+
+/* Focus values */
+
+.project-text-search .file-result.focused,
+.project-text-search .result.focused {
+  background-color: var(--theme-selection-background);
+}
+
+.project-text-search .result.focused .line-number {
+  font-weight: bolder;
+}
+
+.project-text-search .result.focused .query-match {
+  background-color: #ffffff;
+  color: #000000;
+}
 /* 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/>. */
 
 .sources-panel {
   background-color: var(--theme-sidebar-background);
   display: flex;
   flex: 1;
@@ -2920,17 +2912,18 @@ html[dir="rtl"] .breakpoints-list .break
 
 .CodeMirror.cm-s-mozilla-breakpoint .CodeMirror-line span[role=presentation] {
   max-width: 100%;
   overflow: hidden;
   text-overflow: ellipsis;
   display: inline-block;
 }
 
-.CodeMirror.cm-s-mozilla-breakpoint .CodeMirror-code {
+.CodeMirror.cm-s-mozilla-breakpoint .CodeMirror-code,
+.CodeMirror.cm-s-mozilla-breakpoint .CodeMirror-scroll {
   cursor: default;
 }
 /* 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/>. */
 
 .expression-input-form {
   width: 100%;
@@ -3004,20 +2997,16 @@ html[dir="rtl"] .breakpoints-list .break
 :root.theme-light .expression-container:hover {
   background-color: var(--theme-selection-background-hover);
 }
 
 :root.theme-dark .expression-container:hover {
   background-color: var(--theme-selection-background-hover);
 }
 
-:root.theme-firebug .expression-container:hover {
-  background-color: var(--theme-selection-background-hover);
-}
-
 .tree  .tree-node:not(.focused):hover {
   background-color: transparent;
 }
 
 .expression-container__close-btn {
   position: absolute;
   offset-inline-end: 0px;
   top: 0px;
@@ -3153,18 +3142,17 @@ html[dir="rtl"] .breakpoints-list .break
   display: flex;
   justify-content: space-between;
   flex-direction: row;
   align-items: center;
   margin: 0;
   flex-shrink: 0;
 }
 
-.theme-light .frames .location,
-.theme-firebug .frames .location {
+.theme-light .frames .location {
   color: var(--theme-comment);
 }
 
 :root.theme-dark .frames .location {
   color: var(--theme-body-color);
   opacity: 0.6;
 }
 
@@ -3189,17 +3177,16 @@ html[dir="rtl"] .breakpoints-list .break
   color: white;
 }
 
 .frames ul li.selected i.annotation-logo svg path {
   fill: white;
 }
 
 :root.theme-light .frames ul li.selected .location,
-:root.theme-firebug .frames ul li.selected .location,
 :root.theme-dark .frames ul li.selected .location {
   color: white;
 }
 
 .show-more-container {
   display: flex;
 }
 
@@ -4062,17 +4049,16 @@ html .welcomebox .toggle-button-end.coll
 .theme-dark .result-list li.selected {
   background: var(--grey-70);
 }
 
 .result-list li .title {
   line-height: 1.5em;
   word-break: break-all;
   text-overflow: ellipsis;
-  overflow: hidden;
   white-space: nowrap;
 }
 
 .result-list li.selected .title {
   color: white;
 }
 
 .result-list.big li.selected {
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -5108,16 +5108,24 @@ function toggleFrameworkGrouping(toggleV
     });
   };
 }
 
 function showSource(sourceId) {
   return ({ dispatch, getState }) => {
     const source = (0, _selectors.getSource)(getState(), sourceId);
 
+    if ((0, _selectors.getPaneCollapse)(getState(), "start")) {
+      dispatch({
+        type: "TOGGLE_PANE",
+        position: "start",
+        paneCollapsed: false
+      });
+    }
+
     dispatch(setPrimaryPaneTab("sources"));
     dispatch({
       type: "SHOW_SOURCE",
       sourceUrl: ""
     });
 
     dispatch({
       type: "SHOW_SOURCE",
@@ -6428,20 +6436,17 @@ function evaluateExpression(expression) 
  */
 function getMappedExpression(expression) {
   return async function ({ dispatch, getState, client, sourceMaps }) {
     const mappings = (0, _selectors.getSelectedScopeMappings)(getState());
     if (!mappings) {
       return expression;
     }
 
-    return await dispatch({
-      type: "MAP_EXPRESSION_RESULT",
-      [_promise.PROMISE]: parser.mapOriginalExpression(expression, mappings)
-    });
+    return parser.mapOriginalExpression(expression, mappings);
   };
 }
 
 /***/ }),
 
 /***/ 1399:
 /***/ (function(module, exports, __webpack_require__) {
 
@@ -6611,24 +6616,41 @@ var _WasmParser = __webpack_require__(67
 var _WasmDis = __webpack_require__(678);
 
 /* 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/>. */
 
 var wasmStates = Object.create(null);
 
+function maybeWasmSectionNameResolver(data) {
+  try {
+    const parser = new _WasmParser.BinaryReader();
+    parser.setData(data.buffer, 0, data.length);
+    const reader = new _WasmDis.NameSectionReader();
+    reader.read(parser);
+    return reader.hasValidNames() ? reader.getNameResolver() : null;
+  } catch (ex) {
+    // Ignoring any errors during names section retrival.
+    return null;
+  }
+}
+
 /**
  * @memberof utils/wasm
  * @static
  */
 function getWasmText(sourceId, data) {
+  const nameResolver = maybeWasmSectionNameResolver(data);
   const parser = new _WasmParser.BinaryReader();
   parser.setData(data.buffer, 0, data.length);
   const dis = new _WasmDis.WasmDisassembler();
+  if (nameResolver) {
+    dis.nameResolver = nameResolver;
+  }
   dis.addOffsets = true;
   const done = dis.disassembleChunk(parser);
   let result = dis.getResult();
   if (result.lines.length === 0) {
     result = { lines: ["No luck with wast conversion"], offsets: [0], done };
   }
 
   const offsets = result.offsets;
@@ -7014,19 +7036,28 @@ class ManagedTree extends _react.Compone
   }
 
   highlightItem(highlightItems) {
     const { expanded } = this.state;
     // This file is visible, so we highlight it.
     if (expanded.has(this.props.getPath(highlightItems[0]))) {
       this.focusItem(highlightItems[0]);
     } else {
-      // Look at folders starting from the top-level until finds a
-      // closed folder and highlights this folder
-      const index = highlightItems.reverse().findIndex(item => !expanded.has(this.props.getPath(item)));
+      // Look at folders starting from the top-level and expand all the items
+      // which lie in the path of the item to be highlighted
+      highlightItems.reverse();
+      let index = highlightItems.findIndex(item => !expanded.has(this.props.getPath(item)));
+
+      if (this.props.autoExpandOnHighlight) {
+        while (index < highlightItems.length - 1) {
+          this.setExpanded(highlightItems[index], true, false);
+          index++;
+        }
+      }
+
       this.focusItem(highlightItems[index]);
     }
   }
 
   render() {
     const { expanded, focusedItem } = this.state;
     return _react2.default.createElement(
       "div",
@@ -7098,25 +7129,25 @@ function getURL(sourceUrl, debuggeeUrl =
     case "javascript:":
       // Ignore `javascript:` URLs for now
       return def;
 
     case "webpack:":
       // A Webpack source is a special case
       return (0, _lodash.merge)(def, {
         path: path,
-        group: "Webpack",
+        group: "webpack://",
         filename: filename
       });
 
     case "ng:":
       // An Angular source is a special case
       return (0, _lodash.merge)(def, {
         path: path,
-        group: "Angular",
+        group: "ng://",
         filename: filename
       });
 
     case "about:":
       // An about page is a special case
       return (0, _lodash.merge)(def, {
         path: "/",
         group: url,
@@ -9887,17 +9918,17 @@ var _treeOrder = __webpack_require__(154
 
 var _getURL = __webpack_require__(1405);
 
 function isUnderRoot(url, projectRoot) {
   if (!projectRoot) {
     return true;
   }
 
-  return `/${url.group}${url.path}`.startsWith(projectRoot);
+  return `${url.group}${url.path}`.startsWith(projectRoot);
 } /* This Source Code Form is subject to the terms of the Mozilla Public
    * License, v. 2.0. If a copy of the MPL was not distributed with this
    * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 function removeProjectRoot(parts, projectRoot) {
   const rootParts = projectRoot.replace("://", "").split("/");
   return parts.splice(0, rootParts.length - 2);
 }
@@ -9953,17 +9984,17 @@ function traverseTree(url, tree, debugge
   parts.unshift(url.group);
 
   if (projectRoot) {
     removeProjectRoot(parts, projectRoot);
   }
 
   let path = "";
   return parts.reduce((subTree, part, index) => {
-    path = `${path}/${part}`;
+    path = path ? `${path}/${part}` : part;
     const debuggeeHostIfRoot = index === 0 ? debuggeeHost : null;
     return findOrCreateNode(parts, subTree, path, part, index, url, debuggeeHostIfRoot);
   }, tree);
 }
 
 /*
  * Add a source file to a directory node in the tree
  */
@@ -12869,18 +12900,22 @@ Object.defineProperty(exports, "__esModu
 exports.isSelectedFrameVisible = isSelectedFrameVisible;
 
 var _devtoolsSourceMap = __webpack_require__(1360);
 
 var _pause = __webpack_require__(1394);
 
 var _sources = __webpack_require__(1369);
 
-function visibleSourceId(location) {
-  return (0, _devtoolsSourceMap.isOriginalId)(location.sourceId) ? (0, _devtoolsSourceMap.originalToGeneratedId)(location.sourceId) : location.sourceId;
+function getGeneratedId(sourceId) {
+  if ((0, _devtoolsSourceMap.isOriginalId)(sourceId)) {
+    return (0, _devtoolsSourceMap.originalToGeneratedId)(sourceId);
+  }
+
+  return sourceId;
 }
 
 /*
  * Checks to if the selected frame's source is currently
  * selected.
  */
 /* 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
@@ -12889,17 +12924,21 @@ function visibleSourceId(location) {
 function isSelectedFrameVisible(state) {
   const selectedLocation = (0, _sources.getSelectedLocation)(state);
   const selectedFrame = (0, _pause.getSelectedFrame)(state);
 
   if (!selectedFrame || !selectedLocation) {
     return false;
   }
 
-  return visibleSourceId(selectedLocation) === visibleSourceId(selectedFrame.location);
+  if ((0, _devtoolsSourceMap.isOriginalId)(selectedLocation.sourceId)) {
+    return selectedLocation.sourceId === selectedFrame.location.sourceId;
+  }
+
+  return selectedLocation.sourceId === getGeneratedId(selectedFrame.location.sourceId);
 }
 
 /***/ }),
 
 /***/ 1506:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
@@ -16154,17 +16193,17 @@ function findSource(sourceTree, sourceUr
     return sourceTree;
   }
 
   return returnTarget;
 }
 
 function getDirectories(sourceUrl, sourceTree) {
   const url = (0, _getURL.getURL)(sourceUrl);
-  const fullUrl = `/${url.group}${url.path}`;
+  const fullUrl = `${url.group}${url.path}`;
   const parentMap = (0, _utils.createParentMap)(sourceTree);
   const source = findSource(sourceTree, fullUrl);
   if (!source) {
     return [];
   }
 
   let node = source;
   const directories = [];
@@ -16732,16 +16771,25 @@ class SourcesTree extends _react.Compone
         debuggeeUrl,
         projectRoot,
         uncollapsedTree,
         sourceTree
       }));
     }
   }
 
+  renderItemName(name) {
+    const hosts = {
+      "ng://": "Angular",
+      "webpack://": "Webpack"
+    };
+
+    return hosts[name] || name;
+  }
+
   renderEmptyElement(message) {
     return _react2.default.createElement(
       "div",
       { className: "no-sources-message" },
       message
     );
   }
 
@@ -16798,16 +16846,17 @@ class SourcesTree extends _react.Compone
 
     if (isEmpty && !isCustomRoot) {
       return this.renderEmptyElement(L10N.getStr("sources.noSourcesAvailable"));
     }
 
     const treeProps = {
       autoExpandAll: false,
       autoExpandDepth: expanded ? 0 : 1,
+      autoExpandOnHighlight: true,
       expanded,
       getChildren: item => (0, _sourcesTree.nodeHasChildren)(item) ? item.contents : [],
       getParent: item => parentMap.get(item),
       getPath: this.getPath,
       getRoots: roots,
       highlightItems,
       itemHeight: 21,
       key: isEmpty ? "empty" : "full",
@@ -16869,17 +16918,17 @@ var _initialiseProps = function () {
     }
 
     return `${item.path}/${item.name}/${blackBoxedPart}`;
   };
 
   this.getIcon = (sources, item, depth) => {
     const { debuggeeUrl, projectRoot } = this.props;
 
-    if (item.path === "/Webpack") {
+    if (item.path === "webpack://") {
       return _react2.default.createElement(_Svg2.default, { name: "webpack" });
     }
     if (item.path === "/Angular") {
       return _react2.default.createElement(_Svg2.default, { name: "angular" });
     }
 
     if (depth === 0 && projectRoot === "") {
       return _react2.default.createElement("img", {
@@ -16981,17 +17030,17 @@ var _initialiseProps = function () {
         onContextMenu: e => this.onContextMenu(e, item)
       },
       arrow,
       icon,
       _react2.default.createElement(
         "span",
         { className: "label" },
         " ",
-        item.name,
+        this.renderItemName(item.name),
         " "
       )
     );
   };
 };
 
 const mapStateToProps = state => {
   return {
@@ -18290,19 +18339,17 @@ var _editor = __webpack_require__(1358);
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 class Preview extends _react.PureComponent {
   constructor(props) {
     super(props);
 
     this.onMouseOver = e => {
       const { target } = e;
-      if (this.props.selectedFrameVisible) {
-        this.props.updatePreview(target, this.props.editor);
-      }
+      this.props.updatePreview(target, this.props.editor);
     };
 
     this.onMouseUp = () => {
       this.setState({ selecting: false });
       return true;
     };
 
     this.onMouseDown = () => {
@@ -18379,18 +18426,17 @@ const {
   addExpression,
   setPopupObjectProperties,
   updatePreview,
   clearPreview
 } = _actions2.default;
 
 exports.default = (0, _reactRedux.connect)(state => ({
   preview: (0, _selectors.getPreview)(state),
-  selectedSource: (0, _selectors.getSelectedSource)(state),
-  selectedFrameVisible: (0, _selectors.isSelectedFrameVisible)(state)
+  selectedSource: (0, _selectors.getSelectedSource)(state)
 }), {
   addExpression,
   setPopupObjectProperties,
   updatePreview,
   clearPreview
 })(Preview);
 
 /***/ }),
@@ -30457,17 +30503,17 @@ function updatePreview(target, editor) {
         dispatch(clearPreview());
       }
     }
 
     if (isInvalidTarget(target)) {
       return;
     }
 
-    if (!(0, _selectors.isLineInScope)(getState(), tokenPos.line)) {
+    if (!(0, _selectors.isSelectedFrameVisible)(getState()) || !(0, _selectors.isLineInScope)(getState(), tokenPos.line)) {
       return;
     }
 
     const source = (0, _selectors.getSelectedSource)(getState());
     const symbols = (0, _selectors.getSymbols)(getState(), source);
 
     let match;
     if (!symbols || symbols.loading) {
@@ -38772,17 +38818,16 @@ function formatSource(source, root) {
 }
 
 /*
  * Gets the sources that are below a project root
  */
 function getRelativeSources(state) {
   const sources = (0, _selectors.getSources)(state);
   const root = (0, _selectors.getProjectDirectoryRoot)(state);
-
   return sources.valueSeq().toJS().filter(({ url }) => url && url.includes(root)).map(source => formatSource(source, root));
 }
 
 /***/ }),
 
 /***/ 3626:
 /***/ (function(module, exports) {
 
@@ -38981,16 +39026,17 @@ class BreakpointItem extends _react.Comp
 
     const node = _reactDom2.default.findDOMNode(this);
     if (node instanceof HTMLElement) {
       const mountNode = node.querySelector(".breakpoint-label");
       if (node instanceof HTMLElement) {
         // $FlowIgnore
         mountNode.innerHTML = "";
         this.editor.appendToLocalElement(mountNode);
+        this.editor.codeMirror.on("mousedown", (_, e) => e.preventDefault());
       }
     }
   }
 
   render() {
     const {
       breakpoint,
       onClick,
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-asm.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-asm.js
@@ -4,20 +4,16 @@ add_task(async function() {
 
   // After reload() we are getting getSources notifiction for old sources,
   // using the debugger statement to really stop are reloaded page.
   await waitForPaused(dbg);
   await resume(dbg);
 
   await waitForSources(dbg, "doc-asm.html", "asm.js");
 
-  // Expand nodes and make sure more sources appear.
-  is(findAllElements(dbg, "sourceNodes").length, 2);
-
-  await clickElement(dbg, "sourceDirectoryLabel", 2);
   is(findAllElements(dbg, "sourceNodes").length, 4);
 
   await selectSource(dbg, "asm.js");
 
   await addBreakpoint(dbg, "asm.js", 7);
   invokeInTab("runAsm");
 
   await waitForPaused(dbg);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-preview.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-preview.js
@@ -1,11 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+// Tests for preview through Babel's compile output.
+requestLongerTimeout(3);
+
 function getCoordsFromPosition(cm, { line, ch }) {
   return cm.charCoords({ line: ~~line, ch: ~~ch });
 }
 
 async function assertPreviews(dbg, previews) {
   for (const { line, column, expression, result, fields } of previews) {
     hoverAtPos(dbg, { line, ch: column });
 
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-scopes.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-scopes.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // This test can be really slow on debug platforms and should be split.
-requestLongerTimeout(4);
+requestLongerTimeout(6);
 
 // Tests loading sourcemapped sources for Babel's compile output.
 
 async function breakpointScopes(dbg, fixture, { line, column }, scopes) {
   const { selectors: { getBreakpoint, getBreakpoints }, getState } = dbg;
 
   const filename = `fixtures/${fixture}/input.js`;
   await waitForSources(dbg, filename);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-source-maps.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-source-maps.js
@@ -1,29 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-function getCoordsFromPosition(cm, { line, ch }) {
-  return cm.charCoords({ line: ~~line, ch: ~~ch });
-}
 
-function hoverAtPos(dbg, { line, ch }) {
-  info(`Hovering at (${line}, ${ch})\n`);
-  const cm = getCM(dbg);
-  const coords = getCoordsFromPosition(cm, { line: line - 1, ch });
-  const tokenEl = dbg.win.document.elementFromPoint(coords.left, coords.top);
-  const previewed = waitForDispatch(dbg, "SET_PREVIEW");
-  tokenEl.dispatchEvent(
-    new MouseEvent("mouseover", {
-      bubbles: true,
-      cancelable: true,
-      view: dbg.win
-    })
-  );
-  return previewed;
+async function assertNoTooltip(dbg) {
+  await waitForTime(200);
+  const el = findElement(dbg, "tooltip");
+  is(el, null, "Tooltip should not exist")
 }
 
 function assertPreviewTooltip(dbg, { result, expression }) {
   const previewEl = findElement(dbg, "tooltip");
   is(previewEl.innerText, result, "Preview text shown to user");
 
   const preview = dbg.selectors.getPreview(dbg.getState());
   is(`${preview.result}`, result, "Preview.result");
@@ -53,17 +40,26 @@ add_task(async function() {
   await waitForSources(dbg, "entry.js", "output.js", "times2.js", "opts.js");
   await selectSource(dbg, "times2");
   await addBreakpoint(dbg, "times2", 2);
 
   invokeInTab("keepMeAlive");
   await waitForPaused(dbg);
   await waitForSelectedSource(dbg, "times2");
 
-  await hoverAtPos(dbg, { line: 2, ch: 9 });
-  assertPreviewTooltip(dbg, { result: 4, expression: "x" });
+  info(`Test previewing in the original location`)
+  await assertPreviews(dbg, [
+    { line: 2, column: 10, result: 4, expression: "x" }
+  ]);
 
+  info(`Test previewing in the generated location`)
   await dbg.actions.jumpToMappedSelectedLocation();
   await waitForSelectedSource(dbg, "bundle.js");
+  await assertPreviews(dbg, [
+    { line: 70, column: 11, result: 4, expression: "x" }
+  ]);
 
-  await hoverAtPos(dbg, { line: 70, ch: 10 });
-  assertPreviewTooltip(dbg, { result: 4, expression: "x" });
+
+  info(`Test that you can not preview in another original file`);
+  await selectSource(dbg, "output");
+  await hoverAtPos(dbg, { line: 2, ch: 16 });
+  await assertNoTooltip(dbg)
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sources.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sources.js
@@ -19,16 +19,33 @@ async function assertSourceCount(dbg, co
 function getLabel(dbg, index) {
   return findElement(dbg, "sourceNode", index)
     .textContent.trim()
     .replace(/^[\s\u200b]*/g, "");
 }
 
 add_task(async function() {
   const dbg = await initDebugger("doc-sources.html");
+  const { selectors: { getSelectedSource, getExpandedState }, getState } = dbg;
+
+  await waitForSources(dbg, "nested-source");
+  await selectSource(dbg, "nested-source");
+
+  const expanded = getExpandedState(getState());
+
+  ok(
+    expanded.has(
+      `example.com/browser/devtools/client/debugger/new/test/mochitest/examples/nested/nested/`
+    ),
+    "Nodes in path are automatically expanded"
+  );
+});
+
+add_task(async function() {
+  const dbg = await initDebugger("doc-sources.html");
   const { selectors: { getSelectedSource }, getState } = dbg;
 
   await waitForSources(dbg, "simple1", "simple2", "nested-source", "long.js");
 
   // Expand nodes and make sure more sources appear.
   await assertSourceCount(dbg, 2);
   await clickElement(dbg, "sourceDirectoryLabel", 2);
 
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -11,16 +11,17 @@ support-files =
   doc_content_stylesheet_script.css
   doc_copystyles.css
   doc_copystyles.html
   doc_cssom.html
   doc_custom.html
   doc_edit_imported_selector.html
   doc_filter.html
   doc_frame_script.js
+  doc_grid_names.html
   doc_inline_sourcemap.html
   doc_invalid_sourcemap.css
   doc_invalid_sourcemap.html
   doc_keyframeanimation.css
   doc_keyframeanimation.html
   doc_keyframeLineNumbers.html
   doc_media_queries.html
   doc_pseudoelement.html
@@ -171,16 +172,17 @@ skip-if = (os == "win" && debug) # bug 9
 [browser_rules_grid-highlighter-on-navigate.js]
 [browser_rules_grid-highlighter-on-reload.js]
 [browser_rules_grid-highlighter-restored-after-reload.js]
 [browser_rules_grid-toggle_01.js]
 [browser_rules_grid-toggle_01b.js]
 [browser_rules_grid-toggle_02.js]
 [browser_rules_grid-toggle_03.js]
 [browser_rules_grid-toggle_04.js]
+[browser_rules_gridline-names-autocomplete.js]
 [browser_rules_guessIndentation.js]
 [browser_rules_highlight-used-fonts.js]
 [browser_rules_inherited-properties_01.js]
 [browser_rules_inherited-properties_02.js]
 [browser_rules_inherited-properties_03.js]
 [browser_rules_inherited-properties_04.js]
 [browser_rules_inline-source-map.js]
 [browser_rules_invalid.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_gridline-names-autocomplete.js
@@ -0,0 +1,169 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that CSS property values are autocompleted and cycled
+// correctly when editing an existing property in the rule view.
+
+// format :
+//  [
+//    what key to press,
+//    modifers,
+//    expected input box value after keypress,
+//    is the popup open,
+//    is a suggestion selected in the popup,
+//    expect ruleview-changed,
+//  ]
+
+const OPEN = true, SELECTED = true, CHANGE = true;
+const changeTestData = [
+  ["c", {}, "col1-start", OPEN, SELECTED, CHANGE],
+  ["o", {}, "col1-start", OPEN, SELECTED, CHANGE],
+  ["l", {}, "col1-start", OPEN, SELECTED, CHANGE],
+  ["VK_DOWN", {}, "col2-start", OPEN, SELECTED, CHANGE],
+  ["VK_RIGHT", {}, "col2-start", !OPEN, !SELECTED, !CHANGE],
+];
+
+// Creates a new CSS property value.
+// Checks that grid-area autocompletes column and row names.
+const newAreaTestData = [
+  ["g", {}, "grid", OPEN, SELECTED, !CHANGE],
+  ["VK_DOWN", {}, "grid-area", OPEN, SELECTED, !CHANGE],
+  ["VK_TAB", {}, "", !OPEN, !SELECTED, !CHANGE],
+  "grid-line-names-updated",
+  ["c", {}, "col1-start", OPEN, SELECTED, CHANGE],
+  ["VK_BACK_SPACE", {}, "c", !OPEN, !SELECTED, CHANGE],
+  ["VK_BACK_SPACE", {}, "", !OPEN, !SELECTED, CHANGE],
+  ["r", {}, "row1-start", OPEN, SELECTED, CHANGE],
+  ["r", {}, "rr", !OPEN, !SELECTED, CHANGE],
+  ["VK_BACK_SPACE", {}, "r", !OPEN, !SELECTED, CHANGE],
+  ["o", {}, "row1-start", OPEN, SELECTED, CHANGE],
+  ["VK_RETURN", {}, "", !OPEN, !SELECTED, CHANGE],
+];
+
+// Creates a new CSS property value.
+// Checks that grid-row only autocompletes row names.
+const newRowTestData = [
+  ["g", {}, "grid", OPEN, SELECTED, !CHANGE],
+  ["r", {}, "grid", OPEN, SELECTED, !CHANGE],
+  ["i", {}, "grid", OPEN, SELECTED, !CHANGE],
+  ["d", {}, "grid", OPEN, SELECTED, !CHANGE],
+  ["-", {}, "grid-area", OPEN, SELECTED, !CHANGE],
+  ["r", {}, "grid-row", OPEN, SELECTED, !CHANGE],
+  ["VK_RETURN", {}, "", !OPEN, !SELECTED, !CHANGE],
+  "grid-line-names-updated",
+  ["c", {}, "c", !OPEN, !SELECTED, CHANGE],
+  ["VK_BACK_SPACE", {}, "", !OPEN, !SELECTED, CHANGE],
+  ["r", {}, "row1-start", OPEN, SELECTED, CHANGE],
+  ["VK_TAB", {}, "", !OPEN, !SELECTED, CHANGE],
+];
+
+const TEST_URL = URL_ROOT + "doc_grid_names.html";
+
+add_task(async function() {
+  await addTab(TEST_URL);
+  let {toolbox, inspector, view} = await openRuleView();
+
+  info("Test autocompletion changing a preexisting property");
+  await runChangePropertyAutocompletionTest(toolbox, inspector, view, changeTestData);
+
+  info("Test autocompletion creating a new property");
+  await runNewPropertyAutocompletionTest(toolbox, inspector, view, newAreaTestData);
+
+  info("Test autocompletion creating a new property");
+  await runNewPropertyAutocompletionTest(toolbox, inspector, view, newRowTestData);
+});
+
+async function runNewPropertyAutocompletionTest(toolbox, inspector, view, testData) {
+  info("Selecting the test node");
+  await selectNode("#cell2", inspector);
+
+  info("Focusing the css property editable field");
+  let ruleEditor = getRuleViewRuleEditor(view, 0);
+  let editor = await focusNewRuleViewProperty(ruleEditor);
+  let gridLineNamesUpdated = inspector.once("grid-line-names-updated");
+
+  info("Starting to test for css property completion");
+  for (let data of testData) {
+    if (data == "grid-line-names-updated") {
+      await gridLineNamesUpdated;
+      continue;
+    }
+    await testCompletion(data, editor, view);
+  }
+}
+
+async function runChangePropertyAutocompletionTest(toolbox, inspector, view, testData) {
+  info("Selecting the test node");
+  await selectNode("#cell3", inspector);
+
+  let ruleEditor = getRuleViewRuleEditor(view, 1).rule;
+  let prop = ruleEditor.textProps[0];
+
+  info("Focusing the css property editable value");
+  let gridLineNamesUpdated = inspector.once("grid-line-names-updated");
+  let editor = await focusEditableField(view, prop.editor.valueSpan);
+  await gridLineNamesUpdated;
+
+  info("Starting to test for css property completion");
+  for (let data of testData) {
+    // Re-define the editor at each iteration, because the focus may have moved
+    // from property to value and back
+    editor = inplaceEditor(view.styleDocument.activeElement);
+    await testCompletion(data, editor, view);
+  }
+}
+
+async function testCompletion([key, modifiers, completion, open, selected, change],
+                         editor, view) {
+  info("Pressing key " + key);
+  info("Expecting " + completion);
+  info("Is popup opened: " + open);
+  info("Is item selected: " + selected);
+
+  let onDone;
+  if (change) {
+    // If the key triggers a ruleview-changed, wait for that event, it will
+    // always be the last to be triggered and tells us when the preview has
+    // been done.
+    onDone = view.once("ruleview-changed");
+  } else {
+    // Otherwise, expect an after-suggest event (except if the popup gets
+    // closed).
+    onDone = key !== "VK_RIGHT" && key !== "VK_BACK_SPACE"
+             ? editor.once("after-suggest")
+             : null;
+  }
+
+  // Also listening for popup opened/closed events if needed.
+  let popupEvent = open ? "popup-opened" : "popup-closed";
+  let onPopupEvent = editor.popup.isOpen !== open ? once(editor.popup, popupEvent) : null;
+
+  info("Synthesizing key " + key + ", modifiers: " + Object.keys(modifiers));
+
+  EventUtils.synthesizeKey(key, modifiers, view.styleWindow);
+
+  // Flush the debounce for the preview text.
+  view.debounce.flush();
+
+  await onDone;
+  await onPopupEvent;
+
+  // The key might have been a TAB or shift-TAB, in which case the editor will
+  // be a new one
+  editor = inplaceEditor(view.styleDocument.activeElement);
+
+  info("Checking the state");
+  if (completion !== null) {
+    is(editor.input.value, completion, "Correct value is autocompleted");
+  }
+
+  if (!open) {
+    ok(!(editor.popup && editor.popup.isOpen), "Popup is closed");
+  } else {
+    ok(editor.popup.isOpen, "Popup is open");
+    is(editor.popup.selectedIndex !== -1, selected, "An item is selected");
+  }
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/doc_grid_names.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<style type='text/css'>
+  #grid {
+    display: grid;
+    grid-template-rows: [row1-start] auto [row2-start] auto [row2-end];
+    grid-template-columns: [col1-start] 100px [col2-start] 100px [col3-start] 100px [col3-end];
+  }
+  #cell3 {
+    grid-column: "col3-start";
+    grid-row: "row2-start";
+  }
+</style>
+<div id="grid">
+  <div>cell1</div>
+  <div id="cell2">cell2</div>
+  <div id="cell3">cell3</div>
+</div>
\ No newline at end of file
--- a/devtools/client/inspector/rules/views/text-property-editor.js
+++ b/devtools/client/inspector/rules/views/text-property-editor.js
@@ -79,27 +79,28 @@ function TextPropertyEditor(ruleEditor, 
   this.browserWindow = this.doc.defaultView.top;
   this._populatedComputed = false;
   this._hasPendingClick = false;
   this._clickedElementOptions = null;
 
   const toolbox = this.ruleView.inspector.toolbox;
   this.cssProperties = getCssProperties(toolbox);
 
+  this.getGridlineNames = this.getGridlineNames.bind(this);
+  this.update = this.update.bind(this);
+  this.updatePropertyState = this.updatePropertyState.bind(this);
   this._onEnableClicked = this._onEnableClicked.bind(this);
   this._onExpandClicked = this._onExpandClicked.bind(this);
+  this._onNameDone = this._onNameDone.bind(this);
   this._onStartEditing = this._onStartEditing.bind(this);
-  this._onNameDone = this._onNameDone.bind(this);
-  this._onValueDone = this._onValueDone.bind(this);
   this._onSwatchCommit = this._onSwatchCommit.bind(this);
   this._onSwatchPreview = this._onSwatchPreview.bind(this);
   this._onSwatchRevert = this._onSwatchRevert.bind(this);
   this._onValidate = this.ruleView.debounce(this._previewValue, 10, this);
-  this.update = this.update.bind(this);
-  this.updatePropertyState = this.updatePropertyState.bind(this);
+  this._onValueDone = this._onValueDone.bind(this);
 
   this._create();
   this.update();
 }
 
 TextPropertyEditor.prototype = {
   /**
    * Boolean indicating if the name or value is being currently edited.
@@ -311,21 +312,53 @@ TextPropertyEditor.prototype = {
         contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
         property: this.prop,
         defaultIncrement: this.prop.name === "opacity" ? 0.1 : 1,
         popup: this.popup,
         multiline: true,
         maxWidth: () => this.container.getBoundingClientRect().width,
         cssProperties: this.cssProperties,
         cssVariables: this.rule.elementStyle.variables,
+        getGridLineNames: this.getGridlineNames,
       });
     }
   },
 
   /**
+   * Get the grid line names of the grid that the currently selected element is
+   * contained in.
+   *
+   * @return {Object} Contains the names of the cols and rows as arrays
+   * {cols: [], rows: []}.
+   */
+  getGridlineNames: async function() {
+    let gridLineNames = {cols: [], rows: []};
+    let layoutInspector = await this.ruleView.inspector.walker.getLayoutInspector();
+    let gridFront = await layoutInspector.getCurrentGrid(
+      this.ruleView.inspector.selection.nodeFront);
+
+    if (gridFront) {
+      let gridFragments = gridFront.gridFragments;
+
+      for (let gridFragment of gridFragments) {
+        for (let rowLine of gridFragment.rows.lines) {
+          gridLineNames.rows = gridLineNames.rows.concat(rowLine.names);
+        }
+        for (let colLine of gridFragment.cols.lines) {
+          gridLineNames.cols = gridLineNames.cols.concat(colLine.names);
+        }
+      }
+    }
+
+    // Emit message for test files
+    this.ruleView.inspector.emit("grid-line-names-updated");
+    return gridLineNames;
+  },
+
+  /**
    * Get the path from which to resolve requests for this
    * rule's stylesheet.
    *
    * @return {String} the stylesheet's href.
    */
   get sheetHref() {
     let domRule = this.rule.domRule;
     if (domRule) {
--- a/devtools/client/locales/en-US/netmonitor.properties
+++ b/devtools/client/locales/en-US/netmonitor.properties
@@ -53,20 +53,16 @@ netmonitor.security.hostHeader=Host %S:
 # defined:
 #   Organization: <Not Available>
 netmonitor.security.notAvailable=<Not Available>
 
 # LOCALIZATION NOTE (collapseDetailsPane): This is the tooltip for the button
 # that collapses the network details pane in the UI.
 collapseDetailsPane=Hide request details
 
-# LOCALIZATION NOTE (expandDetailsPane): This is the tooltip for the button
-# that expands the network details pane in the UI.
-expandDetailsPane=Show request details
-
 # LOCALIZATION NOTE (headersEmptyText): This is the text displayed in the
 # headers tab of the network details pane when there are no headers available.
 headersEmptyText=No headers for this request
 
 # LOCALIZATION NOTE (headersFilterText): This is the text displayed in the
 # headers tab of the network details pane for the filtering input.
 headersFilterText=Filter headers
 
--- a/devtools/client/netmonitor/src/assets/styles/Toolbar.css
+++ b/devtools/client/netmonitor/src/assets/styles/Toolbar.css
@@ -40,32 +40,8 @@
   bottom: 1px;
 }
 
 .devtools-checkbox-label {
   margin-inline-start: 10px;
   margin-inline-end: 3px;
   white-space: nowrap;
 }
-
-/* Network details panel toggle */
-
-.network-details-panel-toggle:dir(ltr)::before,
-.network-details-panel-toggle.pane-collapsed:dir(rtl)::before {
-  background-image: var(--theme-pane-collapse-image);
-}
-
-.network-details-panel-toggle.pane-collapsed:dir(ltr)::before,
-.network-details-panel-toggle:dir(rtl)::before {
-  background-image: var(--theme-pane-expand-image);
-}
-
-/* Responsive web design support */
-
-@media (max-width: 700px) {
-  .network-details-panel-toggle:dir(ltr)::before {
-    transform: rotate(90deg);
-  }
-
-  .network-details-panel-toggle:dir(rtl)::before {
-    transform: rotate(-90deg);
-  }
-}
--- a/devtools/client/netmonitor/src/assets/styles/netmonitor.css
+++ b/devtools/client/netmonitor/src/assets/styles/netmonitor.css
@@ -1,12 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+@import "resource://devtools/client/shared/components/SidebarToggle.css";
 @import "resource://devtools/client/shared/components/splitter/SplitBox.css";
 @import "resource://devtools/client/shared/components/tree/TreeView.css";
 @import "resource://devtools/client/shared/components/tabs/Tabs.css";
 @import "resource://devtools/client/shared/components/tabs/TabBar.css";
 @import "chrome://devtools/skin/components-frame.css";
 @import "chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css";
 @import "chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css";
 @import "chrome://devtools/content/sourceeditor/codemirror/mozilla.css";
--- a/devtools/client/netmonitor/src/components/NetworkDetailsPanel.js
+++ b/devtools/client/netmonitor/src/components/NetworkDetailsPanel.js
@@ -26,16 +26,17 @@ const { div } = dom;
  */
 function NetworkDetailsPanel({
   connector,
   activeTabId,
   cloneSelectedRequest,
   request,
   selectTab,
   sourceMapService,
+  toggleNetworkDetails,
   openLink,
 }) {
   if (!request) {
     return null;
   }
 
   return (
     div({ className: "network-details-panel" },
@@ -43,16 +44,17 @@ function NetworkDetailsPanel({
         TabboxPanel({
           activeTabId,
           cloneSelectedRequest,
           connector,
           openLink,
           request,
           selectTab,
           sourceMapService,
+          toggleNetworkDetails,
         }) :
         CustomRequestPanel({
           connector,
           request,
         })
     )
   );
 }
@@ -62,21 +64,23 @@ NetworkDetailsPanel.displayName = "Netwo
 NetworkDetailsPanel.propTypes = {
   connector: PropTypes.object.isRequired,
   activeTabId: PropTypes.string,
   cloneSelectedRequest: PropTypes.func.isRequired,
   open: PropTypes.bool,
   request: PropTypes.object,
   selectTab: PropTypes.func.isRequired,
   sourceMapService: PropTypes.object,
+  toggleNetworkDetails: PropTypes.func.isRequired,
   openLink: PropTypes.func,
 };
 
 module.exports = connect(
   (state) => ({
     activeTabId: state.ui.detailsPanelSelectedTab,
     request: getSelectedRequest(state),
   }),
   (dispatch) => ({
     cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
     selectTab: (tabId) => dispatch(Actions.selectDetailsPanelTab(tabId)),
+    toggleNetworkDetails: () => dispatch(Actions.toggleNetworkDetails()),
   }),
 )(NetworkDetailsPanel);
--- a/devtools/client/netmonitor/src/components/TabboxPanel.js
+++ b/devtools/client/netmonitor/src/components/TabboxPanel.js
@@ -15,16 +15,17 @@ const TabPanel = createFactory(require("
 const CookiesPanel = createFactory(require("./CookiesPanel"));
 const HeadersPanel = createFactory(require("./HeadersPanel"));
 const ParamsPanel = createFactory(require("./ParamsPanel"));
 const ResponsePanel = createFactory(require("./ResponsePanel"));
 const SecurityPanel = createFactory(require("./SecurityPanel"));
 const StackTracePanel = createFactory(require("./StackTracePanel"));
 const TimingsPanel = createFactory(require("./TimingsPanel"));
 
+const COLLAPSE_DETAILS_PANE = L10N.getStr("collapseDetailsPane");
 const COOKIES_TITLE = L10N.getStr("netmonitor.tab.cookies");
 const HEADERS_TITLE = L10N.getStr("netmonitor.tab.headers");
 const PARAMS_TITLE = L10N.getStr("netmonitor.tab.params");
 const RESPONSE_TITLE = L10N.getStr("netmonitor.tab.response");
 const SECURITY_TITLE = L10N.getStr("netmonitor.tab.security");
 const STACK_TRACE_TITLE = L10N.getStr("netmonitor.tab.stackTrace");
 const TIMINGS_TITLE = L10N.getStr("netmonitor.tab.timings");
 
@@ -35,28 +36,35 @@ const TIMINGS_TITLE = L10N.getStr("netmo
 function TabboxPanel({
   activeTabId,
   cloneSelectedRequest = () => {},
   connector,
   openLink,
   request,
   selectTab,
   sourceMapService,
+  toggleNetworkDetails,
 }) {
   if (!request) {
     return null;
   }
 
   return (
     Tabbar({
       activeTabId,
       menuDocument: window.parent.document,
       onSelect: selectTab,
       renderOnlySelected: true,
       showAllTabsMenu: true,
+      sidebarToggleButton: {
+        collapsed: false,
+        collapsePaneTitle: COLLAPSE_DETAILS_PANE,
+        expandPaneTitle: "",
+        onClick: toggleNetworkDetails,
+      },
     },
       TabPanel({
         id: PANELS.HEADERS,
         title: HEADERS_TITLE,
       },
         HeadersPanel({
           cloneSelectedRequest,
           connector,
--- a/devtools/client/netmonitor/src/components/Toolbar.js
+++ b/devtools/client/netmonitor/src/components/Toolbar.js
@@ -10,30 +10,27 @@ const dom = require("devtools/client/sha
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { connect } = require("devtools/client/shared/redux/visibility-handler-connect");
 
 const Actions = require("../actions/index");
 const { FILTER_SEARCH_DELAY, FILTER_TAGS } = require("../constants");
 const {
   getRecordingState,
   getTypeFilteredRequests,
-  isNetworkDetailsToggleButtonDisabled,
 } = require("../selectors/index");
 const { autocompleteProvider } = require("../utils/filter-autocomplete-provider");
 const { L10N } = require("../utils/l10n");
 const { fetchNetworkUpdatePacket } = require("../utils/request-utils");
 
 // Components
 const SearchBox = createFactory(require("devtools/client/shared/components/SearchBox"));
 
 const { button, div, input, label, span } = dom;
 
 // Localization
-const COLLAPSE_DETAILS_PANE = L10N.getStr("collapseDetailsPane");
-const EXPAND_DETAILS_PANE = L10N.getStr("expandDetailsPane");
 const SEARCH_KEY_SHORTCUT = L10N.getStr("netmonitor.toolbar.filterFreetext.key");
 const SEARCH_PLACE_HOLDER = L10N.getStr("netmonitor.toolbar.filterFreetext.label");
 const TOOLBAR_CLEAR = L10N.getStr("netmonitor.toolbar.clear");
 const TOOLBAR_TOGGLE_RECORDING = L10N.getStr("netmonitor.toolbar.toggleRecording");
 
 // Preferences
 const DEVTOOLS_DISABLE_CACHE_PREF = "devtools.cache.disabled";
 const DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF = "devtools.netmonitor.persistlog";
@@ -56,19 +53,16 @@ class Toolbar extends Component {
   static get propTypes() {
     return {
       connector: PropTypes.object.isRequired,
       toggleRecording: PropTypes.func.isRequired,
       recording: PropTypes.bool.isRequired,
       clearRequests: PropTypes.func.isRequired,
       requestFilterTypes: PropTypes.object.isRequired,
       setRequestFilterText: PropTypes.func.isRequired,
-      networkDetailsToggleDisabled: PropTypes.bool.isRequired,
-      networkDetailsOpen: PropTypes.bool.isRequired,
-      toggleNetworkDetails: PropTypes.func.isRequired,
       enablePersistentLogs: PropTypes.func.isRequired,
       togglePersistentLogs: PropTypes.func.isRequired,
       persistentLogsEnabled: PropTypes.bool.isRequired,
       disableBrowserCache: PropTypes.func.isRequired,
       toggleBrowserCache: PropTypes.func.isRequired,
       browserCacheDisabled: PropTypes.bool.isRequired,
       toggleRequestFilterType: PropTypes.func.isRequired,
       filteredRequests: PropTypes.array.isRequired,
@@ -87,19 +81,17 @@ class Toolbar extends Component {
   componentDidMount() {
     Services.prefs.addObserver(DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF,
                                this.updatePersistentLogsEnabled);
     Services.prefs.addObserver(DEVTOOLS_DISABLE_CACHE_PREF,
                                this.updateBrowserCacheDisabled);
   }
 
   shouldComponentUpdate(nextProps) {
-    return this.props.networkDetailsOpen !== nextProps.networkDetailsOpen
-    || this.props.networkDetailsToggleDisabled !== nextProps.networkDetailsToggleDisabled
-    || this.props.persistentLogsEnabled !== nextProps.persistentLogsEnabled
+    return this.props.persistentLogsEnabled !== nextProps.persistentLogsEnabled
     || this.props.browserCacheDisabled !== nextProps.browserCacheDisabled
     || this.props.recording !== nextProps.recording
     || !Object.is(this.props.requestFilterTypes, nextProps.requestFilterTypes)
 
     // Filtered requests are useful only when searchbox is focused
     || !!(this.refs.searchbox && this.refs.searchbox.focused);
   }
 
@@ -144,19 +136,16 @@ class Toolbar extends Component {
   }
 
   render() {
     let {
       toggleRecording,
       clearRequests,
       requestFilterTypes,
       setRequestFilterText,
-      networkDetailsToggleDisabled,
-      networkDetailsOpen,
-      toggleNetworkDetails,
       togglePersistentLogs,
       persistentLogsEnabled,
       toggleBrowserCache,
       browserCacheDisabled,
       recording,
     } = this.props;
 
     // Render list of filter-buttons.
@@ -181,29 +170,16 @@ class Toolbar extends Component {
     // Calculate class-list for toggle recording button. The button
     // has two states: pause/play.
     let toggleRecordingButtonClass = [
       "devtools-button",
       "requests-list-pause-button",
       recording ? "devtools-pause-icon" : "devtools-play-icon",
     ].join(" ");
 
-    // Detail toggle button
-    let toggleDetailButtonClassList = [
-      "network-details-panel-toggle",
-      "devtools-button",
-    ];
-
-    if (!networkDetailsOpen) {
-      toggleDetailButtonClassList.push("pane-collapsed");
-    }
-    let toggleDetailButtonClass = toggleDetailButtonClassList.join(" ");
-    let toggleDetailButtonTitle = networkDetailsOpen ? COLLAPSE_DETAILS_PANE :
-      EXPAND_DETAILS_PANE;
-
     // Render the entire toolbar.
     return (
       span({ className: "devtools-toolbar devtools-toolbar-container" },
         span({ className: "devtools-toolbar-group" },
           button({
             className: toggleRecordingButtonClass,
             title: TOOLBAR_TOGGLE_RECORDING,
             onClick: toggleRecording,
@@ -248,44 +224,34 @@ class Toolbar extends Component {
             delay: FILTER_SEARCH_DELAY,
             keyShortcut: SEARCH_KEY_SHORTCUT,
             placeholder: SEARCH_PLACE_HOLDER,
             type: "filter",
             ref: "searchbox",
             onChange: setRequestFilterText,
             onFocus: this.onSearchBoxFocus,
             autocompleteProvider: this.autocompleteProvider,
-          }),
-          button({
-            className: toggleDetailButtonClass,
-            title: toggleDetailButtonTitle,
-            disabled: networkDetailsToggleDisabled,
-            tabIndex: "0",
-            onClick: toggleNetworkDetails,
-          }),
+          })
         )
       )
     );
   }
 }
 
 module.exports = connect(
   (state) => ({
     browserCacheDisabled: state.ui.browserCacheDisabled,
     filteredRequests: getTypeFilteredRequests(state),
-    networkDetailsToggleDisabled: isNetworkDetailsToggleButtonDisabled(state),
-    networkDetailsOpen: state.ui.networkDetailsOpen,
     persistentLogsEnabled: state.ui.persistentLogsEnabled,
     recording: getRecordingState(state),
     requestFilterTypes: state.filters.requestFilterTypes,
   }),
   (dispatch) => ({
     clearRequests: () => dispatch(Actions.clearRequests()),
     disableBrowserCache: (disabled) => dispatch(Actions.disableBrowserCache(disabled)),
     enablePersistentLogs: (enabled) => dispatch(Actions.enablePersistentLogs(enabled)),
     setRequestFilterText: (text) => dispatch(Actions.setRequestFilterText(text)),
     toggleBrowserCache: () => dispatch(Actions.toggleBrowserCache()),
-    toggleNetworkDetails: () => dispatch(Actions.toggleNetworkDetails()),
     toggleRecording: () => dispatch(Actions.toggleRecording()),
     togglePersistentLogs: () => dispatch(Actions.togglePersistentLogs()),
     toggleRequestFilterType: (type) => dispatch(Actions.toggleRequestFilterType(type)),
   }),
 )(Toolbar);
--- a/devtools/client/netmonitor/src/selectors/ui.js
+++ b/devtools/client/netmonitor/src/selectors/ui.js
@@ -1,24 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { createSelector } = require("devtools/client/shared/vendor/reselect");
 const { REQUESTS_WATERFALL } = require("../constants");
-const { getDisplayedRequests } = require("./requests");
 
 const EPSILON = 0.001;
 
-function isNetworkDetailsToggleButtonDisabled(state) {
-  return getDisplayedRequests(state).length == 0;
-}
-
 const getWaterfallScale = createSelector(
   state => state.requests,
   state => state.timingMarkers,
   state => state.ui,
   (requests, timingMarkers, ui) => {
     if (requests.firstStartedMillis === +Infinity || ui.waterfallWidth === null) {
       return null;
     }
@@ -31,11 +26,10 @@ const getWaterfallScale = createSelector
   // Reduce 20px for the last request's requests-list-timings-total
     return Math.min(Math.max(
       (ui.waterfallWidth - REQUESTS_WATERFALL.LABEL_WIDTH - 20) / longestWidth,
       EPSILON), 1);
   }
 );
 
 module.exports = {
-  isNetworkDetailsToggleButtonDisabled,
   getWaterfallScale,
 };
--- a/devtools/client/netmonitor/test/browser_net_brotli.js
+++ b/devtools/client/netmonitor/test/browser_net_brotli.js
@@ -44,18 +44,17 @@ add_task(async function() {
       fullMimeType: "text/plain",
       transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 60),
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 64),
       time: true
     });
 
   wait = waitForDOM(document, ".CodeMirror-code");
   let onResponseContent = monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   await wait;
   await onResponseContent;
   await testResponse("br");
   await teardown(monitor);
 
   function testResponse(type) {
--- a/devtools/client/netmonitor/test/browser_net_clear.js
+++ b/devtools/client/netmonitor/test/browser_net_clear.js
@@ -8,17 +8,16 @@
  */
 
 add_task(async function() {
   let { tab, monitor } = await initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
-  let detailsPanelToggleButton = document.querySelector(".network-details-panel-toggle");
   let clearButton = document.querySelector(".requests-list-clear-button");
 
   store.dispatch(Actions.batchEnable(false));
 
   // Make sure we start in a sane state
   assertNoRequestState();
 
   // Load one request and assert it shows up in the list
@@ -35,43 +34,39 @@ add_task(async function() {
   // Load a second request and make sure they still show up
   onMonitorUpdated = waitForAllRequestsFinished(monitor);
   tab.linkedBrowser.reload();
   await onMonitorUpdated;
 
   assertSingleRequestState();
 
   // Make sure we can now open the network details panel
-  EventUtils.sendMouseEvent({ type: "click" }, detailsPanelToggleButton);
-
-  ok(document.querySelector(".network-details-panel") &&
+  store.dispatch(Actions.toggleNetworkDetails());
+  let detailsPanelToggleButton = document.querySelector(".sidebar-toggle");
+  ok(detailsPanelToggleButton &&
     !detailsPanelToggleButton.classList.contains("pane-collapsed"),
-    "The details pane should be visible after clicking the toggle button.");
+    "The details pane should be visible.");
 
   // Click clear and make sure the details pane closes
   EventUtils.sendMouseEvent({ type: "click" }, clearButton);
 
   assertNoRequestState();
   ok(!document.querySelector(".network-details-panel"),
     "The details pane should not be visible clicking 'clear'.");
 
   return teardown(monitor);
 
   /**
    * Asserts the state of the network monitor when one request has loaded
    */
   function assertSingleRequestState() {
     is(store.getState().requests.requests.size, 1,
       "The request menu should have one item at this point.");
-    is(detailsPanelToggleButton.hasAttribute("disabled"), false,
-      "The pane toggle button should be enabled after a request is made.");
   }
 
   /**
    * Asserts the state of the network monitor when no requests have loaded
    */
   function assertNoRequestState() {
     is(store.getState().requests.requests.size, 0,
       "The request menu should be empty at this point.");
-    is(detailsPanelToggleButton.hasAttribute("disabled"), true,
-      "The pane toggle button should be disabled when the request menu is cleared.");
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_json-b64.js
+++ b/devtools/client/netmonitor/test/browser_net_json-b64.js
@@ -16,18 +16,17 @@ add_task(async function() {
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   // Execute requests.
   await performRequests(monitor, tab, 1);
 
   wait = waitForDOM(document, "#response-panel .CodeMirror-code");
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   await wait;
 
   let tabpanel = document.querySelector("#response-panel");
 
   is(tabpanel.querySelector(".response-error-header") === null, true,
     "The response error header doesn't have the intended visibility.");
--- a/devtools/client/netmonitor/test/browser_net_json-long.js
+++ b/devtools/client/netmonitor/test/browser_net_json-long.js
@@ -46,18 +46,17 @@ add_task(async function() {
       type: "json",
       fullMimeType: "text/json; charset=utf-8",
       size: L10N.getFormatStr("networkMenu.sizeKB",
         L10N.numberWithDecimals(85975 / 1024, 2)),
       time: true
     });
 
   wait = waitForDOM(document, "#response-panel .CodeMirror-code");
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   await wait;
 
   testResponseTab();
 
   await teardown(monitor);
 
--- a/devtools/client/netmonitor/test/browser_net_json-malformed.js
+++ b/devtools/client/netmonitor/test/browser_net_json-malformed.js
@@ -38,18 +38,17 @@ add_task(async function() {
     {
       status: 200,
       statusText: "OK",
       type: "json",
       fullMimeType: "text/json; charset=utf-8"
     });
 
   wait = waitForDOM(document, "#response-panel .CodeMirror-code");
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   await wait;
 
   let tabpanel = document.querySelector("#response-panel");
   is(tabpanel.querySelector(".response-error-header") === null, false,
     "The response error header doesn't have the intended visibility.");
   is(tabpanel.querySelector(".response-error-header").textContent,
--- a/devtools/client/netmonitor/test/browser_net_json-null.js
+++ b/devtools/client/netmonitor/test/browser_net_json-null.js
@@ -15,17 +15,22 @@ add_task(async function() {
   let { L10N } = windowRequire("devtools/client/netmonitor/src/utils/l10n");
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   // Execute requests.
   await performRequests(monitor, tab, 1);
 
-  await openResponsePanel();
+  let onReponsePanelReady = waitForDOM(document, "#response-panel .CodeMirror-code");
+  store.dispatch(Actions.toggleNetworkDetails());
+  EventUtils.sendMouseEvent({ type: "click" },
+    document.querySelector("#response-tab"));
+  await onReponsePanelReady;
+
   checkResponsePanelDisplaysJSON();
 
   let tabpanel = document.querySelector("#response-panel");
   is(tabpanel.querySelectorAll(".tree-section").length, 2,
     "There should be 2 tree sections displayed in this tabpanel.");
   is(tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length, 1,
     "There should be 1 json properties displayed in this tabpanel.");
   is(tabpanel.querySelectorAll(".empty-notice").length, 0,
@@ -52,22 +57,9 @@ add_task(async function() {
     let jsonView = panel.querySelector(".tree-section .treeLabel") || {};
     is(jsonView.textContent === L10N.getStr("jsonScopeName"), true,
       "The response json view has the intended visibility.");
     is(panel.querySelector(".CodeMirror-code") === null, false,
       "The response editor has the intended visibility.");
     is(panel.querySelector(".response-image-box") === null, true,
       "The response image box doesn't have the intended visibility.");
   }
-
-  /**
-   * Open the netmonitor details panel and switch to the response tab.
-   * Returns a promise that will resolve when the response panel DOM element is available.
-   */
-  function openResponsePanel() {
-    let onReponsePanelReady = waitForDOM(document, "#response-panel .CodeMirror-code");
-    EventUtils.sendMouseEvent({ type: "click" },
-      document.querySelector(".network-details-panel-toggle"));
-    EventUtils.sendMouseEvent({ type: "click" },
-      document.querySelector("#response-tab"));
-    return onReponsePanelReady;
-  }
 });
--- a/devtools/client/netmonitor/test/browser_net_json_custom_mime.js
+++ b/devtools/client/netmonitor/test/browser_net_json_custom_mime.js
@@ -40,18 +40,17 @@ add_task(async function() {
       statusText: "OK",
       type: "x-bigcorp-json",
       fullMimeType: "text/x-bigcorp-json; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41),
       time: true
     });
 
   wait = waitForDOM(document, "#response-panel .CodeMirror-code");
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   await wait;
 
   testResponseTab();
 
   await teardown(monitor);
 
--- a/devtools/client/netmonitor/test/browser_net_json_text_mime.js
+++ b/devtools/client/netmonitor/test/browser_net_json_text_mime.js
@@ -41,18 +41,17 @@ add_task(async function() {
       statusText: "OK",
       type: "plain",
       fullMimeType: "text/plain; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41),
       time: true
     });
 
   wait = waitForDOM(document, "#response-panel .CodeMirror-code");
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   await wait;
 
   testResponseTab();
 
   await teardown(monitor);
 
--- a/devtools/client/netmonitor/test/browser_net_jsonp.js
+++ b/devtools/client/netmonitor/test/browser_net_jsonp.js
@@ -59,18 +59,17 @@ add_task(async function() {
       type: "json",
       fullMimeType: "text/json; charset=utf-8",
       size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 54),
       time: true
     });
 
   info("Testing first request");
   wait = waitForDOM(document, "#response-panel .CodeMirror-code");
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   await wait;
 
   testResponseTab("$_0123Fun", "Hello JSONP!");
 
   info("Testing second request");
   wait = waitForDOM(document, "#response-panel .CodeMirror-code");
--- a/devtools/client/netmonitor/test/browser_net_large-response.js
+++ b/devtools/client/netmonitor/test/browser_net_large-response.js
@@ -45,18 +45,17 @@ add_task(async function() {
     "GET",
     CONTENT_TYPE_SJS + "?fmt=html-long",
     {
       status: 200,
       statusText: "OK"
     });
 
   wait = waitForDOM(document, "#response-panel .CodeMirror-code");
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   await wait;
 
   let text = document.querySelector(".CodeMirror-line").textContent;
 
   ok(text.match(/^<p>/), "The text shown in the source editor is incorrect.");
 
--- a/devtools/client/netmonitor/test/browser_net_pane-collapse.js
+++ b/devtools/client/netmonitor/test/browser_net_pane-collapse.js
@@ -6,46 +6,47 @@
 /**
  * Tests if the network monitor panes collapse properly.
  */
 
 add_task(async function() {
   let { tab, monitor } = await initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, windowRequire } = monitor.panelWin;
+  let { document, store, windowRequire } = monitor.panelWin;
+  let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { Prefs } = windowRequire("devtools/client/netmonitor/src/utils/prefs");
-  let detailsPaneToggleButton = document.querySelector(".network-details-panel-toggle");
 
   let wait = waitForNetworkEvents(monitor, 1);
   tab.linkedBrowser.reload();
   await wait;
 
   ok(!document.querySelector(".network-details-panel") &&
-     detailsPaneToggleButton.classList.contains("pane-collapsed"),
+     !document.querySelector(".sidebar-toggle"),
     "The details panel should initially be hidden.");
 
-  EventUtils.sendMouseEvent({ type: "click" }, detailsPaneToggleButton);
+  store.dispatch(Actions.toggleNetworkDetails());
 
   is(~~(document.querySelector(".network-details-panel").clientWidth),
     Prefs.networkDetailsWidth,
     "The details panel has an incorrect width.");
   ok(document.querySelector(".network-details-panel") &&
-     !detailsPaneToggleButton.classList.contains("pane-collapsed"),
+     document.querySelector(".sidebar-toggle"),
     "The details panel should at this point be visible.");
 
-  EventUtils.sendMouseEvent({ type: "click" }, detailsPaneToggleButton);
+  EventUtils.sendMouseEvent({ type: "click" },
+    document.querySelector(".sidebar-toggle"));
 
   ok(!document.querySelector(".network-details-panel") &&
-     detailsPaneToggleButton.classList.contains("pane-collapsed"),
+     !document.querySelector(".sidebar-toggle"),
     "The details panel should not be visible after collapsing.");
 
-  EventUtils.sendMouseEvent({ type: "click" }, detailsPaneToggleButton);
+  store.dispatch(Actions.toggleNetworkDetails());
 
   is(~~(document.querySelector(".network-details-panel").clientWidth),
     Prefs.networkDetailsWidth,
     "The details panel has an incorrect width after uncollapsing.");
   ok(document.querySelector(".network-details-panel") &&
-     !detailsPaneToggleButton.classList.contains("pane-collapsed"),
+     document.querySelector(".sidebar-toggle"),
     "The details panel should be visible again after uncollapsing.");
 
   await teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_pane-network-details.js
+++ b/devtools/client/netmonitor/test/browser_net_pane-network-details.js
@@ -40,88 +40,72 @@ add_task(async function() {
 
   info("Starting test... ");
 
   let wait = waitForNetworkEvents(monitor, 9);
   loadFrameScriptUtils();
   await performRequestsInContent(REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS);
   await wait;
 
-  let toggleButton = document.querySelector(".network-details-panel-toggle");
-
   info("Test with the first request in the list visible");
   EventUtils.sendMouseEvent({ type: "click" },
-      document.querySelector(".requests-list-filter-all-button"));
+    document.querySelector(".requests-list-filter-all-button"));
   testDetailsPanel(true, 0);
 
   info("Test with first request in the list not visible");
   EventUtils.sendMouseEvent({ type: "click" },
-      document.querySelector(".requests-list-filter-all-button"));
+    document.querySelector(".requests-list-filter-all-button"));
   EventUtils.sendMouseEvent({ type: "click" },
-      document.querySelector(".requests-list-filter-js-button"));
+    document.querySelector(".requests-list-filter-js-button"));
   testFilterButtons(monitor, "js");
   testDetailsPanel(true, 2);
 
   info("Test with no request in the list visible i.e. no request match the filters");
   EventUtils.sendMouseEvent({ type: "click" },
-      document.querySelector(".requests-list-filter-all-button"));
+    document.querySelector(".requests-list-filter-all-button"));
   setFreetextFilter("foobar");
   // The network details panel should not open as there are no available requests visible
   testDetailsPanel(false);
 
   await teardown(monitor);
 
   function getSelectedIndex(state) {
     if (!state.requests.selectedId) {
       return -1;
     }
     return getSortedRequests(state).findIndex(r => r.id === state.requests.selectedId);
   }
 
   async function testDetailsPanel(shouldPanelOpen, selectedItemIndex = 0) {
     // Expected default state should be panel closed
-    if (shouldPanelOpen) {
-      // Toggle switch should be enabled only when there are visible requests
-      is(toggleButton.hasAttribute("disabled"), false,
-          "The pane toggle button should be enabled.");
-    } else {
-      is(toggleButton.hasAttribute("disabled"), true,
-          "The pane toggle button should be disabled.");
-    }
-
-    is(toggleButton.classList.contains("pane-collapsed"), true,
-        "The pane toggle button should still indicate that the details pane is " +
-        "collapsed.");
+    ok(!document.querySelector(".sidebar-toggle"),
+      "The pane toggle button should not be visible.");
     is(!!document.querySelector(".network-details-panel"), false,
-        "The details pane should still be hidden.");
+      "The details pane should still be hidden.");
     is(getSelectedRequest(store.getState()), null,
-        "There should still be no selected item in the requests menu.");
+      "There should still be no selected item in the requests menu.");
 
     // Trigger the details panel toggle action
-    EventUtils.sendMouseEvent({ type: "click" }, toggleButton);
+    store.dispatch(Actions.toggleNetworkDetails());
+
+    let toggleButton = document.querySelector(".sidebar-toggle");
 
     if (shouldPanelOpen) {
-      is(toggleButton.hasAttribute("disabled"), false,
-          "The pane toggle button should still be enabled after being pressed.");
       is(toggleButton.classList.contains("pane-collapsed"), false,
-          "The pane toggle button should now indicate that the details pane is " +
-          "not collapsed anymore after being pressed.");
+        "The pane toggle button should now indicate that the details pane is " +
+        "not collapsed anymore after being pressed.");
       is(!!document.querySelector(".network-details-panel"), true,
-          "The details pane should not be hidden after toggle button was pressed.");
+        "The details pane should not be hidden after toggle button was pressed.");
       isnot(getSelectedRequest(store.getState()), null,
-          "There should be a selected item in the requests menu.");
+        "There should be a selected item in the requests menu.");
       is(getSelectedIndex(store.getState()), selectedItemIndex,
-          `The item index ${selectedItemIndex} should be selected in the requests menu.`);
+        `The item index ${selectedItemIndex} should be selected in the requests menu.`);
       // Close the panel
       EventUtils.sendMouseEvent({ type: "click" }, toggleButton);
     } else {
-      is(toggleButton.hasAttribute("disabled"), true,
-          "The pane toggle button should be disabled.");
-      is(toggleButton.classList.contains("pane-collapsed"), true,
-          "The pane toggle button should still indicate that the details pane is " +
-          "collapsed.");
+      ok(!toggleButton, "The pane toggle button should be not visible.");
       is(!!document.querySelector(".network-details-panel"), false,
-          "The details pane should still be hidden.");
+        "The details pane should still be hidden.");
       is(getSelectedRequest(store.getState()), null,
-          "There should still be no selected item in the requests menu.");
+        "There should still be no selected item in the requests menu.");
     }
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_pane-toggle.js
+++ b/devtools/client/netmonitor/test/browser_net_pane-toggle.js
@@ -16,63 +16,50 @@ add_task(async function() {
   let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
   let {
     getSelectedRequest,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
-  let toggleButton = document.querySelector(".network-details-panel-toggle");
-
-  is(toggleButton.hasAttribute("disabled"), true,
-    "The pane toggle button should be disabled when the frontend is opened.");
-  is(toggleButton.classList.contains("pane-collapsed"), true,
-    "The pane toggle button should indicate that the details pane is " +
-    "collapsed when the frontend is opened.");
+  ok(!document.querySelector(".sidebar-toggle"),
+    "The pane toggle button should not be visible.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The details pane should be hidden when the frontend is opened.");
   is(getSelectedRequest(store.getState()), null,
     "There should be no selected item in the requests menu.");
 
   let networkEvent = monitor.panelWin.once(EVENTS.NETWORK_EVENT);
   tab.linkedBrowser.reload();
   await networkEvent;
 
-  is(toggleButton.hasAttribute("disabled"), false,
-    "The pane toggle button should be enabled after the first request.");
-  is(toggleButton.classList.contains("pane-collapsed"), true,
-    "The pane toggle button should still indicate that the details pane is " +
-    "collapsed after the first request.");
+  ok(!document.querySelector(".sidebar-toggle"),
+    "The pane toggle button should not be visible after the first request.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The details pane should still be hidden after the first request.");
   is(getSelectedRequest(store.getState()), null,
     "There should still be no selected item in the requests menu.");
 
-  EventUtils.sendMouseEvent({ type: "click" }, toggleButton);
+  store.dispatch(Actions.toggleNetworkDetails());
 
-  is(toggleButton.hasAttribute("disabled"), false,
-    "The pane toggle button should still be enabled after being pressed.");
+  let toggleButton = document.querySelector(".sidebar-toggle");
+
   is(toggleButton.classList.contains("pane-collapsed"), false,
     "The pane toggle button should now indicate that the details pane is " +
-    "not collapsed anymore after being pressed.");
+    "not collapsed anymore.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The details pane should not be hidden after toggle button was pressed.");
   isnot(getSelectedRequest(store.getState()), null,
     "There should be a selected item in the requests menu.");
   is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
 
   EventUtils.sendMouseEvent({ type: "click" }, toggleButton);
 
-  is(toggleButton.hasAttribute("disabled"), false,
-    "The pane toggle button should still be enabled after being pressed again.");
-  is(toggleButton.classList.contains("pane-collapsed"), true,
-    "The pane toggle button should now indicate that the details pane is " +
-    "collapsed after being pressed again.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The details pane should now be hidden after the toggle button was pressed again.");
   is(getSelectedRequest(store.getState()), null,
     "There should now be no selected item in the requests menu.");
 
   await teardown(monitor);
 
   function getSelectedIndex(state) {
--- a/devtools/client/netmonitor/test/browser_net_post-data-03.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-03.js
@@ -19,18 +19,17 @@ add_task(async function() {
 
   store.dispatch(Actions.batchEnable(false));
 
   // Execute requests.
   await performRequests(monitor, tab, 1);
 
   // Wait for all tree view updated by react
   wait = waitForDOM(document, "#headers-panel .tree-section .treeLabel", 3);
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#headers-tab"));
   await wait;
 
   let tabpanel = document.querySelector("#headers-panel");
   is(tabpanel.querySelectorAll(".tree-section .treeLabel").length, 3,
     "There should be 3 header sections displayed in this tabpanel.");
 
--- a/devtools/client/netmonitor/test/browser_net_post-data-04.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-04.js
@@ -19,18 +19,17 @@ add_task(async function() {
 
   store.dispatch(Actions.batchEnable(false));
 
   // Execute requests.
   await performRequests(monitor, tab, 1);
 
   // Wait for all tree view updated by react
   wait = waitForDOM(document, "#params-panel .tree-section");
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#params-tab"));
   await wait;
 
   let tabpanel = document.querySelector("#params-panel");
 
   ok(tabpanel.querySelector(".treeTable"),
     "The request params doesn't have the indended visibility.");
--- a/devtools/client/netmonitor/test/browser_net_prefs-reload.js
+++ b/devtools/client/netmonitor/test/browser_net_prefs-reload.js
@@ -179,18 +179,17 @@ add_task(async function() {
     let newMonitor = await restartNetMonitor(monitor);
     monitor = newMonitor.monitor;
 
     let networkEvent = waitForNetworkEvents(monitor, 1);
     newMonitor.tab.linkedBrowser.reload();
     await networkEvent;
 
     let wait = waitForDOM(getDoc(), ".network-details-panel");
-    EventUtils.sendMouseEvent({ type: "click" },
-      getDoc().querySelector(".network-details-panel-toggle"));
+    getStore().dispatch(Actions.toggleNetworkDetails());
     await wait;
   }
 
   async function testBottom() {
     await restartNetMonitorAndSetupEnv();
 
     info("Testing prefs reload for a bottom host.");
     storeFirstPrefValues();
--- a/devtools/client/netmonitor/test/browser_net_security-details.js
+++ b/devtools/client/netmonitor/test/browser_net_security-details.js
@@ -19,18 +19,17 @@ add_task(async function() {
   info("Performing a secure request.");
   const REQUESTS_URL = "https://example.com" + CORS_SJS_PATH;
   let wait = waitForNetworkEvents(monitor, 1);
   await ContentTask.spawn(tab.linkedBrowser, REQUESTS_URL, async function(url) {
     content.wrappedJSObject.performRequests(1, url);
   });
   await wait;
 
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#security-tab"));
   await waitUntil(() => document.querySelector(
     "#security-panel .security-info-value"));
 
   let tabpanel = document.querySelector("#security-panel");
   let textboxes = tabpanel.querySelectorAll(".textbox-input");
 
--- a/devtools/client/netmonitor/test/browser_net_security-error.js
+++ b/devtools/client/netmonitor/test/browser_net_security-error.js
@@ -18,18 +18,17 @@ add_task(async function() {
 
   let requestsDone = waitForNetworkEvents(monitor, 1);
   await ContentTask.spawn(tab.linkedBrowser, {}, async function() {
     content.wrappedJSObject.performRequests(1, "https://nocert.example.com");
   });
   await requestsDone;
 
   let securityInfoLoaded = waitForDOM(document, ".security-info-value");
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
 
   await waitUntil(() => document.querySelector("#security-tab"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#security-tab"));
   await securityInfoLoaded;
 
   let errormsg = document.querySelector(".security-info-value");
   isnot(errormsg.textContent, "", "Error message is not empty.");
--- a/devtools/client/netmonitor/test/browser_net_simple-request-details.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request-details.js
@@ -30,18 +30,17 @@ add_task(async function() {
 
   is(getSelectedRequest(store.getState()), undefined,
     "There shouldn't be any selected item in the requests menu.");
   is(store.getState().requests.requests.size, 1,
     "The requests menu should not be empty after the first request.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The network details panel should still be hidden after first request.");
 
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
 
   isnot(getSelectedRequest(store.getState()), undefined,
     "There should be a selected item in the requests menu.");
   is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should not be hidden after toggle button was pressed.");
 
--- a/devtools/client/netmonitor/test/browser_net_simple-request.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request.js
@@ -16,55 +16,43 @@ add_task(async function() {
   let { tab, monitor } = await initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   store.dispatch(Actions.batchEnable(false));
 
-  is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"),
-    true,
-    "The pane toggle button should be disabled when the frontend is opened.");
   ok(document.querySelector(".request-list-empty-notice"),
     "An empty notice should be displayed when the frontend is opened.");
   is(store.getState().requests.requests.size, 0,
     "The requests menu should be empty when the frontend is opened.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The network details panel should be hidden when the frontend is opened.");
 
   await reloadAndWait();
 
-  is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"),
-    false,
-    "The pane toggle button should be enabled after the first request.");
   ok(!document.querySelector(".request-list-empty-notice"),
     "The empty notice should be hidden after the first request.");
   is(store.getState().requests.requests.size, 1,
     "The requests menu should not be empty after the first request.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The network details panel should still be hidden after the first request.");
 
   await reloadAndWait();
 
-  is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"),
-    false,
-    "The pane toggle button should be still be enabled after a reload.");
   ok(!document.querySelector(".request-list-empty-notice"),
     "The empty notice should be still hidden after a reload.");
   is(store.getState().requests.requests.size, 1,
     "The requests menu should not be empty after a reload.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The network details panel should still be hidden after a reload.");
 
   store.dispatch(Actions.clearRequests());
 
-  is(document.querySelector(".network-details-panel-toggle").hasAttribute("disabled"),
-    true,
-    "The pane toggle button should be disabled when after clear.");
   ok(document.querySelector(".request-list-empty-notice"),
     "An empty notice should be displayed again after clear.");
   is(store.getState().requests.requests.size, 0,
     "The requests menu should be empty after clear.");
   is(!!document.querySelector(".network-details-panel"), false,
     "The network details panel should still be hidden after clear.");
 
   return teardown(monitor);
--- a/devtools/client/netmonitor/test/browser_net_sort-01.js
+++ b/devtools/client/netmonitor/test/browser_net_sort-01.js
@@ -46,18 +46,17 @@ add_task(async function() {
     url: "sjs_sorting-test-server.sjs?index=3&" + Math.random(),
     method: "GET3"
   }];
 
   let wait = waitForNetworkEvents(monitor, 5);
   await performRequestsInContent(requests);
   await wait;
 
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
 
   isnot(getSelectedRequest(store.getState()), undefined,
     "There should be a selected item in the requests menu.");
   is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should be visible after toggle button was pressed.");
 
--- a/devtools/client/netmonitor/test/browser_net_sort-02.js
+++ b/devtools/client/netmonitor/test/browser_net_sort-02.js
@@ -46,18 +46,17 @@ add_task(async function() {
     url: "sjs_sorting-test-server.sjs?index=3&" + Math.random(),
     method: "GET3"
   }];
 
   let wait = waitForNetworkEvents(monitor, 5);
   await performRequestsInContent(requests);
   await wait;
 
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
 
   isnot(getSelectedRequest(store.getState()), undefined,
     "There should be a selected item in the requests menu.");
   is(getSelectedIndex(store.getState()), 0,
     "The first item should be selected in the requests menu.");
   is(!!document.querySelector(".network-details-panel"), true,
     "The network details panel should be visible after toggle button was pressed.");
 
--- a/devtools/client/netmonitor/test/browser_net_streaming-response.js
+++ b/devtools/client/netmonitor/test/browser_net_streaming-response.js
@@ -52,18 +52,17 @@ add_task(async function() {
       CONTENT_TYPE_SJS + "?fmt=" + fmt,
       {
         status: 200,
         statusText: "OK"
       });
   });
 
   wait = waitForDOM(document, "#response-panel");
-  EventUtils.sendMouseEvent({ type: "click" },
-    document.querySelector(".network-details-panel-toggle"));
+  store.dispatch(Actions.toggleNetworkDetails());
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   await wait;
 
   store.dispatch(Actions.selectRequest(null));
 
   await selectIndexAndWaitForSourceEditor(monitor, 0);
   // the hls-m3u8 part
--- a/devtools/client/shared/inplace-editor.js
+++ b/devtools/client/shared/inplace-editor.js
@@ -46,16 +46,24 @@ const MAX_POPUP_ENTRIES = 500;
 const FOCUS_FORWARD = focusManager.MOVEFOCUS_FORWARD;
 const FOCUS_BACKWARD = focusManager.MOVEFOCUS_BACKWARD;
 
 const WORD_REGEXP = /\w/;
 const isWordChar = function(str) {
   return str && WORD_REGEXP.test(str);
 };
 
+const GRID_PROPERTY_NAMES = ["grid-area", "grid-row", "grid-row-start",
+                             "grid-row-end", "grid-column", "grid-column-start",
+                             "grid-column-end"];
+const GRID_ROW_PROPERTY_NAMES = ["grid-area", "grid-row", "grid-row-start",
+                                 "grid-row-end"];
+const GRID_COL_PROPERTY_NAMES = ["grid-area", "grid-column", "grid-column-start",
+                                 "grid-column-end"];
+
 /**
  * Helper to check if the provided key matches one of the expected keys.
  * Keys will be prefixed with DOM_VK_ and should match a key in KeyCodes.
  *
  * @param {String} key
  *        the key to check (can be a keyCode).
  * @param {...String} keys
  *        list of possible keys allowed.
@@ -126,16 +134,19 @@ function isKeyIn(key, ...keys) {
  *      defaults to true
  *    {Boolean} preserveTextStyles: If true, do not copy text-related styles
  *              from `element` to the new input.
  *      defaults to false
  *    {Object} cssProperties: An instance of CSSProperties.
  *    {Object} cssVariables: A Map object containing all CSS variables.
  *    {Number} defaultIncrement: The value by which the input is incremented
  *      or decremented by default (0.1 for properties like opacity and 1 by default)
+ *    {Function} getGridLineNames:
+ *       Will be called before offering autocomplete sugestions, if the property is
+ *       a member of GRID_PROPERTY_NAMES.
  */
 function editableField(options) {
   return editableItem(options, function(element, event) {
     if (!options.element.inplaceEditor) {
       new InplaceEditor(options, event);
     }
   });
 }
@@ -290,20 +301,16 @@ function InplaceEditor(options, event) {
   }
 
   this.input.focus();
 
   if (typeof options.selectAll == "undefined" || options.selectAll) {
     this.input.select();
   }
 
-  if (this.contentType == CONTENT_TYPES.CSS_VALUE && this.input.value == "") {
-    this._maybeSuggestCompletion(false);
-  }
-
   this.input.addEventListener("blur", this._onBlur);
   this.input.addEventListener("keypress", this._onKeyPress);
   this.input.addEventListener("input", this._onInput);
   this.input.addEventListener("dblclick", this._stopEventPropagation);
   this.input.addEventListener("click", this._stopEventPropagation);
   this.input.addEventListener("mousedown", this._stopEventPropagation);
   this.input.addEventListener("contextmenu", this._onContextMenu);
   this.doc.defaultView.addEventListener("blur", this._onWindowBlur);
@@ -316,16 +323,18 @@ function InplaceEditor(options, event) {
 
   this._updateSize();
 
   EventEmitter.decorate(this);
 
   if (options.start) {
     options.start(this, event);
   }
+
+  this._getGridNamesBeforeCompletion(options.getGridLineNames);
 }
 
 exports.InplaceEditor = InplaceEditor;
 
 InplaceEditor.CONTENT_TYPES = CONTENT_TYPES;
 
 InplaceEditor.prototype = {
 
@@ -988,16 +997,35 @@ InplaceEditor.prototype = {
       this._acceptPopupSuggestion();
     } else {
       this._apply();
       this._clear();
     }
   },
 
   /**
+   * Before offering autocomplete, set this.gridLineNames as the line names
+   * of the current grid, if they exist.
+   *
+   * @param {Function} getGridLineNames
+   *        A function which gets the line names of the current grid.
+   */
+  _getGridNamesBeforeCompletion: async function(getGridLineNames) {
+    if (getGridLineNames && this.property &&
+        GRID_PROPERTY_NAMES.includes(this.property.name)) {
+      this.gridLineNames = await getGridLineNames();
+    }
+
+    if (this.contentType == CONTENT_TYPES.CSS_VALUE && this.input &&
+        this.input.value == "") {
+      this._maybeSuggestCompletion(false);
+    }
+  },
+
+  /**
    * Event handler called by the autocomplete popup when receiving a click
    * event.
    */
   _onAutocompletePopupClick: function() {
     this._acceptPopupSuggestion();
   },
 
   _acceptPopupSuggestion: function() {
@@ -1562,17 +1590,28 @@ InplaceEditor.prototype = {
    * Returns a list of CSS values valid for a provided property name to use for
    * the autocompletion. This method is overridden by tests in order to use
    * mocked suggestion lists.
    *
    * @param {String} propertyName
    * @return {Array} array of CSS property values (Strings)
    */
   _getCSSValuesForPropertyName: function(propertyName) {
-    return this.cssProperties.getValues(propertyName);
+    let gridLineList = [];
+    if (this.gridLineNames) {
+      if (GRID_ROW_PROPERTY_NAMES.includes(this.property.name)) {
+        gridLineList.push(...this.gridLineNames.rows);
+      }
+      if (GRID_COL_PROPERTY_NAMES.includes(this.property.name)) {
+        gridLineList.push(...this.gridLineNames.cols);
+      }
+    }
+    // Must be alphabetically sorted before comparing the results with
+    // the user input, otherwise we will lose some results.
+    return gridLineList.concat(this.cssProperties.getValues(propertyName)).sort();
   },
 
   /**
    * Returns the list of all CSS variables to use for the autocompletion.
    *
    * @return {Array} array of CSS variable names (Strings)
    */
   _getCSSVariableNames: function() {
--- a/devtools/client/shared/vendor/WASMPARSER_UPGRADING
+++ b/devtools/client/shared/vendor/WASMPARSER_UPGRADING
@@ -1,14 +1,14 @@
 # wasmparser version
 
-Current vesion is: 0.5.3
+Current vesion is: 0.6.0
 
 # Upgrade process
 
 1. Pull latest release from npm and extract WasmDis.js and WasmParser.js, e.g.
 
 ```
-curl https://registry.npmjs.org/wasmparser/-/wasmparser-0.5.3.tgz | tar -x --strip-components 2 package/dist/{WasmDis,WasmParser}.js
+curl https://registry.npmjs.org/wasmparser/-/wasmparser-0.6.0.tgz | tar -x --strip-components 2 package/dist/{WasmDis,WasmParser}.js
 ```
 
 2. Remove reference to source maps (last line)
 
--- a/devtools/client/shared/vendor/WasmDis.js
+++ b/devtools/client/shared/vendor/WasmDis.js
@@ -1,9 +1,19 @@
 "use strict";
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return function (d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
 Object.defineProperty(exports, "__esModule", { value: true });
 /* Copyright 2016 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *     http://www.apache.org/licenses/LICENSE-2.0
@@ -976,8 +986,124 @@ var WasmDisassembler = /** @class */ (fu
                 default:
                     throw new Error("Expectected state: " + reader.state);
             }
         }
     };
     return WasmDisassembler;
 }());
 exports.WasmDisassembler = WasmDisassembler;
+var UNKNOWN_FUNCTION_PREFIX = "unknown";
+var NameSectionNameResolver = /** @class */ (function (_super) {
+    __extends(NameSectionNameResolver, _super);
+    function NameSectionNameResolver(names) {
+        var _this = _super.call(this) || this;
+        _this._names = names;
+        return _this;
+    }
+    NameSectionNameResolver.prototype.getFunctionName = function (index, isImport, isRef) {
+        var name = this._names[index];
+        if (!name)
+            return "$" + UNKNOWN_FUNCTION_PREFIX + index;
+        return isRef ? "$" + name : "$" + name + " (;" + index + ";)";
+    };
+    return NameSectionNameResolver;
+}(DefaultNameResolver));
+var NameSectionReader = /** @class */ (function () {
+    function NameSectionReader() {
+        this._done = false;
+        this._functionsCount = 0;
+        this._functionImportsCount = 0;
+        this._functionNames = null;
+        this._hasNames = false;
+    }
+    NameSectionReader.prototype.read = function (reader) {
+        var _this = this;
+        if (this._done)
+            throw new Error('Invalid state: disassembly process was already finished.');
+        while (true) {
+            if (!reader.read())
+                return false;
+            switch (reader.state) {
+                case 2 /* END_WASM */:
+                    if (!reader.hasMoreBytes()) {
+                        this._done = true;
+                        return true;
+                    }
+                    break;
+                case -1 /* ERROR */:
+                    throw reader.error;
+                case 1 /* BEGIN_WASM */:
+                    this._functionsCount = 0;
+                    this._functionImportsCount = 0;
+                    this._functionNames = [];
+                    this._hasNames = false;
+                    break;
+                case 4 /* END_SECTION */:
+                    break;
+                case 3 /* BEGIN_SECTION */:
+                    var sectionInfo = reader.result;
+                    if (sectionInfo.id === 0 /* Custom */ &&
+                        WasmParser_1.bytesToString(sectionInfo.name) === "name") {
+                        break;
+                    }
+                    if (sectionInfo.id === 3 /* Function */ ||
+                        sectionInfo.id === 2 /* Import */) {
+                        break;
+                    }
+                    reader.skipSection();
+                    break;
+                case 12 /* IMPORT_SECTION_ENTRY */:
+                    var importInfo = reader.result;
+                    if (importInfo.kind === 0 /* Function */)
+                        this._functionImportsCount++;
+                    break;
+                case 13 /* FUNCTION_SECTION_ENTRY */:
+                    this._functionsCount++;
+                    break;
+                case 19 /* NAME_SECTION_ENTRY */:
+                    var nameInfo = reader.result;
+                    if (nameInfo.type !== 1 /* Function */)
+                        break;
+                    var functionNameInfo = nameInfo;
+                    functionNameInfo.names.forEach(function (naming) {
+                        _this._functionNames[naming.index] = WasmParser_1.bytesToString(naming.name);
+                    });
+                    this._hasNames = true;
+                    break;
+                default:
+                    throw new Error("Expectected state: " + reader.state);
+            }
+        }
+    };
+    NameSectionReader.prototype.hasValidNames = function () {
+        return this._hasNames;
+    };
+    NameSectionReader.prototype.getNameResolver = function () {
+        if (!this.hasValidNames())
+            throw new Error("Has no valid name section");
+        // Fix bad names.
+        var functionNamesLength = this._functionImportsCount + this._functionsCount;
+        var functionNames = this._functionNames.slice(0, functionNamesLength);
+        var usedNameAt = Object.create(null);
+        for (var i = 0; i < functionNames.length; i++) {
+            var name_1 = functionNames[i];
+            if (!name_1)
+                continue;
+            var goodName = !(name_1 in usedNameAt) &&
+                !/[^0-9A-Za-z!#$%&'*+.:<=>?@^_`|~\/\-]/.test(name_1) &&
+                name_1.indexOf(UNKNOWN_FUNCTION_PREFIX) !== 0;
+            if (!goodName) {
+                if (usedNameAt[name_1] >= 0) {
+                    // Remove all non-unique names.
+                    functionNames[usedNameAt[name_1]] = null;
+                    usedNameAt[name_1] = -1;
+                }
+                functionNames[i] = null;
+                continue;
+            }
+            usedNameAt[name_1] = i;
+        }
+        return new NameSectionNameResolver(functionNames);
+    };
+    return NameSectionReader;
+}());
+exports.NameSectionReader = NameSectionReader;
--- a/devtools/server/actors/layout.js
+++ b/devtools/server/actors/layout.js
@@ -137,27 +137,30 @@ const LayoutActor = ActorClassWithSpec(l
   destroy() {
     Actor.prototype.destroy.call(this);
 
     this.tabActor = null;
     this.walker = null;
   },
 
   /**
-   * Returns the flex container found by iterating on the given selected node. The current
-   * node can be a flex container or flex item. If it is a flex item, returns the parent
-   * flex container. Otherwise, return null if the current or parent node is not a flex
-   * container.
+   * Helper function for getCurrentGrid and getCurrentFlexbox. Returns the grid or
+   * flex container (whichever is requested) found by iterating on the given selected
+   * node. The current node can be a grid/flex container or grid/flex item. If it is a
+   * grid/flex item, returns the parent grid/flex container. Otherwise, returns null
+   * if the current or parent node is not a grid/flex container.
    *
    * @param  {Node|NodeActor} node
    *         The node to start iterating at.
-   * @return {FlexboxActor|Null} The FlexboxActor of the flex container of the give node.
-   * Otherwise, returns null.
+   * @param {String} type
+   *         Can be "grid" or "flex", the display type we are searching for.
+   * @return {GridActor|FlexboxActor|Null} The GridActor or FlexboxActor of the
+   * grid/flex container of the give node. Otherwise, returns null.
    */
-  getCurrentFlexbox(node) {
+  getCurrentDisplay(node, type) {
     if (isNodeDead(node)) {
       return null;
     }
 
     // Given node can either be a Node or a NodeActor.
     if (node.rawNode) {
       node = node.rawNode;
     }
@@ -166,45 +169,80 @@ const LayoutActor = ActorClassWithSpec(l
       nodeFilterConstants.SHOW_ELEMENT);
     let currentNode = treeWalker.currentNode;
     let displayType = this.walker.getNode(currentNode).displayType;
 
     if (!displayType) {
       return null;
     }
 
-    // Check if the current node is a flex container.
-    if (displayType == "inline-flex" || displayType == "flex") {
-      return new FlexboxActor(this, treeWalker.currentNode);
+    if (type == "flex" &&
+        (displayType == "inline-flex" || displayType == "flex")) {
+      return new FlexboxActor(this, currentNode);
+    } else if (type == "grid" &&
+               (displayType == "inline-grid" || displayType == "grid")) {
+      return new GridActor(this, currentNode);
     }
 
     // Otherwise, check if this is a flex item or the parent node is a flex container.
     while ((currentNode = treeWalker.parentNode())) {
       if (!currentNode) {
         break;
       }
 
       displayType = this.walker.getNode(currentNode).displayType;
 
-      switch (displayType) {
-        case "inline-flex":
-        case "flex":
-          return new FlexboxActor(this, currentNode);
-        case "contents":
-          // Continue walking up the tree since the parent node is a content element.
-          continue;
+      if (type == "flex" &&
+          (displayType == "inline-flex" || displayType == "flex")) {
+        return new FlexboxActor(this, currentNode);
+      } else if (type == "grid" &&
+                 (displayType == "inline-grid" || displayType == "grid")) {
+        return new GridActor(this, currentNode);
+      } else if (displayType == "contents") {
+        // Continue walking up the tree since the parent node is a content element.
+        continue;
       }
 
       break;
     }
 
     return null;
   },
 
   /**
+   * Returns the grid container found by iterating on the given selected node. The current
+   * node can be a grid container or grid item. If it is a grid item, returns the parent
+   * grid container. Otherwise, return null if the current or parent node is not a grid
+   * container.
+   *
+   * @param  {Node|NodeActor} node
+   *         The node to start iterating at.
+   * @return {GridActor|Null} The GridActor of the grid container of the give node.
+   * Otherwise, returns null.
+   */
+  getCurrentGrid(node) {
+    return this.getCurrentDisplay(node, "grid");
+  },
+
+  /**
+   * Returns the flex container found by iterating on the given selected node. The current
+   * node can be a flex container or flex item. If it is a flex item, returns the parent
+   * flex container. Otherwise, return null if the current or parent node is not a flex
+   * container.
+   *
+   * @param  {Node|NodeActor} node
+   *         The node to start iterating at.
+   * @return {FlexboxActor|Null} The FlexboxActor of the flex container of the give node.
+   * Otherwise, returns null.
+   */
+  getCurrentFlexbox(node) {
+    return this.getCurrentDisplay(node, "flex");
+  },
+
+  /**
    * Returns an array of GridActor objects for all the grid elements contained in the
    * given root node.
    *
    * @param  {Node|NodeActor} node
    *         The root node for grid elements
    * @return {Array} An array of GridActor objects.
    */
   getGrids(node) {
--- a/devtools/server/tests/mochitest/setup-in-parent.js
+++ b/devtools/server/tests/mochitest/setup-in-parent.js
@@ -1,12 +1,12 @@
 "use strict";
 
-var {Ci} = require("chrome");
 var Services = require("Services");
+var ChromeUtils = require("ChromeUtils");
 
 exports.setupParent = function({mm, prefix}) {
   let args = [
-    !!mm.QueryInterface(Ci.nsIMessageSender),
+    ChromeUtils.getClassName(mm) == "ChromeMessageSender",
     prefix
   ];
   Services.obs.notifyObservers(null, "test:setupParent", JSON.stringify(args));
 };
--- a/devtools/shared/specs/layout.js
+++ b/devtools/shared/specs/layout.js
@@ -26,16 +26,25 @@ const layoutSpec = generateActorSpec({
       request: {
         node: Arg(0, "domnode"),
       },
       response: {
         flexbox: RetVal("nullable:flexbox")
       }
     },
 
+    getCurrentGrid: {
+      request: {
+        node: Arg(0, "domnode"),
+      },
+      response: {
+        grid: RetVal("nullable:grid")
+      }
+    },
+
     getGrids: {
       request: {
         rootNode: Arg(0, "domnode")
       },
       response: {
         grids: RetVal("array:grid")
       }
     },
--- a/dom/base/test/chrome/cpows_parent.xul
+++ b/dom/base/test/chrome/cpows_parent.xul
@@ -206,18 +206,16 @@
     }
 
     let savedElement = null;
     function recvDomTest(message) {
       savedElement = message.objects.element;
 
       is(savedElement.QueryInterface(Ci.nsISupports), savedElement,
          "QI to nsISupports works");
-      is(savedElement.QueryInterface(Ci.nsIDOMNode), savedElement,
-         "QI to a random (implemented) interface works");
 
       function testNoInterface(savedElement, i) {
         try {
           savedElement.QueryInterface(i);
           ok(false, "should have thrown an exception");
         } catch (e) {
           is(e.result, Cr.NS_ERROR_NO_INTERFACE, "threw the right exception");
         }
--- a/dom/base/test/unit/test_isequalnode.js
+++ b/dom/base/test/unit/test_isequalnode.js
@@ -41,20 +41,20 @@ function test_isEqualNode_setAttribute()
   // NOTE: 0, 2 are whitespace
   var test1 = doc.getElementById("test_setAttribute");
   var node1 = test1.childNodes.item(1);
   var node2 = test1.childNodes.item(3);
 
   check_eq_nodes(node1, node2);
 
 
-  el(node1).setAttribute("bar", "baz");
+  node1.setAttribute("bar", "baz");
   check_neq_nodes(node1, node2);
 
-  el(node2).setAttribute("bar", "baz");
+  node2.setAttribute("bar", "baz");
   check_eq_nodes(node1, node2);
 
 
   // the null namespace is equivalent to no namespace -- section 1.3.3
   // (XML Namespaces) of DOM 3 Core
   node1.setAttributeNS(null, "quux", "17");
   check_neq_nodes(node1, node2);
 
@@ -80,17 +80,17 @@ function test_isEqualNode_setAttribute()
 }
 
 function test_isEqualNode_clones()
 {
   // tests all elements and attributes in the document
   var all_elts = doc.getElementsByTagName("*");
   for (var i = 0; i < all_elts.length; i++)
   {
-    var elt = el(all_elts.item(i));
+    var elt = all_elts.item(i);
     check_eq_nodes(elt, elt.cloneNode(true));
 
     var attrs = elt.attributes;
     for (var j = 0; j < attrs.length; j++)
     {
       var attr = attrs.item(j);
       check_eq_nodes(attr, attr.cloneNode(true));
     }
@@ -381,22 +381,16 @@ function test_isEqualNode_wholeDoc()
     doc2.createTreeWalker(doc2, NodeFilter.SHOW_ALL,
                           null);
   do {
     check_eq_nodes(tw1.currentNode, tw2.currentNode);
     tw1.nextNode();
   } while(tw2.nextNode());
 }
 
-// UTILITY FUNCTIONS
-
-function n(node)  { return node ? node.QueryInterface(nsIDOMNode) : null; }
-function el(node) { return node ? node.QueryInterface(nsIDOMElement) : null; }
-
-
 // TESTING FUNCTIONS
 
 /**
  * Compares the first and third (zero-indexed) child nodes of the element
  * (typically to allow whitespace) referenced by parentId for isEqualNode
  * equality or inequality based on the value of areEqual.
  *
  * Note that this means that the contents of the element referenced by parentId
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -741,17 +741,17 @@ DefineConstructor(JSContext* cx, JS::Han
 static JSObject*
 CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
                       JS::Handle<JSObject*> constructorProto,
                       const js::Class* constructorClass,
                       unsigned ctorNargs, const NamedConstructor* namedConstructors,
                       JS::Handle<JSObject*> proto,
                       const NativeProperties* properties,
                       const NativeProperties* chromeOnlyProperties,
-                      const char* name, bool defineOnGlobal)
+                      const char* name, bool isChrome, bool defineOnGlobal)
 {
   JS::Rooted<JSObject*> constructor(cx);
   MOZ_ASSERT(constructorProto);
   MOZ_ASSERT(constructorClass);
   constructor = JS_NewObjectWithGivenProto(cx, Jsvalify(constructorClass),
                                            constructorProto);
   if (!constructor) {
     return nullptr;
@@ -778,16 +778,23 @@ CreateInterfaceObject(JSContext* cx, JS:
     JS::Rooted<jsid> hasInstanceId(cx,
       SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance)));
     if (!JS_DefineFunctionById(cx, constructor, hasInstanceId,
                                InterfaceHasInstance, 1,
                                // Flags match those of Function[Symbol.hasInstance]
                                JSPROP_READONLY | JSPROP_PERMANENT)) {
       return nullptr;
     }
+
+    if (isChrome && !JS_DefineFunction(cx, constructor, "isInstance",
+                                       InterfaceHasInstance, 1,
+                                       // Don't bother making it enumerable
+                                       0)) {
+      return nullptr;
+    }
   }
 
   if (properties) {
     if (properties->HasStaticMethods() &&
         !DefinePrefable(cx, constructor, properties->StaticMethods())) {
       return nullptr;
     }
 
@@ -797,17 +804,17 @@ CreateInterfaceObject(JSContext* cx, JS:
     }
 
     if (properties->HasConstants() &&
         !DefinePrefable(cx, constructor, properties->Constants())) {
       return nullptr;
     }
   }
 
-  if (chromeOnlyProperties) {
+  if (chromeOnlyProperties && isChrome) {
     if (chromeOnlyProperties->HasStaticMethods() &&
         !DefinePrefable(cx, constructor,
                         chromeOnlyProperties->StaticMethods())) {
       return nullptr;
     }
 
     if (chromeOnlyProperties->HasStaticAttributes() &&
         !DefinePrefable(cx, constructor,
@@ -978,21 +985,24 @@ CreateInterfaceObjects(JSContext* cx, JS
              "to cache it");
   MOZ_ASSERT(bool(constructorClass) == bool(constructorCache),
              "If, and only if, there is an interface object we need to cache "
              "it");
   MOZ_ASSERT(constructorProto || !constructorClass,
              "Must have a constructor proto if we plan to create a constructor "
              "object");
 
+  bool isChrome = nsContentUtils::ThreadsafeIsSystemCaller(cx);
+
   JS::Rooted<JSObject*> proto(cx);
   if (protoClass) {
     proto =
       CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
-                                     properties, chromeOnlyProperties,
+                                     properties,
+                                     isChrome ? chromeOnlyProperties : nullptr,
                                      unscopableNames, isGlobal);
     if (!proto) {
       return;
     }
 
     *protoCache = proto;
   }
   else {
@@ -1000,16 +1010,17 @@ CreateInterfaceObjects(JSContext* cx, JS
   }
 
   JSObject* interface;
   if (constructorClass) {
     interface = CreateInterfaceObject(cx, global, constructorProto,
                                       constructorClass, ctorNargs,
                                       namedConstructors, proto, properties,
                                       chromeOnlyProperties, name,
+                                      isChrome,
                                       defineOnGlobal);
     if (!interface) {
       if (protoCache) {
         // If we fail we need to make sure to clear the value of protoCache we
         // set above.
         *protoCache = nullptr;
       }
       return;
@@ -1743,16 +1754,36 @@ XrayResolveOwnProperty(JSContext* cx, JS
     if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_PROTOTYPE)) {
       return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count ||
              ResolvePrototypeOrConstructor(cx, wrapper, obj,
                                            nativePropertyHooks->mPrototypeID,
                                            JSPROP_PERMANENT | JSPROP_READONLY,
                                            desc, cacheOnHolder);
     }
 
+    if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_ISINSTANCE) &&
+        DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj))->
+          wantsInterfaceHasInstance) {
+      cacheOnHolder = true;
+      JSNativeWrapper interfaceIsInstanceWrapper = { InterfaceIsInstance,
+                                                      nullptr };
+      JSObject* funObj = XrayCreateFunction(cx, wrapper,
+                                            interfaceIsInstanceWrapper, 1, id);
+      if (!funObj) {
+        return false;
+      }
+
+      desc.value().setObject(*funObj);
+      desc.setAttributes(0);
+      desc.object().set(wrapper);
+      desc.setSetter(nullptr);
+      desc.setGetter(nullptr);
+      return true;
+    }
+
     if (id == SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance)) &&
         DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj))->
           wantsInterfaceHasInstance) {
       cacheOnHolder = true;
       JSNativeWrapper interfaceHasInstanceWrapper = { InterfaceHasInstance,
                                                       nullptr };
       JSObject* funObj = XrayCreateFunction(cx, wrapper,
                                             interfaceHasInstanceWrapper, 1, id);
@@ -2495,43 +2526,63 @@ InterfaceHasInstance(JSContext* cx, int 
              "Why do we have a hasInstance hook if we don't have a prototype "
              "ID?");
 
   *bp = (domClass && domClass->mInterfaceChain[depth] == prototypeID);
   return true;
 }
 
 bool
-InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp,
-                    prototypes::ID prototypeID, int depth)
+InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-  if (MOZ_UNLIKELY(args.length() < 1)) {
-    nsPrintfCString message("%s.isInstance",
-                            NamesOfInterfacesWithProtos(prototypeID));
-    return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, message.get());
+
+  // If the thing we were passed is not an object, return false.
+  if (!args.get(0).isObject()) {
+    args.rval().setBoolean(false);
+    return true;
+  }
+
+  // If "this" isn't a DOM constructor or is a constructor for an interface
+  // without a prototype, return false.
+  if (!args.thisv().isObject()) {
+    args.rval().setBoolean(false);
+    return true;
   }
 
-  if (!args[0].isObject()) {
-    nsPrintfCString message("Argument 1 of %s.isInstance",
-                            NamesOfInterfacesWithProtos(prototypeID));
-    return ThrowErrorMessage(cx, MSG_NOT_OBJECT, message.get());
+  JS::Rooted<JSObject*> thisObj(cx, js::CheckedUnwrap(&args.thisv().toObject()));
+  if (!thisObj) {
+    args.rval().setBoolean(false);
+    return true;
+  }
+
+  const js::Class* thisClass = js::GetObjectClass(thisObj);
+  if (!IsDOMIfaceAndProtoClass(thisClass)) {
+    args.rval().setBoolean(false);
+    return true;
+  }
+
+  const DOMIfaceAndProtoJSClass* clasp =
+    DOMIfaceAndProtoJSClass::FromJSClass(thisClass);
+
+  if (clasp->mType != eInterface ||
+      clasp->mPrototypeID == prototypes::id::_ID_Count) {
+    args.rval().setBoolean(false);
+    return true;
   }
 
   JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
-
   const DOMJSClass* domClass =
     GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
 
-  if (domClass && domClass->mInterfaceChain[depth] == prototypeID) {
-    args.rval().setBoolean(true);
-    return true;
-  }
-
-  args.rval().setBoolean(false);
+  bool isInstance =
+    domClass &&
+    domClass->mInterfaceChain[clasp->mDepth] == clasp->mPrototypeID;
+
+  args.rval().setBoolean(isInstance);
   return true;
 }
 
 bool
 ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj)
 {
   JS::Rooted<JSObject*> rootedObj(cx, obj);
   GlobalObject global(cx, rootedObj);
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2704,18 +2704,17 @@ InterfaceHasInstance(JSContext* cx, unsi
 
 bool
 InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
                      JS::Handle<JSObject*> instance,
                      bool* bp);
 
 // Used to implement the cross-context <Interface>.isInstance static method.
 bool
-InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp,
-                    prototypes::ID prototypeID, int depth);
+InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp);
 
 // Helper for lenient getters/setters to report to console.  If this
 // returns false, we couldn't even get a global.
 bool
 ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj);
 
 // Given a JSObject* that represents the chrome side of a JS-implemented WebIDL
 // interface, get the nsIGlobalObject corresponding to the content side, if any.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1767,33 +1767,16 @@ class CGClassObjectMovedHook(CGAbstractC
 
 
 def JSNativeArguments():
     return [Argument('JSContext*', 'cx'),
             Argument('unsigned', 'argc'),
             Argument('JS::Value*', 'vp')]
 
 
-class CGIsInstanceMethod(CGAbstractStaticMethod):
-    """
-    A class for generating the static isInstance method.
-    """
-    def __init__(self, descriptor):
-        assert descriptor.interface.hasInterfacePrototypeObject()
-        CGAbstractStaticMethod.__init__(self, descriptor, "isInstance", "bool",
-                                        JSNativeArguments())
-
-    def definition_body(self):
-        return fill(
-            """
-            return InterfaceIsInstance(cx, argc, vp, prototypes::id::${name},
-                                       PrototypeTraits<prototypes::id::${name}>::Depth);
-            """,
-            name=self.descriptor.name)
-
 class CGClassConstructor(CGAbstractStaticMethod):
     """
     JS-visible constructor for our objects
     """
     def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
         CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool',
                                         JSNativeArguments())
         self._ctor = ctor
@@ -2331,28 +2314,16 @@ class MethodDefiner(PropertyDefiner):
             if m.isStatic():
                 method["nativeName"] = CppKeywords.checkMethodName(IDLToCIdentifier(m.identifier.name))
 
             if isChromeOnly(m):
                 self.chrome.append(method)
             else:
                 self.regular.append(method)
 
-        # Generate the isInstance static method.
-        if (static and
-            (self.descriptor.interface.hasInterfaceObject() and
-             self.descriptor.interface.hasInterfacePrototypeObject())):
-            self.chrome.append({
-                "name": "isInstance",
-                "methodInfo": False,
-                "length": 1,
-                "flags": "JSPROP_ENUMERATE",
-                "condition": MemberCondition(),
-            })
-
         # TODO: Once iterable is implemented, use tiebreak rules instead of
         # failing. Also, may be more tiebreak rules to implement once spec bug
         # is resolved.
         # https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592
         def hasIterator(methods, regular):
             return (any("@@iterator" in m.aliases for m in methods) or
                     any("@@iterator" == r["name"] for r in regular))
 
@@ -2727,17 +2698,16 @@ class ConstDefiner(PropertyDefiner):
             lambda fields: '  { "%s", %s }' % fields,
             '  { 0, JS::UndefinedValue() }',
             'ConstantSpec',
             PropertyDefiner.getControllingCondition, specData)
 
 
 class PropertyArrays():
     def __init__(self, descriptor):
-        self.descriptor = descriptor
         self.staticMethods = MethodDefiner(descriptor, "StaticMethods",
                                            static=True)
         self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes",
                                        static=True)
         self.methods = MethodDefiner(descriptor, "Methods", static=False)
         self.attrs = AttrDefiner(descriptor, "Attributes", static=False)
         self.unforgeableMethods = MethodDefiner(descriptor, "UnforgeableMethods",
                                                 static=False, unforgeable=True)
@@ -2746,21 +2716,17 @@ class PropertyArrays():
         self.consts = ConstDefiner(descriptor, "Constants")
 
     @staticmethod
     def arrayNames():
         return ["staticMethods", "staticAttrs", "methods", "attrs",
                 "unforgeableMethods", "unforgeableAttrs", "consts"]
 
     def hasChromeOnly(self):
-        # All interfaces that generate an interface object and interface
-        # prototype object have a chrome only isInstance static method.
-        return ((self.staticMethods.descriptor.interface.hasInterfaceObject() and
-                 self.staticMethods.descriptor.interface.hasInterfacePrototypeObject()) or
-                any(getattr(self, a).hasChromeOnly() for a in self.arrayNames()))
+        return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
 
     def hasNonChromeOnly(self):
         return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())
 
     def __str__(self):
         define = ""
         for array in self.arrayNames():
             define += str(getattr(self, array))
@@ -3074,17 +3040,17 @@ class CGCreateInterfaceObjectsMethod(CGA
             constructorProto = "nullptr"
 
         isGlobal = self.descriptor.isGlobal() is not None
         if self.properties.hasNonChromeOnly():
             properties = "sNativeProperties.Upcast()"
         else:
             properties = "nullptr"
         if self.properties.hasChromeOnly():
-            chromeProperties = "nsContentUtils::ThreadsafeIsSystemCaller(aCx) ? sChromeOnlyNativeProperties.Upcast() : nullptr"
+            chromeProperties = "sChromeOnlyNativeProperties.Upcast()"
         else:
             chromeProperties = "nullptr"
 
         call = fill(
             """
             JS::Heap<JSObject*>* protoCache = ${protoCache};
             JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
             dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
@@ -12316,19 +12282,16 @@ class CGDescriptor(CGThing):
                                       toBindingNamespace(descriptor.parentPrototypeName)))
 
         jsonifierMethod = None
         crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set()
         unscopableNames = list()
         for n in descriptor.interface.namedConstructors:
             cgThings.append(CGClassConstructor(descriptor, n,
                                                NamedConstructorName(n)))
-        if (descriptor.interface.hasInterfaceObject() and
-            descriptor.interface.hasInterfacePrototypeObject()):
-            cgThings.append(CGIsInstanceMethod(descriptor))
         for m in descriptor.interface.members:
             if m.isMethod() and m.identifier.name == 'QueryInterface':
                 continue
 
             props = memberProperties(m, descriptor)
 
             if m.isMethod():
                 if m.getExtendedAttribute("Unscopable"):
@@ -13864,23 +13827,19 @@ class CGBindingRoot(CGThing):
 
             return (any(isChromeOnly(a) or needsContainsHack(a) or
                         needsCallerType(a)
                         for a in desc.interface.members) or
                     desc.interface.getExtendedAttribute("ChromeOnly") is not None or
                     # JS-implemented interfaces with an interface object get a
                     # chromeonly _create method.  And interfaces with an
                     # interface object might have a ChromeOnly constructor.
-                    # Also interfaces whose interface prototype object is
-                    # generated (which is most of them) for the isInstance
-                    # method.
                     (desc.interface.hasInterfaceObject() and
                      (desc.interface.isJSImplemented() or
-                      (ctor and isChromeOnly(ctor)) or
-                      desc.interface.hasInterfacePrototypeObject())) or
+                      (ctor and isChromeOnly(ctor)))) or
                     # JS-implemented interfaces with clearable cached
                     # attrs have chromeonly _clearFoo methods.
                     (desc.interface.isJSImplemented() and
                      any(clearableCachedAttrs(desc))))
 
         # XXXkhuey ugly hack but this is going away soon.
         bindingHeaders['xpcprivate.h'] = webIDLFile.endswith("EventTarget.webidl")
 
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/JSEventHandler.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/EventTargetBinding.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/TimelineConsumers.h"
 #include "mozilla/EventTimelineMarker.h"
 #include "mozilla/TimeStamp.h"
 
 #include "EventListenerService.h"
 #include "GeckoProfiler.h"
 #include "nsCOMArray.h"
@@ -287,24 +288,21 @@ EventListenerManager::AddEventListenerIn
   listener->mFlags = aFlags;
   listener->mListenerIsHandler = aHandler;
   listener->mHandlerIsString = false;
   listener->mAllEvents = aAllEvents;
   listener->mIsChrome = mIsMainThreadELM &&
     nsContentUtils::LegacyIsCallerChromeOrNativeCode();
 
   // Detect the type of event listener.
-  nsCOMPtr<nsIXPConnectWrappedJS> wjs;
   if (aFlags.mListenerIsJSListener) {
     MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback());
     listener->mListenerType = Listener::eJSEventListener;
   } else if (aListenerHolder.HasWebIDLCallback()) {
     listener->mListenerType = Listener::eWebIDLListener;
-  } else if ((wjs = do_QueryInterface(aListenerHolder.GetXPCOMCallback()))) {
-    listener->mListenerType = Listener::eWrappedJSListener;
   } else {
     listener->mListenerType = Listener::eNativeListener;
   }
   listener->mListener = Move(aListenerHolder);
 
 
   if (aFlags.mInSystemGroup) {
     mMayHaveSystemGroupListeners = true;
@@ -1567,26 +1565,38 @@ EventListenerManager::GetListenerInfo(ns
     nsAutoString eventType;
     if (listener.mAllEvents) {
       eventType.SetIsVoid(true);
     } else if (listener.mListenerType == Listener::eNoListener) {
       continue;
     } else {
       eventType.Assign(Substring(nsDependentAtomString(listener.mTypeAtom), 2));
     }
-    nsCOMPtr<nsIDOMEventListener> callback = listener.mListener.ToXPCOMCallback();
-    if (!callback) {
-      // This will be null for cross-compartment event listeners which have been
-      // destroyed.
-      continue;
+
+    JS::Rooted<JSObject*> callback(RootingCx());
+    if (JSEventHandler* handler = listener.GetJSEventHandler()) {
+      if (handler->GetTypedEventHandler().HasEventHandler()) {
+        callback = handler->GetTypedEventHandler().Ptr()->CallableOrNull();
+        if (!callback) {
+          // This will be null for cross-compartment event listeners
+          // which have been destroyed.
+          continue;
+        }
+      }
+    } else if (listener.mListenerType == Listener::eWebIDLListener) {
+      callback = listener.mListener.GetWebIDLCallback()->CallbackOrNull();
+      if (!callback) {
+        // This will be null for cross-compartment event listeners
+        // which have been destroyed.
+        continue;
+      }
     }
-    // EventListenerInfo is defined in XPCOM, so we have to go ahead
-    // and convert to an XPCOM callback here...
+
     RefPtr<EventListenerInfo> info =
-      new EventListenerInfo(eventType, callback.forget(),
+      new EventListenerInfo(eventType, callback,
                             listener.mFlags.mCapture,
                             listener.mFlags.mAllowUntrustedEvents,
                             listener.mFlags.mInSystemGroup);
     aList->AppendElement(info.forget());
   }
   return NS_OK;
 }
 
@@ -1710,18 +1720,16 @@ EventListenerManager::MarkForCC()
     const Listener& listener = mListeners.ElementAt(i);
     JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
     if (jsEventHandler) {
       const TypedEventHandler& typedHandler =
         jsEventHandler->GetTypedEventHandler();
       if (typedHandler.HasEventHandler()) {
         typedHandler.Ptr()->MarkForCC();
       }
-    } else if (listener.mListenerType == Listener::eWrappedJSListener) {
-      xpc_TryUnmarkWrappedGrayObject(listener.mListener.GetXPCOMCallback());
     } else if (listener.mListenerType == Listener::eWebIDLListener) {
       listener.mListener.GetWebIDLCallback()->MarkForCC();
     }
   }
   if (mRefCnt.IsPurple()) {
     mRefCnt.RemovePurple();
   }
 }
--- a/dom/events/EventListenerManager.h
+++ b/dom/events/EventListenerManager.h
@@ -184,20 +184,23 @@ public:
   {
     EventListenerHolder mListener;
     RefPtr<nsAtom> mTypeAtom; // for the main thread
     nsString mTypeString; // for non-main-threads
     EventMessage mEventMessage;
 
     enum ListenerType : uint8_t
     {
+      // No listener.
       eNoListener,
+      // A generic C++ implementation of nsIDOMEventListener.
       eNativeListener,
+      // An event handler attribute using JSEventHandler.
       eJSEventListener,
-      eWrappedJSListener,
+      // A scripted EventListener.
       eWebIDLListener,
     };
     ListenerType mListenerType;
 
     bool mListenerIsHandler : 1;
     bool mHandlerIsString : 1;
     bool mAllEvents : 1;
     bool mIsChrome : 1;
--- a/dom/events/EventListenerService.cpp
+++ b/dom/events/EventListenerService.cpp
@@ -75,17 +75,47 @@ EventListenerChange::GetCountOfEventList
 
   return NS_OK;
 }
 
 /******************************************************************************
  * mozilla::EventListenerInfo
  ******************************************************************************/
 
-NS_IMPL_CYCLE_COLLECTION(EventListenerInfo, mListener)
+EventListenerInfo::EventListenerInfo(const nsAString& aType,
+                                     JS::Handle<JSObject*> aScriptedListener,
+                                     bool aCapturing,
+                                     bool aAllowsUntrusted,
+                                     bool aInSystemEventGroup)
+  : mType(aType)
+  , mScriptedListener(aScriptedListener)
+  , mCapturing(aCapturing)
+  , mAllowsUntrusted(aAllowsUntrusted)
+  , mInSystemEventGroup(aInSystemEventGroup)
+{
+  HoldJSObjects(this);
+}
+
+EventListenerInfo::~EventListenerInfo()
+{
+  DropJSObjects(this);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerInfo)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerInfo)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerInfo)
+  tmp->mScriptedListener = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(EventListenerInfo)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptedListener)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventListenerInfo)
   NS_INTERFACE_MAP_ENTRY(nsIEventListenerInfo)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerInfo)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerInfo)
@@ -133,38 +163,23 @@ EventListenerInfo::GetListenerObject(JSC
 
 NS_IMPL_ISUPPORTS(EventListenerService, nsIEventListenerService)
 
 bool
 EventListenerInfo::GetJSVal(JSContext* aCx,
                             Maybe<JSAutoCompartment>& aAc,
                             JS::MutableHandle<JS::Value> aJSVal)
 {
-  aJSVal.setNull();
-  nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = do_QueryInterface(mListener);
-  if (wrappedJS) {
-    JS::Rooted<JSObject*> object(aCx, wrappedJS->GetJSObject());
-    if (!object) {
-      return false;
-    }
-    aAc.emplace(aCx, object);
-    aJSVal.setObject(*object);
+  if (mScriptedListener) {
+    aJSVal.setObject(*mScriptedListener);
+    aAc.emplace(aCx, mScriptedListener);
     return true;
   }
 
-  nsCOMPtr<JSEventHandler> jsHandler = do_QueryInterface(mListener);
-  if (jsHandler && jsHandler->GetTypedEventHandler().HasEventHandler()) {
-    JS::Handle<JSObject*> handler =
-      jsHandler->GetTypedEventHandler().Ptr()->CallableOrNull();
-    if (handler) {
-      aAc.emplace(aCx, handler);
-      aJSVal.setObject(*handler);
-      return true;
-    }
-  }
+  aJSVal.setNull();
   return false;
 }
 
 NS_IMETHODIMP
 EventListenerInfo::ToSource(nsAString& aResult)
 {
   aResult.SetIsVoid(true);
 
--- a/dom/events/EventListenerService.h
+++ b/dom/events/EventListenerService.h
@@ -43,42 +43,34 @@ protected:
   nsCOMPtr<dom::EventTarget> mTarget;
   nsTArray<RefPtr<nsAtom>> mChangedListenerNames;
 };
 
 class EventListenerInfo final : public nsIEventListenerInfo
 {
 public:
   EventListenerInfo(const nsAString& aType,
-                    already_AddRefed<nsIDOMEventListener> aListener,
+                    JS::Handle<JSObject*> aScriptedListener,
                     bool aCapturing,
                     bool aAllowsUntrusted,
-                    bool aInSystemEventGroup)
-    : mType(aType)
-    , mListener(aListener)
-    , mCapturing(aCapturing)
-    , mAllowsUntrusted(aAllowsUntrusted)
-    , mInSystemEventGroup(aInSystemEventGroup)
-  {
-  }
+                    bool aInSystemEventGroup);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(EventListenerInfo)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(EventListenerInfo)
   NS_DECL_NSIEVENTLISTENERINFO
 
 protected:
-  virtual ~EventListenerInfo() {}
+ virtual ~EventListenerInfo();
 
   bool GetJSVal(JSContext* aCx,
                 Maybe<JSAutoCompartment>& aAc,
                 JS::MutableHandle<JS::Value> aJSVal);
 
   nsString mType;
-  // nsReftPtr because that is what nsListenerStruct uses too.
-  RefPtr<nsIDOMEventListener> mListener;
+  JS::Heap<JSObject*> mScriptedListener;  // May be null.
   bool mCapturing;
   bool mAllowsUntrusted;
   bool mInSystemEventGroup;
 };
 
 class EventListenerService final : public nsIEventListenerService
 {
   ~EventListenerService();
--- a/dom/interfaces/events/nsIDOMEventListener.idl
+++ b/dom/interfaces/events/nsIDOMEventListener.idl
@@ -8,17 +8,17 @@
 /**
  * The nsIDOMEventListener interface is a callback interface for
  * listening to events in the Document Object Model.
  *
  * For more information on this interface please see 
  * http://www.w3.org/TR/DOM-Level-2-Events/
  */
 
-[scriptable, function, uuid(df31c120-ded6-11d1-bd85-00805f8ae3f4)]
+[uuid(df31c120-ded6-11d1-bd85-00805f8ae3f4)]
 interface nsIDOMEventListener : nsISupports
 {
   /**
    * This method is called whenever an event occurs of the type for which 
    * the EventListener interface was registered.
    *
    * @param   evt The Event contains contextual information about the 
    *              event. It also contains the stopPropagation and 
--- a/dom/webidl/LegacyQueryInterface.webidl
+++ b/dom/webidl/LegacyQueryInterface.webidl
@@ -12,29 +12,19 @@ interface IID;
  // that are exposed in workers.
  Exposed=(Window,Worker,System)]
 interface LegacyQueryInterface {
   // Legacy QueryInterface, only exposed to chrome code on the main thread.
   [Exposed=(Window,System), ChromeOnly]
   nsISupports QueryInterface(IID iid);
 };
 
-Attr implements LegacyQueryInterface;
 BoxObject implements LegacyQueryInterface;
-ChildProcessMessageManager implements LegacyQueryInterface;
-ChromeMessageBroadcaster implements LegacyQueryInterface;
-ChromeMessageSender implements LegacyQueryInterface;
-Comment implements LegacyQueryInterface;
-ContentFrameMessageManager implements LegacyQueryInterface;
-ContentProcessMessageManager implements LegacyQueryInterface;
 DOMParser implements LegacyQueryInterface;
 Document implements LegacyQueryInterface;
 DocumentFragment implements LegacyQueryInterface;
-DocumentType implements LegacyQueryInterface;
 Element implements LegacyQueryInterface;
 Event implements LegacyQueryInterface;
-ProcessingInstruction implements LegacyQueryInterface;
 Selection implements LegacyQueryInterface;
-Text implements LegacyQueryInterface;
 TreeColumns implements LegacyQueryInterface;
 TreeContentView implements LegacyQueryInterface;
 Window implements LegacyQueryInterface;
 XMLHttpRequest implements LegacyQueryInterface;
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -550,32 +550,32 @@ mozilla::ipc::IPCResult
 CompositorBridgeParent::RecvWaitOnTransactionProcessed()
 {
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 CompositorBridgeParent::RecvFlushRendering()
 {
-  if (mOptions.UseWebRender()) {
+  if (mWrBridge) {
     mWrBridge->FlushRendering();
     return IPC_OK();
   }
 
   if (mCompositorScheduler->NeedsComposite()) {
     CancelCurrentCompositeTask();
     ForceComposeToTarget(nullptr);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 CompositorBridgeParent::RecvFlushRenderingAsync()
 {
-  if (mOptions.UseWebRender()) {
+  if (mWrBridge) {
     mWrBridge->FlushRenderingAsync();
     return IPC_OK();
   }
 
   return RecvFlushRendering();
 }
 
 mozilla::ipc::IPCResult
@@ -693,19 +693,19 @@ CompositorBridgeParent::PauseComposition
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
              "PauseComposition() can only be called on the compositor thread");
 
   MonitorAutoLock lock(mPauseCompositionMonitor);
 
   if (!mPaused) {
     mPaused = true;
 
-    if (!mOptions.UseWebRender()) {
+    if (mCompositor) {
       mCompositor->Pause();
-    } else {
+    } else if (mWrBridge) {
       mWrBridge->Pause();
     }
     TimeStamp now = TimeStamp::Now();
     DidComposite(now, now);
   }
 
   // if anyone's waiting to make sure that composition really got paused, tell them
   lock.NotifyAll();
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -101,17 +101,18 @@ const char* const XPCJSRuntime::mStrings
     "undefined",            // IDX_UNDEFINED
     "",                     // IDX_EMPTYSTRING
     "fileName",             // IDX_FILENAME
     "lineNumber",           // IDX_LINENUMBER
     "columnNumber",         // IDX_COLUMNNUMBER
     "stack",                // IDX_STACK
     "message",              // IDX_MESSAGE
     "lastIndex",            // IDX_LASTINDEX
-    "then"                  // IDX_THEN
+    "then",                 // IDX_THEN
+    "isInstance",           // IDX_ISINSTANCE
 };
 
 /***************************************************************************/
 
 // *Some* NativeSets are referenced from mClassInfo2NativeSetMap.
 // *All* NativeSets are referenced from mNativeSetMap.
 // So, in mClassInfo2NativeSetMap we just clear references to the unmarked.
 // In mNativeSetMap we clear the references to the unmarked *and* delete them.
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -457,16 +457,17 @@ public:
         IDX_EMPTYSTRING             ,
         IDX_FILENAME                ,
         IDX_LINENUMBER              ,
         IDX_COLUMNNUMBER            ,
         IDX_STACK                   ,
         IDX_MESSAGE                 ,
         IDX_LASTINDEX               ,
         IDX_THEN                    ,
+        IDX_ISINSTANCE              ,
         IDX_TOTAL_COUNT // just a count of the above
     };
 
     inline JS::HandleId GetStringID(unsigned index) const;
     inline const char* GetStringName(unsigned index) const;
 
     ShortLivedStringBuffer<nsString> mScratchStrings;
     ShortLivedStringBuffer<nsCString> mScratchCStrings;
--- a/js/xpconnect/tests/unit/test_xpcomutils.js
+++ b/js/xpconnect/tests/unit/test_xpcomutils.js
@@ -14,32 +14,32 @@ ChromeUtils.import("resource://gre/modul
 ////////////////////////////////////////////////////////////////////////////////
 //// Tests
 
 add_test(function test_generateQI_string_names()
 {
     var x = {
         QueryInterface: XPCOMUtils.generateQI([
             Ci.nsIClassInfo,
-            "nsIDOMNode"
+            "nsIObserver"
         ])
     };
 
     try {
         x.QueryInterface(Ci.nsIClassInfo);
     } catch(e) {
         do_throw("Should QI to nsIClassInfo");
     }
     try {
-        x.QueryInterface(Ci.nsIDOMNode);
+        x.QueryInterface(Ci.nsIObserver);
     } catch(e) {
-        do_throw("Should QI to nsIDOMNode");
+        do_throw("Should QI to nsIObserver");
     }
     try {
-        x.QueryInterface(Ci.nsIDOMDocument);
+        x.QueryInterface(Ci.nsIObserverService);
         do_throw("QI should not have succeeded!");
     } catch(e) {}
     run_next_test();
 });
 
 
 add_test(function test_generateCI()
 {
--- a/media/libaom/moz.build
+++ b/media/libaom/moz.build
@@ -39,16 +39,17 @@ elif CONFIG['CPU_ARCH'] == 'x86':
         if CONFIG['CC_TYPE'] == 'gcc':
             ASFLAGS += [ '-I%s/media/libaom/config/win/mingw32/' % TOPSRCDIR ]
             LOCAL_INCLUDES += [ '/media/libaom/config/win/mingw32/' ]
             EXPORTS.aom += [ 'config/win/mingw32/aom_config.h' ]
         else:
             ASFLAGS += [ '-I%s/media/libaom/config/win/ia32/' % TOPSRCDIR ]
             LOCAL_INCLUDES += [ '/media/libaom/config/win/ia32/' ]
             EXPORTS.aom += [ 'config/win/ia32/aom_config.h' ]
+            NO_PGO = True # Compiler OOMs, bug 1445922
     else: # Android, Linux, BSDs, etc.
         ASFLAGS += [ '-I%s/media/libaom/config/linux/ia32/' % TOPSRCDIR ]
         LOCAL_INCLUDES += [ '/media/libaom/config/linux/ia32/' ]
         EXPORTS.aom += [ 'config/linux/ia32/aom_config.h' ]
 elif CONFIG['CPU_ARCH'] == 'arm':
     EXPORTS.aom += files['ARM_EXPORTS']
     ASFLAGS += [
         '-I%s/media/libaom/config/linux/arm/' % TOPSRCDIR,
--- a/mobile/android/chrome/geckoview/GeckoViewPromptContent.js
+++ b/mobile/android/chrome/geckoview/GeckoViewPromptContent.js
@@ -2,14 +2,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/. */
 
 ChromeUtils.import("resource://gre/modules/GeckoViewUtils.jsm");
 
 GeckoViewUtils.addLazyEventListener(this, ["click", "contextmenu"], {
   handler: _ =>
-    Cc["@mozilla.org/prompter;1"].getService(Ci.nsIDOMEventListener),
+    Cc["@mozilla.org/prompter;1"].getService().wrappedJSObject,
   options: {
     capture: false,
     mozSystemGroup: true,
   },
 });
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -58,17 +58,16 @@ function sendEvent(browser, event) {
   browser.dispatchEvent(evt);
 }
 
 
 SessionStore.prototype = {
   classID: Components.ID("{8c1f07d6-cba3-4226-a315-8bd43d67d032}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISessionStore,
-                                         Ci.nsIDOMEventListener,
                                          Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
   _windows: {},
   _lastSaveTime: 0,
   _lastQueuedSaveTime: 0,
   _lastBackupTime: 0,
   _interval: 10000,
--- a/mobile/android/components/geckoview/GeckoViewPrompt.js
+++ b/mobile/android/components/geckoview/GeckoViewPrompt.js
@@ -17,17 +17,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
 function PromptFactory() {
   this.wrappedJSObject = this;
 }
 
 PromptFactory.prototype = {
   classID: Components.ID("{076ac188-23c1-4390-aa08-7ef1f78ca5d9}"),
 
   QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsIDOMEventListener, Ci.nsIPromptFactory, Ci.nsIPromptService]),
+    Ci.nsIPromptFactory, Ci.nsIPromptService]),
 
   handleEvent: function(aEvent) {
     switch (aEvent.type) {
       case "click":
         this._handleClick(aEvent);
         break;
       case "contextmenu":
         this._handleContextMenu(aEvent);
--- a/netwerk/test/httpserver/test/test_default_index_handler.js
+++ b/netwerk/test/httpserver/test/test_default_index_handler.js
@@ -103,53 +103,43 @@ function hiddenDataCheck(bytes, uri, pat
   {
     var doc = parser.parseFromString(data, "application/xml");
   }
   catch (e)
   {
     do_throw("document failed to parse as XML");
   }
 
-  // See all the .QueryInterface()s and .item()s happening here?  That's because
-  // xpcshell sucks and doesn't have classinfo, so no automatic interface
-  // flattening or array-style access to items in NodeLists.  Suck.
-
   var body = doc.documentElement.getElementsByTagName("body");
   Assert.equal(body.length, 1);
-  body = body.item(0);
+  body = body[0];
 
   // header
-  var header = body.QueryInterface(Ci.nsIDOMElement)
-                   .getElementsByTagName("h1");
+  var header = body.getElementsByTagName("h1");
   Assert.equal(header.length, 1);
 
-  Assert.equal(header.item(0).QueryInterface(Ci.nsIDOMNode).textContent, path);
+  Assert.equal(header[0].textContent, path);
 
   // files
   var lst = body.getElementsByTagName("ol");
   Assert.equal(lst.length, 1);
-  var items = lst.item(0).QueryInterface(Ci.nsIDOMElement)
-                         .getElementsByTagName("li");
+  var items = lst[0].getElementsByTagName("li");
 
   var ios = Cc["@mozilla.org/network/io-service;1"]
               .getService(Ci.nsIIOService);
 
   var top = ios.newURI(uri);
 
   // N.B. No ERROR_IF_SEE_THIS.txt^ file!
   var dirEntries = [{name: "file.txt", isDirectory: false},
                     {name: "SHOULD_SEE_THIS.txt^", isDirectory: false}];
 
   for (var i = 0; i < items.length; i++)
   {
-    var link = items.item(i)
-                    .childNodes
-                    .item(0)
-                    .QueryInterface(Ci.nsIDOMNode)
-                    .QueryInterface(Ci.nsIDOMElement);
+    var link = items[i].childNodes[0];
     var f = dirEntries[i];
 
     var sep = f.isDirectory ? "/" : "";
 
     Assert.equal(link.textContent, f.name + sep);
 
     uri = ios.newURI(link.getAttribute("href"), null, top);
     Assert.equal(decodeURIComponent(uri.pathQueryRef), path + f.name + sep);
@@ -187,49 +177,39 @@ function dataCheck(bytes, uri, path, dir
   {
     var doc = parser.parseFromString(data, "application/xml");
   }
   catch (e)
   {
     do_throw("document failed to parse as XML");
   }
 
-  // See all the .QueryInterface()s and .item()s happening here?  That's because
-  // xpcshell sucks and doesn't have classinfo, so no automatic interface
-  // flattening or array-style access to items in NodeLists.  Suck.
-
   var body = doc.documentElement.getElementsByTagName("body");
   Assert.equal(body.length, 1);
-  body = body.item(0);
+  body = body[0];
 
   // header
-  var header = body.QueryInterface(Ci.nsIDOMElement)
-                   .getElementsByTagName("h1");
+  var header = body.getElementsByTagName("h1");
   Assert.equal(header.length, 1);
 
-  Assert.equal(header.item(0).QueryInterface(Ci.nsIDOMNode).textContent, path);
+  Assert.equal(header[0].textContent, path);
 
   // files
   var lst = body.getElementsByTagName("ol");
   Assert.equal(lst.length, 1);
-  var items = lst.item(0).QueryInterface(Ci.nsIDOMElement)
-                         .getElementsByTagName("li");
+  var items = lst[0].getElementsByTagName("li");
 
   var ios = Cc["@mozilla.org/network/io-service;1"]
               .getService(Ci.nsIIOService);
 
   var dirURI = ios.newURI(uri);
 
   for (var i = 0; i < items.length; i++)
   {
-    var link = items.item(i)
-                    .childNodes
-                    .item(0)
-                    .QueryInterface(Ci.nsIDOMNode)
-                    .QueryInterface(Ci.nsIDOMElement);
+    var link = items[i].childNodes[0];
     var f = dirEntries[i];
 
     var sep = f.isDirectory ? "/" : "";
 
     Assert.equal(link.textContent, f.name + sep);
 
     uri = ios.newURI(link.getAttribute("href"), null, top);
     Assert.equal(decodeURIComponent(uri.pathQueryRef), path + f.name + sep);
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -387,17 +387,17 @@ function promiseObserved(topic, test = (
     Services.obs.addObserver(observer, topic);
   });
 }
 
 function getMessageManager(target) {
   if (target.frameLoader) {
     return target.frameLoader.messageManager;
   }
-  return target.QueryInterface(Ci.nsIMessageSender);
+  return target;
 }
 
 function flushJarCache(jarPath) {
   Services.obs.notifyObservers(null, "flush-cache-entry", jarPath);
 }
 
 /**
  * Convert any of several different representations of a date/time to a Date object.
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm
@@ -43,17 +43,16 @@ Services.cpmm.addMessageListener("clearR
 // These mirror signon.* prefs.
 var gEnabled, gAutofillForms, gStoreWhenAutocompleteOff;
 var gLastRightClickTimeStamp = Number.NEGATIVE_INFINITY;
 
 var observer = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                           Ci.nsIFormSubmitObserver,
                                           Ci.nsIWebProgressListener,
-                                          Ci.nsIDOMEventListener,
                                           Ci.nsISupportsWeakReference]),
 
   // nsIFormSubmitObserver
   notify(formElement, aWindow, actionURI) {
     log("observer notified for form submission.");
 
     // We're invoked before the content's |onsubmit| handlers, so we
     // can grab form data before it might be modified (see bug 257781).
--- a/toolkit/components/passwordmgr/test/pwmgr_common.js
+++ b/toolkit/components/passwordmgr/test/pwmgr_common.js
@@ -156,17 +156,17 @@ function registerRunTests() {
       username.name = "testuser";
       form.appendChild(username);
       var password = document.createElement("input");
       password.name = "testpass";
       password.type = "password";
       form.appendChild(password);
 
       var observer = SpecialPowers.wrapCallback(function(subject, topic, data) {
-        var formLikeRoot = subject.QueryInterface(SpecialPowers.Ci.nsIDOMNode);
+        var formLikeRoot = subject;
         if (formLikeRoot.id !== "observerforcer")
           return;
         SpecialPowers.removeObserver(observer, "passwordmgr-processed-form");
         formLikeRoot.remove();
         SimpleTest.executeSoon(() => {
           var runTestEvent = new Event("runTests");
           window.dispatchEvent(runTestEvent);
           resolve();
--- a/toolkit/components/prompts/content/tabprompts.xml
+++ b/toolkit/components/prompts/content/tabprompts.xml
@@ -79,17 +79,17 @@
                     <button anonid="button1" label="&cancelButton.label;"/>
 #endif
                 </hbox>
             </vbox>
         </hbox>
         <spacer flex="2"/>
     </xbl:content>
 
-    <implementation implements="nsIDOMEventListener">
+    <implementation>
         <constructor>
         <![CDATA[
             let self = this;
             function getElement(anonid) {
                 return document.getAnonymousElementByAttribute(self, "anonid", anonid);
             }
 
             this.ui = {
deleted file mode 100644
--- a/toolkit/content/select-child.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-ChromeUtils.defineModuleGetter(this, "SelectContentHelper",
-                               "resource://gre/modules/SelectContentHelper.jsm");
-
-addEventListener("mozshowdropdown", event => {
-  if (!event.isTrusted)
-    return;
-
-  if (!SelectContentHelper.open) {
-    new SelectContentHelper(event.target, {isOpenedViaTouch: false}, this);
-  }
-});
-
-addEventListener("mozshowdropdown-sourcetouch", event => {
-  if (!event.isTrusted)
-    return;
-
-  if (!SelectContentHelper.open) {
-    new SelectContentHelper(event.target, {isOpenedViaTouch: true}, this);
-  }
-});
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -7,17 +7,17 @@
 <bindings id="browserBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <binding id="browser">
     <content clickthrough="never">
       <children/>
     </content>
-    <implementation type="application/javascript" implements="nsIObserver, nsIDOMEventListener, nsIBrowser">
+    <implementation type="application/javascript" implements="nsIObserver, nsIBrowser">
       <property name="autoscrollEnabled">
         <getter>
           <![CDATA[
             if (this.getAttribute("autoscroll") == "false")
               return false;
 
             return this.mPrefs.getBoolPref("general.autoScroll", true);
           ]]>
--- a/toolkit/content/widgets/button.xml
+++ b/toolkit/content/widgets/button.xml
@@ -256,17 +256,17 @@
         event.preventDefault();
       ]]>
       </handler>
     </handlers>
   </binding>
 
   <binding id="menu-button-base"
            extends="chrome://global/content/bindings/button.xml#button-base">
-    <implementation implements="nsIDOMEventListener">
+    <implementation>
       <constructor>
         this.init();
       </constructor>
 
       <method name="init">
         <body>
         <![CDATA[
           var btn = document.getAnonymousElementByAttribute(this, "anonid", "button");
--- a/toolkit/content/widgets/colorpicker.xml
+++ b/toolkit/content/widgets/colorpicker.xml
@@ -102,17 +102,17 @@
           <xul:image class="colorpickertile" color="#330033"/>
         </xul:hbox>
       </xul:vbox>
       <!-- Something to take tab focus
       <button style="border : 0px; width: 0px; height: 0px;"/>
       -->
     </content>
 
-    <implementation implements="nsIDOMEventListener">
+    <implementation>
       <property name="color">
         <getter><![CDATA[
           return this.mSelectedCell ? this.mSelectedCell.getAttribute("color") : null;
         ]]></getter>
         <setter><![CDATA[
           if (!val)
             return val;
           var uppercaseVal = val.toUpperCase();
--- a/toolkit/content/widgets/remote-browser.xml
+++ b/toolkit/content/widgets/remote-browser.xml
@@ -6,17 +6,17 @@
 
 <bindings id="firefoxBrowserBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <binding id="remote-browser" extends="chrome://global/content/bindings/browser.xml#browser">
 
     <implementation type="application/javascript"
-                    implements="nsIObserver, nsIDOMEventListener, nsIRemoteBrowser">
+                    implements="nsIObserver, nsIRemoteBrowser">
 
       <field name="_securityUI">null</field>
 
       <property name="securityUI"
                 readonly="true">
         <getter><![CDATA[
           if (!this._securityUI) {
             // Don't attempt to create the remote web progress if the
--- a/toolkit/content/widgets/scrollbox.xml
+++ b/toolkit/content/widgets/scrollbox.xml
@@ -609,17 +609,17 @@
                          xbl:inherits="orient,collapsed=notoverflowing,disabled=scrolledtoend"
                          anonid="scrollbutton-down"
                          onclick="_distanceScroll(event);"
                          onmousedown="if (event.button == 0) _startScroll(1);"
                          onmouseup="if (event.button == 0) _stopScroll();"
                          onmouseover="_continueScroll(1);"
                          onmouseout="_pauseScroll();"/>
     </content>
-    <implementation implements="nsITimerCallback, nsIDOMEventListener">
+    <implementation implements="nsITimerCallback">
       <constructor><![CDATA[
         this._scrollDelay =
           this._prefBranch.getIntPref("toolkit.scrollbox.clickToScroll.scrollDelay",
                                       this._scrollDelay);
       ]]></constructor>
 
       <destructor><![CDATA[
         // Release timer to avoid reference cycles.
--- a/toolkit/content/widgets/tabbox.xml
+++ b/toolkit/content/widgets/tabbox.xml
@@ -12,17 +12,17 @@
   <binding id="tab-base">
     <resources>
       <stylesheet src="chrome://global/skin/tabbox.css"/>
     </resources>
   </binding>
 
   <binding id="tabbox"
            extends="chrome://global/content/bindings/tabbox.xml#tab-base">
-    <implementation implements="nsIDOMEventListener">
+    <implementation>
       <property name="handleCtrlTab">
         <setter>
         <![CDATA[
           this.setAttribute("handleCtrlTab", val);
           return val;
         ]]>
         </setter>
         <getter>
--- a/toolkit/mozapps/extensions/test/browser/browser.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser.ini
@@ -58,16 +58,17 @@ skip-if = os == "linux" && !debug # Bug 
 [browser_checkAddonCompatibility.js]
 [browser_details.js]
 [browser_discovery.js]
 [browser_dragdrop.js]
 skip-if = buildapp == 'mulet'
 [browser_dragdrop_incompat.js]
 [browser_experiments.js]
 [browser_file_xpi_no_process_switch.js]
+skip-if = !debug && ((os == 'linux' && bits == '64') || (os == 'win' && os_version == '6.1')) # Bug 1449071 - disable on Linux64 and Windows 7 due to frequent failures
 [browser_getmorethemes.js]
 [browser_globalwarnings.js]
 [browser_gmpProvider.js]
 skip-if = os == 'linux' && !debug # Bug 1398766
 [browser_inlinesettings_browser.js]
 [browser_installssl.js]
 [browser_langpack_signing.js]
 [browser_legacy.js]