Bug 1078539 - Add a doorhanger widget for the developer edition notification to browser console, developer tools, webide and responsive design mode. r=jryans, a=lmandel
authorJordan Santell <jsantell@gmail.com>
Fri, 07 Nov 2014 12:19:00 -0500
changeset 226025 e7f8aa528841
parent 226024 9cd882996cbe
child 226026 7dfbe52d1a2b
push id4111
push userryanvm@gmail.com
push date2014-11-11 15:36 +0000
treeherdermozilla-beta@e7f8aa528841 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjryans, lmandel
bugs1078539
milestone34.0
Bug 1078539 - Add a doorhanger widget for the developer edition notification to browser console, developer tools, webide and responsive design mode. r=jryans, a=lmandel
browser/app/profile/firefox.js
browser/devtools/framework/dev-edition-promo/dev-edition-logo.png
browser/devtools/framework/dev-edition-promo/dev-edition-promo.css
browser/devtools/framework/dev-edition-promo/dev-edition-promo.xul
browser/devtools/framework/toolbox.js
browser/devtools/jar.mn
browser/devtools/responsivedesign/responsivedesign.jsm
browser/devtools/shared/doorhanger.js
browser/devtools/shared/moz.build
browser/devtools/webconsole/hudservice.js
browser/devtools/webide/content/webide.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1270,16 +1270,27 @@ pref("services.sync.prefs.sync.security.
 pref("services.sync.prefs.sync.security.default_personal_cert", true);
 pref("services.sync.prefs.sync.security.tls.version.min", true);
 pref("services.sync.prefs.sync.security.tls.version.max", true);
 pref("services.sync.prefs.sync.signon.rememberSignons", true);
 pref("services.sync.prefs.sync.spellchecker.dictionary", true);
 pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
 #endif
 
+// Developer edition promo preferences
+pref("devtools.devedition.promo.shown", false);
+pref("devtools.devedition.promo.url", "https://mozilla.org/firefox/developer");
+
+// Only potentially show in beta release
+#ifdef MOZ_UPDATE_CHANNEL == beta
+  pref("devtools.devedition.promo.enabled", true);
+#else
+  pref("devtools.devedition.promo.enabled", false);
+#endif
+
 // Disable the error console
 pref("devtools.errorconsole.enabled", false);
 
 // Developer toolbar and GCLI preferences
 pref("devtools.toolbar.enabled", true);
 pref("devtools.toolbar.visible", false);
 pref("devtools.commands.dir", "");
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4b90768d2d172703a185da55bf3ab9e619ca6a63
GIT binary patch
literal 6764
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
O0000<MNUMnLSTaEL<B(q
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/dev-edition-promo/dev-edition-promo.css
@@ -0,0 +1,94 @@
+/* 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/. */
+
+window {
+  -moz-appearance: none;
+  background-color: transparent;
+}
+
+#doorhanger-container {
+  width: 450px;
+}
+
+#top-panel {
+  padding: 20px;
+  background: #343c45; /* toolbars */
+  color: #8fa1b2; /* body text */
+/*
+ * Sloppy preprocessing since UNIX_BUT_NOT_MAC is only defined
+ * in `browser/app/profile/firefox.js`, which this file cannot
+ * depend on. Must style font-size to target linux.
+ */
+%ifdef XP_UNIX
+%ifndef XP_MACOSX
+  font-size: 13px;
+%else
+  font-size: 15px;
+%endif
+%else
+  font-size: 15px;
+%endif
+  line-height: 19px;
+  min-height: 100px;
+}
+
+#top-panel h1 {
+  font-weight: bold;
+  font-family: Open Sans, sans-serif;
+  font-size: 1.1em;
+}
+
+#top-panel p {
+  font-family: Open Sans, sans-serif;
+  font-size: 0.9em;
+  width: 300px;
+  display: block;
+  margin: 5px 0px 0px 0px;
+}
+
+#icon {
+  background-image: url("chrome://browser/content/devtools/framework/dev-edition-logo.png");
+  background-size: 64px 64px;
+  background-repeat: no-repeat;
+  width: 64px;
+  height: 64px;
+  margin-right: 20px;
+}
+
+#lower-panel {
+  padding: 20px;
+  background-color: #252c33; /* tab toolbars */
+  min-height: 75px;
+  border-top: 1px solid #292e33; /* text high contrast (light) */
+}
+
+#button-container {
+  margin: auto 20px;
+}
+
+#button-container button {
+  font: message-box !important;
+  font-size: 16px !important;
+  cursor: pointer;
+  width: 125px;
+  opacity: 1;
+  position: static;
+  -moz-appearance: none;
+  border-radius: 5px;
+  height: 30px;
+  width: 450px;
+  /* Override embossed borders on Windows/Linux */
+  border: none;
+}
+
+#close {
+  background-color: transparent;
+  color: #8fa1b2; /* body text */
+}
+
+#go {
+  margin-left: 100px;
+  background-color: #70bf53; /* green */
+  color: #f5f7fa; /* selection text color */
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/dev-edition-promo/dev-edition-promo.xul
@@ -0,0 +1,36 @@
+<?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/. -->
+<!DOCTYPE window [
+<!ENTITY % toolboxDTD SYSTEM "chrome://browser/locale/devtools/toolbox.dtd" >
+ %toolboxDTD;
+]>
+<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
+<?xml-stylesheet rel="stylesheet" href="chrome://browser/content/devtools/framework/dev-edition-promo.css" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="dev-edition-promo">
+  <vbox id="doorhanger-container">
+    <hbox flex="1" id="top-panel">
+      <image id="icon" />
+      <vbox id="info">
+        <h1>Using Developer Tools in your browser?</h1>
+        <p>Download Firefox Developer Edition, our first browser made just for you.</p>
+      </vbox>
+    </hbox>
+    <hbox id="lower-panel" flex="1">
+      <hbox id="button-container" flex="1">
+        <button id="close"
+                flex="1"
+                standalone="true"
+                label="No thanks">
+        </button>
+        <button id="go"
+                flex="1"
+                standalone="true"
+                label="Learn more »">
+        </button>
+      </hbox>
+    </hbox>
+  </vbox>
+</window>
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -12,16 +12,17 @@ const MIN_ZOOM = 0.5;
 const MAX_ZOOM = 2;
 
 let {Cc, Ci, Cu} = require("chrome");
 let {Promise: promise} = require("resource://gre/modules/Promise.jsm");
 let EventEmitter = require("devtools/toolkit/event-emitter");
 let Telemetry = require("devtools/shared/telemetry");
 let {getHighlighterUtils} = require("devtools/framework/toolbox-highlighter-utils");
 let HUDService = require("devtools/webconsole/hudservice");
