merge fx-team to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 07 Jul 2016 11:39:46 +0200
changeset 384960 44e5098659ef8c0476fb4ff8085025a37e4dc216
parent 384959 b572895ade9e53c485fee34def41378f040a930e (current diff)
parent 384784 c0e2702226bbb18cc6f907e0a6c41e2bc3727e74 (diff)
child 385002 4764b9f8e6d4ef9823237f01ca3901759ce8daeb
push id22385
push userbmo:wpan@mozilla.com
push dateThu, 07 Jul 2016 12:18:17 +0000
reviewersmerge
milestone50.0a1
merge fx-team to mozilla-central a=merge
browser/themes/shared/devedition/urlbar-arrow.png
browser/themes/shared/devedition/urlbar-arrow@2x.png
browser/themes/shared/urlbar-arrow.png
browser/themes/shared/urlbar-arrow@2x.png
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -518,16 +518,20 @@ toolbar:not(#TabsToolbar) > #personal-bo
 #urlbar[pageproxystate="invalid"] > #identity-box > #identity-icon-labels {
   visibility: collapse;
 }
 
 #urlbar[pageproxystate="invalid"] > #identity-box {
   pointer-events: none;
 }
 
+#urlbar[pageproxystate="invalid"] > #identity-box > #notification-popup-box {
+  pointer-events: auto;
+}
+
 #identity-icon-labels {
   max-width: 18em;
 }
 @media (max-width: 700px) {
   #urlbar-container {
     min-width: 45ch;
   }
   #identity-icon-labels {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -709,69 +709,74 @@
                      enablehistory="true"
                      maxrows="10"
                      newlines="stripsurroundingwhitespace"
                      ontextentered="this.handleCommand(param);"
                      ontextreverted="return this.handleRevert();"
                      pageproxystate="invalid"
                      onfocus="document.getElementById('identity-box').style.MozUserFocus= 'normal'"
                      onblur="setTimeout(() => { document.getElementById('identity-box').style.MozUserFocus = ''; }, 0);">
-              <box id="notification-popup-box" hidden="true" align="center">
-                <image id="default-notification-icon" class="notification-anchor-icon" role="button"
-                       aria-label="&urlbar.defaultNotificationAnchor.label;"/>
-                <image id="geo-notification-icon" class="notification-anchor-icon geo-icon" role="button"
-                       aria-label="&urlbar.geolocationNotificationAnchor.label;"/>
-                <image id="addons-notification-icon" class="notification-anchor-icon install-icon" role="button"
-                       aria-label="&urlbar.addonsNotificationAnchor.label;"/>
-                <image id="indexedDB-notification-icon" class="notification-anchor-icon indexedDB-icon" role="button"
-                       aria-label="&urlbar.indexedDBNotificationAnchor.label;"/>
-                <image id="login-fill-notification-icon" class="notification-anchor-icon login-icon" role="button"
-                       aria-label="&urlbar.loginFillNotificationAnchor.label;"/>
-                <image id="password-notification-icon" class="notification-anchor-icon login-icon" role="button"
-                       aria-label="&urlbar.passwordNotificationAnchor.label;"/>
-                <image id="plugins-notification-icon" class="notification-anchor-icon plugin-icon" role="button"
-                       aria-label="&urlbar.pluginsNotificationAnchor.label;"/>
-                <image id="web-notifications-notification-icon" class="notification-anchor-icon desktop-notification-icon" role="button"
-                       aria-label="&urlbar.webNotsNotificationAnchor3.label;"/>
-                <image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon camera-icon" role="button"
-                       aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
-                <image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon camera-icon in-use" role="button"
-                       aria-label="&urlbar.webRTCSharingDevicesNotificationAnchor.label;"/>
-                <image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon microphone-icon" role="button"
-                       aria-label="&urlbar.webRTCShareMicrophoneNotificationAnchor.label;"/>
-                <image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon microphone-icon in-use" role="button"
-                       aria-label="&urlbar.webRTCSharingMicrophoneNotificationAnchor.label;"/>
-                <image id="webRTC-shareScreen-notification-icon" class="notification-anchor-icon screen-icon" role="button"
-                       aria-label="&urlbar.webRTCShareScreenNotificationAnchor.label;"/>
-                <image id="webRTC-sharingScreen-notification-icon" class="notification-anchor-icon screen-icon in-use" role="button"
-                       aria-label="&urlbar.webRTCSharingScreenNotificationAnchor.label;"/>
-                <image id="pointerLock-notification-icon" class="notification-anchor-icon pointerLock-icon" role="button"
-                       aria-label="&urlbar.pointerLockNotificationAnchor.label;"/>
-                <image id="servicesInstall-notification-icon" class="notification-anchor-icon service-icon" role="button"
-                       aria-label="&urlbar.servicesNotificationAnchor.label;"/>
-                <image id="translate-notification-icon" class="notification-anchor-icon translation-icon" role="button"
-                       aria-label="&urlbar.translateNotificationAnchor.label;"/>
-                <image id="translated-notification-icon" class="notification-anchor-icon translation-icon in-use" role="button"
-                       aria-label="&urlbar.translatedNotificationAnchor.label;"/>
-                <image id="eme-notification-icon" class="notification-anchor-icon drm-icon" role="button"
-                       aria-label="&urlbar.emeNotificationAnchor.label;"/>
-              </box>
               <!-- Use onclick instead of normal popup= syntax since the popup
                    code fires onmousedown, and hence eats our favicon drag events.
                    We only add the identity-box button to the tab order when the location bar
                    has focus, otherwise pressing F6 focuses it instead of the location bar -->
               <box id="identity-box" role="button"
                    align="center"
                    aria-label="&urlbar.viewSiteInfo.label;"
                    onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
                    onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
                    ondragstart="gIdentityHandler.onDragStart(event);">
                 <image id="identity-icon"
                        consumeanchor="identity-box"
                        onclick="PageProxyClickHandler(event);"/>
+                <box id="notification-popup-box"
+                     hidden="true"
+                     tooltiptext=""
+                     onmouseover="document.getElementById('identity-icon').classList.add('no-hover');"
+                     onmouseout="document.getElementById('identity-icon').classList.remove('no-hover');"
+                     align="center">
+                  <image id="default-notification-icon" class="notification-anchor-icon" role="button"
+                         aria-label="&urlbar.defaultNotificationAnchor.label;"/>
+                  <image id="geo-notification-icon" class="notification-anchor-icon geo-icon" role="button"
+                         aria-label="&urlbar.geolocationNotificationAnchor.label;"/>
+                  <image id="addons-notification-icon" class="notification-anchor-icon install-icon" role="button"
+                         aria-label="&urlbar.addonsNotificationAnchor.label;"/>
+                  <image id="indexedDB-notification-icon" class="notification-anchor-icon indexedDB-icon" role="button"
+                         aria-label="&urlbar.indexedDBNotificationAnchor.label;"/>
+                  <image id="login-fill-notification-icon" class="notification-anchor-icon login-icon" role="button"
+                         aria-label="&urlbar.loginFillNotificationAnchor.label;"/>
+                  <image id="password-notification-icon" class="notification-anchor-icon login-icon" role="button"
+                         aria-label="&urlbar.passwordNotificationAnchor.label;"/>
+                  <image id="plugins-notification-icon" class="notification-anchor-icon plugin-icon" role="button"
+                         aria-label="&urlbar.pluginsNotificationAnchor.label;"/>
+                  <image id="web-notifications-notification-icon" class="notification-anchor-icon desktop-notification-icon" role="button"
+                         aria-label="&urlbar.webNotsNotificationAnchor3.label;"/>
+                  <image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon camera-icon" role="button"
+                         aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
+                  <image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon camera-icon in-use" role="button"
+                         aria-label="&urlbar.webRTCSharingDevicesNotificationAnchor.label;"/>
+                  <image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon microphone-icon" role="button"
+                         aria-label="&urlbar.webRTCShareMicrophoneNotificationAnchor.label;"/>
+                  <image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon microphone-icon in-use" role="button"
+                         aria-label="&urlbar.webRTCSharingMicrophoneNotificationAnchor.label;"/>
+                  <image id="webRTC-shareScreen-notification-icon" class="notification-anchor-icon screen-icon" role="button"
+                         aria-label="&urlbar.webRTCShareScreenNotificationAnchor.label;"/>
+                  <image id="webRTC-sharingScreen-notification-icon" class="notification-anchor-icon screen-icon in-use" role="button"
+                         aria-label="&urlbar.webRTCSharingScreenNotificationAnchor.label;"/>
+                  <image id="pointerLock-notification-icon" class="notification-anchor-icon pointerLock-icon" role="button"
+                         aria-label="&urlbar.pointerLockNotificationAnchor.label;"/>
+                  <image id="servicesInstall-notification-icon" class="notification-anchor-icon service-icon" role="button"
+                         aria-label="&urlbar.servicesNotificationAnchor.label;"/>
+                  <image id="translate-notification-icon" class="notification-anchor-icon translation-icon" role="button"
+                         aria-label="&urlbar.translateNotificationAnchor.label;"/>
+                  <image id="translated-notification-icon" class="notification-anchor-icon translation-icon in-use" role="button"
+                         aria-label="&urlbar.translatedNotificationAnchor.label;"/>
+                  <image id="eme-notification-icon" class="notification-anchor-icon drm-icon" role="button"
+                         aria-label="&urlbar.emeNotificationAnchor.label;"/>
+                </box>
                 <image id="tracking-protection-icon"/>
                 <image id="connection-icon"/>
                 <hbox id="identity-icon-labels">
                   <label id="identity-icon-label" class="plain" flex="1"/>
                   <label id="identity-icon-country-label" class="plain"/>
                 </hbox>
               </box>
               <box id="urlbar-display-box" align="center">
--- a/browser/base/content/tab-shape.inc.svg
+++ b/browser/base/content/tab-shape.inc.svg
@@ -4,12 +4,8 @@
 
 <svg:clipPath id="tab-curve-clip-path-start" clipPathUnits="objectBoundingBox">
   <svg:path d="m 1,0.0625 0.05,0 0,0.938 -1,0 0,-0.028 C 0.32082458,0.95840561 0.4353096,0.81970962 0.48499998,0.5625 0.51819998,0.3905 0.535,0.0659 1,0.0625 z"/>
 </svg:clipPath>
 
 <svg:clipPath id="tab-curve-clip-path-end" clipPathUnits="objectBoundingBox">
   <svg:path d="m 0,0.0625 -0.05,0 0,0.938 1,0 0,-0.028 C 0.67917542,0.95840561 0.56569036,0.81970962 0.51599998,0.5625 0.48279998,0.3905 0.465,0.0659 0,0.0625 z"/>
 </svg:clipPath>
