Merge fx-team to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 27 Jun 2014 16:51:49 -0700
changeset 191188 f127b565e53febcbc7b7100b63280a62f9d2720b
parent 191167 c90b38c47a1d13fe99dbf7eb5a4e42359301e49e (current diff)
parent 191187 f2e8982acbf03ff2bba72bdd804913911f91b3fb (diff)
child 191210 08e9e0b78f811a2007b08cb6c41f9810ad482585
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmerge
milestone33.0a1
Merge fx-team to m-c a=merge
--- a/browser/components/customizableui/test/browser_938980_navbar_collapsed.js
+++ b/browser/components/customizableui/test/browser_938980_navbar_collapsed.js
@@ -19,34 +19,34 @@ add_task(function() {
   is(navbar.collapsed, false, "Customization reset should restore visibility to the navbar");
 });
 
 // Customization reset should restore collapsed-state to default-collapsed toolbars.
 add_task(function() {
   ok(CustomizableUI.inDefaultState, "Everything should be in its default state");
 
   is(bookmarksToolbar.collapsed, true, "Test should start with bookmarks toolbar collapsed");
-  is(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should have height=0");
-  isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
+  ok(bookmarksToolbar.collapsed, "bookmarksToolbar should be collapsed");
+  ok(!tabsToolbar.collapsed, "TabsToolbar should not be collapsed");
   is(navbar.collapsed, false, "The nav-bar should be shown by default");
 
   setToolbarVisibility(bookmarksToolbar, true);
   setToolbarVisibility(navbar, false);
-  isnot(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should be visible now");
-  ok(navbar.getBoundingClientRect().height <= 1, "navbar should have height=0 or 1 (due to border)");
+  ok(!bookmarksToolbar.collapsed, "bookmarksToolbar should be visible now");
+  ok(navbar.collapsed, "navbar should be collapsed");
   is(CustomizableUI.inDefaultState, false, "Should no longer be in default state");
 
   yield startCustomizing();
   gCustomizeMode.reset();
   yield waitForCondition(function() !gCustomizeMode.resetting);
   yield endCustomizing();
 
   is(bookmarksToolbar.collapsed, true, "Customization reset should restore collapsed-state to the bookmarks toolbar");
-  isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
-  is(bookmarksToolbar.getBoundingClientRect().height, 0, "The bookmarksToolbar should have height=0 after reset");
+  ok(!tabsToolbar.collapsed, "TabsToolbar should not be collapsed");
+  ok(bookmarksToolbar.collapsed, "The bookmarksToolbar should be collapsed after reset");
   ok(CustomizableUI.inDefaultState, "Everything should be back to default state");
 });
 
 // Check that the menubar will be collapsed by resetting, if the platform supports it.
 add_task(function() {
   let menubar = document.getElementById("toolbar-menubar");
   const canMenubarCollapse = CustomizableUI.isToolbarDefaultCollapsed(menubar.id);
   if (!canMenubarCollapse) {
@@ -69,35 +69,35 @@ add_task(function() {
 
   is(menubar.getAttribute("autohide"), "true", "The menubar should have autohide=true after reset");
   is(menubar.getBoundingClientRect().height, 0, "The menubar should have height=0 after reset");
 });
 
 // Customization reset should restore collapsed-state to default-collapsed toolbars.
 add_task(function() {
   ok(CustomizableUI.inDefaultState, "Everything should be in its default state");
-  is(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should have height=0");
-  isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
+  ok(bookmarksToolbar.collapsed, "bookmarksToolbar should be collapsed");
+  ok(!tabsToolbar.collapsed, "TabsToolbar should not be collapsed");
 
   setToolbarVisibility(bookmarksToolbar, true);
-  isnot(bookmarksToolbar.getBoundingClientRect().height, 0, "bookmarksToolbar should be visible now");
+  ok(!bookmarksToolbar.collapsed, "bookmarksToolbar should be visible now");
   is(CustomizableUI.inDefaultState, false, "Should no longer be in default state");
 
   yield startCustomizing();
 
-  isnot(bookmarksToolbar.getBoundingClientRect().height, 0, "The bookmarksToolbar should be visible before reset");
-  isnot(navbar.getBoundingClientRect().height, 0, "The navbar should be visible before reset");
-  isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
+  ok(!bookmarksToolbar.collapsed, "The bookmarksToolbar should be visible before reset");
+  ok(!navbar.collapsed, "The navbar should be visible before reset");
+  ok(!tabsToolbar.collapsed, "TabsToolbar should not be collapsed");
 
   gCustomizeMode.reset();
   yield waitForCondition(function() !gCustomizeMode.resetting);
 
-  is(bookmarksToolbar.getBoundingClientRect().height, 0, "The bookmarksToolbar should have height=0 after reset");
-  isnot(tabsToolbar.getBoundingClientRect().height, 0, "TabsToolbar should have non-zero height");
-  isnot(navbar.getBoundingClientRect().height, 0, "The navbar should still be visible after reset");
+  ok(bookmarksToolbar.collapsed, "The bookmarksToolbar should be collapsed after reset");
+  ok(!tabsToolbar.collapsed, "TabsToolbar should not be collapsed");
+  ok(!navbar.collapsed, "The navbar should still be visible after reset");
   ok(CustomizableUI.inDefaultState, "Everything should be back to default state");
   yield endCustomizing();
 });
 
 // Check that the menubar will be collapsed by resetting, if the platform supports it.
 add_task(function() {
   let menubar = document.getElementById("toolbar-menubar");
   const canMenubarCollapse = CustomizableUI.isToolbarDefaultCollapsed(menubar.id);
--- a/browser/devtools/framework/gDevTools.jsm
+++ b/browser/devtools/framework/gDevTools.jsm
@@ -272,19 +272,20 @@ DevTools.prototype = {
       this._toolboxes.set(target, toolbox);
 
       toolbox.once("destroyed", () => {
         this._toolboxes.delete(target);
         this.emit("toolbox-destroyed", target);
       });
 
       // If we were asked for a specific tool then we need to wait for the
-      // tool to be ready, otherwise we can just wait for toolbox open
+      // tool to be ready and selected, otherwise we can just wait for the
+      // toolbox open promise.
       if (toolId != null) {
-        toolbox.once(toolId + "-ready", (event, panel) => {
+        toolbox.once(toolId + "-selected", (event, panel) => {
           this.emit("toolbox-ready", toolbox);
           deferred.resolve(toolbox);
         });
         toolbox.open();
       }
       else {
         toolbox.open().then(() => {
           deferred.resolve(toolbox);
--- a/browser/devtools/framework/test/browser_devtools_api.js
+++ b/browser/devtools/framework/test/browser_devtools_api.js
@@ -34,23 +34,17 @@ function runTests(aTab) {
     "The tool is not registered");
 
   gDevTools.registerTool(toolDefinition);
   is(gDevTools.getToolDefinitionMap().has(toolId), true,
     "The tool is registered");
 
   let target = TargetFactory.forTab(gBrowser.selectedTab);
 
-  gDevTools.showToolbox(target, toolId).then(function(toolbox) {
-    // Wait for the test tool to be visible and selected.
-    let { promise: testToolShown, resolve } = promise.defer();
-    toolbox.once("test-tool-selected", resolve);
-
-    return testToolShown.then(() => toolbox);
-  }).then(function (toolbox) {
+  gDevTools.showToolbox(target, toolId).then(function (toolbox) {
     is(toolbox.target, target, "toolbox target is correct");
     is(toolbox._host.hostTab, gBrowser.selectedTab, "toolbox host is correct");
     continueTests(toolbox);
   });
 }
 
 function continueTests(toolbox, panel) {
   ok(toolbox.getCurrentPanel(), "panel value is correct");
--- a/browser/devtools/framework/test/browser_new_activation_workflow.js
+++ b/browser/devtools/framework/test/browser_new_activation_workflow.js
@@ -48,20 +48,18 @@ function selectAndCheckById(id) {
 }
 
 function testToggle() {
   toolbox.once("destroyed", () => {
     // Cannot reuse a target after it's destroyed.
     target = TargetFactory.forTab(gBrowser.selectedTab);
     gDevTools.showToolbox(target, "styleeditor").then(function(aToolbox) {
       toolbox = aToolbox;
-      aToolbox.once("styleeditor-selected", () => {
-        is(toolbox.currentToolId, "styleeditor", "The style editor is selected");
-        finishUp();
-      });
+      is(toolbox.currentToolId, "styleeditor", "The style editor is selected");
+      finishUp();
     });
   });
 
   toolbox.destroy();
 }
 
 function finishUp() {
   toolbox.destroy().then(function() {
--- a/browser/devtools/framework/test/browser_toolbox_tabsswitch_shortcuts.js
+++ b/browser/devtools/framework/test/browser_toolbox_tabsswitch_shortcuts.js
@@ -19,17 +19,19 @@ function test() {
                   .map(def => def.id);
       gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.BOTTOM)
                .then(testShortcuts);
     });
   });
 }
 
 function testShortcuts(aToolbox, aIndex) {
-  if (aIndex == toolIDs.length) {
+  if (aIndex === undefined) {
+    aIndex = 1;
+  } else if (aIndex == toolIDs.length) {
     aIndex = 0;
     if (secondTime) {
       secondTime = false;
       reverse = true;
       aIndex = toolIDs.length - 2;
     }
     else {
       secondTime = true;
@@ -50,30 +52,24 @@ function testShortcuts(aToolbox, aIndex)
                      .getAttribute("key");
     prevKey = toolbox.doc.getElementById("toolbox-previous-tool-key")
                      .getAttribute("key");
   }
   info("Toolbox fired a `ready` event");
 
   toolbox.once("select", onSelect);
 
-  if (aIndex != null) {
-    // This if block is to allow the call of onSelect without shortcut press for
-    // the first time. That happens because on opening of toolbox, one tool gets
-    // selected atleast.
-
-    let key = (reverse ? prevKey: nextKey);
-    let modifiers = {
-      accelKey: true
-    };
-    idIndex = aIndex;
-    info("Testing shortcut to switch to tool " + aIndex + ":" + toolIDs[aIndex] +
-         " using key " + key);
-    EventUtils.synthesizeKey(key, modifiers, toolbox.doc.defaultView);
-  }
+  let key = (reverse ? prevKey: nextKey);
+  let modifiers = {
+    accelKey: true
+  };
+  idIndex = aIndex;
+  info("Testing shortcut to switch to tool " + aIndex + ":" + toolIDs[aIndex] +
+       " using key " + key);
+  EventUtils.synthesizeKey(key, modifiers, toolbox.doc.defaultView);
 }
 
 function onSelect(event, id) {
   info("toolbox-select event from " + id);
 
   is(toolIDs.indexOf(id), idIndex,
      "Correct tool is selected on pressing the shortcut for " + id);
   // Execute soon to reset the stack trace.
--- a/browser/devtools/framework/test/browser_toolbox_window_shortcuts.js
+++ b/browser/devtools/framework/test/browser_toolbox_window_shortcuts.js
@@ -36,43 +36,39 @@ function test() {
     let target = TargetFactory.forTab(gBrowser.selectedTab);
     idIndex = 0;
     gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.WINDOW)
              .then(testShortcuts);
   });
 }
 
 function testShortcuts(aToolbox, aIndex) {
-  if (aIndex == toolIDs.length) {
+  if (aIndex === undefined) {
+    aIndex = 1;
+  } else if (aIndex == toolIDs.length) {
     tidyUp();
     return;
   }
 
   toolbox = aToolbox;
   info("Toolbox fired a `ready` event");
 
   toolbox.once("select", selectCB);
 
-  if (aIndex != null) {
-    // This if block is to allow the call of selectCB without shortcut press for
-    // the first time. That happens because on opening of toolbox, one tool gets
-    // selected atleast.
-
-    let key = gDevTools._tools.get(toolIDs[aIndex]).key;
-    let toolModifiers = gDevTools._tools.get(toolIDs[aIndex]).modifiers;
-    let modifiers = {
-      accelKey: toolModifiers.contains("accel"),
-      altKey: toolModifiers.contains("alt"),
-      shiftKey: toolModifiers.contains("shift"),
-    };
-    idIndex = aIndex;
-    info("Testing shortcut for tool " + aIndex + ":" + toolIDs[aIndex] +
-         " using key " + key);
-    EventUtils.synthesizeKey(key, modifiers, toolbox.doc.defaultView.parent);
-  }
+  let key = gDevTools._tools.get(toolIDs[aIndex]).key;
+  let toolModifiers = gDevTools._tools.get(toolIDs[aIndex]).modifiers;
+  let modifiers = {
+    accelKey: toolModifiers.contains("accel"),
+    altKey: toolModifiers.contains("alt"),
+    shiftKey: toolModifiers.contains("shift"),
+  };
+  idIndex = aIndex;
+  info("Testing shortcut for tool " + aIndex + ":" + toolIDs[aIndex] +
+       " using key " + key);
+  EventUtils.synthesizeKey(key, modifiers, toolbox.doc.defaultView.parent);
 }
 
 function selectCB(event, id) {
   info("toolbox-select event from " + id);
 
   is(toolIDs.indexOf(id), idIndex,
      "Correct tool is selected on pressing the shortcut for " + id);
 
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -182,16 +182,17 @@ InspectorPanel.prototype = {
     return deferred.promise;
   },
 
   _onBeforeNavigate: function() {
     this._defaultNode = null;
     this.selection.setNodeFront(null);
     this._destroyMarkup();
     this.isDirty = false;
+    this._pendingSelection = null;
   },
 
   _getPageStyle: function() {
     return this._toolbox.inspector.getPageStyle().then(pageStyle => {
       this.pageStyle = pageStyle;
     });
   },
 
@@ -199,28 +200,45 @@ InspectorPanel.prototype = {
    * Return a promise that will resolve to the default node for selection.
    */
   _getDefaultNodeForSelection: function() {
     if (this._defaultNode) {
       return this._defaultNode;
     }
     let walker = this.walker;
     let rootNode = null;
+    let pendingSelection = this._pendingSelection;
+
+    // A helper to tell if the target has or is about to navigate.
+    // this._pendingSelection changes on "will-navigate" and "new-root" events.
+    let hasNavigated = () => pendingSelection !== this._pendingSelection;
 
     // If available, set either the previously selected node or the body
     // as default selected, else set documentElement
     return walker.getRootNode().then(aRootNode => {
+      if (hasNavigated()) {
+        return promise.reject("navigated; resolution of _defaultNode aborted");
+      }
+
       rootNode = aRootNode;
       return walker.querySelector(rootNode, this.selectionCssSelector);
     }).then(front => {
+      if (hasNavigated()) {
+        return promise.reject("navigated; resolution of _defaultNode aborted");
+      }
+
       if (front) {
         return front;
       }
       return walker.querySelector(rootNode, "body");
     }).then(front => {
+      if (hasNavigated()) {
+        return promise.reject("navigated; resolution of _defaultNode aborted");
+      }
+
       if (front) {
         return front;
       }
       return this.walker.documentElement(this.walker.rootNode);
     }).then(node => {
       if (walker !== this.walker) {
         promise.reject(null);
       }
@@ -334,17 +352,17 @@ InspectorPanel.prototype = {
           return;
         }
         this.markup.expandNode(this.selection.nodeFront);
         this.setupSearchBox();
         this.emit("new-root");
       });
     };
     this._pendingSelection = onNodeSelected;
-    this._getDefaultNodeForSelection().then(onNodeSelected);
+    this._getDefaultNodeForSelection().then(onNodeSelected, console.error);
   },
 
   _selectionCssSelector: null,
 
   /**
    * Set the currently selected node unique css selector.
    * Will store the current target url along with it to allow pre-selection at
    * reload
--- a/browser/devtools/projecteditor/lib/projecteditor.js
+++ b/browser/devtools/projecteditor/lib/projecteditor.js
@@ -698,17 +698,19 @@ var ProjectEditor = Class({
    * Whether or not menu items should be able to be enabled.
    * Note that even if this is true, certain menu items will not be
    * enabled until the correct state is achieved (for instance, the
    * 'copy' menu item is only enabled when there is a selection).
    * But if this is false, then nothing will be enabled.
    */
   set menuEnabled(val) {
     this._menuEnabled = val;
-    this._updateMenuItems();
+    if (this._loaded) {
+      this._updateMenuItems();
+    }
   },
 
   get menuEnabled() {
     return this._menuEnabled;
   }
 });
 
 
--- a/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -1948,27 +1948,29 @@ Widgets.JSObject.prototype = Heritage.ex
    *        element. If not provided, the anchor is appended to |this.element|
    *        if it is available. If |appendTo| is provided and if it is a falsy
    *        value, the anchor is not appended to any element.
    * @return DOMElement
    *         The DOM element of the new anchor.
    */
   _anchor: function(text, options = {})
   {
-    if (!options.onClick && !options.href) {
-      options.onClick = this._onClick;
+    if (!options.onClick) {
+      // If the anchor has an URL, open it in a new tab. If not, show the
+      // current object actor.
+      options.onClick = options.href ? this._onClickAnchor : this._onClick;
     }
 
     let anchor = this.el("a", {
       class: options.className,
       draggable: false,
       href: options.href || "#",
     }, text);
 
-    this.message._addLinkCallback(anchor, !options.href ? options.onClick : null);
+    this.message._addLinkCallback(anchor, options.onClick);
 
     if (options.appendTo) {
       options.appendTo.appendChild(anchor);
     } else if (!("appendTo" in options) && this.element) {
       this.element.appendChild(anchor);
     }
 
     return anchor;
--- a/browser/devtools/webconsole/hudservice.js
+++ b/browser/devtools/webconsole/hudservice.js
@@ -487,18 +487,18 @@ WebConsole.prototype = {
     let showSource = ({ DebuggerView }) => {
       if (DebuggerView.Sources.containsValue(aSourceURL)) {
         DebuggerView.setEditorLocation(aSourceURL, aSourceLine,
                                        { noDebug: true }).then(() => {
           this.ui.emit("source-in-debugger-opened");
         });
         return;
       }
-      toolbox.selectTool("webconsole");
-      this.viewSource(aSourceURL, aSourceLine);
+      toolbox.selectTool("webconsole")
+             .then(() => this.viewSource(aSourceURL, aSourceLine));
     }
 
     // If the Debugger was already open, switch to it and try to show the
     // source immediately. Otherwise, initialize it and wait for the sources
     // to be added first.
     let debuggerAlreadyOpen = toolbox.getPanel("jsdebugger");
     toolbox.selectTool("jsdebugger").then(({ panelWin: dbg }) => {
       if (debuggerAlreadyOpen) {
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -253,16 +253,17 @@ run-if = os == "mac"
 [browser_webconsole_js_input_expansion.js]
 [browser_webconsole_jsterm.js]
 [browser_webconsole_live_filtering_of_message_types.js]
 [browser_webconsole_live_filtering_on_search_strings.js]
 [browser_webconsole_message_node_id.js]
 [browser_webconsole_netlogging.js]
 [browser_webconsole_network_panel.js]
 [browser_webconsole_notifications.js]
+[browser_webconsole_open-links-without-callback.js]
 [browser_webconsole_output_copy_newlines.js]
 [browser_webconsole_output_order.js]
 [browser_webconsole_property_provider.js]
 [browser_webconsole_scratchpad_panel_link.js]
 [browser_webconsole_split.js]
 [browser_webconsole_split_escape_key.js]
 [browser_webconsole_view_source.js]
 [browser_webconsole_reflow.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_open-links-without-callback.js
@@ -0,0 +1,50 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that if a link without an onclick callback is clicked the link is
+// opened in a new tab and no exception occurs (bug 999236).
+
+function test() {
+  function* runner() {
+    const TEST_EVAL_STRING = "document";
+    const TEST_PAGE_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
+    const {tab} = yield loadTab(TEST_PAGE_URI);
+    const hud = yield openConsole(tab);
+
+    hud.jsterm.execute(TEST_EVAL_STRING);
+
+    const EXPECTED_OUTPUT = new RegExp("HTMLDocument \.+");
+
+    let messages = yield waitForMessages({
+      webconsole: hud,
+      messages: [{
+        name: "JS eval output",
+        text: EXPECTED_OUTPUT,
+        category: CATEGORY_OUTPUT,
+      }],
+    });
+
+    let messageNode = messages[0].matched.values().next().value;
+
+    // The correct anchor is second in the message node; the first anchor has
+    // class .cm-variable. Ignore the first one by not matching anchors that
+    // have the class .cm-variable.
+    let urlNode = messageNode.querySelector("a:not(.cm-variable)");
+
+    let linkOpened = false;
+    let oldOpenUILinkIn = window.openUILinkIn;
+    window.openUILinkIn = function(aLink) {
+      if (aLink == TEST_PAGE_URI) {
+        linkOpened = true;
+      }
+    }
+
+    EventUtils.synthesizeMouseAtCenter(urlNode, {}, hud.iframeWindow);
+
+    ok(linkOpened, "Clicking the URL opens the desired page");
+    window.openUILinkIn = oldOpenUILinkIn;
+  }
+
+  Task.spawn(runner).then(finishTest);
+}
--- a/browser/devtools/webide/content/addons.js
+++ b/browser/devtools/webide/content/addons.js
@@ -1,30 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cu = Components.utils;
 const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
-const {GetAvailableAddons} = require("devtools/webide/addons");
+const {GetAvailableAddons, ForgetAddonsList} = require("devtools/webide/addons");
 const Strings = Services.strings.createBundle("chrome://webide/content/webide.properties");
 
 window.addEventListener("load", function onLoad() {
   window.removeEventListener("load", onLoad);
   document.querySelector("#aboutaddons").onclick = function() {
     window.parent.UI.openInBrowser("about:addons");
   }
   document.querySelector("#close").onclick = CloseUI;
   GetAvailableAddons().then(BuildUI, (e) => {
     console.error(e);
     window.alert(Strings.formatStringFromName("error_cantFetchAddonsJSON", [e], 1));
   });
 }, true);
 
+window.addEventListener("unload", function onUnload() {
+  window.removeEventListener("unload", onUnload);
+  ForgetAddonsList();
+}, true);
+
 function CloseUI() {
   window.parent.UI.openProject();
 }
 
 function BuildUI(addons) {
   BuildItem(addons.adb, true /* is adb */);
   for (let addon of addons.simulators) {
     BuildItem(addon, false /* is adb */);
--- a/browser/devtools/webide/content/webide.js
+++ b/browser/devtools/webide/content/webide.js
@@ -22,19 +22,18 @@ const {Devices} = Cu.import("resource://
 const {GetAvailableAddons} = require("devtools/webide/addons");
 const {GetTemplatesJSON, GetAddonsJSON} = require("devtools/webide/remote-resources");
 
 const Strings = Services.strings.createBundle("chrome://webide/content/webide.properties");
 
 const HTML = "http://www.w3.org/1999/xhtml";
 const HELP_URL = "https://developer.mozilla.org/Firefox_OS/Using_the_App_Manager#Troubleshooting";
 
-// download some JSON early.
+// download template index early
 GetTemplatesJSON(true);
-GetAddonsJSON(true);
 
 // See bug 989619
 console.log = console.log.bind(console);
 console.warn = console.warn.bind(console);
 console.error = console.error.bind(console);
 
 window.addEventListener("load", function onLoad() {
   window.removeEventListener("load", onLoad);
--- a/browser/devtools/webide/modules/addons.js
+++ b/browser/devtools/webide/modules/addons.js
@@ -61,32 +61,36 @@ let GetAvailableAddons_promise = null;
 let GetAvailableAddons = exports.GetAvailableAddons = function() {
   if (!GetAvailableAddons_promise) {
     let deferred = promise.defer();
     GetAvailableAddons_promise = deferred.promise;
     let addons = {
       simulators: [],
       adb: null
     }
-    GetAddonsJSON().then(json => {
+    GetAddonsJSON(true).then(json => {
       for (let stability in json) {
         for (let version of json[stability]) {
           addons.simulators.push(new SimulatorAddon(stability, version));
         }
       }
       addons.adb = new ADBAddon();
       deferred.resolve(addons);
     }, e => {
       GetAvailableAddons_promise = null;
       deferred.reject(e);
     });
   }
   return GetAvailableAddons_promise;
 }
 
+exports.ForgetAddonsList = function() {
+  GetAvailableAddons_promise = null;
+}
+
 function Addon() {}
 Addon.prototype = {
   _status: "unknown",
   set status(value) {
     if (this._status != value) {
       this._status = value;
       this.emit("update");
     }
--- a/browser/devtools/webide/themes/details.css
+++ b/browser/devtools/webide/themes/details.css
@@ -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/. */
 
 body {
   margin: 0;
   background-color: white;
-  font-family: Lucida Grande, Helvetica, Helvetica Neue, sans-serif;
-  font-size: 12px;
+  font: message-box;
 }
 
 .hidden {
   display: none;
 }
 
 h1, h3, p {
   margin: 0;
@@ -25,16 +24,17 @@ h1, h3, p {
 #toolbar > button {
   -moz-appearance: none;
   background-color: transparent;
   border-width: 0 1px 0 0;
   border-color: #AAA;
   border-style: solid;
   margin: 0;
   padding: 0 12px;
+  font-family: inherit;
   font-weight: bold;
   height: 24px;
 }
 
 #toolbar > button:hover {
   background-color: #CCC;
   cursor: pointer;
 }
--- a/browser/devtools/webide/themes/webide.css
+++ b/browser/devtools/webide/themes/webide.css
@@ -29,17 +29,20 @@
 window.busy .action-button,
 window:not(.busy) #action-busy {
   display: none;
 }
 
 /* Panel buttons */
 
 .panel-button {
+  -moz-appearance: none;
   -moz-box-align: center;
+  border-width: 0;
+  background: none;
 }
 
 .panel-button-anchor {
   list-style-image: url('icons.png');
   -moz-image-region: rect(43px, 563px, 61px, 535px);
   width: 12px;
   height: 7px;
   margin-bottom: -5px;
@@ -118,16 +121,17 @@ panel > .panel-arrowcontainer > .panel-a
   width: 180px;
 }
 
 .panel-item,
 .panel-item-help {
   padding: 3px 12px;
   margin: 0;
   -moz-appearance: none;
+  border-width: 0;
 }
 
 .panel-item-help {
   font-size: 0.9em;
 }
 
 .panel-item:hover,
 .panel-item-help:hover {
--- a/mobile/android/base/home/HomePager.java
+++ b/mobile/android/base/home/HomePager.java
@@ -59,22 +59,22 @@ public class HomePager extends ViewPager
     // Current load state of HomePager.
     private LoadState mLoadState;
 
     // Listens for when the current panel changes.
     private OnPanelChangeListener mPanelChangedListener;
 
     // This is mostly used by UI tests to easily fetch
     // specific list views at runtime.
-    static final String LIST_TAG_HISTORY = "history";
-    static final String LIST_TAG_BOOKMARKS = "bookmarks";
-    static final String LIST_TAG_READING_LIST = "reading_list";
-    static final String LIST_TAG_TOP_SITES = "top_sites";
-    static final String LIST_TAG_RECENT_TABS = "recent_tabs";
-    static final String LIST_TAG_BROWSER_SEARCH = "browser_search";
+    public static final String LIST_TAG_HISTORY = "history";
+    public static final String LIST_TAG_BOOKMARKS = "bookmarks";
+    public static final String LIST_TAG_READING_LIST = "reading_list";
+    public static final String LIST_TAG_TOP_SITES = "top_sites";
+    public static final String LIST_TAG_RECENT_TABS = "recent_tabs";
+    public static final String LIST_TAG_BROWSER_SEARCH = "browser_search";
 
     public interface OnUrlOpenListener {
         public enum Flags {
             ALLOW_SWITCH_TO_TAB,
             OPEN_WITH_INTENT
         }
 
         public void onUrlOpen(String url, EnumSet<Flags> flags);
--- a/mobile/android/base/tests/AboutHomeTest.java
+++ b/mobile/android/base/tests/AboutHomeTest.java
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tests;
 
 import java.util.ArrayList;
 
 import org.mozilla.gecko.Actions;
+import org.mozilla.gecko.home.HomePager;
 
 import android.support.v4.view.ViewPager;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ListAdapter;
 import android.widget.ListView;
 import android.widget.TabWidget;
@@ -105,17 +106,17 @@ abstract class AboutHomeTest extends Pix
         }
     }
 
     // @return the View associated with bookmark for the provided url or null if the link is not bookmarked
     protected View getDisplayedBookmark(String url) {
         openAboutHomeTab(AboutHomeTabs.BOOKMARKS);
         mSolo.hideSoftKeyboard();
         getInstrumentation().waitForIdleSync();
-        ListView bookmarksTabList = findListViewWithTag("bookmarks");
+        ListView bookmarksTabList = findListViewWithTag(HomePager.LIST_TAG_BOOKMARKS);
         waitForNonEmptyListToLoad(bookmarksTabList);
         ListAdapter adapter = bookmarksTabList.getAdapter();
         if (adapter != null) {
             for (int i = 0; i < adapter.getCount(); i++ ) {
                 // I am unable to click the view taken with getView for some reason so getting the child at i
                 bookmarksTabList.smoothScrollToPosition(i);
                 View bookmarkView = bookmarksTabList.getChildAt(i);
                 if (bookmarkView instanceof android.widget.LinearLayout) {
--- a/mobile/android/base/tests/testAddSearchEngine.java
+++ b/mobile/android/base/tests/testAddSearchEngine.java
@@ -1,16 +1,17 @@
 package org.mozilla.gecko.tests;
 
 import java.util.ArrayList;
 
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.Actions;
+import org.mozilla.gecko.home.HomePager;
 
 import android.widget.ImageView;
 import android.widget.ListAdapter;
 import android.widget.ListView;
 
 /**
  * Test adding a search engine from an input field context menu.
  * 1. Get the number of existing search engines from the SearchEngine:Data event and as displayed in about:home.
@@ -127,17 +128,17 @@ public class testAddSearchEngine extends
      * @param expectedCount The expected number of search engines.
      */
     public void verifyDisplayedSearchEnginesCount(final int expectedCount) {
         mSolo.clearEditText(0);
         mActions.sendKeys(SEARCH_TEXT);
         boolean correctNumSearchEnginesDisplayed = waitForTest(new BooleanTest() {
             @Override
             public boolean test() {
-                ListView list = findListViewWithTag("browser_search");
+                ListView list = findListViewWithTag(HomePager.LIST_TAG_BROWSER_SEARCH);
                 if (list == null) {
                     return false;
                 }
                 ListAdapter adapter = list.getAdapter();
                 if (adapter == null) {
                     return false;
                 }
                 return (adapter.getCount() == expectedCount);
--- a/mobile/android/base/tests/testBookmarkFolders.java
+++ b/mobile/android/base/tests/testBookmarkFolders.java
@@ -1,11 +1,12 @@
 package org.mozilla.gecko.tests;
 
 import org.mozilla.gecko.sync.Utils;
+import org.mozilla.gecko.home.HomePager;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.net.Uri;
 import android.view.View;
 import android.widget.ListAdapter;
 import android.widget.ListView;
 import android.widget.TextView;
@@ -24,17 +25,17 @@ public class testBookmarkFolders extends
 
     private void checkBookmarkList() {
         openAboutHomeTab(AboutHomeTabs.BOOKMARKS);
         waitForText(StringHelper.DESKTOP_FOLDER_LABEL);
         clickOnBookmarkFolder(StringHelper.DESKTOP_FOLDER_LABEL);
         waitForText(StringHelper.TOOLBAR_FOLDER_LABEL);
 
         // Verify the number of folders displayed in the Desktop Bookmarks folder is correct
-        ListView desktopFolderContent = findListViewWithTag("bookmarks");
+        ListView desktopFolderContent = findListViewWithTag(HomePager.LIST_TAG_BOOKMARKS);
         ListAdapter adapter = desktopFolderContent.getAdapter();
         if (mDevice.type.equals("tablet")) { // On tablets it's 4 folders and 1 view for top padding
             mAsserter.is(adapter.getCount(), 5, "Checking that the correct number of folders is displayed in the Desktop Bookmarks folder");
         } else { // On phones it's just the 4 folders
             mAsserter.is(adapter.getCount(), 4, "Checking that the correct number of folders is displayed in the Desktop Bookmarks folder");
         }
 
         clickOnBookmarkFolder(StringHelper.TOOLBAR_FOLDER_LABEL);
@@ -93,17 +94,17 @@ public class testBookmarkFolders extends
         mAsserter.ok(success, "Trying to click on the " + folderName + " folder","The " + folderName + " folder was clicked");
     }
 
     private View getBookmarkFolderView(String folderName) {
         openAboutHomeTab(AboutHomeTabs.BOOKMARKS);
         mSolo.hideSoftKeyboard();
         getInstrumentation().waitForIdleSync();
 
-        ListView bookmarksTabList = findListViewWithTag("bookmarks");
+        ListView bookmarksTabList = findListViewWithTag(HomePager.LIST_TAG_BOOKMARKS);
         if (!waitForNonEmptyListToLoad(bookmarksTabList)) {
             return null;
         }
 
         ListAdapter adapter = bookmarksTabList.getAdapter();
         if (adapter == null) {
             return null;
         }
--- a/mobile/android/base/tests/testBookmarklets.java
+++ b/mobile/android/base/tests/testBookmarklets.java
@@ -1,11 +1,12 @@
 package org.mozilla.gecko.tests;
 
 import org.mozilla.gecko.Actions;
+import org.mozilla.gecko.home.HomePager;
 
 import android.database.Cursor;
 import android.widget.ListView;
 
 
 public class testBookmarklets extends AboutHomeTest {
     public void testBookmarklets() {
         final String url = getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
@@ -32,17 +33,17 @@ public class testBookmarklets extends Ab
 
         // add the bookmarklet to the database. there's currently no way to
         // add this using the UI, so we go through the content provider.
         mDatabaseHelper.addOrUpdateMobileBookmark(title, js);
 
         // Open about:home in the Bookmarks page
         openAboutHomeTab(AboutHomeTabs.BOOKMARKS);
 
-        ListView bookmarks = findListViewWithTag("bookmarks");
+        ListView bookmarks = findListViewWithTag(HomePager.LIST_TAG_BOOKMARKS);
         mAsserter.is(waitForNonEmptyListToLoad(bookmarks), true, "list is properly loaded");
 
         int width = mDriver.getGeckoWidth();
         int height = mDriver.getGeckoHeight();
 
         // Scroll down so that the bookmarks list has more items on screen.
         mActions.drag(width / 2, width / 2, height - 10, height / 2);
 
--- a/mobile/android/base/tests/testHistory.java
+++ b/mobile/android/base/tests/testHistory.java
@@ -1,14 +1,16 @@
 package org.mozilla.gecko.tests;
 
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ListView;
 
+import org.mozilla.gecko.home.HomePager;
+
 public class testHistory extends AboutHomeTest {
     private View mFirstChild;
 
     public void testHistory() {
         blockForGeckoReady();
 
         String url = getAbsoluteUrl("/robocop/robocop_blank_01.html");
         String url2 = getAbsoluteUrl("/robocop/robocop_blank_02.html");
@@ -18,17 +20,17 @@ public class testHistory extends AboutHo
         verifyPageTitle("Browser Blank Page 01");
         inputAndLoadUrl(url2);
         verifyPageTitle("Browser Blank Page 02");
         inputAndLoadUrl(url3);
         verifyPageTitle("Browser Blank Page 03");
 
         openAboutHomeTab(AboutHomeTabs.HISTORY);
 
-        final ListView hList = findListViewWithTag("history");
+        final ListView hList = findListViewWithTag(HomePager.LIST_TAG_HISTORY);
         mAsserter.is(waitForNonEmptyListToLoad(hList), true, "list is properly loaded");
 
         // Click on the history item and wait for the page to load
         // wait for the history list to be populated
         mFirstChild = null;
         boolean success = waitForTest(new BooleanTest() {
             @Override
             public boolean test() {
--- a/mobile/android/base/tests/testReaderMode.java
+++ b/mobile/android/base/tests/testReaderMode.java
@@ -1,13 +1,14 @@
 package org.mozilla.gecko.tests;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.Actions;
+import org.mozilla.gecko.home.HomePager;
 import org.mozilla.gecko.R;
 
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ListView;
 
 import com.jayway.android.robotium.solo.Condition;
 import com.jayway.android.robotium.solo.Solo;
@@ -102,34 +103,34 @@ public class testReaderMode extends Abou
         contentEventExpecter.blockForEvent();
         contentEventExpecter.unregisterListener();
 
         // Check if the page is present in the Reading List
         mAsserter.ok(mSolo.waitForText("Robocop Text Page"), "Verify if the page is added to your Reading List", "The page is present in your Reading List");
 
         // Check if the page is added in History tab like a Reading List item
         openAboutHomeTab(AboutHomeTabs.HISTORY);
-        list = findListViewWithTag("history");
+        list = findListViewWithTag(HomePager.LIST_TAG_HISTORY);
         child = list.getChildAt(1);
         mAsserter.ok(child != null, "item can be retrieved", child != null ? child.toString() : "null!");
         mSolo.clickLongOnView(child);
         mAsserter.ok(mSolo.waitForText("Open in Reader"), "Verify if the page is present in history as a Reading List item", "The page is present in history as a Reading List item");
         mActions.sendSpecialKey(Actions.SpecialKey.BACK); // Dismiss the context menu
         mSolo.waitForText("Robocop Text Page");
 
         // Verify separately the Reading List entries for tablets and phone because for tablets there is an extra child in UI design
         if (devType.equals("phone")) {
             childNo = 1;
         }
         else {
             childNo = 2;
         }
         // Verify if the page is present to your Reading List
         openAboutHomeTab(AboutHomeTabs.READING_LIST);
-        list = findListViewWithTag("reading_list");
+        list = findListViewWithTag(HomePager.LIST_TAG_READING_LIST);
         child = list.getChildAt(childNo-1);
         mAsserter.ok(child != null, "Verify if the page is present to your Reading List", "The page is present in your Reading List");
         contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
         mSolo.clickOnView(child);
         contentEventExpecter.blockForEvent();
         contentEventExpecter.unregisterListener();
         verifyPageTitle("Robocop Text Page");
 
@@ -138,17 +139,17 @@ public class testReaderMode extends Abou
         width = mDriver.getGeckoLeft() + 50;
         mAsserter.dumpLog("Long Clicking at width = " + String.valueOf(width) + " and height = " + String.valueOf(height));
         mSolo.clickOnScreen(width,height);
         mAsserter.ok(mSolo.waitForText("Page removed from your Reading List"), "Waiting for the page to removed from your Reading List", "The page is removed from your Reading List");
         verifyPageTitle("Robocop Text Page");
 
         //Check if the Reading List is empty
         openAboutHomeTab(AboutHomeTabs.READING_LIST);
-        list = findListViewWithTag("reading_list");
+        list = findListViewWithTag(HomePager.LIST_TAG_READING_LIST);
         child = list.getChildAt(childNo-1);
         mAsserter.ok(child == null, "Verify if the Reading List is empty", "The Reading List is empty");
     }
 
     // Get the reader icon method
     protected View getReaderIcon() {
         View pageActionLayout = mSolo.getView(R.id.page_action_layout);
         final ViewGroup actionLayoutEntry = (ViewGroup)pageActionLayout;
--- a/mobile/android/base/tests/testShareLink.java
+++ b/mobile/android/base/tests/testShareLink.java
@@ -1,14 +1,15 @@
 package org.mozilla.gecko.tests;
 
 import java.util.ArrayList;
 import java.util.List;
 
 import org.mozilla.gecko.Actions;
+import org.mozilla.gecko.home.HomePager;
 
 import android.app.Activity;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Build;
 import android.view.View;
 import android.view.ViewGroup;
@@ -63,17 +64,17 @@ public class testShareLink extends About
         float top = mDriver.getGeckoTop() + 30 * mDevice.density;
         float left = mDriver.getGeckoLeft() + mDriver.getGeckoWidth() / 2;
         mSolo.clickLongOnScreen(left, top);
         verifySharePopup("Share Link",shareOptions,"Link");
 
         // Test the share popup in the Bookmarks page
         openAboutHomeTab(AboutHomeTabs.BOOKMARKS);
 
-        final ListView bookmarksList = findListViewWithTag("bookmarks");
+        final ListView bookmarksList = findListViewWithTag(HomePager.LIST_TAG_BOOKMARKS);
         mAsserter.is(waitForNonEmptyListToLoad(bookmarksList), true, "list is properly loaded");
 
         int headerViewsCount = bookmarksList.getHeaderViewsCount();
         View bookmarksItem = bookmarksList.getChildAt(headerViewsCount);
         if (bookmarksItem == null) {
             mAsserter.dumpLog("no child at index " + headerViewsCount + "; waiting for one...");
             Condition listWaitCondition = new Condition() {
                 @Override
@@ -108,26 +109,26 @@ public class testShareLink extends About
         // Test the share popup in Top Sites.
         openAboutHomeTab(AboutHomeTabs.TOP_SITES);
 
         // Scroll down a bit so that the top sites list has more items on screen.
         int width = mDriver.getGeckoWidth();
         int height = mDriver.getGeckoHeight();
         mActions.drag(width / 2, width / 2, height - 10, height / 2);
 
-        ListView topSitesList = findListViewWithTag("top_sites");
+        ListView topSitesList = findListViewWithTag(HomePager.LIST_TAG_TOP_SITES);
         mAsserter.is(waitForNonEmptyListToLoad(topSitesList), true, "list is properly loaded");
         View mostVisitedItem = topSitesList.getChildAt(topSitesList.getHeaderViewsCount());
         mSolo.clickLongOnView(mostVisitedItem);
         verifySharePopup(shareOptions,"top_sites");
 
         // Test the share popup in the history tab
         openAboutHomeTab(AboutHomeTabs.HISTORY);
 
-        ListView mostRecentList = findListViewWithTag("history");
+        ListView mostRecentList = findListViewWithTag(HomePager.LIST_TAG_HISTORY);
         mAsserter.is(waitForNonEmptyListToLoad(mostRecentList), true, "list is properly loaded");
 
         // Getting second child after header views because the first is the "Today" label
         View mostRecentItem = mostRecentList.getChildAt(mostRecentList.getHeaderViewsCount() + 1);
         mSolo.clickLongOnView(mostRecentItem);
         verifySharePopup(shareOptions,"most recent");
     }
 
--- a/toolkit/components/passwordmgr/LoginStore.jsm
+++ b/toolkit/components/passwordmgr/LoginStore.jsm
@@ -184,16 +184,24 @@ LoginStore.prototype = {
                                                     { humanReadable: true });
             yield openInfo.file.close();
             yield OS.File.move(this.path, openInfo.path);
           } catch (e2) {
             Cu.reportError(e2);
           }
         }
 
+        // In some rare cases it's possible for logins to have been added to
+        // our database between the call to OS.File.read and when we've been
+        // notified that there was a problem with it. In that case, leave the
+        // synchronously-added data alone. See bug 1029128, comment 4.
+        if (this.dataReady) {
+          return;
+        }
+
         // In any case, initialize a new object to host the data.
         this.data = {
           nextId: 1,
         };
       }
 
       this._processLoadedData();
     }.bind(this));
--- a/toolkit/components/passwordmgr/storage-json.js
+++ b/toolkit/components/passwordmgr/storage-json.js
@@ -6,16 +6,18 @@
 
 /**
  * nsILoginManagerStorage implementation for the JSON back-end.
  */
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Globals
 
+"use strict";
+
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
                                   "resource://gre/modules/LoginHelper.jsm");
@@ -132,22 +134,20 @@ this.LoginManagerStorage_json.prototype 
 
     /*
      * addLogin
      *
      */
     addLogin : function (login) {
         this._store.ensureDataReady();
 
-        let encUsername, encPassword;
-
         // Throws if there are bogus values.
         LoginHelper.checkLoginValues(login);
 
-        [encUsername, encPassword, encType] = this._encryptLogin(login);
+        let [encUsername, encPassword, encType] = this._encryptLogin(login);
 
         // Clone the login, so we don't modify the caller's object.
         let loginClone = login.clone();
 
         // Initialize the nsILoginMetaInfo fields, unless the caller gave us values
         loginClone.QueryInterface(Ci.nsILoginMetaInfo);
         if (loginClone.guid) {
             if (!this._isGuidUnique(loginClone.guid))
--- a/widget/gtk/gtk3drawing.c
+++ b/widget/gtk/gtk3drawing.c
@@ -15,17 +15,17 @@
 #include "nsDebug.h"
 #include "prinrval.h"
 
 #include <math.h>
 
 static GtkWidget* gProtoWindow;
 static GtkWidget* gProtoLayout;
 static GtkWidget* gButtonWidget;
-static GtkWidget* gToggleMenuButtonWidget;
+static GtkWidget* gToggleButtonWidget;
 static GtkWidget* gButtonArrowWidget;
 static GtkWidget* gCheckboxWidget;
 static GtkWidget* gRadiobuttonWidget;
 static GtkWidget* gHorizScrollbarWidget;
 static GtkWidget* gVertScrollbarWidget;
 static GtkWidget* gSpinWidget;
 static GtkWidget* gHScaleWidget;
 static GtkWidget* gVScaleWidget;
@@ -157,31 +157,35 @@ ensure_vpaned_widget()
     if (!gVPanedWidget) {
         gVPanedWidget = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
         setup_widget_prototype(gVPanedWidget);
     }
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
-ensure_toggle_menu_button_widget()
+ensure_toggle_button_widget()
 {
-    if (!gToggleMenuButtonWidget) {
-        gToggleMenuButtonWidget = gtk_menu_button_new();
-        setup_widget_prototype(gToggleMenuButtonWidget);
+    if (!gToggleButtonWidget) {
+        gToggleButtonWidget = gtk_toggle_button_new();
+        setup_widget_prototype(gToggleButtonWidget);
   }
   return MOZ_GTK_SUCCESS;
 }
 
 static gint
 ensure_button_arrow_widget()
 {
     if (!gButtonArrowWidget) {
-        ensure_toggle_menu_button_widget();
-        gButtonArrowWidget = gtk_bin_get_child(GTK_BIN(gToggleMenuButtonWidget));
+        ensure_toggle_button_widget();
+
+        gButtonArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
+        gtk_container_add(GTK_CONTAINER(gToggleButtonWidget), gButtonArrowWidget);
+        gtk_widget_realize(gButtonArrowWidget);
+        gtk_widget_show(gButtonArrowWidget);
     }
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 ensure_checkbox_widget()
 {
     if (!gCheckboxWidget) {
@@ -324,18 +328,18 @@ ensure_combo_box_widgets()
             g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer)
                                       &gComboBoxArrowWidget);
             gtk_widget_realize(gComboBoxArrowWidget);
         }
     } else {
         /* Shouldn't be reached with current internal gtk implementation; we
          * use a generic toggle button as last resort fallback to avoid
          * crashing. */
-        ensure_toggle_menu_button_widget();
-        gComboBoxButtonWidget = gToggleMenuButtonWidget;
+        ensure_toggle_button_widget();
+        gComboBoxButtonWidget = gToggleButtonWidget;
     }
 
     if (!gComboBoxArrowWidget) {
         /* Shouldn't be reached with current internal gtk implementation;
          * we gButtonArrowWidget as last resort fallback to avoid
          * crashing. */
         ensure_button_arrow_widget();
         gComboBoxArrowWidget = gButtonArrowWidget;
@@ -430,18 +434,18 @@ ensure_combo_box_entry_widgets()
             g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer)
                                       &gComboBoxEntryArrowWidget);
             gtk_widget_realize(gComboBoxEntryArrowWidget);
         }
     } else {
         /* Shouldn't be reached with current internal gtk implementation;
          * we use a generic toggle button as last resort fallback to avoid
          * crashing. */
-        ensure_toggle_menu_button_widget();
-        gComboBoxEntryButtonWidget = gToggleMenuButtonWidget;
+        ensure_toggle_button_widget();
+        gComboBoxEntryButtonWidget = gToggleButtonWidget;
     }
 
     if (!gComboBoxEntryArrowWidget) {
         /* Shouldn't be reached with current internal gtk implementation;
          * we gButtonArrowWidget as last resort fallback to avoid
          * crashing. */
         ensure_button_arrow_widget();
         gComboBoxEntryArrowWidget = gButtonArrowWidget;
@@ -3108,20 +3112,20 @@ moz_gtk_widget_paint(GtkThemeWidgetType 
 {
     /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=694086
      */
     cairo_new_path(cr);
 
     switch (widget) {
     case MOZ_GTK_BUTTON:
         if (state->depressed) {
-            ensure_toggle_menu_button_widget();
+            ensure_toggle_button_widget();
             return moz_gtk_button_paint(cr, rect, state,
                                         (GtkReliefStyle) flags,
-                                        gToggleMenuButtonWidget, direction);
+                                        gToggleButtonWidget, direction);
         }
         ensure_button_widget();
         return moz_gtk_button_paint(cr, rect, state,
                                     (GtkReliefStyle) flags, gButtonWidget,
                                     direction);
         break;
     case MOZ_GTK_CHECKBUTTON:
     case MOZ_GTK_RADIOBUTTON:
@@ -3324,17 +3328,17 @@ moz_gtk_shutdown()
 
     /* TODO - replace it with appropriate widget */
     if (gTreeHeaderSortArrowWidget)
         gtk_widget_destroy(gTreeHeaderSortArrowWidget);
 
     gProtoWindow = NULL;
     gProtoLayout = NULL;
     gButtonWidget = NULL;
-    gToggleMenuButtonWidget = NULL;
+    gToggleButtonWidget = NULL;
     gButtonArrowWidget = NULL;
     gCheckboxWidget = NULL;
     gRadiobuttonWidget = NULL;
     gHorizScrollbarWidget = NULL;
     gVertScrollbarWidget = NULL;
     gSpinWidget = NULL;
     gHScaleWidget = NULL;
     gVScaleWidget = NULL;