Bug 1210430 - Use Promise.jsm to make beta doorhanger work again;r=jsantell, a=lizzard
authorBrian Grinstead <bgrinstead@mozilla.com>
Thu, 01 Oct 2015 13:26:53 -0700
changeset 606978 9872ab4aa51149d870ee97be5c090d7b12d279fe
parent 606977 4d84411677226f6c48e7f688acd8ba93bf2e3632
child 606979 3fb257ea7183c55c314267357fd3473bbc0d948f
push id93006
push userjyavenard@mozilla.com
push dateThu, 15 Oct 2015 05:15:33 +0000
treeherdertry@45ea2a01301e [default view] [failures only]
reviewersjsantell, lizzard
bugs1210430
milestone43.0a2
Bug 1210430 - Use Promise.jsm to make beta doorhanger work again;r=jsantell, a=lizzard
devtools/client/shared/doorhanger.js
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/doorhanger.js
@@ -0,0 +1,160 @@
+/* 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 { Ci, Cc } = require("chrome");
+const { Services } = require("resource://gre/modules/Services.jsm");
+const { DOMHelpers } = require("resource:///modules/devtools/client/shared/DOMHelpers.jsm");
+const { Task } = require("resource://gre/modules/Task.jsm");
+const { Promise } = require("resource://gre/modules/Promise.jsm");
+const { setTimeout } = require("sdk/timers");
+const { getMostRecentBrowserWindow } = require("sdk/window/utils");
+
+const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const DEV_EDITION_PROMO_URL = "chrome://devtools/content/framework/dev-edition-promo/dev-edition-promo.xul";
+const DEV_EDITION_PROMO_ENABLED_PREF = "devtools.devedition.promo.enabled";
+const DEV_EDITION_PROMO_SHOWN_PREF = "devtools.devedition.promo.shown";
+const DEV_EDITION_PROMO_URL_PREF = "devtools.devedition.promo.url";
+const LOCALE = Cc["@mozilla.org/chrome/chrome-registry;1"]
+               .getService(Ci.nsIXULChromeRegistry)
+               .getSelectedLocale("global");
+
+/**
+ * Only show Dev Edition promo if it's enabled (beta channel),
+ * if it has not been shown before, and it's a locale build
+ * for `en-US`
+ */
+function shouldDevEditionPromoShow () {
+  return Services.prefs.getBoolPref(DEV_EDITION_PROMO_ENABLED_PREF) &&
+         !Services.prefs.getBoolPref(DEV_EDITION_PROMO_SHOWN_PREF) &&
+         LOCALE === "en-US";
+}
+
+var TYPES = {
+  // The Developer Edition promo doorhanger, called by
+  // opening the toolbox, browser console, WebIDE, or responsive design mode
+  // in Beta releases. Only displayed once per profile.
+  deveditionpromo: {
+    predicate: shouldDevEditionPromoShow,
+    success: () => Services.prefs.setBoolPref(DEV_EDITION_PROMO_SHOWN_PREF, true),
+    action: () => {
+      let url = Services.prefs.getCharPref(DEV_EDITION_PROMO_URL_PREF);
+      getGBrowser().selectedTab = getGBrowser().addTab(url);
+    },
+    url: DEV_EDITION_PROMO_URL
+  }
+};
+
+var panelAttrs = {
+  orient: "vertical",
+  hidden: "false",
+  consumeoutsideclicks: "true",
+  noautofocus: "true",
+  align: "start",
+  role: "alert"
+};
+
+/**
+ * Helper to call a doorhanger, defined in `TYPES`, with defined conditions,
+ * success handlers and loads its own XUL in a frame. Takes an object with
+ * several properties:
+ *
+ * @param {XULWindow} window
+ *        The window that should house the doorhanger.
+ * @param {String} type
+ *        The type of doorhanger to be displayed is, using the `TYPES` definition.
+ * @param {String} selector
+ *        The selector that the doorhanger should be appended to within `window`.
+ *        Defaults to a XUL Document's `window` element.
+ */
+exports.showDoorhanger = Task.async(function *({ window, type, anchor }) {
+  let { predicate, success, url, action } = TYPES[type];
+  // Abort if predicate fails
+  if (!predicate()) {
+    return;
+  }
+
+  // Call success function to set preferences/cleanup immediately,
+  // so if triggered multiple times, only happens once (Windows/Linux)
+  success();
+
+  // Wait 200ms to prevent flickering where the popup is displayed
+  // before the underlying window (Windows 7, 64bit)
+  yield wait(200);
+
+  let document = window.document;
+
+  let panel = document.createElementNS(XULNS, "panel");
+  let frame = document.createElementNS(XULNS, "iframe");
+  let parentEl = document.querySelector("window");
+
+  frame.setAttribute("src", url);
+  let close = () => parentEl.removeChild(panel);
+
+  setDoorhangerStyle(panel, frame);
+
+  panel.appendChild(frame);
+  parentEl.appendChild(panel);
+
+  yield onFrameLoad(frame);
+
+  panel.openPopup(anchor);
+
+  let closeBtn = frame.contentDocument.querySelector("#close");
+  if (closeBtn) {
+    closeBtn.addEventListener("click", close);
+  }
+
+  let goBtn = frame.contentDocument.querySelector("#go");
+  if (goBtn) {
+    goBtn.addEventListener("click", () => {
+      if (action) {
+        action();
+      }
+      close();
+    });
+  }
+});
+
+function setDoorhangerStyle (panel, frame) {
+  Object.keys(panelAttrs).forEach(prop => panel.setAttribute(prop, panelAttrs[prop]));
+  panel.style.margin = "20px";
+  panel.style.borderRadius = "5px";
+  panel.style.border = "none";
+  panel.style.MozAppearance = "none";
+  panel.style.backgroundColor = "transparent";
+
+  frame.style.borderRadius = "5px";
+  frame.setAttribute("flex", "1");
+  frame.setAttribute("width", "450");
+  frame.setAttribute("height", "179");
+}
+
+function onFrameLoad (frame) {
+  let { resolve, promise } = Promise.defer();
+
+  if (frame.contentWindow) {
+    let domHelper = new DOMHelpers(frame.contentWindow);
+    domHelper.onceDOMReady(resolve);
+  } else {
+    let callback = () => {
+      frame.removeEventListener("DOMContentLoaded", callback);
+      resolve();
+    }
+    frame.addEventListener("DOMContentLoaded", callback);
+  }
+
+  return promise;
+}
+
+function getGBrowser () {
+  return getMostRecentBrowserWindow().gBrowser;
+}
+
+function wait (n) {
+  let { resolve, promise } = Promise.defer();
+  setTimeout(resolve, n);
+  return promise;
+}