merge fx-team to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 22 Jun 2015 13:56:39 +0200
changeset 267939 cddf3b36b5e2c2d9553f71c83a3ae943da73006f
parent 267908 20d8b6076d9bad03c262e85b8f24c7c7307bbc2a (current diff)
parent 267938 171e623cf40c76eaa1c22bfba9807d8f4f77e8d0 (diff)
child 268051 be81b8d6fae99c89e8b14591b11dd26eec0a416e
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-esr52@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone41.0a1
merge fx-team to mozilla-central a=merge
browser/devtools/performance/system.js
browser/devtools/performance/views/jit-optimizations.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -266,16 +266,17 @@ pref("general.autoScroll", false);
 #else
 pref("general.autoScroll", true);
 #endif
 
 // At startup, check if we're the default browser and prompt user if not.
 pref("browser.shell.checkDefaultBrowser", true);
 pref("browser.shell.shortcutFavicons",true);
 pref("browser.shell.mostRecentDateSetAsDefault", "");
+pref("browser.shell.windows10DefaultBrowserABTest", -1);
 
 // 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
 // The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore
 pref("browser.startup.page",                1);
 pref("browser.startup.homepage",            "chrome://branding/locale/browserconfig.properties");
 
 pref("browser.slowStartup.notificationDisabled", false);
 pref("browser.slowStartup.timeThreshold", 40000);
@@ -291,17 +292,17 @@ pref("browser.casting.enabled", false);
 pref("browser.chrome.site_icons", true);
 pref("browser.chrome.favicons", true);
 // browser.warnOnQuit == false will override all other possible prompts when quitting or restarting
 pref("browser.warnOnQuit", true);
 // browser.showQuitWarning specifically controls the quit warning dialog. We
 // might still show the window closing dialog with showQuitWarning == false.
 pref("browser.showQuitWarning", false);
 pref("browser.fullscreen.autohide", true);
-pref("browser.fullscreen.animateUp", 1);
+pref("browser.fullscreen.animate", true);
 pref("browser.overlink-delay", 80);
 
 #ifdef UNIX_BUT_NOT_MAC
 pref("browser.urlbar.clickSelectsAll", false);
 #else
 pref("browser.urlbar.clickSelectsAll", true);
 #endif
 #ifdef UNIX_BUT_NOT_MAC
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -1,16 +1,14 @@
 # -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 var FullScreen = {