-
-<svg:clipPath id="tab-hover-clip-path" clipPathUnits="objectBoundingBox">
-  <svg:path d="M 0,0.2 0,1 1,1, 1,0.2 z"/>
-</svg:clipPath>
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1760,22 +1760,22 @@
         </body>
       </method>
 
       <method name="_createBrowser">
         <parameter name="aParams"/>
         <body>
           <![CDATA[
             // Supported parameters:
-            // userContextId, remote, isPreloadBrowser, uriIsAboutBlank
+            // userContextId, remote, isPreloadBrowser, uriIsAboutBlank, permanentKey
 
             const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
             let b = document.createElementNS(NS_XUL, "browser");
-            b.permanentKey = {};
+            b.permanentKey = aParams.permanentKey || {};
             b.setAttribute("type", "content-targetable");
             b.setAttribute("message", "true");
             b.setAttribute("messagemanagergroup", "browsers");
             b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
             b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
 
             if (aParams.userContextId) {
               b.setAttribute("usercontextid", aParams.userContextId);
@@ -1860,22 +1860,26 @@
             // If we open a new tab with the newtab URL in the default
             // userContext, check if there is a preloaded browser ready.
             // Private windows are not included because both the label and the
             // icon for the tab would be set incorrectly (see bug 1195981).
             if (aURI == BROWSER_NEW_TAB_URL &&
                 !aParams.userContextId &&
                 !PrivateBrowsingUtils.isWindowPrivate(window)) {
               browser = this._getPreloadedBrowser();
-              usingPreloadedContent = !!browser;
+              if (browser) {
+                usingPreloadedContent = true;
+                aTab.permanentKey = browser.permanentKey;
+              }
             }
 
             if (!browser) {
               // No preloaded browser found, create one.
-              browser = this._createBrowser({remote: remote,
+              browser = this._createBrowser({permanentKey: aTab.permanentKey,
+                                             remote: remote,
                                              uriIsAboutBlank: uriIsAboutBlank,
                                              userContextId: aParams.userContextId});
             }
 
             let notificationbox = this.getNotificationBox(browser);
             let uniqueId = this._generateUniquePanelID();
             notificationbox.id = uniqueId;
             aTab.linkedPanel = uniqueId;
@@ -2008,16 +2012,17 @@
 
             // If this new tab is owned by another, assert that relationship
             if (aOwner)
               t.owner = aOwner;
 
             var position = this.tabs.length - 1;
             t._tPos = position;
             t.lastAccessed = Date.now();
+            t.permanentKey = {};
             this.tabContainer._setPositionalAttributes();
 
             this.tabContainer.updateVisibility();
 
             // Currently in this incarnation of bug 906076, we are forcing the
             // browser to immediately be linked.  In future incarnations of this
             // bug this will be removed so we can leave the tab in its "lazy"
             // state to be exploited for startup optimization.  Note that for
@@ -4448,16 +4453,17 @@
           if (this.AppConstants.platform == "macosx") {
             els.addSystemEventListener(document, "keypress", this, false);
           }
           window.addEventListener("sizemodechange", this, false);
 
           var uniqueId = this._generateUniquePanelID();
           this.mPanelContainer.childNodes[0].id = uniqueId;
           this.mCurrentTab.linkedPanel = uniqueId;
+          this.mCurrentTab.permanentKey = this.mCurrentBrowser.permanentKey;
           this.mCurrentTab._tPos = 0;
           this.mCurrentTab._fullyOpen = true;
           this.mCurrentTab.lastAccessed = Infinity;
           this.mCurrentTab.cachePosition = 0;
           this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
           this._tabForBrowser.set(this.mCurrentBrowser, this.mCurrentTab);
 
           // set up the shared autoscroll popup
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_2.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_2.js
@@ -233,10 +233,34 @@ var tests = [
       dismissNotification(popup);
     },
     onHidden: function (popup) {
       ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered");
       this.notification.remove();
       ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
       window.locationbar.visible = true;
     }
+  },
+  // Test that dismissed popupnotifications can be opened on about:blank
+  // (where the rest of the identity block is disabled)
+  { id: "Test#11",
+    run: function() {
+      this.oldSelectedTab = gBrowser.selectedTab;
+      gBrowser.selectedTab = gBrowser.addTab("about:blank");
+
+      this.notifyObj = new BasicNotification(this.id);
+      this.notifyObj.anchorID = "geo-notification-icon";
+      this.notifyObj.addOptions({dismissed: true});
+      this.notification = showNotification(this.notifyObj);
+
+      EventUtils.synthesizeMouse(document.getElementById("geo-notification-icon"), 0, 0, {});
+    },
+    onShown: function(popup) {
+      checkPopup(popup, this.notifyObj);
+      dismissNotification(popup);
+    },
+    onHidden: function(popup) {
+      this.notification.remove();
+      gBrowser.removeTab(gBrowser.selectedTab);
+      gBrowser.selectedTab = this.oldSelectedTab;
+    }
   }
 ];
--- a/browser/modules/WindowsPreviewPerTab.jsm
+++ b/browser/modules/WindowsPreviewPerTab.jsm
@@ -42,41 +42,41 @@
  * when this threshold has been crossed.
  */
 this.EXPORTED_SYMBOLS = ["AeroPeek"];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
+Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
-Cu.import("resource://gre/modules/PageThumbs.jsm");
 
 // Pref to enable/disable preview-per-tab
 const TOGGLE_PREF_NAME = "browser.taskbar.previews.enable";
 // Pref to determine the magic auto-disable threshold
 const DISABLE_THRESHOLD_PREF_NAME = "browser.taskbar.previews.max";
 // Pref to control the time in seconds that tab contents live in the cache
 const CACHE_EXPIRATION_TIME_PREF_NAME = "browser.taskbar.previews.cachetime";
 
 const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Various utility properties
 XPCOMUtils.defineLazyServiceGetter(this, "imgTools",
                                    "@mozilla.org/image/tools;1",
                                    "imgITools");
-XPCOMUtils.defineLazyServiceGetter(this, "faviconSvc",
-                                   "@mozilla.org/browser/favicon-service;1",
-                                   "nsIFaviconService");
+XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
+                                  "resource://gre/modules/PageThumbs.jsm");
 
 // nsIURI -> imgIContainer
-function _imageFromURI(doc, uri, privateMode, callback) {
+function _imageFromURI(uri, privateMode, callback) {
   let channel = NetUtil.newChannel({
     uri: uri,
     loadUsingSystemPrincipal: true,
     contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE
   });
 
   try {
     channel.QueryInterface(Ci.nsIPrivateBrowsingChannel);
@@ -89,29 +89,30 @@ function _imageFromURI(doc, uri, private
       return;
     try {
       let out_img = { value: null };
       imgTools.decodeImageData(inputStream, channel.contentType, out_img);
       callback(out_img.value);
     } catch (e) {
       // We failed, so use the default favicon (only if this wasn't the default
       // favicon).
-      let defaultURI = faviconSvc.defaultFavicon;
+      let defaultURI = PlacesUtils.favicons.defaultFavicon;
       if (!defaultURI.equals(uri))
-        _imageFromURI(doc, defaultURI, privateMode, callback);
+        _imageFromURI(defaultURI, privateMode, callback);
     }
   });
 }
 
 // string? -> imgIContainer
-function getFaviconAsImage(doc, iconurl, privateMode, callback) {
-  if (iconurl)
-    _imageFromURI(doc, NetUtil.newURI(iconurl), privateMode, callback);
-  else
-    _imageFromURI(doc, faviconSvc.defaultFavicon, privateMode, callback);
+function getFaviconAsImage(iconurl, privateMode, callback) {
+  if (iconurl) {
+    _imageFromURI(NetUtil.newURI(iconurl), privateMode, callback);
+  } else {
+    _imageFromURI(PlacesUtils.favicons.defaultFavicon, privateMode, callback);
+  }
 }
 
 // Snaps the given rectangle to be pixel-aligned at the given scale
 function snapRectAtScale(r, scale) {
   let x = Math.floor(r.x * scale);
   let y = Math.floor(r.y * scale);
   let width = Math.ceil((r.x + r.width) * scale) - x;
   let height = Math.ceil((r.y + r.height) * scale) - y;
@@ -447,17 +448,16 @@ TabWindow.prototype = {
                   .getInterface(Ci.nsIWebNavigation)
                   .QueryInterface(Ci.nsIDocShell);
     let preview = AeroPeek.taskbar.createTaskbarTabPreview(docShell, controller);
     preview.visible = AeroPeek.enabled;
     preview.active = this.tabbrowser.selectedTab == controller.tab;
     // Grab the default favicon
     getFaviconAsImage(
       null,
-      null,
       PrivateBrowsingUtils.isWindowPrivate(this.win),
       function (img) {
         // It is possible that we've already gotten the real favicon, so make sure
         // we have not set one before setting this default one.
         if (!preview.icon)
           preview.icon = img;
       });
     return preview;
@@ -601,30 +601,51 @@ TabWindow.prototype = {
 
   onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
     if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
       this.invalidateTabPreview(aBrowser);
     }
   },
 
+  directRequestProtocols: new Set([
+    "file", "chrome", "resource", "about"
+  ]),
   onLinkIconAvailable: function (aBrowser, aIconURL) {
-    let self = this;
+    if (!aIconURL) {
+      return;
+    }
+    let tab = this.tabbrowser.getTabForBrowser(aBrowser);
+    let shouldRequestFaviconURL = true;
+    try {
+      urlObject = NetUtil.newURI(aIconURL);
+      shouldRequestFaviconURL =
+        !this.directRequestProtocols.has(urlObject.scheme);
+    } catch (ex) {}
+
+    let requestURL = shouldRequestFaviconURL ?
+      "moz-anno:favicon:" + aIconURL :
+      aIconURL;
+
     getFaviconAsImage(
-      null,
-      aIconURL,
+      requestURL,
       PrivateBrowsingUtils.isWindowPrivate(this.win),
-      function (img) {
-        let index = self.tabbrowser.browsers.indexOf(aBrowser);
-        // Only add it if we've found the index.  The tab could have closed!
+      img => {
+        let index = this.tabbrowser.browsers.indexOf(aBrowser);
+        // Only add it if we've found the index and the URI is still the same.
+        // The tab could have closed, and there's no guarantee the icons
+        // will have finished fetching 'in order'.
         if (index != -1) {
-          let tab = self.tabbrowser.tabs[index];
-          self.previews.get(tab).icon = img;
+          let tab = this.tabbrowser.tabs[index];
+          if (tab.getAttribute("image") == aIconURL) {
+            this.previews.get(tab).icon = img;
+          }
         }
-      });
+      }
+    );
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// AeroPeek
 
 /*
  * This object acts as global storage and external interface for this feature.
@@ -658,16 +679,17 @@ this.AeroPeek = {
     this.taskbar = Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar);
     this.available = this.taskbar.available;
     if (!this.available)
       return;
 
     this.prefs.addObserver(TOGGLE_PREF_NAME, this, false);
     this.prefs.addObserver(DISABLE_THRESHOLD_PREF_NAME, this, false);
     this.prefs.addObserver(CACHE_EXPIRATION_TIME_PREF_NAME, this, false);
+    PlacesUtils.history.addObserver(this, true);
 
     this.cacheLifespan = this.prefs.getIntPref(CACHE_EXPIRATION_TIME_PREF_NAME);
 
     this.maxpreviews = this.prefs.getIntPref(DISABLE_THRESHOLD_PREF_NAME);
 
     this.enabled = this._prefenabled = this.prefs.getBoolPref(TOGGLE_PREF_NAME);
   },
 
@@ -756,17 +778,41 @@ this.AeroPeek = {
         break;
       case "timer-callback":
         this.previews.forEach(function (preview) {
           let controller = preview.controller.wrappedJSObject;
           controller.resetCanvasPreview();
         });
         break;
     }
-  }
+  },
+
+  /* nsINavHistoryObserver implementation */
+  onBeginUpdateBatch() {},
+  onEndUpdateBatch() {},
+  onVisit() {},
+  onTitleChanged() {},
+  onFrecencyChanged() {},
+  onManyFrecenciesChanged() {},
+  onDeleteURI() {},
+  onClearHistory() {},
+  onDeleteVisits() {},
+  onPageChanged(uri, changedConst, newValue) {
+    if (this._enabled && changedConst == Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
+      for (let win of this.windows) {
+        for (let [tab, preview] of win.previews) {
+          if (tab.getAttribute("image") == newValue) {
+            win.onLinkIconAvailable(tab.linkedBrowser, newValue);
+          }
+        }
+      }
+    }
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, Ci.nsINavHistoryObserver]),
 };
 
 XPCOMUtils.defineLazyGetter(AeroPeek, "cacheTimer", () =>
   Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer)
 );
 
 XPCOMUtils.defineLazyServiceGetter(AeroPeek, "prefs",
                                    "@mozilla.org/preferences-service;1",
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -859,16 +859,24 @@ menuitem:not([type]):not(.menuitem-toolt
   -moz-appearance: none;
   -moz-box-align: stretch;
 }
 
 .urlbar-input-box {
   margin-inline-start: 0;
 }
 
+.urlbar-input-box,
+#urlbar-display-box {
+  padding-inline-start: 4px;
+  border-inline-start: 1px solid var(--urlbar-separator-color);
+  border-image: linear-gradient(transparent 15%, var(--urlbar-separator-color) 15%, var(--urlbar-separator-color) 85%, transparent 85%);
+  border-image-slice: 1;
+}
+
 .urlbar-history-dropmarker {
   -moz-appearance: toolbarbutton-dropdown;
   transition: opacity 0.15s ease;
 }
 
 #urlbar-wrapper[switchingtabs] > #urlbar > .urlbar-textbox-container > .urlbar-history-dropmarker {
   transition: none;
 }
@@ -953,18 +961,16 @@ menuitem:not([type]):not(.menuitem-toolt
 
 #urlbar-search-splitter + #search-container > #searchbar > .searchbar-textbox {
   margin-inline-start: 0;
 }
 
 #urlbar-display-box {
   margin-top: -1px;
   margin-bottom: -1px;
-  border-inline-end: 1px solid #AAA;
-  margin-inline-end: 3px;
 }
 
 .urlbar-display {
   margin-top: 0;
   margin-bottom: 0;
   margin-inline-start: 0;
   color: GrayText;
 }
@@ -1006,19 +1012,16 @@ menuitem:not([type]):not(.menuitem-toolt
   max-width: 28em;
 }
 
 .addon-install-confirmation-name {
   font-weight: bold;
 }
 
 /* Notification icon box */
-#notification-popup-box {
-  border-radius: 2.5px 0 0 2.5px;
-}
 
 .notification-anchor-icon:-moz-focusring {
   outline: 1px dotted -moz-DialogText;
 }
 
 /* Translation infobar */
 
 %include ../shared/translation/infobar.inc.css
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -1571,20 +1571,16 @@ toolbar .toolbarbutton-1 > .toolbarbutto
 
 @media (-moz-mac-yosemite-theme) {
   #urlbar:not([focused="true"]):not(:-moz-window-inactive) > #identity-box {
     margin-top: -2px;
     margin-bottom: -2px;
     padding-top: 4px;
     padding-bottom: 4px;
   }
-  #identity-box {
-    --identity-box-hover-background-color: rgb(240,237,237);
-    --identity-box-selected-background-color: rgb(220,217,217);
-  }
 }
 
 #identity-box:-moz-locale-dir(ltr) {
   border-top-left-radius: 2px;
   border-bottom-left-radius: 2px;
 }
 
 #identity-box:-moz-locale-dir(rtl) {
@@ -1599,16 +1595,24 @@ toolbar .toolbarbutton-1 > .toolbarbutto
   padding-inline-end: 5px;
 }
 
 .urlbar-input-box {
   margin-inline-start: 0;
   padding: 3px 0 2px;
 }
 
+.urlbar-input-box,
+#urlbar-display-box {
+  padding-inline-start: 4px;
+  border-inline-start: 1px solid var(--urlbar-separator-color);
+  border-image: linear-gradient(transparent 15%, var(--urlbar-separator-color) 15%, var(--urlbar-separator-color) 85%, transparent 85%);
+  border-image-slice: 1;
+}
+
 .urlbar-history-dropmarker {
   padding: 0 3px;
   list-style-image: var(--urlbar-dropmarker-url);
   -moz-image-region: var(--urlbar-dropmarker-region);
   transition: opacity 0.15s ease;
 }
 
 #urlbar-wrapper[switchingtabs] > #urlbar > .urlbar-textbox-container > .urlbar-history-dropmarker {
@@ -1684,21 +1688,16 @@ toolbar .toolbarbutton-1 > .toolbarbutto
 #search-container {
   min-width: calc(54px + 11ch);
 }
 
 #wrapper-urlbar-container[place="palette"] {
   max-width: 20em;
 }
 
-#urlbar-display-box {
-  border-inline-end: 1px solid #AAA;
-  margin-inline-end: 3px;
-}
-
 .urlbar-display {
   margin-top: 0;
   margin-bottom: 0;
   color: GrayText;
 }
 
 %include ../shared/urlbarSearchSuggestionsNotification.inc.css
 
@@ -2588,26 +2587,16 @@ toolbarbutton.chevron > .toolbarbutton-m
 #tabbrowser-tabs {
   -moz-box-align: stretch;
 }
 
 .tabs-newtab-button > .toolbarbutton-icon {
   padding: 6px 0 4px;
 }
 
-/* Background tabs:
- *
- * Decrease the height of the hoverable region of background tabs whenever the tabs are at the top
- * of the window (e.g. no menubar, tabs in titlebar, etc.) to make it easier to drag the window by
- * the titlebar. We don't need this in fullscreen since window dragging is not an issue there.
- */
-#main-window[tabsintitlebar]:not([inFullscreen]) .tab-background-middle:not([visuallyselected=true]) {
-  clip-path: url(chrome://browser/content/browser.xul#tab-hover-clip-path);
-}
-
 /**
  * Tab Drag and Drop
  */
 
 .tab-drop-indicator {
   list-style-image: url(chrome://browser/skin/tabbrowser/tabDragIndicator.png);
   margin-top: -2px;
   z-index: 3;
@@ -2971,20 +2960,16 @@ menuitem:hover > hbox > .alltabs-endimag
   list-style-image: none;
   height: 2px;
   margin-inline-end: -4em;
   background-color: Highlight;
 }
 
 %include ../shared/notification-icons.inc.css
 
-#notification-popup-box {
-  border-radius: 2px 0 0 2px;
-}
-
 .notification-anchor-icon:-moz-focusring {
   box-shadow: 0 0 2px 1px -moz-mac-focusring inset,
               0 0 3px 2px -moz-mac-focusring;
 }
 
 #social-notification-icon > .toolbarbutton-icon {
   height: 16px;
 }
--- a/browser/themes/osx/tabbrowser/tab-selected-end-yosemite-inactive.svg
+++ b/browser/themes/osx/tabbrowser/tab-selected-end-yosemite-inactive.svg
@@ -11,18 +11,14 @@
         height: 100%;
         width: 100%;
       }
     </style>
 
     <clipPath id="tab-curve-clip-path-end" clipPathUnits="objectBoundingBox">
       <path d="m 0,0.0625 -0.05,0 0,0.938 1,0 0,-0.028 C 0.67917542,0.95840561 0.56569036,0.81970962 0.51599998,0.5625 0.48279998,0.3905 0.465,0.0659 0,0.0625 z"/>
     </clipPath>