+let {showDoorhanger} = require("devtools/shared/doorhanger");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
 Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
@@ -94,16 +95,17 @@ function Toolbox(target, selectedTool, h
   this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this);
   this.destroy = this.destroy.bind(this);
   this.highlighterUtils = getHighlighterUtils(this);
   this._highlighterReady = this._highlighterReady.bind(this);
   this._highlighterHidden = this._highlighterHidden.bind(this);
   this._prefChanged = this._prefChanged.bind(this);
   this._saveSplitConsoleHeight = this._saveSplitConsoleHeight.bind(this);
   this._onFocus = this._onFocus.bind(this);
+  this._showDevEditionPromo = this._showDevEditionPromo.bind(this);
 
   this._target.on("close", this.destroy);
 
   if (!hostType) {
     hostType = Services.prefs.getCharPref(this._prefs.LAST_HOST);
   }
   if (!selectedTool) {
     selectedTool = Services.prefs.getCharPref(this._prefs.LAST_TOOL);
@@ -118,16 +120,18 @@ function Toolbox(target, selectedTool, h
   EventEmitter.decorate(this);
 
   this._target.on("navigate", this._refreshHostTitle);
   this._target.on("frame-update", this._updateFrames);
 
   this.on("host-changed", this._refreshHostTitle);
   this.on("select", this._refreshHostTitle);
 
+  this.on("ready", this._showDevEditionPromo);
+
   gDevTools.on("tool-registered", this._toolRegistered);
   gDevTools.on("tool-unregistered", this._toolUnregistered);
 }
 exports.Toolbox = Toolbox;
 
 /**
  * The toolbox can be 'hosted' either embedded in a browser window
  * or in a separate window.
@@ -1555,10 +1559,23 @@ Toolbox.prototype = {
   },
 
   _highlighterReady: function() {
     this.emit("highlighter-ready");
   },
 
   _highlighterHidden: function() {
     this.emit("highlighter-hide");
+  },
+
+  /**
+   * For displaying the promotional Doorhanger on first opening of
+   * the developer tools, promoting the Developer Edition.
+   */
+  _showDevEditionPromo: function() {
+    // Do not display in browser toolbox
+    if (this.target.chrome) {
+      return;
+    }
+    let window = this.frame.contentWindow;
+    showDoorhanger({ window, type: "deveditionpromo" });
   }
 };
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -89,16 +89,19 @@ browser.jar:
     content/browser/devtools/commandline/commands-index.js             (commandline/commands-index.js)
     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)
     content/browser/devtools/framework/toolbox-process-window.xul      (framework/toolbox-process-window.xul)
     content/browser/devtools/framework/toolbox-process-window.js       (framework/toolbox-process-window.js)
