2014-10-23 04:18:31 +04:00
|
|
|
/* 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");
|
2016-02-27 15:51:10 +03:00
|
|
|
const Services = require("Services");
|
2015-10-14 02:18:43 +03:00
|
|
|
const { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
|
2016-05-17 22:07:55 +03:00
|
|
|
const { Task } = require("devtools/shared/task");
|
2015-10-01 23:26:53 +03:00
|
|
|
const { Promise } = require("resource://gre/modules/Promise.jsm");
|
2014-10-23 04:18:31 +04:00
|
|
|
const { getMostRecentBrowserWindow } = require("sdk/window/utils");
|
|
|
|
|
|
|
|
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
2015-09-21 20:02:37 +03:00
|
|
|
const DEV_EDITION_PROMO_URL = "chrome://devtools/content/framework/dev-edition-promo/dev-edition-promo.xul";
|
2014-10-23 04:18:31 +04:00
|
|
|
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`
|
|
|
|
*/
|
2016-05-17 21:25:54 +03:00
|
|
|
function shouldDevEditionPromoShow() {
|
2014-10-23 04:18:31 +04:00
|
|
|
return Services.prefs.getBoolPref(DEV_EDITION_PROMO_ENABLED_PREF) &&
|
|
|
|
!Services.prefs.getBoolPref(DEV_EDITION_PROMO_SHOWN_PREF) &&
|
|
|
|
LOCALE === "en-US";
|
|
|
|
}
|
|
|
|
|
2015-09-15 21:19:45 +03:00
|
|
|
var TYPES = {
|
2014-10-23 04:18:31 +04:00
|
|
|
// 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,
|
2016-05-19 17:35:45 +03:00
|
|
|
success: () => {
|
|
|
|
return Services.prefs.setBoolPref(DEV_EDITION_PROMO_SHOWN_PREF, true);
|
|
|
|
},
|
2014-10-23 04:18:31 +04:00
|
|
|
action: () => {
|
|
|
|
let url = Services.prefs.getCharPref(DEV_EDITION_PROMO_URL_PREF);
|
|
|
|
getGBrowser().selectedTab = getGBrowser().addTab(url);
|
|
|
|
},
|
|
|
|
url: DEV_EDITION_PROMO_URL
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-09-15 21:19:45 +03:00
|
|
|
var panelAttrs = {
|
2014-10-23 04:18:31 +04:00
|
|
|
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
|
2016-05-19 17:35:45 +03:00
|
|
|
* The type of doorhanger to be displayed is, using the `TYPES`
|
|
|
|
* definition.
|
2014-10-23 04:18:31 +04:00
|
|
|
* @param {String} selector
|
2016-05-19 17:35:45 +03:00
|
|
|
* The selector that the doorhanger should be appended to within
|
|
|
|
* `window`. Defaults to a XUL Document's `window` element.
|
2014-10-23 04:18:31 +04:00
|
|
|
*/
|
2016-05-17 21:25:54 +03:00
|
|
|
exports.showDoorhanger = Task.async(function* ({ window, type, anchor }) {
|
2014-10-23 04:18:31 +04:00
|
|
|
let { predicate, success, url, action } = TYPES[type];
|
|
|
|
// Abort if predicate fails
|
|
|
|
if (!predicate()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-07 20:19:00 +03:00
|
|
|
// 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);
|
|
|
|
|
2014-10-23 04:18:31 +04:00
|
|
|
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();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-05-17 21:25:54 +03:00
|
|
|
function setDoorhangerStyle(panel, frame) {
|
2016-05-19 17:35:45 +03:00
|
|
|
Object.keys(panelAttrs).forEach(prop => {
|
|
|
|
return panel.setAttribute(prop, panelAttrs[prop]);
|
|
|
|
});
|
2014-10-23 04:18:31 +04:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2016-05-17 21:25:54 +03:00
|
|
|
function onFrameLoad(frame) {
|
2014-10-23 04:18:31 +04:00
|
|
|
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();
|
2016-05-17 21:25:54 +03:00
|
|
|
};
|
2014-10-23 04:18:31 +04:00
|
|
|
frame.addEventListener("DOMContentLoaded", callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
return promise;
|
|
|
|
}
|
|
|
|
|
2016-05-17 21:25:54 +03:00
|
|
|
function getGBrowser() {
|
2014-10-23 04:18:31 +04:00
|
|
|
return getMostRecentBrowserWindow().gBrowser;
|
|
|
|
}
|
2014-11-07 20:19:00 +03:00
|
|
|
|
2016-05-17 21:25:54 +03:00
|
|
|
function wait(n) {
|
2014-11-07 20:19:00 +03:00
|
|
|
let { resolve, promise } = Promise.defer();
|
|
|
|
setTimeout(resolve, n);
|
|
|
|
return promise;
|
|
|
|
}
|