-
-    <clipPath id="tab-hover-clip-path" clipPathUnits="objectBoundingBox">
-      <path d="M 0,0.2 0,1 1,1, 1,0.2 z"/>
-    </clipPath>
   </defs>
 
   <foreignObject width="30" height="31" clip-path="url(#tab-curve-clip-path-end)">
     <div id="tab-background-fill" xmlns="http://www.w3.org/1999/xhtml"></div>
   </foreignObject>
 </svg>
--- a/browser/themes/osx/tabbrowser/tab-selected-start-yosemite-inactive.svg
+++ b/browser/themes/osx/tabbrowser/tab-selected-start-yosemite-inactive.svg
@@ -11,18 +11,14 @@
         height: 100%;
         width: 100%;
       }
     </style>
 
     <clipPath id="tab-curve-clip-path-start" clipPathUnits="objectBoundingBox">
       <path d="m 1,0.0625 0.05,0 0,0.938 -1,0 0,-0.028 C 0.32082458,0.95840561 0.4353096,0.81970962 0.48499998,0.5625 0.51819998,0.3905 0.535,0.0659 1,0.0625 z"/>
     </clipPath>
-
-    <clipPath id="tab-hover-clip-path" clipPathUnits="objectBoundingBox">
-      <path d="M 0,0.2 0,1 1,1, 1,0.2 z"/>
-    </clipPath>
   </defs>
 
   <foreignObject width="30" height="31" clip-path="url(#tab-curve-clip-path-start)">
     <div id="tab-background-fill" xmlns="http://www.w3.org/1999/xhtml"></div>
   </foreignObject>
 </svg>
--- a/browser/themes/shared/addons/addon-install-anchor.svg
+++ b/browser/themes/shared/addons/addon-install-anchor.svg
@@ -5,23 +5,15 @@
 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
      width="16" height="16" viewBox="0 0 16 16">
   <defs>
     <style>
       use:not(:target) {
         display: none;
       }
       .style-icon-notification {
-        fill: #666;
-      }
-      .style-icon-notification.hover {
-        fill: #808080;
-      }
-      .style-icon-notification.active {
-        fill: #4d4d4d;
+        fill: #999;
       }
     </style>
-    <path id="shape-notifcations-addons" d="M10,15c0.5,0,1-0.4,1-1v-3c0,0,0-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2c0-1.8-0.9-2-1.5-2 c-1.1,0-1.1,0.7-1.8,0.7C11,7.7,11,7,11,7V6c0-0.6-0.5-1-1-1H8c0,0-0.8,0-0.8-0.8C7.2,3.6,8,3.6,8,2.5C8,1.9,7.8,1,6,1 C4.2,1,4,1.9,4,2.5c0,1.1,0.8,1.1,0.8,1.8C4.8,5,4,5,4,5H2C1.5,5,1,5.4,1,6l0,1.5c0,0-0.1,1,1.1,1c0.8,0,0.9-1,1.9-1 C4.5,7.4,5,8,5,9c0,1-0.5,1.6-1,1.6c-1,0-1.1-1.1-1.9-1.1C0.9,9.5,1,10.8,1,10.8V14c0,0.6,0.5,1,1,1l2.6,0c0,0,1.1,0,1.1-1 c0-0.8-1-0.1-1-1.1c0-0.5,0.7-1.2,1.8-1.2s1.8,0.7,1.8,1.2c0,1-1.1,0.3-1.1,1.1c0,1,1.2,1,1.2,1H10z"/>
+    <path id="shape-notifications-addons" d="M10,15c0.5,0,1-0.4,1-1v-3c0,0,0-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2c0-1.8-0.9-2-1.5-2 c-1.1,0-1.1,0.7-1.8,0.7C11,7.7,11,7,11,7V6c0-0.6-0.5-1-1-1H8c0,0-0.8,0-0.8-0.8C7.2,3.6,8,3.6,8,2.5C8,1.9,7.8,1,6,1 C4.2,1,4,1.9,4,2.5c0,1.1,0.8,1.1,0.8,1.8C4.8,5,4,5,4,5H2C1.5,5,1,5.4,1,6l0,1.5c0,0-0.1,1,1.1,1c0.8,0,0.9-1,1.9-1 C4.5,7.4,5,8,5,9c0,1-0.5,1.6-1,1.6c-1,0-1.1-1.1-1.9-1.1C0.9,9.5,1,10.8,1,10.8V14c0,0.6,0.5,1,1,1l2.6,0c0,0,1.1,0,1.1-1 c0-0.8-1-0.1-1-1.1c0-0.5,0.7-1.2,1.8-1.2s1.8,0.7,1.8,1.2c0,1-1.1,0.3-1.1,1.1c0,1,1.2,1,1.2,1H10z"/>
   </defs>
-  <use id="default" xlink:href="#shape-notifcations-addons" class="style-icon-notification"/>
-  <use id="hover" xlink:href="#shape-notifcations-addons" class="style-icon-notification hover"/>
-  <use id="active" xlink:href="#shape-notifcations-addons" class="style-icon-notification active"/>
+  <use id="default" xlink:href="#shape-notifications-addons" class="style-icon-notification"/>
 </svg>
--- a/browser/themes/shared/devedition.inc.css
+++ b/browser/themes/shared/devedition.inc.css
@@ -56,18 +56,16 @@
   --urlbar-dropmarker-2x-region: rect(0px, 11px, 14px, 0px);
   --urlbar-dropmarker-hover-2x-region: rect(0, 22px, 14px, 11px);
   --urlbar-dropmarker-active-2x-region: rect(0px, 33px, 14px, 22px);
 }
 
 :root[devtoolstheme="dark"] #identity-box {
   --identity-box-chrome-color: #46afe3;
   --identity-box-verified-background-color: transparent;
-  --identity-box-hover-background-color: rgba(231,230,230,.2);
-  --identity-box-selected-background-color: rgba(211,210,210,.2);
 }
 
 :root[devtoolstheme="light"] {
   --url-and-searchbar-background-color: #fff;
 
   --chrome-background-color: #E3E4E6;
   --chrome-color: #18191a;
   --chrome-secondary-background-color: #f5f6f7;
@@ -204,33 +202,16 @@ toolbar[brighttext] #downloads-indicator
 }
 
 window:not([chromehidden~="toolbar"]) #urlbar-wrapper {
   overflow: -moz-hidden-unscrollable;
   clip-path: none;
   margin-inline-start: 0;
 }
 
-/* Swap out the white arrow with a dark one for the dark theme */
-:root[devtoolstheme="dark"] #notification-popup-box {
-  border-image: url("chrome://browser/skin/devedition/urlbar-arrow.png") 0 8 0 0 fill;
-}
-
-@media (min-resolution: 1.1dppx) {
-  :root[devtoolstheme="dark"] #notification-popup-box {
-    border-image: url("chrome://browser/skin/devedition/urlbar-arrow@2x.png") 0 16 0 0 fill;
-  }
-}
-
-/* The (white) notification box background color should match the theme */
-#notification-popup-box {
-  border-radius: 0;
-  background-color: var(--url-and-searchbar-background-color);
-}
-
 /* Nav bar specific stuff */
 #nav-bar {
   margin-top: 0 !important;
   border-top: none !important;
   border-bottom: none !important;
   border-radius: 0 !important;
   box-shadow: 0 -1px var(--chrome-nav-bar-separator-color) !important;
 }
deleted file mode 100644
index c14afc780b4a4941865209e2dc99542c383ce56c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index e6867a49898e503367172f98f5b78c7f9ccd17ce..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/shared/error-pages.css
+++ b/browser/themes/shared/error-pages.css
@@ -5,16 +5,21 @@
 @import url("chrome://global/skin/in-content/info-pages.css");
 
 body {
   background-size: 64px 32px;
   background-repeat: repeat-x;
   /* Top padding for when the window height is small.
      Bottom padding to keep everything centered. */
   padding: 75px 0;
+  /* info-pages.css sets a minimum width of 13em to the content
+   * container. If we don't set a min-width here, the content
+   * gets clipped in iframes with small width. We don't accomodate
+   * any padding to prioritize real estate in the small viewport. */
+  min-width: 13em;
 }
 
 .button-container {
   display: flex;
   flex-flow: row wrap;
 }
 
 .button-spacer {
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -1,116 +1,84 @@
 %if 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/. */
 %endif
 
 #identity-box {
-  --identity-box-hover-background-color: rgb(231,230,230);
-  --identity-box-selected-background-color: rgb(211,210,210);
   --identity-box-verified-color: hsl(92,100%,30%);
 %ifdef MOZ_OFFICIAL_BRANDING
   --identity-box-chrome-color: rgb(229,115,0);
 %else
 %if MOZ_UPDATE_CHANNEL == aurora
   --identity-box-chrome-color: rgb(51,30,84);
 %else
   --identity-box-chrome-color: rgb(0,33,71);
 %endif
 %endif
 
-  border-inline-end: 1px solid var(--urlbar-separator-color);
-  border-image: linear-gradient(transparent 15%,
-                                var(--urlbar-separator-color) 15%,
-                                var(--urlbar-separator-color) 85%,
-                                transparent 85%);
-  border-image-slice: 1;
   font-size: .9em;
   padding: 3px 5px;
-  margin-inline-end: 4px;
   overflow: hidden;
   /* The latter two properties have a transition to handle the delayed hiding of
      the forward button when hovered. */
   transition: background-color 150ms ease, padding-left, padding-right;
 }
 
-#identity-box:hover,
-#identity-box[open=true] {
-  border-image-source: none;
-}
-
-#identity-box:hover {
-  background-color: var(--identity-box-hover-background-color);
-}
-
-#identity-box:hover:active,
-#identity-box[open=true] {
-  background-color: var(--identity-box-selected-background-color);
-}
-
 #urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
   color: var(--identity-box-verified-color);
 }
 
 #urlbar[pageproxystate="valid"] > #identity-box.chromeUI {
   color: var(--identity-box-chrome-color);
 }
 
 #identity-icon-labels:-moz-locale-dir(ltr) {
   padding-left: 2px;
 }
 
 #identity-icon-labels:-moz-locale-dir(rtl) {
   padding-right: 2px;
 }
 
-#notification-popup-box:not([hidden]) + #identity-box {
-  padding-inline-start: 10px;
-  border-radius: 0;
-}
-
-@conditionalForwardWithUrlbar@ > #urlbar > #identity-box {
-  border-radius: 0;
-}
-
-@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
+@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #identity-box {
   padding-inline-start: calc(var(--backbutton-urlbar-overlap) + 5px);
 }
 
-@conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
+@conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] + #urlbar > #identity-box {
   /* Forward button hiding is delayed when hovered, so we should use the same
      delay for the identity box. We handle both horizontal paddings (for LTR and
      RTL), the latter two delays here are for padding-left and padding-right. */
   transition-delay: 0s, 100s, 100s;
 }
 
-@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
+@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #identity-box {
   /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
   padding-inline-start: calc(var(--backbutton-urlbar-overlap) + 5.01px);
 }
 
 /* MAIN IDENTITY ICON */
 
 #identity-icon {
   width: 16px;
   height: 16px;
   list-style-image: url(chrome://browser/skin/identity-icon.svg#normal);
 }
 
-#identity-box:hover > #identity-icon,
+#identity-box:hover > #identity-icon:not(.no-hover),
 #identity-box[open=true] > #identity-icon {
   list-style-image: url(chrome://browser/skin/identity-icon.svg#hover);
 }
 
 #identity-box.grantedPermissions > #identity-icon {
   list-style-image: url(chrome://browser/skin/identity-icon.svg#notice);
 }
 
-#identity-box.grantedPermissions:hover > #identity-icon,
+#identity-box.grantedPermissions:hover > #identity-icon:not(.no-hover),
 #identity-box.grantedPermissions[open=true] > #identity-icon {
   list-style-image: url(chrome://browser/skin/identity-icon.svg#notice-hover);
 }
 
 #urlbar[pageproxystate="valid"] > #identity-box.chromeUI > #identity-icon {
   list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
 }
 
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -111,18 +111,16 @@
   skin/classic/browser/translating-16.png                      (../shared/translation/translating-16.png)
   skin/classic/browser/translating-16@2x.png                   (../shared/translation/translating-16@2x.png)
   skin/classic/browser/translation-16.png                      (../shared/translation/translation-16.png)
   skin/classic/browser/translation-16@2x.png                   (../shared/translation/translation-16@2x.png)
   skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
   skin/classic/browser/undoCloseTab@2x.png                     (../shared/undoCloseTab@2x.png)
   skin/classic/browser/update-badge.svg                        (../shared/update-badge.svg)
   skin/classic/browser/update-badge-failed.svg                 (../shared/update-badge-failed.svg)
-  skin/classic/browser/urlbar-arrow.png                        (../shared/urlbar-arrow.png)
-  skin/classic/browser/urlbar-arrow@2x.png                     (../shared/urlbar-arrow@2x.png)
   skin/classic/browser/warning.svg                             (../shared/warning.svg)
   skin/classic/browser/cert-error.svg                          (../shared/incontent-icons/cert-error.svg)
   skin/classic/browser/session-restore.svg                     (../shared/incontent-icons/session-restore.svg)
   skin/classic/browser/tab-crashed.svg                         (../shared/incontent-icons/tab-crashed.svg)
   skin/classic/browser/favicon-search-16.svg                   (../shared/favicon-search-16.svg)
   skin/classic/browser/icon-search-64.svg                      (../shared/incontent-icons/icon-search-64.svg)
   skin/classic/browser/welcome-back.svg                        (../shared/incontent-icons/welcome-back.svg)
   skin/classic/browser/reader-tour.png                         (../shared/reader/reader-tour.png)
@@ -145,16 +143,14 @@
   skin/classic/browser/panic-panel/icons@2x.png                (../shared/panic-panel/icons@2x.png)
   skin/classic/browser/privatebrowsing/aboutPrivateBrowsing.css (../shared/privatebrowsing/aboutPrivateBrowsing.css)
   skin/classic/browser/privatebrowsing/check.svg               (../shared/privatebrowsing/check.svg)
   skin/classic/browser/privatebrowsing/favicon.svg             (../shared/privatebrowsing/favicon.svg)
   skin/classic/browser/privatebrowsing/private-browsing.svg    (../shared/privatebrowsing/private-browsing.svg)
   skin/classic/browser/privatebrowsing/tracking-protection-off.svg (../shared/privatebrowsing/tracking-protection-off.svg)
   skin/classic/browser/privatebrowsing/tracking-protection.svg (../shared/privatebrowsing/tracking-protection.svg)
   skin/classic/browser/devedition/urlbar-history-dropmarker.svg (../shared/devedition/urlbar-history-dropmarker.svg)
-  skin/classic/browser/devedition/urlbar-arrow.png             (../shared/devedition/urlbar-arrow.png)
-  skin/classic/browser/devedition/urlbar-arrow@2x.png          (../shared/devedition/urlbar-arrow@2x.png)
   skin/classic/browser/urlbar-star.svg                         (../shared/urlbar-star.svg)
   skin/classic/browser/urlbar-tab.svg                          (../shared/urlbar-tab.svg)
   skin/classic/browser/usercontext/personal.svg                (../shared/usercontext/personal.svg)
   skin/classic/browser/usercontext/work.svg                    (../shared/usercontext/work.svg)
   skin/classic/browser/usercontext/banking.svg                 (../shared/usercontext/banking.svg)
   skin/classic/browser/usercontext/shopping.svg                (../shared/usercontext/shopping.svg)
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -1,56 +1,28 @@
 %if 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/. */
 %endif
 
 #notification-popup-box {