+    content/browser/devtools/framework/dev-edition-promo.xul           (framework/dev-edition-promo/dev-edition-promo.xul)
+*   content/browser/devtools/framework/dev-edition-promo.css           (framework/dev-edition-promo/dev-edition-promo.css)
+    content/browser/devtools/framework/dev-edition-logo.png            (framework/dev-edition-promo/dev-edition-logo.png)
     content/browser/devtools/inspector/inspector.xul                   (inspector/inspector.xul)
     content/browser/devtools/inspector/inspector.css                   (inspector/inspector.css)
     content/browser/devtools/connect.xhtml                             (framework/connect/connect.xhtml)
     content/browser/devtools/connect.css                               (framework/connect/connect.css)
     content/browser/devtools/connect.js                                (framework/connect/connect.js)
     content/browser/devtools/app-manager/template.js                   (app-manager/content/template.js)
     content/browser/devtools/app-manager/utils.js                      (app-manager/content/utils.js)
     content/browser/devtools/app-manager/connection-footer.js          (app-manager/content/connection-footer.js)
--- a/browser/devtools/responsivedesign/responsivedesign.jsm
+++ b/browser/devtools/responsivedesign/responsivedesign.jsm
@@ -12,16 +12,17 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 Cu.import("resource:///modules/devtools/FloatingScrollbars.jsm");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
                                   "resource://gre/modules/SystemAppProxy.jsm");
 
 var require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
+let {showDoorhanger} = require("devtools/shared/doorhanger");
 let {TouchEventHandler} = require("devtools/touch-events");
 
 this.EXPORTED_SYMBOLS = ["ResponsiveUIManager"];
 
 const MIN_WIDTH = 50;
 const MIN_HEIGHT = 50;
 
 const MAX_WIDTH = 10000;
@@ -212,16 +213,23 @@ function ResponsiveUI(aWindow, aTab)
 
   if (this.browser.contentWindow.document &&
       this.browser.contentWindow.document.readyState == "complete") {
     this.onPageLoad();
   }
 
   // E10S: We should be using target here. See bug 1028234
   ResponsiveUIManager.emit("on", { tab: this.tab });
+
+  // Hook to display promotional Developer Edition doorhanger. Only displayed once.
+  showDoorhanger({
+    window: this.mainWindow,
+    type: "deveditionpromo",
+    anchor: this.chromeDoc.querySelector("#content")
+  });
 }
 
 ResponsiveUI.prototype = {
   _transitionsEnabled: true,
   get transitionsEnabled() this._transitionsEnabled,
   set transitionsEnabled(aValue) {
     this._transitionsEnabled = aValue;
     if (aValue && !this._resizing && this.stack.hasAttribute("responsivemode")) {
new file mode 100644
--- /dev/null
+++ b/browser/devtools/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/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://browser/content/devtools/framework/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";
+}
+
+let 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
+  }
+};
+
+let 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;
+}
--- a/browser/devtools/shared/moz.build
+++ b/browser/devtools/shared/moz.build
@@ -28,16 +28,17 @@ EXTRA_JS_MODULES.devtools += [
     'widgets/VariablesView.jsm',
     'widgets/VariablesViewController.jsm',
     'widgets/ViewHelpers.jsm',
 ]
 
 EXTRA_JS_MODULES.devtools.shared += [
     'autocomplete-popup.js',
     'd3.js',
+    'doorhanger.js',
     'frame-script-utils.js',
     'inplace-editor.js',
     'observable-object.js',
     'telemetry.js',
     'theme-switching.js',
     'undo.js',
 ]
 
