зеркало из https://github.com/mozilla/gecko-dev.git
merge fx-team to mozilla-central
This commit is contained in:
Коммит
a3284daeb0
|
@ -233,6 +233,12 @@ pref("xpinstall.whitelist.add.180", "marketplace.firefox.com");
|
||||||
|
|
||||||
pref("lightweightThemes.update.enabled", true);
|
pref("lightweightThemes.update.enabled", true);
|
||||||
|
|
||||||
|
// UI tour experience.
|
||||||
|
pref("browser.uitour.enabled", true);
|
||||||
|
pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/themes/");
|
||||||
|
pref("browser.uitour.pinnedTabUrl", "https://support.mozilla.org/%LOCALE%/kb/pinned-tabs-keep-favorite-websites-open");
|
||||||
|
pref("browser.uitour.whitelist.add.260", "www.mozilla.org,support.mozilla.org");
|
||||||
|
|
||||||
pref("keyword.enabled", true);
|
pref("keyword.enabled", true);
|
||||||
|
|
||||||
pref("general.useragent.locale", "@AB_CD@");
|
pref("general.useragent.locale", "@AB_CD@");
|
||||||
|
@ -660,6 +666,8 @@ pref("plugins.update.notifyUser", false);
|
||||||
|
|
||||||
pref("plugins.click_to_play", true);
|
pref("plugins.click_to_play", true);
|
||||||
|
|
||||||
|
pref("plugins.clickToActivateInfo.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/clicktoplay");
|
||||||
|
|
||||||
// let all plugins except Flash default to click-to-play
|
// let all plugins except Flash default to click-to-play
|
||||||
pref("plugin.default.state", 1);
|
pref("plugin.default.state", 1);
|
||||||
pref("plugin.state.flash", 2);
|
pref("plugin.state.flash", 2);
|
||||||
|
|
|
@ -704,6 +704,9 @@ var gPluginHandler = {
|
||||||
else if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
|
else if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
|
||||||
url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
|
url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
url = Services.urlFormatter.formatURLPref("plugins.clickToActivateInfo.url");
|
||||||
|
}
|
||||||
pluginInfo.detailsLink = url;
|
pluginInfo.detailsLink = url;
|
||||||
|
|
||||||
centerActions.push(pluginInfo);
|
centerActions.push(pluginInfo);
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
<window id="main-window"
|
<window id="main-window"
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
onload="gBrowserInit.onLoad()" onunload="gBrowserInit.onUnload()" onclose="return WindowIsClosing();"
|
onload="gBrowserInit.onLoad()" onunload="gBrowserInit.onUnload()" onclose="return WindowIsClosing();"
|
||||||
title="&mainWindow.title;@PRE_RELEASE_SUFFIX@"
|
title="&mainWindow.title;@PRE_RELEASE_SUFFIX@"
|
||||||
|
@ -183,6 +184,22 @@
|
||||||
</hbox>
|
</hbox>
|
||||||
</panel>
|
</panel>
|
||||||
|
|
||||||
|
<!-- UI tour experience -->
|
||||||
|
<panel id="UITourTooltip"
|
||||||
|
type="arrow"
|
||||||
|
hidden="true"
|
||||||
|
consumeoutsideclicks="false"
|
||||||
|
noautofocus="true"
|
||||||
|
align="start"
|
||||||
|
orient="vertical"
|
||||||
|
role="alert">
|
||||||
|
<label id="UITourTooltipTitle" flex="1"/>
|
||||||
|
<description id="UITourTooltipDescription" flex="1"/>
|
||||||
|
</panel>
|
||||||
|
<html:div id="UITourHighlightContainer" style="position:relative">
|
||||||
|
<html:div id="UITourHighlight"></html:div>
|
||||||
|
</html:div>
|
||||||
|
|
||||||
<panel id="socialActivatedNotification"
|
<panel id="socialActivatedNotification"
|
||||||
type="arrow"
|
type="arrow"
|
||||||
hidden="true"
|
hidden="true"
|
||||||
|
|
|
@ -15,6 +15,8 @@ XPCOMUtils.defineLazyModuleGetter(this,
|
||||||
"InsecurePasswordUtils", "resource://gre/modules/InsecurePasswordUtils.jsm");
|
"InsecurePasswordUtils", "resource://gre/modules/InsecurePasswordUtils.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
|
||||||
|
"resource:///modules/UITour.jsm");
|
||||||
|
|
||||||
// Creates a new nsIURI object.
|
// Creates a new nsIURI object.
|
||||||
function makeURI(uri, originCharset, baseURI) {
|
function makeURI(uri, originCharset, baseURI) {
|
||||||
|
@ -49,6 +51,15 @@ if (Services.prefs.getBoolPref("browser.tabs.remote")) {
|
||||||
addEventListener("blur", function(event) {
|
addEventListener("blur", function(event) {
|
||||||
LoginManagerContent.onUsernameInput(event);
|
LoginManagerContent.onUsernameInput(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addEventListener("mozUITour", function(event) {
|
||||||
|
if (!Services.prefs.getBoolPref("browser.uitour.enabled"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let handled = UITour.onPageEvent(event);
|
||||||
|
if (handled)
|
||||||
|
addEventListener("pagehide", UITour);
|
||||||
|
}, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let AboutHomeListener = {
|
let AboutHomeListener = {
|
||||||
|
|
|
@ -472,6 +472,7 @@ BrowserGlue.prototype = {
|
||||||
ShumwayUtils.init();
|
ShumwayUtils.init();
|
||||||
webrtcUI.init();
|
webrtcUI.init();
|
||||||
AboutHome.init();
|
AboutHome.init();
|
||||||
|
SessionStore.init();
|
||||||
|
|
||||||
if (Services.prefs.getBoolPref("browser.tabs.remote"))
|
if (Services.prefs.getBoolPref("browser.tabs.remote"))
|
||||||
ContentClick.init();
|
ContentClick.init();
|
||||||
|
@ -612,7 +613,6 @@ BrowserGlue.prototype = {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SessionStore.init(aWindow);
|
|
||||||
this._trackSlowStartup();
|
this._trackSlowStartup();
|
||||||
|
|
||||||
// Offer to reset a user's profile if it hasn't been used for 60 days.
|
// Offer to reset a user's profile if it hasn't been used for 60 days.
|
||||||
|
|
|
@ -156,8 +156,8 @@ this.SessionStore = {
|
||||||
SessionStoreInternal.canRestoreLastSession = val;
|
SessionStoreInternal.canRestoreLastSession = val;
|
||||||
},
|
},
|
||||||
|
|
||||||
init: function ss_init(aWindow) {
|
init: function ss_init() {
|
||||||
SessionStoreInternal.init(aWindow);
|
SessionStoreInternal.init();
|
||||||
},
|
},
|
||||||
|
|
||||||
getBrowserState: function ss_getBrowserState() {
|
getBrowserState: function ss_getBrowserState() {
|
||||||
|
@ -368,15 +368,11 @@ let SessionStoreInternal = {
|
||||||
/**
|
/**
|
||||||
* Initialize the sessionstore service.
|
* Initialize the sessionstore service.
|
||||||
*/
|
*/
|
||||||
init: function (aWindow) {
|
init: function () {
|
||||||
if (this._initialized) {
|
if (this._initialized) {
|
||||||
throw new Error("SessionStore.init() must only be called once!");
|
throw new Error("SessionStore.init() must only be called once!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aWindow) {
|
|
||||||
throw new Error("SessionStore.init() must be called with a valid window.");
|
|
||||||
}
|
|
||||||
|
|
||||||
this._disabledForMultiProcess = Services.prefs.getBoolPref("browser.tabs.remote");
|
this._disabledForMultiProcess = Services.prefs.getBoolPref("browser.tabs.remote");
|
||||||
if (this._disabledForMultiProcess) {
|
if (this._disabledForMultiProcess) {
|
||||||
this._deferredInitialized.resolve();
|
this._deferredInitialized.resolve();
|
||||||
|
@ -390,20 +386,6 @@ let SessionStoreInternal = {
|
||||||
|
|
||||||
this._initPrefs();
|
this._initPrefs();
|
||||||
this._initialized = true;
|
this._initialized = true;
|
||||||
|
|
||||||
// Wait until nsISessionStartup has finished reading the session data.
|
|
||||||
gSessionStartup.onceInitialized.then(() => {
|
|
||||||
// Parse session data and start restoring.
|
|
||||||
let initialState = this.initSession();
|
|
||||||
|
|
||||||
// Start tracking the given (initial) browser window.
|
|
||||||
if (!aWindow.closed) {
|
|
||||||
this.onLoad(aWindow, initialState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let everyone know we're done.
|
|
||||||
this._deferredInitialized.resolve();
|
|
||||||
}, Cu.reportError);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
initSession: function ssi_initSession() {
|
initSession: function ssi_initSession() {
|
||||||
|
@ -489,7 +471,6 @@ let SessionStoreInternal = {
|
||||||
this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
|
this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
|
||||||
|
|
||||||
this._performUpgradeBackup();
|
this._performUpgradeBackup();
|
||||||
this._sessionInitialized = true;
|
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
},
|
},
|
||||||
|
@ -876,7 +857,34 @@ let SessionStoreInternal = {
|
||||||
onOpen: function ssi_onOpen(aWindow) {
|
onOpen: function ssi_onOpen(aWindow) {
|
||||||
let onload = () => {
|
let onload = () => {
|
||||||
aWindow.removeEventListener("load", onload);
|
aWindow.removeEventListener("load", onload);
|
||||||
this.onLoad(aWindow);
|
|
||||||
|
if (this._sessionInitialized) {
|
||||||
|
this.onLoad(aWindow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't call this.onLoad since initialization
|
||||||
|
// hasn't completed, so we'll wait until it is done.
|
||||||
|
// Even if additional windows are opened and wait
|
||||||
|
// for initialization as well, the first opened
|
||||||
|
// window should execute first, and this.onLoad
|
||||||
|
// will be called with the initialState.
|
||||||
|
gSessionStartup.onceInitialized.then(() => {
|
||||||
|
if (aWindow.closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._sessionInitialized) {
|
||||||
|
this.onLoad(aWindow);
|
||||||
|
} else {
|
||||||
|
let initialState = this.initSession();
|
||||||
|
this._sessionInitialized = true;
|
||||||
|
this.onLoad(aWindow, initialState);
|
||||||
|
|
||||||
|
// Let everyone know we're done.
|
||||||
|
this._deferredInitialized.resolve();
|
||||||
|
}
|
||||||
|
}, Cu.reportError);
|
||||||
};
|
};
|
||||||
|
|
||||||
aWindow.addEventListener("load", onload);
|
aWindow.addEventListener("load", onload);
|
||||||
|
|
|
@ -1537,6 +1537,7 @@ NetworkDetailsView.prototype = {
|
||||||
}));
|
}));
|
||||||
this._json = new VariablesView($("#response-content-json"),
|
this._json = new VariablesView($("#response-content-json"),
|
||||||
Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
|
Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
|
||||||
|
onlyEnumVisible: true,
|
||||||
searchPlaceholder: L10N.getStr("jsonFilterText")
|
searchPlaceholder: L10N.getStr("jsonFilterText")
|
||||||
}));
|
}));
|
||||||
VariablesViewController.attach(this._json);
|
VariablesViewController.attach(this._json);
|
||||||
|
@ -1874,7 +1875,7 @@ NetworkDetailsView.prototype = {
|
||||||
let sanitizedJSON = aString.replace(jsonpRegex, "");
|
let sanitizedJSON = aString.replace(jsonpRegex, "");
|
||||||
let callbackPadding = aString.match(jsonpRegex);
|
let callbackPadding = aString.match(jsonpRegex);
|
||||||
|
|
||||||
// Make sure this is an valid JSON object first. If so, nicely display
|
// Make sure this is a valid JSON object first. If so, nicely display
|
||||||
// the parsing results in a variables view. Otherwise, simply show
|
// the parsing results in a variables view. Otherwise, simply show
|
||||||
// the contents as plain text.
|
// the contents as plain text.
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -610,66 +610,23 @@ var Scratchpad = {
|
||||||
deferred.resolve(aError);
|
deferred.resolve(aError);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let reject = aReason => deferred.reject(aReason);
|
|
||||||
let objectClient = new ObjectClient(this.debuggerClient, aError);
|
let objectClient = new ObjectClient(this.debuggerClient, aError);
|
||||||
|
objectClient.getPrototypeAndProperties(aResponse => {
|
||||||
// Because properties on Error objects are lazily added, this roundabout
|
if (aResponse.error) {
|
||||||
// way of getting all the properties is required, rather than simply
|
deferred.reject(aResponse);
|
||||||
// using getPrototypeAndProperties. See bug 724768.
|
return;
|
||||||
let names = ["message", "stack", "fileName", "lineNumber"];
|
|
||||||
let promises = names.map(aName => {
|
|
||||||
let deferred = promise.defer();
|
|
||||||
|
|
||||||
objectClient.getProperty(aName, aResponse => {
|
|
||||||
if (aResponse.error) {
|
|
||||||
deferred.reject(aResponse);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
deferred.resolve({
|
|
||||||
name: aName,
|
|
||||||
descriptor: aResponse.descriptor
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
// We also need to use getPrototypeAndProperties to retrieve any
|
|
||||||
// safeGetterValues in case this is a DOM error.
|
|
||||||
let deferred = promise.defer();
|
|
||||||
objectClient.getPrototypeAndProperties(aResponse => {
|
|
||||||
if (aResponse.error) {
|
|
||||||
deferred.reject(aResponse);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
deferred.resolve(aResponse);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
promises.push(deferred.promise);
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.all(promises).then(aProperties => {
|
|
||||||
let error = {};
|
|
||||||
let safeGetters;
|
|
||||||
|
|
||||||
// Combine all the property descriptor/getter values into one object.
|
|
||||||
for (let property of aProperties) {
|
|
||||||
if (property.descriptor) {
|
|
||||||
error[property.name] = property.descriptor.value;
|
|
||||||
}
|
|
||||||
else if (property.safeGetterValues) {
|
|
||||||
safeGetters = property.safeGetterValues;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (safeGetters) {
|
let { ownProperties, safeGetterValues } = aResponse;
|
||||||
for (let key of Object.keys(safeGetters)) {
|
let error = Object.create(null);
|
||||||
if (!error.hasOwnProperty(key)) {
|
|
||||||
error[key] = safeGetters[key].getterValue;
|
// Combine all the property descriptor/getter values into one object.
|
||||||
}
|
for (let key of Object.keys(safeGetterValues)) {
|
||||||
}
|
error[key] = safeGetterValues[key].getterValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let key of Object.keys(ownProperties)) {
|
||||||
|
error[key] = ownProperties[key].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assemble the best possible stack we can given the properties we have.
|
// Assemble the best possible stack we can given the properties we have.
|
||||||
|
@ -693,23 +650,23 @@ var Scratchpad = {
|
||||||
deferred.resolve(error.message + stack);
|
deferred.resolve(error.message + stack);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
objectClient.getDisplayString(aResult => {
|
objectClient.getDisplayString(aResponse => {
|
||||||
if (aResult.error) {
|
if (aResponse.error) {
|
||||||
deferred.reject(aResult);
|
deferred.reject(aResponse);
|
||||||
}
|
}
|
||||||
else if (aResult.displayString.type == "null") {
|
else if (typeof aResponse.displayString == "string") {
|
||||||
deferred.resolve(stack);
|
deferred.resolve(aResponse.displayString + stack);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
deferred.resolve(aResult.displayString + stack);
|
deferred.resolve(stack);
|
||||||
}
|
}
|
||||||
}, reject);
|
});
|
||||||
}
|
}
|
||||||
}, reject);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return deferred.promise.then(aMessage => {
|
return deferred.promise.then(aMessage => {
|
||||||
console.log(aMessage);
|
console.error(aMessage);
|
||||||
this.writeAsComment("Exception: " + aMessage);
|
this.writeAsComment("Exception: " + aMessage);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -43,6 +43,7 @@ function testColorUtils() {
|
||||||
let color = new colorUtils.CssColor(authored);
|
let color = new colorUtils.CssColor(authored);
|
||||||
|
|
||||||
// Check all values.
|
// Check all values.
|
||||||
|
info("Checking values for " + authored);
|
||||||
is(color.name, name, "color.name === name");
|
is(color.name, name, "color.name === name");
|
||||||
is(color.hex, hex, "color.hex === hex");
|
is(color.hex, hex, "color.hex === hex");
|
||||||
is(color.hsl, hsl, "color.hsl === hsl");
|
is(color.hsl, hsl, "color.hsl === hsl");
|
||||||
|
@ -291,14 +292,18 @@ function getTestData() {
|
||||||
{authored: "whitesmoke", name: "whitesmoke", hex: "#F5F5F5", hsl: "hsl(0, 0%, 96%)", rgb: "rgb(245, 245, 245)"},
|
{authored: "whitesmoke", name: "whitesmoke", hex: "#F5F5F5", hsl: "hsl(0, 0%, 96%)", rgb: "rgb(245, 245, 245)"},
|
||||||
{authored: "yellow", name: "yellow", hex: "#FF0", hsl: "hsl(60, 100%, 50%)", rgb: "rgb(255, 255, 0)"},
|
{authored: "yellow", name: "yellow", hex: "#FF0", hsl: "hsl(60, 100%, 50%)", rgb: "rgb(255, 255, 0)"},
|
||||||
{authored: "yellowgreen", name: "yellowgreen", hex: "#9ACD32", hsl: "hsl(79.742, 61%, 50%)", rgb: "rgb(154, 205, 50)"},
|
{authored: "yellowgreen", name: "yellowgreen", hex: "#9ACD32", hsl: "hsl(79.742, 61%, 50%)", rgb: "rgb(154, 205, 50)"},
|
||||||
{authored: "transparent", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"},
|
{authored: "rgba(0, 0, 0, 0)", name: "rgba(0, 0, 0, 0)", hex: "rgba(0, 0, 0, 0)", hsl: "hsla(0, 0%, 0%, 0)", rgb: "rgba(0, 0, 0, 0)"},
|
||||||
{authored: "rgba(0, 0, 0, 0)", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"},
|
{authored: "hsla(0, 0%, 0%, 0)", name: "rgba(0, 0, 0, 0)", hex: "rgba(0, 0, 0, 0)", hsl: "hsla(0, 0%, 0%, 0)", rgb: "rgba(0, 0, 0, 0)"},
|
||||||
{authored: "hsla(0, 0%, 0%, 0)", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"},
|
|
||||||
{authored: "rgba(50, 60, 70, 0.5)", name: "rgba(50, 60, 70, 0.5)", hex: "rgba(50, 60, 70, 0.5)", hsl: "hsla(210, 17%, 24%, 0.5)", rgb: "rgba(50, 60, 70, 0.5)"},
|
{authored: "rgba(50, 60, 70, 0.5)", name: "rgba(50, 60, 70, 0.5)", hex: "rgba(50, 60, 70, 0.5)", hsl: "hsla(210, 17%, 24%, 0.5)", rgb: "rgba(50, 60, 70, 0.5)"},
|
||||||
{authored: "rgba(0, 0, 0, 0.3)", name: "rgba(0, 0, 0, 0.3)", hex: "rgba(0, 0, 0, 0.3)", hsl: "hsla(0, 0%, 0%, 0.3)", rgb: "rgba(0, 0, 0, 0.3)"},
|
{authored: "rgba(0, 0, 0, 0.3)", name: "rgba(0, 0, 0, 0.3)", hex: "rgba(0, 0, 0, 0.3)", hsl: "hsla(0, 0%, 0%, 0.3)", rgb: "rgba(0, 0, 0, 0.3)"},
|
||||||
{authored: "rgba(255, 255, 255, 0.6)", name: "rgba(255, 255, 255, 0.6)", hex: "rgba(255, 255, 255, 0.6)", hsl: "hsla(0, 0%, 100%, 0.6)", rgb: "rgba(255, 255, 255, 0.6)"},
|
{authored: "rgba(255, 255, 255, 0.6)", name: "rgba(255, 255, 255, 0.6)", hex: "rgba(255, 255, 255, 0.6)", hsl: "hsla(0, 0%, 100%, 0.6)", rgb: "rgba(255, 255, 255, 0.6)"},
|
||||||
{authored: "rgba(127, 89, 45, 1)", name: "#7F592D", hex: "#7F592D", hsl: "hsl(32.195, 48%, 34%)", rgb: "rgb(127, 89, 45)"},
|
{authored: "rgba(127, 89, 45, 1)", name: "#7F592D", hex: "#7F592D", hsl: "hsl(32.195, 48%, 34%)", rgb: "rgb(127, 89, 45)"},
|
||||||
{authored: "hsla(19.304, 56%, 40%, 1)", name: "#9F512C", hex: "#9F512C", hsl: "hsl(19.304, 57%, 40%)", rgb: "rgb(159, 81, 44)"},
|
{authored: "hsla(19.304, 56%, 40%, 1)", name: "#9F512C", hex: "#9F512C", hsl: "hsl(19.304, 57%, 40%)", rgb: "rgb(159, 81, 44)"},
|
||||||
{authored: "invalidColor", name: "", hex: "", hsl: "", rgb: ""}
|
{authored: "currentcolor", name: "currentcolor", hex: "currentcolor", hsl: "currentcolor", rgb: "currentcolor"},
|
||||||
|
{authored: "inherit", name: "inherit", hex: "inherit", hsl: "inherit", rgb: "inherit"},
|
||||||
|
{authored: "initial", name: "initial", hex: "initial", hsl: "initial", rgb: "initial"},
|
||||||
|
{authored: "invalidColor", name: "", hex: "", hsl: "", rgb: ""},
|
||||||
|
{authored: "transparent", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"},
|
||||||
|
{authored: "unset", name: "unset", hex: "unset", hsl: "unset", rgb: "unset"}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -461,7 +461,7 @@ VariablesViewController.prototype = {
|
||||||
scope.expanded = true;
|
scope.expanded = true;
|
||||||
scope.locked = true;
|
scope.locked = true;
|
||||||
|
|
||||||
let variable = scope.addItem();
|
let variable = scope.addItem("", { enumerable: true });
|
||||||
let expanded;
|
let expanded;
|
||||||
|
|
||||||
if (aOptions.objectActor) {
|
if (aOptions.objectActor) {
|
||||||
|
|
|
@ -0,0 +1,426 @@
|
||||||
|
// 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/.
|
||||||
|
|
||||||
|
this.EXPORTED_SYMBOLS = ["UITour"];
|
||||||
|
|
||||||
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
|
||||||
|
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "PermissionsUtils",
|
||||||
|
"resource://gre/modules/PermissionsUtils.jsm");
|
||||||
|
|
||||||
|
|
||||||
|
const UITOUR_PERMISSION = "uitour";
|
||||||
|
const PREF_PERM_BRANCH = "browser.uitour.";
|
||||||
|
|
||||||
|
|
||||||
|
this.UITour = {
|
||||||
|
originTabs: new WeakMap(),
|
||||||
|
pinnedTabs: new WeakMap(),
|
||||||
|
urlbarCapture: new WeakMap(),
|
||||||
|
|
||||||
|
highlightEffects: ["wobble", "zoom", "color"],
|
||||||
|
targets: new Map([
|
||||||
|
["backforward", "#unified-back-forward-button"],
|
||||||
|
["appmenu", "#appmenu-button"],
|
||||||
|
["home", "#home-button"],
|
||||||
|
["urlbar", "#urlbar"],
|
||||||
|
["bookmarks", "#bookmarks-menu-button"],
|
||||||
|
["search", "#searchbar"],
|
||||||
|
["searchprovider", function UITour_target_searchprovider(aDocument) {
|
||||||
|
let searchbar = aDocument.getElementById("searchbar");
|
||||||
|
return aDocument.getAnonymousElementByAttribute(searchbar,
|
||||||
|
"anonid",
|
||||||
|
"searchbar-engine-button");
|
||||||
|
}],
|
||||||
|
]),
|
||||||
|
|
||||||
|
onPageEvent: function(aEvent) {
|
||||||
|
let contentDocument = null;
|
||||||
|
if (aEvent.target instanceof Ci.nsIDOMHTMLDocument)
|
||||||
|
contentDocument = aEvent.target;
|
||||||
|
else if (aEvent.target instanceof Ci.nsIDOMHTMLElement)
|
||||||
|
contentDocument = aEvent.target.ownerDocument;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Ignore events if they're not from a trusted origin.
|
||||||
|
if (!this.ensureTrustedOrigin(contentDocument))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (typeof aEvent.detail != "object")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let action = aEvent.detail.action;
|
||||||
|
if (typeof action != "string" || !action)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let data = aEvent.detail.data;
|
||||||
|
if (typeof data != "object")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let window = this.getChromeWindow(contentDocument);
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case "showHighlight": {
|
||||||
|
let target = this.getTarget(window, data.target);
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
this.showHighlight(target);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "hideHighlight": {
|
||||||
|
this.hideHighlight(window);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "showInfo": {
|
||||||
|
let target = this.getTarget(window, data.target, true);
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
this.showInfo(target, data.title, data.text);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "hideInfo": {
|
||||||
|
this.hideInfo(window);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "previewTheme": {
|
||||||
|
this.previewTheme(data.theme);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "resetTheme": {
|
||||||
|
this.resetTheme();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "addPinnedTab": {
|
||||||
|
this.ensurePinnedTab(window, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "removePinnedTab": {
|
||||||
|
this.removePinnedTab(window);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "showMenu": {
|
||||||
|
this.showMenu(window, data.name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "startUrlbarCapture": {
|
||||||
|
if (typeof data.text != "string" || !data.text ||
|
||||||
|
typeof data.url != "string" || !data.url) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uri = null;
|
||||||
|
try {
|
||||||
|
uri = Services.io.newURI(data.url, null, null);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let secman = Services.scriptSecurityManager;
|
||||||
|
let principal = contentDocument.nodePrincipal;
|
||||||
|
let flags = secman.DISALLOW_INHERIT_PRINCIPAL;
|
||||||
|
try {
|
||||||
|
secman.checkLoadURIWithPrincipal(principal, uri, flags);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.startUrlbarCapture(window, data.text, data.url);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "endUrlbarCapture": {
|
||||||
|
this.endUrlbarCapture(window);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let tab = window.gBrowser._getTabForContentWindow(contentDocument.defaultView);
|
||||||
|
if (!this.originTabs.has(window))
|
||||||
|
this.originTabs.set(window, new Set());
|
||||||
|
this.originTabs.get(window).add(tab);
|
||||||
|
|
||||||
|
tab.addEventListener("TabClose", this);
|
||||||
|
window.gBrowser.tabContainer.addEventListener("TabSelect", this);
|
||||||
|
window.addEventListener("SSWindowClosing", this);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleEvent: function(aEvent) {
|
||||||
|
switch (aEvent.type) {
|
||||||
|
case "pagehide": {
|
||||||
|
let window = this.getChromeWindow(aEvent.target);
|
||||||
|
this.teardownTour(window);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "TabClose": {
|
||||||
|
let window = aEvent.target.ownerDocument.defaultView;
|
||||||
|
this.teardownTour(window);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "TabSelect": {
|
||||||
|
let window = aEvent.target.ownerDocument.defaultView;
|
||||||
|
let pinnedTab = this.pinnedTabs.get(window);
|
||||||
|
if (pinnedTab && pinnedTab.tab == window.gBrowser.selectedTab)
|
||||||
|
break;
|
||||||
|
let originTabs = this.originTabs.get(window);
|
||||||
|
if (originTabs && originTabs.has(window.gBrowser.selectedTab))
|
||||||
|
break;
|
||||||
|
|
||||||
|
this.teardownTour(window);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "SSWindowClosing": {
|
||||||
|
let window = aEvent.target;
|
||||||
|
this.teardownTour(window, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "input": {
|
||||||
|
if (aEvent.target.id == "urlbar") {
|
||||||
|
let window = aEvent.target.ownerDocument.defaultView;
|
||||||
|
this.handleUrlbarInput(window);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
teardownTour: function(aWindow, aWindowClosing = false) {
|
||||||
|
aWindow.gBrowser.tabContainer.removeEventListener("TabSelect", this);
|
||||||
|
aWindow.removeEventListener("SSWindowClosing", this);
|
||||||
|
|
||||||
|
let originTabs = this.originTabs.get(aWindow);
|
||||||
|
if (originTabs) {
|
||||||
|
for (let tab of originTabs)
|
||||||
|
tab.removeEventListener("TabClose", this);
|
||||||
|
}
|
||||||
|
this.originTabs.delete(aWindow);
|
||||||
|
|
||||||
|
if (!aWindowClosing) {
|
||||||
|
this.hideHighlight(aWindow);
|
||||||
|
this.hideInfo(aWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.endUrlbarCapture(aWindow);
|
||||||
|
this.removePinnedTab(aWindow);
|
||||||
|
this.resetTheme();
|
||||||
|
},
|
||||||
|
|
||||||
|
getChromeWindow: function(aContentDocument) {
|
||||||
|
return aContentDocument.defaultView
|
||||||
|
.window
|
||||||
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIWebNavigation)
|
||||||
|
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||||
|
.rootTreeItem
|
||||||
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindow)
|
||||||
|
.wrappedJSObject;
|
||||||
|
},
|
||||||
|
|
||||||
|
importPermissions: function() {
|
||||||
|
try {
|
||||||
|
PermissionsUtils.importFromPrefs(PREF_PERM_BRANCH, UITOUR_PERMISSION);
|
||||||
|
} catch (e) {
|
||||||
|
Cu.reportError(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ensureTrustedOrigin: function(aDocument) {
|
||||||
|
if (aDocument.defaultView.top != aDocument.defaultView)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let uri = aDocument.documentURIObject;
|
||||||
|
|
||||||
|
if (uri.schemeIs("chrome"))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!uri.schemeIs("https"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this.importPermissions();
|
||||||
|
let permission = Services.perms.testPermission(uri, UITOUR_PERMISSION);
|
||||||
|
return permission == Services.perms.ALLOW_ACTION;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTarget: function(aWindow, aTargetName, aSticky = false) {
|
||||||
|
if (typeof aTargetName != "string" || !aTargetName)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (aTargetName == "pinnedtab")
|
||||||
|
return this.ensurePinnedTab(aWindow, aSticky);
|
||||||
|
|
||||||
|
let targetQuery = this.targets.get(aTargetName);
|
||||||
|
if (!targetQuery)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (typeof targetQuery == "function")
|
||||||
|
return targetQuery(aWindow.document);
|
||||||
|
|
||||||
|
return aWindow.document.querySelector(targetQuery);
|
||||||
|
},
|
||||||
|
|
||||||
|
previewTheme: function(aTheme) {
|
||||||
|
let origin = Services.prefs.getCharPref("browser.uitour.themeOrigin");
|
||||||
|
let data = LightweightThemeManager.parseTheme(aTheme, origin);
|
||||||
|
if (data)
|
||||||
|
LightweightThemeManager.previewTheme(data);
|
||||||
|
},
|
||||||
|
|
||||||
|
resetTheme: function() {
|
||||||
|
LightweightThemeManager.resetPreview();
|
||||||
|
},
|
||||||
|
|
||||||
|
ensurePinnedTab: function(aWindow, aSticky = false) {
|
||||||
|
let tabInfo = this.pinnedTabs.get(aWindow);
|
||||||
|
|
||||||
|
if (tabInfo) {
|
||||||
|
tabInfo.sticky = tabInfo.sticky || aSticky;
|
||||||
|
} else {
|
||||||
|
let url = Services.urlFormatter.formatURLPref("browser.uitour.pinnedTabUrl");
|
||||||
|
|
||||||
|
let tab = aWindow.gBrowser.addTab(url);
|
||||||
|
aWindow.gBrowser.pinTab(tab);
|
||||||
|
tab.addEventListener("TabClose", () => {
|
||||||
|
this.pinnedTabs.delete(aWindow);
|
||||||
|
});
|
||||||
|
|
||||||
|
tabInfo = {
|
||||||
|
tab: tab,
|
||||||
|
sticky: aSticky
|
||||||
|
};
|
||||||
|
this.pinnedTabs.set(aWindow, tabInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tabInfo.tab;
|
||||||
|
},
|
||||||
|
|
||||||
|
removePinnedTab: function(aWindow) {
|
||||||
|
let tabInfo = this.pinnedTabs.get(aWindow);
|
||||||
|
if (tabInfo)
|
||||||
|
aWindow.gBrowser.removeTab(tabInfo.tab);
|
||||||
|
},
|
||||||
|
|
||||||
|
showHighlight: function(aTarget) {
|
||||||
|
let highlighter = aTarget.ownerDocument.getElementById("UITourHighlight");
|
||||||
|
|
||||||
|
let randomEffect = Math.floor(Math.random() * this.highlightEffects.length);
|
||||||
|
if (randomEffect == this.highlightEffects.length)
|
||||||
|
randomEffect--; // On the order of 1 in 2^62 chance of this happening.
|
||||||
|
highlighter.setAttribute("active", this.highlightEffects[randomEffect]);
|
||||||
|
|
||||||
|
let targetRect = aTarget.getBoundingClientRect();
|
||||||
|
|
||||||
|
highlighter.style.height = targetRect.height + "px";
|
||||||
|
highlighter.style.width = targetRect.width + "px";
|
||||||
|
|
||||||
|
let highlighterRect = highlighter.getBoundingClientRect();
|
||||||
|
|
||||||
|
let top = targetRect.top + (targetRect.height / 2) - (highlighterRect.height / 2);
|
||||||
|
highlighter.style.top = top + "px";
|
||||||
|
let left = targetRect.left + (targetRect.width / 2) - (highlighterRect.width / 2);
|
||||||
|
highlighter.style.left = left + "px";
|
||||||
|
},
|
||||||
|
|
||||||
|
hideHighlight: function(aWindow) {
|
||||||
|
let tabData = this.pinnedTabs.get(aWindow);
|
||||||
|
if (tabData && !tabData.sticky)
|
||||||
|
this.removePinnedTab(aWindow);
|
||||||
|
|
||||||
|
let highlighter = aWindow.document.getElementById("UITourHighlight");
|
||||||
|
highlighter.removeAttribute("active");
|
||||||
|
},
|
||||||
|
|
||||||
|
showInfo: function(aAnchor, aTitle, aDescription) {
|
||||||
|
aAnchor.focus();
|
||||||
|
|
||||||
|
let document = aAnchor.ownerDocument;
|
||||||
|
let tooltip = document.getElementById("UITourTooltip");
|
||||||
|
let tooltipTitle = document.getElementById("UITourTooltipTitle");
|
||||||
|
let tooltipDesc = document.getElementById("UITourTooltipDescription");
|
||||||
|
|
||||||
|
tooltip.hidePopup();
|
||||||
|
|
||||||
|
tooltipTitle.textContent = aTitle;
|
||||||
|
tooltipDesc.textContent = aDescription;
|
||||||
|
|
||||||
|
let alignment = "bottomcenter topright";
|
||||||
|
let anchorRect = aAnchor.getBoundingClientRect();
|
||||||
|
|
||||||
|
tooltip.hidden = false;
|
||||||
|
tooltip.openPopup(aAnchor, alignment);
|
||||||
|
},
|
||||||
|
|
||||||
|
hideInfo: function(aWindow) {
|
||||||
|
let tooltip = aWindow.document.getElementById("UITourTooltip");
|
||||||
|
tooltip.hidePopup();
|
||||||
|
},
|
||||||
|
|
||||||
|
showMenu: function(aWindow, aMenuName) {
|
||||||
|
function openMenuButton(aId) {
|
||||||
|
let menuBtn = aWindow.document.getElementById(aId);
|
||||||
|
if (menuBtn && menuBtn.boxObject)
|
||||||
|
menuBtn.boxObject.QueryInterface(Ci.nsIMenuBoxObject).openMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aMenuName == "appmenu")
|
||||||
|
openMenuButton("appmenu-button");
|
||||||
|
else if (aMenuName == "bookmarks")
|
||||||
|
openMenuButton("bookmarks-menu-button");
|
||||||
|
},
|
||||||
|
|
||||||
|
startUrlbarCapture: function(aWindow, aExpectedText, aUrl) {
|
||||||
|
let urlbar = aWindow.document.getElementById("urlbar");
|
||||||
|
this.urlbarCapture.set(aWindow, {
|
||||||
|
expected: aExpectedText.toLocaleLowerCase(),
|
||||||
|
url: aUrl
|
||||||
|
});
|
||||||
|
urlbar.addEventListener("input", this);
|
||||||
|
},
|
||||||
|
|
||||||
|
endUrlbarCapture: function(aWindow) {
|
||||||
|
let urlbar = aWindow.document.getElementById("urlbar");
|
||||||
|
urlbar.removeEventListener("input", this);
|
||||||
|
this.urlbarCapture.delete(aWindow);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleUrlbarInput: function(aWindow) {
|
||||||
|
if (!this.urlbarCapture.has(aWindow))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let urlbar = aWindow.document.getElementById("urlbar");
|
||||||
|
|
||||||
|
let {expected, url} = this.urlbarCapture.get(aWindow);
|
||||||
|
|
||||||
|
if (urlbar.value.toLocaleLowerCase().localeCompare(expected) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
urlbar.handleRevert();
|
||||||
|
|
||||||
|
let tab = aWindow.gBrowser.addTab(url, {
|
||||||
|
owner: aWindow.gBrowser.selectedTab,
|
||||||
|
relatedToCurrent: true
|
||||||
|
});
|
||||||
|
aWindow.gBrowser.selectedTab = tab;
|
||||||
|
},
|
||||||
|
};
|
|
@ -15,6 +15,7 @@ EXTRA_JS_MODULES += [
|
||||||
'SitePermissions.jsm',
|
'SitePermissions.jsm',
|
||||||
'Social.jsm',
|
'Social.jsm',
|
||||||
'TabCrashReporter.jsm',
|
'TabCrashReporter.jsm',
|
||||||
|
'UITour.jsm',
|
||||||
'offlineAppCache.jsm',
|
'offlineAppCache.jsm',
|
||||||
'openLocationLastURL.jsm',
|
'openLocationLastURL.jsm',
|
||||||
'webappsUI.jsm',
|
'webappsUI.jsm',
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
|
|
||||||
[browser_NetworkPrioritizer.js]
|
[browser_NetworkPrioritizer.js]
|
||||||
|
[browser_UITour.js]
|
||||||
|
support-files = uitour.*
|
|
@ -0,0 +1,212 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
let gTestTab;
|
||||||
|
let gContentAPI;
|
||||||
|
|
||||||
|
Components.utils.import("resource:///modules/UITour.jsm");
|
||||||
|
|
||||||
|
function is_hidden(element) {
|
||||||
|
var style = element.ownerDocument.defaultView.getComputedStyle(element, "");
|
||||||
|
if (style.display == "none")
|
||||||
|
return true;
|
||||||
|
if (style.visibility != "visible")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Hiding a parent element will hide all its children
|
||||||
|
if (element.parentNode != element.ownerDocument)
|
||||||
|
return is_hidden(element.parentNode);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_element_visible(element, msg) {
|
||||||
|
isnot(element, null, "Element should not be null, when checking visibility");
|
||||||
|
ok(!is_hidden(element), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_element_hidden(element, msg) {
|
||||||
|
isnot(element, null, "Element should not be null, when checking visibility");
|
||||||
|
ok(is_hidden(element), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTestPage(callback, untrustedHost = false) {
|
||||||
|
if (gTestTab)
|
||||||
|
gBrowser.removeTab(gTestTab);
|
||||||
|
|
||||||
|
let url = getRootDirectory(gTestPath) + "uitour.html";
|
||||||
|
if (untrustedHost)
|
||||||
|
url = url.replace("chrome://mochitests/content/", "http://example.com/");
|
||||||
|
|
||||||
|
gTestTab = gBrowser.addTab(url);
|
||||||
|
gBrowser.selectedTab = gTestTab;
|
||||||
|
|
||||||
|
gTestTab.linkedBrowser.addEventListener("load", function onLoad() {
|
||||||
|
gTestTab.linkedBrowser.removeEventListener("load", onLoad);
|
||||||
|
|
||||||
|
let contentWindow = Components.utils.waiveXrays(gTestTab.linkedBrowser.contentDocument.defaultView);
|
||||||
|
gContentAPI = contentWindow.Mozilla.UITour;
|
||||||
|
|
||||||
|
waitForFocus(callback, contentWindow);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
Services.prefs.setBoolPref("browser.uitour.enabled", true);
|
||||||
|
|
||||||
|
waitForExplicitFinish();
|
||||||
|
|
||||||
|
registerCleanupFunction(function() {
|
||||||
|
delete window.UITour;
|
||||||
|
delete window.gContentAPI;
|
||||||
|
if (gTestTab)
|
||||||
|
gBrowser.removeTab(gTestTab);
|
||||||
|
delete window.gTestTab;
|
||||||
|
Services.prefs.clearUserPref("browser.uitour.enabled", true);
|
||||||
|
});
|
||||||
|
|
||||||
|
function done() {
|
||||||
|
if (gTestTab)
|
||||||
|
gBrowser.removeTab(gTestTab);
|
||||||
|
gTestTab = null;
|
||||||
|
|
||||||
|
let highlight = document.getElementById("UITourHighlight");
|
||||||
|
is_element_hidden(highlight, "Highlight should be hidden after UITour tab is closed");
|
||||||
|
|
||||||
|
let popup = document.getElementById("UITourTooltip");
|
||||||
|
isnot(["hidding","closed"].indexOf(popup.state), -1, "Popup should be closed/hidding after UITour tab is closed");
|
||||||
|
|
||||||
|
is(UITour.pinnedTabs.get(window), null, "Any pinned tab should be closed after UITour tab is closed");
|
||||||
|
|
||||||
|
executeSoon(nextTest);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextTest() {
|
||||||
|
if (tests.length == 0) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let test = tests.shift();
|
||||||
|
|
||||||
|
loadTestPage(function() {
|
||||||
|
test(done);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
nextTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
let tests = [
|
||||||
|
function test_disabled(done) {
|
||||||
|
Services.prefs.setBoolPref("browser.uitour.enabled", false);
|
||||||
|
|
||||||
|
let highlight = document.getElementById("UITourHighlight");
|
||||||
|
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||||
|
|
||||||
|
gContentAPI.showHighlight("urlbar");
|
||||||
|
is_element_hidden(highlight, "Highlight should not be shown when feature is disabled");
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref("browser.uitour.enabled", true);
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
function test_untrusted_host(done) {
|
||||||
|
loadTestPage(function() {
|
||||||
|
let highlight = document.getElementById("UITourHighlight");
|
||||||
|
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||||
|
|
||||||
|
gContentAPI.showHighlight("urlbar");
|
||||||
|
is_element_hidden(highlight, "Highlight should not be shown on a untrusted domain");
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, true);
|
||||||
|
},
|
||||||
|
function test_highlight(done) {
|
||||||
|
let highlight = document.getElementById("UITourHighlight");
|
||||||
|
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||||
|
|
||||||
|
gContentAPI.showHighlight("urlbar");
|
||||||
|
is_element_visible(highlight, "Highlight should be shown after showHighlight()");
|
||||||
|
|
||||||
|
gContentAPI.hideHighlight();
|
||||||
|
is_element_hidden(highlight, "Highlight should be hidden after hideHighlight()");
|
||||||
|
|
||||||
|
gContentAPI.showHighlight("urlbar");
|
||||||
|
is_element_visible(highlight, "Highlight should be shown after showHighlight()");
|
||||||
|
gContentAPI.showHighlight("backforward");
|
||||||
|
is_element_visible(highlight, "Highlight should be shown after showHighlight()");
|
||||||
|
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
function test_info_1(done) {
|
||||||
|
let popup = document.getElementById("UITourTooltip");
|
||||||
|
let title = document.getElementById("UITourTooltipTitle");
|
||||||
|
let desc = document.getElementById("UITourTooltipDescription");
|
||||||
|
popup.addEventListener("popupshown", function onPopupShown() {
|
||||||
|
popup.removeEventListener("popupshown", onPopupShown);
|
||||||
|
is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
|
||||||
|
is(title.textContent, "test title", "Popup should have correct title");
|
||||||
|
is(desc.textContent, "test text", "Popup should have correct description text");
|
||||||
|
|
||||||
|
popup.addEventListener("popuphidden", function onPopupHidden() {
|
||||||
|
popup.removeEventListener("popuphidden", onPopupHidden);
|
||||||
|
|
||||||
|
popup.addEventListener("popupshown", function onPopupShown() {
|
||||||
|
popup.removeEventListener("popupshown", onPopupShown);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
gContentAPI.showInfo("urlbar", "test title", "test text");
|
||||||
|
|
||||||
|
});
|
||||||
|
gContentAPI.hideInfo();
|
||||||
|
});
|
||||||
|
|
||||||
|
gContentAPI.showInfo("urlbar", "test title", "test text");
|
||||||
|
},
|
||||||
|
function test_info_2(done) {
|
||||||
|
let popup = document.getElementById("UITourTooltip");
|
||||||
|
let title = document.getElementById("UITourTooltipTitle");
|
||||||
|
let desc = document.getElementById("UITourTooltipDescription");
|
||||||
|
popup.addEventListener("popupshown", function onPopupShown() {
|
||||||
|
popup.removeEventListener("popupshown", onPopupShown);
|
||||||
|
is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
|
||||||
|
is(title.textContent, "urlbar title", "Popup should have correct title");
|
||||||
|
is(desc.textContent, "urlbar text", "Popup should have correct description text");
|
||||||
|
|
||||||
|
gContentAPI.showInfo("search", "search title", "search text");
|
||||||
|
executeSoon(function() {
|
||||||
|
is(popup.popupBoxObject.anchorNode, document.getElementById("searchbar"), "Popup should be anchored to the searchbar");
|
||||||
|
is(title.textContent, "search title", "Popup should have correct title");
|
||||||
|
is(desc.textContent, "search text", "Popup should have correct description text");
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gContentAPI.showInfo("urlbar", "urlbar title", "urlbar text");
|
||||||
|
},
|
||||||
|
function test_pinnedTab(done) {
|
||||||
|
is(UITour.pinnedTabs.get(window), null, "Should not already have a pinned tab");
|
||||||
|
|
||||||
|
gContentAPI.addPinnedTab();
|
||||||
|
let tabInfo = UITour.pinnedTabs.get(window);
|
||||||
|
isnot(tabInfo, null, "Should have recorded data about a pinned tab after addPinnedTab()");
|
||||||
|
isnot(tabInfo.tab, null, "Should have added a pinned tab after addPinnedTab()");
|
||||||
|
is(tabInfo.tab.pinned, true, "Tab should be marked as pinned");
|
||||||
|
|
||||||
|
let tab = tabInfo.tab;
|
||||||
|
|
||||||
|
gContentAPI.removePinnedTab();
|
||||||
|
isnot(gBrowser.tabs[0], tab, "First tab should not be the pinned tab");
|
||||||
|
let tabInfo = UITour.pinnedTabs.get(window);
|
||||||
|
is(tabInfo, null, "Should not have any data about the removed pinned tab after removePinnedTab()");
|
||||||
|
|
||||||
|
gContentAPI.addPinnedTab();
|
||||||
|
gContentAPI.addPinnedTab();
|
||||||
|
gContentAPI.addPinnedTab();
|
||||||
|
is(gBrowser.tabs[1].pinned, false, "After multiple calls of addPinnedTab, should still only have one pinned tab");
|
||||||
|
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
];
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>UITour test</title>
|
||||||
|
<script type="application/javascript" src="uitour.js">
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>UITour tests</h1>
|
||||||
|
<p>Because Firefox is...</p>
|
||||||
|
<p>Never gonna let you down</p>
|
||||||
|
<p>Never gonna give you up</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,115 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
// Copied from the proposed JS library for Bedrock (ie, www.mozilla.org).
|
||||||
|
|
||||||
|
// create namespace
|
||||||
|
if (typeof Mozilla == 'undefined') {
|
||||||
|
var Mozilla = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
(function($) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// create namespace
|
||||||
|
if (typeof Mozilla.UITour == 'undefined') {
|
||||||
|
Mozilla.UITour = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var themeIntervalId = null;
|
||||||
|
function _stopCyclingThemes() {
|
||||||
|
if (themeIntervalId) {
|
||||||
|
clearInterval(themeIntervalId);
|
||||||
|
themeIntervalId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _sendEvent(action, data) {
|
||||||
|
var event = new CustomEvent('mozUITour', {
|
||||||
|
bubbles: true,
|
||||||
|
detail: {
|
||||||
|
action: action,
|
||||||
|
data: data || {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log("Sending mozUITour event: ", event);
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY = 10 * 1000;
|
||||||
|
|
||||||
|
Mozilla.UITour.showHighlight = function(target) {
|
||||||
|
_sendEvent('showHighlight', {
|
||||||
|
target: target
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Mozilla.UITour.hideHighlight = function() {
|
||||||
|
_sendEvent('hideHighlight');
|
||||||
|
};
|
||||||
|
|
||||||
|
Mozilla.UITour.showInfo = function(target, title, text) {
|
||||||
|
_sendEvent('showInfo', {
|
||||||
|
target: target,
|
||||||
|
title: title,
|
||||||
|
text: text
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Mozilla.UITour.hideInfo = function() {
|
||||||
|
_sendEvent('hideInfo');
|
||||||
|
};
|
||||||
|
|
||||||
|
Mozilla.UITour.previewTheme = function(theme) {
|
||||||
|
_stopCyclingThemes();
|
||||||
|
|
||||||
|
_sendEvent('previewTheme', {
|
||||||
|
theme: JSON.stringify(theme)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Mozilla.UITour.resetTheme = function() {
|
||||||
|
_stopCyclingThemes();
|
||||||
|
|
||||||
|
_sendEvent('resetTheme');
|
||||||
|
};
|
||||||
|
|
||||||
|
Mozilla.UITour.cycleThemes = function(themes, delay, callback) {
|
||||||
|
_stopCyclingThemes();
|
||||||
|
|
||||||
|
if (!delay) {
|
||||||
|
delay = Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextTheme() {
|
||||||
|
var theme = themes.shift();
|
||||||
|
themes.push(theme);
|
||||||
|
|
||||||
|
_sendEvent('previewTheme', {
|
||||||
|
theme: JSON.stringify(theme),
|
||||||
|
state: true
|
||||||
|
});
|
||||||
|
|
||||||
|
callback(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
themeIntervalId = setInterval(nextTheme, delay);
|
||||||
|
nextTheme();
|
||||||
|
};
|
||||||
|
|
||||||
|
Mozilla.UITour.addPinnedTab = function() {
|
||||||
|
_sendEvent('addPinnedTab');
|
||||||
|
};
|
||||||
|
|
||||||
|
Mozilla.UITour.removePinnedTab = function() {
|
||||||
|
_sendEvent('removePinnedTab');
|
||||||
|
};
|
||||||
|
|
||||||
|
Mozilla.UITour.showMenu = function(name) {
|
||||||
|
_sendEvent('showMenu', {
|
||||||
|
name: name
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})();
|
|
@ -2113,6 +2113,90 @@ toolbar[mode="text"] toolbarbutton.chevron > .toolbarbutton-icon {
|
||||||
-moz-margin-end: 2px;
|
-moz-margin-end: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* UI Tour */
|
||||||
|
|
||||||
|
@keyframes uitour-wobble {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg) translateX(2px) rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg) translateX(2px) rotate(-360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes uitour-zoom {
|
||||||
|
from {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes uitour-color {
|
||||||
|
from {
|
||||||
|
border-color: #5B9CD9;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
border-color: #FF0000;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
border-color: #5B9CD9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html|div#UITourHighlight {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
min-height: 32px;
|
||||||
|
min-width: 32px;
|
||||||
|
display: none;
|
||||||
|
border: 2px #5B9CD9 solid;
|
||||||
|
box-shadow: 0 0 2px #5B9CD9, inset 0 0 1px #5B9CD9;
|
||||||
|
border-radius: 20px;
|
||||||
|
z-index: 10000000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
html|div#UITourHighlight[active] {
|
||||||
|
display: block;
|
||||||
|
animation-delay: 2s;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
html|div#UITourHighlight[active="wobble"] {
|
||||||
|
animation-name: uitour-wobble;
|
||||||
|
animation-duration: 1s;
|
||||||
|
}
|
||||||
|
html|div#UITourHighlight[active="zoom"] {
|
||||||
|
animation-name: uitour-zoom;
|
||||||
|
animation-duration: 1s;
|
||||||
|
}
|
||||||
|
html|div#UITourHighlight[active="color"] {
|
||||||
|
animation-name: uitour-color;
|
||||||
|
animation-duration: 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#UITourTooltip {
|
||||||
|
max-width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#UITourTooltipTitle {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 130%;
|
||||||
|
margin: 0 0 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#UITourTooltipDescription {
|
||||||
|
max-width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Social toolbar item */
|
||||||
|
|
||||||
#social-provider-button {
|
#social-provider-button {
|
||||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||||
list-style-image: url(chrome://browser/skin/social/services-16.png);
|
list-style-image: url(chrome://browser/skin/social/services-16.png);
|
||||||
|
|
|
@ -3728,6 +3728,88 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
|
||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* UI Tour */
|
||||||
|
|
||||||
|
@keyframes uitour-wobble {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg) translateX(2px) rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg) translateX(2px) rotate(-360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes uitour-zoom {
|
||||||
|
from {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes uitour-color {
|
||||||
|
from {
|
||||||
|
border-color: #5B9CD9;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
border-color: #FF0000;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
border-color: #5B9CD9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html|div#UITourHighlight {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
min-height: 32px;
|
||||||
|
min-width: 32px;
|
||||||
|
display: none;
|
||||||
|
border: 2px #5B9CD9 solid;
|
||||||
|
box-shadow: 0 0 2px #5B9CD9, inset 0 0 1px #5B9CD9;
|
||||||
|
border-radius: 20px;
|
||||||
|
z-index: 10000000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
html|div#UITourHighlight[active] {
|
||||||
|
display: block;
|
||||||
|
animation-delay: 2s;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
html|div#UITourHighlight[active="wobble"] {
|
||||||
|
animation-name: uitour-wobble;
|
||||||
|
animation-duration: 1s;
|
||||||
|
}
|
||||||
|
html|div#UITourHighlight[active="zoom"] {
|
||||||
|
animation-name: uitour-zoom;
|
||||||
|
animation-duration: 1s;
|
||||||
|
}
|
||||||
|
html|div#UITourHighlight[active="color"] {
|
||||||
|
animation-name: uitour-color;
|
||||||
|
animation-duration: 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#UITourTooltip {
|
||||||
|
max-width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#UITourTooltipTitle {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 130%;
|
||||||
|
margin: 0 0 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#UITourTooltipDescription {
|
||||||
|
max-width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
/* === social toolbar button === */
|
/* === social toolbar button === */
|
||||||
|
|
||||||
#social-toolbar-item > .toolbarbutton-1 {
|
#social-toolbar-item > .toolbarbutton-1 {
|
||||||
|
|
|
@ -2855,6 +2855,87 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
|
||||||
-moz-margin-end: 5px;
|
-moz-margin-end: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* UI Tour */
|
||||||
|
|
||||||
|
@keyframes uitour-wobble {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg) translateX(2px) rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg) translateX(2px) rotate(-360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes uitour-zoom {
|
||||||
|
from {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes uitour-color {
|
||||||
|
from {
|
||||||
|
border-color: #5B9CD9;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
border-color: #FF0000;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
border-color: #5B9CD9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html|div#UITourHighlight {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
min-height: 32px;
|
||||||
|
min-width: 32px;
|
||||||
|
display: none;
|
||||||
|
border: 2px #5B9CD9 solid;
|
||||||
|
box-shadow: 0 0 2px #5B9CD9, inset 0 0 1px #5B9CD9;
|
||||||
|
border-radius: 20px;
|
||||||
|
z-index: 10000000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
html|div#UITourHighlight[active] {
|
||||||
|
display: block;
|
||||||
|
animation-delay: 2s;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
html|div#UITourHighlight[active="wobble"] {
|
||||||
|
animation-name: uitour-wobble;
|
||||||
|
animation-duration: 1s;
|
||||||
|
}
|
||||||
|
html|div#UITourHighlight[active="zoom"] {
|
||||||
|
animation-name: uitour-zoom;
|
||||||
|
animation-duration: 1s;
|
||||||
|
}
|
||||||
|
html|div#UITourHighlight[active="color"] {
|
||||||
|
animation-name: uitour-color;
|
||||||
|
animation-duration: 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#UITourTooltip {
|
||||||
|
}
|
||||||
|
|
||||||
|
#UITourTooltipTitle {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 130%;
|
||||||
|
margin: 0 0 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#UITourTooltipDescription {
|
||||||
|
max-width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
/* Social toolbar item */
|
/* Social toolbar item */
|
||||||
|
|
||||||
#social-provider-button {
|
#social-provider-button {
|
||||||
|
|
|
@ -1776,13 +1776,14 @@ abstract public class BrowserApp extends GeckoApp
|
||||||
// Set attribute for the menu item in cache, if available
|
// Set attribute for the menu item in cache, if available
|
||||||
if (mAddonMenuItemsCache != null && !mAddonMenuItemsCache.isEmpty()) {
|
if (mAddonMenuItemsCache != null && !mAddonMenuItemsCache.isEmpty()) {
|
||||||
for (MenuItemInfo item : mAddonMenuItemsCache) {
|
for (MenuItemInfo item : mAddonMenuItemsCache) {
|
||||||
if (item.id == id) {
|
if (item.id == id) {
|
||||||
item.checkable = options.optBoolean("checkable", item.checkable);
|
item.label = options.optString("name", item.label);
|
||||||
item.checked = options.optBoolean("checked", item.checked);
|
item.checkable = options.optBoolean("checkable", item.checkable);
|
||||||
item.enabled = options.optBoolean("enabled", item.enabled);
|
item.checked = options.optBoolean("checked", item.checked);
|
||||||
item.visible = options.optBoolean("visible", item.visible);
|
item.enabled = options.optBoolean("enabled", item.enabled);
|
||||||
break;
|
item.visible = options.optBoolean("visible", item.visible);
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1791,6 +1792,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||||
|
|
||||||
MenuItem menuItem = mMenu.findItem(id);
|
MenuItem menuItem = mMenu.findItem(id);
|
||||||
if (menuItem != null) {
|
if (menuItem != null) {
|
||||||
|
menuItem.setTitle(options.optString("name", menuItem.getTitle().toString()));
|
||||||
menuItem.setCheckable(options.optBoolean("checkable", menuItem.isCheckable()));
|
menuItem.setCheckable(options.optBoolean("checkable", menuItem.isCheckable()));
|
||||||
menuItem.setChecked(options.optBoolean("checked", menuItem.isChecked()));
|
menuItem.setChecked(options.optBoolean("checked", menuItem.isChecked()));
|
||||||
menuItem.setEnabled(options.optBoolean("enabled", menuItem.isEnabled()));
|
menuItem.setEnabled(options.optBoolean("enabled", menuItem.isEnabled()));
|
||||||
|
|
|
@ -4170,7 +4170,7 @@ var BrowserEventHandler = {
|
||||||
if (this._scrollableElement != null) {
|
if (this._scrollableElement != null) {
|
||||||
// Discard if it's the top-level scrollable, we let Java handle this
|
// Discard if it's the top-level scrollable, we let Java handle this
|
||||||
let doc = BrowserApp.selectedBrowser.contentDocument;
|
let doc = BrowserApp.selectedBrowser.contentDocument;
|
||||||
if (this._scrollableElement != doc.documentElement)
|
if (this._scrollableElement != doc.body && this._scrollableElement != doc.documentElement)
|
||||||
sendMessageToJava({ type: "Panning:Override" });
|
sendMessageToJava({ type: "Panning:Override" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4257,6 +4257,7 @@ var BrowserEventHandler = {
|
||||||
|
|
||||||
let doc = BrowserApp.selectedBrowser.contentDocument;
|
let doc = BrowserApp.selectedBrowser.contentDocument;
|
||||||
if (this._scrollableElement == null ||
|
if (this._scrollableElement == null ||
|
||||||
|
this._scrollableElement == doc.body ||
|
||||||
this._scrollableElement == doc.documentElement) {
|
this._scrollableElement == doc.documentElement) {
|
||||||
sendMessageToJava({ type: "Panning:CancelOverride" });
|
sendMessageToJava({ type: "Panning:CancelOverride" });
|
||||||
return;
|
return;
|
||||||
|
@ -4627,14 +4628,15 @@ var BrowserEventHandler = {
|
||||||
let scrollable = false;
|
let scrollable = false;
|
||||||
while (elem) {
|
while (elem) {
|
||||||
/* Element is scrollable if its scroll-size exceeds its client size, and:
|
/* Element is scrollable if its scroll-size exceeds its client size, and:
|
||||||
* - It has overflow 'auto' or 'scroll', or
|
* - It has overflow 'auto' or 'scroll'
|
||||||
* - It's a textarea or HTML node, or
|
* - It's a textarea
|
||||||
|
* - It's an HTML/BODY node
|
||||||
* - It's a select element showing multiple rows
|
* - It's a select element showing multiple rows
|
||||||
*/
|
*/
|
||||||
if (checkElem) {
|
if (checkElem) {
|
||||||
if ((elem.scrollTopMax > 0 || elem.scrollLeftMax > 0) &&
|
if ((elem.scrollTopMax > 0 || elem.scrollLeftMax > 0) &&
|
||||||
(this._hasScrollableOverflow(elem) ||
|
(this._hasScrollableOverflow(elem) ||
|
||||||
elem.mozMatchesSelector("html, textarea")) ||
|
elem.mozMatchesSelector("html, body, textarea")) ||
|
||||||
(elem instanceof HTMLSelectElement && (elem.size > 1 || elem.multiple))) {
|
(elem instanceof HTMLSelectElement && (elem.size > 1 || elem.multiple))) {
|
||||||
scrollable = true;
|
scrollable = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -262,9 +262,9 @@ AlertDownloadProgressListener.prototype = {
|
||||||
case Ci.nsIDownloadManager.DOWNLOAD_FINISHED: {
|
case Ci.nsIDownloadManager.DOWNLOAD_FINISHED: {
|
||||||
Downloads.removeNotification(aDownload);
|
Downloads.removeNotification(aDownload);
|
||||||
if (aDownload.isPrivate) {
|
if (aDownload.isPrivate) {
|
||||||
let index = this._privateDownloads.indexOf(aDownload);
|
let index = Downloads._privateDownloads.indexOf(aDownload);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
this._privateDownloads.splice(index, 1);
|
Downloads._privateDownloads.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -956,7 +956,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
startFade : function (element, fadeIn, immediate) {
|
startFade : function (element, fadeIn, immediate) {
|
||||||
if (element.className == "controlBar" && fadeIn) {
|
if (element.classList.contains("controlBar") && fadeIn) {
|
||||||
// Bug 493523, the scrubber doesn't call valueChanged while hidden,
|
// Bug 493523, the scrubber doesn't call valueChanged while hidden,
|
||||||
// so our dependent state (eg, timestamp in the thumb) will be stale.
|
// so our dependent state (eg, timestamp in the thumb) will be stale.
|
||||||
// As a workaround, update it manually when it first becomes unhidden.
|
// As a workaround, update it manually when it first becomes unhidden.
|
||||||
|
@ -975,11 +975,11 @@
|
||||||
// when we remove the attribute.
|
// when we remove the attribute.
|
||||||
element.clientTop;
|
element.clientTop;
|
||||||
element.removeAttribute("fadeout");
|
element.removeAttribute("fadeout");
|
||||||
if (element.className == "controlBar")
|
if (element.classList.contains("controlBar"))
|
||||||
this.controlsSpacer.removeAttribute("hideCursor");
|
this.controlsSpacer.removeAttribute("hideCursor");
|
||||||
} else {
|
} else {
|
||||||
element.setAttribute("fadeout", true);
|
element.setAttribute("fadeout", true);
|
||||||
if (element.className == "controlBar" && !this.hasError() &&
|
if (element.classList.contains("controlBar") && !this.hasError() &&
|
||||||
document.mozFullScreenElement == this.video)
|
document.mozFullScreenElement == this.video)
|
||||||
this.controlsSpacer.setAttribute("hideCursor", true);
|
this.controlsSpacer.setAttribute("hideCursor", true);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,14 @@ const REGEX_HSL_3_TUPLE = /^\bhsl\(([\d.]+),\s*([\d.]+%),\s*([\d.]+%)\)$/i;
|
||||||
*/
|
*/
|
||||||
const REGEX_ALL_COLORS = /#[0-9a-fA-F]{3}\b|#[0-9a-fA-F]{6}\b|hsl\(.*?\)|hsla\(.*?\)|rgba?\(.*?\)|\b[a-zA-Z-]+\b/g;
|
const REGEX_ALL_COLORS = /#[0-9a-fA-F]{3}\b|#[0-9a-fA-F]{6}\b|hsl\(.*?\)|hsla\(.*?\)|rgba?\(.*?\)|\b[a-zA-Z-]+\b/g;
|
||||||
|
|
||||||
|
const SPECIALVALUES = new Set([
|
||||||
|
"currentcolor",
|
||||||
|
"initial",
|
||||||
|
"inherit",
|
||||||
|
"transparent",
|
||||||
|
"unset"
|
||||||
|
]);
|
||||||
|
|
||||||
let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
|
let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,7 +97,7 @@ CssColor.prototype = {
|
||||||
authored: null,
|
authored: null,
|
||||||
|
|
||||||
get hasAlpha() {
|
get hasAlpha() {
|
||||||
if (!this.valid || this.transparent) {
|
if (!this.valid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this._getRGBATuple().a !== 1;
|
return this._getRGBATuple().a !== 1;
|
||||||
|
@ -99,28 +107,35 @@ CssColor.prototype = {
|
||||||
return this._validateColor(this.authored);
|
return this._validateColor(this.authored);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true for all transparent values e.g. rgba(0, 0, 0, 0).
|
||||||
|
*/
|
||||||
get transparent() {
|
get transparent() {
|
||||||
try {
|
try {
|
||||||
let tuple = this._getRGBATuple();
|
let tuple = this._getRGBATuple();
|
||||||
return tuple === "transparent";
|
return !(tuple.r || tuple.g || tuple.b || tuple.a);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get specialValue() {
|
||||||
|
if (SPECIALVALUES.has(this.authored)) {
|
||||||
|
return this.authored;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
get name() {
|
get name() {
|
||||||
if (!this.valid) {
|
if (!this.valid) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
if (this.authored === "transparent") {
|
if (this.specialValue) {
|
||||||
return "transparent";
|
return this.specialValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let tuple = this._getRGBATuple();
|
let tuple = this._getRGBATuple();
|
||||||
|
|
||||||
if (tuple === "transparent") {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
if (tuple.a !== 1) {
|
if (tuple.a !== 1) {
|
||||||
return this.rgb;
|
return this.rgb;
|
||||||
}
|
}
|
||||||
|
@ -135,12 +150,12 @@ CssColor.prototype = {
|
||||||
if (!this.valid) {
|
if (!this.valid) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
if (this.specialValue) {
|
||||||
|
return this.specialValue;
|
||||||
|
}
|
||||||
if (this.hasAlpha) {
|
if (this.hasAlpha) {
|
||||||
return this.rgba;
|
return this.rgba;
|
||||||
}
|
}
|
||||||
if (this.transparent) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
|
|
||||||
let hex = this.longHex;
|
let hex = this.longHex;
|
||||||
if (hex.charAt(1) == hex.charAt(2) &&
|
if (hex.charAt(1) == hex.charAt(2) &&
|
||||||
|
@ -155,12 +170,12 @@ CssColor.prototype = {
|
||||||
if (!this.valid) {
|
if (!this.valid) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
if (this.specialValue) {
|
||||||
|
return this.specialValue;
|
||||||
|
}
|
||||||
if (this.hasAlpha) {
|
if (this.hasAlpha) {
|
||||||
return this.rgba;
|
return this.rgba;
|
||||||
}
|
}
|
||||||
if (this.transparent) {
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
return this.rgb.replace(/\brgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/gi, function(_, r, g, b) {
|
return this.rgb.replace(/\brgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/gi, function(_, r, g, b) {
|
||||||
return "#" + ((1 << 24) + (r << 16) + (g << 8) + (b << 0)).toString(16).substr(-6).toUpperCase();
|
return "#" + ((1 << 24) + (r << 16) + (g << 8) + (b << 0)).toString(16).substr(-6).toUpperCase();
|
||||||
});
|
});
|
||||||
|
@ -170,8 +185,8 @@ CssColor.prototype = {
|
||||||
if (!this.valid) {
|
if (!this.valid) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
if (this.transparent) {
|
if (this.specialValue) {
|
||||||
return "transparent";
|
return this.specialValue;
|
||||||
}
|
}
|
||||||
if (!this.hasAlpha) {
|
if (!this.hasAlpha) {
|
||||||
if (this.authored.startsWith("rgb(")) {
|
if (this.authored.startsWith("rgb(")) {
|
||||||
|
@ -188,8 +203,8 @@ CssColor.prototype = {
|
||||||
if (!this.valid) {
|
if (!this.valid) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
if (this.transparent) {
|
if (this.specialValue) {
|
||||||
return "transparent";
|
return this.specialValue;
|
||||||
}
|
}
|
||||||
if (this.authored.startsWith("rgba(")) {
|
if (this.authored.startsWith("rgba(")) {
|
||||||
// The color is valid and begins with rgba(. Return the authored value.
|
// The color is valid and begins with rgba(. Return the authored value.
|
||||||
|
@ -206,8 +221,8 @@ CssColor.prototype = {
|
||||||
if (!this.valid) {
|
if (!this.valid) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
if (this.transparent) {
|
if (this.specialValue) {
|
||||||
return "transparent";
|
return this.specialValue;
|
||||||
}
|
}
|
||||||
if (this.authored.startsWith("hsl(")) {
|
if (this.authored.startsWith("hsl(")) {
|
||||||
// The color is valid and begins with hsl(. Return the authored value.
|
// The color is valid and begins with hsl(. Return the authored value.
|
||||||
|
@ -223,8 +238,8 @@ CssColor.prototype = {
|
||||||
if (!this.valid) {
|
if (!this.valid) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
if (this.transparent) {
|
if (this.specialValue) {
|
||||||
return "transparent";
|
return this.specialValue;
|
||||||
}
|
}
|
||||||
if (this.authored.startsWith("hsla(")) {
|
if (this.authored.startsWith("hsla(")) {
|
||||||
// The color is valid and begins with hsla(. Return the authored value.
|
// The color is valid and begins with hsla(. Return the authored value.
|
||||||
|
@ -290,7 +305,7 @@ CssColor.prototype = {
|
||||||
let computed = win.getComputedStyle(span).color;
|
let computed = win.getComputedStyle(span).color;
|
||||||
|
|
||||||
if (computed === "transparent") {
|
if (computed === "transparent") {
|
||||||
return "transparent";
|
return {r: 0, g: 0, b: 0, a: 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
let rgba = computed.match(REGEX_RGBA_4_TUPLE);
|
let rgba = computed.match(REGEX_RGBA_4_TUPLE);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче