Bug 981418 - add onBeforeCreated handler for Australis widget, r=jaws
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Tue, 11 Mar 2014 00:35:53 +0000
changeset 173118 fdebf60103c313f6dde473e6c92818e07b6fba42
parent 173117 569c2bd2234e2e7d71813d7f7235b1c7df596e3b
child 173119 f8f0091f945c9e7767cb38dbee791a08766cc834
push id26391
push usercbook@mozilla.com
push dateWed, 12 Mar 2014 11:20:34 +0000
treeherdermozilla-central@a56837cfc67c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws
bugs981418
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 981418 - add onBeforeCreated handler for Australis widget, r=jaws
browser/components/customizableui/src/CustomizableUI.jsm
browser/components/customizableui/test/browser.ini
browser/components/customizableui/test/browser_981418-widget-onbeforecreated-handler.js
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -1077,26 +1077,25 @@ let CustomizableUIInternal = {
       throw new Error("buildWidget was passed a non-widget to build.");
     }
 
     LOG("Building " + aWidget.id + " of type " + aWidget.type);
 
     let node;
     if (aWidget.type == "custom") {
       if (aWidget.onBuild) {
-        try {
-          node = aWidget.onBuild(aDocument);
-        } catch (ex) {
-          ERROR("Custom widget with id " + aWidget.id + " threw an error: " + ex.message);
-        }
+        node = aWidget.onBuild(aDocument);
       }
       if (!node || !(node instanceof aDocument.defaultView.XULElement))
         ERROR("Custom widget with id " + aWidget.id + " does not return a valid node");
     }
     else {
+      if (aWidget.onBeforeCreated) {
+        aWidget.onBeforeCreated(aDocument);
+      }
       node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
 
       node.setAttribute("id", aWidget.id);
       node.setAttribute("widget-id", aWidget.id);
       node.setAttribute("widget-type", aWidget.type);
       if (aWidget.disabled) {
         node.setAttribute("disabled", true);
       }
@@ -1966,16 +1965,17 @@ let CustomizableUIInternal = {
     if ("type" in aData && gSupportedWidgetTypes.has(aData.type)) {
       widget.type = aData.type;
     } else {
       widget.type = "button";
     }
 
     widget.disabled = aData.disabled === true;
 
+    this.wrapWidgetEventHandler("onBeforeCreated", widget);
     this.wrapWidgetEventHandler("onClick", widget);
     this.wrapWidgetEventHandler("onCreated", widget);
 
     if (widget.type == "button") {
       widget.onCommand = typeof aData.onCommand == "function" ?
                            aData.onCommand :
                            null;
     } else if (widget.type == "view") {
@@ -2723,16 +2723,22 @@ this.CustomizableUI = {
    *                  'custom' - for fine-grained control over the creation
    *                             of the widget.
    * - viewId:        Only useful for views (and required there): the id of the
    *                  <panelview> that should be shown when clicking the widget.
    * - onBuild(aDoc): Only useful for custom widgets (and required there); a
    *                  function that will be invoked with the document in which
    *                  to build a widget. Should return the DOM node that has
    *                  been constructed.
+   * - onBeforeCreated(aDoc): Attached to all non-custom widgets; a function
+   *                  that will be invoked before the widget gets a DOM node
+   *                  constructed, passing the document in which that will happen.
+   *                  This is useful especially for 'view' type widgets that need
+   *                  to construct their views on the fly (e.g. from bootstrapped
+   *                  add-ons)
    * - onCreated(aNode): Attached to all widgets; a function that will be invoked
    *                  whenever the widget has a DOM node constructed, passing the
    *                  constructed node as an argument.
    * - onCommand(aEvt): Only useful for button widgets; a function that will be
    *                    invoked when the user activates the button.
    * - onClick(aEvt): Attached to all widgets; a function that will be invoked
    *                  when the user clicks the widget.
    * - onViewShowing(aEvt): Only useful for views; a function that will be
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -69,9 +69,10 @@ skip-if = os == "linux"
 [browser_968565_insert_before_hidden_items.js]
 [browser_969427_recreate_destroyed_widget_after_reset.js]
 [browser_969661_character_encoding_navbar_disabled.js]
 [browser_970511_undo_restore_default.js]
 [browser_972267_customizationchange_events.js]
 [browser_973932_addonbar_currentset.js]
 [browser_975719_customtoolbars_behaviour.js]
 [browser_978084_dragEnd_after_move.js]
+[browser_981418-widget-onbeforecreated-handler.js]
 [browser_panel_toggle.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_981418-widget-onbeforecreated-handler.js
@@ -0,0 +1,90 @@
+/* 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 kWidgetId = 'test-981418-widget-onbeforecreated';
+
+// Should be able to add broken view widget
+add_task(function testAddOnBeforeCreatedWidget() {
+  let viewShownDeferred = Promise.defer();
+  let onBeforeCreatedCalled = false;
+  let widgetSpec = {
+    id: kWidgetId,
+    type: 'view',
+    viewId: kWidgetId + 'idontexistyet',
+    onBeforeCreated: function(doc) {
+      let view = doc.createElement("panelview");
+      view.id = kWidgetId + 'idontexistyet';
+      let label = doc.createElement("label");
+      label.setAttribute("value", "Hello world");
+      label.className = 'panel-subview-header';
+      view.appendChild(label);
+      document.getElementById("PanelUI-multiView").appendChild(view);
+      onBeforeCreatedCalled = true;
+    },
+    onViewShowing: function() {
+      viewShownDeferred.resolve();
+    }
+  };
+
+  let noError = true;
+  try {
+    CustomizableUI.createWidget(widgetSpec);
+    CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_NAVBAR);
+  } catch (ex) {
+    Cu.reportError(ex);
+    noError = false;
+  }
+  ok(noError, "Should not throw an exception trying to add the widget.");
+  ok(onBeforeCreatedCalled, "onBeforeCreated should have been called");
+
+  let widgetNode = document.getElementById(kWidgetId);
+  ok(widgetNode, "Widget should exist");
+  if (widgetNode) {
+    try {
+      widgetNode.click();
+
+      let shownTimeout = setTimeout(() => viewShownDeferred.reject("Panel not shown within 20s"), 20000);
+      yield viewShownDeferred.promise;
+      clearTimeout(shownTimeout);
+      ok(true, "Found view shown");
+
+      let tempPanel = document.getElementById("customizationui-widget-panel");
+      let panelHiddenPromise = promisePanelElementHidden(window, tempPanel);
+      tempPanel.hidePopup();
+      yield panelHiddenPromise;
+
+      CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_PANEL);
+      yield PanelUI.show();
+
+      viewShownDeferred = Promise.defer();
+      widgetNode.click();
+
+      shownTimeout = setTimeout(() => viewShownDeferred.reject("Panel not shown within 20s"), 20000);
+      yield viewShownDeferred.promise;
+      clearTimeout(shownTimeout);
+      ok(true, "Found view shown");
+
+      let panelHidden = promisePanelHidden(window);
+      PanelUI.hide();
+      yield panelHidden;
+    } catch (ex) {
+      ok(false, "Unexpected exception (like a timeout for one of the yields) " +
+                "when testing view widget.");
+    }
+  }
+
+  noError = true;
+  try {
+    CustomizableUI.destroyWidget(kWidgetId);
+  } catch (ex) {
+    Cu.reportError(ex);
+    noError = false;
+  }
+  ok(noError, "Should not throw an exception trying to remove the broken view widget.");
+});
+
+add_task(function asyncCleanup() {
+  yield resetCustomization();
+});