-  _XULNS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
-
   _MESSAGES: [
     "DOMFullscreen:Request",
     "DOMFullscreen:NewOrigin",
     "DOMFullscreen:Exit",
   ],
 
   init: function() {
     // called when we go into full screen, even if initiated by a web page script
@@ -58,37 +56,34 @@ var FullScreen = {
       this._fullScrToggler = document.getElementById("fullscr-toggler");
       this._fullScrToggler.addEventListener("mouseover", this._expandCallback, false);
       this._fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
     }
 
     if (enterFS) {
       gNavToolbox.setAttribute("inFullscreen", true);
       document.documentElement.setAttribute("inFullscreen", true);
+      if (!document.mozFullScreen && this.useLionFullScreen)
+        document.documentElement.setAttribute("OSXLionFullscreen", true);
     } else {
       gNavToolbox.removeAttribute("inFullscreen");
       document.documentElement.removeAttribute("inFullscreen");
+      document.documentElement.removeAttribute("OSXLionFullscreen");
     }
 
-    // show/hide menubars, toolbars (except the full screen toolbar)
-    // On OS X Lion, we don't want to hide toolbars when entering
-    // fullscreen, unless we're entering DOM fullscreen.
-    if (document.mozFullScreen || !this.useLionFullScreen) {
-      this.showXULChrome("toolbar", !enterFS);
-    }
+    if (!document.mozFullScreen)
+      this._updateToolbars(enterFS);
 
     if (enterFS) {
       document.addEventListener("keypress", this._keyToggleCallback, false);
       document.addEventListener("popupshown", this._setPopupOpen, false);
       document.addEventListener("popuphidden", this._setPopupOpen, false);
-      this._shouldAnimate = true;
-      // We don't animate the toolbar collapse if in DOM full-screen mode,
-      // as the size of the content area would still be changing after the
-      // mozfullscreenchange event fired, which could confuse content script.
-      this.hideNavToolbox(document.mozFullScreen);
+      // In DOM fullscreen mode, we hide toolbars with CSS
+      if (!document.mozFullScreen)
+        this.hideNavToolbox(true);
     }
     else {
       this.showNavToolbox(false);
       // This is needed if they use the context menu to quit fullscreen
       this._isPopupOpen = false;
       this.cleanup();
       // In TabsInTitlebar._update(), we cancel the appearance update on
       // resize event for exiting fullscreen, since that happens before we
@@ -203,20 +198,16 @@ var FullScreen = {
     gBrowser.tabContainer.addEventListener("TabClose", this.exitDomFullScreen);
     gBrowser.tabContainer.addEventListener("TabSelect", this.exitDomFullScreen);
 
     // Add listener to detect when the fullscreen window is re-focused.
     // If a fullscreen window loses focus, we show a warning when the
     // fullscreen window is refocused.
     window.addEventListener("activate", this);
 
-    // Cancel any "hide the toolbar" animation which is in progress, and make
-    // the toolbar hide immediately.
-    this.hideNavToolbox(true);
-    this._fullScrToggler.hidden = true;
     return true;
   },
 
   cleanup: function () {
     if (!window.fullScreen) {
       MousePosTracker.removeListener(this);
       document.removeEventListener("keypress", this._keyToggleCallback, false);
       document.removeEventListener("popupshown", this._setPopupOpen, false);
@@ -227,23 +218,16 @@ var FullScreen = {
   cleanupDomFullscreen: function () {
     this.cancelWarning();
     gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
     gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
     gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
     window.removeEventListener("activate", this);
 
     document.documentElement.removeAttribute("inDOMFullscreen");
-    this.showNavToolbox();
-    // If we are still in fullscreen mode, re-hide
-    // the toolbox with animation.
-    if (window.fullScreen) {
-      this._shouldAnimate = true;
-      this.hideNavToolbox();
-    }
 
     window.messageManager
           .broadcastAsyncMessage("DOMFullscreen:CleanUp");
   },
 
   _isRemoteBrowser: function (aBrowser) {
     return gMultiProcessBrowser && aBrowser.getAttribute("remote") == "true";
   },
@@ -267,50 +251,45 @@ var FullScreen = {
   {
     FullScreen.hideNavToolbox();
   },
   _keyToggleCallback: function(aEvent)
   {
     // if we can use the keyboard (eg Ctrl+L or Ctrl+E) to open the toolbars, we
     // should provide a way to collapse them too.
     if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
-      FullScreen.hideNavToolbox(true);
+      FullScreen.hideNavToolbox();
     }
     // F6 is another shortcut to the address bar, but its not covered in OpenLocation()
     else if (aEvent.keyCode == aEvent.DOM_VK_F6)
       FullScreen.showNavToolbox();
   },
 
   // Checks whether we are allowed to collapse the chrome
   _isPopupOpen: false,
   _isChromeCollapsed: false,
-  _safeToCollapse: function(forceHide)
-  {
+  _safeToCollapse: function () {
     if (!gPrefService.getBoolPref("browser.fullscreen.autohide"))
       return false;
 
-    if (!forceHide) {
-      // a popup menu is open in chrome: don't collapse chrome
-      if (this._isPopupOpen)
-        return false;
-      // On OS X Lion we don't want to hide toolbars.
-      if (this.useLionFullScreen)
-        return false;
-    }
+    // a popup menu is open in chrome: don't collapse chrome
+    if (this._isPopupOpen)
+      return false;
+
+    // On OS X Lion we don't want to hide toolbars.
+    if (this.useLionFullScreen)
+      return false;
 
     // a textbox in chrome is focused (location bar anyone?): don't collapse chrome
     if (document.commandDispatcher.focusedElement &&
         document.commandDispatcher.focusedElement.ownerDocument == document &&
         document.commandDispatcher.focusedElement.localName == "input") {
-      if (forceHide)
-        // hidden textboxes that still have focus are bad bad bad
-        document.commandDispatcher.focusedElement.blur();
-      else
-        return false;
+      return false;
     }
+
     return true;
   },
 
   _setPopupOpen: function(aEvent)
   {
     // Popups should only veto chrome collapsing if they were opened when the chrome was not collapsed.
     // Otherwise, they would not affect chrome and the user would expect the chrome to go away.
     // e.g. we wouldn't want the autoscroll icon firing this event, so when the user
@@ -328,19 +307,16 @@ var FullScreen = {
   {
     aItem.setAttribute("checked", gPrefService.getBoolPref("browser.fullscreen.autohide"));
   },
   setAutohide: function()
   {
     gPrefService.setBoolPref("browser.fullscreen.autohide", !gPrefService.getBoolPref("browser.fullscreen.autohide"));
   },
 
-  // Animate the toolbars disappearing
-  _shouldAnimate: true,
-
   cancelWarning: function(event) {
     if (!this.warningBox)
       return;
     this.warningBox.removeEventListener("transitionend", this);
     if (this.warningFadeOutTimeout) {
       clearTimeout(this.warningFadeOutTimeout);
       this.warningFadeOutTimeout = null;
     }
@@ -509,92 +485,60 @@ var FullScreen = {
         bottom: rect.bottom,
         left: rect.left,
         right: rect.right
       };
       MousePosTracker.addListener(this);
     }
   },
 
-  hideNavToolbox: function(forceHide = false) {
-    this._fullScrToggler.hidden = document.mozFullScreen;
-    if (this._isChromeCollapsed) {
-      if (forceHide) {
-        gNavToolbox.removeAttribute("fullscreenShouldAnimate");
-      }
+  hideNavToolbox: function (aAnimate = false) {
+    if (this._isChromeCollapsed || !this._safeToCollapse())
       return;
-    }
-    if (!this._safeToCollapse(forceHide)) {
-      this._fullScrToggler.hidden = true;
-      return;
-    }
 
-    // browser.fullscreen.animateUp
-    // 0 - never animate up
-    // 1 - animate only for first collapse after entering fullscreen (default for perf's sake)
-    // 2 - animate every time it collapses
-    let animateUp = gPrefService.getIntPref("browser.fullscreen.animateUp");
-    if (animateUp == 0) {
-      this._shouldAnimate = false;
-    } else if (animateUp == 2) {
-      this._shouldAnimate = true;
-    }
-    if (this._shouldAnimate && !forceHide) {
+    this._fullScrToggler.hidden = false;
+
+    if (aAnimate && gPrefService.getBoolPref("browser.fullscreen.animate")) {
       gNavToolbox.setAttribute("fullscreenShouldAnimate", true);
-      this._shouldAnimate = false;
       // Hide the fullscreen toggler until the transition ends.
       let listener = () => {
         gNavToolbox.removeEventListener("transitionend", listener, true);
         if (this._isChromeCollapsed)
           this._fullScrToggler.hidden = false;
       };
       gNavToolbox.addEventListener("transitionend", listener, true);
       this._fullScrToggler.hidden = true;
     }
 
     gNavToolbox.style.marginTop =
       -gNavToolbox.getBoundingClientRect().height + "px";
     this._isChromeCollapsed = true;
     MousePosTracker.removeListener(this);
   },
 
-  showXULChrome: function(aTag, aShow)
-  {
-    var els = document.getElementsByTagNameNS(this._XULNS, aTag);
-
-    for (let el of els) {
-      // XXX don't interfere with previously collapsed toolbars
-      if (el.getAttribute("fullscreentoolbar") == "true") {
-        if (!aShow) {
-          // Give the main nav bar and the tab bar the fullscreen context menu,
-          // otherwise remove context menu to prevent breakage
-          el.setAttribute("saved-context", el.getAttribute("context"));
-          if (el.id == "nav-bar" || el.id == "TabsToolbar")
-            el.setAttribute("context", "autohide-context");
-          else
-            el.removeAttribute("context");
+  _updateToolbars: function (aEnterFS) {
+    for (let el of document.querySelectorAll("toolbar[fullscreentoolbar=true]")) {
+      if (aEnterFS) {
+        // Give the main nav bar and the tab bar the fullscreen context menu,
+        // otherwise remove context menu to prevent breakage
+        el.setAttribute("saved-context", el.getAttribute("context"));
+        if (el.id == "nav-bar" || el.id == "TabsToolbar")
+          el.setAttribute("context", "autohide-context");
+        else
+          el.removeAttribute("context");
 
-          // Set the inFullscreen attribute to allow specific styling
-          // in fullscreen mode
-          el.setAttribute("inFullscreen", true);
+        // Set the inFullscreen attribute to allow specific styling
+        // in fullscreen mode
+        el.setAttribute("inFullscreen", true);
+      } else {
+        if (el.hasAttribute("saved-context")) {
+          el.setAttribute("context", el.getAttribute("saved-context"));
+          el.removeAttribute("saved-context");
         }
-        else {
-          if (el.hasAttribute("saved-context")) {
-            el.setAttribute("context", el.getAttribute("saved-context"));
-            el.removeAttribute("saved-context");
-          }
-          el.removeAttribute("inFullscreen");
-        }
-      } else {
-        // use moz-collapsed so it doesn't persist hidden/collapsed,
-        // so that new windows don't have missing toolbars
-        if (aShow)
-          el.removeAttribute("moz-collapsed");
-        else
-          el.setAttribute("moz-collapsed", "true");
+        el.removeAttribute("inFullscreen");
       }
     }
 
     ToolbarIconColor.inferFromText();
 
     // For Lion fullscreen, all fullscreen controls are hidden, don't
     // bother to touch them. If we don't stop here, the following code
     // could cause the native fullscreen button be shown unexpectedly.
@@ -609,17 +553,17 @@ var FullScreen = {
     if (fullscreenctls.parentNode == navbar && ctlsOnTabbar) {
       fullscreenctls.removeAttribute("flex");
       document.getElementById("TabsToolbar").appendChild(fullscreenctls);
     }
     else if (fullscreenctls.parentNode.id == "TabsToolbar" && !ctlsOnTabbar) {
       fullscreenctls.setAttribute("flex", "1");
       navbar.appendChild(fullscreenctls);
     }
-    fullscreenctls.hidden = aShow;
+    fullscreenctls.hidden = !aEnterFS;
   }
 };
 XPCOMUtils.defineLazyGetter(FullScreen, "useLionFullScreen", function() {
   // We'll only use OS X Lion full screen if we're
   // * on OS X
   // * on Lion or higher (Darwin 11+)
   // * have fullscreenbutton="true"
 #ifdef XP_MACOSX
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -285,21 +285,24 @@ toolbar[customizing] > .overflow-button 
 %ifdef XP_WIN
 #main-window[sizemode="maximized"] #titlebar-buttonbox {
   -moz-appearance: -moz-window-button-box-maximized;
 }
 %endif
 
 %endif
 
+#main-window[inDOMFullscreen] #navigator-toolbox,
+#main-window[inDOMFullscreen] #fullscr-toggler,
 #main-window[inDOMFullscreen] #sidebar-box,
 #main-window[inDOMFullscreen] #sidebar-splitter {
   visibility: collapse;
 }
 
+#main-window[inFullscreen]:not([OSXLionFullscreen]) toolbar:not([fullscreentoolbar=true]),
 #main-window[inFullscreen] #global-notificationbox,
 #main-window[inFullscreen] #high-priority-global-notificationbox {
   visibility: collapse;
 }
 
 #navigator-toolbox[fullscreenShouldAnimate] {
   transition: 1.5s margin-top ease-out;
 }
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -1150,18 +1150,19 @@ html[dir="rtl"] .room-context-btn-edit {
      https://code.google.com/p/chromium/issues/detail?id=400829 */
   overflow: hidden;
 }
 
 .media-wrapper > .remote > .remote-video {
   object-fit: cover;
 }
 
-/* Note: we can't use flex for the text-chat-view as this lets it overflow
-   the expected column heights, and we ca't fix its height. */
+/* Note: we can't use "display: flex;" for the text-chat-view itself,
+   as this lets it overflow the expected column heights, and we can't
+   fix its height. */
 .media-wrapper > .text-chat-view {
   flex: 0 0 auto;
   /* Text chat is a fixed 200px width for normal displays. */
   width: 200px;
   height: 100%;
 }
 
 .media-wrapper.showing-local-streams > .text-chat-view {
@@ -1171,21 +1172,28 @@ html[dir="rtl"] .room-context-btn-edit {
 }
 
 .media-wrapper.showing-local-streams.receiving-screen-share > .text-chat-view {
   /* When we're displaying the local streams, then we need to make the text
      chat view a bit shorter to give room. */
   height: calc(100% - 300px);
 }
 
+/* Temporarily slaved from .media-wrapper until we use it in more places
+   to avoid affecting the conversation window on desktop. */
 .media-wrapper > .text-chat-view > .text-chat-entries {
   /* 40px is the height of .text-chat-box. */
   height: calc(100% - 40px);
 }
 
+.media-wrapper > .text-chat-disabled > .text-chat-entries {
+  /* When text chat is disabled, the entries box should be 100% height. */
+  height: 100%;
+}
+
 .media-wrapper > .local {
   flex: 0 1 auto;
   width: 200px;
   height: 150px;
 }
 
 .media-wrapper.receiving-screen-share > .screen {
   order: 1;
@@ -1225,22 +1233,29 @@ html[dir="rtl"] .room-context-btn-edit {
     /* A reasonable height */
     height: 70%;
   }
 
   .media-wrapper.receiving-screen-share > .focus-stream {
     height: 50%;
   }
 
+  /* Temporarily slaved from .media-wrapper until we use it in more places
+     to avoid affecting the conversation window on desktop. */
   .media-wrapper > .text-chat-view > .text-chat-entries {
     /* 40px is the height of .text-chat-box. */
     height: calc(100% - 40px);
     width: 100%;
   }
 
+  .media-wrapper > .text-chat-disabled > .text-chat-entries {
+    /* When text chat is disabled, the entries box should be 100% height. */
+    height: 100%;
+  }
+
   .media-wrapper > .local {
     /* Position over the remote video */
     position: absolute;
     /* Make sure its on top */
     z-index: 1001;
     margin: 3px;
     right: 0;
     /* 29px is (30% of 50px high header) + (height toolbar (38px) +
@@ -1443,17 +1458,17 @@ html[dir="rtl"] .standalone .room-conver
   flex-flow: column nowrap;
 }
 
 .fx-embedded .video-layout-wrapper {
   flex: 1 1 auto;
 }
 
 .text-chat-view {
-  background: #fff;
+  background: white;
 }
 
 .fx-embedded .text-chat-view {
   flex: 1 0 auto;
   display: flex;
   flex-flow: column nowrap;
 }
 
@@ -1467,17 +1482,17 @@ html[dir="rtl"] .standalone .room-conver
 .text-chat-box {
   flex: 0 0 auto;
   max-height: 40px;
   min-height: 40px;
   width: 100%;
 }
 
 .text-chat-entries {
-  overflow: scroll;
+  overflow: auto;
 }
 
 .text-chat-entry {
   text-align: end;
   margin-bottom: 1.5em;
 }
 
 .text-chat-entry > p {
@@ -1485,16 +1500,20 @@ html[dir="rtl"] .standalone .room-conver
   border-style: solid;
   border-color: #0095dd;
   border-radius: 10000px;
   padding: .5em 1em;
   /* Drop the default margins from the 'p' element. */
   margin: 0;
   /* inline-block stops the elements taking 100% of the text-chat-view width */
   display: inline-block;
+  /* Split really long strings with no spaces appropriately, whilst limiting the
+     width to 100%. */
+  max-width: 100%;
+  word-wrap: break-word;
 }
 
 .text-chat-entry.received {
   text-align: start;
 }
 
 .text-chat-entry.received > p {
   border-color: #d8d8d8;
@@ -1517,21 +1536,23 @@ html[dir="rtl"] .standalone .room-conver
   margin: auto;
 }
 
 .text-chat-box > form > input {
   width: 100%;
   height: 40px;
   padding: 0 .5em .5em;
   font-size: 1.1em;
+  border: 0;
+  border-top: 1px solid #999;
 }
 
-.fx-embedded .text-chat-box > form > input {
-  border: 0;
-  border-top: 1px solid #999;
+/* turn the visible border blue as a visual indicator of focus */
+.text-chat-box > form > input:focus {
+  border-top: 1px solid #66c9f2;
 }
 
 @media screen and (max-width:640px) {
   .standalone-context-url {
     /* XXX We haven't got UX for standalone yet, so temporarily not displaying
        on narrow window widths. See bug 1153827. */
     display: none;
   }
--- a/browser/components/loop/content/shared/js/textChatView.js
+++ b/browser/components/loop/content/shared/js/textChatView.js
@@ -76,18 +76,22 @@ loop.shared.views.TextChatView = (functi
       // Scroll only if we're right at the bottom of the display.
       this.shouldScroll = node.scrollHeight === node.scrollTop + node.clientHeight;
     },
 
     componentDidUpdate: function() {
       if (this.shouldScroll) {
         // This ensures the paint is complete.
         window.requestAnimationFrame(function() {
-          var node = this.getDOMNode();
-          node.scrollTop = node.scrollHeight - node.clientHeight;
+          try {
+            var node = this.getDOMNode();
+            node.scrollTop = node.scrollHeight - node.clientHeight;
+          } catch (ex) {
+            console.error("TextChatEntriesView.componentDidUpdate exception", ex);
+          }
         }.bind(this));
       }
     },
 
     render: function() {
       if (!this.props.messageList.length) {
         return null;
       }
@@ -95,25 +99,24 @@ loop.shared.views.TextChatView = (functi
       return (
         React.createElement("div", {className: "text-chat-entries"}, 
           React.createElement("div", {className: "text-chat-scroller"}, 
             
               this.props.messageList.map(function(entry, i) {
                 if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
                   switch (entry.contentType) {
                     case CHAT_CONTENT_TYPES.ROOM_NAME:
-                      return React.createElement(TextChatRoomName, {message: entry.message});
+                      return React.createElement(TextChatRoomName, {key: i, message: entry.message});
                     case CHAT_CONTENT_TYPES.CONTEXT:
                       return (
-                        React.createElement("div", {className: "context-url-view-wrapper"}, 
+                        React.createElement("div", {key: i, className: "context-url-view-wrapper"}, 
                           React.createElement(sharedViews.ContextUrlView, {
                             allowClick: true, 
                             description: entry.message, 
                             dispatcher: this.props.dispatcher, 
-                            key: i, 
                             showContextTitle: true, 
                             thumbnail: entry.extraData.thumbnail, 
                             url: entry.extraData.location, 
                             useDesktopPaths: false})
                         )
                       );
                     default:
                       console.error("Unsupported contentType", entry.contentType);
@@ -256,18 +259,23 @@ loop.shared.views.TextChatView = (functi
         });
         hasNonSpecialMessages = !!messageList.length;
       }
 
       if (!this.props.showAlways && !this.state.textChatEnabled && !messageList.length) {
         return null;
       }
 
+      var textChatViewClasses = React.addons.classSet({
+        "text-chat-view": true,
+        "text-chat-disabled": !this.state.textChatEnabled
+      });
+
       return (
-        React.createElement("div", {className: "text-chat-view"}, 
+        React.createElement("div", {className: textChatViewClasses}, 
           React.createElement(TextChatEntriesView, {
             dispatcher: this.props.dispatcher, 
             messageList: messageList}), 
           React.createElement(TextChatInputView, {
             dispatcher: this.props.dispatcher, 
             showPlaceholder: !hasNonSpecialMessages, 
             textChatEnabled: this.state.textChatEnabled})
         )
--- a/browser/components/loop/content/shared/js/textChatView.jsx
+++ b/browser/components/loop/content/shared/js/textChatView.jsx
@@ -76,18 +76,22 @@ loop.shared.views.TextChatView = (functi
       // Scroll only if we're right at the bottom of the display.
       this.shouldScroll = node.scrollHeight === node.scrollTop + node.clientHeight;
     },
 
     componentDidUpdate: function() {
       if (this.shouldScroll) {
         // This ensures the paint is complete.
         window.requestAnimationFrame(function() {
-          var node = this.getDOMNode();
-          node.scrollTop = node.scrollHeight - node.clientHeight;
+          try {
+            var node = this.getDOMNode();
+            node.scrollTop = node.scrollHeight - node.clientHeight;
+          } catch (ex) {
+            console.error("TextChatEntriesView.componentDidUpdate exception", ex);
+          }
         }.bind(this));
       }
     },
 
     render: function() {
       if (!this.props.messageList.length) {
         return null;
       }
@@ -95,25 +99,24 @@ loop.shared.views.TextChatView = (functi
       return (
         <div className="text-chat-entries">
           <div className="text-chat-scroller">
             {
               this.props.messageList.map(function(entry, i) {
                 if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
                   switch (entry.contentType) {
                     case CHAT_CONTENT_TYPES.ROOM_NAME:
-                      return <TextChatRoomName message={entry.message}/>;
+                      return <TextChatRoomName key={i} message={entry.message}/>;
                     case CHAT_CONTENT_TYPES.CONTEXT:
                       return (
-                        <div className="context-url-view-wrapper">
+                        <div key={i} className="context-url-view-wrapper">
                           <sharedViews.ContextUrlView
                             allowClick={true}
                             description={entry.message}
                             dispatcher={this.props.dispatcher}
-                            key={i}
                             showContextTitle={true}
                             thumbnail={entry.extraData.thumbnail}
                             url={entry.extraData.location}
                             useDesktopPaths={false} />
                         </div>
                       );
                     default:
                       console.error("Unsupported contentType", entry.contentType);
@@ -256,18 +259,23 @@ loop.shared.views.TextChatView = (functi
         });
         hasNonSpecialMessages = !!messageList.length;
       }
 
       if (!this.props.showAlways && !this.state.textChatEnabled && !messageList.length) {
         return null;
       }
 
+      var textChatViewClasses = React.addons.classSet({
+        "text-chat-view": true,
+        "text-chat-disabled": !this.state.textChatEnabled
+      });
+
       return (
-        <div className="text-chat-view">
+        <div className={textChatViewClasses}>
           <TextChatEntriesView
             dispatcher={this.props.dispatcher}
             messageList={messageList} />
           <TextChatInputView
             dispatcher={this.props.dispatcher}
             showPlaceholder={!hasNonSpecialMessages}
             textChatEnabled={this.state.textChatEnabled} />
         </div>
--- a/browser/components/loop/ui/react-frame-component.js
+++ b/browser/components/loop/ui/react-frame-component.js
@@ -64,19 +64,23 @@ window.Frame = React.createClass({
       var iframeHead = childDoc.querySelector("head");
       var parentHeadChildren = document.querySelector("head").children;
 
       [].forEach.call(parentHeadChildren, function(parentHeadNode) {
 
         // if this node is a CSS stylesheet...
         if (isStyleSheet(parentHeadNode)) {
           // and it has a class different from the one that this frame does,
-          // return immediately instead of appending it.
-          if (parentHeadNode.hasAttribute("class") && this.props.cssClass &&
-            parentHeadNode.getAttribute("class") !== this.props.cssClass) {
+          // return immediately instead of appending it.  Note that this
+          // explicitly does not check for cssClass existence, because
+          // non-existence of cssClass will be different from a style
+          // element that does have a class on it, and we want it to return
+          // in that case.
+          if (parentHeadNode.hasAttribute("class") &&
+              parentHeadNode.getAttribute("class") !== this.props.cssClass) {
             return;
           }
         }
 
         iframeHead.appendChild(parentHeadNode.cloneNode(true));
       }.bind(this));
 
       var contents = React.createElement("div",
--- a/browser/components/loop/ui/ui-showcase.js
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -288,33 +288,58 @@
     client: {},
     mozLoop: navigator.mozLoop,
     sdkDriver: mockSDK
   });
   var textChatStore = new loop.store.TextChatStore(dispatcher, {
     sdkDriver: mockSDK
   });
 
-  textChatStore.setStoreState({
-    // XXX Disabled until we start sorting out some of the layouts.
-    textChatEnabled: false
-  });
-
   // Update the text chat store with the room info.
   textChatStore.updateRoomInfo(new sharedActions.UpdateRoomInfo({
     roomName: "A Very Long Conversation Name",
     roomOwner: "fake",
     roomUrl: "http://showcase",
     urls: [{
       description: "A wonderful page!",
       location: "http://wonderful.invalid"
       // use the fallback thumbnail
     }]
   }));
 
+  textChatStore.setStoreState({textChatEnabled: true});
+
+  dispatcher.dispatch(new sharedActions.SendTextChatMessage({
+    contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
+    message: "Rheet!"
+  }));
+  dispatcher.dispatch(new sharedActions.ReceivedTextChatMessage({
+    contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
+    message: "Hi there"
+  }));
+  dispatcher.dispatch(new sharedActions.SendTextChatMessage({
+    contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
+    message: "Check out this menu from DNA Pizza:" +
+    " http://example.com/DNA/pizza/menu/lots-of-different-kinds-of-pizza/" +
+    "%8D%E0%B8%88%E0%B8%A1%E0%B8%A3%E0%8D%E0%B8%88%E0%B8%A1%E0%B8%A3%E0%"
+  }));
+  dispatcher.dispatch(new sharedActions.SendTextChatMessage({
+    contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
+    message: "Nowforareallylongwordwithoutspacesorpunctuationwhichshouldcause" +
+    "linewrappingissuesifthecssiswrong"
+  }));
+  dispatcher.dispatch(new sharedActions.ReceivedTextChatMessage({
+    contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
+    message: "That avocado monkey-brains pie sounds tasty!"
+  }));
+  dispatcher.dispatch(new sharedActions.SendTextChatMessage({
+    contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
+    message: "What time should we meet?"
+  }));
+
   loop.store.StoreMixin.register({
     activeRoomStore: activeRoomStore,
     conversationStore: conversationStore,
     feedbackStore: feedbackStore,
     textChatStore: textChatStore
   });
 
   // Local mocks
@@ -816,28 +841,28 @@
               React.createElement("div", {className: "standalone"}, 
                 React.createElement(UnsupportedDeviceView, {platform: "ios"})
               )
             )
           ), 
 
           React.createElement(Section, {name: "DesktopRoomConversationView"}, 
             React.createElement(FramedExample, {width: 298, height: 254, 
-              summary: "Desktop room conversation (invitation)"}, 
+              summary: "Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)"}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopRoomConversationView, {
                   roomStore: invitationRoomStore, 
                   dispatcher: dispatcher, 
                   mozLoop: navigator.mozLoop, 
                   localPosterUrl: "sample-img/video-screen-local.png", 
                   roomState: ROOM_STATES.INIT})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 298, height: 254, 
+            React.createElement(FramedExample, {width: 298, height: 394, dashed: true, 
               summary: "Desktop room conversation (loading)"}, 
               /* Hide scrollbars here. Rotating loading div overflows and causes
                scrollbars to appear */
               React.createElement("div", {className: "fx-embedded overflow-hidden"}, 
                 React.createElement(DesktopRoomConversationView, {
                   roomStore: desktopRoomStoreLoading, 
                   dispatcher: dispatcher, 
                   mozLoop: navigator.mozLoop, 
@@ -855,28 +880,28 @@
                   dispatcher: dispatcher, 
                   mozLoop: navigator.mozLoop, 
                   localPosterUrl: "sample-img/video-screen-local.png", 
                   remotePosterUrl: "sample-img/video-screen-remote.png", 
                   roomState: ROOM_STATES.HAS_PARTICIPANTS})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 298, height: 254, 
+            React.createElement(FramedExample, {width: 298, height: 394, dashed: true, 
                            summary: "Desktop room conversation local face-mute"}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopRoomConversationView, {
                   roomStore: desktopLocalFaceMuteRoomStore, 
                   dispatcher: dispatcher, 
                   mozLoop: navigator.mozLoop, 
                   remotePosterUrl: "sample-img/video-screen-remote.png"})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 298, height: 254, 
+            React.createElement(FramedExample, {width: 298, height: 394, dashed: true, 
                            summary: "Desktop room conversation remote face-mute"}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopRoomConversationView, {
                   roomStore: desktopRemoteFaceMuteRoomStore, 
                   dispatcher: dispatcher, 
                   mozLoop: navigator.mozLoop, 
                   localPosterUrl: "sample-img/video-screen-local.png"})
               )
@@ -1063,16 +1088,17 @@
                   activeRoomStore: failedRoomStore, 
                   isFirefox: false})
               )
             )
           ), 
 
           React.createElement(Section, {name: "StandaloneRoomView (Mobile)"}, 
             React.createElement(FramedExample, {width: 600, height: 480, cssClass: "standalone", 
+                           dashed: true, 
                            onContentsRendered: updatingActiveRoomStore.forcedUpdate, 
                            summary: "Standalone room conversation (has-participants, 600x480)"}, 
                 React.createElement("div", {className: "standalone"}, 
                   React.createElement(StandaloneRoomView, {
                     dispatcher: dispatcher, 
                     activeRoomStore: updatingActiveRoomStore, 
                     roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                     isFirefox: true, 
@@ -1092,24 +1118,36 @@
                     isFirefox: true, 
                     localPosterUrl: "sample-img/video-screen-local.png", 
                     remotePosterUrl: "sample-img/video-screen-remote.png", 
                     screenSharePosterUrl: "sample-img/video-screen-terminal.png"})
                 )
             )
           ), 
 
-          React.createElement(Section, {name: "TextChatView (standalone)"}, 
-            React.createElement(FramedExample, {width: 200, height: 400, cssClass: "standalone", 
-                          summary: "Standalone Text Chat conversation (200 x 400)"}, 
+          React.createElement(Section, {name: "TextChatView"}, 
+            React.createElement(FramedExample, {width: 298, height: 160, dashed: true, 
+                           summary: "TextChatView: desktop embedded"}, 
+              React.createElement("div", {className: "fx-embedded"}, 
+                React.createElement(TextChatView, {dispatcher: dispatcher, 
+                              showAlways: false, 
+                              showRoomName: false})
+              )
+            ), 
+
+            React.createElement(FramedExample, {width: 200, height: 400, dashed: true, 
+                           cssClass: "standalone", 
+                           summary: "Standalone Text Chat conversation (200x400)"}, 
               React.createElement("div", {className: "standalone text-chat-example"}, 
-                React.createElement(TextChatView, {
-                  dispatcher: dispatcher, 
-                  showAlways: true, 
-                  showRoomName: true})
+                React.createElement("div", {className: "media-wrapper"}, 
+                  React.createElement(TextChatView, {
+                    dispatcher: dispatcher, 
+                    showAlways: true, 
+                    showRoomName: true})
+                )
               )
             )
           ), 
 
           React.createElement(Section, {name: "SVG icons preview", className: "svg-icons"}, 
             React.createElement(Example, {summary: "10x10"}, 
               React.createElement(SVGIcons, {size: "10x10"})
             ), 
--- a/browser/components/loop/ui/ui-showcase.jsx
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -288,33 +288,58 @@
     client: {},
     mozLoop: navigator.mozLoop,
     sdkDriver: mockSDK
   });
   var textChatStore = new loop.store.TextChatStore(dispatcher, {
     sdkDriver: mockSDK
   });
 
-  textChatStore.setStoreState({
-    // XXX Disabled until we start sorting out some of the layouts.
-    textChatEnabled: false
-  });
-
   // Update the text chat store with the room info.
   textChatStore.updateRoomInfo(new sharedActions.UpdateRoomInfo({
     roomName: "A Very Long Conversation Name",
     roomOwner: "fake",
     roomUrl: "http://showcase",
     urls: [{
       description: "A wonderful page!",
       location: "http://wonderful.invalid"
       // use the fallback thumbnail
     }]
   }));
 
+  textChatStore.setStoreState({textChatEnabled: true});
+
+  dispatcher.dispatch(new sharedActions.SendTextChatMessage({
+    contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
+    message: "Rheet!"
+  }));
+  dispatcher.dispatch(new sharedActions.ReceivedTextChatMessage({
+    contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
+    message: "Hi there"
+  }));
+  dispatcher.dispatch(new sharedActions.SendTextChatMessage({
+    contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
+    message: "Check out this menu from DNA Pizza:" +
+    " http://example.com/DNA/pizza/menu/lots-of-different-kinds-of-pizza/" +
+    "%8D%E0%B8%88%E0%B8%A1%E0%B8%A3%E0%8D%E0%B8%88%E0%B8%A1%E0%B8%A3%E0%"
+  }));
+  dispatcher.dispatch(new sharedActions.SendTextChatMessage({
+    contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
+    message: "Nowforareallylongwordwithoutspacesorpunctuationwhichshouldcause" +
+    "linewrappingissuesifthecssiswrong"
+  }));
+  dispatcher.dispatch(new sharedActions.ReceivedTextChatMessage({
+    contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
+    message: "That avocado monkey-brains pie sounds tasty!"
+  }));
+  dispatcher.dispatch(new sharedActions.SendTextChatMessage({
+    contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
+    message: "What time should we meet?"
+  }));
+
   loop.store.StoreMixin.register({
     activeRoomStore: activeRoomStore,
     conversationStore: conversationStore,
     feedbackStore: feedbackStore,
     textChatStore: textChatStore
   });
 
   // Local mocks
@@ -816,28 +841,28 @@
               <div className="standalone">
                 <UnsupportedDeviceView platform="ios"/>
               </div>
             </Example>
           </Section>
 
           <Section name="DesktopRoomConversationView">
             <FramedExample width={298} height={254}
-              summary="Desktop room conversation (invitation)">
+              summary="Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)">
               <div className="fx-embedded">
                 <DesktopRoomConversationView
                   roomStore={invitationRoomStore}
                   dispatcher={dispatcher}
                   mozLoop={navigator.mozLoop}
                   localPosterUrl="sample-img/video-screen-local.png"
                   roomState={ROOM_STATES.INIT} />
               </div>
             </FramedExample>
 
-            <FramedExample width={298} height={254}
+            <FramedExample width={298} height={394} dashed={true}
               summary="Desktop room conversation (loading)">
               {/* Hide scrollbars here. Rotating loading div overflows and causes
                scrollbars to appear */}
               <div className="fx-embedded overflow-hidden">
                 <DesktopRoomConversationView
                   roomStore={desktopRoomStoreLoading}
                   dispatcher={dispatcher}
                   mozLoop={navigator.mozLoop}
@@ -855,28 +880,28 @@
                   dispatcher={dispatcher}
                   mozLoop={navigator.mozLoop}
                   localPosterUrl="sample-img/video-screen-local.png"
                   remotePosterUrl="sample-img/video-screen-remote.png"
                   roomState={ROOM_STATES.HAS_PARTICIPANTS} />
               </div>
             </FramedExample>
 
-            <FramedExample width={298} height={254}
+            <FramedExample width={298} height={394} dashed={true}
                            summary="Desktop room conversation local face-mute">
               <div className="fx-embedded">
                 <DesktopRoomConversationView
                   roomStore={desktopLocalFaceMuteRoomStore}
                   dispatcher={dispatcher}
                   mozLoop={navigator.mozLoop}
                   remotePosterUrl="sample-img/video-screen-remote.png" />
               </div>
             </FramedExample>
 
-            <FramedExample width={298} height={254}
+            <FramedExample width={298} height={394} dashed={true}
                            summary="Desktop room conversation remote face-mute">
               <div className="fx-embedded">
                 <DesktopRoomConversationView
                   roomStore={desktopRemoteFaceMuteRoomStore}
                   dispatcher={dispatcher}
                   mozLoop={navigator.mozLoop}
                   localPosterUrl="sample-img/video-screen-local.png" />
               </div>
@@ -1063,16 +1088,17 @@
                   activeRoomStore={failedRoomStore}
                   isFirefox={false} />
               </div>
             </FramedExample>
           </Section>
 
           <Section name="StandaloneRoomView (Mobile)">
             <FramedExample width={600} height={480} cssClass="standalone"
+                           dashed={true}
                            onContentsRendered={updatingActiveRoomStore.forcedUpdate}
                            summary="Standalone room conversation (has-participants, 600x480)">
                 <div className="standalone">
                   <StandaloneRoomView
                     dispatcher={dispatcher}
                     activeRoomStore={updatingActiveRoomStore}
                     roomState={ROOM_STATES.HAS_PARTICIPANTS}
                     isFirefox={true}
@@ -1092,24 +1118,36 @@
                     isFirefox={true}
                     localPosterUrl="sample-img/video-screen-local.png"
                     remotePosterUrl="sample-img/video-screen-remote.png"
                     screenSharePosterUrl="sample-img/video-screen-terminal.png" />
                 </div>
             </FramedExample>
           </Section>
 
-          <Section name="TextChatView (standalone)">
-            <FramedExample width={200} height={400} cssClass="standalone"
-                          summary="Standalone Text Chat conversation (200 x 400)">
+          <Section name="TextChatView">
+            <FramedExample width={298} height={160} dashed={true}
+                           summary="TextChatView: desktop embedded">
+              <div className="fx-embedded">
+                <TextChatView dispatcher={dispatcher}
+                              showAlways={false}
+                              showRoomName={false} />
+              </div>
+            </FramedExample>
+
+            <FramedExample width={200} height={400} dashed={true}
+                           cssClass="standalone"
+                           summary="Standalone Text Chat conversation (200x400)">
               <div className="standalone text-chat-example">
-                <TextChatView
-                  dispatcher={dispatcher}
-                  showAlways={true}
-                  showRoomName={true} />
+                <div className="media-wrapper">
+                  <TextChatView
+                    dispatcher={dispatcher}
+                    showAlways={true}
+                    showRoomName={true} />
+                </div>
               </div>
             </FramedExample>
           </Section>
 
           <Section name="SVG icons preview" className="svg-icons">
             <Example summary="10x10">
               <SVGIcons size="10x10"/>
             </Example>
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -161,16 +161,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/ReaderParent.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher",
                                   "resource://gre/modules/AddonWatcher.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
                                   "resource://gre/modules/LightweightThemeManager.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
+                                  "resource://gre/modules/AppConstants.jsm");
+
 const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
 const PREF_PLUGINS_UPDATEURL  = "plugins.update.url";
 
 // Seconds of idle before trying to create a bookmarks backup.
 const BOOKMARKS_BACKUP_IDLE_TIME_SEC = 8 * 60;
 // Minimum interval between backups.  We try to not create more than one backup
 // per interval.
 const BOOKMARKS_BACKUP_MIN_INTERVAL_DAYS = 1;
@@ -1120,16 +1123,27 @@ BrowserGlue.prototype = {
       }
       catch (ex) { /* Don't break the default prompt if telemetry is broken. */ }
 
       if (isDefault) {
         let now = Date.now().toString().slice(0, -3);
         Services.prefs.setCharPref("browser.shell.mostRecentDateSetAsDefault", now);
       }
 
+      if (Services.prefs.getIntPref("browser.shell.windows10DefaultBrowserABTest") == -1) {
+        let abTest = Math.round(Math.random());
+        Services.prefs.setIntPref("browser.shell.windows10DefaultBrowserABTest", abTest);
+      }
+
+      if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
+        let abTest = Services.prefs.getIntPref("browser.shell.windows10DefaultBrowserABTest");
+        let result = abTest * 2 + Number(isDefault);
+        Services.telemetry.getHistogramById("WIN_10_DEFAULT_BROWSER_AB_TEST").add(result);
+      }
+
       if (shouldCheck && !isDefault && !willRecoverSession) {
         Services.tm.mainThread.dispatch(function() {
           DefaultBrowserCheck.prompt(RecentWindow.getMostRecentBrowserWindow());
         }.bind(this), Ci.nsIThread.DISPATCH_NORMAL);
       }
     }
 
 #ifdef E10S_TESTING_ONLY
--- a/browser/components/shell/nsWindowsShellService.cpp
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -739,16 +739,17 @@ nsWindowsShellService::SetDefaultBrowser
 
   if (aForAllUsers) {
     appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal");
   } else {
     appHelperPath.AppendLiteral(" /SetAsDefaultAppUser");
   }
 
   nsresult rv = LaunchHelper(appHelperPath);
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (NS_SUCCEEDED(rv) && IsWin8OrLater()) {
     if (aClaimAllTypes) {
       if (IsWin10OrLater()) {
         rv = LaunchModernSettingsDialogDefaultApps();
       } else {
         rv = LaunchControlPanelDefaultPrograms();
       }
       // The above call should never really fail, but just in case
@@ -756,38 +757,44 @@ nsWindowsShellService::SetDefaultBrowser
       if (NS_FAILED(rv)) {
         if (IsWin10OrLater()) {
           rv = InvokeHTTPOpenAsVerb();
         } else {
           rv = LaunchHTTPHandlerPane();
         }
       }
     } else {
-      // Windows 10 blocks attempts to load the HTTP Handler
-      // association dialog, so the modern Settings dialog
-      // is opened with the Default Apps view loaded.
+      // Windows 10 blocks attempts to load the
+      // HTTP Handler association dialog.
       if (IsWin10OrLater()) {
-        rv = InvokeHTTPOpenAsVerb();
+        if (prefs) {
+          int32_t abTest;
+          rv = prefs->GetIntPref("browser.shell.windows10DefaultBrowserABTest", &abTest);
+          if (NS_SUCCEEDED(rv) && abTest == 0) {
+            rv = InvokeHTTPOpenAsVerb();
+          } else {
+            rv = LaunchModernSettingsDialogDefaultApps();
+          }
+        }
       } else {
         rv = LaunchHTTPHandlerPane();
       }
 
       // The above call should never really fail, but just in case
       // fall back to showing control panel for all defaults
       if (NS_FAILED(rv)) {
         if (IsWin10OrLater()) {
           rv = LaunchModernSettingsDialogDefaultApps();
         } else {
           rv = LaunchControlPanelDefaultPrograms();
         }
       }
     }
   }
 
-  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (prefs) {
     (void) prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
--- a/browser/devtools/framework/test/browser_toolbox_tool_ready.js
+++ b/browser/devtools/framework/test/browser_toolbox_tool_ready.js
@@ -13,16 +13,21 @@ function performChecks(target) {
     let toolIds = gDevTools.getToolDefinitionArray()
                            .filter(def => def.isTargetSupported(target))
                            .map(def => def.id);
 
     let toolbox;
     for (let index = 0; index < toolIds.length; index++) {
       let toolId = toolIds[index];
 
+      // FIXME Bug 1175850 - Enable storage inspector tests after upgrading for E10S
+      if (toolId === "storage") {
+        continue;
+      }
+
       info("About to open " + index + "/" + toolId);
       toolbox = yield gDevTools.showToolbox(target, toolId);
       ok(toolbox, "toolbox exists for " + toolId);
       is(toolbox.currentToolId, toolId, "currentToolId should be " + toolId);
 
       let panel = toolbox.getCurrentPanel();
       ok(panel.isReady, toolId + " panel should be ready");
     }
--- a/browser/devtools/framework/test/browser_toolbox_tool_remote_reopen.js
+++ b/browser/devtools/framework/test/browser_toolbox_tool_remote_reopen.js
@@ -44,16 +44,21 @@ function runTools(target) {
     let toolIds = gDevTools.getToolDefinitionArray()
                            .filter(def => def.isTargetSupported(target))
                            .map(def => def.id);
 
     let toolbox;
     for (let index = 0; index < toolIds.length; index++) {
       let toolId = toolIds[index];
 
+      // FIXME Bug 1175850 - Enable storage inspector tests after upgrading for E10S
+      if (toolId === "storage") {
+        continue;
+      }
+
       info("About to open " + index + "/" + toolId);
       toolbox = yield gDevTools.showToolbox(target, toolId, "window");
       ok(toolbox, "toolbox exists for " + toolId);
       is(toolbox.currentToolId, toolId, "currentToolId should be " + toolId);
 
       let panel = toolbox.getCurrentPanel();
       ok(panel.isReady, toolId + " panel should be ready");
     }
--- a/browser/devtools/framework/test/browser_toolbox_window_shortcuts.js
+++ b/browser/devtools/framework/test/browser_toolbox_window_shortcuts.js
@@ -38,16 +38,22 @@ function testShortcuts(aToolbox, aIndex)
   } else if (aIndex == toolIDs.length) {
     tidyUp();
     return;
   }
 
   toolbox = aToolbox;
   info("Toolbox fired a `ready` event");
 
+  // FIXME Bug 1175850 - Enable storage inspector tests after upgrading for E10S
+  if (toolIDs[aIndex] === "storage") {
+    testShortcuts(toolbox, aIndex + 1);
+    return;
+  }
+
   toolbox.once("select", selectCB);
 
   let key = gDevTools._tools.get(toolIDs[aIndex]).key;
   let toolModifiers = gDevTools._tools.get(toolIDs[aIndex]).modifiers;
   let modifiers = {
     accelKey: toolModifiers.includes("accel"),
     altKey: toolModifiers.includes("alt"),
     shiftKey: toolModifiers.includes("shift"),
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* globals gDevTools, DOMHelpers, toolboxStrings, InspectorFront, Selection,
-   CommandUtils, DevToolsUtils, screenManager, oscpu, Hosts, is64Bit,
-   osString, showDoorhanger, getHighlighterUtils, getPerformanceFront */
+   CommandUtils, DevToolsUtils, Hosts, osString, showDoorhanger,
+   getHighlighterUtils, getPerformanceFront */
 
 "use strict";
 
 const MAX_ORDINAL = 99;
 const ZOOM_PREF = "devtools.toolbox.zoomValue";
 const SPLITCONSOLE_ENABLED_PREF = "devtools.toolbox.splitconsoleEnabled";
 const SPLITCONSOLE_HEIGHT_PREF = "devtools.toolbox.splitconsoleHeight";
 const MIN_ZOOM = 0.5;
@@ -56,29 +56,21 @@ loader.lazyRequireGetter(this, "Selectio
 loader.lazyRequireGetter(this, "InspectorFront",
   "devtools/server/actors/inspector", true);
 loader.lazyRequireGetter(this, "DevToolsUtils",
   "devtools/toolkit/DevToolsUtils");
 loader.lazyRequireGetter(this, "showDoorhanger",
   "devtools/shared/doorhanger", true);
 loader.lazyRequireGetter(this, "getPerformanceFront",
   "devtools/performance/front", true);
+loader.lazyRequireGetter(this, "system",
+  "devtools/toolkit/shared/system");
 loader.lazyGetter(this, "osString", () => {
   return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
 });
-loader.lazyGetter(this, "screenManager", () => {
-  return Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager);
-});
-loader.lazyGetter(this, "oscpu", () => {
-  return Cc["@mozilla.org/network/protocol;1?name=http"]
-           .getService(Ci.nsIHttpProtocolHandler).oscpu;
-});
-loader.lazyGetter(this, "is64Bit", () => {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).is64Bit;
-});
 loader.lazyGetter(this, "registerHarOverlay", () => {
   return require("devtools/netmonitor/har/toolbox-overlay.js").register;
 });
 
 // White-list buttons that can be toggled to prevent adding prefs for
 // addons that have manually inserted toolbarbuttons into DOM.
 // (By default, supported target is only local tab)
 const ToolboxButtons = exports.ToolboxButtons = [
@@ -432,21 +424,19 @@ Toolbox.prototype = {
 
       this.emit("ready");
     }.bind(this)).then(null, console.error.bind(console));
   },
 
   _pingTelemetry: function() {
     this._telemetry.toolOpened("toolbox");
 
-    this._telemetry.logOncePerBrowserVersion(OS_HISTOGRAM,
-                                             this._getOsCpu());
-    this._telemetry.logOncePerBrowserVersion(OS_IS_64_BITS, is64Bit ? 1 : 0);
-    this._telemetry.logOncePerBrowserVersion(SCREENSIZE_HISTOGRAM,
-                                             this._getScreenDimensions());
+    this._telemetry.logOncePerBrowserVersion(OS_HISTOGRAM, system.getOSCPU());
+    this._telemetry.logOncePerBrowserVersion(OS_IS_64_BITS, system.is64Bit ? 1 : 0);
+    this._telemetry.logOncePerBrowserVersion(SCREENSIZE_HISTOGRAM, system.getScreenDimensions());
   },
 
   /**
    * Because our panels are lazy loaded this is a good place to watch for
    * "pref-changed" events.
    * @param  {String} event
    *         The event type, "pref-changed".
    * @param  {Object} data
@@ -1796,91 +1786,16 @@ Toolbox.prototype = {
    * Get the toolbox's notification box
    *
    * @return The notification box element.
    */
   getNotificationBox: function() {
     return this.doc.getElementById("toolbox-notificationbox");
   },
 
-  _getScreenDimensions: function() {
-    let width = {};
-    let height = {};
-
-    screenManager.primaryScreen.GetRect({}, {}, width, height);
-    let dims = width.value + "x" + height.value;
-
-    if (width.value < 800 || height.value < 600) {
-      return 0;
-    }
-    if (dims === "800x600") {
-      return 1;
-    }
-    if (dims === "1024x768") {
-      return 2;
-    }
-    if (dims === "1280x800") {
-      return 3;
-    }
-    if (dims === "1280x1024") {
-      return 4;
-    }
-    if (dims === "1366x768") {
-      return 5;
-    }
-    if (dims === "1440x900") {
-      return 6;
-    }
-    if (dims === "1920x1080") {
-      return 7;
-    }
-    if (dims === "2560×1440") {
-      return 8;
-    }
-    if (dims === "2560×1600") {
-      return 9;
-    }
-    if (dims === "2880x1800") {
-      return 10;
-    }
-    if (width.value > 2880 || height.value > 1800) {
-      return 12;
-    }
-
-    // Other dimension such as a VM.
-    return 11;
-  },
-
-  _getOsCpu: function() {
-    if (oscpu.includes("NT 5.1") || oscpu.includes("NT 5.2")) {
-      return 0;
-    }
-    if (oscpu.includes("NT 6.0")) {
-      return 1;
-    }
-    if (oscpu.includes("NT 6.1")) {
-      return 2;
-    }
-    if (oscpu.includes("NT 6.2")) {
-      return 3;
-    }
-    if (oscpu.includes("NT 6.3")) {
-      return 4;
-    }
-    if (oscpu.includes("OS X")) {
-      return 5;
-    }
-    if (oscpu.includes("Linux")) {
-      return 6;
-    }
-
-    // Other OS.
-    return 12;
-  },
-
   /**
    * Destroy the current host, and remove event listeners from its frame.
    *
    * @return {promise} to be resolved when the host is destroyed.
    */
   destroyHost: function() {
     // The host iframe's contentDocument may already be gone.
     if (this.doc) {
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -94,30 +94,31 @@ browser.jar:
     content/browser/devtools/webaudioeditor/models.js                  (webaudioeditor/models.js)
     content/browser/devtools/webaudioeditor/controller.js              (webaudioeditor/controller.js)
     content/browser/devtools/webaudioeditor/views/utils.js             (webaudioeditor/views/utils.js)
     content/browser/devtools/webaudioeditor/views/context.js           (webaudioeditor/views/context.js)
     content/browser/devtools/webaudioeditor/views/inspector.js         (webaudioeditor/views/inspector.js)
     content/browser/devtools/webaudioeditor/views/properties.js        (webaudioeditor/views/properties.js)
     content/browser/devtools/webaudioeditor/views/automation.js        (webaudioeditor/views/automation.js)
     content/browser/devtools/performance.xul                           (performance/performance.xul)
-*   content/browser/devtools/performance/system.js                     (performance/system.js)
     content/browser/devtools/performance/performance-controller.js     (performance/performance-controller.js)
     content/browser/devtools/performance/performance-view.js           (performance/performance-view.js)
     content/browser/devtools/performance/views/overview.js             (performance/views/overview.js)
     content/browser/devtools/performance/views/toolbar.js              (performance/views/toolbar.js)
     content/browser/devtools/performance/views/details.js              (performance/views/details.js)
     content/browser/devtools/performance/views/details-subview.js      (performance/views/details-abstract-subview.js)
     content/browser/devtools/performance/views/details-waterfall.js    (performance/views/details-waterfall.js)
     content/browser/devtools/performance/views/details-js-call-tree.js      (performance/views/details-js-call-tree.js)
     content/browser/devtools/performance/views/details-js-flamegraph.js     (performance/views/details-js-flamegraph.js)
     content/browser/devtools/performance/views/details-memory-call-tree.js  (performance/views/details-memory-call-tree.js)
     content/browser/devtools/performance/views/details-memory-flamegraph.js (performance/views/details-memory-flamegraph.js)
+    content/browser/devtools/performance/views/details-optimizations.js     (performance/views/details-optimizations.js)
+    content/browser/devtools/performance/views/optimizations-list.js        (performance/views/optimizations-list.js)
+    content/browser/devtools/performance/views/frames-list.js               (performance/views/frames-list.js)
     content/browser/devtools/performance/views/recordings.js           (performance/views/recordings.js)
-    content/browser/devtools/performance/views/jit-optimizations.js    (performance/views/jit-optimizations.js)
     content/browser/devtools/commandline.css                           (commandline/commandline.css)
     content/browser/devtools/commandlineoutput.xhtml                   (commandline/commandlineoutput.xhtml)
     content/browser/devtools/commandlinetooltip.xhtml                  (commandline/commandlinetooltip.xhtml)
 *   content/browser/devtools/framework/toolbox-window.xul              (framework/toolbox-window.xul)
     content/browser/devtools/framework/toolbox-options.xul             (framework/toolbox-options.xul)
     content/browser/devtools/framework/toolbox-options.js              (framework/toolbox-options.js)
     content/browser/devtools/framework/toolbox.xul                     (framework/toolbox.xul)
     content/browser/devtools/framework/options-panel.css               (framework/options-panel.css)
--- a/browser/devtools/performance/modules/logic/recording-model.js
+++ b/browser/devtools/performance/modules/logic/recording-model.js
@@ -180,18 +180,17 @@ RecordingModel.prototype = {
       return Date.now() - this._localStartTime;
     } else {
       return this._duration;
     }
   },
 
   /**
    * Returns configuration object of specifying whether the recording
-   * was started withTicks, withMemory and withAllocations and other
-   * recording options.
+   * was started withTicks, withMemory and withAllocations, and other configurations.
    * @return object
    */
   getConfiguration: function () {
     return this._configuration;
   },
 
   /**
    * Gets the accumulated markers in the current recording.
--- a/browser/devtools/performance/modules/logic/tree-model.js
+++ b/browser/devtools/performance/modules/logic/tree-model.js
@@ -454,27 +454,28 @@ FrameNode.prototype = {
    */
   _computeInfo: function() {
     let categoryData = CATEGORY_MAPPINGS[this.category] || {};
     let parsedData = FrameUtils.parseLocation(this.location, this.line, this.column);
     parsedData.nodeType = "Frame";
     parsedData.categoryData = categoryData;
     parsedData.isContent = this.isContent;
     parsedData.isMetaCategory = this.isMetaCategory;
+    parsedData.hasOptimizations = this.hasOptimizations();
 
     return this._data = parsedData;
   },
 
   /**
    * Returns whether or not the frame node has an JITOptimizations model.
    *
    * @return {Boolean}
    */
   hasOptimizations: function () {
-    return !!this._optimizations;
+    return !this.isMetaCategory && !!this._optimizations;
   },
 
   /**
    * Returns the underlying JITOptimizations model representing
    * the optimization attempts occuring in this frame.
    *
    * @return {JITOptimizations|null}
    */
--- a/browser/devtools/performance/modules/widgets/graphs.js
+++ b/browser/devtools/performance/modules/widgets/graphs.js
@@ -5,18 +5,18 @@
 
 /**
  * This file contains the base line graph that all Performance line graphs use.
  */
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
 const { Task } = require("resource://gre/modules/Task.jsm");
 const { Heritage } = require("resource:///modules/devtools/ViewHelpers.jsm");
-const { LineGraphWidget } = require("devtools/shared/widgets/Graphs");
-const { BarGraphWidget } = require("devtools/shared/widgets/Graphs");
+const LineGraphWidget = require("devtools/shared/widgets/LineGraphWidget");
+const BarGraphWidget = require("devtools/shared/widgets/BarGraphWidget");
 const { CanvasGraphUtils } = require("devtools/shared/widgets/Graphs");
 
 loader.lazyRequireGetter(this, "promise");
 loader.lazyRequireGetter(this, "EventEmitter",
   "devtools/toolkit/event-emitter");
 
 loader.lazyRequireGetter(this, "colorUtils",
   "devtools/css-color", true);
--- a/browser/devtools/performance/modules/widgets/tree-view.js
+++ b/browser/devtools/performance/modules/widgets/tree-view.js
@@ -11,16 +11,18 @@
 const { Cc, Ci, Cu, Cr } = require("chrome");
 const { L10N } = require("devtools/performance/global");
 const { Heritage } = require("resource:///modules/devtools/ViewHelpers.jsm");
 const { AbstractTreeItem } = require("resource:///modules/devtools/AbstractTreeItem.jsm");
 
 const MILLISECOND_UNITS = L10N.getStr("table.ms");
 const PERCENTAGE_UNITS = L10N.getStr("table.percentage");
 const URL_LABEL_TOOLTIP = L10N.getStr("table.url.tooltiptext");
+const VIEW_OPTIMIZATIONS_TOOLTIP = L10N.getStr("table.view-optimizations.tooltiptext");
+
 const CALL_TREE_INDENTATION = 16; // px
 
 const DEFAULT_SORTING_PREDICATE = (frameA, frameB) => {
   let dataA = frameA.getDisplayedData();
   let dataB = frameB.getDisplayedData();
   if (this.inverted) {
     // Invert trees, sort by selfPercentage, and then totalPercentage
     if (dataA.selfPercentage === dataB.selfPercentage) {
@@ -80,20 +82,24 @@ const sum = vals => vals.reduce((a, b) =
  * @param number autoExpandDepth [optional]
  *        The depth to which the tree should automatically expand. Defualts to
  *        the caller's `autoExpandDepth` if a caller exists, otherwise defaults
  *        to DEFAULT_AUTO_EXPAND_DEPTH.
  * @param object visibleCells
  *        An object specifying which cells are visible in the tree. Defaults to
  *        the caller's `visibleCells` if a caller exists, otherwise defaults
  *        to DEFAULT_VISIBLE_CELLS.
+ * @param boolean showOptimizationHint [optional]
+ *        Whether or not to show an icon indicating if the frame has optimization
+ *        data.
  */
 function CallView({
   caller, frame, level, hidden, inverted,
-  sortingPredicate, autoExpandDepth, visibleCells
+  sortingPredicate, autoExpandDepth, visibleCells,
+  showOptimizationHint
 }) {
   AbstractTreeItem.call(this, {
     parent: caller,
     level: level|0 - (hidden ? 1 : 0)
   });
 
   this.sortingPredicate = sortingPredicate != null
     ? sortingPredicate
@@ -109,16 +115,17 @@ function CallView({
     ? visibleCells
     : caller ? caller.visibleCells
              : Object.create(DEFAULT_VISIBLE_CELLS);
 
   this.caller = caller;
   this.frame = frame;
   this.hidden = hidden;
   this.inverted = inverted;
+  this.showOptimizationHint = showOptimizationHint;
 
   this._onUrlClick = this._onUrlClick.bind(this);
 };
 
 CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
   /**
    * Creates the view for this tree node.
    * @param nsIDOMNode document
@@ -251,16 +258,26 @@ CallView.prototype = Heritage.extend(Abs
   },
   _createFunctionCell: function(doc, arrowNode, frameName, frameInfo, frameLevel) {
     let cell = doc.createElement("hbox");
     cell.className = "call-tree-cell";
     cell.style.MozMarginStart = (frameLevel * CALL_TREE_INDENTATION) + "px";
     cell.setAttribute("type", "function");
     cell.appendChild(arrowNode);
 
+    // Render optimization link to JIT view if the frame
+    // has optimizations
+    if (this.root.showOptimizationHint && frameInfo.hasOptimizations && !frameInfo.isMetaCategory) {
+      let icon = doc.createElement("description");
+      icon.setAttribute("tooltiptext", VIEW_OPTIMIZATIONS_TOOLTIP);
+      icon.setAttribute("type", "linkable");
+      icon.className = "opt-icon";
+      cell.appendChild(icon);
+    }
+
     // Don't render a name label node if there's no function name. A different
     // location label node will be rendered instead.
     if (frameName) {
       let nameNode = doc.createElement("description");
       nameNode.className = "plain call-tree-name";
       nameNode.setAttribute("flex", "1");
       nameNode.setAttribute("crop", "end");
       nameNode.setAttribute("value", frameName);
@@ -275,16 +292,17 @@ CallView.prototype = Heritage.extend(Abs
     // Don't render an expando-arrow for leaf nodes.
     let hasDescendants = Object.keys(this.frame.calls).length > 0;
     if (!hasDescendants) {
       arrowNode.setAttribute("invisible", "");
     }
 
     return cell;
   },
+
   _appendFunctionDetailsCells: function(doc, cell, frameInfo) {
     if (frameInfo.fileName) {
       let urlNode = doc.createElement("description");
       urlNode.className = "plain call-tree-url";
       urlNode.setAttribute("flex", "1");
       urlNode.setAttribute("crop", "end");
       urlNode.setAttribute("value", frameInfo.fileName);
       urlNode.setAttribute("tooltiptext", URL_LABEL_TOOLTIP + " → " + frameInfo.url);
--- a/browser/devtools/performance/performance-controller.js
+++ b/browser/devtools/performance/performance-controller.js
@@ -1,22 +1,28 @@
 /* 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 { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+const { devtools: loader } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const require = loader.require;
+
 const { Task } = require("resource://gre/modules/Task.jsm");
 const { Heritage, ViewHelpers, WidgetMethods } = require("resource:///modules/devtools/ViewHelpers.jsm");
 
 loader.lazyRequireGetter(this, "Services");
 loader.lazyRequireGetter(this, "promise");
 loader.lazyRequireGetter(this, "EventEmitter",
   "devtools/toolkit/event-emitter");
 loader.lazyRequireGetter(this, "DevToolsUtils",
   "devtools/toolkit/DevToolsUtils");
+loader.lazyRequireGetter(this, "system",
+  "devtools/toolkit/shared/system");
 
 // Logic modules
 
 loader.lazyRequireGetter(this, "L10N",
   "devtools/performance/global", true);
 loader.lazyRequireGetter(this, "TIMELINE_BLUEPRINT",
   "devtools/performance/markers", true);
 loader.lazyRequireGetter(this, "RecordingUtils",
@@ -524,17 +530,17 @@ let PerformanceController = {
   getMultiprocessStatus: function () {
     // If testing, set both supported and enabled to true so we
     // have realtime rendering tests in non-e10s. This function is
     // overridden wholesale in tests when we want to test multiprocess support
     // specifically.
     if (gDevTools.testing) {
       return { supported: true, enabled: true };
     }
-    let supported = SYSTEM.MULTIPROCESS_SUPPORTED;
+    let supported = system.constants.E10S_TESTING_ONLY;
     // This is only checked on tool startup -- requires a restart if
     // e10s subsequently enabled.
     let enabled = this._e10s;
     return { supported, enabled };
   },
 
   /**
    * Called on init, sets an `e10s` attribute on the main view container with
--- a/browser/devtools/performance/performance.xul
+++ b/browser/devtools/performance/performance.xul
@@ -9,30 +9,31 @@
 <?xml-stylesheet href="chrome://browser/skin/devtools/performance.css" type="text/css"?>
 <!DOCTYPE window [
   <!ENTITY % profilerDTD SYSTEM "chrome://browser/locale/devtools/profiler.dtd">
   %profilerDTD;
 ]>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script src="chrome://browser/content/devtools/theme-switching.js"/>
-  <script type="application/javascript" src="performance/system.js"/>
   <script type="application/javascript" src="performance/performance-controller.js"/>
   <script type="application/javascript" src="performance/performance-view.js"/>
   <script type="application/javascript" src="performance/views/overview.js"/>
   <script type="application/javascript" src="performance/views/toolbar.js"/>
   <script type="application/javascript" src="performance/views/details-subview.js"/>
   <script type="application/javascript" src="performance/views/details-waterfall.js"/>
   <script type="application/javascript" src="performance/views/details-js-call-tree.js"/>
   <script type="application/javascript" src="performance/views/details-js-flamegraph.js"/>
   <script type="application/javascript" src="performance/views/details-memory-call-tree.js"/>
   <script type="application/javascript" src="performance/views/details-memory-flamegraph.js"/>
+  <script type="application/javascript" src="performance/views/details-optimizations.js"/>
   <script type="application/javascript" src="performance/views/details.js"/>
   <script type="application/javascript" src="performance/views/recordings.js"/>
-  <script type="application/javascript" src="performance/views/jit-optimizations.js"/>
+  <script type="application/javascript" src="performance/views/optimizations-list.js"/>
+  <script type="application/javascript" src="performance/views/frames-list.js"/>
 
   <popupset id="performance-options-popupset">
     <menupopup id="performance-filter-menupopup"/>
     <menupopup id="performance-options-menupopup">
       <menuitem id="option-show-platform-data"
                 type="checkbox"
                 data-pref="show-platform-data"
                 label="&profilerUI.showPlatformData;"
@@ -135,16 +136,21 @@
                          label="Allocations Tree"
                          hidden="true"
                          data-view="memory-calltree" />
           <toolbarbutton id="select-memory-flamegraph-view"
                          class="devtools-toolbarbutton devtools-button"
                          label="Allocations Chart"
                          hidden="true"
                          data-view="memory-flamegraph" />
+          <toolbarbutton id="select-optimizations-view"
+                         class="devtools-toolbarbutton devtools-button"
+                         label="Optimizations"
+                         hidden="true"
+                         data-view="optimizations" />
         </hbox>
         <spacer flex="1"></spacer>
         <hbox id="performance-toolbar-controls-options"
               class="devtools-toolbarbutton-group">
           <toolbarbutton id="performance-options-button"
                          class="devtools-toolbarbutton devtools-option-toolbarbutton"
                          popup="performance-options-menupopup"
                          tooltiptext="&profilerUI.options.gear.tooltiptext;"/>
@@ -288,30 +294,16 @@
                     <label class="plain call-tree-header"
                            type="function"
                            crop="end"
                            value="&profilerUI.table.function;"
                            tooltiptext="&profilerUI.table.function.tooltip;"/>
                   </hbox>
                   <vbox class="call-tree-cells-container" flex="1"/>
                 </vbox>
-
-                <splitter id="js-call-tree-splitter" class="devtools-side-splitter"/>
-
-                <vbox id="jit-optimizations-view" hidden="true">
-                  <toolbar id="jit-optimizations-toolbar" class="devtools-toolbar">
-                    <hbox id="jit-optimizations-header">
-                      <span class="jit-optimizations-title">&profilerUI.JITOptimizationsTitle;</span>
-                      <span class="header-function-name" />
-                      <span class="header-file opt-url debugger-link" />
-                      <span class="header-line opt-line" />
-                    </hbox>
-                  </toolbar>
-                  <vbox id="jit-optimizations-raw-view"></vbox>
-                </vbox>
               </hbox>
 
               <!-- JS FlameChart -->
               <hbox id="js-flamegraph-view" flex="1">
               </hbox>
 
               <!-- Memory Tree -->
               <vbox id="memory-calltree-view" flex="1">
@@ -333,15 +325,55 @@
                 </hbox>
                 <vbox class="call-tree-cells-container" flex="1"/>
               </vbox>
 
               <!-- Memory FlameChart -->
               <hbox id="memory-flamegraph-view" flex="1">
               </hbox>
 
+              <!-- JIT View -->
+              <hbox id="optimizations-view" flex="1">
+                <hbox id="graph-placeholder" flex="1">
+                </hbox>
+                <splitter id="optimizations-splitter" class="devtools-side-splitter"/>
+                <tabbox id="optimizations-tabs"
+                        class="devtools-sidebar-tabs"
+                        handleCtrlTab="false">
+                  <tabs>
+                    <tab id="optimizations-optimizations-tab"
+                         label="Optimizations" />
+                    <tab id="optimizations-frames-tab"
+                         label="Frames" />
+                  </tabs>
+                  <tabpanels flex="1">
+
+                    <!-- Optimizations Panel -->
+                    <tabpanel id="optimizations-tabpanel"
+                              class="tabpanel-content">
+                      <vbox id="jit-optimizations-view">
+                        <toolbar id="jit-optimizations-toolbar" class="devtools-toolbar">
+                          <hbox id="jit-optimizations-header">
+                            <span class="jit-optimizations-title">&profilerUI.JITOptimizationsTitle;</span>
+                            <span class="header-function-name" />
+                            <span class="header-file opt-url debugger-link" />
+                            <span class="header-line opt-line" />
+                          </hbox>
+                        </toolbar>
+                        <vbox id="jit-optimizations-raw-view"></vbox>
+                      </vbox>
+                    </tabpanel>
+
+                    <!-- Frames Panel -->
+                    <tabpanel id="frames-tabpanel"
+                              class="tabpanel-content">
+                    </tabpanel>
+                  </tabpanels>
+                </tabbox>
+              </hbox>
+              <!-- /JIT View -->
             </deck>
           </deck>
         </vbox>
       </deck>
     </vbox>
   </hbox>
 </window>
deleted file mode 100644
--- a/browser/devtools/performance/system.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-/**
- * A dump file to attach preprocessing directives consumable to the controller
- * without littering our code with directives.
- */
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-const { devtools: loader } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-const require = loader.require;
-
-const SYSTEM = {};
-
-// If e10s is possible on the platform.
-#ifdef E10S_TESTING_ONLY
-SYSTEM.MULTIPROCESS_SUPPORTED = true;
-#endif
--- a/browser/devtools/performance/test/browser.ini
+++ b/browser/devtools/performance/test/browser.ini
@@ -61,18 +61,18 @@ support-files =
 [browser_perf-front-profiler-02.js]
 [browser_perf-front-profiler-03.js]
 [browser_perf-front-profiler-04.js]
 #[browser_perf-front-profiler-05.js] bug 1077464
 #[browser_perf-front-profiler-06.js]
 [browser_perf-front-01.js]
 [browser_perf-front-02.js]
 [browser_perf-highlighted.js]
-[browser_perf-jit-view-01.js]
-[browser_perf-jit-view-02.js]
+#[browser_perf-jit-view-01.js] bug 1176056
+#[browser_perf-jit-view-02.js] bug 1176056
 [browser_perf-loading-01.js]
 [browser_perf-loading-02.js]
 [browser_perf-marker-details-01.js]
 skip-if = os == 'linux' # Bug 1172120
 [browser_perf-options-01.js]
 [browser_perf-options-02.js]
 [browser_perf-options-03.js]
 [browser_perf-options-invert-call-tree-01.js]
@@ -132,15 +132,16 @@ skip-if = os == 'linux' # Bug 1172120
 [browser_profiler_tree-view-03.js]
 [browser_profiler_tree-view-04.js]
 [browser_profiler_tree-view-05.js]
 [browser_profiler_tree-view-06.js]
 [browser_profiler_tree-view-07.js]
 [browser_profiler_tree-view-08.js]
 [browser_profiler_tree-view-09.js]
 [browser_profiler_tree-view-10.js]
+[browser_profiler_tree-view-11.js]
 [browser_timeline-filters-01.js]
 [browser_timeline-filters-02.js]
 [browser_timeline-waterfall-background.js]
 [browser_timeline-waterfall-generic.js]
 [browser_timeline-waterfall-rerender.js]
 [browser_timeline-waterfall-sidebar.js]
 skip-if = os == 'linux' # Bug 1161817
--- a/browser/devtools/performance/test/browser_perf-marker-details-01.js
+++ b/browser/devtools/performance/test/browser_perf-marker-details-01.js
@@ -18,69 +18,77 @@ function* spawnTest() {
     return { submarkers: markers };
   };
 
   const MARKER_TYPES = [
     "Styles", "Reflow", "Paint", "ConsoleTime", "TimeStamp"
   ];
 
   yield startRecording(panel);
+  ok(true, "Recording has started.");
+
   yield waitUntil(() => {
-    // Wait until we get 3 different markers.
+    // Wait until we get all the different markers.
     let markers = PerformanceController.getCurrentRecording().getMarkers();
     return MARKER_TYPES.every(type => markers.some(m => m.name === type));
   });
-  yield stopRecording(panel);
 
-  // Select everything
-  let timeline = OverviewView.graphs.get("timeline");
-  let rerendered = WaterfallView.once(EVENTS.WATERFALL_RENDERED);
-  timeline.setSelection({ start: 0, end: timeline.width });
-  yield rerendered;
+  yield stopRecording(panel);
+  ok(true, "Recording has ended.");
+
+  info("No need to select everything in the timeline.");
+  info("All the markers should be displayed by default.");
 
   let bars = $$(".waterfall-marker-bar");
   let markers = PerformanceController.getCurrentRecording().getMarkers();
 
+  info(`Got ${bars.length} bars and ${markers.length} markers.`);
+  info("Markers types from datasrc: " + Array.map(markers, e => e.name));
+  info("Markers names from sidebar: " + Array.map(bars, e => e.parentNode.parentNode.querySelector(".waterfall-marker-name").getAttribute("value")));
+
   ok(bars.length >= MARKER_TYPES.length, `Got at least ${MARKER_TYPES.length} markers (1)`);
   ok(markers.length >= MARKER_TYPES.length, `Got at least ${MARKER_TYPES.length} markers (2)`);
 
   const tests = {
     ConsoleTime: function (marker) {
+      info("Got `ConsoleTime` marker with data: " + JSON.stringify(marker));
       shouldHaveStack($, "startStack", marker);
       shouldHaveStack($, "endStack", marker);
       shouldHaveLabel($, "Timer Name:", "!!!", marker);
       return true;
     },
     TimeStamp: function (marker) {
+      info("Got `TimeStamp` marker with data: " + JSON.stringify(marker));
       shouldHaveLabel($, "Label:", "go", marker);
       shouldHaveStack($, "stack", marker);
       return true;
     },
     Styles: function (marker) {
+      info("Got `Styles` marker with data: " + JSON.stringify(marker));
       if (marker.restyleHint) {
         shouldHaveLabel($, "Restyle Hint:", marker.restyleHint.replace(/eRestyle_/g, ""), marker);
       }
       if (marker.stack) {
         shouldHaveStack($, "stack", marker);
         return true;
       }
     },
     Reflow: function (marker) {
+      info("Got `Reflow` marker with data: " + JSON.stringify(marker));
       if (marker.stack) {
         shouldHaveStack($, "stack", marker);
         return true;
       }
     }
   };
 
   // Keep track of all marker tests that are finished so we only
   // run through each marker test once, so we don't spam 500 redundant
   // tests.
   let testsDone = [];
-  let TOTAL_TESTS = 4;
 
   for (let i = 0; i < bars.length; i++) {
     let bar = bars[i];
     let m = markers[i];
     EventUtils.sendMouseEvent({ type: "mousedown" }, bar);
 
     if (tests[m.name]) {
       if (testsDone.indexOf(m.name) === -1) {
@@ -88,17 +96,17 @@ function* spawnTest() {
         if (fullTestComplete) {
           testsDone.push(m.name);
         }
       }
     } else {
       info(`TODO: Need to add marker details tests for ${m.name}`);
     }
 
-    if (testsDone.length === TOTAL_TESTS) {
+    if (testsDone.length === Object.keys(tests).length) {
       break;
     }
   }
 
   yield teardown(panel);
   finish();
 }
 
--- a/browser/devtools/performance/test/browser_perf-options-enable-optimizations.js
+++ b/browser/devtools/performance/test/browser_perf-options-enable-optimizations.js
@@ -1,44 +1,45 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that `enable-jit-optimizations` sets the recording to subsequently
- * display optimizations info.
+ * enable the Optimizations View.
  */
 function* spawnTest() {
   let { panel } = yield initPerformance(SIMPLE_URL);
-  let { EVENTS, PerformanceController, $, DetailsView, JsCallTreeView } = panel.panelWin;
+  let { EVENTS, PerformanceController, $, DetailsView, WaterfallView, OptimizationsView } = panel.panelWin;
   Services.prefs.setBoolPref(JIT_PREF, true);
 
 
   yield startRecording(panel);
-  let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
+  let rendered = once(OptimizationsView, EVENTS.OPTIMIZATIONS_RENDERED);
   yield stopRecording(panel);
 
-  yield DetailsView.selectView("js-calltree");
-  ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
+  yield DetailsView.selectView("optimizations");
+  ok(DetailsView.isViewSelected(OptimizationsView), "The Optimizations View is now selected.");
   yield rendered;
 
   let recording = PerformanceController.getCurrentRecording();
   is(recording.getConfiguration().withJITOptimizations, true, "recording model has withJITOptimizations as true");
 
   // Set back to false, should not affect display of first recording
   info("Disabling enable-jit-optimizations");
   Services.prefs.setBoolPref(JIT_PREF, false);
-  is($("#jit-optimizations-view").hidden, false, "JIT Optimizations panel is displayed when feature enabled.");
+  is($("#select-optimizations-view").hidden, false,
+    "JIT Optimizations selector still available since the recording has it enabled.");
 
   yield startRecording(panel);
-  rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
+  rendered = once(WaterfallView, EVENTS.WATERFALL_RENDERED);
   yield stopRecording(panel);
 
-  yield DetailsView.selectView("js-calltree");
-  ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
+  ok(DetailsView.isViewSelected(WaterfallView), "The waterfall view is now selected.");
   yield rendered;
 
   recording = PerformanceController.getCurrentRecording();
   is(recording.getConfiguration().withJITOptimizations, false, "recording model has withJITOptimizations as false");
-  is($("#jit-optimizations-view").hidden, true, "JIT Optimizations panel is hidden when feature disabled");
+  is($("#select-optimizations-view").hidden, true,
+    "JIT Optimizations selector is hidden if recording did not enable optimizations.");
 
   yield teardown(panel);
   finish();
 }
--- a/browser/devtools/performance/test/browser_perf-recording-model-02.js
+++ b/browser/devtools/performance/test/browser_perf-recording-model-02.js
@@ -8,19 +8,19 @@
 let BUFFER_SIZE = 20000;
 
 function* spawnTest() {
   let { target, front } = yield initBackend(SIMPLE_URL, { TEST_MOCK_PROFILER_CHECK_TIMER: 10 });
   let config = { bufferSize: BUFFER_SIZE };
 
   let model = yield front.startRecording(config);
   let [_, stats] = yield onceSpread(front, "profiler-status");
-  is(stats.totalSize, BUFFER_SIZE, `profiler-status event has correct totalSize: ${stats.totalSize}`);
-  ok(stats.position < BUFFER_SIZE, `profiler-status event has correct position: ${stats.position}`);
-  is(stats.generation, 0, `profiler-status event has correct generation: ${stats.generation}`);
+  is(stats.totalSize, BUFFER_SIZE, `profiler-status event has totalSize: ${stats.totalSize}`);
+  ok(stats.position < BUFFER_SIZE, `profiler-status event has position: ${stats.position}`);
+  ok(stats.generation >= 0, `profiler-status event has generation: ${stats.generation}`);
   ok(stats.isActive, `profiler-status event is isActive`);
   is(typeof stats.currentTime, "number", `profiler-status event has currentTime`);
 
   // Halt once more for a buffer status to ensure we're beyond 0
   yield once(front, "profiler-status");
 
   let lastBufferStatus = 0;
   let checkCount = 0;
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_profiler_tree-view-11.js
@@ -0,0 +1,153 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that if a recording set `withJITOptimizations` on, then an
+ * icon is next to the frame with optimizations
+ */
+
+const RecordingUtils = devtools.require("devtools/performance/recording-utils");
+const { CATEGORY_MASK } = devtools.require("devtools/performance/global");
+
+function* spawnTest() {
+  let { panel } = yield initPerformance(SIMPLE_URL);
+  let { EVENTS, $, $$, window, PerformanceController } = panel.panelWin;
+  let { OverviewView, DetailsView, JITOptimizationsView, JsCallTreeView, RecordingsView } = panel.panelWin;
+
+  let profilerData = { threads: [gThread] };
+
+  Services.prefs.setBoolPref(JIT_PREF, true);
+  Services.prefs.setBoolPref(PLATFORM_DATA_PREF, false);
+  Services.prefs.setBoolPref(INVERT_PREF, false);
+
+  // Make two recordings, so we have one to switch to later, as the
+  // second one will have fake sample data
+  yield startRecording(panel);
+  yield stopRecording(panel);
+
+  yield DetailsView.selectView("js-calltree");
+
+  yield injectAndRenderProfilerData();
+
+  let rows = $$("#js-calltree-view .call-tree-item");
+  is(rows.length, 4, "4 call tree rows exist");
+  for (let row of rows) {
+    let name = $(".call-tree-name", row).value;
+    switch (name) {
+      case "A":
+        ok($(".opt-icon", row), "found an opt icon on a leaf node with opt data");
+        break;
+      case "C":
+        ok(!$(".opt-icon", row), "frames without opt data do not have an icon");
+        break;
+      case "Gecko":
+        ok(!$(".opt-icon", row), "meta category frames with opt data do not have an icon");
+        break;
+      case "(root)":
+        ok(!$(".opt-icon", row), "root frame certainly does not have opt data");
+        break;
+      default:
+        ok(false, `Unidentified frame: ${name}`);
+        break;
+    }
+  }
+
+  yield teardown(panel);
+  finish();
+
+  function *injectAndRenderProfilerData() {
+    // Get current recording and inject our mock data
+    info("Injecting mock profile data");
+    let recording = PerformanceController.getCurrentRecording();
+    recording._profile = profilerData;
+
+    // Force a rerender
+    let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
+    JsCallTreeView.render(OverviewView.getTimeInterval());
+    yield rendered;
+  }
+}
+
+let gUniqueStacks = new RecordingUtils.UniqueStacks();
+
+function uniqStr(s) {
+  return gUniqueStacks.getOrAddStringIndex(s);
+}
+
+// Since deflateThread doesn't handle deflating optimization info, use
+// placeholder names A_O1, B_O2, and B_O3, which will be used to manually
+// splice deduped opts into the profile.
+let gThread = RecordingUtils.deflateThread({
+  samples: [{
+    time: 0,
+    frames: [
+      { location: "(root)" }
+    ]
+  }, {
+    time: 5,
+    frames: [
+      { location: "(root)" },
+      { location: "A (http://foo:1)" },
+    ]
+  }, {
+    time: 5 + 1,
+    frames: [
+      { location: "(root)" },
+      { location: "C (http://foo/bar/baz:56)" }
+    ]
+  }, {
+    time: 5 + 1 + 2,
+    frames: [
+      { location: "(root)" },
+      { category: CATEGORY_MASK("other"),  location: "PlatformCode" }
+    ]
+  }],
+  markers: []
+}, gUniqueStacks);
+
+// 3 RawOptimizationSites
+let gRawSite1 = {
+  _testFrameInfo: { name: "A", line: "12", file: "@baz" },
+  line: 12,
+  column: 2,
+  types: [{
+    mirType: uniqStr("Object"),
+    site: uniqStr("A (http://foo/bar/bar:12)"),
+    typeset: [{
+        keyedBy: uniqStr("constructor"),
+        name: uniqStr("Foo"),
+        location: uniqStr("A (http://foo/bar/baz:12)")
+    }, {
+        keyedBy: uniqStr("primitive"),
+        location: uniqStr("self-hosted")
+    }]
+  }],
+  attempts: {
+    schema: {
+      outcome: 0,
+      strategy: 1
+    },
+    data: [
+      [uniqStr("Failure1"), uniqStr("SomeGetter1")],
+      [uniqStr("Failure2"), uniqStr("SomeGetter2")],
+      [uniqStr("Failure3"), uniqStr("SomeGetter3")]
+    ]
+  }
+};
+
+gThread.frameTable.data.forEach((frame) => {
+  const LOCATION_SLOT = gThread.frameTable.schema.location;
+  const OPTIMIZATIONS_SLOT = gThread.frameTable.schema.optimizations;
+
+  let l = gThread.stringTable[frame[LOCATION_SLOT]];
+  switch (l) {
+  case "A (http://foo:1)":
+    frame[LOCATION_SLOT] = uniqStr("A (http://foo:1)");
+    frame[OPTIMIZATIONS_SLOT] = gRawSite1;
+    break;
+  case "PlatformCode":
+    frame[LOCATION_SLOT] = uniqStr("PlatformCode");
+    frame[OPTIMIZATIONS_SLOT] = gRawSite1;
+    break;
+  }
+});
--- a/browser/devtools/performance/test/browser_timeline-filters-01.js
+++ b/browser/devtools/performance/test/browser_timeline-filters-01.js
@@ -20,16 +20,17 @@ function* spawnTest() {
     // Wait until we get 3 different markers.
     let markers = PerformanceController.getCurrentRecording().getMarkers();
     return markers.some(m => m.name == "Styles") &&
            markers.some(m => m.name == "Reflow") &&
            markers.some(m => m.name == "Paint");
   });
 
   yield stopRecording(panel);
+  ok(true, "Recording has ended.");
 
   // Push some fake markers of a type we do not have a blueprint for
   let markers = PerformanceController.getCurrentRecording().getMarkers();
   let endTime = markers[markers.length - 1].end;
   markers.push({ name: "CustomMarker", start: endTime + EPSILON, end: endTime + (EPSILON * 2) });
   markers.push({ name: "CustomMarker", start: endTime + (EPSILON * 3), end: endTime + (EPSILON * 4) });
 
   // Invalidate marker cache
--- a/browser/devtools/performance/test/browser_timeline-waterfall-sidebar.js
+++ b/browser/devtools/performance/test/browser_timeline-waterfall-sidebar.js
@@ -28,19 +28,26 @@ function* spawnTest() {
     return markers.some(m => m.name == "Styles") &&
            markers.some(m => m.name == "Reflow") &&
            markers.some(m => m.name == "Paint");
   });
 
   yield stopRecording(panel);
   ok(true, "Recording has ended.");
 
+  info("No need to select everything in the timeline.");
+  info("All the markers should be displayed by default.");
+
   let bars = $$(".waterfall-marker-bar");
   let markers = PerformanceController.getCurrentRecording().getMarkers();
 
+  info(`Got ${bars.length} bars and ${markers.length} markers.`);
+  info("Markers types from datasrc: " + Array.map(markers, e => e.name));
+  info("Markers names from sidebar: " + Array.map(bars, e => e.parentNode.parentNode.querySelector(".waterfall-marker-name").getAttribute("value")));
+
   ok(bars.length > 2, "Got at least 3 markers (1)");
   ok(markers.length > 2, "Got at least 3 markers (2)");
 
   let toMs = ms => L10N.getFormatStrWithNumbers("timeline.tick", ms);
 
   for (let i = 0; i < bars.length; i++) {
     let bar = bars[i];
     let mkr = markers[i];
--- a/browser/devtools/performance/views/details-js-call-tree.js
+++ b/browser/devtools/performance/views/details-js-call-tree.js
@@ -6,60 +6,57 @@
 /**
  * CallTree view containing profiler call tree, controlled by DetailsView.
  */
 let JsCallTreeView = Heritage.extend(DetailsSubview, {
 
   rerenderPrefs: [
     "invert-call-tree",
     "show-platform-data",
-    "flatten-tree-recursion"
+    "flatten-tree-recursion",
   ],
 
   rangeChangeDebounceTime: 75, // ms
 
   /**
    * Sets up the view with event binding.
    */
   initialize: function () {
     DetailsSubview.initialize.call(this);
 
-    this._onPrefChanged = this._onPrefChanged.bind(this);
     this._onLink = this._onLink.bind(this);
 
     this.container = $("#js-calltree-view .call-tree-cells-container");
-    JITOptimizationsView.initialize();
   },
 
   /**
    * Unbinds events.
    */
   destroy: function () {
     this.container = null;
-    JITOptimizationsView.destroy();
     DetailsSubview.destroy.call(this);
   },
 
   /**
    * Method for handling all the set up for rendering a new call tree.
    *
    * @param object interval [optional]
    *        The { startTime, endTime }, in milliseconds.
    */
   render: function (interval={}) {
+    let recording = PerformanceController.getCurrentRecording();
+    let profile = recording.getProfile();
     let options = {
       contentOnly: !PerformanceController.getOption("show-platform-data"),
       invertTree: PerformanceController.getOption("invert-call-tree"),
-      flattenRecursion: PerformanceController.getOption("flatten-tree-recursion")
+      flattenRecursion: PerformanceController.getOption("flatten-tree-recursion"),
+      showOptimizationHint: recording.getConfiguration().withJITOptimizations,
     };
-    let recording = PerformanceController.getCurrentRecording();
-    let profile = recording.getProfile();
     let threadNode = this._prepareCallTree(profile, interval, options);
     this._populateCallTree(threadNode, options);
-    this._toggleJITOptimizationsView(recording);
     this.emit(EVENTS.JS_CALL_TREE_RENDERED);
   },
 
   /**
    * Fired on the "link" event for the call tree in this container.
    */
   _onLink: function (_, treeItem) {
     let { url, line } = treeItem.frame.getInfo();
@@ -103,49 +100,37 @@ let JsCallTreeView = Heritage.extend(Det
 
     let root = new CallView({
       frame: frameNode,
       inverted: inverted,
       // The synthesized root node is hidden in inverted call trees.
       hidden: inverted,
       // Call trees should only auto-expand when not inverted. Passing undefined
       // will default to the CALL_TREE_AUTO_EXPAND depth.
-      autoExpandDepth: inverted ? 0 : undefined
+      autoExpandDepth: inverted ? 0 : undefined,
+      showOptimizationHint: options.showOptimizationHint
     });
 
     // Bind events.
     root.on("link", this._onLink);
 
-    // Pipe "focus" events to the view, used by
-    // tests and JITOptimizationsView.
-    root.on("focus", (_, node) => this.emit("focus", node));
+    // Pipe "focus" events to the view, mostly for tests
+    root.on("focus", () => this.emit("focus"));
+    // TODO tests for optimization event and rendering
+    // optimization bubbles in call tree
+    root.on("optimization", (_, node) => this.emit("optimization", node));
 
     // Clear out other call trees.
     this.container.innerHTML = "";
     root.attachTo(this.container);
 
     // When platform data isn't shown, hide the cateogry labels, since they're
     // only available for C++ frames. Pass *false* to make them invisible.
     root.toggleCategories(!options.contentOnly);
 
     // Return the CallView for tests
     return root;
   },
 
-  /**
-   * Displays or hides the optimizations view based on the recordings
-   * optimizations feature.
-   *
-   * @param {RecordingModel} recording
-   */
-  _toggleJITOptimizationsView: function (recording) {
-    if (recording && recording.getConfiguration().withJITOptimizations) {
-      JITOptimizationsView.show();
-      JITOptimizationsView.render();
-    } else {
-      JITOptimizationsView.hide();
-    }
-  },
-
   toString: () => "[object JsCallTreeView]"
 });
 
 EventEmitter.decorate(JsCallTreeView);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/views/details-optimizations.js
@@ -0,0 +1,174 @@
+/* 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";
+
+let OptimizationsView = Heritage.extend(DetailsSubview, {
+
+  rerenderPrefs: [
+    "show-platform-data",
+    "flatten-tree-recursion",
+  ],
+
+  rangeChangeDebounceTime: 75, // ms
+
+  /**
+   * Sets up the view with event binding.
+   */
+  initialize: function () {
+    DetailsSubview.initialize.call(this);
+    this.reset = this.reset.bind(this);
+    this.tabs = $("#optimizations-tabs");
+    this._onFramesListSelect = this._onFramesListSelect.bind(this);
+
+    OptimizationsListView.initialize();
+    FramesListView.initialize({ container: $("#frames-tabpanel") });
+    FramesListView.on("select", this._onFramesListSelect);
+  },
+
+  /**
+   * Unbinds events.
+   */
+  destroy: function () {
+    DetailsSubview.destroy.call(this);
+    this.tabs = this._threadNode = this._frameNode = null;
+
+    FramesListView.off("select", this._onFramesListSelect);
+    FramesListView.destroy();
+    OptimizationsListView.destroy();
+  },
+
+  /**
+   * Selects a tab by name.
+   *
+   * @param {string} name
+   *                 Can be "frames" or "optimizations"
+   */
+  selectTabByName: function (name="frames") {
+    switch(name) {
+    case "optimizations":
+      this.tabs.selectedIndex = 0;
+      break;
+    case "frames":
+      this.tabs.selectedIndex = 1;
+      break;
+    }
+  },
+
+  /**
+   * Method for handling all the set up for rendering a new call tree.
+   *
+   * @param object interval [optional]
+   *        The { startTime, endTime }, in milliseconds.
+   */
+  render: function (interval={}) {
+    let options = {
+      contentOnly: !PerformanceController.getOption("show-platform-data"),
+      flattenRecursion: PerformanceController.getOption("flatten-tree-recursion"),
+      // Always invert the tree for the optimizations view so we can quickly
+      // get leaves
+      invertTree: true,
+    };
+    let recording = PerformanceController.getCurrentRecording();
+    let profile = recording.getProfile();
+
+    this.reset();
+    // TODO bug 1175662
+    // Share thread nodes between details view
+    this.threadNode = this._prepareThreadNode(profile, interval, options);
+    this.emit(EVENTS.OPTIMIZATIONS_RENDERED);
+  },
+
+  /**
+   * The main thread node used in this recording that contains
+   * all potential frame nodes to select.
+   */
+  set threadNode(threadNode) {
+    if (threadNode === this._threadNode) {
+      return;
+    }
+    this._threadNode = threadNode;
+    // Also clear out the current frame node as its no
+    // longer relevent
+    this.frameNode = null;
+    this._setAndRenderFramesList();
+  },
+  get threadNode() {
+    return this._threadNode;
+  },
+
+  /**
+   * frameNode is the frame node selected currently to inspect
+   * the optimization tiers over time and strategies.
+   */
+  set frameNode(frameNode) {
+    if (frameNode === this._frameNode) {
+      return;
+    }
+    this._frameNode = frameNode;
+
+    // If no frame selected, jump to the frame list view. If just selected
+    // a frame, jump to optimizations view.
+    // TODO test for this bug 1176056
+    this.selectTabByName(frameNode ? "optimizations" : "frames");
+    this._setAndRenderTierGraph();
+    this._setAndRenderOptimizationsList();
+  },
+
+  get frameNode() {
+    return this._frameNode;
+  },
+
+  /**
+   * Clears the frameNode so that tier and opts list
+   * views are cleared.
+   */
+  reset: function () {
+    this.threadNode = this.frameNode = null;
+  },
+
+  /**
+   * Called when the recording is stopped and prepares data to
+   * populate the graph.
+   */
+  _prepareThreadNode: function (profile, { startTime, endTime }, options) {
+    let thread = profile.threads[0];
+    let { contentOnly, invertTree, flattenRecursion } = options;
+    let threadNode = new ThreadNode(thread, { startTime, endTime, contentOnly, invertTree, flattenRecursion });
+    return threadNode;
+  },
+
+  /**
+   * Renders the tier graph.
+   */
+  _setAndRenderTierGraph: function () {
+    // TODO bug 1150299
+  },
+
+  /**
+   * Renders the frames list.
+   */
+  _setAndRenderFramesList: function () {
+    FramesListView.setCurrentThread(this.threadNode);
+    FramesListView.render();
+  },
+
+  /**
+   * Renders the optimizations list.
+   */
+  _setAndRenderOptimizationsList: function () {
+    OptimizationsListView.setCurrentFrame(this.frameNode);
+    OptimizationsListView.render();
+  },
+
+  /**
+   * Called when a frame is selected via the FramesListView
+   */
+  _onFramesListSelect: function (_, frameNode) {
+    this.frameNode = frameNode;
+  },
+
+  toString: () => "[object OptimizationsView]"
+});
+
+EventEmitter.decorate(OptimizationsView);
--- a/browser/devtools/performance/views/details.js
+++ b/browser/devtools/performance/views/details.js
@@ -34,16 +34,21 @@ let DetailsView = {
       actors: ["memory"],
       features: ["withAllocations"]
     },
     "memory-flamegraph": {
       id: "memory-flamegraph-view",
       view: MemoryFlameGraphView,
       actors: ["memory", "timeline"],
       features: ["withAllocations"]
+    },
+    "optimizations": {
+      id: "optimizations-view",
+      view: OptimizationsView,
+      features: ["withJITOptimizations"],
     }
   },
 
   /**
    * Sets up the view with event binding, initializes subviews.
    */
   initialize: Task.async(function *() {
     this.el = $("#details-pane");
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/views/frames-list.js
@@ -0,0 +1,114 @@
+/* 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 HTML_NS = "http://www.w3.org/1999/xhtml";
+const PERCENTAGE_UNITS = L10N.getStr("table.percentage");
+
+/**
+ * View for rendering a list of all youngest-frames in a profiler recording.
+ */
+
+let FramesListView = {
+
+  // Current `<li>` element selected.
+  _selectedItem: null,
+
+  /**
+   * Initialization function called when the tool starts up.
+   */
+  initialize: function ({ container }) {
+    this._onFrameListClick = this._onFrameListClick.bind(this);
+
+    this.container = container;
+    this.list = document.createElementNS(HTML_NS, "ul");
+    this.list.setAttribute("class", "frames-list");
+    this.list.addEventListener("click", this._onFrameListClick, false);
+
+    this.container.appendChild(this.list);
+  },
+
+  /**
+   * Destruction function called when the tool cleans up.
+   */
+  destroy: function () {
+    this.list.removeEventListener("click", this._onFrameListClick, false);
+    this.container.innerHTML = "";
+    this.container = this.list = null;
+  },
+
+  /**
+   * Sets the thread node used for subsequent rendering.
+   *
+   * @param {ThreadNode} threadNode
+   */
+  setCurrentThread: function (threadNode) {
+    this.threadNode = threadNode;
+  },
+
+  /**
+   * Renders a list of leaf frames with optimizations in
+   * order of hotness from the current ThreadNode.
+   */
+  render: function () {
+    this.list.innerHTML = "";
+
+    if (!this.threadNode) {
+      return;
+    }
+
+    let totalSamples = this.threadNode.samples;
+    let sortedFrames = this.threadNode.calls.sort((a, b) => a.youngestFrameSamples < b.youngestFrameSamples ? 1 : -1);
+    for (let frame of sortedFrames) {
+      if (!frame.hasOptimizations()) {
+        continue;
+      }
+      let info = frame.getInfo();
+      let el = document.createElementNS(HTML_NS, "li");
+      let percentage = frame.youngestFrameSamples / totalSamples * 100;
+      let percentageText = L10N.numberWithDecimals(percentage, 2) + PERCENTAGE_UNITS;
+      let label = `(${percentageText}) ${info.functionName}`;
+      el.textContent = label;
+      el.setAttribute("tooltip", label);
+      el.setAttribute("data-location", frame.location);
+      this.list.appendChild(el);
+    }
+  },
+
+  /**
+   * Fired when a frame in the list is clicked.
+   */
+  _onFrameListClick: function (e) {
+    // If no threadNode (no renders), abort;
+    // also only allow left click to trigger this event
+    if (!this.threadNode || e.button !== 0) {
+      return;
+    }
+
+    let target = e.target;
+    let location = target.getAttribute("data-location");
+    if (!location) {
+      return;
+    }
+
+    for (let frame of this.threadNode.calls) {
+      if (frame.location === location) {
+        // If found, set the selected class on element, remove it
+        // from previous element, and emit event "select"
+        if (this._selectedItem) {
+          this._selectedItem.classList.remove("selected");
+        }
+        this._selectedItem = target;
+        target.classList.add("selected");
+        console.log("Emitting select on", this, frame);
+        this.emit("select", frame);
+        break;
+      }
+    }
+  },
+
+  toString: () => "[object FramesListView]"
+};
+
+EventEmitter.decorate(FramesListView);
rename from browser/devtools/performance/views/jit-optimizations.js
rename to browser/devtools/performance/views/optimizations-list.js
--- a/browser/devtools/performance/views/jit-optimizations.js
+++ b/browser/devtools/performance/views/optimizations-list.js
@@ -5,57 +5,51 @@
 
 const URL_LABEL_TOOLTIP = L10N.getStr("table.url.tooltiptext");
 const OPTIMIZATION_FAILURE = L10N.getStr("jit.optimizationFailure");
 const JIT_SAMPLES = L10N.getStr("jit.samples2");
 const JIT_EMPTY_TEXT = L10N.getStr("jit.empty");
 const PROPNAME_MAX_LENGTH = 4;
 
 /**
- * View for rendering JIT Optimization data. The terminology and types
- * used here can be referenced:
+ * View for rendering a list of all optmizations found in a frame.
+ * The terminology and types used here can be referenced:
  * @see browser/devtools/performance/modules/logic/jit.js
  */
 
-let JITOptimizationsView = {
+let OptimizationsListView = {
 
   _currentFrame: null,
 
   /**
    * Initialization function called when the tool starts up.
    */
   initialize: function () {
     this.reset = this.reset.bind(this);
-    this._onFocusFrame = this._onFocusFrame.bind(this);
 
     this.el = $("#jit-optimizations-view");
     this.$headerName = $("#jit-optimizations-header .header-function-name");
     this.$headerFile = $("#jit-optimizations-header .header-file");
     this.$headerLine = $("#jit-optimizations-header .header-line");
 
     this.tree = new TreeWidget($("#jit-optimizations-raw-view"), {
       sorted: false,
       emptyText: JIT_EMPTY_TEXT
     });
 
     // Start the tree by resetting.
     this.reset();
-
-    PerformanceController.on(EVENTS.RECORDING_SELECTED, this.reset);
-    JsCallTreeView.on("focus", this._onFocusFrame);
   },
 
   /**
    * Destruction function called when the tool cleans up.
    */
   destroy: function () {
     this.tree = null;
     this.$headerName = this.$headerFile = this.$headerLine = this.el = null;
-    PerformanceController.off(EVENTS.RECORDING_SELECTED, this.reset);
-    JsCallTreeView.off("focus", this._onFocusFrame);
   },
 
   /**
    * Takes a FrameNode, with corresponding optimization data to be displayed
    * in the view.
    *
    * @param {FrameNode} frameNode
    */
@@ -88,42 +82,22 @@ let JITOptimizationsView = {
 
   /**
    * Clears out data in the tree.
    */
   clear: function () {
     this.tree.clear();
   },
 
-  show: function () {
-    this.el.hidden = false;
-  },
-
-  hide: function () {
-    this.el.hidden = true;
-  },
-
-  /**
-   * Helper to determine whether or not this view should be enabled.
-   */
-  isEnabled: function () {
-    let recording = PerformanceController.getCurrentRecording();
-    return !!(recording && recording.getConfiguration().withJITOptimizations);
-  },
-
   /**
    * Takes a JITOptimizations object and builds a view containing all attempted
    * optimizations for this frame. This view is very verbose and meant for those
    * who understand JIT compilers.
    */
   render: function () {
-    if (!this.isEnabled()) {
-      return;
-    }
-
     let frameNode = this.getCurrentFrame();
 
     if (!frameNode) {
       this.reset();
       return;
     }
 
     let view = this.tree;
@@ -380,36 +354,13 @@ let JITOptimizationsView = {
 
   _isLinkableURL: function (url) {
     return url && url.indexOf &&
        (url.indexOf("http") === 0 ||
         url.indexOf("resource://") === 0 ||
         url.indexOf("file://") === 0);
   },
 
-  /**
-   * Called when the JSCallTreeView focuses on a frame.
-   */
-
-  _onFocusFrame: function (_, view) {
-    if (!view.frame) {
-      return;
-    }
-
-    // Only attempt to rerender if this is new -- focus is called even
-    // when the window removes focus and comes back, so this prevents
-    // repeating rendering of the same frame
-    let shouldRender = this.getCurrentFrame() !== view.frame;
-
-    // Save the frame even if the view is disabled, so we can
-    // render it if it becomes enabled
-    this.setCurrentFrame(view.frame);
-
-    if (shouldRender) {
-      this.render();
-    }
-  },
-
-  toString: () => "[object JITOptimizationsView]"
+  toString: () => "[object OptimizationsListView]"
 
 };
 
-EventEmitter.decorate(JITOptimizationsView);
+EventEmitter.decorate(OptimizationsListView);
--- a/browser/devtools/shared/moz.build
+++ b/browser/devtools/shared/moz.build
@@ -43,20 +43,23 @@ EXTRA_JS_MODULES.devtools.shared += [
     'source-utils.js',
     'telemetry.js',
     'theme-switching.js',
     'theme.js',
     'undo.js'
 ]
 
 EXTRA_JS_MODULES.devtools.shared.widgets += [
+    'widgets/BarGraphWidget.js',
     'widgets/CubicBezierPresets.js',
     'widgets/CubicBezierWidget.js',
     'widgets/FastListWidget.js',
     'widgets/FilterWidget.js',
     'widgets/FlameGraph.js',
     'widgets/Graphs.js',
+    'widgets/LineGraphWidget.js',
     'widgets/MdnDocsWidget.js',
+    'widgets/MountainGraphWidget.js',
     'widgets/Spectrum.js',
     'widgets/TableWidget.js',
     'widgets/Tooltip.js',
     'widgets/TreeWidget.js',
 ]
--- a/browser/devtools/shared/test/browser.ini
+++ b/browser/devtools/shared/test/browser.ini
@@ -68,19 +68,20 @@ support-files =
 [browser_graphs-10a.js]
 [browser_graphs-10b.js]
 [browser_graphs-10c.js]
 [browser_graphs-11a.js]
 [browser_graphs-11b.js]
 [browser_graphs-12.js]
 [browser_graphs-13.js]
 [browser_graphs-14.js]
+[browser_graphs-15.js]
+[browser_graphs-16.js]
 [browser_inplace-editor-01.js]
 [browser_inplace-editor-02.js]
-[browser_graphs-15.js]
 [browser_layoutHelpers.js]
 skip-if = e10s # Layouthelpers test should not run in a content page.
 [browser_layoutHelpers-getBoxQuads.js]
 skip-if = e10s # Layouthelpers test should not run in a content page.
 [browser_mdn-docs-01.js]
 [browser_mdn-docs-02.js]
 [browser_num-l10n.js]
 [browser_observableobject.js]
--- a/browser/devtools/shared/test/browser_graphs-01.js
+++ b/browser/devtools/shared/test/browser_graphs-01.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that graph widgets works properly.
 
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
   finish();
 });
--- a/browser/devtools/shared/test/browser_graphs-02.js
+++ b/browser/devtools/shared/test/browser_graphs-02.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that graph widgets can properly add data, regions and highlights.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
 const TEST_REGIONS = [{ start: 320, end: 460 }, { start: 780, end: 860 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-03.js
+++ b/browser/devtools/shared/test/browser_graphs-03.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that graph widgets can handle clients getting/setting the
 // selection or cursor.
 
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-04.js
+++ b/browser/devtools/shared/test/browser_graphs-04.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that graph widgets can correctly compare selections and cursors.
 
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-05.js
+++ b/browser/devtools/shared/test/browser_graphs-05.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that graph widgets can correctly determine which regions are hovered.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
 const TEST_REGIONS = [{ start: 320, end: 460 }, { start: 780, end: 860 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-06.js
+++ b/browser/devtools/shared/test/browser_graphs-06.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests if clicking on regions adds a selection spanning that region.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
 const TEST_REGIONS = [{ start: 320, end: 460 }, { start: 780, end: 860 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-07a.js
+++ b/browser/devtools/shared/test/browser_graphs-07a.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests if selecting, resizing, moving selections and zooming in/out works.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-07b.js
+++ b/browser/devtools/shared/test/browser_graphs-07b.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests if selections can't be added via clicking, while not allowed.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-07c.js
+++ b/browser/devtools/shared/test/browser_graphs-07c.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests if movement via event dispatching using screenX / screenY
 // works.  All of the other tests directly use the graph's mouse event
 // callbacks with textX / testY for convenience.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-07d.js
+++ b/browser/devtools/shared/test/browser_graphs-07d.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that selections are drawn onto the canvas.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
 const TEST_REGIONS = [{ start: 320, end: 460 }, { start: 780, end: 860 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-07e.js
+++ b/browser/devtools/shared/test/browser_graphs-07e.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that selections are drawn onto the canvas.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 let CURRENT_ZOOM = 1;
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
--- a/browser/devtools/shared/test/browser_graphs-08.js
+++ b/browser/devtools/shared/test/browser_graphs-08.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests if a selection is dropped when clicking outside of it.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-09a.js
+++ b/browser/devtools/shared/test/browser_graphs-09a.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that line graphs properly create the gutter and tooltips.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-09b.js
+++ b/browser/devtools/shared/test/browser_graphs-09b.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that line graphs properly use the tooltips configuration properties.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-09c.js
+++ b/browser/devtools/shared/test/browser_graphs-09c.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that line graphs hide the tooltips when there's no data available.
 
 const TEST_DATA = [];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-09d.js
+++ b/browser/devtools/shared/test/browser_graphs-09d.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that line graphs hide the 'max' tooltip when the distance between
 // the 'min' and 'max' tooltip is too small.
 
 const TEST_DATA = [{ delta: 100, value: 60 }, { delta: 200, value: 59.9 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-09e.js
+++ b/browser/devtools/shared/test/browser_graphs-09e.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that line graphs hide the gutter and tooltips when there's no data,
 // but show them when there is.
 
 const NO_DATA = [];
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
 
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-09f.js
+++ b/browser/devtools/shared/test/browser_graphs-09f.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests the constructor options for `min`, `max` and `avg` on displaying the
 // gutter/tooltips and lines.
 
 const TEST_DATA = [{ delta: 100, value: 60 }, { delta: 200, value: 1 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-10a.js
+++ b/browser/devtools/shared/test/browser_graphs-10a.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that graphs properly handle resizing.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-10b.js
+++ b/browser/devtools/shared/test/browser_graphs-10b.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that graphs aren't refreshed when the owner window resizes but
 // the graph dimensions stay the same.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-10c.js
+++ b/browser/devtools/shared/test/browser_graphs-10c.js
@@ -1,13 +1,13 @@
 
 // Tests that graphs properly handle resizing.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-11a.js
+++ b/browser/devtools/shared/test/browser_graphs-11a.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that bar graph create a legend as expected.
 
-let {BarGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let BarGraphWidget = devtools.require("devtools/shared/widgets/BarGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 const CATEGORIES = [
   { color: "#46afe3", label: "Foo" },
   { color: "#eb5368", label: "Bar" },
   { color: "#70bf53", label: "Baz" }
 ];
 
--- a/browser/devtools/shared/test/browser_graphs-11b.js
+++ b/browser/devtools/shared/test/browser_graphs-11b.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that bar graph's legend items handle mouseover/mouseout.
 
-let {BarGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let BarGraphWidget = devtools.require("devtools/shared/widgets/BarGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 const CATEGORIES = [
   { color: "#46afe3", label: "Foo" },
   { color: "#eb5368", label: "Bar" },
   { color: "#70bf53", label: "Baz" }
 ];
 
--- a/browser/devtools/shared/test/browser_graphs-12.js
+++ b/browser/devtools/shared/test/browser_graphs-12.js
@@ -1,14 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that canvas graphs can have their selection linked.
 
-let {LineGraphWidget,BarGraphWidget,CanvasGraphUtils} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
+let BarGraphWidget = devtools.require("devtools/shared/widgets/BarGraphWidget");
+let {CanvasGraphUtils} = devtools.require("devtools/shared/widgets/Graphs");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-13.js
+++ b/browser/devtools/shared/test/browser_graphs-13.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that graph widgets may have a fixed width or height.
 
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-14.js
+++ b/browser/devtools/shared/test/browser_graphs-14.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that graph widgets correctly emit mouse input events.
 
 const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
--- a/browser/devtools/shared/test/browser_graphs-15.js
+++ b/browser/devtools/shared/test/browser_graphs-15.js
@@ -1,30 +1,31 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that graph widgets correctly emit mouse input events.
 
 const FAST_FPS = 60;
 const SLOW_FPS = 10;
+
 // Each element represents a second
 const FRAMES= [FAST_FPS, FAST_FPS, FAST_FPS, SLOW_FPS, FAST_FPS];
 const TEST_DATA = [];
 const INTERVAL = 100;
 const DURATION = 5000; // 5s
 let t = 0;
 for (let frameRate of FRAMES) {
   for (let i = 0; i < frameRate; i++) {
     let delta = Math.floor(1000 / frameRate); // Duration between frames at this rate
     t += delta;
     TEST_DATA.push(t);
   }
 }
 
-let {LineGraphWidget} = devtools.require("devtools/shared/widgets/Graphs");
+let LineGraphWidget = devtools.require("devtools/shared/widgets/LineGraphWidget");
 let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
 
 add_task(function*() {
   yield promiseTab("about:blank");
   yield performTest();
   gBrowser.removeCurrentTab();
 });
 
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/test/browser_graphs-16.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that mounta graphs work as expected.
+
+let MountainGraphWidget = devtools.require("devtools/shared/widgets/MountainGraphWidget");
+let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
+
+const TEST_DATA = [
+  { delta: 0, values: [0.1, 0.5, 0.3] },
+  { delta: 1, values: [0.25, 0, 0.5] },
+  { delta: 2, values: [0.5, 0.25, 0.1] },
+  { delta: 3, values: [0, 0.75, 0] },
+  { delta: 4, values: [0.75, 0, 0.25] }
+];
+
+const SECTIONS = [
+  { color: "red" },
+  { color: "green" },
+  { color: "blue" }
+];
+
+add_task(function*() {
+  yield promiseTab("about:blank");
+  yield performTest();
+  gBrowser.removeCurrentTab();
+});
+
+function* performTest() {
+  let [host, win, doc] = yield createHost();
+  let graph = new MountainGraphWidget(doc.body);
+  yield graph.once("ready");
+
+  testGraph(graph);
+
+  yield graph.destroy();
+  host.destroy();
+}
+
+function testGraph(graph) {
+  graph.format = SECTIONS;
+  graph.setData(TEST_DATA);
+  ok(true, "The graph didn't throw any erorrs.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/widgets/BarGraphWidget.js
@@ -0,0 +1,476 @@
+"use strict";
+
+const { Cc, Ci, Cu, Cr } = require("chrome");
+
+const { Heritage, setNamedTimeout, clearNamedTimeout } = require("resource:///modules/devtools/ViewHelpers.jsm");
+const { AbstractCanvasGraph, CanvasGraphUtils } = require("devtools/shared/widgets/Graphs");
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+
+// Bar graph constants.
+
+const GRAPH_DAMPEN_VALUES_FACTOR = 0.75;
+const GRAPH_BARS_MARGIN_TOP = 1; // px
+const GRAPH_BARS_MARGIN_END = 1; // px
+const GRAPH_MIN_BARS_WIDTH = 5; // px
+const GRAPH_MIN_BLOCKS_HEIGHT = 1; // px
+
+const GRAPH_BACKGROUND_GRADIENT_START = "rgba(0,136,204,0.0)";
+const GRAPH_BACKGROUND_GRADIENT_END = "rgba(255,255,255,0.25)";
+
+const GRAPH_CLIPHEAD_LINE_COLOR = "#666";
+const GRAPH_SELECTION_LINE_COLOR = "#555";
+const GRAPH_SELECTION_BACKGROUND_COLOR = "rgba(0,136,204,0.25)";
+const GRAPH_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
+const GRAPH_REGION_BACKGROUND_COLOR = "transparent";
+const GRAPH_REGION_STRIPES_COLOR = "rgba(237,38,85,0.2)";
+
+const GRAPH_HIGHLIGHTS_MASK_BACKGROUND = "rgba(255,255,255,0.75)";
+const GRAPH_HIGHLIGHTS_MASK_STRIPES = "rgba(255,255,255,0.5)";
+
+const GRAPH_LEGEND_MOUSEOVER_DEBOUNCE = 50; // ms
+
+/**
+ * A bar graph, plotting tuples of values as rectangles.
+ *
+ * @see AbstractCanvasGraph for emitted events and other options.
+ *
+ * Example usage:
+ *   let graph = new BarGraphWidget(node);
+ *   graph.format = ...;
+ *   graph.once("ready", () => {
+ *     graph.setData(src);
+ *   });
+ *
+ * The `graph.format` traits are mandatory and will determine how the values
+ * are styled as "blocks" in every "bar":
+ *   [
+ *     { color: "#f00", label: "Foo" },
+ *     { color: "#0f0", label: "Bar" },
+ *     ...
+ *     { color: "#00f", label: "Baz" }
+ *   ]
+ *
+ * Data source format:
+ *   [
+ *     { delta: x1, values: [y11, y12, ... y1n] },
+ *     { delta: x2, values: [y21, y22, ... y2n] },
+ *     ...
+ *     { delta: xm, values: [ym1, ym2, ... ymn] }
+ *   ]
+ * where each item in the array represents a "bar", for which every value
+ * represents a "block" inside that "bar", plotted at the "delta" position.
+ *
+ * @param nsIDOMNode parent
+ *        The parent node holding the graph.
+ */
+this.BarGraphWidget = function(parent, ...args) {
+  AbstractCanvasGraph.apply(this, [parent, "bar-graph", ...args]);
+
+  this.once("ready", () => {
+    this._onLegendMouseOver = this._onLegendMouseOver.bind(this);
+    this._onLegendMouseOut = this._onLegendMouseOut.bind(this);
+    this._onLegendMouseDown = this._onLegendMouseDown.bind(this);
+    this._onLegendMouseUp = this._onLegendMouseUp.bind(this);
+    this._createLegend();
+  });
+};
+
+BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
+  clipheadLineColor: GRAPH_CLIPHEAD_LINE_COLOR,
+  selectionLineColor: GRAPH_SELECTION_LINE_COLOR,
+  selectionBackgroundColor: GRAPH_SELECTION_BACKGROUND_COLOR,
+  selectionStripesColor: GRAPH_SELECTION_STRIPES_COLOR,
+  regionBackgroundColor: GRAPH_REGION_BACKGROUND_COLOR,
+  regionStripesColor: GRAPH_REGION_STRIPES_COLOR,
+
+  /**
+   * List of colors used to fill each block inside every bar, also
+   * corresponding to labels displayed in this graph's legend.
+   * @see constructor
+   */
+  format: null,
+
+  /**
+   * Optionally offsets the `delta` in the data source by this scalar.
+   */
+  dataOffsetX: 0,
+
+  /**
+   * Optionally uses this value instead of the last tick in the data source
+   * to compute the horizontal scaling.
+   */
+  dataDuration: 0,
+
+  /**
+   * The scalar used to multiply the graph values to leave some headroom
+   * on the top.
+   */
+  dampenValuesFactor: GRAPH_DAMPEN_VALUES_FACTOR,
+
+  /**
+   * Bars that are too close too each other in the graph will be combined.
+   * This scalar specifies the required minimum width of each bar.
+   */
+  minBarsWidth: GRAPH_MIN_BARS_WIDTH,
+
+  /**
+   * Blocks in a bar that are too thin inside the bar will not be rendered.
+   * This scalar specifies the required minimum height of each block.
+   */
+  minBlocksHeight: GRAPH_MIN_BLOCKS_HEIGHT,
+
+  /**
+   * Renders the graph's background.
+   * @see AbstractCanvasGraph.prototype.buildBackgroundImage
+   */
+  buildBackgroundImage: function() {
+    let { canvas, ctx } = this._getNamedCanvas("bar-graph-background");
+    let width = this._width;
+    let height = this._height;
+
+    let gradient = ctx.createLinearGradient(0, 0, 0, height);
+    gradient.addColorStop(0, GRAPH_BACKGROUND_GRADIENT_START);
+    gradient.addColorStop(1, GRAPH_BACKGROUND_GRADIENT_END);
+    ctx.fillStyle = gradient;
+    ctx.fillRect(0, 0, width, height);
+
+    return canvas;
+  },
+
+  /**
+   * Renders the graph's data source.
+   * @see AbstractCanvasGraph.prototype.buildGraphImage
+   */
+  buildGraphImage: function() {
+    if (!this.format || !this.format.length) {
+      throw "The graph format traits are mandatory to style the data source.";
+    }
+    let { canvas, ctx } = this._getNamedCanvas("bar-graph-data");
+    let width = this._width;
+    let height = this._height;
+
+    let totalTypes = this.format.length;
+    let totalTicks = this._data.length;
+    let lastTick = this._data[totalTicks - 1].delta;
+
+    let minBarsWidth = this.minBarsWidth * this._pixelRatio;
+    let minBlocksHeight = this.minBlocksHeight * this._pixelRatio;
+
+    let duration = this.dataDuration || lastTick;
+    let dataScaleX = this.dataScaleX = width / (duration - this.dataOffsetX);
+    let dataScaleY = this.dataScaleY = height / this._calcMaxHeight({
+      data: this._data,
+      dataScaleX: dataScaleX,
+      minBarsWidth: minBarsWidth
+    }) * this.dampenValuesFactor;
+
+    // Draw the graph.
+
+    // Iterate over the blocks, then the bars, to draw all rectangles of
+    // the same color in a single pass. See the @constructor for more
+    // information about the data source, and how a "bar" contains "blocks".
+
+    this._blocksBoundingRects = [];
+    let prevHeight = [];
+    let scaledMarginEnd = GRAPH_BARS_MARGIN_END * this._pixelRatio;
+    let scaledMarginTop = GRAPH_BARS_MARGIN_TOP * this._pixelRatio;
+
+    for (let type = 0; type < totalTypes; type++) {
+      ctx.fillStyle = this.format[type].color || "#000";
+      ctx.beginPath();
+
+      let prevRight = 0;
+      let skippedCount = 0;
+      let skippedHeight = 0;
+
+      for (let tick = 0; tick < totalTicks; tick++) {
+        let delta = this._data[tick].delta;
+        let value = this._data[tick].values[type] || 0;
+        let blockRight = (delta - this.dataOffsetX) * dataScaleX;
+        let blockHeight = value * dataScaleY;
+
+        let blockWidth = blockRight - prevRight;
+        if (blockWidth < minBarsWidth) {
+          skippedCount++;
+          skippedHeight += blockHeight;
+          continue;
+        }
+
+        let averageHeight = (blockHeight + skippedHeight) / (skippedCount + 1);
+        if (averageHeight >= minBlocksHeight) {
+          let bottom = height - ~~prevHeight[tick];
+          ctx.moveTo(prevRight, bottom);
+          ctx.lineTo(prevRight, bottom - averageHeight);
+          ctx.lineTo(blockRight, bottom - averageHeight);
+          ctx.lineTo(blockRight, bottom);
+
+          // Remember this block's type and location.
+          this._blocksBoundingRects.push({
+            type: type,
+            start: prevRight,
+            end: blockRight,
+            top: bottom - averageHeight,
+            bottom: bottom
+          });
+
+          if (prevHeight[tick] === undefined) {
+            prevHeight[tick] = averageHeight + scaledMarginTop;
+          } else {
+            prevHeight[tick] += averageHeight + scaledMarginTop;
+          }
+        }
+
+        prevRight += blockWidth + scaledMarginEnd;
+        skippedHeight = 0;
+        skippedCount = 0;
+      }
+
+      ctx.fill();
+    }
+
+    // The blocks bounding rects isn't guaranteed to be sorted ascending by
+    // block location on the X axis. This should be the case, for better
+    // cache cohesion and a faster `buildMaskImage`.
+    this._blocksBoundingRects.sort((a, b) => a.start > b.start ? 1 : -1);
+
+    // Update the legend.
+
+    while (this._legendNode.hasChildNodes()) {
+      this._legendNode.firstChild.remove();
+    }
+    for (let { color, label } of this.format) {
+      this._createLegendItem(color, label);
+    }
+
+    return canvas;
+  },
+
+  /**
+   * Renders the graph's mask.
+   * Fades in only the parts of the graph that are inside the specified areas.
+   *
+   * @param array highlights
+   *        A list of { start, end } values. Optionally, each object
+   *        in the list may also specify { top, bottom } pixel values if the
+   *        highlighting shouldn't span across the full height of the graph.
+   * @param boolean inPixels
+   *        Set this to true if the { start, end } values in the highlights
+   *        list are pixel values, and not values from the data source.
+   * @param function unpack [optional]
+   *        @see AbstractCanvasGraph.prototype.getMappedSelection
+   */
+  buildMaskImage: function(highlights, inPixels = false, unpack = e => e.delta) {
+    // A null `highlights` array is used to clear the mask. An empty array
+    // will mask the entire graph.
+    if (!highlights) {
+      return null;
+    }
+
+    // Get a render target for the highlights. It will be overlaid on top of
+    // the existing graph, masking the areas that aren't highlighted.
+
+    let { canvas, ctx } = this._getNamedCanvas("graph-highlights");
+    let width = this._width;
+    let height = this._height;
+
+    // Draw the background mask.
+
+    let pattern = AbstractCanvasGraph.getStripePattern({
+      ownerDocument: this._document,
+      backgroundColor: GRAPH_HIGHLIGHTS_MASK_BACKGROUND,
+      stripesColor: GRAPH_HIGHLIGHTS_MASK_STRIPES
+    });
+    ctx.fillStyle = pattern;
+    ctx.fillRect(0, 0, width, height);
+
+    // Clear highlighted areas.
+
+    let totalTicks = this._data.length;
+    let firstTick = unpack(this._data[0]);
+    let lastTick = unpack(this._data[totalTicks - 1]);
+
+    for (let { start, end, top, bottom } of highlights) {
+      if (!inPixels) {
+        start = CanvasGraphUtils.map(start, firstTick, lastTick, 0, width);
+        end = CanvasGraphUtils.map(end, firstTick, lastTick, 0, width);
+      }
+      let firstSnap = findFirst(this._blocksBoundingRects, e => e.start >= start);
+      let lastSnap = findLast(this._blocksBoundingRects, e => e.start >= start && e.end <= end);
+
+      let x1 = firstSnap ? firstSnap.start : start;
+      let x2 = lastSnap ? lastSnap.end : firstSnap ? firstSnap.end : end;
+      let y1 = top || 0;
+      let y2 = bottom || height;
+      ctx.clearRect(x1, y1, x2 - x1, y2 - y1);
+    }
+
+    return canvas;
+  },
+
+  /**
+   * A list storing the bounding rectangle for each drawn block in the graph.
+   * Created whenever `buildGraphImage` is invoked.
+   */
+  _blocksBoundingRects: null,
+
+  /**
+   * Calculates the height of the tallest bar that would eventially be rendered
+   * in this graph.
+   *
+   * Bars that are too close too each other in the graph will be combined.
+   * @see `minBarsWidth`
+   *
+   * @return number
+   *         The tallest bar height in this graph.
+   */
+  _calcMaxHeight: function({ data, dataScaleX, minBarsWidth }) {
+    let maxHeight = 0;
+    let prevRight = 0;
+    let skippedCount = 0;
+    let skippedHeight = 0;
+    let scaledMarginEnd = GRAPH_BARS_MARGIN_END * this._pixelRatio;
+
+    for (let { delta, values } of data) {
+      let barRight = (delta - this.dataOffsetX) * dataScaleX;
+      let barHeight = values.reduce((a, b) => a + b, 0);
+
+      let barWidth = barRight - prevRight;
+      if (barWidth < minBarsWidth) {
+        skippedCount++;
+        skippedHeight += barHeight;
+        continue;
+      }
+
+      let averageHeight = (barHeight + skippedHeight) / (skippedCount + 1);
+      maxHeight = Math.max(averageHeight, maxHeight);
+
+      prevRight += barWidth + scaledMarginEnd;
+      skippedHeight = 0;
+      skippedCount = 0;
+    }
+
+    return maxHeight;
+  },
+
+  /**
+   * Creates the legend container when constructing this graph.
+   */
+  _createLegend: function() {
+    let legendNode = this._legendNode = this._document.createElementNS(HTML_NS, "div");
+    legendNode.className = "bar-graph-widget-legend";
+    this._container.appendChild(legendNode);
+  },
+
+  /**
+   * Creates a legend item when constructing this graph.
+   */
+  _createLegendItem: function(color, label) {
+    let itemNode = this._document.createElementNS(HTML_NS, "div");
+    itemNode.className = "bar-graph-widget-legend-item";
+
+    let colorNode = this._document.createElementNS(HTML_NS, "span");
+    colorNode.setAttribute("view", "color");
+    colorNode.setAttribute("data-index", this._legendNode.childNodes.length);
+    colorNode.style.backgroundColor = color;
+    colorNode.addEventListener("mouseover", this._onLegendMouseOver);
+    colorNode.addEventListener("mouseout", this._onLegendMouseOut);
+    colorNode.addEventListener("mousedown", this._onLegendMouseDown);
+    colorNode.addEventListener("mouseup", this._onLegendMouseUp);
+
+    let labelNode = this._document.createElementNS(HTML_NS, "span");
+    labelNode.setAttribute("view", "label");
+    labelNode.textContent = label;
+
+    itemNode.appendChild(colorNode);
+    itemNode.appendChild(labelNode);
+    this._legendNode.appendChild(itemNode);
+  },
+
+  /**
+   * Invoked whenever a color node in the legend is hovered.
+   */
+  _onLegendMouseOver: function(e) {
+    setNamedTimeout("bar-graph-debounce", GRAPH_LEGEND_MOUSEOVER_DEBOUNCE, () => {
+      let type = e.target.dataset.index;
+      let rects = this._blocksBoundingRects.filter(e => e.type == type);
+
+      this._originalHighlights = this._mask;
+      this._hasCustomHighlights = true;
+      this.setMask(rects, true);
+
+      this.emit("legend-hover", [type, rects]);
+    });
+  },
+
+  /**
+   * Invoked whenever a color node in the legend is unhovered.
+   */
+  _onLegendMouseOut: function() {
+    clearNamedTimeout("bar-graph-debounce");
+
+    if (this._hasCustomHighlights) {
+      this.setMask(this._originalHighlights);
+      this._hasCustomHighlights = false;
+      this._originalHighlights = null;
+    }
+
+    this.emit("legend-unhover");
+  },
+
+  /**
+   * Invoked whenever a color node in the legend is pressed.
+   */
+  _onLegendMouseDown: function(e) {
+    e.preventDefault();
+    e.stopPropagation();
+
+    let type = e.target.dataset.index;
+    let rects = this._blocksBoundingRects.filter(e => e.type == type);
+    let leftmost = rects[0];
+    let rightmost = rects[rects.length - 1];
+    if (!leftmost || !rightmost) {
+      this.dropSelection();
+    } else {
+      this.setSelection({ start: leftmost.start, end: rightmost.end });
+    }
+
+    this.emit("legend-selection", [leftmost, rightmost]);
+  },
+
+  /**
+   * Invoked whenever a color node in the legend is released.
+   */
+  _onLegendMouseUp: function(e) {
+    e.preventDefault();
+    e.stopPropagation();
+  }
+});
+
+/**
+ * Finds the first element in an array that validates a predicate.
+ * @param array
+ * @param function predicate
+ * @return number
+ */
+function findFirst(array, predicate) {
+  for (let i = 0, len = array.length; i < len; i++) {
+    let element = array[i];
+    if (predicate(element)) return element;
+  }
+}
+
+/**
+ * Finds the last element in an array that validates a predicate.
+ * @param array
+ * @param function predicate
+ * @return number
+ */
+function findLast(array, predicate) {
+  for (let i = array.length - 1; i >= 0; i--) {
+    let element = array[i];
+    if (predicate(element)) return element;
+  }
+}
+
+module.exports = BarGraphWidget;
--- a/browser/devtools/shared/widgets/Graphs.js
+++ b/browser/devtools/shared/widgets/Graphs.js
@@ -1,34 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
 
 const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
-const { ViewHelpers } = require("resource:///modules/devtools/ViewHelpers.jsm");
 const { Heritage, setNamedTimeout, clearNamedTimeout } = require("resource:///modules/devtools/ViewHelpers.jsm");
 
 loader.lazyRequireGetter(this, "promise");
 loader.lazyRequireGetter(this, "EventEmitter",
   "devtools/toolkit/event-emitter");
 
 loader.lazyImporter(this, "DevToolsWorker",
   "resource://gre/modules/devtools/shared/worker.js");
 loader.lazyImporter(this, "LayoutHelpers",
   "resource://gre/modules/devtools/LayoutHelpers.jsm");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const GRAPH_SRC = "chrome://browser/content/devtools/graphs-frame.xhtml";
 const WORKER_URL = "resource:///modules/devtools/GraphsWorker.js";
 
-const L10N = new ViewHelpers.L10N();
-
 // Generic constants.
 
 const GRAPH_RESIZE_EVENTS_DRAIN = 100; // ms
 const GRAPH_WHEEL_ZOOM_SENSITIVITY = 0.00075;
 const GRAPH_WHEEL_SCROLL_SENSITIVITY = 0.1;
 const GRAPH_WHEEL_MIN_SELECTION_WIDTH = 10; // px
 
 const GRAPH_SELECTION_BOUNDARY_HOVER_LINE_WIDTH = 4; // px
@@ -39,63 +36,16 @@ const GRAPH_MAX_SELECTION_RIGHT_PADDING 
 const GRAPH_REGION_LINE_WIDTH = 1; // px
 const GRAPH_REGION_LINE_COLOR = "rgba(237,38,85,0.8)";
 
 const GRAPH_STRIPE_PATTERN_WIDTH = 16; // px
 const GRAPH_STRIPE_PATTERN_HEIGHT = 16; // px
 const GRAPH_STRIPE_PATTERN_LINE_WIDTH = 2; // px
 const GRAPH_STRIPE_PATTERN_LINE_SPACING = 4; // px
 
-// Line graph constants.
-
-const LINE_GRAPH_DAMPEN_VALUES = 0.85;
-const LINE_GRAPH_TOOLTIP_SAFE_BOUNDS = 8; // px
-const LINE_GRAPH_MIN_MAX_TOOLTIP_DISTANCE = 14; // px
-
-const LINE_GRAPH_BACKGROUND_COLOR = "#0088cc";
-const LINE_GRAPH_STROKE_WIDTH = 1; // px
-const LINE_GRAPH_STROKE_COLOR = "rgba(255,255,255,0.9)";
-const LINE_GRAPH_HELPER_LINES_DASH = [5]; // px
-const LINE_GRAPH_HELPER_LINES_WIDTH = 1; // px
-const LINE_GRAPH_MAXIMUM_LINE_COLOR = "rgba(255,255,255,0.4)";
-const LINE_GRAPH_AVERAGE_LINE_COLOR = "rgba(255,255,255,0.7)";
-const LINE_GRAPH_MINIMUM_LINE_COLOR = "rgba(255,255,255,0.9)";
-const LINE_GRAPH_BACKGROUND_GRADIENT_START = "rgba(255,255,255,0.25)";
-const LINE_GRAPH_BACKGROUND_GRADIENT_END = "rgba(255,255,255,0.0)";
-
-const LINE_GRAPH_CLIPHEAD_LINE_COLOR = "#fff";
-const LINE_GRAPH_SELECTION_LINE_COLOR = "#fff";
-const LINE_GRAPH_SELECTION_BACKGROUND_COLOR = "rgba(44,187,15,0.25)";
-const LINE_GRAPH_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
-const LINE_GRAPH_REGION_BACKGROUND_COLOR = "transparent";
-const LINE_GRAPH_REGION_STRIPES_COLOR = "rgba(237,38,85,0.2)";
-
-// Bar graph constants.
-
-const BAR_GRAPH_DAMPEN_VALUES = 0.75;
-const BAR_GRAPH_BARS_MARGIN_TOP = 1; // px
-const BAR_GRAPH_BARS_MARGIN_END = 1; // px
-const BAR_GRAPH_MIN_BARS_WIDTH = 5; // px
-const BAR_GRAPH_MIN_BLOCKS_HEIGHT = 1; // px
-
-const BAR_GRAPH_BACKGROUND_GRADIENT_START = "rgba(0,136,204,0.0)";
-const BAR_GRAPH_BACKGROUND_GRADIENT_END = "rgba(255,255,255,0.25)";
-
-const BAR_GRAPH_CLIPHEAD_LINE_COLOR = "#666";
-const BAR_GRAPH_SELECTION_LINE_COLOR = "#555";
-const BAR_GRAPH_SELECTION_BACKGROUND_COLOR = "rgba(0,136,204,0.25)";
-const BAR_GRAPH_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
-const BAR_GRAPH_REGION_BACKGROUND_COLOR = "transparent";
-const BAR_GRAPH_REGION_STRIPES_COLOR = "rgba(237,38,85,0.2)";
-
-const BAR_GRAPH_HIGHLIGHTS_MASK_BACKGROUND = "rgba(255,255,255,0.75)";
-const BAR_GRAPH_HIGHLIGHTS_MASK_STRIPES = "rgba(255,255,255,0.5)";
-
-const BAR_GRAPH_LEGEND_MOUSEOVER_DEBOUNCE = 50; // ms
-
 /**
  * Small data primitives for all graphs.
  */
 this.GraphCursor = function() {
   this.x = null;
   this.y = null;
 };
 
@@ -1235,792 +1185,16 @@ AbstractCanvasGraph.prototype = {
    */
   _onResize: function() {
     if (this.hasData()) {
       setNamedTimeout(this._uid, GRAPH_RESIZE_EVENTS_DRAIN, this.refresh);
     }
   }
 };
 
-/**
- * A basic line graph, plotting values on a curve and adding helper lines
- * and tooltips for maximum, average and minimum values.
- *
- * @see AbstractCanvasGraph for emitted events and other options.
- *
- * Example usage:
- *   let graph = new LineGraphWidget(node, "units");
- *   graph.once("ready", () => {
- *     graph.setData(src);
- *   });
- *
- * Data source format:
- *   [
- *     { delta: x1, value: y1 },
- *     { delta: x2, value: y2 },
- *     ...
- *     { delta: xn, value: yn }
- *   ]
- * where each item in the array represents a point in the graph.
- *
- * @param nsIDOMNode parent
- *        The parent node holding the graph.
- * @param object options [optional]
- *        `metric`: The metric displayed in the graph, e.g. "fps" or "bananas".
- *        `min`: Boolean whether to show the min tooltip/gutter/line (default: true)
- *        `max`: Boolean whether to show the max tooltip/gutter/line (default: true)
- *        `avg`: Boolean whether to show the avg tooltip/gutter/line (default: true)
- */
-this.LineGraphWidget = function(parent, options, ...args) {
-  options = options || {};
-  let metric = options.metric;
-
-  this._showMin = options.min !== false;
-  this._showMax = options.max !== false;
-  this._showAvg = options.avg !== false;
-  AbstractCanvasGraph.apply(this, [parent, "line-graph", ...args]);
-
-  this.once("ready", () => {
-    // Create all gutters and tooltips incase the showing of min/max/avg
-    // are changed later
-    this._gutter = this._createGutter();
-
-    this._maxGutterLine = this._createGutterLine("maximum");
-    this._maxTooltip = this._createTooltip("maximum", "start", "max", metric);
-    this._minGutterLine = this._createGutterLine("minimum");
-    this._minTooltip = this._createTooltip("minimum", "start", "min", metric);
-    this._avgGutterLine = this._createGutterLine("average");
-    this._avgTooltip = this._createTooltip("average", "end", "avg", metric);
-  });
-};
-
-LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
-  backgroundColor: LINE_GRAPH_BACKGROUND_COLOR,
-  backgroundGradientStart: LINE_GRAPH_BACKGROUND_GRADIENT_START,
-  backgroundGradientEnd: LINE_GRAPH_BACKGROUND_GRADIENT_END,
-  strokeColor: LINE_GRAPH_STROKE_COLOR,
-  strokeWidth: LINE_GRAPH_STROKE_WIDTH,
-  maximumLineColor: LINE_GRAPH_MAXIMUM_LINE_COLOR,
-  averageLineColor: LINE_GRAPH_AVERAGE_LINE_COLOR,
-  minimumLineColor: LINE_GRAPH_MINIMUM_LINE_COLOR,
-  clipheadLineColor: LINE_GRAPH_CLIPHEAD_LINE_COLOR,
-  selectionLineColor: LINE_GRAPH_SELECTION_LINE_COLOR,
-  selectionBackgroundColor: LINE_GRAPH_SELECTION_BACKGROUND_COLOR,
-  selectionStripesColor: LINE_GRAPH_SELECTION_STRIPES_COLOR,
-  regionBackgroundColor: LINE_GRAPH_REGION_BACKGROUND_COLOR,
-  regionStripesColor: LINE_GRAPH_REGION_STRIPES_COLOR,
-
-  /**
-   * Optionally offsets the `delta` in the data source by this scalar.
-   */
-  dataOffsetX: 0,
-
-  /**
-   * Optionally uses this value instead of the last tick in the data source
-   * to compute the horizontal scaling.
-   */
-  dataDuration: 0,
-
-  /**
-   * The scalar used to multiply the graph values to leave some headroom.
-   */
-  dampenValuesFactor: LINE_GRAPH_DAMPEN_VALUES,
-
-  /**
-   * Specifies if min/max/avg tooltips have arrow handlers on their sides.
-   */
-  withTooltipArrows: true,
-
-  /**
-   * Specifies if min/max/avg tooltips are positioned based on the actual
-   * values, or just placed next to the graph corners.
-   */
-  withFixedTooltipPositions: false,
-
-  /**
-   * Takes a list of numbers and plots them on a line graph representing
-   * the rate of occurences in a specified interval. Useful for drawing
-   * framerate, for example, from a sequence of timestamps.
-   *
-   * @param array timestamps
-   *        A list of numbers representing time, ordered ascending. For example,
-   *        this can be the raw data received from the framerate actor, which
-   *        represents the elapsed time on each refresh driver tick.
-   * @param number interval
-   *        The maximum amount of time to wait between calculations.
-   * @param number duration
-   *        The duration of the recording in milliseconds.
-   */
-  setDataFromTimestamps: Task.async(function*(timestamps, interval, duration) {
-    let {
-      plottedData,
-      plottedMinMaxSum
-    } = yield CanvasGraphUtils._performTaskInWorker("plotTimestampsGraph", {
-      timestamps, interval, duration
-    });
-
-    this._tempMinMaxSum = plottedMinMaxSum;
-    this.setData(plottedData);
-  }),
-
-  /**
-   * Renders the graph's data source.
-   * @see AbstractCanvasGraph.prototype.buildGraphImage
-   */
-  buildGraphImage: function() {
-    let { canvas, ctx } = this._getNamedCanvas("line-graph-data");
-    let width = this._width;
-    let height = this._height;
-
-    let totalTicks = this._data.length;
-    let firstTick = totalTicks ? this._data[0].delta : 0;
-    let lastTick = totalTicks ? this._data[totalTicks - 1].delta : 0;
-    let maxValue = Number.MIN_SAFE_INTEGER;
-    let minValue = Number.MAX_SAFE_INTEGER;
-    let avgValue = 0;
-
-    if (this._tempMinMaxSum) {
-      maxValue = this._tempMinMaxSum.maxValue;
-      minValue = this._tempMinMaxSum.minValue;
-      avgValue = this._tempMinMaxSum.avgValue;
-    } else {
-      let sumValues = 0;
-      for (let { delta, value } of this._data) {
-        maxValue = Math.max(value, maxValue);
-        minValue = Math.min(value, minValue);
-        sumValues += value;
-      }
-      avgValue = sumValues / totalTicks;
-    }
-
-    let duration = this.dataDuration || lastTick;
-    let dataScaleX = this.dataScaleX = width / (duration - this.dataOffsetX);
-    let dataScaleY = this.dataScaleY = height / maxValue * this.dampenValuesFactor;
-
-    // Draw the background.
-
-    ctx.fillStyle = this.backgroundColor;
-    ctx.fillRect(0, 0, width, height);
-
-    // Draw the graph.
-
-    let gradient = ctx.createLinearGradient(0, height / 2, 0, height);
-    gradient.addColorStop(0, this.backgroundGradientStart);
-    gradient.addColorStop(1, this.backgroundGradientEnd);
-    ctx.fillStyle = gradient;
-    ctx.strokeStyle = this.strokeColor;
-    ctx.lineWidth = this.strokeWidth * this._pixelRatio;
-    ctx.beginPath();
-
-    for (let { delta, value } of this._data) {
-      let currX = (delta - this.dataOffsetX) * dataScaleX;
-      let currY = height - value * dataScaleY;
-
-      if (delta == firstTick) {
-        ctx.moveTo(-LINE_GRAPH_STROKE_WIDTH, height);
-        ctx.lineTo(-LINE_GRAPH_STROKE_WIDTH, currY);
-      }
-
-      ctx.lineTo(currX, currY);
-
-      if (delta == lastTick) {
-        ctx.lineTo(width + LINE_GRAPH_STROKE_WIDTH, currY);
-        ctx.lineTo(width + LINE_GRAPH_STROKE_WIDTH, height);
-      }
-    }
-
-    ctx.fill();
-    ctx.stroke();
-
-    this._drawOverlays(ctx, minValue, maxValue, avgValue, dataScaleY);
-
-    return canvas;
-  },
-
-  /**
-   * Draws the min, max and average horizontal lines, along with their
-   * repsective tooltips.
-   *
-   * @param CanvasRenderingContext2D ctx
-   * @param number minValue
-   * @param number maxValue
-   * @param number avgValue
-   * @param number dataScaleY
-   */
-  _drawOverlays: function(ctx, minValue, maxValue, avgValue, dataScaleY) {
-    let width = this._width;
-    let height = this._height;
-    let totalTicks = this._data.length;
-
-    // Draw the maximum value horizontal line.
-    if (this._showMax) {
-      ctx.strokeStyle = this.maximumLineColor;
-      ctx.lineWidth = LINE_GRAPH_HELPER_LINES_WIDTH;
-      ctx.setLineDash(LINE_GRAPH_HELPER_LINES_DASH);
-      ctx.beginPath();
-      let maximumY = height - maxValue * dataScaleY;
-      ctx.moveTo(0, maximumY);
-      ctx.lineTo(width, maximumY);
-      ctx.stroke();
-    }
-
-    // Draw the average value horizontal line.
-    if (this._showAvg) {
-      ctx.strokeStyle = this.averageLineColor;
-      ctx.lineWidth = LINE_GRAPH_HELPER_LINES_WIDTH;
-      ctx.setLineDash(LINE_GRAPH_HELPER_LINES_DASH);
-      ctx.beginPath();
-      let averageY = height - avgValue * dataScaleY;
-      ctx.moveTo(0, averageY);
-      ctx.lineTo(width, averageY);
-      ctx.stroke();
-    }
-
-    // Draw the minimum value horizontal line.
-    if (this._showMin) {
-      ctx.strokeStyle = this.minimumLineColor;
-      ctx.lineWidth = LINE_GRAPH_HELPER_LINES_WIDTH;
-      ctx.setLineDash(LINE_GRAPH_HELPER_LINES_DASH);
-      ctx.beginPath();
-      let minimumY = height - minValue * dataScaleY;
-      ctx.moveTo(0, minimumY);
-      ctx.lineTo(width, minimumY);
-      ctx.stroke();
-    }
-
-    // Update the tooltips text and gutter lines.
-
-    this._maxTooltip.querySelector("[text=value]").textContent =
-      L10N.numberWithDecimals(maxValue, 2);
-    this._avgTooltip.querySelector("[text=value]").textContent =
-      L10N.numberWithDecimals(avgValue, 2);
-    this._minTooltip.querySelector("[text=value]").textContent =
-      L10N.numberWithDecimals(minValue, 2);
-
-    let bottom = height / this._pixelRatio;
-    let maxPosY = map(maxValue * this.dampenValuesFactor, 0, maxValue, bottom, 0);
-    let avgPosY = map(avgValue * this.dampenValuesFactor, 0, maxValue, bottom, 0);
-    let minPosY = map(minValue * this.dampenValuesFactor, 0, maxValue, bottom, 0);
-
-    let safeTop = LINE_GRAPH_TOOLTIP_SAFE_BOUNDS;
-    let safeBottom = bottom - LINE_GRAPH_TOOLTIP_SAFE_BOUNDS;
-
-    let maxTooltipTop = (this.withFixedTooltipPositions
-      ? safeTop : clamp(maxPosY, safeTop, safeBottom));
-    let avgTooltipTop = (this.withFixedTooltipPositions
-      ? safeTop : clamp(avgPosY, safeTop, safeBottom));
-    let minTooltipTop = (this.withFixedTooltipPositions
-      ? safeBottom : clamp(minPosY, safeTop, safeBottom));
-
-    this._maxTooltip.style.top = maxTooltipTop + "px";
-    this._avgTooltip.style.top = avgTooltipTop + "px";
-    this._minTooltip.style.top = minTooltipTop + "px";
-
-    this._maxGutterLine.style.top = maxPosY + "px";
-    this._avgGutterLine.style.top = avgPosY + "px";
-    this._minGutterLine.style.top = minPosY + "px";
-
-    this._maxTooltip.setAttribute("with-arrows", this.withTooltipArrows);
-    this._avgTooltip.setAttribute("with-arrows", this.withTooltipArrows);
-    this._minTooltip.setAttribute("with-arrows", this.withTooltipArrows);
-
-    let distanceMinMax = Math.abs(maxTooltipTop - minTooltipTop);
-    this._maxTooltip.hidden = this._showMax === false || !totalTicks || distanceMinMax < LINE_GRAPH_MIN_MAX_TOOLTIP_DISTANCE;
-    this._avgTooltip.hidden = this._showAvg === false || !totalTicks;
-    this._minTooltip.hidden = this._showMin === false || !totalTicks;
-    this._gutter.hidden = (this._showMin === false && this._showAvg === false && this._showMax === false) || !totalTicks;
-
-    this._maxGutterLine.hidden = this._showMax === false;
-    this._avgGutterLine.hidden = this._showAvg === false;
-    this._minGutterLine.hidden = this._showMin === false;
-  },
-
-  /**
-   * Creates the gutter node when constructing this graph.
-   * @return nsIDOMNode
-   */
-  _createGutter: function() {
-    let gutter = this._document.createElementNS(HTML_NS, "div");
-    gutter.className = "line-graph-widget-gutter";
-    gutter.setAttribute("hidden", true);
-    this._container.appendChild(gutter);
-
-    return gutter;
-  },
-
-  /**
-   * Creates the gutter line nodes when constructing this graph.
-   * @return nsIDOMNode
-   */
-  _createGutterLine: function(type) {
-    let line = this._document.createElementNS(HTML_NS, "div");
-    line.className = "line-graph-widget-gutter-line";
-    line.setAttribute("type", type);
-    this._gutter.appendChild(line);
-
-    return line;
-  },
-
-  /**
-   * Creates the tooltip nodes when constructing this graph.
-   * @return nsIDOMNode
-   */
-  _createTooltip: function(type, arrow, info, metric) {
-    let tooltip = this._document.createElementNS(HTML_NS, "div");
-    tooltip.className = "line-graph-widget-tooltip";
-    tooltip.setAttribute("type", type);
-    tooltip.setAttribute("arrow", arrow);
-    tooltip.setAttribute("hidden", true);
-
-    let infoNode = this._document.createElementNS(HTML_NS, "span");
-    infoNode.textContent = info;
-    infoNode.setAttribute("text", "info");
-
-    let valueNode = this._document.createElementNS(HTML_NS, "span");
-    valueNode.textContent = 0;
-    valueNode.setAttribute("text", "value");
-
-    let metricNode = this._document.createElementNS(HTML_NS, "span");
-    metricNode.textContent = metric;
-    metricNode.setAttribute("text", "metric");
-
-    tooltip.appendChild(infoNode);
-    tooltip.appendChild(valueNode);
-    tooltip.appendChild(metricNode);
-    this._container.appendChild(tooltip);
-
-    return tooltip;
-  }
-});
-
-/**
- * A bar graph, plotting tuples of values as rectangles.
- *
- * @see AbstractCanvasGraph for emitted events and other options.
- *
- * Example usage:
- *   let graph = new BarGraphWidget(node);
- *   graph.format = ...;
- *   graph.once("ready", () => {
- *     graph.setData(src);
- *   });
- *
- * The `graph.format` traits are mandatory and will determine how the values
- * are styled as "blocks" in every "bar":
- *   [
- *     { color: "#f00", label: "Foo" },
- *     { color: "#0f0", label: "Bar" },
- *     ...
- *     { color: "#00f", label: "Baz" }
- *   ]
- *
- * Data source format:
- *   [
- *     { delta: x1, values: [y11, y12, ... y1n] },
- *     { delta: x2, values: [y21, y22, ... y2n] },
- *     ...
- *     { delta: xm, values: [ym1, ym2, ... ymn] }
- *   ]
- * where each item in the array represents a "bar", for which every value
- * represents a "block" inside that "bar", plotted at the "delta" position.
- *
- * @param nsIDOMNode parent
- *        The parent node holding the graph.
- */
-this.BarGraphWidget = function(parent, ...args) {
-  AbstractCanvasGraph.apply(this, [parent, "bar-graph", ...args]);
-
-  // Populated with [node, event, listener] entries which need to be removed
-  // when this graph is being destroyed.
-  this.outstandingEventListeners = [];
-
-  this.once("ready", () => {
-    this._onLegendMouseOver = this._onLegendMouseOver.bind(this);
-    this._onLegendMouseOut = this._onLegendMouseOut.bind(this);
-    this._onLegendMouseDown = this._onLegendMouseDown.bind(this);
-    this._onLegendMouseUp = this._onLegendMouseUp.bind(this);
-    this._createLegend();
-  });
-
-  this.once("destroyed", () => {
-    for (let [node, event, listener] of this.outstandingEventListeners) {
-      node.removeEventListener(event, listener);
-    }
-    this.outstandingEventListeners = null;
-  });
-};
-
-BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
-  clipheadLineColor: BAR_GRAPH_CLIPHEAD_LINE_COLOR,
-  selectionLineColor: BAR_GRAPH_SELECTION_LINE_COLOR,
-  selectionBackgroundColor: BAR_GRAPH_SELECTION_BACKGROUND_COLOR,
-  selectionStripesColor: BAR_GRAPH_SELECTION_STRIPES_COLOR,
-  regionBackgroundColor: BAR_GRAPH_REGION_BACKGROUND_COLOR,
-  regionStripesColor: BAR_GRAPH_REGION_STRIPES_COLOR,
-
-  /**
-   * List of colors used to fill each block inside every bar, also
-   * corresponding to labels displayed in this graph's legend.
-   */
-  format: null,
-
-  /**
-   * Optionally offsets the `delta` in the data source by this scalar.
-   */
-  dataOffsetX: 0,
-
-  /**
-   * The scalar used to multiply the graph values to leave some headroom
-   * on the top.
-   */
-  dampenValuesFactor: BAR_GRAPH_DAMPEN_VALUES,
-
-  /**
-   * Bars that are too close too each other in the graph will be combined.
-   * This scalar specifies the required minimum width of each bar.
-   */
-  minBarsWidth: BAR_GRAPH_MIN_BARS_WIDTH,
-
-  /**
-   * Blocks in a bar that are too thin inside the bar will not be rendered.
-   * This scalar specifies the required minimum height of each block.
-   */
-  minBlocksHeight: BAR_GRAPH_MIN_BLOCKS_HEIGHT,
-
-  /**
-   * Renders the graph's background.
-   * @see AbstractCanvasGraph.prototype.buildBackgroundImage
-   */
-  buildBackgroundImage: function() {
-    let { canvas, ctx } = this._getNamedCanvas("bar-graph-background");
-    let width = this._width;
-    let height = this._height;
-
-    let gradient = ctx.createLinearGradient(0, 0, 0, height);
-    gradient.addColorStop(0, BAR_GRAPH_BACKGROUND_GRADIENT_START);
-    gradient.addColorStop(1, BAR_GRAPH_BACKGROUND_GRADIENT_END);
-    ctx.fillStyle = gradient;
-    ctx.fillRect(0, 0, width, height);
-
-    return canvas;
-  },
-
-  /**
-   * Renders the graph's data source.
-   * @see AbstractCanvasGraph.prototype.buildGraphImage
-   */
-  buildGraphImage: function() {
-    if (!this.format || !this.format.length) {
-      throw "The graph format traits are mandatory to style the data source.";
-    }
-    let { canvas, ctx } = this._getNamedCanvas("bar-graph-data");
-    let width = this._width;
-    let height = this._height;
-
-    let totalTypes = this.format.length;
-    let totalTicks = this._data.length;
-    let lastTick = this._data[totalTicks - 1].delta;
-
-    let minBarsWidth = this.minBarsWidth * this._pixelRatio;
-    let minBlocksHeight = this.minBlocksHeight * this._pixelRatio;
-
-    let dataScaleX = this.dataScaleX = width / (lastTick - this.dataOffsetX);
-    let dataScaleY = this.dataScaleY = height / this._calcMaxHeight({
-      data: this._data,
-      dataScaleX: dataScaleX,
-      minBarsWidth: minBarsWidth
-    }) * this.dampenValuesFactor;
-
-    // Draw the graph.
-
-    // Iterate over the blocks, then the bars, to draw all rectangles of
-    // the same color in a single pass. See the @constructor for more
-    // information about the data source, and how a "bar" contains "blocks".
-
-    this._blocksBoundingRects = [];
-    let prevHeight = [];
-    let scaledMarginEnd = BAR_GRAPH_BARS_MARGIN_END * this._pixelRatio;
-    let unscaledMarginTop = BAR_GRAPH_BARS_MARGIN_TOP;
-
-    for (let type = 0; type < totalTypes; type++) {
-      ctx.fillStyle = this.format[type].color || "#000";
-      ctx.beginPath();
-
-      let prevRight = 0;
-      let skippedCount = 0;
-      let skippedHeight = 0;
-
-      for (let tick = 0; tick < totalTicks; tick++) {
-        let delta = this._data[tick].delta;
-        let value = this._data[tick].values[type] || 0;
-        let blockRight = (delta - this.dataOffsetX) * dataScaleX;
-        let blockHeight = value * dataScaleY;
-
-        let blockWidth = blockRight - prevRight;
-        if (blockWidth < minBarsWidth) {
-          skippedCount++;
-          skippedHeight += blockHeight;
-          continue;
-        }
-
-        let averageHeight = (blockHeight + skippedHeight) / (skippedCount + 1);
-        if (averageHeight >= minBlocksHeight) {
-          let bottom = height - ~~prevHeight[tick];
-          ctx.moveTo(prevRight, bottom);
-          ctx.lineTo(prevRight, bottom - averageHeight);
-          ctx.lineTo(blockRight, bottom - averageHeight);
-          ctx.lineTo(blockRight, bottom);
-
-          // Remember this block's type and location.
-          this._blocksBoundingRects.push({
-            type: type,
-            start: prevRight,
-            end: blockRight,
-            top: bottom - averageHeight,
-            bottom: bottom
-          });
-
-          if (prevHeight[tick] === undefined) {
-            prevHeight[tick] = averageHeight + unscaledMarginTop;
-          } else {
-            prevHeight[tick] += averageHeight + unscaledMarginTop;
-          }
-        }
-
-        prevRight += blockWidth + scaledMarginEnd;
-        skippedHeight = 0;
-        skippedCount = 0;
-      }
-
-      ctx.fill();
-    }
-
-    // The blocks bounding rects isn't guaranteed to be sorted ascending by
-    // block location on the X axis. This should be the case, for better
-    // cache cohesion and a faster `buildMaskImage`.
-    this._blocksBoundingRects.sort((a, b) => a.start > b.start ? 1 : -1);
-
-    // Update the legend.
-
-    while (this._legendNode.hasChildNodes()) {
-      this._legendNode.firstChild.remove();
-    }
-    for (let { color, label } of this.format) {
-      this._createLegendItem(color, label);
-    }
-
-    return canvas;
-  },
-
-  /**
-   * Renders the graph's mask.
-   * Fades in only the parts of the graph that are inside the specified areas.
-   *
-   * @param array highlights
-   *        A list of { start, end } values. Optionally, each object
-   *        in the list may also specify { top, bottom } pixel values if the
-   *        highlighting shouldn't span across the full height of the graph.
-   * @param boolean inPixels
-   *        Set this to true if the { start, end } values in the highlights
-   *        list are pixel values, and not values from the data source.
-   * @param function unpack [optional]
-   *        @see AbstractCanvasGraph.prototype.getMappedSelection
-   */
-  buildMaskImage: function(highlights, inPixels = false, unpack = e => e.delta) {
-    // A null `highlights` array is used to clear the mask. An empty array
-    // will mask the entire graph.
-    if (!highlights) {
-      return null;
-    }
-
-    // Get a render target for the highlights. It will be overlaid on top of
-    // the existing graph, masking the areas that aren't highlighted.
-
-    let { canvas, ctx } = this._getNamedCanvas("graph-highlights");
-    let width = this._width;
-    let height = this._height;
-
-    // Draw the background mask.
-
-    let pattern = AbstractCanvasGraph.getStripePattern({
-      ownerDocument: this._document,
-      backgroundColor: BAR_GRAPH_HIGHLIGHTS_MASK_BACKGROUND,
-      stripesColor: BAR_GRAPH_HIGHLIGHTS_MASK_STRIPES
-    });
-    ctx.fillStyle = pattern;
-    ctx.fillRect(0, 0, width, height);
-
-    // Clear highlighted areas.
-
-    let totalTicks = this._data.length;
-    let firstTick = unpack(this._data[0]);
-    let lastTick = unpack(this._data[totalTicks - 1]);
-
-    for (let { start, end, top, bottom } of highlights) {
-      if (!inPixels) {
-        start = map(start, firstTick, lastTick, 0, width);
-        end = map(end, firstTick, lastTick, 0, width);
-      }
-      let firstSnap = findFirst(this._blocksBoundingRects, e => e.start >= start);
-      let lastSnap = findLast(this._blocksBoundingRects, e => e.start >= start && e.end <= end);
-
-      let x1 = firstSnap ? firstSnap.start : start;
-      let x2 = lastSnap ? lastSnap.end : firstSnap ? firstSnap.end : end;
-      let y1 = top || 0;
-      let y2 = bottom || height;
-      ctx.clearRect(x1, y1, x2 - x1, y2 - y1);
-    }
-
-    return canvas;
-  },
-
-  /**
-   * A list storing the bounding rectangle for each drawn block in the graph.
-   * Created whenever `buildGraphImage` is invoked.
-   */
-  _blocksBoundingRects: null,
-
-  /**
-   * Calculates the height of the tallest bar that would eventially be rendered
-   * in this graph.
-   *
-   * Bars that are too close too each other in the graph will be combined.
-   * @see `minBarsWidth`
-   *
-   * @return number
-   *         The tallest bar height in this graph.
-   */
-  _calcMaxHeight: function({ data, dataScaleX, minBarsWidth }) {
-    let maxHeight = 0;
-    let prevRight = 0;
-    let skippedCount = 0;
-    let skippedHeight = 0;
-    let scaledMarginEnd = BAR_GRAPH_BARS_MARGIN_END * this._pixelRatio;
-
-    for (let { delta, values } of data) {
-      let barRight = (delta - this.dataOffsetX) * dataScaleX;
-      let barHeight = values.reduce((a, b) => a + b, 0);
-
-      let barWidth = barRight - prevRight;
-      if (barWidth < minBarsWidth) {
-        skippedCount++;
-        skippedHeight += barHeight;
-        continue;
-      }
-
-      let averageHeight = (barHeight + skippedHeight) / (skippedCount + 1);
-      maxHeight = Math.max(averageHeight, maxHeight);
-
-      prevRight += barWidth + scaledMarginEnd;
-      skippedHeight = 0;
-      skippedCount = 0;
-    }
-
-    return maxHeight;
-  },
-
-  /**
-   * Creates the legend container when constructing this graph.
-   */
-  _createLegend: function() {
-    let legendNode = this._legendNode = this._document.createElementNS(HTML_NS, "div");
-    legendNode.className = "bar-graph-widget-legend";
-    this._container.appendChild(legendNode);
-  },
-
-  /**
-   * Creates a legend item when constructing this graph.
-   */
-  _createLegendItem: function(color, label) {
-    let itemNode = this._document.createElementNS(HTML_NS, "div");
-    itemNode.className = "bar-graph-widget-legend-item";
-
-    let colorNode = this._document.createElementNS(HTML_NS, "span");
-    colorNode.setAttribute("view", "color");
-    colorNode.setAttribute("data-index", this._legendNode.childNodes.length);
-    colorNode.style.backgroundColor = color;
-    colorNode.addEventListener("mouseover", this._onLegendMouseOver);
-    colorNode.addEventListener("mouseout", this._onLegendMouseOut);
-    colorNode.addEventListener("mousedown", this._onLegendMouseDown);
-    colorNode.addEventListener("mouseup", this._onLegendMouseUp);
-
-    this.outstandingEventListeners.push([colorNode, "mouseover", this._onLegendMouseOver]);
-    this.outstandingEventListeners.push([colorNode, "mouseout", this._onLegendMouseOut]);
-    this.outstandingEventListeners.push([colorNode, "mousedown", this._onLegendMouseDown]);
-    this.outstandingEventListeners.push([colorNode, "mouseup", this._onLegendMouseUp]);
-
-    let labelNode = this._document.createElementNS(HTML_NS, "span");
-    labelNode.setAttribute("view", "label");
-    labelNode.textContent = label;
-
-    itemNode.appendChild(colorNode);
-    itemNode.appendChild(labelNode);
-    this._legendNode.appendChild(itemNode);
-  },
-
-  /**
-   * Invoked whenever a color node in the legend is hovered.
-   */
-  _onLegendMouseOver: function(e) {
-    setNamedTimeout("bar-graph-debounce", BAR_GRAPH_LEGEND_MOUSEOVER_DEBOUNCE, () => {
-      let type = e.target.dataset.index;
-      let rects = this._blocksBoundingRects.filter(e => e.type == type);
-
-      this._originalHighlights = this._mask;
-      this._hasCustomHighlights = true;
-      this.setMask(rects, true);
-
-      this.emit("legend-hover", [type, rects]);
-    });
-  },
-
-  /**
-   * Invoked whenever a color node in the legend is unhovered.
-   */
-  _onLegendMouseOut: function() {
-    clearNamedTimeout("bar-graph-debounce");
-
-    if (this._hasCustomHighlights) {
-      this.setMask(this._originalHighlights);
-      this._hasCustomHighlights = false;
-      this._originalHighlights = null;
-    }
-
-    this.emit("legend-unhover");
-  },
-
-  /**
-   * Invoked whenever a color node in the legend is pressed.
-   */
-  _onLegendMouseDown: function(e) {
-    e.preventDefault();
-    e.stopPropagation();
-
-    let type = e.target.dataset.index;
-    let rects = this._blocksBoundingRects.filter(e => e.type == type);
-    let leftmost = rects[0];
-    let rightmost = rects[rects.length - 1];
-    if (!leftmost || !rightmost) {
-      this.dropSelection();
-    } else {
-      this.setSelection({ start: leftmost.start, end: rightmost.end });
-    }
-
-    this.emit("legend-selection", [leftmost, rightmost]);
-  },
-
-  /**
-   * Invoked whenever a color node in the legend is released.
-   */
-  _onLegendMouseUp: function(e) {
-    e.preventDefault();
-    e.stopPropagation();
-  }
-});
-
 // Helper functions.
 
 /**
  * Creates an iframe element with the provided source URL, appends it to
  * the specified node and invokes the callback once the content is loaded.
  *
  * @param string url
  *        The desired source URL for the iframe.
@@ -2194,51 +1368,16 @@ function map(value, istart, istop, ostar
  * @return number
  */
 function clamp(value, min, max) {
   if (value < min) return min;
   if (value > max) return max;
   return value;
 }
 
-/**
- * Calculates the squared distance between two 2D points.
- */
-function distSquared(x0, y0, x1, y1) {
-  let xs = x1 - x0;
-  let ys = y1 - y0;
-  return xs * xs + ys * ys;
-}
-
-/**
- * Finds the first element in an array that validates a predicate.
- * @param array
- * @param function predicate
- * @return number
- */
-function findFirst(array, predicate) {
-  for (let i = 0, len = array.length; i < len; i++) {
-    let element = array[i];
-    if (predicate(element)) return element;
-  }
-}
-
 exports.GraphCursor = GraphCursor;
 exports.GraphArea = GraphArea;
 exports.GraphAreaDragger = GraphAreaDragger;
 exports.GraphAreaResizer = GraphAreaResizer;
 exports.AbstractCanvasGraph = AbstractCanvasGraph;
-exports.LineGraphWidget = LineGraphWidget;
-exports.BarGraphWidget = BarGraphWidget;
 exports.CanvasGraphUtils = CanvasGraphUtils;
-
-/**
- * Finds the last element in an array that validates a predicate.
- * @param array
- * @param function predicate
- * @return number
- */
-function findLast(array, predicate) {
-  for (let i = array.length - 1; i >= 0; i--) {
-    let element = array[i];
-    if (predicate(element)) return element;
-  }
-}
+exports.CanvasGraphUtils.map = map;
+exports.CanvasGraphUtils.clamp = clamp;
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/widgets/LineGraphWidget.js
@@ -0,0 +1,386 @@
+"use strict";
+
+const { Cc, Ci, Cu, Cr } = require("chrome");
+
+const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
+const { ViewHelpers, Heritage } = require("resource:///modules/devtools/ViewHelpers.jsm");
+const { AbstractCanvasGraph, CanvasGraphUtils } = require("devtools/shared/widgets/Graphs");
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const L10N = new ViewHelpers.L10N();
+
+// Line graph constants.
+
+const GRAPH_DAMPEN_VALUES_FACTOR = 0.85;
+const GRAPH_TOOLTIP_SAFE_BOUNDS = 8; // px
+const GRAPH_MIN_MAX_TOOLTIP_DISTANCE = 14; // px
+
+const GRAPH_BACKGROUND_COLOR = "#0088cc";
+const GRAPH_STROKE_WIDTH = 1; // px
+const GRAPH_STROKE_COLOR = "rgba(255,255,255,0.9)";
+const GRAPH_HELPER_LINES_DASH = [5]; // px
+const GRAPH_HELPER_LINES_WIDTH = 1; // px
+const GRAPH_MAXIMUM_LINE_COLOR = "rgba(255,255,255,0.4)";
+const GRAPH_AVERAGE_LINE_COLOR = "rgba(255,255,255,0.7)";
+const GRAPH_MINIMUM_LINE_COLOR = "rgba(255,255,255,0.9)";
+const GRAPH_BACKGROUND_GRADIENT_START = "rgba(255,255,255,0.25)";
+const GRAPH_BACKGROUND_GRADIENT_END = "rgba(255,255,255,0.0)";
+
+const GRAPH_CLIPHEAD_LINE_COLOR = "#fff";
+const GRAPH_SELECTION_LINE_COLOR = "#fff";
+const GRAPH_SELECTION_BACKGROUND_COLOR = "rgba(44,187,15,0.25)";
+const GRAPH_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
+const GRAPH_REGION_BACKGROUND_COLOR = "transparent";
+const GRAPH_REGION_STRIPES_COLOR = "rgba(237,38,85,0.2)";
+
+/**
+ * A basic line graph, plotting values on a curve and adding helper lines
+ * and tooltips for maximum, average and minimum values.
+ *
+ * @see AbstractCanvasGraph for emitted events and other options.
+ *
+ * Example usage:
+ *   let graph = new LineGraphWidget(node, "units");
+ *   graph.once("ready", () => {
+ *     graph.setData(src);
+ *   });
+ *
+ * Data source format:
+ *   [
+ *     { delta: x1, value: y1 },
+ *     { delta: x2, value: y2 },
+ *     ...
+ *     { delta: xn, value: yn }
+ *   ]
+ * where each item in the array represents a point in the graph.
+ *
+ * @param nsIDOMNode parent
+ *        The parent node holding the graph.
+ * @param object options [optional]
+ *        `metric`: The metric displayed in the graph, e.g. "fps" or "bananas".
+ *        `min`: Boolean whether to show the min tooltip/gutter/line (default: true)
+ *        `max`: Boolean whether to show the max tooltip/gutter/line (default: true)
+ *        `avg`: Boolean whether to show the avg tooltip/gutter/line (default: true)
+ */
+this.LineGraphWidget = function(parent, options = {}, ...args) {
+  let { metric, min, max, avg } = options;
+
+  this._showMin = min !== false;
+  this._showMax = max !== false;
+  this._showAvg = avg !== false;
+
+  AbstractCanvasGraph.apply(this, [parent, "line-graph", ...args]);
+
+  this.once("ready", () => {
+    // Create all gutters and tooltips incase the showing of min/max/avg
+    // are changed later
+    this._gutter = this._createGutter();
+    this._maxGutterLine = this._createGutterLine("maximum");
+    this._maxTooltip = this._createTooltip("maximum", "start", "max", metric);
+    this._minGutterLine = this._createGutterLine("minimum");
+    this._minTooltip = this._createTooltip("minimum", "start", "min", metric);
+    this._avgGutterLine = this._createGutterLine("average");
+    this._avgTooltip = this._createTooltip("average", "end", "avg", metric);
+  });
+};
+
+LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
+  backgroundColor: GRAPH_BACKGROUND_COLOR,
+  backgroundGradientStart: GRAPH_BACKGROUND_GRADIENT_START,
+  backgroundGradientEnd: GRAPH_BACKGROUND_GRADIENT_END,
+  strokeColor: GRAPH_STROKE_COLOR,
+  strokeWidth: GRAPH_STROKE_WIDTH,
+  maximumLineColor: GRAPH_MAXIMUM_LINE_COLOR,
+  averageLineColor: GRAPH_AVERAGE_LINE_COLOR,
+  minimumLineColor: GRAPH_MINIMUM_LINE_COLOR,
+  clipheadLineColor: GRAPH_CLIPHEAD_LINE_COLOR,
+  selectionLineColor: GRAPH_SELECTION_LINE_COLOR,
+  selectionBackgroundColor: GRAPH_SELECTION_BACKGROUND_COLOR,
+  selectionStripesColor: GRAPH_SELECTION_STRIPES_COLOR,
+  regionBackgroundColor: GRAPH_REGION_BACKGROUND_COLOR,
+  regionStripesColor: GRAPH_REGION_STRIPES_COLOR,
+
+  /**
+   * Optionally offsets the `delta` in the data source by this scalar.
+   */
+  dataOffsetX: 0,
+
+  /**
+   * Optionally uses this value instead of the last tick in the data source
+   * to compute the horizontal scaling.
+   */
+  dataDuration: 0,
+
+  /**
+   * The scalar used to multiply the graph values to leave some headroom.
+   */
+  dampenValuesFactor: GRAPH_DAMPEN_VALUES_FACTOR,
+
+  /**
+   * Specifies if min/max/avg tooltips have arrow handlers on their sides.
+   */
+  withTooltipArrows: true,
+
+  /**
+   * Specifies if min/max/avg tooltips are positioned based on the actual
+   * values, or just placed next to the graph corners.
+   */
+  withFixedTooltipPositions: false,
+
+  /**
+   * Takes a list of numbers and plots them on a line graph representing
+   * the rate of occurences in a specified interval. Useful for drawing
+   * framerate, for example, from a sequence of timestamps.
+   *
+   * @param array timestamps
+   *        A list of numbers representing time, ordered ascending. For example,
+   *        this can be the raw data received from the framerate actor, which
+   *        represents the elapsed time on each refresh driver tick.
+   * @param number interval
+   *        The maximum amount of time to wait between calculations.
+   * @param number duration
+   *        The duration of the recording in milliseconds.
+   */
+  setDataFromTimestamps: Task.async(function*(timestamps, interval, duration) {
+    let {
+      plottedData,
+      plottedMinMaxSum
+    } = yield CanvasGraphUtils._performTaskInWorker("plotTimestampsGraph", {
+      timestamps, interval, duration
+    });
+
+    this._tempMinMaxSum = plottedMinMaxSum;
+    this.setData(plottedData);
+  }),
+
+  /**
+   * Renders the graph's data source.
+   * @see AbstractCanvasGraph.prototype.buildGraphImage
+   */
+  buildGraphImage: function() {
+    let { canvas, ctx } = this._getNamedCanvas("line-graph-data");
+    let width = this._width;
+    let height = this._height;
+
+    let totalTicks = this._data.length;
+    let firstTick = totalTicks ? this._data[0].delta : 0;
+    let lastTick = totalTicks ? this._data[totalTicks - 1].delta : 0;
+    let maxValue = Number.MIN_SAFE_INTEGER;
+    let minValue = Number.MAX_SAFE_INTEGER;
+    let avgValue = 0;
+
+    if (this._tempMinMaxSum) {
+      maxValue = this._tempMinMaxSum.maxValue;
+      minValue = this._tempMinMaxSum.minValue;
+      avgValue = this._tempMinMaxSum.avgValue;
+    } else {
+      let sumValues = 0;
+      for (let { delta, value } of this._data) {
+        maxValue = Math.max(value, maxValue);
+        minValue = Math.min(value, minValue);
+        sumValues += value;
+      }
+      avgValue = sumValues / totalTicks;
+    }
+
+    let duration = this.dataDuration || lastTick;
+    let dataScaleX = this.dataScaleX = width / (duration - this.dataOffsetX);
+    let dataScaleY = this.dataScaleY = height / maxValue * this.dampenValuesFactor;
+
+    // Draw the background.
+
+    ctx.fillStyle = this.backgroundColor;
+    ctx.fillRect(0, 0, width, height);
+
+    // Draw the graph.
+
+    let gradient = ctx.createLinearGradient(0, height / 2, 0, height);
+    gradient.addColorStop(0, this.backgroundGradientStart);
+    gradient.addColorStop(1, this.backgroundGradientEnd);
+    ctx.fillStyle = gradient;
+    ctx.strokeStyle = this.strokeColor;
+    ctx.lineWidth = this.strokeWidth * this._pixelRatio;
+    ctx.beginPath();
+
+    for (let { delta, value } of this._data) {
+      let currX = (delta - this.dataOffsetX) * dataScaleX;
+      let currY = height - value * dataScaleY;
+
+      if (delta == firstTick) {
+        ctx.moveTo(-GRAPH_STROKE_WIDTH, height);
+        ctx.lineTo(-GRAPH_STROKE_WIDTH, currY);
+      }
+
+      ctx.lineTo(currX, currY);
+
+      if (delta == lastTick) {
+        ctx.lineTo(width + GRAPH_STROKE_WIDTH, currY);
+        ctx.lineTo(width + GRAPH_STROKE_WIDTH, height);
+      }
+    }
+
+    ctx.fill();
+    ctx.stroke();
+
+    this._drawOverlays(ctx, minValue, maxValue, avgValue, dataScaleY);
+
+    return canvas;
+  },
+
+  /**
+   * Draws the min, max and average horizontal lines, along with their
+   * repsective tooltips.
+   *
+   * @param CanvasRenderingContext2D ctx
+   * @param number minValue
+   * @param number maxValue
+   * @param number avgValue
+   * @param number dataScaleY
+   */
+  _drawOverlays: function(ctx, minValue, maxValue, avgValue, dataScaleY) {
+    let width = this._width;
+    let height = this._height;
+    let totalTicks = this._data.length;
+
+    // Draw the maximum value horizontal line.
+    if (this._showMax) {
+      ctx.strokeStyle = this.maximumLineColor;
+      ctx.lineWidth = GRAPH_HELPER_LINES_WIDTH;
+      ctx.setLineDash(GRAPH_HELPER_LINES_DASH);
+      ctx.beginPath();
+      let maximumY = height - maxValue * dataScaleY;
+      ctx.moveTo(0, maximumY);
+      ctx.lineTo(width, maximumY);
+      ctx.stroke();
+    }
+
+    // Draw the average value horizontal line.
+    if (this._showAvg) {
+      ctx.strokeStyle = this.averageLineColor;
+      ctx.lineWidth = GRAPH_HELPER_LINES_WIDTH;
+      ctx.setLineDash(GRAPH_HELPER_LINES_DASH);
+      ctx.beginPath();
+      let averageY = height - avgValue * dataScaleY;
+      ctx.moveTo(0, averageY);
+      ctx.lineTo(width, averageY);
+      ctx.stroke();
+    }
+
+    // Draw the minimum value horizontal line.
+    if (this._showMin) {
+      ctx.strokeStyle = this.minimumLineColor;
+      ctx.lineWidth = GRAPH_HELPER_LINES_WIDTH;
+      ctx.setLineDash(GRAPH_HELPER_LINES_DASH);
+      ctx.beginPath();
+      let minimumY = height - minValue * dataScaleY;
+      ctx.moveTo(0, minimumY);
+      ctx.lineTo(width, minimumY);
+      ctx.stroke();
+    }
+
+    // Update the tooltips text and gutter lines.
+
+    this._maxTooltip.querySelector("[text=value]").textContent =
+      L10N.numberWithDecimals(maxValue, 2);
+    this._avgTooltip.querySelector("[text=value]").textContent =
+      L10N.numberWithDecimals(avgValue, 2);
+    this._minTooltip.querySelector("[text=value]").textContent =
+      L10N.numberWithDecimals(minValue, 2);
+
+    let bottom = height / this._pixelRatio;
+    let maxPosY = CanvasGraphUtils.map(maxValue * this.dampenValuesFactor, 0, maxValue, bottom, 0);
+    let avgPosY = CanvasGraphUtils.map(avgValue * this.dampenValuesFactor, 0, maxValue, bottom, 0);
+    let minPosY = CanvasGraphUtils.map(minValue * this.dampenValuesFactor, 0, maxValue, bottom, 0);
+
+    let safeTop = GRAPH_TOOLTIP_SAFE_BOUNDS;
+    let safeBottom = bottom - GRAPH_TOOLTIP_SAFE_BOUNDS;
+
+    let maxTooltipTop = (this.withFixedTooltipPositions
+      ? safeTop : CanvasGraphUtils.clamp(maxPosY, safeTop, safeBottom));
+    let avgTooltipTop = (this.withFixedTooltipPositions
+      ? safeTop : CanvasGraphUtils.clamp(avgPosY, safeTop, safeBottom));
+    let minTooltipTop = (this.withFixedTooltipPositions
+      ? safeBottom : CanvasGraphUtils.clamp(minPosY, safeTop, safeBottom));
+
+    this._maxTooltip.style.top = maxTooltipTop + "px";
+    this._avgTooltip.style.top = avgTooltipTop + "px";
+    this._minTooltip.style.top = minTooltipTop + "px";
+
+    this._maxGutterLine.style.top = maxPosY + "px";
+    this._avgGutterLine.style.top = avgPosY + "px";
+    this._minGutterLine.style.top = minPosY + "px";
+
+    this._maxTooltip.setAttribute("with-arrows", this.withTooltipArrows);
+    this._avgTooltip.setAttribute("with-arrows", this.withTooltipArrows);
+    this._minTooltip.setAttribute("with-arrows", this.withTooltipArrows);
+
+    let distanceMinMax = Math.abs(maxTooltipTop - minTooltipTop);
+    this._maxTooltip.hidden = this._showMax === false || !totalTicks || distanceMinMax < GRAPH_MIN_MAX_TOOLTIP_DISTANCE;
+    this._avgTooltip.hidden = this._showAvg === false || !totalTicks;
+    this._minTooltip.hidden = this._showMin === false || !totalTicks;
+    this._gutter.hidden = (this._showMin === false && this._showAvg === false && this._showMax === false) || !totalTicks;
+
+    this._maxGutterLine.hidden = this._showMax === false;
+    this._avgGutterLine.hidden = this._showAvg === false;
+    this._minGutterLine.hidden = this._showMin === false;
+  },
+
+  /**
+   * Creates the gutter node when constructing this graph.
+   * @return nsIDOMNode
+   */
+  _createGutter: function() {
+    let gutter = this._document.createElementNS(HTML_NS, "div");
+    gutter.className = "line-graph-widget-gutter";
+    gutter.setAttribute("hidden", true);
+    this._container.appendChild(gutter);
+
+    return gutter;
+  },
+
+  /**
+   * Creates the gutter line nodes when constructing this graph.
+   * @return nsIDOMNode
+   */
+  _createGutterLine: function(type) {
+    let line = this._document.createElementNS(HTML_NS, "div");
+    line.className = "line-graph-widget-gutter-line";
+    line.setAttribute("type", type);
+    this._gutter.appendChild(line);
+
+    return line;
+  },
+
+  /**
+   * Creates the tooltip nodes when constructing this graph.
+   * @return nsIDOMNode
+   */
+  _createTooltip: function(type, arrow, info, metric) {
+    let tooltip = this._document.createElementNS(HTML_NS, "div");
+    tooltip.className = "line-graph-widget-tooltip";
+    tooltip.setAttribute("type", type);
+    tooltip.setAttribute("arrow", arrow);
+    tooltip.setAttribute("hidden", true);
+
+    let infoNode = this._document.createElementNS(HTML_NS, "span");
+    infoNode.textContent = info;
+    infoNode.setAttribute("text", "info");
+
+    let valueNode = this._document.createElementNS(HTML_NS, "span");
+    valueNode.textContent = 0;
+    valueNode.setAttribute("text", "value");
+
+    let metricNode = this._document.createElementNS(HTML_NS, "span");
+    metricNode.textContent = metric;
+    metricNode.setAttribute("text", "metric");
+
+    tooltip.appendChild(infoNode);
+    tooltip.appendChild(valueNode);
+    tooltip.appendChild(metricNode);
+    this._container.appendChild(tooltip);
+
+    return tooltip;
+  }
+});
+
+module.exports = LineGraphWidget;
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/widgets/MountainGraphWidget.js
@@ -0,0 +1,196 @@
+"use strict";
+
+const { Cc, Ci, Cu, Cr } = require("chrome");
+
+const { Heritage } = require("resource:///modules/devtools/ViewHelpers.jsm");
+const { AbstractCanvasGraph, CanvasGraphUtils } = require("devtools/shared/widgets/Graphs");
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+
+// Bar graph constants.
+
+const GRAPH_DAMPEN_VALUES_FACTOR = 0.9;
+
+const GRAPH_BACKGROUND_COLOR = "#ddd";
+const GRAPH_STROKE_WIDTH = 2; // px
+const GRAPH_STROKE_COLOR = "rgba(255,255,255,0.9)";
+const GRAPH_HELPER_LINES_DASH = [5]; // px
+const GRAPH_HELPER_LINES_WIDTH = 1; // px
+
+const GRAPH_CLIPHEAD_LINE_COLOR = "#fff";
+const GRAPH_SELECTION_LINE_COLOR = "#fff";
+const GRAPH_SELECTION_BACKGROUND_COLOR = "rgba(44,187,15,0.25)";
+const GRAPH_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
+const GRAPH_REGION_BACKGROUND_COLOR = "transparent";
+const GRAPH_REGION_STRIPES_COLOR = "rgba(237,38,85,0.2)";
+
+/**
+ * A mountain graph, plotting sets of values as line graphs.
+ *
+ * @see AbstractCanvasGraph for emitted events and other options.
+ *
+ * Example usage:
+ *   let graph = new MountainGraphWidget(node);
+ *   graph.format = ...;
+ *   graph.once("ready", () => {
+ *     graph.setData(src);
+ *   });
+ *
+ * The `graph.format` traits are mandatory and will determine how each
+ * section of the moutain will be styled:
+ *   [
+ *     { color: "#f00", ... },
+ *     { color: "#0f0", ... },
+ *     ...
+ *     { color: "#00f", ... }
+ *   ]
+ *
+ * Data source format:
+ *   [
+ *     { delta: x1, values: [y11, y12, ... y1n] },
+ *     { delta: x2, values: [y21, y22, ... y2n] },
+ *     ...
+ *     { delta: xm, values: [ym1, ym2, ... ymn] }
+ *   ]
+ * where the [ymn] values is assumed to aready be normalized from [0..1].
+ *
+ * @param nsIDOMNode parent
+ *        The parent node holding the graph.
+ */
+this.MountainGraphWidget = function(parent, ...args) {
+  AbstractCanvasGraph.apply(this, [parent, "mountain-graph", ...args]);
+};
+
+MountainGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
+  backgroundColor: GRAPH_BACKGROUND_COLOR,
+  strokeColor: GRAPH_STROKE_COLOR,
+  strokeWidth: GRAPH_STROKE_WIDTH,
+  clipheadLineColor: GRAPH_CLIPHEAD_LINE_COLOR,
+  selectionLineColor: GRAPH_SELECTION_LINE_COLOR,
+  selectionBackgroundColor: GRAPH_SELECTION_BACKGROUND_COLOR,
+  selectionStripesColor: GRAPH_SELECTION_STRIPES_COLOR,
+  regionBackgroundColor: GRAPH_REGION_BACKGROUND_COLOR,
+  regionStripesColor: GRAPH_REGION_STRIPES_COLOR,
+
+  /**
+   * List of rules used to style each section of the mountain.
+   * @see constructor
+   * @type array
+   */
+  format: null,
+
+  /**
+   * Optionally offsets the `delta` in the data source by this scalar.
+   */
+  dataOffsetX: 0,
+
+  /**
+   * Optionally uses this value instead of the last tick in the data source
+   * to compute the horizontal scaling.
+   */
+  dataDuration: 0,
+
+  /**
+   * The scalar used to multiply the graph values to leave some headroom
+   * on the top.
+   */
+  dampenValuesFactor: GRAPH_DAMPEN_VALUES_FACTOR,
+
+  /**
+   * Renders the graph's background.
+   * @see AbstractCanvasGraph.prototype.buildBackgroundImage
+   */
+  buildBackgroundImage: function() {
+    let { canvas, ctx } = this._getNamedCanvas("mountain-graph-background");
+    let width = this._width;
+    let height = this._height;
+
+    ctx.fillStyle = this.backgroundColor;
+    ctx.fillRect(0, 0, width, height);
+
+    return canvas;
+  },
+
+  /**
+   * Renders the graph's data source.
+   * @see AbstractCanvasGraph.prototype.buildGraphImage
+   */
+  buildGraphImage: function() {
+    if (!this.format || !this.format.length) {
+      throw "The graph format traits are mandatory to style the data source.";
+    }
+    let { canvas, ctx } = this._getNamedCanvas("mountain-graph-data");
+    let width = this._width;
+    let height = this._height;
+
+    let totalSections = this.format.length;
+    let totalTicks = this._data.length;
+    let firstTick = this._data[0].delta;
+    let lastTick = this._data[totalTicks - 1].delta;
+
+    let duration = this.dataDuration || lastTick;
+    let dataScaleX = this.dataScaleX = width / (duration - this.dataOffsetX);
+    let dataScaleY = this.dataScaleY = height * this.dampenValuesFactor;
+
+    // Draw the graph.
+
+    let prevHeights = Array.from({ length: totalTicks }).fill(0);
+
+    ctx.globalCompositeOperation = "destination-over";
+    ctx.strokeStyle = this.strokeColor;
+    ctx.lineWidth = this.strokeWidth * this._pixelRatio;
+
+    for (let section = 0; section < totalSections; section++) {
+      ctx.fillStyle = this.format[section].color || "#000";
+      ctx.beginPath();
+
+      for (let tick = 0; tick < totalTicks; tick++) {
+        let { delta, values } = this._data[tick];
+        let currX = (delta - this.dataOffsetX) * dataScaleX;
+        let currY = values[section] * dataScaleY;
+        let prevY = prevHeights[tick];
+
+        if (delta == firstTick) {
+          ctx.moveTo(-GRAPH_STROKE_WIDTH, height);
+          ctx.lineTo(-GRAPH_STROKE_WIDTH, height - currY - prevY);
+        }
+
+        ctx.lineTo(currX, height - currY - prevY);
+
+        if (delta == lastTick) {
+          ctx.lineTo(width + GRAPH_STROKE_WIDTH, height - currY - prevY);
+          ctx.lineTo(width + GRAPH_STROKE_WIDTH, height);
+        }
+
+        prevHeights[tick] += currY;
+      }
+
+      ctx.fill();
+      ctx.stroke();
+    }
+
+    ctx.globalCompositeOperation = "source-over";
+    ctx.lineWidth = GRAPH_HELPER_LINES_WIDTH;
+    ctx.setLineDash(GRAPH_HELPER_LINES_DASH);
+
+    // Draw the maximum value horizontal line.
+
+    ctx.beginPath();
+    let maximumY = height * this.dampenValuesFactor;
+    ctx.moveTo(0, maximumY);
+    ctx.lineTo(width, maximumY);
+    ctx.stroke();
+
+    // Draw the average value horizontal line.
+
+    ctx.beginPath();
+    let averageY = height / 2 * this.dampenValuesFactor;
+    ctx.moveTo(0, averageY);
+    ctx.lineTo(width, averageY);
+    ctx.stroke();
+
+    return canvas;
+  }
+});
+
+module.exports = MountainGraphWidget;
--- a/browser/devtools/webaudioeditor/includes.js
+++ b/browser/devtools/webaudioeditor/includes.js
@@ -20,17 +20,17 @@ const { Task } = Cu.import("resource://g
 const { Class } = require("sdk/core/heritage");
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const STRINGS_URI = "chrome://browser/locale/devtools/webaudioeditor.properties"
 const L10N = new ViewHelpers.L10N(STRINGS_URI);
 const Telemetry = require("devtools/shared/telemetry");
 const telemetry = new Telemetry();
 
 devtools.lazyRequireGetter(this, "LineGraphWidget",
-  "devtools/shared/widgets/Graphs", true);
+  "devtools/shared/widgets/LineGraphWidget");
 
 // `AUDIO_NODE_DEFINITION` defined in the controller's initialization,
 // which describes all the properties of an AudioNode
 let AUDIO_NODE_DEFINITION;
 
 // Override DOM promises with Promise.jsm helpers
 const { defer, all } = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
 
--- a/browser/locales/en-US/chrome/browser/devtools/profiler.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/profiler.properties
@@ -106,16 +106,22 @@ table.idle=(idle)
 # labels which, when clicked, jump to the debugger.
 table.url.tooltiptext=View source in Debugger
 
 # LOCALIZATION NOTE (table.zoom.tooltiptext):
 # This string is displayed in the call tree as the tooltip text for the 'zoom'
 # buttons (small magnifying glass icons) which spawn a new tab.
 table.zoom.tooltiptext=Inspect frame in new tab
 
+# LOCALIZATION NOTE (table.view-optimizations.tooltiptext):
+# This string is displayed in the icon displayed next to frames that
+# have optimization data
+table.view-optimizations.tooltiptext=View optimizations in JIT View
+
+
 # LOCALIZATION NOTE (recordingsList.saveDialogTitle):
 # This string is displayed as a title for saving a recording to disk.
 recordingsList.saveDialogTitle=Save profile…
 
 # LOCALIZATION NOTE (recordingsList.saveDialogJSONFilter):
 # This string is displayed as a filter for saving a recording to disk.
 recordingsList.saveDialogJSONFilter=JSON Files
 
--- a/browser/modules/BrowserUITelemetry.jsm
+++ b/browser/modules/BrowserUITelemetry.jsm
@@ -436,16 +436,22 @@ this.BrowserUITelemetry = {
       this._countMouseUpEvent("click-bookmarks-menu-button", action,
                               aEvent.button);
     }
   },
 
   _checkForBuiltinItem: function(aEvent) {
     let item = aEvent.originalTarget;
 
+    // We don't want to count clicks on the private browsing
+    // button for privacy reasons. See bug 1176391.
+    if (item.id == "privatebrowsing-button") {
+      return;
+    }
+
     // We special-case the bookmarks-menu-button, since we want to
     // monitor more than just clicks on it.
     if (item.id == "bookmarks-menu-button" ||
         getIDBasedOnFirstIDedAncestor(item) == "bookmarks-menu-button") {
       this._bookmarksMenuButtonMouseUp(aEvent);
       return;
     }
 
@@ -646,17 +652,19 @@ this.BrowserUITelemetry = {
   _contextMenuItemWhitelist: new Set([
     "close-without-interaction", // for closing the menu without clicking it.
     "custom-page-item", // The ID we use for page-provided items
     "unknown", // The bucket for stuff with no id.
     // Everything we know of so far (which will exclude add-on items):
     "navigation", "back", "forward", "reload", "stop", "bookmarkpage",
     "spell-no-suggestions", "spell-add-to-dictionary",
     "spell-undo-add-to-dictionary", "openlinkincurrent", "openlinkintab",
-    "openlink", "openlinkprivate", "bookmarklink", "sharelink", "savelink",
+    "openlink",
+    // "openlinkprivate" intentionally omitted for privacy reasons. See bug 1176391.
+    "bookmarklink", "sharelink", "savelink",
     "marklinkMenu", "copyemail", "copylink", "media-play", "media-pause",
     "media-mute", "media-unmute", "media-playbackrate",
     "media-playbackrate-050x", "media-playbackrate-100x",
     "media-playbackrate-150x", "media-playbackrate-200x",
     "media-showcontrols", "media-hidecontrols", "video-showstats",
     "video-hidestats", "video-fullscreen", "leave-dom-fullscreen",
     "reloadimage", "viewimage", "viewvideo", "copyimage-contents", "copyimage",
     "copyvideourl", "copyaudiourl", "saveimage", "shareimage", "sendimage",
@@ -674,16 +682,23 @@ this.BrowserUITelemetry = {
     "bidi-text-direction-toggle", "bidi-page-direction-toggle", "inspect",
     "media-eme-learn-more"
   ]),
 
   _contextMenuInteractions: {},
 
   registerContextMenuInteraction: function(keys, itemID) {
     if (itemID) {
+      if (itemID == "openlinkprivate") {
+        // Don't record anything, not even an other-item count
+        // if the user chose to open in a private window. See
+        // bug 1176391.
+        return;
+      }
+
       if (!this._contextMenuItemWhitelist.has(itemID)) {
         itemID = "other-item";
       }
       keys.push(itemID);
     }
 
     this._countEvent(keys, this._contextMenuInteractions);
   },
--- a/browser/themes/shared/devtools/performance.inc.css
+++ b/browser/themes/shared/devtools/performance.inc.css
@@ -64,16 +64,20 @@
   list-style-image: url(performance-icons.svg#details-call-tree);
 }
 
 #select-js-flamegraph-view,
 #select-memory-flamegraph-view {
   list-style-image: url(performance-icons.svg#details-flamegraph);
 }
 
+#select-optimizations-view {
+  list-style-image: url(profiler-stopwatch.svg);
+}
+
 /* Recording buttons */
 
 #main-record-button {
   list-style-image: url(profiler-stopwatch.svg);
 }
 
 #main-record-button[checked] {
   list-style-image: url(profiler-stopwatch-checked.svg);
@@ -632,36 +636,63 @@ menuitem.marker-color-graphs-blue:before
 }
 .opt-url:hover {
   text-decoration: underline;
 }
 .opt-url.debugger-link {
   cursor: pointer;
 }
 
-#jit-optimizations-view .opt-icon::before {
+.opt-icon::before {
   content: "";
   background-image: url(chrome://browser/skin/devtools/webconsole.svg);
   background-repeat: no-repeat;
   background-size: 72px 60px;
+  /* show grey "i" bubble by default */
+  background-position: -36px -36px;
   width: 12px;
   height: 12px;
   display: inline-block;
 
-  margin: 5px 6px 0 0;
   max-height: 12px;
 }
-.theme-light #jit-optimizations-view .opt-icon::before {
+
+#jit-optimizations-view .opt-icon::before {
+  margin: 5px 6px 0 0;
+}
+description.opt-icon {
+  margin: 0px 0px 0px 0px;
+}
+description.opt-icon::before {
+  margin: 1px 4px 0px 0px;
+}
+.theme-light .opt-icon::before {
   background-image: url(chrome://browser/skin/devtools/webconsole.svg#light-icons);
 }
-
-#jit-optimizations-view .opt-icon[severity=warning]::before {
+.opt-icon[severity=warning]::before {
   background-position: -24px -24px;
 }
+.opt-icon[type=linkable]::before {
+  cursor: pointer;
+}
 
+ul.frames-list {
+  list-style-type: none;
+  padding: 0px;
+  margin: 0px;
+}
+
+ul.frames-list li {
+  cursor: pointer;
+}
+
+ul.frames-list li.selected {
+  background-color: var(--theme-selection-background);
+  color: var(--theme-selection-color);
+}
 
 /**
  * Configurable Options
  *
  * Elements can be tagged with a class and visibility is controlled via a
  * preference being applied or removed.
  */
 
--- a/browser/themes/shared/devtools/widgets.inc.css
+++ b/browser/themes/shared/devtools/widgets.inc.css
@@ -1020,20 +1020,16 @@
 }
 
 .line-graph-widget-tooltip[type=average] > [text=value] {
   color: var(--theme-highlight-orange);
 }
 
 /* Bar graph widget */
 
-.bar-graph-widget-canvas {
-  background: #f7f7f7;
-}
-
 .bar-graph-widget-legend {
   position: absolute;
   top: 4px;
   left: 8px;
   color: #292e33;
   font-size: 80%;
   pointer-events: none;
 }
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1460,22 +1460,23 @@ richlistitem[selected="true"][current="t
 .ac-result-type-keyword,
 .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage),
 richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
   width: 16px;
   height: 16px;
 }
 
-@media (-moz-os-version: windows-xp),
-       not all and (-moz-windows-default-theme) {
-  .ac-result-type-keyword[selected="true"],
-  .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected),
-  richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
-    list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
+@media not all and (-moz-os-version: windows-vista) and (-moz-windows-default-theme) {
+  @media not all and (-moz-os-version: windows-win7) and (-moz-windows-default-theme) {
+    .ac-result-type-keyword[selected="true"],
+    .autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected),
+    richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
+      list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
+    }
   }
 }
 
 .ac-result-type-tag,
 .autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
   list-style-image: url("chrome://browser/skin/places/tag.png");
   width: 16px;
   height: 16px;
index bfdec23e33bd261c9b63925a04c01f5cdd58335d..c310dac494d79c6c1478449bc00175400ac69025
GIT binary patch
literal 93724
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%0DjC*
zQsuW89LkYQ46(i)sou!z{If4tkr+e&=IR-c^Z0?=RH{vpXD8I^^sJ#i?OE62RkI7G
zd{P=tQCQP+i7mqLvm*tZy$hetvcG@+{1I$T%M~4cu;|4>L%3mFMSWO#TQe~+8I;@e
zd>j<O!pbf_luZb}`9-m?_#UyQG@-ul^M+-n5KCq-hbhP_xbeFQ@rfBu`VYJ-a)Iyy
zL^)4*FVWUcPgi%UHQQa;lhOx9e_SjQNt;{{9X{m$Mgmf&M~(jWG=csgTccmnuGh;|
zf!zf^=6=dAZ%C@#M1Lin(JIC!by}8)3l|p^6(br8OiYAlcTkfFce-_}LJc>a2JX#{
zr>h%SaHW?V52lavHVKIeN2KZ5%W3VAEB`EvncVtv6)~WzdagxBY4p?LMYhh#UO&p&
zC-wN^008B)hWMN<{vLMMU;W{pAR)D&<6tj$NYhLTuVlpQ;fN;lIF3TtIa!iPo;Uw1
zH@Qe+#5`O;b)G+`e=@u4%}@8%mxOys2V&`5+84T$4*;DOD$>6!;2*0lMQ>jDPOVlL
z(7r7Do_x-vn51CK@xx5b(h^`&ypsYO9p13Ip$M6_dD0geuHmXtqgBLod<RFby!w^E
zG(ar5`T+onNL@vbHk&nqdYOwDVsAd~;3d+?Z`Y;8FO)QQu+{&Gy8135tZYTTyyYR{
zDh96kJM^_rz0Cd*Q-yU9Bvl_kOre@p?K$z&T=A2^`gHQnv1Bi|zFFuW==mh5B79=d
zLlry$_2Cx(#3u1J;|KdTZj|col)X64r6HT1-7<M*0Xpy*4I%4*71H{<Nxt!Tsg2&y
z*IT8U3C2X0^rox{B#1mk-;u*J8<$=Z7l&^we4(|_5;c7eHzI!X^F(Mg9MMyq*bC`@
zo0&{B+qxL%{OK+PbS^D{_mjxF70WkI_|M4vBO^anRb*A@G*|hDMn4HLNwLNY$ESWR
zV0gpn&KezpB*2>%t--2o*Xj%Psa`^m(RJZf&MVhT(E~--*vrLNF?J@c$;0m!crQRW
zKu)RJ_swd8wmi_e%{SEJ<napBecN0+$9(@xL42smv1g*Wfa3~8eH!*vIvBYspLx8_
znu!k<siX03p*0y^{9yC=y%tw2u;GaF`;&V|wUBmAZt|K7yvg=lte#1c`MUJ0@10Zd
zeW^h609p$dA2EAVd<mJva+G$c<((4>*`$4b2QGqplvBTp?s8P}f?ii-ZO~7Lv6}vI
z+jU2&A2%vkQfnE!WbxB~*LgALb&>MQIL}@<pp7eF8xM!U|MA^kwc;9SnIc99Igx#_
zN2Z*c#lLa&l2h#ZGFAEd$b*4U7u9*iU;g0y!UmY&_;ec~sVL2!v<ca{$9KDL&HaR?
zU8s>(xf#}u-9FEDWzF9dJ^?zh!A@~1ye{J90r=%yfi%OZFb%s!ceh8#qpsJX^cD6Y
z$>eI4$d5QpUaDJ=Y~|pubH3VH?bHBf_5`-ax37~#BU7Ax>(4&}(8?Wp_Ck#?|3H8T
zd)mfow3+6KgNv23C;>`bP@tf};VpGLoI|NOBU=tB7fc|1R<)7_`JSx0ZB{~d@tZba
zLBl^s9omWRxZn{->WU>5S$i+G7&tFvctYj-n0G(M{J0wM#Gh%G2k`o%vClXyC_IRy
zrscf<w!6I&QrBg=Kj%e|#0fW$XWK<qCKGk7^;IU5Trgt3Ft++2H8L<V)=KWi_=PJu
z@!&+U_%U+ZuHQ9r{o9!oqMNXT9veHft=<_+EdQAJq%O?4;qyg*nCS7g^a*XR$7*EZ
z;7D>brOx;7G*ac$xYRC8*2weF>eG)$?d*WXD{3(nVJ!ivTMey6`(9HOjXiO{djT|v
zbRgTMu-%5ulL$$`DfsB&{?0upD_vA|q%|cy<+UZ~Veo+NR9GU&wt>YL@8tOEs9K?z
z?)lnH+*yXGBba?O1E#VM$rNpov*RvI#IA~q+(u(`)URV_gyNH#e#TaXy8l&RVdR$C
zvOe(}Muj*I)@#B8_NLt*M?}Z7KkQ`ipHrzAqEG&5@-p*3*+PxK_U@Aj3tad-A}H?E
zA{kRUO{ek%m2K?UgdP+o`D#LoAILI82NPOF(P5NZA?9&KD;q+O<?hLw1_V#buD7b`
zd(o-;c4{8c?u(at{fo)pyoVFnf(4}OrkV>%r=P>F8lcAZ+9vCaj3G-_zU;|Uj%C7&
z#wkAYluR5uhK5*9Qh{tXOmMi3B6huF8O(yK@dmd@;@Q|t=mblt^nE3=abDeKOJwO%
zt{#)^rw@I`<NZ}xIdxms9V9snjz{+zYTI4v*xQ1_oCU1IPai-`MT#%VF!P_YJO!HS
zYf{lyIKNJSJ%3y5;{O9PAbu8{aGPMR7cEPbMbvsYG|6<G0zWMF|BCLIm%6V|H80x1
zsIt(MX5Nt^L^)1wQPpUB%{$AXU5~2r8;`r2l{=PN`fhiASg)Gmpa)}?JMf$3U~uaI
z;&hba7*rtt`>`eTjM<A}8mG0W89(@9d0oBgf0I!&=DgA#c)h*})0Eeu=c6EK&{wXH
zaXjd0r|zlUk~Q6;_<t8rSrAy>cz<_P7gjT-URG?uM%IppG%EMq!@-jFYMVR;7xMr8
d_V=+LDh0<*)!n|I^1t`TR1`GitK{AX{U5D7N0tBp
index eb790b766e85d2e0e93f24e77aafbfa03ef62899..d1d52568bcafa3125a44008846847017dedd6a8b
GIT binary patch
literal 47300
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
D=)GyS
--- a/build/mobile/robocop/Actions.java
+++ b/build/mobile/robocop/Actions.java
@@ -4,17 +4,27 @@
 
 package org.mozilla.gecko;
 import android.database.Cursor;
 
 public interface Actions {
 
     /** Special keys supported by sendSpecialKey() */
     public enum SpecialKey {
-        DOWN, UP, LEFT, RIGHT, ENTER, MENU, BACK
+        DOWN,
+        UP,
+        LEFT,
+        RIGHT,
+        ENTER,
+        MENU,
+        /**
+         * @deprecated Use Solo.goBack() in Robocop instead.
+         */
+        @Deprecated
+        BACK
     }
 
     public interface EventExpecter {
         /** Blocks until the event has been received. Subsequent calls will return immediately. */
         public void blockForEvent();
         public void blockForEvent(long millis, boolean failOnTimeout);
 
         /** Blocks until the event has been received and returns data associated with the event. */
@@ -35,17 +45,17 @@ public interface Actions {
 
     public interface RepeatedEventExpecter extends EventExpecter {
         /** Blocks until at least one event has been received, and no events have been received in the last <code>millis</code> milliseconds. */
         public void blockUntilClear(long millis);
     }
 
     /**
      * Sends an event to Gecko.
-     * 
+     *
      * @param geckoEvent The geckoEvent JSONObject's type
      */
     void sendGeckoEvent(String geckoEvent, String data);
 
     /**
      * Sends a preferences get event to Gecko.
      *
      * @param requestId The id of this request.
@@ -67,32 +77,32 @@ public interface Actions {
      * @param requestId The id of this request.
      */
     void sendPreferencesRemoveObserversEvent(int requestid);
 
     /**
      * Listens for a gecko event to be sent from the Gecko instance.
      * The returned object can be used to test if the event has been
      * received. Note that only one event is listened for.
-     * 
+     *
      * @param geckoEvent The geckoEvent JSONObject's type
      */
     RepeatedEventExpecter expectGeckoEvent(String geckoEvent);
 
     /**
      * Listens for a paint event. Note that calling expectPaint() will
      * invalidate the event expecters returned from any previous calls
      * to expectPaint(); calling any methods on those invalidated objects
      * will result in undefined behaviour.
      */
     RepeatedEventExpecter expectPaint();
 
-    /** 
-     * Send a string to the application 
-     * 
+    /**
+     * Send a string to the application
+     *
      * @param keysToSend The string to send
      */
     void sendKeys(String keysToSend);
 
     /**
      * Send a special keycode to the element
      *
      * @param key The special key to send
--- a/mobile/android/base/DoorHangerPopup.java
+++ b/mobile/android/base/DoorHangerPopup.java
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import java.util.HashSet;
 
+import android.widget.PopupWindow;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.json.JSONArray;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.widget.AnchoredPopup;
 import org.mozilla.gecko.widget.DoorHanger;
@@ -19,16 +20,17 @@ import org.mozilla.gecko.widget.DoorHang
 import android.content.Context;
 import android.util.Log;
 import android.view.View;
 import org.mozilla.gecko.widget.DoorhangerConfig;
 
 public class DoorHangerPopup extends AnchoredPopup
                              implements GeckoEventListener,
                                         Tabs.OnTabsChangedListener,
+                                        PopupWindow.OnDismissListener,
                                         DoorHanger.OnButtonClickListener {
     private static final String LOGTAG = "GeckoDoorHangerPopup";
 
     // Stores a set of all active DoorHanger notifications. A DoorHanger is
     // uniquely identified by its tabId and value.
     private final HashSet<DoorHanger> mDoorHangers;
 
     // Whether or not the doorhanger popup is disabled.
@@ -38,16 +40,18 @@ public class DoorHangerPopup extends Anc
         super(context);
 
         mDoorHangers = new HashSet<DoorHanger>();
 
         EventDispatcher.getInstance().registerGeckoThreadListener(this,
             "Doorhanger:Add",
             "Doorhanger:Remove");
         Tabs.registerOnTabsChangedListener(this);
+
+        setOnDismissListener(this);
     }
 
     void destroy() {
         EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
             "Doorhanger:Add",
             "Doorhanger:Remove");
         Tabs.unregisterOnTabsChangedListener(this);
     }
@@ -134,30 +138,23 @@ public class DoorHangerPopup extends Anc
 
     // This callback is automatically executed on the UI thread.
     @Override
     public void onTabChanged(final Tab tab, final Tabs.TabEvents msg, final Object data) {
         switch(msg) {
             case CLOSED:
                 // Remove any doorhangers for a tab when it's closed (make
                 // a temporary set to avoid a ConcurrentModificationException)
-                HashSet<DoorHanger> doorHangersToRemove = new HashSet<DoorHanger>();
-                for (DoorHanger dh : mDoorHangers) {
-                    if (dh.getTabId() == tab.getId())
-                        doorHangersToRemove.add(dh);
-                }
-                for (DoorHanger dh : doorHangersToRemove) {
-                    removeDoorHanger(dh);
-                }
+                removeTabDoorHangers(tab.getId(), true);
                 break;
 
             case LOCATION_CHANGE:
                 // Only remove doorhangers if the popup is hidden or if we're navigating to a new URL
                 if (!isShowing() || !data.equals(tab.getURL()))
-                    removeTransientDoorHangers(tab.getId());
+                    removeTabDoorHangers(tab.getId(), false);
 
                 // Update the popup if the location change was on the current tab
                 if (Tabs.getInstance().isSelectedTab(tab))
                     updatePopup();
                 break;
 
             case SELECTED:
                 // Always update the popup when a new tab is selected. This will cover cases
@@ -233,26 +230,32 @@ public class DoorHangerPopup extends Anc
      */
     void removeDoorHanger(final DoorHanger doorHanger) {
         mDoorHangers.remove(doorHanger);
         mContent.removeView(doorHanger);
     }
 
     /**
      * Removes doorhangers for a given tab.
+     * @param tabId identifier of the tab to remove doorhangers from
+     * @param forceRemove boolean for force-removing tabs. If true, all doorhangers associated
+     *                    with  the tab specified are removed; if false, only remove the doorhangers
+     *                    that are not persistent, as specified by the doorhanger options.
      *
      * This method must be called on the UI thread.
      */
-    void removeTransientDoorHangers(int tabId) {
+    void removeTabDoorHangers(int tabId, boolean forceRemove) {
         // Make a temporary set to avoid a ConcurrentModificationException
         HashSet<DoorHanger> doorHangersToRemove = new HashSet<DoorHanger>();
         for (DoorHanger dh : mDoorHangers) {
             // Only remove transient doorhangers for the given tab
-            if (dh.getTabId() == tabId && dh.shouldRemove(isShowing()))
-                doorHangersToRemove.add(dh);
+            if (dh.getTabId() == tabId
+                && (forceRemove || (!forceRemove && dh.shouldRemove(isShowing())))) {
+                    doorHangersToRemove.add(dh);
+            }
         }
 
         for (DoorHanger dh : doorHangersToRemove) {
             removeDoorHanger(dh);
         }
     }
 
     /**
@@ -269,37 +272,45 @@ public class DoorHangerPopup extends Anc
         if (tab == null || mDoorHangers.size() == 0 || !mInflated || mDisabled) {
             dismiss();
             return;
         }
 
         // Show doorhangers for the selected tab
         int tabId = tab.getId();
         boolean shouldShowPopup = false;
+        DoorHanger firstDoorhanger = null;
         for (DoorHanger dh : mDoorHangers) {
             if (dh.getTabId() == tabId) {
                 dh.setVisibility(View.VISIBLE);
                 shouldShowPopup = true;
+                if (firstDoorhanger == null) {
+                    firstDoorhanger = dh;
+                } else {
+                    dh.hideTitle();
+                }
             } else {
                 dh.setVisibility(View.GONE);
             }
         }
- 
+
         // Dismiss the popup if there are no doorhangers to show for this tab
         if (!shouldShowPopup) {
             dismiss();
             return;
         }
 
         showDividers();
         if (isShowing()) {
             show();
             return;
         }
 
+        firstDoorhanger.showTitle(tab.getFavicon(), tab.getBaseDomain());
+
         // Make the popup focusable for accessibility. This gets done here
         // so the node can be accessibility focused, but on pre-ICS devices this
         // causes crashes, so it is done after the popup is shown.
         if (Versions.feature14Plus) {
             setFocusable(true);
         }
 
         show();
@@ -323,15 +334,21 @@ public class DoorHangerPopup extends Anc
             }
         }
         if (lastVisibleDoorHanger != null) {
             lastVisibleDoorHanger.hideDivider();
         }
     }
 
     @Override
+    public void onDismiss() {
+        final int tabId = Tabs.getInstance().getSelectedTab().getId();
+        removeTabDoorHangers(tabId, true);
+    }
+
+    @Override
     public void dismiss() {
         // If the popup is focusable while it is hidden, we run into crashes
         // on pre-ICS devices when the popup gets focus before it is shown.
         setFocusable(false);
         super.dismiss();
     }
 }
--- a/mobile/android/base/db/BrowserDatabaseHelper.java
+++ b/mobile/android/base/db/BrowserDatabaseHelper.java
@@ -1,26 +1,31 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- */
 /* 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/. */
 
 package org.mozilla.gecko.db;
 
+import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserContract.Favicons;
 import org.mozilla.gecko.db.BrowserContract.History;
 import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
 import org.mozilla.gecko.db.BrowserContract.SearchHistory;
 import org.mozilla.gecko.db.BrowserContract.Thumbnails;
+import org.mozilla.gecko.util.FileUtils;
+
 import static org.mozilla.gecko.db.DBUtils.qualifyColumn;
 
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.database.SQLException;
 import android.database.sqlite.SQLiteDatabase;
@@ -29,26 +34,28 @@ import android.database.sqlite.SQLiteOpe
 import android.net.Uri;
 import android.os.Build;
 import android.util.Log;
 
 
 final class BrowserDatabaseHelper extends SQLiteOpenHelper {
     private static final String LOGTAG = "GeckoBrowserDBHelper";
 
-    public static final int DATABASE_VERSION = 23;
+    public static final int DATABASE_VERSION = 24;
     public static final String DATABASE_NAME = "browser.db";
 
     final protected Context mContext;
 
     static final String TABLE_BOOKMARKS = Bookmarks.TABLE_NAME;
     static final String TABLE_HISTORY = History.TABLE_NAME;
     static final String TABLE_FAVICONS = Favicons.TABLE_NAME;
     static final String TABLE_THUMBNAILS = Thumbnails.TABLE_NAME;
     static final String TABLE_READING_LIST = ReadingListItems.TABLE_NAME;
+    static final String TABLE_TABS = TabsProvider.TABLE_TABS;
+    static final String TABLE_CLIENTS = TabsProvider.TABLE_CLIENTS;
 
     static final String VIEW_COMBINED = Combined.VIEW_NAME;
     static final String VIEW_BOOKMARKS_WITH_FAVICONS = Bookmarks.VIEW_WITH_FAVICONS;
     static final String VIEW_HISTORY_WITH_FAVICONS = History.VIEW_WITH_FAVICONS;
     static final String VIEW_COMBINED_WITH_FAVICONS = Combined.VIEW_WITH_FAVICONS;
 
     static final String TABLE_BOOKMARKS_JOIN_FAVICONS = TABLE_BOOKMARKS + " LEFT OUTER JOIN " +
             TABLE_FAVICONS + " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.FAVICON_ID) + " = " +
@@ -169,16 +176,64 @@ final class BrowserDatabaseHelper extend
 
         db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_HISTORY_WITH_FAVICONS + " AS " +
                 "SELECT " + qualifyColumn(TABLE_HISTORY, "*") +
                 ", " + qualifyColumn(TABLE_FAVICONS, Favicons.DATA) + " AS " + History.FAVICON +
                 ", " + qualifyColumn(TABLE_FAVICONS, Favicons.URL) + " AS " + History.FAVICON_URL +
                 " FROM " + TABLE_HISTORY_JOIN_FAVICONS);
     }
 
+    private void createClientsTable(SQLiteDatabase db) {
+        debug("Creating " + TABLE_CLIENTS + " table");
+
+        // Table for client's name-guid mapping.
+        db.execSQL("CREATE TABLE " + TABLE_CLIENTS + "(" +
+                BrowserContract.Clients.GUID + " TEXT PRIMARY KEY," +
+                BrowserContract.Clients.NAME + " TEXT," +
+                BrowserContract.Clients.LAST_MODIFIED + " INTEGER," +
+                BrowserContract.Clients.DEVICE_TYPE + " TEXT" +
+                ");");
+
+        // Index on GUID.
+        db.execSQL("CREATE INDEX " + TabsProvider.INDEX_CLIENTS_GUID +
+                " ON " + TABLE_CLIENTS + "(" + BrowserContract.Clients.GUID + ")");
+    }
+
+    private void createTabsTable(SQLiteDatabase db) {
+        debug("Creating tabs.db: " + db.getPath());
+        debug("Creating " + TABLE_TABS + " table");
+
+        // Table for each tab on any client.
+        db.execSQL("CREATE TABLE " + TABLE_TABS + "(" +
+                BrowserContract.Tabs._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
+                BrowserContract.Tabs.CLIENT_GUID + " TEXT," +
+                BrowserContract.Tabs.TITLE + " TEXT," +
+                BrowserContract.Tabs.URL + " TEXT," +
+                BrowserContract.Tabs.HISTORY + " TEXT," +
+                BrowserContract.Tabs.FAVICON + " TEXT," +
+                BrowserContract.Tabs.LAST_USED + " INTEGER," +
+                BrowserContract.Tabs.POSITION + " INTEGER" +
+                ");");
+
+        // Indices on CLIENT_GUID and POSITION.
+        db.execSQL("CREATE INDEX " + TabsProvider.INDEX_TABS_GUID +
+                " ON " + TABLE_TABS + "(" + BrowserContract.Tabs.CLIENT_GUID + ")");
+        db.execSQL("CREATE INDEX " + TabsProvider.INDEX_TABS_POSITION +
+                " ON " + TABLE_TABS + "(" + BrowserContract.Tabs.POSITION + ")");
+    }
+
+    // Insert a client row for our local Fennec client.
+    private void createLocalClient(SQLiteDatabase db) {
+        debug("Inserting local Fennec client into " + TABLE_CLIENTS + " table");
+
+        ContentValues values = new ContentValues();
+        values.put(BrowserContract.Clients.LAST_MODIFIED, System.currentTimeMillis());
+        db.insertOrThrow(TABLE_CLIENTS, null, values);
+    }
+
     private void createCombinedViewOn19(SQLiteDatabase db) {
         /*
         The v19 combined view removes the redundant subquery from the v16
         combined view and reorders the columns as necessary to prevent this
         from breaking any code that might be referencing columns by index.
 
         The rows in the ensuing view are, in order:
 
@@ -281,31 +336,64 @@ final class BrowserDatabaseHelper extend
         for (Table table : BrowserProvider.sTables) {
             table.onCreate(db);
         }
 
         createBookmarksTable(db);
         createHistoryTable(db);
         createFaviconsTable(db);
         createThumbnailsTable(db);
+        createTabsTable(db);
+        createClientsTable(db);
+        createLocalClient(db);
 
         createBookmarksWithFaviconsView(db);
         createHistoryWithFaviconsView(db);
         createCombinedViewOn19(db);
 
         createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID,
             R.string.bookmarks_folder_places, 0);
 
         createOrUpdateAllSpecialFolders(db);
         createSearchHistoryTable(db);
         createReadingListTable(db, TABLE_READING_LIST);
         didCreateCurrentReadingListTable = true;      // Mostly correct, in the absence of transactions.
         createReadingListIndices(db, TABLE_READING_LIST);
     }
 
+    /**
+     * Copies the tabs and clients tables out of the given tabs.db file and into the destinationDB.
+     *
+     * @param tabsDBFile Path to existing tabs.db.
+     * @param destinationDB The destination database.
+     */
+    public void copyTabsDB(File tabsDBFile, SQLiteDatabase destinationDB) {
+        createTabsTable(destinationDB);
+        createClientsTable(destinationDB);
+
+        SQLiteDatabase oldTabsDB = null;
+        try {
+            oldTabsDB = SQLiteDatabase.openDatabase(tabsDBFile.getPath(), null, SQLiteDatabase.OPEN_READONLY);
+
+            if (!DBUtils.copyTable(oldTabsDB, TABLE_CLIENTS, destinationDB, TABLE_CLIENTS)) {
+                Log.e(LOGTAG, "Failed to migrate table clients; ignoring.");
+            }
+            if (!DBUtils.copyTable(oldTabsDB, TABLE_TABS, destinationDB, TABLE_TABS)) {
+                Log.e(LOGTAG, "Failed to migrate table tabs; ignoring.");
+            }
+        } catch (Exception e) {
+            Log.e(LOGTAG, "Exception occurred while trying to copy from " + tabsDBFile.getPath() +
+                    " to " + destinationDB.getPath() + "; ignoring.", e);
+        } finally {
+            if (oldTabsDB != null) {
+                oldTabsDB.close();
+            }
+        }
+    }
+
     private void createSearchHistoryTable(SQLiteDatabase db) {
         debug("Creating " + SearchHistory.TABLE_NAME + " table");
 
         db.execSQL("CREATE TABLE " + SearchHistory.TABLE_NAME + "(" +
                     SearchHistory._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
                     SearchHistory.QUERY + " TEXT UNIQUE NOT NULL, " +
                     SearchHistory.DATE_LAST_VISITED + " INTEGER, " +
                     SearchHistory.VISITS + " INTEGER ) ");
@@ -873,16 +961,36 @@ final class BrowserDatabaseHelper extend
 
         // Now switch these tables over and recreate the indices.
         db.execSQL("DROP TABLE " + TABLE_READING_LIST);
         db.execSQL("ALTER TABLE tmp_rl RENAME TO " + TABLE_READING_LIST);
 
         createReadingListIndices(db, TABLE_READING_LIST);
     }
 
+    private void upgradeDatabaseFrom23to24(SQLiteDatabase db) {
+        // Version 24 consolidates the tabs and clients table into browser.db.  Before, they lived in tabs.db.
+        // It's easier to copy the existing data than to arrange for Sync to re-populate it.
+        try {
+            final File oldTabsDBFile = new File(GeckoProfile.get(mContext).getDir(), "tabs.db");
+            copyTabsDB(oldTabsDBFile, db);
+        } catch (Exception e) {
+            Log.e(LOGTAG, "Got exception copying tabs and clients data from tabs.db to browser.db; ignoring.", e);
+        }
+
+        // Delete the database, the shared memory, and the log.
+        for (String filename : new String[] { "tabs.db", "tabs.db-shm", "tabs.db-wal" }) {
+            final File file = new File(GeckoProfile.get(mContext).getDir(), filename);
+            try {
+                FileUtils.delete(file);
+            } catch (Exception e) {
+                Log.e(LOGTAG, "Exception occurred while trying to delete " + file.getPath() + "; ignoring.", e);
+            }
+        }
+    }
 
     private void createV19CombinedView(SQLiteDatabase db) {
         db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED);
         db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED_WITH_FAVICONS);
 
         createCombinedViewOn19(db);
     }
 
@@ -945,16 +1053,20 @@ final class BrowserDatabaseHelper extend
 
                 case 22:
                     upgradeDatabaseFrom21to22(db);
                     break;
 
                 case 23:
                     upgradeDatabaseFrom22to23(db);
                     break;
+
+                case 24:
+                    upgradeDatabaseFrom23to24(db);
+                    break;
             }
         }
 
         for (Table table : BrowserProvider.sTables) {
             table.onUpgrade(db, oldVersion, newVersion);
         }
 
         // Delete the obsolete favicon database after all other upgrades complete.
--- a/mobile/android/base/db/DBUtils.java
+++ b/mobile/android/base/db/DBUtils.java
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.db;
 
 import android.annotation.TargetApi;
+import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteStatement;
 import android.os.Build;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoProfile;
 
 import android.content.ContentValues;
@@ -110,16 +111,60 @@ public class DBUtils {
         // If we needed to retry, but we succeeded, report that in telemetry.
         // Failures are indicated by a lower frequency of UNLOCKED than LOCKED.
         if (attempt > 1) {
             Telemetry.addToHistogram(HISTOGRAM_DATABASE_UNLOCKED, attempt - 1);
         }
     }
 
     /**
+     * Copies a table <b>between</b> database files.
+     *
+     * This method assumes that the source table and destination table already exist in the
+     * source and destination databases, respectively.
+     *
+     * The table is copied row-by-row in a single transaction.
+     *
+     * @param source The source database that the table will be copied from.
+     * @param sourceTableName The name of the source table.
+     * @param destination The destination database that the table will be copied to.
+     * @param destinationTableName The name of the destination table.
+     * @return true if all rows were copied; false otherwise.
+     */
+    public static boolean copyTable(SQLiteDatabase source, String sourceTableName,
+                                    SQLiteDatabase destination, String destinationTableName) {
+        Cursor cursor = null;
+        try {
+            destination.beginTransaction();
+
+            cursor = source.query(sourceTableName, null, null, null, null, null, null);
+            Log.d(LOGTAG, "Trying to copy " + cursor.getCount() + " rows from " + sourceTableName + " to " + destinationTableName);
+
+            final ContentValues contentValues = new ContentValues();
+            while (cursor.moveToNext()) {
+                contentValues.clear();
+                DatabaseUtils.cursorRowToContentValues(cursor, contentValues);
+                destination.insert(destinationTableName, null, contentValues);
+            }
+
+            destination.setTransactionSuccessful();
+            Log.d(LOGTAG, "Successfully copied " + cursor.getCount() + " rows from " + sourceTableName + " to " + destinationTableName);
+            return true;
+        } catch (Exception e) {
+            Log.w(LOGTAG, "Got exception copying rows from " + sourceTableName + " to " + destinationTableName + "; ignoring.", e);
+            return false;
+        } finally {
+            destination.endTransaction();
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    /**
      * Verifies that 0-byte arrays aren't added as favicon or thumbnail data.
      * @param values        ContentValues of query
      * @param columnName    Name of data column to verify
      */
     public static void stripEmptyByteArray(ContentValues values, String columnName) {
         if (values.containsKey(columnName)) {
             byte[] data = values.getAsByteArray(columnName);
             if (data == null || data.length == 0) {
--- a/mobile/android/base/db/TabsProvider.java
+++ b/mobile/android/base/db/TabsProvider.java
@@ -18,21 +18,17 @@ import android.content.Context;
 import android.content.UriMatcher;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
 import android.text.TextUtils;
 
-public class TabsProvider extends PerProfileDatabaseProvider<TabsProvider.TabsDatabaseHelper> {
-    static final String DATABASE_NAME = "tabs.db";
-
-    static final int DATABASE_VERSION = 3;
-
+public class TabsProvider extends SharedBrowserDatabaseProvider {
     static final String TABLE_TABS = "tabs";
     static final String TABLE_CLIENTS = "clients";
 
     static final int TABS = 600;
     static final int TABS_ID = 601;
     static final int CLIENTS = 602;
     static final int CLIENTS_ID = 603;
     static final int CLIENTS_RECENCY = 604;
@@ -95,118 +91,16 @@ public class TabsProvider extends PerPro
     private static final String projectColumn(String table, String column) {
         return table + "." + column;
     }
 
     private static final String selectColumn(String table, String column) {
         return projectColumn(table, column) + " = ?";
     }
 
-    final class TabsDatabaseHelper extends SQLiteOpenHelper {
-        public TabsDatabaseHelper(Context context, String databasePath) {
-            super(context, databasePath, null, DATABASE_VERSION);
-        }
-
-        @Override
-        public void onCreate(SQLiteDatabase db) {
-            debug("Creating tabs.db: " + db.getPath());
-            debug("Creating " + TABLE_TABS + " table");
-
-            // Table for each tab on any client.
-            db.execSQL("CREATE TABLE " + TABLE_TABS + "(" +
-                       Tabs._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
-                       Tabs.CLIENT_GUID + " TEXT," +
-                       Tabs.TITLE + " TEXT," +
-                       Tabs.URL + " TEXT," +
-                       Tabs.HISTORY + " TEXT," +
-                       Tabs.FAVICON + " TEXT," +
-                       Tabs.LAST_USED + " INTEGER," +
-                       Tabs.POSITION + " INTEGER" +
-                       ");");
-
-            // Indices on CLIENT_GUID and POSITION.
-            db.execSQL("CREATE INDEX " + INDEX_TABS_GUID +
-                       " ON " + TABLE_TABS + "(" + Tabs.CLIENT_GUID + ")");
-            db.execSQL("CREATE INDEX " + INDEX_TABS_POSITION +
-                       " ON " + TABLE_TABS + "(" + Tabs.POSITION + ")");
-
-            debug("Creating " + TABLE_CLIENTS + " table");
-
-            // Table for client's name-guid mapping.
-            db.execSQL("CREATE TABLE " + TABLE_CLIENTS + "(" +
-                       Clients.GUID + " TEXT PRIMARY KEY," +
-                       Clients.NAME + " TEXT," +
-                       Clients.LAST_MODIFIED + " INTEGER," +
-                       Clients.DEVICE_TYPE + " TEXT" +
-                       ");");
-
-            // Index on GUID.
-            db.execSQL("CREATE INDEX " + INDEX_CLIENTS_GUID +
-                       " ON " + TABLE_CLIENTS + "(" + Clients.GUID + ")");
-
-            createLocalClient(db);
-        }
-
-        // Insert a client row for our local Fennec client.
-        private void createLocalClient(SQLiteDatabase db) {
-            debug("Inserting local Fennec client into " + TABLE_CLIENTS + " table");
-
-            ContentValues values = new ContentValues();
-            values.put(BrowserContract.Clients.LAST_MODIFIED, System.currentTimeMillis());
-            db.insertOrThrow(TABLE_CLIENTS, null, values);
-        }
-
-        protected void upgradeDatabaseFrom2to3(SQLiteDatabase db) {
-            debug("Setting remote client device types to 'mobile' in " + TABLE_CLIENTS + " table");
-
-            // Add type to client, defaulting to mobile. This is correct for our
-            // local client; all remote clients will be updated by Sync.
-            db.execSQL("ALTER TABLE " + TABLE_CLIENTS + " ADD COLUMN " + BrowserContract.Clients.DEVICE_TYPE + " TEXT DEFAULT 'mobile'");
-        }
-
-        @Override
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            debug("Upgrading tabs.db: " + db.getPath() + " from " +
-                  oldVersion + " to " + newVersion);
-
-            // We have to do incremental upgrades until we reach the current
-            // database schema version.
-            for (int v = oldVersion + 1; v <= newVersion; v++) {
-                switch(v) {
-                    case 2:
-                        createLocalClient(db);
-                        break;
-
-                    case 3:
-                        upgradeDatabaseFrom2to3(db);
-                        break;
-                 }
-             }
-        }
-
-        @Override
-        public void onOpen(SQLiteDatabase db) {
-            debug("Opening tabs.db: " + db.getPath());
-            db.rawQuery("PRAGMA synchronous=OFF", null).close();
-
-            if (shouldUseTransactions()) {
-                // Modern Android allows WAL to be enabled through a mode flag.
-                if (Versions.preJB) {
-                    db.enableWriteAheadLogging();
-                }
-                db.setLockingEnabled(false);
-                return;
-            }
-
-            // If we're not using transactions (in particular, prior to
-            // Honeycomb), then we can do some lesser optimizations.
-            db.rawQuery("PRAGMA journal_mode=PERSIST", null).close();
-        }
-    }
-
     @Override
     public String getType(Uri uri) {
         final int match = URI_MATCHER.match(uri);
 
         trace("Getting URI type: " + uri);
 
         switch (match) {
             case TABS:
@@ -429,19 +323,9 @@ public class TabsProvider extends PerPro
 
     int deleteValues(Uri uri, String selection, String[] selectionArgs, String table) {
         debug("Deleting tabs for URI: " + uri);
 
         final SQLiteDatabase db = getWritableDatabase(uri);
         beginWrite(db);
         return db.delete(table, selection, selectionArgs);
     }
-
-    @Override
-    protected TabsDatabaseHelper createDatabaseHelper(Context context, String databasePath) {
-        return new TabsDatabaseHelper(context, databasePath);
-    }
-
-    @Override
-    protected String getDatabaseName() {
-        return DATABASE_NAME;
-    }
 }
--- a/mobile/android/base/resources/layout/doorhanger.xml
+++ b/mobile/android/base/resources/layout/doorhanger.xml
@@ -3,28 +3,41 @@
    - 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/. -->
 
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
     <LinearLayout android:layout_width="match_parent"
                   android:layout_height="wrap_content"
                   android:orientation="horizontal"
-                  android:padding="@dimen/doorhanger_padding">
+                  android:padding="@dimen/doorhanger_section_padding_small">
 
         <ImageView android:id="@+id/doorhanger_icon"
                    android:layout_width="@dimen/doorhanger_icon_size"
                    android:layout_height="@dimen/doorhanger_icon_size"
                    android:layout_gravity="center_horizontal"
                    android:paddingRight="@dimen/doorhanger_section_padding_small"
                    android:visibility="gone"/>
 
-        <ViewStub android:id="@+id/content"
-                  android:layout_width="match_parent"
-                  android:layout_height="wrap_content"/>
+        <LinearLayout android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:orientation="vertical">
+
+            <TextView android:id="@+id/doorhanger_title"
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
+                      android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"
+                      android:visibility="gone"/>
+
+            <ViewStub android:id="@+id/content"
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"/>
+
+        </LinearLayout>
 
     </LinearLayout>
 
     <LinearLayout android:layout_width="match_parent"
                   android:layout_height="wrap_content"
                   android:orientation="horizontal">
 
         <Button android:id="@+id/doorhanger_button_negative"
--- a/mobile/android/base/resources/layout/login_doorhanger.xml
+++ b/mobile/android/base/resources/layout/login_doorhanger.xml
@@ -3,22 +3,16 @@
    - 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/. -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:orientation="vertical">
 
-    <TextView android:id="@+id/doorhanger_title"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
-              android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"/>
-
     <TextView android:id="@+id/doorhanger_message"
               android:focusable="true"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:layout_marginBottom="@dimen/doorhanger_section_padding_large"
               android:textAppearance="@style/TextAppearance.DoorHanger.Medium"/>
 
     <TextView android:id="@+id/doorhanger_link"
--- a/mobile/android/base/resources/layout/login_edit_dialog.xml
+++ b/mobile/android/base/resources/layout/login_edit_dialog.xml
@@ -1,17 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:padding="@dimen/doorhanger_padding"
+              android:padding="@dimen/doorhanger_section_padding_small"
               android:orientation="vertical">
 
     <EditText android:id="@+id/username_edit"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:inputType="textNoSuggestions"
               android:hint="@string/doorhanger_login_edit_username_hint"/>
 
@@ -20,11 +20,11 @@
               android:layout_height="wrap_content"
               android:inputType="textPassword"
               android:hint="@string/doorhanger_login_edit_password_hint"/>
 
     <CheckBox android:id="@+id/checkbox_toggle_password"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:text="@string/doorhanger_login_edit_toggle"
-              android:layout_marginTop="@dimen/doorhanger_padding"/>
+              android:layout_marginTop="@dimen/doorhanger_subsection_padding"/>
 
 </LinearLayout>
--- a/mobile/android/base/resources/layout/site_identity.xml
+++ b/mobile/android/base/resources/layout/site_identity.xml
@@ -6,23 +6,23 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:orientation="vertical">
 
     <LinearLayout android:layout_width="match_parent"
                   android:layout_height="wrap_content"
                   android:orientation="horizontal"
-                  android:padding="@dimen/doorhanger_padding">
+                  android:padding="@dimen/doorhanger_section_padding_small">
 
         <ImageView android:id="@+id/larry"
                    android:layout_width="@dimen/doorhanger_icon_size"
                    android:layout_height="@dimen/doorhanger_icon_size"
                    android:src="@drawable/larry"
-                   android:paddingRight="@dimen/doorhanger_padding"/>
+                   android:paddingRight="@dimen/doorhanger_section_padding_small"/>
 
         <LinearLayout android:layout_width="0dp"
                      android:layout_height="wrap_content"
                      android:orientation="vertical"
                      android:layout_weight="1.0">
 
             <include layout="@layout/site_identity_unknown" />
 
@@ -30,17 +30,17 @@
                           android:layout_width="match_parent"
                           android:layout_height="wrap_content"
                           android:visibility="gone"
                           android:orientation="vertical">
 
                 <TextView android:id="@+id/site_identity_title"
                           android:layout_width="match_parent"
                           android:layout_height="wrap_content"
-                          android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
+                          android:layout_marginBottom="@dimen/doorhanger_subsection_padding"
                           android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"/>
 
                 <TextView android:id="@+id/site_identity_encrypted"
                           android:layout_width="match_parent"
                           android:layout_height="wrap_content"
                           android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
                           android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Bold"
                           android:textColor="@color/affirmative_green"
@@ -78,17 +78,17 @@
 
             </LinearLayout>
             <TextView android:id="@+id/site_settings_link"
                       android:layout_width="match_parent"
                       android:layout_height="wrap_content"
                       android:textAppearance="@style/TextAppearance.DoorHanger.Medium"
                       android:textColor="@color/link_blue"
                       android:layout_marginTop="@dimen/doorhanger_section_padding_large"
-                      android:layout_marginBottom="@dimen/doorhanger_padding"
+                      android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
                       android:text="@string/contextmenu_site_settings"
                       android:visibility="gone"/>
          </LinearLayout>
     </LinearLayout>
 
     <View android:id="@+id/divider_doorhanger"
           android:layout_width="match_parent"
           android:layout_height="1dp"
--- a/mobile/android/base/resources/values/dimens.xml
+++ b/mobile/android/base/resources/values/dimens.xml
@@ -97,21 +97,21 @@
     <dimen name="search_row_height">48dp</dimen>
 
     <!-- Padding at the top of the site identity popup, when no identity data is available. -->
     <dimen name="identity_padding_top">5dp</dimen>
 
     <dimen name="doorhanger_width">300dp</dimen>
     <dimen name="doorhanger_input_width">250dp</dimen>
     <dimen name="doorhanger_spinner_textsize">9sp</dimen>
-    <dimen name="doorhanger_padding">15dp</dimen>
     <dimen name="doorhanger_offsetX">12dp</dimen>
     <dimen name="doorhanger_offsetY">67dp</dimen>
     <dimen name="doorhanger_GB_offsetY">7dp</dimen>
     <dimen name="doorhanger_drawable_padding">5dp</dimen>
+    <dimen name="doorhanger_subsection_padding">8dp</dimen>
     <dimen name="doorhanger_section_padding_small">20dp</dimen>
     <dimen name="doorhanger_section_padding_large">30dp</dimen>
     <dimen name="doorhanger_icon_size">60dp</dimen>
 
     <dimen name="context_menu_item_horizontal_padding">10dp</dimen>
 
     <dimen name="flow_layout_spacing">6dp</dimen>
     <dimen name="menu_item_icon">21dp</dimen>
--- a/mobile/android/base/toolbar/SiteIdentityPopup.java
+++ b/mobile/android/base/toolbar/SiteIdentityPopup.java
@@ -3,33 +3,33 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.toolbar;
 
 import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.widget.ImageView;
 import android.widget.Toast;
 import org.json.JSONException;
 import org.json.JSONArray;
 import org.mozilla.gecko.AboutPages;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.SiteIdentity;
 import org.mozilla.gecko.SiteIdentity.SecurityMode;
 import org.mozilla.gecko.SiteIdentity.MixedMode;
 import org.mozilla.gecko.SiteIdentity.TrackingMode;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
-import org.mozilla.gecko.favicons.Favicons;
-import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.widget.AnchoredPopup;
 import org.mozilla.gecko.widget.DoorHanger;
 import org.mozilla.gecko.widget.DoorHanger.OnButtonClickListener;
 import org.json.JSONObject;
 
 import android.content.Context;
@@ -169,20 +169,19 @@ public class SiteIdentityPopup extends A
                 public void run() {
                     mSiteSettingsLink.setVisibility(hasPermissions ? View.VISIBLE : View.GONE);
                 }
             });
         }
     }
 
     private void addLoginsToTab(JSONObject data) throws JSONException {
-        final JSONObject titleObj = data.getJSONObject("title");
         final JSONArray logins = data.getJSONArray("logins");
 
-        final SiteLogins siteLogins = new SiteLogins(titleObj, logins);
+        final SiteLogins siteLogins = new SiteLogins(logins);
         Tabs.getInstance().getSelectedTab().setSiteLogins(siteLogins);
     }
 
     private void addSelectLoginDoorhanger(Tab tab) throws JSONException {
         final SiteLogins siteLogins = tab.getSiteLogins();
         if (siteLogins == null) {
             return;
         }
@@ -236,18 +235,16 @@ public class SiteIdentityPopup extends A
             username = mContext.getString(R.string.doorhanger_login_no_username);
         }
 
         final String message = mContext.getString(R.string.doorhanger_login_select_message).replace(FORMAT_S, username);
         config.setMessage(message);
 
         // Set options.
         final JSONObject options = new JSONObject();
-        final JSONObject titleObj = siteLogins.getTitle();
-        options.put("title", titleObj);
 
         // Add action text only if there are other logins to select.
         if (logins.length() > 1) {
 
             final JSONObject actionText = new JSONObject();
             actionText.put("type", "SELECT");
 
             final JSONObject bundle = new JSONObject();
@@ -288,33 +285,16 @@ public class SiteIdentityPopup extends A
             mIdentityUnknownContainer.setVisibility(View.GONE);
         } else {
             mIdentityKnownContainer.setVisibility(View.GONE);
             mIdentityUnknownContainer.setVisibility(View.VISIBLE);
         }
     }
 
     private void updateIdentityInformation(final SiteIdentity siteIdentity) {
-        final String host = siteIdentity.getHost();
-        mTitle.setText(host);
-
-        final String hostUrl = siteIdentity.getOrigin();
-        Favicons.getSizedFaviconForPageFromLocal(mContext, hostUrl, 32, new OnFaviconLoadedListener() {
-            @Override
-            public void onFaviconLoaded(String url, String faviconURL, Bitmap favicon) {
-                if (favicon == null) {
-                    // If there is no favicon, clear the compound drawable.
-                    mTitle.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
-                } else {
-                    mTitle.setCompoundDrawablesWithIntrinsicBounds(new BitmapDrawable(mContext.getResources(), favicon), null, null, null);
-                    mTitle.setCompoundDrawablePadding((int) mContext.getResources().getDimension(R.dimen.doorhanger_drawable_padding));
-                }
-            }
-        });
-
         mEncrypted.setVisibility(siteIdentity.getEncrypted() ? View.VISIBLE : View.GONE);
 
         mHost.setText(siteIdentity.getHost());
 
         String owner = siteIdentity.getOwner();
         if (owner == null) {
             mOwnerLabel.setVisibility(View.GONE);
             mOwner.setVisibility(View.GONE);
@@ -446,16 +426,23 @@ public class SiteIdentityPopup extends A
         }
 
         try {
             addSelectLoginDoorhanger(selectedTab);
         } catch (JSONException e) {
             Log.e(LOGTAG, "Error adding selectLogin doorhanger", e);
         }
 
+        mTitle.setText(selectedTab.getBaseDomain());
+        final Bitmap favicon = selectedTab.getFavicon();
+        if (favicon != null) {
+            mTitle.setCompoundDrawablesWithIntrinsicBounds(new BitmapDrawable(mContext.getResources(), favicon), null, null, null);
+            mTitle.setCompoundDrawablePadding((int) mContext.getResources().getDimension(R.dimen.doorhanger_drawable_padding));
+        }
+
         showDividers();
 
         super.show();
     }
 
     // Show the right dividers
     private void showDividers() {
         final int count = mContent.getChildCount();
@@ -486,16 +473,17 @@ public class SiteIdentityPopup extends A
     }
 
     @Override
     public void dismiss() {
         super.dismiss();
         removeMixedContentNotification();
         removeTrackingContentNotification();
         removeSelectLoginDoorhanger();
+        mTitle.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
         mDivider.setVisibility(View.GONE);
     }
 
     private class ContentNotificationButtonListener implements OnButtonClickListener {
         @Override
         public void onButtonClick(JSONObject response, DoorHanger doorhanger) {
             GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Reload", response.toString());
             GeckoAppShell.sendEventToGecko(e);
--- a/mobile/android/base/widget/DefaultDoorHanger.java
+++ b/mobile/android/base/widget/DefaultDoorHanger.java
@@ -104,17 +104,17 @@ public class DefaultDoorHanger extends D
             final ViewGroup group = (ViewGroup) findViewById(R.id.doorhanger_inputs);
             group.setVisibility(VISIBLE);
 
             for (int i = 0; i < inputs.length(); i++) {
                 try {
                     PromptInput input = PromptInput.getInput(inputs.getJSONObject(i));
                     mInputs.add(input);
 
-                    final int padding = mResources.getDimensionPixelSize(R.dimen.doorhanger_padding);
+                    final int padding = mResources.getDimensionPixelSize(R.dimen.doorhanger_section_padding_small);
                     View v = input.getView(getContext());
                     styleInput(input, v);
                     v.setPadding(0, 0, 0, padding);
                     group.addView(v);
                 } catch(JSONException ex) { }
             }
         }
 
@@ -193,17 +193,17 @@ public class DefaultDoorHanger extends D
         mMessage.setText(titleWithLink);
         mMessage.setMovementMethod(LinkMovementMethod.getInstance());
     }
 
     private void styleInput(PromptInput input, View view) {
         if (input instanceof PromptInput.MenulistInput) {
             styleDropdownInputs(input, view);
         }
-        view.setPadding(0, 0, 0, mResources.getDimensionPixelSize(R.dimen.doorhanger_padding));
+        view.setPadding(0, 0, 0, mResources.getDimensionPixelSize(R.dimen.doorhanger_subsection_padding));
     }
 
     private void styleDropdownInputs(PromptInput input, View view) {
         PromptInput.MenulistInput spinInput = (PromptInput.MenulistInput) input;
 
         if (spinInput.textView != null) {
             spinInput.textView.setTextColor(sSpinnerTextColor);
         }
--- a/mobile/android/base/widget/DoorHanger.java
+++ b/mobile/android/base/widget/DoorHanger.java
@@ -2,24 +2,29 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.widget;
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewStub;
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 import org.json.JSONObject;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Tabs;
 
 public abstract class DoorHanger extends LinearLayout {
 
     public static DoorHanger Get(Context context, DoorhangerConfig config) {
         final Type type = config.getType();
         switch (type) {
             case LOGIN:
                 return new LoginDoorHanger(context, config);
@@ -49,16 +54,17 @@ public abstract class DoorHanger extends
     private final int mTabId;
 
     // DoorHanger identifier.
     private final String mIdentifier;
 
     protected final Type mType;
 
     protected final ImageView mIcon;
+    protected final TextView mDoorhangerTitle;
 
     protected final Context mContext;
     protected final Resources mResources;
 
     protected int mDividerColor;
 
     protected boolean mPersistWhileVisible;
     protected int mPersistenceCount;
@@ -73,16 +79,17 @@ public abstract class DoorHanger extends
         mIdentifier = config.getId();
         mType = type;
 
         LayoutInflater.from(context).inflate(R.layout.doorhanger, this);
         setOrientation(VERTICAL);
 
         mDivider = findViewById(R.id.divider_doorhanger);
         mIcon = (ImageView) findViewById(R.id.doorhanger_icon);
+        mDoorhangerTitle = (TextView) findViewById(R.id.doorhanger_title);
 
         mNegativeButton = (Button) findViewById(R.id.doorhanger_button_negative);
         mPositiveButton = (Button) findViewById(R.id.doorhanger_button_positive);
         mOnButtonClickListener = config.getButtonClickListener();
 
         mDividerColor = mResources.getColor(R.color.divider_light);
 
         final ViewStub contentStub = (ViewStub) findViewById(R.id.content);
@@ -170,9 +177,22 @@ public abstract class DoorHanger extends
         }
 
         if (System.currentTimeMillis() <= mTimeout) {
             return false;
         }
 
         return true;
     }
+
+    public void showTitle(Bitmap favicon, String title) {
+        mDoorhangerTitle.setText(title);
+        mDoorhangerTitle.setCompoundDrawablesWithIntrinsicBounds(new BitmapDrawable(getResources(), favicon), null, null, null);
+        if (favicon != null) {
+            mDoorhangerTitle.setCompoundDrawablePadding((int) mContext.getResources().getDimension(R.dimen.doorhanger_drawable_padding));
+        }
+        mDoorhangerTitle.setVisibility(VISIBLE);
+    }
+
+    public void hideTitle() {
+        mDoorhangerTitle.setVisibility(GONE);
+    }
 }
--- a/mobile/android/base/widget/LoginDoorHanger.java
+++ b/mobile/android/base/widget/LoginDoorHanger.java
@@ -30,25 +30,23 @@ import org.json.JSONArray;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
 
 public class LoginDoorHanger extends DoorHanger {
     private static final String LOGTAG = "LoginDoorHanger";
     private enum ActionType { EDIT, SELECT }
 
-    private final TextView mTitle;
     private final TextView mMessage;
     private final TextView mLink;
     private final DoorhangerConfig.ButtonConfig mButtonConfig;
 
     public LoginDoorHanger(Context context, DoorhangerConfig config) {
         super(context, config, Type.LOGIN);
 
-        mTitle = (TextView) findViewById(R.id.doorhanger_title);
         mMessage = (TextView) findViewById(R.id.doorhanger_message);
         mLink = (TextView) findViewById(R.id.doorhanger_link);
         mIcon.setImageResource(R.drawable.icon_key);
         mIcon.setVisibility(View.VISIBLE);
 
         mButtonConfig = config.getPositiveButtonConfig();
 
         loadConfig(config);
@@ -71,40 +69,16 @@ public class LoginDoorHanger extends Doo
     protected int getContentResource() {
         return R.layout.login_doorhanger;
     }
 
     @Override
     protected void setOptions(final JSONObject options) {
         super.setOptions(options);
 
-        final JSONObject titleObj = options.optJSONObject("title");
-        if (titleObj != null) {
-
-            try {
-                final String text = titleObj.getString("text");
-                mTitle.setText(text);
-            } catch (JSONException e) {
-                Log.e(LOGTAG, "Error loading title from options JSON", e);
-            }
-
-            final String resource = titleObj.optString("resource");
-            if (resource != null) {
-                Favicons.getSizedFaviconForPageFromLocal(mContext, resource, 32, new OnFaviconLoadedListener() {
-                    @Override
-                    public void onFaviconLoaded(String url, String faviconURL, Bitmap favicon) {
-                        if (favicon != null) {
-                            mTitle.setCompoundDrawablesWithIntrinsicBounds(new BitmapDrawable(mResources, favicon), null, null, null);
-                            mTitle.setCompoundDrawablePadding((int) mResources.getDimension(R.dimen.doorhanger_drawable_padding));
-                        }
-                    }
-                });
-            }
-        }
-
         final JSONObject actionText = options.optJSONObject("actionText");
         addActionText(actionText);
     }
 
     @Override
     protected OnClickListener makeOnButtonClickListener(final int id) {
         return new Button.OnClickListener() {
             @Override
--- a/mobile/android/base/widget/SiteLogins.java
+++ b/mobile/android/base/widget/SiteLogins.java
@@ -1,22 +1,16 @@
 package org.mozilla.gecko.widget;
 
 import org.json.JSONArray;
 import org.json.JSONObject;
 
 public class SiteLogins {
-    private final JSONObject titleObj;
     private final JSONArray logins;
 
-    public SiteLogins(JSONObject titleObj, JSONArray logins) {
+    public SiteLogins(JSONArray logins) {
         this.logins = logins;
-        this.titleObj = titleObj;
     }
 
     public JSONArray getLogins() {
         return logins;
     }
-
-    public JSONObject getTitle() {
-        return titleObj;
-    }
 }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -2325,24 +2325,17 @@ var NativeWindow = {
    *        persistWhileVisible:
    *                     A boolean. If true, a visible notification will always
    *                     persist across location changes.
    *        timeout:     A time in milliseconds. The notification will not
    *                     automatically dismiss before this time.
    *
    *        checkbox:    A string to appear next to a checkbox under the notification
    *                     message. The button callback functions will be called with
-   *                     the checked state as an argument.                   
-   *
-   *        title:       An object that specifies text to display as the title, and
-   *                     optionally a resource, such as a favicon cache url that can be
-   *                     used to fetch a favicon from the FaviconCache. (This can be
-   *                     generalized to other resources if the situation arises.)
-   *                     { text: <title>,
-   *                       resource: <resource_url> }
+   *                     the checked state as an argument.
    *
    *        actionText:  An object that specifies a clickable string, a type of action,
    *                     and a bundle blob for the consumer to create a click action.
    *                     { text: <text>,
    *                       type: <type>,
    *                       bundle: <blob-object> }
    *
    * @param aCategory
--- a/mobile/android/components/LoginManagerPrompter.js
+++ b/mobile/android/components/LoginManagerPrompter.js
@@ -135,28 +135,26 @@ LoginManagerPrompter.prototype = {
         Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION").add(PROMPT_DISPLAYED);
     },
 
 
     /*
      * _showLoginNotification
      *
      * Displays a notification doorhanger.
-     * @param aTitle
-     *        Object with title and optional resource to display with the title, such as a favicon key
      * @param aBody
      *        String message to be displayed in the doorhanger
      * @param aButtons
      *        Buttons to display with the doorhanger
      * @param aUsername
      *        Username string used in creating a doorhanger action
      * @param aPassword
      *        Password string used in creating a doorhanger action
      */
-    _showLoginNotification : function (aTitle, aBody, aButtons, aUsername, aPassword) {
+    _showLoginNotification : function (aBody, aButtons, aUsername, aPassword) {
         let notifyWin = this._window.top;
         let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject;
         let browser = chromeWin.BrowserApp.getBrowserForWindow(notifyWin);
         let tabID = chromeWin.BrowserApp.getTabForBrowser(browser).id;
 
         let actionText = {
             text: aUsername,
             type: "EDIT",
@@ -169,17 +167,16 @@ LoginManagerPrompter.prototype = {
 
         // Sites like Gmail perform a funky redirect dance before you end up
         // at the post-authentication page. I don't see a good way to
         // heuristically determine when to ignore such location changes, so
         // we'll try ignoring location changes based on a time interval.
         let options = {
             persistWhileVisible: true,
             timeout: Date.now() + 10000,
-            title: aTitle,
             actionText: actionText
         }
 
         var nativeWindow = this._getNativeWindow();
         if (nativeWindow)
             nativeWindow.doorhanger.show(aBody, "password", aButtons, tabID, options, "LOGIN");
     },
 
@@ -191,19 +188,16 @@ LoginManagerPrompter.prototype = {
      * save the specified login. This allows the user to see the results of
      * their login, and only save a login which they know worked.
      *
      */
     _showSaveLoginNotification : function (aLogin) {
         let brandShortName = this._strBundle.brand.GetStringFromName("brandShortName");
         let notificationText  = this._getLocalizedString("saveLogin", [brandShortName]);
 
-        let displayHost = this._getShortDisplayHost(aLogin.hostname);
-        let title = { text: displayHost, resource: aLogin.hostname };
-
         let username = aLogin.username ? this._sanitizeUsername(aLogin.username) : "";
 
         // The callbacks in |buttons| have a closure to access the variables
         // in scope here; set one to |this._pwmgr| so we can get back to pwmgr
         // without a getService() call.
         var pwmgr = this._pwmgr;
         let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_REMEMBER_ACTION");
 
@@ -225,17 +219,17 @@ LoginManagerPrompter.prototype = {
                     }
                     pwmgr.addLogin(aLogin);
                     promptHistogram.add(PROMPT_ADD);
                 },
                 positive: true
             }
         ];
 
-        this._showLoginNotification(title, notificationText, buttons, aLogin.username, aLogin.password);
+        this._showLoginNotification(notificationText, buttons, aLogin.username, aLogin.password);
     },
 
     /*
      * promptToChangePassword
      *
      * Called when we think we detect a password change for an existing
      * login, when the form being submitted contains multiple password
      * fields.
@@ -256,19 +250,16 @@ LoginManagerPrompter.prototype = {
         var notificationText;
         if (aOldLogin.username) {
             let displayUser = this._sanitizeUsername(aOldLogin.username);
             notificationText  = this._getLocalizedString("updatePassword", [displayUser]);
         } else {
             notificationText  = this._getLocalizedString("updatePasswordNoUser");
         }
 
-        let displayHost = this._getShortDisplayHost(aOldLogin.hostname);
-        let title = { text: displayHost, resource: aOldLogin.hostname };
-
         // The callbacks in |buttons| have a closure to access the variables
         // in scope here; set one to |this._pwmgr| so we can get back to pwmgr
         // without a getService() call.
         var self = this;
         let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_UPDATE_ACTION");
 
         var buttons = [
             {
@@ -285,17 +276,17 @@ LoginManagerPrompter.prototype = {
                    self._updateLogin(aOldLogin, password);
 
                    promptHistogram.add(PROMPT_UPDATE);
                 },
                 positive: true
             }
         ];
 
-        this._showLoginNotification(title, notificationText, buttons, aOldLogin.username, aNewPassword);
+        this._showLoginNotification(notificationText, buttons, aOldLogin.username, aNewPassword);
     },
 
 
     /*
      * promptToChangePasswordWithUsernames
      *
      * Called when we detect a password change in a form submission, but we
      * don't know which existing login (username) it's for. Asks the user
@@ -457,43 +448,14 @@ LoginManagerPrompter.prototype = {
             var handler = Services.io.getProtocolHandler(scheme);
             if (port != handler.defaultPort)
                 hostname += ":" + port;
         }
 
         return hostname;
     },
 
-
-    /*
-     * _getShortDisplayHost
-     *
-     * Converts a login's hostname field (a URL) to a short string for
-     * prompting purposes. Eg, "http://foo.com" --> "foo.com", or
-     * "ftp://www.site.co.uk" --> "site.co.uk".
-     */
-    _getShortDisplayHost: function (aURIString) {
-        var displayHost;
-
-        var eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"].
-                          getService(Ci.nsIEffectiveTLDService);
-        var idnService = Cc["@mozilla.org/network/idn-service;1"].
-                         getService(Ci.nsIIDNService);
-        try {
-            var uri = Services.io.newURI(aURIString, null, null);
-            var baseDomain = eTLDService.getBaseDomain(uri);
-            displayHost = idnService.convertToDisplayIDN(baseDomain, {});
-        } catch (e) {
-            this.log("_getShortDisplayHost couldn't process " + aURIString);
-        }
-
-        if (!displayHost)
-            displayHost = aURIString;
-
-        return displayHost;
-    },
-
 }; // end of LoginManagerPrompter implementation
 
 
 var component = [LoginManagerPrompter];
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);
 
--- a/mobile/android/tests/browser/robocop/BaseTest.java
+++ b/mobile/android/tests/browser/robocop/BaseTest.java
@@ -796,17 +796,17 @@ abstract class BaseTest extends BaseRobo
 
         public void back() {
             Actions.EventExpecter pageShowExpecter = mActions.expectGeckoEvent("Content:PageShow");
 
             if (devType.equals("tablet")) {
                 Element backBtn = mDriver.findElement(getActivity(), R.id.back);
                 backBtn.click();
             } else {
-                mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+                mSolo.goBack();
             }
 
             pageShowExpecter.blockForEvent();
             pageShowExpecter.unregisterListener();
         }
 
         public void forward() {
             Actions.EventExpecter pageShowExpecter = mActions.expectGeckoEvent("Content:PageShow");
@@ -864,17 +864,17 @@ abstract class BaseTest extends BaseRobo
             }
             ensureMenuClosed();
         }
 
         // On some devices, the menu may not be dismissed after clicking on an
         // item. Close it here.
         private void ensureMenuClosed() {
             if (mSolo.searchText("^New Tab$")) {
-                mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+                mSolo.goBack();
             }
          }
     }
 
     /**
      * Gets the string representation of a stack trace.
      *
      * @param t Throwable to get stack trace for
--- a/mobile/android/tests/browser/robocop/ContentContextMenuTest.java
+++ b/mobile/android/tests/browser/robocop/ContentContextMenuTest.java
@@ -93,17 +93,17 @@ abstract class ContentContextMenuTest ex
         waitForText(pageTitle); // Even if this fails, it won't assert
         if (!mSolo.searchText(shareOption)) {
             openWebContentContextMenu(shareOption); // Open the context menu if it is not already
         }
         mSolo.clickOnText(shareOption);
         mAsserter.ok(waitForText(shareOption), "Checking that the share pop-up is displayed", "The pop-up has been displayed");
 
         // Close the Share Link option menu and wait for the page to be focused again
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
         waitForText(pageTitle);
     }
 
     protected void verifyBookmarkLinkOption(String bookmarkOption, String link) {
         if (!mSolo.searchText(bookmarkOption)) {
             openWebContentContextMenu(bookmarkOption); // Open the context menu if it is not already
         }
         mSolo.clickOnText(bookmarkOption);
--- a/mobile/android/tests/browser/robocop/testAboutHomePageNavigation.java
+++ b/mobile/android/tests/browser/robocop/testAboutHomePageNavigation.java
@@ -109,11 +109,11 @@ public class testAboutHomePageNavigation
         mSolo.typeText(0, "woot");
         mAsserter.is(pager.getCurrentItem(), 0, "Searching switched to all pages tab");
         mSolo.scrollToSide(Solo.LEFT);
         mAsserter.is(pager.getCurrentItem(), 0, "Dragging left is not allowed when searching");
 
         mSolo.scrollToSide(Solo.RIGHT);
         mAsserter.is(pager.getCurrentItem(), 0, "Dragging right is not allowed when searching");
 
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
     */
 }
--- a/mobile/android/tests/browser/robocop/testAddSearchEngine.java
+++ b/mobile/android/tests/browser/robocop/testAddSearchEngine.java
@@ -156,13 +156,13 @@ public class testAddSearchEngine extends
                     return false;
                 }
 
                 return (searchResultList.getAdapter().getCount() + searchEngineBar.getAdapter().getCount() == expectedCount);
             }
         }, MAX_WAIT_TEST_MS);
 
         // Exit about:home
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
         waitForText(mStringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
         mAsserter.ok(correctNumSearchEnginesDisplayed, expectedCount + " Search Engines should be displayed" , "The correct number of Search Engines has been displayed");
     }
 }
--- a/mobile/android/tests/browser/robocop/testAddonManager.java
+++ b/mobile/android/tests/browser/robocop/testAddonManager.java
@@ -38,17 +38,17 @@ public class testAddonManager extends Pi
 
         tabEventExpecter.unregisterListener();
         contentEventExpecter.unregisterListener();
 
         // Verify the url
         verifyUrlBarTitle(aboutAddonsURL);
 
         // Close the Add-on Manager
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
 
         // Load the about:addons page and verify it was loaded
         loadAndPaint(aboutAddonsURL);
         verifyUrlBarTitle(aboutAddonsURL);
 
         // Setup wait for tab to spawn and load
         tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
         contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
--- a/mobile/android/tests/browser/robocop/testBookmarksPanel.java
+++ b/mobile/android/tests/browser/robocop/testBookmarksPanel.java
@@ -105,17 +105,17 @@ public class testBookmarksPanel extends 
         openBookmarkContextMenu(shareableURL);
         for (String contextMenuOption : mStringHelper.BOOKMARK_CONTEXT_MENU_ITEMS) {
             mAsserter.ok(mSolo.searchText(contextMenuOption),
                     "Checking that the context menu option is present",
                     contextMenuOption + " is present");
         }
 
         // Close the menu.
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
 
         openBookmarkContextMenu(nonShareableURL);
         for (String contextMenuOption : mStringHelper.BOOKMARK_CONTEXT_MENU_ITEMS) {
             // This link is not shareable: skip the "Share" option.
             if ("Share".equals(contextMenuOption)) {
                 continue;
             }
 
@@ -126,17 +126,17 @@ public class testBookmarksPanel extends 
 
         // The use of Solo.searchText is potentially fragile as It will only
         // scroll the most recently drawn view. Works fine for now though.
         mAsserter.ok(!mSolo.searchText("Share"),
                 "Checking that the Share option is not present",
                 "Share option is not present");
 
         // Close the menu.
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
     }
 
    /**
     * @param bookmarkUrl URL of the bookmark to edit
     * @param values String array with the new values for all fields
     */
     private void editBookmark(String bookmarkUrl, String[] values) {
         openBookmarkContextMenu(bookmarkUrl);
--- a/mobile/android/tests/browser/robocop/testClearPrivateData.java
+++ b/mobile/android/tests/browser/robocop/testClearPrivateData.java
@@ -1,15 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tests;
 
-import org.mozilla.gecko.Actions;
 import org.mozilla.gecko.R;
 
 import com.jayway.android.robotium.solo.Condition;
 import android.view.View;
 
 /**
  * This patch tests the clear private data options:
  * - clear history option by: adding and checking that clear private
@@ -85,20 +84,20 @@ public class testClearPrivateData extend
         loadCheckDismiss(passwordStrings[2], loginUrl, passwordStrings[0]);
         checkDevice(title, getAbsoluteUrl(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL));
     }
 
     // clear private data and verify the device type because for phone there is an extra back action to exit the settings menu
     public void checkDevice(final String title, final String url) {
         clearPrivateData();
         if (mDevice.type.equals("phone")) {
-            mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+            mSolo.goBack();
             mAsserter.ok(waitForText(mStringHelper.PRIVACY_SECTION_LABEL), "waiting to perform one back", "one back");
         }
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
         verifyUrlBarTitle(url);
     }
 
     // Load a URL, verify that the doorhanger appears and dismiss it
     public void loadCheckDismiss(String option, String url, String message) {
         loadUrlAndWait(url);
         waitForText(message);
         mAsserter.is(mSolo.searchText(message), true, "Doorhanger:" + message + " has been displayed");
--- a/mobile/android/tests/browser/robocop/testImportFromAndroid.java
+++ b/mobile/android/tests/browser/robocop/testImportFromAndroid.java
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tests;
 
 import java.util.ArrayList;
 
-import org.mozilla.gecko.Actions;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.GeckoProfile;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.Browser;
@@ -50,17 +49,17 @@ public class testImportFromAndroid exten
         // Import the bookmarks and history
         importDataFromAndroid();
 
         // Get the Android history and the Firefox bookmarks and history lists
         firefoxHistory = mDatabaseHelper.getBrowserDBUrls(DatabaseHelper.BrowserDataType.HISTORY);
         firefoxBookmarks = mDatabaseHelper.getBrowserDBUrls(DatabaseHelper.BrowserDataType.BOOKMARKS);
 
         /**
-         * Add a delay to make sure the imported items are added to the array lists 
+         * Add a delay to make sure the imported items are added to the array lists
          * if there are a lot of history items in the Android Browser database
          */
         boolean success = waitForCondition(new Condition() {
             @Override
             public boolean isSatisfied() {
                 if (androidData.size() <= firefoxHistory.size()) {
                     return true;
                 } else {
@@ -152,20 +151,20 @@ public class testImportFromAndroid exten
         }, MAX_WAIT_TIMEOUT);
 
         mAsserter.ok(importComplete, "Waiting for import to finish and the pop-up to be dismissed", "Import was completed and the pop-up was dismissed");
 
         // Import has finished. Waiting to get back to the Settings Menu and looking for the Import&Export subsection
         if ("phone".equals(mDevice.type)) {
             // Phones don't have headers like tablets, so we need to pop up one more level.
             waitForText(mStringHelper.IMPORT_FROM_ANDROID_LABEL);
-            mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+            mSolo.goBack();
         }
         waitForText(mStringHelper.PRIVACY_SECTION_LABEL); // Settings is a header for the settings menu page. Waiting for Privacy ensures we are back in the top Settings view
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK); // Exit Settings
+        mSolo.goBack(); // Exit Settings
         // Make sure the settings menu has been closed.
         mAsserter.ok(mSolo.waitForText(mStringHelper.TITLE_PLACE_HOLDER), "Waiting for search bar", "Search bar found");
 
     }
 
     public ArrayList<String> getAndroidUrls(String data) {
         // Return bookmarks or history depending on what the user asks for
         ArrayList<String> urls = new ArrayList<String>();
--- a/mobile/android/tests/browser/robocop/testInputUrlBar.java
+++ b/mobile/android/tests/browser/robocop/testInputUrlBar.java
@@ -83,20 +83,20 @@ public final class testInputUrlBar exten
             public void run() {
                 editText.selectAll();
             }
         });
         mActions.sendKeys("uv");
         assertUrlBarText("uv");
 
         // Dismiss the VKB
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
 
         // Dismiss editing mode
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
 
         waitForText(mStringHelper.TITLE_PLACE_HOLDER);
 
         // URL bar should have forgotten about "uv" text.
         startEditingMode();
         assertUrlBarText(mStringHelper.ABOUT_HOME_URL);
 
         int width = mDriver.getGeckoWidth() / 2;
--- a/mobile/android/tests/browser/robocop/testMasterPassword.java
+++ b/mobile/android/tests/browser/robocop/testMasterPassword.java
@@ -72,20 +72,20 @@ public class testMasterPassword extends 
         mAsserter.ok(mSolo.waitForText("^Use master password$"), "Checking if Use master password is present", "Use master password is present");
         mSolo.clickOnText("^Use master password$");
         mAsserter.ok(mSolo.waitForText("Remove Master Password"), "Checking if the password is enabled", "The password is enabled");
         clickOnButton("Cancel"); // Go back to settings menu
 
         if ("phone".equals(mDevice.type)) {
             // Phones don't have headers like tablets, so we need to pop up one more level.
             waitForText("Use master password");
-            mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+            mSolo.goBack();
         }
         waitForText(mStringHelper.SETTINGS_LABEL);
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);// Close the Settings Menu
+        mSolo.goBack();// Close the Settings Menu
     }
 
     public void disableMasterPassword(String password, String badPassword) {
 
         // Look for the 'Settings' menu if this device/OS uses it
         selectSettingsItem(mStringHelper.PRIVACY_SECTION_LABEL, mStringHelper.MASTER_PASSWORD_LABEL);
         waitForText("^Remove Master Password$");
 
@@ -166,20 +166,20 @@ public class testMasterPassword extends 
         waitForText("^Use master password$");
         mSolo.clickOnText("^Use master password$");
         mAsserter.ok(mSolo.searchText("^Remove Master Password$"), "Checking if the master password was disabled by clearing private data", "The master password is not disabled by clearing private data");
         clickOnButton("Cancel"); // Close the Master Password menu
 
         if ("phone".equals(mDevice.type)) {
             // Phones don't have headers like tablets, so we need to pop up one more level.
             waitForText("Use master password");
-            mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+            mSolo.goBack();
         }
         waitForText(mStringHelper.SETTINGS_LABEL);
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);// Close the Settings Menu
+        mSolo.goBack();// Close the Settings Menu
         // Make sure the settings menu has been closed.
         mAsserter.ok(mSolo.waitForText("Browser Blank Page 01"), "Waiting for blank browser page after exiting settings", "Blank browser page present");
     }
 
     public void verifyLoginPage(String password, String badPassword) {
         String LOGIN_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_LOGIN_01_URL);
         String option [] = {"Save", "Don't save"};
 
--- a/mobile/android/tests/browser/robocop/testPromptGridInput.java
+++ b/mobile/android/tests/browser/robocop/testPromptGridInput.java
@@ -1,16 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tests;
 
-import org.mozilla.gecko.Actions;
-
 public class testPromptGridInput extends BaseTest {
     protected int index = 1;
     public void testPromptGridInput() {
         blockForGeckoReady();
 
         test(1);
 
         testGridItem("Icon 1");
@@ -24,17 +22,17 @@ public class testPromptGridInput extends
         testGridItem("Icon 9");
         testGridItem("Icon 10");
         testGridItem("Icon 11");
 
         mSolo.clickOnText("Icon 11");
         mSolo.clickOnText("OK");
 
         mAsserter.ok(waitForText("PASS"), "test passed", "PASS");
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
     }
 
     public void testGridItem(String title) {
         // Force the list to scroll if necessary
         mSolo.waitForText(title, 1, 500, true);
         mAsserter.ok(waitForText(title), "Found grid item", title);
     }
 
--- a/mobile/android/tests/browser/robocop/testSettingsMenuItems.java
+++ b/mobile/android/tests/browser/robocop/testSettingsMenuItems.java
@@ -2,17 +2,16 @@ package org.mozilla.gecko.tests;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
-import org.mozilla.gecko.Actions;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.util.HardwareUtils;
 
 /** This patch tests the Sections present in the Settings Menu and the
  *  default values for them
  */
 public class testSettingsMenuItems extends PixelTest {
     // Customize menu items.
@@ -139,17 +138,17 @@ public class testSettingsMenuItems exten
         // Set special handling for Settings items that are conditionally built.
         updateConditionalSettings(settingsMenuItems);
 
         selectMenuItem(mStringHelper.SETTINGS_LABEL);
         mAsserter.ok(mSolo.waitForText(mStringHelper.SETTINGS_LABEL),
                 "The Settings menu did not load", mStringHelper.SETTINGS_LABEL);
 
         // Dismiss the Settings screen and verify that the view is returned to about:home page
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
 
         // Waiting for page title to appear to be sure that is fully loaded before opening the menu
         mAsserter.ok(mSolo.waitForText(mStringHelper.TITLE_PLACE_HOLDER), "about:home did not load",
                 mStringHelper.TITLE_PLACE_HOLDER);
         verifyUrl(mStringHelper.ABOUT_HOME_URL);
 
         selectMenuItem(mStringHelper.SETTINGS_LABEL);
         mAsserter.ok(mSolo.waitForText(mStringHelper.SETTINGS_LABEL),
@@ -276,26 +275,26 @@ public class testSettingsMenuItems exten
                                      "The " + itemChoice + " choice is present in section " + section);
                     }
 
                     // Leave submenu after checking.
                     if (waitForText("^Cancel$")) {
                         mSolo.clickOnText("^Cancel$");
                     } else {
                         // Some submenus aren't dialogs, but are nested screens; exit using "back".
-                        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+                        mSolo.goBack();
                     }
                 }
             }
 
             // Navigate back if on a phone. Tablets shouldn't do this because they use headers and fragments.
             if (mDevice.type.equals("phone")) {
                 int menuDepth = menuPath.length;
                 while (menuDepth > 0) {
-                    mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+                    mSolo.goBack();
                     menuDepth--;
                     // Sleep so subsequent back actions aren't lost.
                     mSolo.sleep(150);
                 }
             }
         }
     }
 }
--- a/mobile/android/tests/browser/robocop/testShareLink.java
+++ b/mobile/android/tests/browser/robocop/testShareLink.java
@@ -51,17 +51,17 @@ public class testShareLink extends About
         shareOptions = getShareOptions();
         ArrayList<String> displayedOptions = getShareOptionsList();
         for (String option:shareOptions) {
              // Verify if the option is present in the list of displayed share options
              mAsserter.ok(optionDisplayed(option, displayedOptions), "Share option found", option);
         }
 
         // Test share from the urlbar context menu
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK); // Close the share menu
+        mSolo.goBack(); // Close the share menu
         mSolo.clickLongOnText(urlTitle);
         verifySharePopup(shareOptions,"urlbar");
 
         // The link has a 60px height, so let's try to hit the middle
         float top = mDriver.getGeckoTop() + 30 * mDevice.density;
         float left = mDriver.getGeckoLeft() + mDriver.getGeckoWidth() / 2;
         mSolo.clickLongOnScreen(left, top);
         verifySharePopup("Share Link",shareOptions,"Link");
@@ -140,17 +140,17 @@ public class testShareLink extends About
         waitForText(shareItemText);
         mSolo.clickOnText(shareItemText);
         waitForText("Share via");
         ArrayList<String> displayedOptions = getSharePopupOption();
         for (String option:shareOptions) {
              // Verify if the option is present in the list of displayed share options
              mAsserter.ok(optionDisplayed(option, displayedOptions), "Share option for " + openedFrom + (openedFrom.equals("urlbar") ? "" : " item") + " found", option);
         }
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
         /**
          * Adding a wait for the page title to make sure the Awesomebar will be dismissed
          * Because of Bug 712370 the Awesomescreen will be dismissed when the Share Menu is closed
          * so there is no need for handling this different depending on where the share menu was invoked from
          * TODO: Look more into why the delay is needed here now and it was working before
          */
         waitForText(urlTitle);
     }
--- a/mobile/android/tests/browser/robocop/testSystemPages.java
+++ b/mobile/android/tests/browser/robocop/testSystemPages.java
@@ -30,17 +30,17 @@ public class testSystemPages extends Pix
         /* Load system pages from url and check that the pages are loaded in the same tab */
         checkUrl(urls);
 
         /* Verify that the search field is not in the focus by pressing back. That will load the previous
            about: page if there is no the keyboard to dismiss, meaning that the search field was not in focus */
         loadAndPaint(mStringHelper.ABOUT_ABOUT_URL);
 
         // Press back to verify if the keyboard is dismissed or the previous about: page loads
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+        mSolo.goBack();
         // may not get a paint on Back...pause briefly to make sure it completes
         mSolo.sleep(AFTER_BACK_SLEEP_MS);
 
         // We will use the "about:" page as our reference page.
         loadAndPaint(mStringHelper.ABOUT_SCHEME);
         verifyUrl(mStringHelper.ABOUT_SCHEME); // Verify that the previous about: page is loaded, meaning no keyboard was present
 
         // Load system pages by navigating through the UI.
--- a/mobile/android/tests/browser/robocop/testTitleBar.java
+++ b/mobile/android/tests/browser/robocop/testTitleBar.java
@@ -1,11 +1,10 @@
 package org.mozilla.gecko.tests;
 
-import org.mozilla.gecko.Actions;
 import org.mozilla.gecko.util.HardwareUtils;
 
 /**
  * This patch tests the option that shows the full URL and title in the URL Bar
  */
 
 public class testTitleBar extends PixelTest {
     public void testTitleBar() {
@@ -40,19 +39,19 @@ public class testTitleBar extends PixelT
 
     // Entering settings, changing the options: show title/page address option and verifing the device type because for phone there is an extra back action to exit the settings menu
     public void selectOption(String option) {
         selectSettingsItem(mStringHelper.DISPLAY_SECTION_LABEL, mStringHelper.TITLE_BAR_LABEL);
         mAsserter.ok(waitForText(mStringHelper.SHOW_PAGE_TITLE_LABEL), "Waiting for the pop-up to open", "Pop up with the options was openend");
         mSolo.clickOnText(option);
         mAsserter.ok(waitForText(mStringHelper.CHARACTER_ENCODING_LABEL), "Waiting to press the option", "The pop-up is dismissed once clicked");
         if (mDevice.type.equals("phone")) {
-            mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+            mSolo.goBack();
             mAsserter.ok(waitForText(mStringHelper.CUSTOMIZE_SECTION_LABEL), "Waiting to perform one back", "One back performed");
-            mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+            mSolo.goBack();
             mAsserter.ok(waitForText(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL), "Waiting to exit settings", "Exit settings done");
         }
         else {
-            mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+            mSolo.goBack();
             mAsserter.ok(waitForText(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL), "Waiting to exit settings", "Exit settings done");
         }
     }
 }
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -6875,16 +6875,23 @@
     "n_buckets": "100",
     "description": "The mean number of pinned tabs (app tabs) in all windows for a session for devtools users."
   },
   "BROWSER_IS_USER_DEFAULT": {
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "The result of the startup default desktop browser check."
   },
+  "WIN_10_DEFAULT_BROWSER_AB_TEST": {
+    "alert_emails": ["jwein@mozilla.com"],
+    "expires_in_version": "45",
+    "kind": "enumerated",
+    "n_values": 4,
+    "description": "A/B test of different default browser dialogs on Windows 10 (0=openas-notdefault, 1=openas-default, 2=modernsettings-notdefault, 3=modernsettings-default)."
+  },
   "BROWSER_IS_ASSIST_DEFAULT": {
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "The result of the default browser check for assist intent."
   },
   "MIXED_CONTENT_PAGE_LOAD": {
     "expires_in_version": "never",
     "kind": "enumerated",
--- a/toolkit/devtools/server/actors/device.js
+++ b/toolkit/devtools/server/actors/device.js
@@ -4,152 +4,32 @@
 
 const {Cc, Ci, Cu, CC} = require("chrome");
 const Services = require("Services");
 const protocol = require("devtools/server/protocol");
 const {method, RetVal} = protocol;
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const {LongStringActor} = require("devtools/server/actors/string");
 const {DebuggerServer} = require("devtools/server/main");
+const {getSystemInfo, getSetting} = require("devtools/toolkit/shared/system");
 
 Cu.import("resource://gre/modules/PermissionsTable.jsm")
 
-const APP_MAP = {
-  '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'firefox',
-  '{3550f703-e582-4d05-9a08-453d09bdfdc6}': 'thunderbird',
-  '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}': 'seamonkey',
-  '{718e30fb-e89b-41dd-9da7-e25a45638b28}': 'sunbird',
-  '{3c2e2abc-06d4-11e1-ac3b-374f68613e61}': 'b2g',
-  '{aa3c5121-dab2-40e2-81ca-7ea25febc110}': 'mobile/android',
-  '{a23983c0-fd0e-11dc-95ff-0800200c9a66}': 'mobile/xul'
-}
-
 let DeviceActor = exports.DeviceActor = protocol.ActorClass({
   typeName: "device",
 
   _desc: null,
 
-  _getAppIniString : function(section, key) {
-    let inifile = Services.dirsvc.get("GreD", Ci.nsIFile);
-    inifile.append("application.ini");
-
-    if (!inifile.exists()) {
-      inifile = Services.dirsvc.get("CurProcD", Ci.nsIFile);
-      inifile.append("application.ini");
-    }
-
-    if (!inifile.exists()) {
-      return undefined;
-    }
-
-    let iniParser = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].getService(Ci.nsIINIParserFactory).createINIParser(inifile);
-    try {
-      return iniParser.getString(section, key);
-    } catch (e) {
-      return undefined;
-    }
-  },
-
-  _getSetting: function(name) {
-    let deferred = promise.defer();
-
-    if ("@mozilla.org/settingsService;1" in Cc) {
-      let settingsService = Cc["@mozilla.org/settingsService;1"].getService(Ci.nsISettingsService);
-      let req = settingsService.createLock().get(name, {
-        handle: (name, value) => deferred.resolve(value),
-        handleError: (error) => deferred.reject(error),
-      });
-    } else {
-      deferred.reject(new Error("No settings service"));
-    }
-    return deferred.promise;
-  },
-
   getDescription: method(function() {
-    // Most of this code is inspired from Nightly Tester Tools:
-    // https://wiki.mozilla.org/Auto-tools/Projects/NightlyTesterTools
-
-    let appInfo = Services.appinfo;
-    let win = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
-
-    desc = {
-      appid: appInfo.ID,
-      apptype: APP_MAP[appInfo.ID],
-      vendor: appInfo.vendor,
-      name: appInfo.name,
-      version: appInfo.version,
-      appbuildid: appInfo.appBuildID,
-      platformbuildid: appInfo.platformBuildID,
-      platformversion: appInfo.platformVersion,
-      geckobuildid: appInfo.platformBuildID,
-      geckoversion: appInfo.platformVersion,
-      changeset: this._getAppIniString("App", "SourceStamp"),
-      locale: Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).getSelectedLocale("global"),
-      os: null,
-      hardware: "unknown",
-      processor: appInfo.XPCOMABI.split("-")[0],
-      compiler: appInfo.XPCOMABI.split("-")[1],
-      brandName: null,
-      channel: null,
-      profile: null,
-    };
-    if (win) {
-      let utils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
-      desc.dpi = utils.displayDPI;
-      desc.useragent = win.navigator.userAgent;
-      desc.width = win.screen.width;
-      desc.height = win.screen.height;
-    }
-
-    // Profile
-    let profd = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
-    let profservice = Cc["@mozilla.org/toolkit/profile-service;1"].getService(Ci.nsIToolkitProfileService);
-    var profiles = profservice.profiles;
-    while (profiles.hasMoreElements()) {
-      let profile = profiles.getNext().QueryInterface(Ci.nsIToolkitProfile);
-      if (profile.rootDir.path == profd.path) {
-        desc.profile = profile.name;
-        break;
-      }
-    }
-
-    if (!desc.profile) {
-      desc.profile = profd.leafName;
-    }
-
-    // Channel
-    try {
-      desc.channel = Services.prefs.getCharPref('app.update.channel');
-    } catch(e) {}
-
-    if (desc.apptype == "b2g") {
-      // B2G specific
-      desc.os = "B2G";
-
-      return this._getSetting('deviceinfo.hardware')
-      .then(value => desc.hardware = value)
-      .then(() => this._getSetting('deviceinfo.os'))
-      .then(value => desc.version = value)
-      .then(() => desc);
-    }
-
-    // Not B2G
-    desc.os = appInfo.OS;
-    let bundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
-    if (bundle) {
-      desc.brandName = bundle.GetStringFromName("brandFullName");
-    }
-
-    return desc;
-
+    return getSystemInfo();
   }, {request: {},response: { value: RetVal("json")}}),
 
   getWallpaper: method(function() {
     let deferred = promise.defer();
-    this._getSetting("wallpaper.image").then((blob) => {
+    getSetting("wallpaper.image").then((blob) => {
       let FileReader = CC("@mozilla.org/files/filereader;1");
       let reader = new FileReader();
       let conn = this.conn;
       reader.addEventListener("load", function() {
         let str = new LongStringActor(conn, reader.result);
         deferred.resolve(str);
       });
       reader.addEventListener("error", function() {
--- a/toolkit/devtools/server/actors/object.js
+++ b/toolkit/devtools/server/actors/object.js
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Cu, Ci } = require("chrome");
 const { GeneratedLocation } = require("devtools/server/actors/common");
 const { DebuggerServer } = require("devtools/server/main")
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
-const { dbg_assert } = DevToolsUtils;
+const { dbg_assert, dumpn } = DevToolsUtils;
 const PromiseDebugging = require("PromiseDebugging");
 
 const TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
       "Uint32Array", "Int8Array", "Int16Array", "Int32Array", "Float32Array",
       "Float64Array"];
 
 // Number of items to preview in objects, arrays, maps, sets, lists,
 // collections, etc.
--- a/toolkit/devtools/server/actors/storage.js