-  position: relative;
-  background-color: #fff;
-  background-clip: padding-box;
-  padding-left: 3px;
-  border-width: 0 8px 0 0;
-  border-style: solid;
-  border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 fill;
-  margin-inline-end: -8px;
-}
-
-@media (min-resolution: 1.1dppx) {
-  #notification-popup-box {
-    border-image: url("chrome://browser/skin/urlbar-arrow@2x.png") 0 16 0 0 fill;
-  }
-}
-
-@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box {
-  padding-left: calc(var(--backbutton-urlbar-overlap) + 3px);
-}
-
-/* This changes the direction of the main notification box on the url bar. */
-#notification-popup-box:-moz-locale-dir(rtl),
-/* This adds a second flip for the notification anchors, as they don't switch direction
-   for RTL mode. */
-.notification-anchor-icon:-moz-locale-dir(rtl) {
-  transform: scaleX(-1);
-}
-
-/* For the anchor icons in the chat window, we don't have the notification popup box,
-   so we need to cancel the RTL transform. */
-.notification-anchor-icon.chat-toolbarbutton:-moz-locale-dir(rtl) {
-  transform: none;
+  padding: 5px 0px;
+  margin: -5px 0px;
+  margin-inline-end: -5px;
+  padding-inline-end: 5px;
 }
 
 /* This class can be used alone or in combination with the class defining the
    type of icon displayed. This rule must be defined before the others in order
    for its list-style-image to be overridden. */
 .notification-anchor-icon {
   width: 16px;
   height: 16px;
-  margin: 0 2px;
+  margin-inline-start: 2px;
 %ifdef MOZ_WIDGET_GTK
   list-style-image: url(moz-icon://stock/gtk-dialog-info?size=16);
 %else
   list-style-image: url(chrome://global/skin/icons/information-16.png);
 %endif
 }
 
 @media (min-resolution: 1.1dppx) {
@@ -242,24 +214,16 @@
 }
 
 /* INSTALL ADDONS */
 
 .install-icon {
   list-style-image: url(chrome://browser/skin/addons/addon-install-anchor.svg#default);
 }
 
-.install-icon:hover {
-  list-style-image: url(chrome://browser/skin/addons/addon-install-anchor.svg#hover);
-}
-
-.install-icon:hover:active {
-  list-style-image: url(chrome://browser/skin/addons/addon-install-anchor.svg#active);
-}
-
 .popup-notification-icon[popupid="xpinstall-disabled"],
 .popup-notification-icon[popupid="addon-install-blocked"],
 .popup-notification-icon[popupid="addon-install-origin-blocked"] {
   list-style-image: url(chrome://browser/skin/addons/addon-install-blocked.svg);
 }
 
 .popup-notification-icon[popupid="addon-progress"] {
   list-style-image: url(chrome://browser/skin/addons/addon-install-downloading.svg);
@@ -309,24 +273,16 @@
 .plugin-icon.plugin-blocked {
   list-style-image: url(chrome://browser/skin/notification-pluginBlocked.png);
 }
 
 .plugin-icon {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-.plugin-icon:hover {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-
-.plugin-icon:active {
-  -moz-image-region: rect(0, 48px, 16px, 32px);
-}
-
 %ifdef XP_MACOSX
 @media (min-resolution: 1.1dppx) {
   .plugin-icon {
     list-style-image: url(chrome://browser/skin/notification-pluginNormal@2x.png);
   }
 
   .plugin-icon.plugin-hidden {
     list-style-image: url(chrome://browser/skin/notification-pluginAlert@2x.png);
@@ -334,24 +290,16 @@
 
   .plugin-icon.plugin-blocked {
     list-style-image: url(chrome://browser/skin/notification-pluginBlocked@2x.png);
   }
 
   .plugin-icon {
     -moz-image-region: rect(0, 32px, 32px, 0);
   }
-
-  .plugin-icon:hover {
-    -moz-image-region: rect(0, 64px, 32px, 32px);
-  }
-
-  .plugin-icon:active {
-    -moz-image-region: rect(0, 96px, 32px, 64px);
-  }
 }
 %endif
 
 #notification-popup-box[hidden] {
   /* Override display:none to make the pluginBlockedNotification animation work
      when showing the notification repeatedly. */
   display: -moz-box;
   visibility: collapse;
deleted file mode 100644
index ed83d8aac957701a9ad96594c39401a8dc2de50f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 91d9f8d8b3b7b7d3282f0fceec952822b0fce91f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1260,16 +1260,24 @@ html|*.urlbar-input:-moz-lwtheme::-moz-p
 .urlbar-textbox-container {
   -moz-box-align: stretch;
 }
 
 .urlbar-input-box {
   margin-inline-start: 0;
 }
 
+.urlbar-input-box,
+#urlbar-display-box {
+  padding-inline-start: 4px;
+  border-inline-start: 1px solid var(--urlbar-separator-color);
+  border-image: linear-gradient(transparent 15%, var(--urlbar-separator-color) 15%, var(--urlbar-separator-color) 85%, transparent 85%);
+  border-image-slice: 1;
+}
+
 #urlbar-icons {
   -moz-box-align: center;
 }
 
 .urlbar-icon {
   padding: 0 3px;
   /* 16x16 icon with border-box sizing */
   width: 22px;
@@ -1309,21 +1317,16 @@ html|*.urlbar-input:-moz-lwtheme::-moz-p
   border: none;
   background: transparent;
 }
 
 #urlbar-search-splitter + #search-container > #searchbar > .searchbar-textbox {
   margin-inline-start: 0;
 }
 
-#urlbar-display-box {
-  border-inline-end: 1px solid #AAA;
-  margin-inline-end: 3px;
-}
-
 .urlbar-display {
   margin-top: 0;
   margin-bottom: 0;
   margin-inline-start: 0;
   color: GrayText;
 }
 
 %include ../shared/urlbarSearchSuggestionsNotification.inc.css
@@ -1965,26 +1968,16 @@ html|span.ac-emphasize-text-url {
 }
 
 /* tabbrowser-tab focus ring */
 .tabbrowser-tab:focus > .tab-stack > .tab-content {
   outline: 1px dotted;
   outline-offset: -6px;
 }
 
-/* Background tabs:
- *
- * Decrease the height of the hoverable region of background tabs whenever the tabs are at the top
- * of the window (e.g. no menubar, tabs in titlebar, etc.) to make it easier to drag the window by
- * the titlebar. We don't need this in fullscreen since window dragging is not an issue there.
- */
-#main-window[tabsintitlebar][sizemode=normal] #toolbar-menubar[autohide="true"][inactive] + #TabsToolbar .tab-background-middle:not([visuallyselected=true]) {
-  clip-path: url(chrome://browser/content/browser.xul#tab-hover-clip-path);
-}
-
 /* Tab DnD indicator */
 .tab-drop-indicator {
   list-style-image: url(chrome://browser/skin/tabbrowser/tabDragIndicator.png);
   margin-bottom: -9px;
   z-index: 3;
 }
 
 /* Tab close button */
@@ -2177,19 +2170,16 @@ toolbarbutton.bookmark-item[dragover="tr
   max-width: 28em;
 }
 
 .addon-install-confirmation-name {
   font-weight: bold;
 }
 
 /* Notification icon box */
-#notification-popup-box {
-  border-radius: 2.5px 0 0 2.5px;
-}
 
 .notification-anchor-icon:-moz-focusring {
   outline: 1px dotted -moz-DialogText;
 }
 
 /* Translation infobar */
 
 %include ../shared/translation/infobar.inc.css
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -129,16 +129,29 @@ var removeTab = Task.async(function* (ta
   let onClose = once(gBrowser.tabContainer, "TabClose");
   gBrowser.removeTab(tab);
   yield onClose;
 
   info("Tab removed and finished closing");
 });
 
 /**
+ * Refresh the given tab.
+ * @param {Object} tab The tab to be refreshed.
+ * @return Promise<undefined> resolved when the tab is successfully refreshed.
+ */
+var refreshTab = Task.async(function*(tab) {
+  info("Refreshing tab.");
+  const finished = once(gBrowser.selectedBrowser, "load", true);
+  gBrowser.reloadTab(gBrowser.selectedTab);
+  yield finished;
+  info("Tab finished refreshing.");
+});
+
+/**
  * Simulate a key event from a <key> element.
  * @param {DOMNode} key
  */
 function synthesizeKeyFromKeyTag(key) {
   is(key && key.tagName, "key", "Successfully retrieved the <key> node");
 
   let modifiersAttr = key.getAttribute("modifiers");
 
--- a/devtools/client/locales/en-US/storage.properties
+++ b/devtools/client/locales/en-US/storage.properties
@@ -32,16 +32,20 @@ storage.menuLabel=Storage Inspector
 storage.panelLabel=Storage Panel
 
 # LOCALIZATION NOTE (storage.tooltip3):
 # This string is displayed in the tooltip of the tab when the storage editor is
 # displayed inside the developer tools window.
 # A keyboard shortcut for Storage Inspector will be shown inside the brackets.
 storage.tooltip3=Storage Inspector (Cookies, Local Storage, …) (%S)
 
+# LOCALIZATION NOTE (storage.filter.key):
+# Key shortcut used to focus the filter box on top of the data view
+storage.filter.key=CmdOrCtrl+F
+
 # LOCALIZATION NOTE (tree.emptyText):
 # This string is displayed when the Storage Tree is empty. This can happen when
 # there are no websites on the current page (about:blank)
 tree.emptyText=No hosts on the page
 
 # LOCALIZATION NOTE (table.emptyText):
 # This string is displayed when there are no rows in the Storage Table for the
 # selected host.
--- a/devtools/client/memory/test/browser/browser.ini
+++ b/devtools/client/memory/test/browser/browser.ini
@@ -1,14 +1,15 @@
 [DEFAULT]
 tags = devtools devtools-memory
 subsuite = devtools
 support-files =
   head.js
   doc_big_tree.html
+  doc_empty.html
   doc_steady_allocation.html
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/framework/test/shared-redux-head.js
 
 [browser_memory_allocationStackDisplay_01.js]
     skip-if = debug # bug 1219554
 [browser_memory_displays_01.js]
 [browser_memory_clear_snapshots.js]
@@ -18,12 +19,13 @@ support-files =
 [browser_memory_filter_01.js]
 [browser_memory_individuals_01.js]
 [browser_memory_keyboard.js]
 [browser_memory_keyboard-snapshot-list.js]
 [browser_memory_no_allocation_stacks.js]
 [browser_memory_no_auto_expand.js]
     skip-if = debug # bug 1219554
 [browser_memory_percents_01.js]
+[browser_memory_refresh_does_not_leak.js]
 [browser_memory_simple_01.js]
 [browser_memory_transferHeapSnapshot_e10s_01.js]
 [browser_memory_tree_map-01.js]
 [browser_memory_tree_map-02.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/test/browser/browser_memory_refresh_does_not_leak.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that refreshing the page with devtools open does not leak the old
+// windows from previous navigations.
+//
+// IF THIS TEST STARTS FAILING, YOU ARE LEAKING EVERY WINDOW EVER NAVIGATED TO
+// WHILE DEVTOOLS ARE OPEN! THIS IS NOT SPECIFIC TO THE MEMORY TOOL ONLY!
+
+"use strict";
+
+const HeapSnapshotFileUtils = require("devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
+const { getLabelAndShallowSize } = require("devtools/shared/heapsnapshot/DominatorTreeNode");
+
+const TEST_URL = "http://example.com/browser/devtools/client/memory/test/browser/doc_empty.html";
+
+function* getWindowsInSnapshot(front) {
+  dumpn("Taking snapshot.");
+  const path = yield front.saveHeapSnapshot();
+  dumpn("Took snapshot with path = " + path);
+  const snapshot = ChromeUtils.readHeapSnapshot(path);
+  dumpn("Read snapshot into memory, taking census.");
+  const report = snapshot.takeCensus({
+    breakdown: {
+      by: "objectClass",
+      then: { by: "bucket" },
+      other: { by: "count", count: true, bytes: false },
+    }
+  });
+  dumpn("Took census, window count = " + report.Window.count);
+  return report.Window;
+}
+
+const DESCRIPTION = {
+  by: "coarseType",
+  objects: {
+    by: "objectClass",
+    then: { by: "count", count: true, bytes: false },
+    other: { by: "count", count: true, bytes: false },
+  },
+  strings: { by: "count", count: true, bytes: false },
+  scripts: {
+    by: "internalType",
+    then: { by: "count", count: true, bytes: false },
+  },
+  other: {
+    by: "internalType",
+    then: { by: "count", count: true, bytes: false },
+  }
+};
+
+this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) {
+  const heapWorker = panel.panelWin.gHeapAnalysesClient;
+  const front = panel.panelWin.gFront;
+  const store = panel.panelWin.gStore;
+  const { getState, dispatch } = store;
+  const doc = panel.panelWin.document;
+
+  const startWindows = yield getWindowsInSnapshot(front);
+  dumpn("Initial windows found = " + startWindows.map(w => "0x" + w.toString(16)).join(", "));
+  is(startWindows.length, 1);
+
+  yield refreshTab(tab);
+
+  const endWindows = yield getWindowsInSnapshot(front);
+  is(endWindows.length, 1);
+
+  if (endWindows.length === 1) {
+    return;
+  }
+
+  dumpn("Test failed, diagnosing leaking windows.");
+  dumpn("(This may fail if a moving GC has relocated the initial Window objects.)");
+
+  dumpn("Taking full runtime snapshot.");
+  const path = yield front.saveHeapSnapshot({ boundaries: { runtime: true } });
+  dumpn("Full runtime's snapshot path = " + path);
+
+  dumpn("Reading full runtime heap snapshot.");
+  const snapshot = ChromeUtils.readHeapSnapshot(path);
+  dumpn("Done reading full runtime heap snapshot.");
+
+  const dominatorTree = snapshot.computeDominatorTree();
+  const paths = snapshot.computeShortestPaths(dominatorTree.root, startWindows, 50);
+
+  for (let i = 0; i < startWindows.length; i++) {
+    dumpn("Shortest retaining paths for leaking Window 0x" + startWindows[i].toString(16) + " =========================");
+    let j = 0;
+    for (let retainingPath of paths.get(startWindows[i])) {
+      if (retainingPath.find(part => part.predecessor === startWindows[i])) {
+        // Skip paths that loop out from the target window and back to it again.
+        continue;
+      }
+
+      dumpn("    Path #" + (++j) + ": --------------------------------------------------------------------");
+      for (let part of retainingPath) {
+        const { label } = getLabelAndShallowSize(part.predecessor, snapshot, DESCRIPTION);
+        dumpn("        0x" + part.predecessor.toString(16) +
+              " (" + label.join(" > ") + ")");
+        dumpn("               |");
+        dumpn("              " + part.edge);
+        dumpn("               |");
+        dumpn("               V");
+      }
+      dumpn("        0x" + startWindows[i].toString(16) + " (objects > Window)");
+    }
+  }
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/memory/test/browser/doc_empty.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8"/>
+    </head>
+    <body>
+        This is an empty window.
+    </body>
+</html>
--- a/devtools/client/shared/test/browser.ini
+++ b/devtools/client/shared/test/browser.ini
@@ -119,16 +119,17 @@ skip-if = e10s # Bug 1221911, bug 122228
 [browser_html_tooltip-04.js]
 [browser_html_tooltip-05.js]
 [browser_html_tooltip_arrow-01.js]
 [browser_html_tooltip_arrow-02.js]
 [browser_html_tooltip_consecutive-show.js]
 [browser_html_tooltip_offset.js]
 [browser_html_tooltip_variable-height.js]
 [browser_html_tooltip_width-auto.js]
+[browser_html_tooltip_xul-wrapper.js]
 [browser_inplace-editor-01.js]
 [browser_inplace-editor-02.js]
 [browser_inplace-editor_autocomplete_01.js]
 [browser_inplace-editor_autocomplete_02.js]
 [browser_inplace-editor_autocomplete_offset.js]
 [browser_inplace-editor_maxwidth.js]
 [browser_key_shortcuts.js]
 [browser_layoutHelpers.js]
--- a/devtools/client/shared/test/browser_html_tooltip-01.js
+++ b/devtools/client/shared/test/browser_html_tooltip-01.js
@@ -4,63 +4,57 @@
 
 "use strict";
 
 /**
  * Test the HTMLTooltip show & hide methods.
  */
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
-const TEST_WINDOW_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
+const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
   <?xml-stylesheet href="chrome://global/skin/global.css"?>
   <?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
   <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    title="Tooltip test">
     <vbox flex="1">
       <hbox id="box1" flex="1">test1</hbox>
       <hbox id="box2" flex="1">test2</hbox>
       <hbox id="box3" flex="1">test3</hbox>
       <hbox id="box4" flex="1">test4</hbox>
     </vbox>
   </window>`;
 
-const TEST_PAGE_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
-  <?xml-stylesheet href="chrome://global/skin/global.css"?>
-  <?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
-  <page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-   title="Tooltip test with document using a Page element">
-    <vbox flex="1">
-      <hbox id="box1" flex="1">test1</hbox>
-    </vbox>
-  </page>`;
-
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 function getTooltipContent(doc) {
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "50px";
   div.style.boxSizing = "border-box";
   div.textContent = "tooltip";
   return div;
 }
 
 add_task(function* () {
-  info("Test showing a basic tooltip in XUL document using <window>");
-  yield testTooltipForUri(TEST_WINDOW_URI);
+  let [,, doc] = yield createHost("bottom", TEST_URI);
 
-  info("Test showing a basic tooltip in XUL document using <page>");
-  yield testTooltipForUri(TEST_PAGE_URI);
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
 });
 
-function* testTooltipForUri(uri) {
-  let tab = yield addTab("about:blank");
-  let [,, doc] = yield createHost("bottom", uri);
-
-  let tooltip = new HTMLTooltip({doc}, {});
+function* runTests(doc) {
+  yield addTab("about:blank");
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
 
   info("Set tooltip content");
   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
 
   is(tooltip.isVisible(), false, "Tooltip is not visible");
 
   info("Show the tooltip and check the expected events are fired.");
 
@@ -85,10 +79,10 @@ function* testTooltipForUri(uri) {
   tooltip.hide();
 
   yield onPopupHidden;
   is(hidden, 1, "Event hidden was fired once");
 
   yield waitForReflow(tooltip);
   is(tooltip.isVisible(), false, "Tooltip is not visible");
 
-  yield removeTab(tab);
+  tooltip.destroy();
 }
--- a/devtools/client/shared/test/browser_html_tooltip-02.js
+++ b/devtools/client/shared/test/browser_html_tooltip-02.js
@@ -22,47 +22,59 @@ const TEST_URI = `data:text/xml;charset=
       <hbox id="box4" flex="1">test4</hbox>
       <iframe id="frame" width="200"></iframe>
     </vbox>
   </window>`;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 add_task(function* () {
   yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
+});
+
+function* runTests(doc) {
   yield testClickInTooltipContent(doc);
   yield testConsumeOutsideClicksFalse(doc);
   yield testConsumeOutsideClicksTrue(doc);
   yield testClickInOuterIframe(doc);
   yield testClickInInnerIframe(doc);
-});
+}
 
 function* testClickInTooltipContent(doc) {
   info("Test a tooltip is not closed when clicking inside itself");
 
-  let tooltip = new HTMLTooltip({doc}, {});
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
   let onTooltipContainerClick = once(tooltip.container, "click");
   EventUtils.synthesizeMouseAtCenter(tooltip.container, {}, doc.defaultView);
   yield onTooltipContainerClick;
   is(tooltip.isVisible(), true, "Tooltip is still visible");
 
   tooltip.destroy();
 }
 
 function* testConsumeOutsideClicksFalse(doc) {
   info("Test closing a tooltip via click with consumeOutsideClicks: false");
   let box4 = doc.getElementById("box4");
 
-  let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: false});
+  let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: false, useXulWrapper});
   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
   let onBox4Clicked = once(box4, "click");
   let onHidden = once(tooltip, "hidden");
   EventUtils.synthesizeMouseAtCenter(box4, {}, doc.defaultView);
   yield onHidden;
   yield onBox4Clicked;
@@ -75,17 +87,17 @@ function* testConsumeOutsideClicksFalse(
 function* testConsumeOutsideClicksTrue(doc) {
   info("Test closing a tooltip via click with consumeOutsideClicks: true");
   let box4 = doc.getElementById("box4");
 
   // Count clicks on box4
   let box4clicks = 0;
   box4.addEventListener("click", () => box4clicks++);
 
-  let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: true});
+  let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: true, useXulWrapper});
   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
   let onHidden = once(tooltip, "hidden");
   EventUtils.synthesizeMouseAtCenter(box4, {}, doc.defaultView);
   yield onHidden;
 
   is(box4clicks, 0, "box4 catched no click event");
@@ -93,32 +105,32 @@ function* testConsumeOutsideClicksTrue(d
 
   tooltip.destroy();
 }
 
 function* testClickInOuterIframe(doc) {
   info("Test clicking an iframe outside of the tooltip closes the tooltip");
   let frame = doc.getElementById("frame");
 
-  let tooltip = new HTMLTooltip({doc});
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
   tooltip.setContent(getTooltipContent(doc), {width: 100, height: 50});
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
   let onHidden = once(tooltip, "hidden");
   EventUtils.synthesizeMouseAtCenter(frame, {}, doc.defaultView);
   yield onHidden;
 
   is(tooltip.isVisible(), false, "Tooltip is hidden");
   tooltip.destroy();
 }
 
 function* testClickInInnerIframe(doc) {
   info("Test clicking an iframe inside the tooltip content does not close the tooltip");
 
-  let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: false});
+  let tooltip = new HTMLTooltip({doc}, {consumeOutsideClicks: false, useXulWrapper});
 
   let iframe = doc.createElementNS(HTML_NS, "iframe");
   iframe.style.width = "100px";
   iframe.style.height = "50px";
   tooltip.setContent(iframe, {width: 100, height: 50});
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
   let onTooltipContainerClick = once(tooltip.container, "click");
--- a/devtools/client/shared/test/browser_html_tooltip-03.js
+++ b/devtools/client/shared/test/browser_html_tooltip-03.js
@@ -26,37 +26,51 @@ const TEST_URI = `data:text/xml;charset=
         <textbox id="box4-input"></textbox>
       </hbox>
     </vbox>
   </window>`;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 add_task(function* () {
   yield addTab("about:blank");
   let [, , doc] = yield createHost("bottom", TEST_URI);
 
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
+});
+
+function* runTests(doc) {
   yield testNoAutoFocus(doc);
   yield testAutoFocus(doc);
   yield testAutoFocusPreservesFocusChange(doc);
-});
+}
 
 function* testNoAutoFocus(doc) {
   yield focusNode(doc, "#box4-input");
   ok(doc.activeElement.closest("#box4-input"), "Focus is in the #box4-input");
 
   info("Test a tooltip without autofocus will not take focus");
   let tooltip = yield createTooltip(doc, false);
 
   yield showTooltip(tooltip, doc.getElementById("box1"));
   ok(doc.activeElement.closest("#box4-input"), "Focus is still in the #box4-input");
 
   yield hideTooltip(tooltip);
   yield blurNode(doc, "#box4-input");
+
+  tooltip.destroy();
 }
 
 function* testAutoFocus(doc) {
   yield focusNode(doc, "#box4-input");
   ok(doc.activeElement.closest("#box4-input"), "Focus is in the #box4-input");
 
   info("Test autofocus tooltip takes focus when displayed, " +
     "and restores the focus when hidden");
@@ -65,16 +79,18 @@ function* testAutoFocus(doc) {
   yield showTooltip(tooltip, doc.getElementById("box1"));
   ok(doc.activeElement.closest(".tooltip-content"), "Focus is in the tooltip");
 
   yield hideTooltip(tooltip);
   ok(doc.activeElement.closest("#box4-input"), "Focus is in the #box4-input");
 
   info("Blur the textbox before moving to the next test to reset the state.");
   yield blurNode(doc, "#box4-input");
+
+  tooltip.destroy();
 }
 
 function* testAutoFocusPreservesFocusChange(doc) {
   yield focusNode(doc, "#box4-input");
   ok(doc.activeElement.closest("#box4-input"), "Focus is still in the #box3-input");
 
   info("Test autofocus tooltip takes focus when displayed, " +
     "but does not try to restore the active element if it is not focused when hidden");
@@ -87,16 +103,18 @@ function* testAutoFocusPreservesFocusCha
   yield focusNode(doc, "#box3-input");
   ok(doc.activeElement.closest("#box3-input"), "Focus moved to the #box3-input");
 
   yield hideTooltip(tooltip);
   ok(doc.activeElement.closest("#box3-input"), "Focus is still in the #box3-input");
 
   info("Blur the textbox before moving to the next test to reset the state.");
   yield blurNode(doc, "#box3-input");
+
+  tooltip.destroy();
 }
 
 /**
  * Fpcus the node corresponding to the provided selector in the provided document. Returns
  * a promise that will resolve when receiving the focus event on the node.
  */
 function focusNode(doc, selector) {
   let node = doc.querySelector(selector);
@@ -121,17 +139,17 @@ function blurNode(doc, selector) {
  *
  * @param {Document} doc
  *        Document in which the tooltip should be created
  * @param {Boolean} autofocus
  * @return {Promise} promise that will resolve the HTMLTooltip instance created when the
  *         tooltip content will be ready.
  */
 function* createTooltip(doc, autofocus) {
-  let tooltip = new HTMLTooltip({doc}, {autofocus});
+  let tooltip = new HTMLTooltip({doc}, {autofocus, useXulWrapper});
   let div = doc.createElementNS(HTML_NS, "div");
   div.classList.add("tooltip-content");
   div.style.height = "50px";
   div.innerHTML = '<input type="text"></input>';
 
   tooltip.setContent(div, {width: 150, height: 50});
   return tooltip;
 }
--- a/devtools/client/shared/test/browser_html_tooltip-04.js
+++ b/devtools/client/shared/test/browser_html_tooltip-04.js
@@ -35,17 +35,17 @@ const TOOLTIP_WIDTH = 100;
 add_task(function* () {
   // Force the toolbox to be 400px high;
   yield pushPref("devtools.toolbox.footer.height", 400);
 
   yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
   info("Create HTML tooltip");
-  let tooltip = new HTMLTooltip({doc}, {});
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper: false});
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "100%";
   tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
 
   let box1 = doc.getElementById("box1");
   let box2 = doc.getElementById("box2");
   let box3 = doc.getElementById("box3");
   let box4 = doc.getElementById("box4");
@@ -100,9 +100,11 @@ add_task(function* () {
 
   info("Try to display the tooltip on bottom of box4.");
   yield showTooltip(tooltip, box4, {position: "bottom"});
   expectedTooltipGeometry = {position: "top", height, width};
   checkTooltipGeometry(tooltip, box4, expectedTooltipGeometry);
   yield hideTooltip(tooltip);
 
   is(tooltip.isVisible(), false, "Tooltip is not visible");
+
+  tooltip.destroy();
 });
--- a/devtools/client/shared/test/browser_html_tooltip-05.js
+++ b/devtools/client/shared/test/browser_html_tooltip-05.js
@@ -27,22 +27,21 @@ const {HTMLTooltip} = require("devtools/
 loadHelperScript("helper_html_tooltip.js");
 
 const TOOLTIP_HEIGHT = 200;
 const TOOLTIP_WIDTH = 200;
 
 add_task(function* () {
   // Force the toolbox to be 200px high;
   yield pushPref("devtools.toolbox.footer.height", 200);
-
   yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
   info("Create HTML tooltip");
-  let tooltip = new HTMLTooltip({doc}, {});
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper: false});
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "100%";
   tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
 
   let box1 = doc.getElementById("box1");
   let box2 = doc.getElementById("box2");
   let box3 = doc.getElementById("box3");
   let box4 = doc.getElementById("box4");
@@ -52,17 +51,17 @@ add_task(function* () {
   // height of 150px.
   info("Display the tooltip on box1.");
   yield showTooltip(tooltip, box1);
   let expectedTooltipGeometry = {position: "bottom", height: 150, width};
   checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
   yield hideTooltip(tooltip);
 
   info("Try to display the tooltip on top of box1.");
-  yield showTooltip(tooltip, box1, "top");
+  yield showTooltip(tooltip, box1, {position: "top"});
   expectedTooltipGeometry = {position: "bottom", height: 150, width};
   checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
   yield hideTooltip(tooltip);
 
   // box2: Can not fit above or below box2, default to bottom with a reduced
   // height of 100px.
   info("Try to display the tooltip on box2.");
   yield showTooltip(tooltip, box2);
@@ -100,9 +99,11 @@ add_task(function* () {
 
   info("Try to display the tooltip on bottom of box4.");
   yield showTooltip(tooltip, box4, {position: "bottom"});
   expectedTooltipGeometry = {position: "top", height: 150, width};
   checkTooltipGeometry(tooltip, box4, expectedTooltipGeometry);
   yield hideTooltip(tooltip);
 
   is(tooltip.isVisible(), false, "Tooltip is not visible");
+
+  tooltip.destroy();
 });
--- a/devtools/client/shared/test/browser_html_tooltip_arrow-01.js
+++ b/devtools/client/shared/test/browser_html_tooltip_arrow-01.js
@@ -44,25 +44,37 @@ const TEST_URI = `data:text/xml;charset=
       ${getAnchor("top: 0; right: 50px;")}
       ${getAnchor("top: 0; right: 75px;")}
     </vbox>
   </window>`;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 add_task(function* () {
   // Force the toolbox to be 200px high;
   yield pushPref("devtools.toolbox.footer.height", 200);
 
   yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
+});
+
+function* runTests(doc) {
   info("Create HTML tooltip");
-  let tooltip = new HTMLTooltip({doc}, {type: "arrow"});
+  let tooltip = new HTMLTooltip({doc}, {type: "arrow", useXulWrapper});
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "35px";
   tooltip.setContent(div, {width: 200, height: 35});
 
   let {right: docRight} = doc.documentElement.getBoundingClientRect();
 
   let elements = [...doc.querySelectorAll(".anchor")];
   for (let el of elements) {
@@ -86,9 +98,11 @@ add_task(function* () {
 
     let isInPanel = arrowBounds.left >= panelBounds.left &&
                     arrowBounds.right <= panelBounds.right;
     ok(isInPanel,
       "The tooltip arrow remains inside the tooltip panel horizontally");
 
     yield hideTooltip(tooltip);
   }
-});
+
+  tooltip.destroy();
+}
--- a/devtools/client/shared/test/browser_html_tooltip_arrow-02.js
+++ b/devtools/client/shared/test/browser_html_tooltip_arrow-02.js
@@ -38,25 +38,36 @@ const TEST_URI = `data:text/xml;charset=
       ${getAnchor("top: 130px; width: 200px; right: 0;")}
       ${getAnchor("top: 140px; width: 250px; right: 0;")}
     </vbox>
   </window>`;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 add_task(function* () {
   // Force the toolbox to be 200px high;
   yield pushPref("devtools.toolbox.footer.height", 200);
 
-  yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
+});
+
+function* runTests(doc) {
   info("Create HTML tooltip");
-  let tooltip = new HTMLTooltip({doc}, {type: "arrow"});
+  let tooltip = new HTMLTooltip({doc}, {type: "arrow", useXulWrapper});
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "35px";
   tooltip.setContent(div, {width: 200, height: 35});
 
   let {right: docRight} = doc.documentElement.getBoundingClientRect();
 
   let elements = [...doc.querySelectorAll(".anchor")];
   for (let el of elements) {
@@ -79,9 +90,11 @@ add_task(function* () {
       "Tooltip arrow is aligned with the anchor, or stuck on viewport's edge.");
 
     let isInPanel = arrowBounds.left >= panelBounds.left &&
                     arrowBounds.right <= panelBounds.right;
     ok(isInPanel,
       "The tooltip arrow remains inside the tooltip panel horizontally");
     yield hideTooltip(tooltip);
   }
-});
+
+  tooltip.destroy();
+}
--- a/devtools/client/shared/test/browser_html_tooltip_consecutive-show.js
+++ b/devtools/client/shared/test/browser_html_tooltip_consecutive-show.js
@@ -29,27 +29,26 @@ loadHelperScript("helper_html_tooltip.js
 function getTooltipContent(doc) {
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "50px";
   div.textContent = "tooltip";
   return div;
 }
 
 add_task(function* () {
-  yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
   let box1 = doc.getElementById("box1");
   let box2 = doc.getElementById("box2");
   let box3 = doc.getElementById("box3");
   let box4 = doc.getElementById("box4");
 
   let width = 100, height = 50;
 
-  let tooltip = new HTMLTooltip({doc}, {});
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper: false});
   tooltip.setContent(getTooltipContent(doc), {width, height});
 
   info("Show the tooltip on each of the 4 hbox, without calling hide in between");
 
   info("Show tooltip on box1");
   tooltip.show(box1);
   checkTooltipGeometry(tooltip, box1, {position: "bottom", width, height});
 
@@ -62,9 +61,11 @@ add_task(function* () {
   checkTooltipGeometry(tooltip, box3, {position: "top", width, height});
 
   info("Show tooltip on box4");
   tooltip.show(box4);
   checkTooltipGeometry(tooltip, box4, {position: "top", width, height});
 
   info("Hide tooltip before leaving test");
   yield hideTooltip(tooltip);
+
+  tooltip.destroy();
 });
--- a/devtools/client/shared/test/browser_html_tooltip_offset.js
+++ b/devtools/client/shared/test/browser_html_tooltip_offset.js
@@ -25,27 +25,26 @@ const TEST_URI = `data:text/xml;charset=
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
 add_task(function* () {
   // Force the toolbox to be 200px high;
   yield pushPref("devtools.toolbox.footer.height", 200);
 
-  yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
   info("Test a tooltip is not closed when clicking inside itself");
 
   let box1 = doc.getElementById("box1");
   let box2 = doc.getElementById("box2");
   let box3 = doc.getElementById("box3");
   let box4 = doc.getElementById("box4");
 
-  let tooltip = new HTMLTooltip({doc}, {});
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper: false});
 
   let div = doc.createElementNS(HTML_NS, "div");
   div.style.height = "100px";
   div.style.boxSizing = "border-box";
   div.textContent = "tooltip";
   tooltip.setContent(div, {width: 50, height: 100});
 
   info("Display the tooltip on box1.");
--- a/devtools/client/shared/test/browser_html_tooltip_variable-height.js
+++ b/devtools/client/shared/test/browser_html_tooltip_variable-height.js
@@ -24,24 +24,36 @@ const TEST_URI = `data:text/xml;charset=
 
 const CONTAINER_HEIGHT = 200;
 const CONTAINER_WIDTH = 200;
 const TOOLTIP_HEIGHT = 50;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 add_task(function* () {
   // Force the toolbox to be 400px tall => 50px for each box.
   yield pushPref("devtools.toolbox.footer.height", 400);
 
   yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
-  let tooltip = new HTMLTooltip({doc}, {});
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
+});
+
+function* runTests(doc) {
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
   info("Set tooltip content 50px tall, but request a container 200px tall");
   let tooltipContent = doc.createElementNS(HTML_NS, "div");
   tooltipContent.style.cssText = "height: " + TOOLTIP_HEIGHT + "px; background: red;";
   tooltip.setContent(tooltipContent, {width: CONTAINER_WIDTH, height: CONTAINER_HEIGHT});
 
   info("Show the tooltip and check the container and panel height.");
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
@@ -66,9 +78,11 @@ add_task(function* () {
   yield onPanelClick;
   is(tooltip.isVisible(), true, "Tooltip is still visible");
 
   info("Click below the tooltip container, the tooltip should be closed.");
   onHidden = once(tooltip, "hidden");
   EventUtils.synthesizeMouse(tooltip.container, 100, CONTAINER_HEIGHT + 10,
     {}, doc.defaultView);
   yield onHidden;
-});
+
+  tooltip.destroy();
+}
--- a/devtools/client/shared/test/browser_html_tooltip_width-auto.js
+++ b/devtools/client/shared/test/browser_html_tooltip_width-auto.js
@@ -20,28 +20,42 @@ const TEST_URI = `data:text/xml;charset=
       <hbox id="box3" flex="1">test3</hbox>
       <hbox id="box4" flex="1">test4</hbox>
     </vbox>
   </window>`;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
+let useXulWrapper;
+
 add_task(function* () {
   yield addTab("about:blank");
   let [,, doc] = yield createHost("bottom", TEST_URI);
 
-  let tooltip = new HTMLTooltip({doc}, {});
+  info("Run tests for a Tooltip without using a XUL panel");
+  useXulWrapper = false;
+  yield runTests(doc);
+
+  info("Run tests for a Tooltip with a XUL panel");
+  useXulWrapper = true;
+  yield runTests(doc);
+});
+
+function* runTests(doc) {
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper});
   info("Create tooltip content width to 150px");
   let tooltipContent = doc.createElementNS(HTML_NS, "div");
   tooltipContent.style.cssText = "height: 100%; width: 150px; background: red;";
 
   info("Set tooltip content using width:auto");
   tooltip.setContent(tooltipContent, {width: "auto", height: 50});
 
   info("Show the tooltip and check the tooltip panel width.");
   yield showTooltip(tooltip, doc.getElementById("box1"));
 
   let panelRect = tooltip.panel.getBoundingClientRect();
   is(panelRect.width, 150, "Tooltip panel has the expected width.");
 
   yield hideTooltip(tooltip);
-});
+
+  tooltip.destroy();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_xul-wrapper.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip can overflow out of the toolbox when using a XUL panel wrapper.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
+  <?xml-stylesheet href="chrome://global/skin/global.css"?>
+  <?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
+  <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+   title="Tooltip test">
+    <vbox flex="1">
+      <hbox id="box1" style="height: 50px">test1</hbox>
+      <hbox id="box2" style="height: 50px">test2</hbox>
+      <hbox id="box3" style="height: 50px">test3</hbox>
+      <hbox id="box4" style="height: 50px">test4</hbox>
+    </vbox>
+  </window>`;
+
+const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
+loadHelperScript("helper_html_tooltip.js");
+
+// The test toolbox will be 200px tall, the anchors are 50px tall, therefore, the maximum
+// tooltip height that could fit in the toolbox is 150px. Setting 160px, the tooltip will
+// either have to overflow or to be resized.
+const TOOLTIP_HEIGHT = 160;
+const TOOLTIP_WIDTH = 200;
+
+add_task(function* () {
+  // Force the toolbox to be 200px high;
+  yield pushPref("devtools.toolbox.footer.height", 200);
+
+  let [, win, doc] = yield createHost("bottom", TEST_URI);
+
+  info("Resizing window to have some space below the window.");
+  let originalWidth = win.top.outerWidth;
+  let originalHeight = win.top.outerHeight;
+  win.top.resizeBy(0, -100);
+
+  info("Create HTML tooltip");
+  let tooltip = new HTMLTooltip({doc}, {useXulWrapper: true});
+  let div = doc.createElementNS(HTML_NS, "div");
+  div.style.height = "200px";
+  div.style.background = "red";
+  tooltip.setContent(div, {width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT});
+
+  let box1 = doc.getElementById("box1");
+
+  // Above box1: check that the tooltip can overflow onto the content page.
+  info("Display the tooltip above box1.");
+  yield showTooltip(tooltip, box1, {position: "top"});
+  checkTooltip(tooltip, "top", TOOLTIP_HEIGHT);
+  yield hideTooltip(tooltip);
+
+  // Below box1: check that the tooltip can overflow out of the browser window.
+  info("Display the tooltip below box1.");
+  yield showTooltip(tooltip, box1, {position: "bottom"});
+  checkTooltip(tooltip, "bottom", TOOLTIP_HEIGHT);
+  yield hideTooltip(tooltip);
+
+  is(tooltip.isVisible(), false, "Tooltip is not visible");
+
+  tooltip.destroy();
+
+  info("Restore original window dimensions.");
+  win.top.resizeTo(originalWidth, originalHeight);
+});
+
+function checkTooltip(tooltip, position, height) {
+  is(tooltip.position, position, "Actual tooltip position is " + position);
+  let rect = tooltip.container.getBoundingClientRect();
+  is(rect.height, height, "Actual tooltip height is " + height);
+  // Testing the actual left/top offsets is not relevant here as it is handled by the XUL
+  // panel.
+}
--- a/devtools/client/shared/widgets/HTMLTooltip.js
+++ b/devtools/client/shared/widgets/HTMLTooltip.js
@@ -3,16 +3,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const EventEmitter = require("devtools/shared/event-emitter");
 const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipToggle");
+const {listenOnce} = require("devtools/shared/async-utils");
+const {Task} = require("devtools/shared/task");
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 
 const POSITION = {
   TOP: "top",
   BOTTOM: "bottom",
 };
@@ -44,37 +46,41 @@ const EXTRA_BORDER = {
 
 /**
  * Calculate the vertical position & offsets to use for the tooltip. Will attempt to
  * respect the provided height and position preferences, unless the available height
  * prevents this.
  *
  * @param {DOMRect} anchorRect
  *        Bounding rectangle for the anchor, relative to the tooltip document.
- * @param {DOMRect} docRect
- *        Bounding rectange for the tooltip document owner.
+ * @param {DOMRect} viewportRect
+ *        Bounding rectangle for the viewport. top/left can be different from 0 if some
+ *        space should not be used by tooltips (for instance OS toolbars, taskbars etc.).
  * @param {Number} height
  *        Preferred height for the tooltip.
  * @param {String} pos
  *        Preferred position for the tooltip. Possible values: "top" or "bottom".
  * @return {Object}
  *         - {Number} top: the top offset for the tooltip.
  *         - {Number} height: the height to use for the tooltip container.
  *         - {String} computedPosition: Can differ from the preferred position depending
  *           on the available height). "top" or "bottom"
  */
-const calculateVerticalPosition = function (anchorRect, docRect, height, pos, offset) {
+const calculateVerticalPosition =
+function (anchorRect, viewportRect, height, pos, offset) {
   let {TOP, BOTTOM} = POSITION;
 
   let {top: anchorTop, height: anchorHeight} = anchorRect;
-  let {bottom: docBottom} = docRect;
+
+  // Translate to the available viewport space before calculating dimensions and position.
+  anchorTop -= viewportRect.top;
 
   // Calculate available space for the tooltip.
   let availableTop = anchorTop;
-  let availableBottom = docBottom - (anchorTop + anchorHeight);
+  let availableBottom = viewportRect.height - (anchorTop + anchorHeight);
 
   // Find POSITION
   let keepPosition = false;
   if (pos === TOP) {
     keepPosition = availableTop >= height + offset;
   } else if (pos === BOTTOM) {
     keepPosition = availableBottom >= height + offset;
   }
@@ -85,47 +91,53 @@ const calculateVerticalPosition = functi
   // Calculate HEIGHT.
   let availableHeight = pos === TOP ? availableTop : availableBottom;
   height = Math.min(height, availableHeight - offset);
   height = Math.floor(height);
 
   // Calculate TOP.
   let top = pos === TOP ? anchorTop - height - offset : anchorTop + anchorHeight + offset;
 
+  // Translate back to absolute coordinates by re-including viewport top margin.
+  top += viewportRect.top;
+
   return {top, height, computedPosition: pos};
 };
 
 /**
  * Calculate the vertical position & offsets to use for the tooltip. Will attempt to
  * respect the provided height and position preferences, unless the available height
  * prevents this.
  *
  * @param {DOMRect} anchorRect
  *        Bounding rectangle for the anchor, relative to the tooltip document.
- * @param {DOMRect} docRect
- *        Bounding rectange for the tooltip document owner.
+ * @param {DOMRect} viewportRect
+ *        Bounding rectangle for the viewport. top/left can be different from 0 if some
+ *        space should not be used by tooltips (for instance OS toolbars, taskbars etc.).
  * @param {Number} width
  *        Preferred width for the tooltip.
  * @return {Object}
  *         - {Number} left: the left offset for the tooltip.
  *         - {Number} width: the width to use for the tooltip container.
  *         - {Number} arrowLeft: the left offset to use for the arrow element.
  */
-const calculateHorizontalPosition = function (anchorRect, docRect, width, type, offset) {
+const calculateHorizontalPosition =
+function (anchorRect, viewportRect, width, type, offset) {
   let {left: anchorLeft, width: anchorWidth} = anchorRect;
-  let {right: docRight} = docRect;
+
+  // Translate to the available viewport space before calculating dimensions and position.
+  anchorLeft -= viewportRect.left;
 
   // Calculate WIDTH.
-  let availableWidth = docRight;
-  width = Math.min(width, availableWidth);
+  width = Math.min(width, viewportRect.width);
 
   // Calculate LEFT.
   // By default the tooltip is aligned with the anchor left edge. Unless this
   // makes it overflow the viewport, in which case is shifts to the left.
-  let left = Math.min(anchorLeft + offset, docRight - width);
+  let left = Math.min(anchorLeft + offset, viewportRect.width - width);
 
   // Calculate ARROW LEFT (tooltip's LEFT might be updated)
   let arrowLeft;
   // Arrow style tooltips may need to be shifted to the left
   if (type === TYPE.ARROW) {
     let arrowCenter = left + ARROW_OFFSET + ARROW_WIDTH / 2;
     let anchorCenter = anchorLeft + anchorWidth / 2;
     // If the anchor is too narrow, align the arrow and the anchor center.
@@ -136,16 +148,19 @@ const calculateHorizontalPosition = func
     arrowLeft = Math.min(ARROW_OFFSET, (anchorWidth - ARROW_WIDTH) / 2) | 0;
     // Translate the coordinate to tooltip container
     arrowLeft += anchorLeft - left;
     // Make sure the arrow remains in the tooltip container.
     arrowLeft = Math.min(arrowLeft, width - ARROW_WIDTH);
     arrowLeft = Math.max(arrowLeft, 0);
   }
 
+  // Translate back to absolute coordinates by re-including viewport left margin.
+  left += viewportRect.left;
+
   return {left, width, arrowLeft};
 };
 
 /**
  * Get the bounding client rectangle for a given node, relative to a custom
  * reference element (instead of the default for getBoundingClientRect which
  * is always the element's ownerDocument).
  */
@@ -172,38 +187,61 @@ const getRelativeRect = function (node, 
  * @param {Object}
  *        - {String} type
  *          Display type of the tooltip. Possible values: "normal", "arrow"
  *        - {Boolean} autofocus
  *          Defaults to false. Should the tooltip be focused when opening it.
  *        - {Boolean} consumeOutsideClicks
  *          Defaults to true. The tooltip is closed when clicking outside.
  *          Should this event be stopped and consumed or not.
+ *        - {Boolean} useXulWrapper
+ *          Defaults to true. If the tooltip is hosted in a XUL document, use a XUL panel
+ *          in order to use all the screen viewport available.
  */
-function HTMLTooltip(toolbox,
-  {type = "normal", autofocus = false, consumeOutsideClicks = true} = {}) {
+function HTMLTooltip(toolbox, {
+    type = "normal",
+    autofocus = false,
+    consumeOutsideClicks = true,
+    useXulWrapper = true,
+  } = {}) {
   EventEmitter.decorate(this);
 
   this.doc = toolbox.doc;
   this.type = type;
   this.autofocus = autofocus;
   this.consumeOutsideClicks = consumeOutsideClicks;
+  this.useXulWrapper = useXulWrapper;
+
+  this._position = null;
 
   // Use the topmost window to listen for click events to close the tooltip
   this.topWindow = this.doc.defaultView.top;
 
   this._onClick = this._onClick.bind(this);
 
   this._toggle = new TooltipToggle(this);
   this.startTogglingOnHover = this._toggle.start.bind(this._toggle);
   this.stopTogglingOnHover = this._toggle.stop.bind(this._toggle);
 
   this.container = this._createContainer();
 
-  if (this._isXUL()) {
+  if (this._isXUL() && this.useXulWrapper) {
+    // When using a XUL panel as the wrapper, the actual markup for the tooltip is as
+    // follows :
+    // <panel> <!-- XUL panel used to position the tooltip anywhere on screen -->
+    //   <div> <!-- div wrapper used to isolate the tooltip container -->
+    //     <div> <! the actual tooltip.container element -->
+    this.xulPanelWrapper = this._createXulPanelWrapper();
+    let inner = this.doc.createElementNS(XHTML_NS, "div");
+    inner.classList.add("tooltip-xul-wrapper-inner");
+
+    this.doc.documentElement.appendChild(this.xulPanelWrapper);
+    this.xulPanelWrapper.appendChild(inner);
+    inner.appendChild(this.container);
+  } else if (this._isXUL()) {
     this.doc.documentElement.appendChild(this.container);
   } else {
     // In non-XUL context the container is ready to use as is.
     this.doc.body.appendChild(this.container);
   }
 }
 
 module.exports.HTMLTooltip = HTMLTooltip;
@@ -220,16 +258,23 @@ HTMLTooltip.prototype = {
   /**
    * The arrow element. Might be null depending on the tooltip type.
    */
   get arrow() {
     return this.container.querySelector(".tooltip-arrow");
   },
 
   /**
+   * Retrieve the displayed position used for the tooltip. Null if the tooltip is hidden.
+   */
+  get position() {
+    return this.isVisible() ? this._position : null;
+  },
+
+  /**
    * Set the tooltip content element. The preferred width/height should also be
    * specified here.
    *
    * @param {Element} content
    *        The tooltip content, should be a HTML element.
    * @param {Object}
    *        - {Number} width: preferred width for the tooltip container. If not specified
    *          the tooltip container will be measured before being displayed, and the
@@ -255,107 +300,167 @@ HTMLTooltip.prototype = {
    * @param {Object}
    *        - {String} position: optional, possible values: top|bottom
    *          If layout permits, the tooltip will be displayed on top/bottom
    *          of the anchor. If ommitted, the tooltip will be displayed where
    *          more space is available.
    *        - {Number} x: optional, horizontal offset between the anchor and the tooltip
    *        - {Number} y: optional, vertical offset between the anchor and the tooltip
    */
-  show: function (anchor, {position, x = 0, y = 0} = {}) {
+  show: Task.async(function* (anchor, {position, x = 0, y = 0} = {}) {
     // Get anchor geometry
     let anchorRect = getRelativeRect(anchor, this.doc);
-    // Get document geometry
-    let docRect = this.doc.documentElement.getBoundingClientRect();
+    if (this.useXulWrapper) {
+      anchorRect = this._convertToScreenRect(anchorRect);
+    }
+
+    // Get viewport size
+    let viewportRect = this._getViewportRect();
 
     let themeHeight = EXTRA_HEIGHT[this.type] + 2 * EXTRA_BORDER[this.type];
     let preferredHeight = this.preferredHeight + themeHeight;
 
     let {top, height, computedPosition} =
-      calculateVerticalPosition(anchorRect, docRect, preferredHeight, position, y);
+      calculateVerticalPosition(anchorRect, viewportRect, preferredHeight, position, y);
 
-    // Apply height and top information before measuring the content width (if "auto").
+    this._position = computedPosition;
+    // Apply height before measuring the content width (if width="auto").
     let isTop = computedPosition === POSITION.TOP;
     this.container.classList.toggle("tooltip-top", isTop);
     this.container.classList.toggle("tooltip-bottom", !isTop);
     this.container.style.height = height + "px";
-    this.container.style.top = top + "px";
 
-    let themeWidth = 2 * EXTRA_BORDER[this.type];
-    let preferredWidth = this.preferredWidth === "auto" ?
-      this._measureContainerWidth() : this.preferredWidth + themeWidth;
+    let preferredWidth;
+    if (this.preferredWidth === "auto") {
+      preferredWidth = this._measureContainerWidth();
+    } else {
+      let themeWidth = 2 * EXTRA_BORDER[this.type];
+      preferredWidth = this.preferredWidth + themeWidth;
+    }
 
     let {left, width, arrowLeft} =
-      calculateHorizontalPosition(anchorRect, docRect, preferredWidth, this.type, x);
+      calculateHorizontalPosition(anchorRect, viewportRect, preferredWidth, this.type, x);
 
     this.container.style.width = width + "px";
-    this.container.style.left = left + "px";
 
     if (this.type === TYPE.ARROW) {
       this.arrow.style.left = arrowLeft + "px";
     }
 
+    if (this.useXulWrapper) {
+      this._showXulWrapperAt(left, top);
+    } else {
+      this.container.style.left = left + "px";
+      this.container.style.top = top + "px";
+    }
+
     this.container.classList.add("tooltip-visible");
 
     // Keep a pointer on the focused element to refocus it when hiding the tooltip.
     this._focusedElement = this.doc.activeElement;
 
     this.doc.defaultView.clearTimeout(this.attachEventsTimer);
     this.attachEventsTimer = this.doc.defaultView.setTimeout(() => {
       this._maybeFocusTooltip();
       this.topWindow.addEventListener("click", this._onClick, true);
       this.emit("shown");
     }, 0);
+  }),
+
+  /**
+   * Calculate the rect of the viewport that limits the tooltip dimensions. When using a
+   * XUL panel wrapper, the viewport will be able to use the whole screen (excluding space
+   * reserved by the OS for toolbars etc.). Otherwise, the viewport is limited to the
+   * tooltip's document.
+   *
+   * @return {Object} DOMRect-like object with the Number properties: top, right, bottom,
+   *         left, width, height
+   */
+  _getViewportRect: function () {
+    if (this.useXulWrapper) {
+      // availLeft/Top are the coordinates first pixel available on the screen for
+      // applications (excluding space dedicated for OS toolbars, menus etc...)
+      // availWidth/Height are the dimensions available to applications excluding all
+      // the OS reserved space
+      let {availLeft, availTop, availHeight, availWidth} = this.doc.defaultView.screen;
+      return {
+        top: availTop,
+        right: availLeft + availWidth,
+        bottom: availTop + availHeight,
+        left: availLeft,
+        width: availWidth,
+        height: availHeight,
+      };
+    }
+
+    return this.doc.documentElement.getBoundingClientRect();
   },
 
   _measureContainerWidth: function () {
+    let xulParent = this.container.parentNode;
+    if (this.useXulWrapper && !this.isVisible()) {
+      // Move the container out of the XUL Panel to measure it.
+      this.doc.documentElement.appendChild(this.container);
+    }
+
     this.container.classList.add("tooltip-hidden");
-    this.container.style.left = "0px";
     this.container.style.width = "auto";
     let width = this.container.getBoundingClientRect().width;
     this.container.classList.remove("tooltip-hidden");
+
+    if (this.useXulWrapper && !this.isVisible()) {
+      xulParent.appendChild(this.container);
+    }
+
     return width;
   },
 
   /**
    * Hide the current tooltip. The event "hidden" will be fired when the tooltip
    * is hidden.
    */
-  hide: function () {
+  hide: Task.async(function* () {
     this.doc.defaultView.clearTimeout(this.attachEventsTimer);
     if (!this.isVisible()) {
       return;
     }
 
     this.topWindow.removeEventListener("click", this._onClick, true);
     this.container.classList.remove("tooltip-visible");
+    if (this.useXulWrapper) {
+      yield this._hideXulWrapper();
+    }
+
     this.emit("hidden");
 
     let tooltipHasFocus = this.container.contains(this.doc.activeElement);
     if (tooltipHasFocus && this._focusedElement) {
       this._focusedElement.focus();
       this._focusedElement = null;
     }
-  },
+  }),
 
   /**
    * Check if the tooltip is currently displayed.
    * @return {Boolean} true if the tooltip is visible
    */
   isVisible: function () {
     return this.container.classList.contains("tooltip-visible");
   },
 
   /**
    * Destroy the tooltip instance. Hide the tooltip if displayed, remove the
    * tooltip container from the document.
    */
   destroy: function () {
     this.hide();
     this.container.remove();
+    if (this.xulPanelWrapper) {
+      this.xulPanelWrapper.remove();
+    }
   },
 
   _createContainer: function () {
     let container = this.doc.createElementNS(XHTML_NS, "div");
     container.setAttribute("type", this.type);
     container.classList.add("tooltip-container");
 
     let html = '<div class="tooltip-filler"></div>';
@@ -403,28 +508,69 @@ HTMLTooltip.prototype = {
       }
       win = win.parent;
     }
 
     return false;
   },
 
   /**
-   * Check if the tooltip's owner document is a XUL document.
-   */
-  _isXUL: function () {
-    return this.doc.documentElement.namespaceURI === XUL_NS;
-  },
-
-  /**
    * If the tootlip is configured to autofocus and a focusable element can be found,
    * focus it.
    */
   _maybeFocusTooltip: function () {
     // Simplied selector targetting elements that can receive the focus, full version at
     // http://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus .
     let focusableSelector = "a, button, iframe, input, select, textarea";
     let focusableElement = this.panel.querySelector(focusableSelector);
     if (this.autofocus && focusableElement) {
       focusableElement.focus();
     }
   },
+
+  /**
+   * Check if the tooltip's owner document is a XUL document.
+   */
+  _isXUL: function () {
+    return this.doc.documentElement.namespaceURI === XUL_NS;
+  },
+
+  _createXulPanelWrapper: function () {
+    let panel = this.doc.createElementNS(XUL_NS, "panel");
+
+    // XUL panel is only a way to display DOM elements outside of the document viewport,
+    // so disable all features that impact the behavior.
+    panel.setAttribute("animate", false);
+    panel.setAttribute("consumeoutsideclicks", false);
+    panel.setAttribute("noautofocus", true);
+    panel.setAttribute("ignorekeys", true);
+
+    panel.setAttribute("level", "float");
+    panel.setAttribute("class", "tooltip-xul-wrapper");
+
+    return panel;
+  },
+
+  _showXulWrapperAt: function (left, top) {
+    let onPanelShown = listenOnce(this.xulPanelWrapper, "popupshown");
+    this.xulPanelWrapper.openPopupAtScreen(left, top, false);
+    return onPanelShown;
+  },
+
+  _hideXulWrapper: function () {
+    let onPanelHidden = listenOnce(this.xulPanelWrapper, "popuphidden");
+    this.xulPanelWrapper.hidePopup();
+    return onPanelHidden;
+  },
+
+  /**
+   * Convert from coordinates relative to the tooltip's document, to coordinates relative
+   * to the "available" screen. By "available" we mean the screen, excluding the OS bars
+   * display on screen edges.
+   */
+  _convertToScreenRect: function ({left, top, width, height}) {
+    // mozInnerScreenX/Y are the coordinates of the top left corner of the window's
+    // viewport, excluding chrome UI.
+    left += this.doc.defaultView.mozInnerScreenX;
+    top += this.doc.defaultView.mozInnerScreenY;
+    return {top, right: left + width, bottom: top + height, left, width, height};
+  },
 };
--- a/devtools/client/storage/test/browser.ini
+++ b/devtools/client/storage/test/browser.ini
@@ -31,12 +31,13 @@ support-files =
 [browser_storage_dynamic_updates.js]
 [browser_storage_empty_objectstores.js]
 [browser_storage_indexeddb_delete.js]
 [browser_storage_indexeddb_delete_blocked.js]
 [browser_storage_localstorage_edit.js]
 [browser_storage_localstorage_error.js]
 [browser_storage_overflow.js]
 [browser_storage_search.js]
+[browser_storage_search_keyboard_trap.js]
 [browser_storage_sessionstorage_edit.js]
 [browser_storage_sidebar.js]
 [browser_storage_sidebar_update.js]
 [browser_storage_values.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/storage/test/browser_storage_search_keyboard_trap.js
@@ -0,0 +1,15 @@
+// Test ability to focus search field by using keyboard
+"use strict";
+
+add_task(function* () {
+  yield openTabAndSetupStorage(MAIN_DOMAIN + "storage-search.html");
+
+  gUI.tree.expandAll();
+  yield selectTreeItem(["localStorage", "http://test1.example.org"]);
+
+  yield focusSearchBoxUsingShortcut(gPanelWindow);
+  ok(containsFocus(gPanelWindow.document, gUI.searchBox),
+     "Focus is in a searchbox");
+
+  yield finishTests();
+});
--- a/devtools/client/storage/test/head.js
+++ b/devtools/client/storage/test/head.js
@@ -788,8 +788,42 @@ function* checkState(state) {
     is(items.size, names.length,
       `There is correct number of rows in ${storeName}`);
     for (let name of names) {
       ok(items.has(name),
         `There is item with name '${name}' in ${storeName}`);
     }
   }
 }
+
+/**
+ * Checks if document's active element is within the given element.
+ * @param  {HTMLDocument}  doc document with active element in question
+ * @param  {DOMNode}       container element tested on focus containment
+ * @return {Boolean}
+ */
+function containsFocus(doc, container) {
+  let elm = doc.activeElement;
+  while (elm) {
+    if (elm === container) {
+      return true;
+    }
+    elm = elm.parentNode;
+  }
+  return false;
+}
+
+var focusSearchBoxUsingShortcut = Task.async(function* (panelWin, callback) {
+  info("Focusing search box");
+  let searchBox = panelWin.document.getElementById("storage-searchbox");
+  let focused = once(searchBox, "focus");
+
+  panelWin.focus();
+  let strings = Services.strings.createBundle(
+    "chrome://devtools/locale/storage.properties");
+  synthesizeKeyShortcut(strings.GetStringFromName("storage.filter.key"));
+
+  yield focused;
+
+  if (callback) {
+    callback();
+  }
+});
--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Task} = require("devtools/shared/task");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {LocalizationHelper} = require("devtools/client/shared/l10n");
+const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
 
 loader.lazyRequireGetter(this, "TreeWidget",
                          "devtools/client/shared/widgets/TreeWidget", true);
 loader.lazyRequireGetter(this, "TableWidget",
                          "devtools/client/shared/widgets/TableWidget", true);
 loader.lazyRequireGetter(this, "ViewHelpers",
                          "devtools/client/shared/widgets/view-helpers");
 loader.lazyImporter(this, "VariablesView",
@@ -106,16 +107,25 @@ function StorageUI(front, target, panelW
   this.sidebar.setAttribute("width", "300");
   this.view = new VariablesView(this.sidebar.firstChild,
                                 GENERIC_VARIABLES_VIEW_SETTINGS);
 
   this.searchBox = this._panelDoc.getElementById("storage-searchbox");
   this.filterItems = this.filterItems.bind(this);
   this.searchBox.addEventListener("command", this.filterItems);
 
+  let shortcuts = new KeyShortcuts({
+    window: this._panelDoc.defaultView,
+  });
+  let key = L10N.getStr("storage.filter.key");
+  shortcuts.on(key, (name, event) => {
+    event.preventDefault();
+    this.searchBox.focus();
+  });
+
   this.front.listStores().then(storageTypes => {
     this.populateStorageTree(storageTypes);
   }).then(null, console.error);
 
   this.onUpdate = this.onUpdate.bind(this);
   this.front.on("stores-update", this.onUpdate);
   this.onCleared = this.onCleared.bind(this);
   this.front.on("stores-cleared", this.onCleared);
--- a/devtools/client/themes/tooltips.css
+++ b/devtools/client/themes/tooltips.css
@@ -104,16 +104,27 @@
   position: fixed;
   z-index: 9999;
   display: none;
   background: transparent;
   pointer-events: none;
   overflow: hidden;
 }
 
+.tooltip-xul-wrapper {
+  -moz-appearance: none;
+  background: transparent;
+  overflow: visible;
+  border-style: none;
+}
+
+.tooltip-xul-wrapper .tooltip-container {
+  position: absolute;
+}
+
 .tooltip-top {
   flex-direction: column;
 }
 
 .tooltip-bottom {
   flex-direction: column-reverse;
 }
 
@@ -132,16 +143,21 @@
 }
 
 /* Tooltip : arrow style */
 
 .tooltip-container[type="arrow"] {
   filter: drop-shadow(0 3px 4px var(--theme-tooltip-shadow));
 }
 
+.tooltip-xul-wrapper .tooltip-container[type="arrow"] {
+  /* When displayed in a XUL panel the drop shadow would be abruptly cut by the panel */
+  filter: none;
+}
+
 .tooltip-container[type="arrow"] > .tooltip-panel {
   position: relative;
   flex-grow: 0;
   min-height: 10px;
   box-sizing: border-box;
   width: 100%;
 
   border: 3px solid var(--theme-tooltip-border);
@@ -260,17 +276,17 @@
 }
 
 .event-tooltip-debugger-icon:hover {
   opacity: 1;
 }
 
 .event-tooltip-content-box {
   display: none;
-  height: 54px;
+  height: 100px;
   overflow: hidden;
   margin-inline-end: 0;
   border: 1px solid var(--theme-splitter-color);
   border-width: 1px 0 0 0;
 }
 
 .event-toolbox-content-box iframe {
   height: 100%;
--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -8,16 +8,27 @@
   Indeed, this pseudo-class is only available to chrome code.
   This stylesheet is loaded as a ua stylesheet via the addon sdk, so having this
   pseudo-class is important.
   Having bug 1086532 fixed would make it possible to load this stylesheet in a
   <style scoped> node instead, directly in the native anonymous container
   element.
 */
 
+:-moz-native-anonymous {
+  /*
+  Content CSS applying to the html element impact the highlighters.
+  To avoid that, possible cases have been set to initial.
+  */
+  text-transform: initial;
+  text-indent: initial;
+  letter-spacing: initial;
+  word-spacing: initial;
+}
+
 :-moz-native-anonymous .highlighter-container {
   --highlighter-guide-color: #08c;
   --highlighter-content-color: #87ceeb;
   --highlighter-bubble-text-color: hsl(216, 33%, 97%);
   --highlighter-bubble-background-color: hsl(214, 13%, 24%);
   --highlighter-bubble-border-color: rgba(255, 255, 255, 0.2);
 }
 
--- a/devtools/server/actors/memory.js
+++ b/devtools/server/actors/memory.js
@@ -42,18 +42,18 @@ exports.MemoryActor = protocol.ActorClas
   },
 
   attach: actorBridgeWithSpec("attach"),
 
   detach: actorBridgeWithSpec("detach"),
 
   getState: actorBridgeWithSpec("getState"),
 
-  saveHeapSnapshot: function () {
-    return this.bridge.saveHeapSnapshot();
+  saveHeapSnapshot: function (boundaries) {
+    return this.bridge.saveHeapSnapshot(boundaries);
   },
 
   takeCensus: actorBridgeWithSpec("takeCensus"),
 
   startRecordingAllocations: actorBridgeWithSpec("startRecordingAllocations"),
 
   stopRecordingAllocations: actorBridgeWithSpec("stopRecordingAllocations"),
 
--- a/devtools/server/actors/script.js
+++ b/devtools/server/actors/script.js
@@ -615,17 +615,17 @@ const ThreadActor = ActorClassWithSpec(t
     }
 
     if (this.state !== "detached") {
       return { error: "wrongState",
                message: "Current state is " + this.state };
     }
 
     this._state = "attached";
-    this._debuggerSourcesSeen = new Set();
+    this._debuggerSourcesSeen = new WeakSet();
 
     Object.assign(this._options, aRequest.options || {});
     this.sources.setOptions(this._options);
     this.sources.on("newSource", this.onSourceEvent);
     this.sources.on("updatedSource", this.onSourceEvent);
 
     // Initialize an event loop stack. This can't be done in the constructor,
     // because this.conn is not yet initialized by the actor pool at that time.
--- a/devtools/server/performance/memory.js
+++ b/devtools/server/performance/memory.js
@@ -115,18 +115,18 @@ var Memory = exports.Memory = Class({
     }
   },
 
   /**
    * Handler for the parent actor's "window-ready" event.
    */
   _onWindowReady: function ({ isTopLevel }) {
     if (this.state == "attached") {
+      this._clearDebuggees();
       if (isTopLevel && this.isRecordingAllocations()) {
-        this._clearDebuggees();
         this._frameCache.initFrames();
       }
       this.dbg.addDebuggees();
     }
   },
 
   /**
    * Returns a boolean indicating whether or not allocation
@@ -135,25 +135,29 @@ var Memory = exports.Memory = Class({
   isRecordingAllocations: function () {
     return this.dbg.memory.trackingAllocationSites;
   },
 
   /**
    * Save a heap snapshot scoped to the current debuggees' portion of the heap
    * graph.
    *
+   * @param {Object|null} boundaries
+   *
    * @returns {String} The snapshot id.
    */
-  saveHeapSnapshot: expectState("attached", function () {
+  saveHeapSnapshot: expectState("attached", function (boundaries = null) {
     // If we are observing the whole process, then scope the snapshot
     // accordingly. Otherwise, use the debugger's debuggees.
-    const opts = this.parent instanceof ChromeActor || this.parent instanceof ChildProcessActor
-      ? { runtime: true }
-      : { debugger: this.dbg };
-    const path = ThreadSafeChromeUtils.saveHeapSnapshot(opts);
+    if (!boundaries) {
+      boundaries = this.parent instanceof ChromeActor || this.parent instanceof ChildProcessActor
+        ? { runtime: true }
+        : { debugger: this.dbg };
+    }
+    const path = ThreadSafeChromeUtils.saveHeapSnapshot(boundaries);
     return HeapSnapshotFileUtils.getSnapshotIdFromPath(path);
   }, "saveHeapSnapshot"),
 
   /**
    * Take a census of the heap. See js/src/doc/Debugger/Debugger.Memory.md for
    * more information.
    */
   takeCensus: expectState("attached", function () {
--- a/devtools/shared/fronts/memory.js
+++ b/devtools/shared/fronts/memory.js
@@ -30,20 +30,24 @@ const MemoryFront = protocol.FrontClassW
    *
    * Note that this is safe to call for actors inside sandoxed child processes,
    * as we jump through the correct IPDL hoops.
    *
    * @params Boolean options.forceCopy
    *         Always force a bulk data copy of the saved heap snapshot, even when
    *         the server and client share a file system.
    *
+   * @params {Object|undefined} options.boundaries
+   *         The boundaries for the heap snapshot. See
+   *         ThreadSafeChromeUtils.webidl for more details.
+   *
    * @returns Promise<String>
    */
   saveHeapSnapshot: protocol.custom(Task.async(function* (options = {}) {
-    const snapshotId = yield this._saveHeapSnapshotImpl();
+    const snapshotId = yield this._saveHeapSnapshotImpl(options.boundaries);
 
     if (!options.forceCopy &&
         (yield HeapSnapshotFileUtils.haveHeapSnapshotTempFile(snapshotId))) {
       return HeapSnapshotFileUtils.getHeapSnapshotTempFilePath(snapshotId);
     }
 
     return yield this.transferHeapSnapshot(snapshotId);
   }), {
--- a/devtools/shared/specs/memory.js
+++ b/devtools/shared/specs/memory.js
@@ -106,16 +106,19 @@ const memorySpec = generateActorSpec({
       request: {},
       response: RetVal("json"),
     },
     residentUnique: {
       request: {},
       response: { value: RetVal("number") }
     },
     saveHeapSnapshot: {
+      request: {
+        boundaries: Arg(0, "nullable:json")
+      },
       response: {
         snapshotId: RetVal("string")
       }
     },
   },
 });
 
 exports.memorySpec = memorySpec;
--- a/toolkit/modules/PopupNotifications.jsm
+++ b/toolkit/modules/PopupNotifications.jsm
@@ -950,16 +950,18 @@ PopupNotifications.prototype = {
     if (type == "keypress" &&
         !(event.charCode == Ci.nsIDOMKeyEvent.DOM_VK_SPACE ||
           event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN))
       return;
 
     if (this._currentNotifications.length == 0)
       return;
 
+    event.stopPropagation();
+
     // Get the anchor that is the immediate child of the icon box
     let anchor = event.target;
     while (anchor && anchor.parentNode != this.iconBox)
       anchor = anchor.parentNode;
 
     if (!anchor) {
       return;
     }
--- a/xpcom/base/nsSystemInfo.cpp
+++ b/xpcom/base/nsSystemInfo.cpp
@@ -27,16 +27,19 @@
 
 #ifdef XP_MACOSX
 #include "MacHelpers.h"
 #endif
 
 #ifdef MOZ_WIDGET_GTK
 #include <gtk/gtk.h>
 #include <dlfcn.h>
+#endif
+
+#if defined (XP_LINUX) && !defined (ANDROID)
 #include <unistd.h>
 #include <fstream>
 #include "mozilla/Tokenizer.h"
 #include "nsCharSeparatedTokenizer.h"
 
 #include <map>
 #include <string>
 #endif
@@ -68,17 +71,17 @@ NS_EXPORT int android_sdk_version;
 
 // Slot for NS_InitXPCOM2 to pass information to nsSystemInfo::Init.
 // Only set to nonzero (potentially) if XP_UNIX.  On such systems, the
 // system call to discover the appropriate value is not thread-safe,
 // so we must call it before going multithreaded, but nsSystemInfo::Init
 // only happens well after that point.
 uint32_t nsSystemInfo::gUserUmask = 0;
 
-#if defined (MOZ_WIDGET_GTK)
+#if defined (XP_LINUX) && !defined (ANDROID)
 static void
 SimpleParseKeyValuePairs(const std::string& aFilename,
                          std::map<nsCString, nsCString>& aKeyValuePairs)
 {
   std::ifstream input(aFilename.c_str());
   for (std::string line; std::getline(input, line); ) {
     nsAutoCString key, value;
 
@@ -492,17 +495,17 @@ nsSystemInfo::Init()
   MOZ_ASSERT(sizeof(sysctlValue32) == len);
 
   len = sizeof(sysctlValue32);
   if (!sysctlbyname("machdep.cpu.stepping", &sysctlValue32, &len, NULL, 0)) {
     cpuStepping = static_cast<int>(sysctlValue32);
   }
   MOZ_ASSERT(sizeof(sysctlValue32) == len);
 
-#elif defined (MOZ_WIDGET_GTK)
+#elif defined (XP_LINUX) && !defined (ANDROID)
   // Get vendor, family, model, stepping, physical cores, L3 cache size
   // from /proc/cpuinfo file
   {
     std::map<nsCString, nsCString> keyValuePairs;
     SimpleParseKeyValuePairs("/proc/cpuinfo", keyValuePairs);
 
     // cpuVendor from "vendor_id"
     cpuVendor.Assign(keyValuePairs[NS_LITERAL_CSTRING("vendor_id")]);