Bug 1078539 - Add a doorhanger widget for the developer edition notification to browser console, developer tools, webide and responsive design mode. r=jryans
authorJordan Santell <jsantell@gmail.com>
Wed, 22 Oct 2014 17:18:31 -0700
changeset 214471 3e45eeacb23162e7c1ce1f08f4f1de334fce0c80
parent 214470 c2ced33d9744df346e1b6b50c6710cf03cb4e619
child 214472 5caf99f630593a318543c943c29aabb8dafffab6
push id51494
push userkwierso@gmail.com
push dateFri, 07 Nov 2014 03:08:20 +0000
treeherdermozilla-inbound@c4b831696f15 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjryans
bugs1078539
milestone36.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 1078539 - Add a doorhanger widget for the developer edition notification to browser console, developer tools, webide and responsive design mode. r=jryans
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
@@ -1301,16 +1301,27 @@ pref("services.sync.prefs.sync.xpinstall
 #ifdef MOZ_DEV_EDITION
 pref("browser.devedition.theme.enabled", true);
 pref("browser.devedition.theme.showCustomizeButton", true);
 #else
 pref("browser.devedition.theme.enabled", false);
 pref("browser.devedition.theme.showCustomizeButton", false);
 #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");
 
@@ -95,16 +96,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);
@@ -119,16 +121,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.
@@ -1644,10 +1648,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
@@ -98,16 +98,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,149 @@
+/* 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;
+  }
+
+  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();
+    });
+  }
+
+  // Call success function to set preferences, etc.
+  success();
+});
+
+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;
+}
--- 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
@@ -18,16 +18,17 @@ const {AppManager} = require("devtools/w
 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 utils = require("devtools/webide/utils");
 const Telemetry = require("devtools/shared/telemetry");
 const {RuntimeScanners, WiFiScanner} = require("devtools/webide/runtimes");
+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);
@@ -120,16 +121,21 @@ let UI = {
     // if a modification happened, it happened when the window was
     // not focused.
     if (AppManager.selectedProject &&
         AppManager.selectedProject.type != "mainProcess" &&
         AppManager.selectedProject.type != "runtimeApp" &&
         AppManager.selectedProject.type != "tab") {
       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();
         this.autoConnectRuntime();