--- a/browser/devtools/webconsole/hudservice.js
+++ b/browser/devtools/webconsole/hudservice.js
@@ -14,16 +14,17 @@ let Heritage = require("sdk/core/heritag
 loader.lazyGetter(this, "Telemetry", () => require("devtools/shared/telemetry"));
 loader.lazyGetter(this, "WebConsoleFrame", () => require("devtools/webconsole/webconsole").WebConsoleFrame);
 loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise");
 loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
 loader.lazyImporter(this, "devtools", "resource://gre/modules/devtools/Loader.jsm");
 loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
 loader.lazyImporter(this, "DebuggerServer", "resource://gre/modules/devtools/dbg-server.jsm");
 loader.lazyImporter(this, "DebuggerClient", "resource://gre/modules/devtools/dbg-client.jsm");
+loader.lazyGetter(this, "showDoorhanger", () => require("devtools/shared/doorhanger").showDoorhanger);
 
 const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
 let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
 
 const BROWSER_CONSOLE_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
 
 // The preference prefix for all of the Browser Console filters.
 const BROWSER_CONSOLE_FILTER_PREFS_PREFIX = "devtools.browserconsole.filter.";
@@ -699,25 +700,32 @@ BrowserConsole.prototype = Heritage.exte
     this.ui._filterPrefsPrefix = BROWSER_CONSOLE_FILTER_PREFS_PREFIX;
 
     let window = this.iframeWindow;
 
     // Make sure that the closing of the Browser Console window destroys this
     // instance.
     let onClose = () => {
       window.removeEventListener("unload", onClose);
+      window.removeEventListener("focus", onFocus);
       this.destroy();
     };
     window.addEventListener("unload", onClose);
 
     // Make sure Ctrl-W closes the Browser Console window.
     window.document.getElementById("cmd_close").removeAttribute("disabled");
 
     this._telemetry.toolOpened("browserconsole");
 
+    // Create an onFocus handler just to display the dev edition promo.
+    // This is to prevent race conditions in some environments.
+    // Hook to display promotional Developer Edition doorhanger. Only displayed once.
+    let onFocus = () => showDoorhanger({ window, type: "deveditionpromo" });
+    window.addEventListener("focus", onFocus);
+
     this._bc_init = this.$init();
     return this._bc_init;
   },
 
   $destroy: WebConsole.prototype.destroy,
 
   /**
    * Destroy the object.
--- a/browser/devtools/webide/content/webide.js
+++ b/browser/devtools/webide/content/webide.js
@@ -16,16 +16,17 @@ const {Services} = Cu.import("resource:/
 const {AppProjects} = require("devtools/app-manager/app-projects");
 const {Connection} = require("devtools/client/connection-manager");
 const {AppManager} = require("devtools/webide/app-manager");
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const ProjectEditor = require("projecteditor/projecteditor");
 const {Devices} = Cu.import("resource://gre/modules/devtools/Devices.jsm");
 const {GetAvailableAddons} = require("devtools/webide/addons");
 const {GetTemplatesJSON, GetAddonsJSON} = require("devtools/webide/remote-resources");
+const {showDoorhanger} = require("devtools/shared/doorhanger");
 
 const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/webide.properties");
 
 const HTML = "http://www.w3.org/1999/xhtml";
 const HELP_URL = "https://developer.mozilla.org/docs/Tools/WebIDE/Troubleshooting";
 
 // download template index early
 GetTemplatesJSON(true);
@@ -112,16 +113,21 @@ let UI = {
     // we need to validate the project regularly. Let's assume that
     // if a modification happened, it happened when the window was
     // not focused.
     if (AppManager.selectedProject &&
         AppManager.selectedProject.type != "mainProcess" &&
         AppManager.selectedProject.type != "runtimeApp") {
       AppManager.validateProject(AppManager.selectedProject);
     }
+
+    // Hook to display promotional Developer Edition doorhanger. Only displayed once.
+    // Hooked into the `onfocus` event because sometimes does not work
+    // when run at the end of `init`. ¯\(°_o)/¯
+    showDoorhanger({ window, type: "deveditionpromo", anchor: document.querySelector("#deck") });
   },
 
   appManagerUpdate: function(event, what, details) {
     // Got a message from app-manager.js
     switch (what) {
       case "runtimelist":
         this.updateRuntimeList();
         break;