Bug 1496827 - Remove the "notification" binding. r=bgrins
Differential Revision: https://phabricator.services.mozilla.com/D11650
This commit is contained in:
@@ -215,7 +215,6 @@ var CaptivePortalWatcher = {
|
||||
// Returning true prevents the notification from closing.
|
||||
return true;
|
||||
},
|
||||
isDefault: true,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -482,8 +482,7 @@ var gPluginHandler = {
|
||||
let crashurl = formatURL("app.support.baseURL", true);
|
||||
crashurl += "plugin-crashed-notificationbar";
|
||||
link.href = crashurl;
|
||||
let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
|
||||
description.appendChild(link);
|
||||
notification.messageText.appendChild(link);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1058,11 +1058,6 @@ browser[tabmodalPromptShowing] {
|
||||
-moz-box-align: end;
|
||||
}
|
||||
|
||||
/* Translation */
|
||||
notification[value="translation"] {
|
||||
-moz-binding: url("chrome://browser/content/translation-infobar.xml#translationbar");
|
||||
}
|
||||
|
||||
/*** Visibility of downloads indicator controls ***/
|
||||
|
||||
/* Bug 924050: If we've loaded the indicator, for now we hide it in the menu panel,
|
||||
|
||||
@@ -242,6 +242,11 @@ XPCOMUtils.defineLazyGetter(this, "Win7Features", function() {
|
||||
return null;
|
||||
});
|
||||
|
||||
customElements.setElementCreationCallback("translation-notification", () => {
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://browser/content/translation-notification.js", window);
|
||||
});
|
||||
|
||||
var gBrowser;
|
||||
var gLastValidURLStr = "";
|
||||
var gInPrintPreviewMode = false;
|
||||
|
||||
@@ -47,10 +47,10 @@ async function test_decoder_doctor_notification(data, notificationMessage,
|
||||
}
|
||||
ok(notification, "Got decoder-doctor-notification notification");
|
||||
|
||||
is(notification.getAttribute("label"), notificationMessage,
|
||||
is(notification.messageText.textContent, notificationMessage,
|
||||
"notification message should match expectation");
|
||||
|
||||
let button = notification.children[0];
|
||||
let button = notification.querySelector("button");
|
||||
if (!label) {
|
||||
ok(!button, "There should not be button");
|
||||
return;
|
||||
|
||||
@@ -100,7 +100,7 @@ async function testRealRefresh(refreshPage, delay) {
|
||||
let notification = notificationBox.currentNotification;
|
||||
|
||||
ok(notification, "Notification should be visible");
|
||||
is(notification.value, "refresh-blocked",
|
||||
is(notification.getAttribute("value"), "refresh-blocked",
|
||||
"Should be showing the right notification");
|
||||
|
||||
// Then click the button to allow the refresh.
|
||||
|
||||
@@ -45,7 +45,7 @@ add_task(async function() {
|
||||
let notification = box.currentNotification;
|
||||
|
||||
ok(notification, "Notification should be visible");
|
||||
is(notification.value, "drmContentDisabled",
|
||||
is(notification.getAttribute("value"), "drmContentDisabled",
|
||||
"Should be showing the right notification");
|
||||
|
||||
// Verify the "Enable DRM" button is there.
|
||||
|
||||
@@ -27,7 +27,7 @@ add_task(async function() {
|
||||
ok(notification, "Infobar was shown.");
|
||||
is(notification.priority, notificationBox.PRIORITY_WARNING_MEDIUM,
|
||||
"Correct priority.");
|
||||
is(notification.getAttribute("label"),
|
||||
is(notification.messageText.textContent,
|
||||
"The GlobalTestPlugin plugin has crashed.",
|
||||
"Correct message.");
|
||||
});
|
||||
|
||||
@@ -14,19 +14,8 @@ function promiseNotification(aBrowser, value, expected, input) {
|
||||
let notificationBox = aBrowser.getNotificationBox(aBrowser.selectedBrowser);
|
||||
if (expected) {
|
||||
info("Waiting for " + value + " notification");
|
||||
let checkForNotification = function() {
|
||||
if (notificationBox.getNotificationWithValue(value)) {
|
||||
info("Saw the notification");
|
||||
notificationObserver.disconnect();
|
||||
notificationObserver = null;
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
if (notificationObserver) {
|
||||
notificationObserver.disconnect();
|
||||
}
|
||||
notificationObserver = new MutationObserver(checkForNotification);
|
||||
notificationObserver.observe(notificationBox.stack, {childList: true});
|
||||
resolve(BrowserTestUtils.waitForNotificationInNotificationBox(
|
||||
notificationBox, value));
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
is(notificationBox.getNotificationWithValue(value), null,
|
||||
@@ -152,7 +141,7 @@ function get_test_function_for_localhost_with_hostname(hostName, isPrivate) {
|
||||
let notificationBox = browser.getNotificationBox(tab.linkedBrowser);
|
||||
let notification = notificationBox.getNotificationWithValue("keyword-uri-fixup");
|
||||
let docLoadPromise = waitForDocLoadAndStopIt("http://" + hostName + "/", tab.linkedBrowser);
|
||||
notification.querySelector(".notification-button-default").click();
|
||||
notification.querySelector("button").click();
|
||||
|
||||
// check pref value
|
||||
let prefValue = Services.prefs.getBoolPref(pref);
|
||||
|
||||
@@ -49,7 +49,7 @@ add_task(async function test_slow_content_script() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
|
||||
|
||||
let notification = await BrowserTestUtils.waitForGlobalNotificationBar(window, "process-hang");
|
||||
let text = document.getAnonymousElementByAttribute(notification, "anonid", "messageText").textContent;
|
||||
let text = notification.messageText.textContent;
|
||||
|
||||
ok(text.includes("\u201cSlow Script Extension\u201d"),
|
||||
"Label is correct");
|
||||
|
||||
@@ -22,13 +22,12 @@ add_task(async function() {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
is(notification.type, "info", "We expect this notification to have the type of 'info'.");
|
||||
isnot(notification.image, null, "We expect this notification to have an icon.");
|
||||
is(notification.getAttribute("type"), "info",
|
||||
"We expect this notification to have the type of 'info'.");
|
||||
ok(notification.messageImage.getAttribute("src"),
|
||||
"We expect this notification to have an icon.");
|
||||
|
||||
let buttons = notification.getElementsByClassName("notification-button-default");
|
||||
is(buttons.length, 1, "We expect see one default button.");
|
||||
|
||||
buttons = notification.getElementsByClassName("notification-button");
|
||||
let buttons = notification.getElementsByClassName("notification-button");
|
||||
is(buttons.length, 1, "We expect see one button.");
|
||||
|
||||
let button = buttons[0];
|
||||
|
||||
@@ -333,7 +333,8 @@ function testShowNotification() {
|
||||
ok(updateBox, "Update notification box should have been displayed");
|
||||
if (updateBox) {
|
||||
if (testCase.notificationText) {
|
||||
is(updateBox.label, testCase.notificationText, "Update notification box " +
|
||||
is(updateBox.messageText.textContent, testCase.notificationText,
|
||||
"Update notification box " +
|
||||
"should have the label provided by the update");
|
||||
}
|
||||
if (testCase.notificationButtonLabel) {
|
||||
|
||||
@@ -228,7 +228,8 @@ TranslationUI.prototype = {
|
||||
showTranslationInfoBar() {
|
||||
let notificationBox = this.notificationBox;
|
||||
let notif = notificationBox.appendNotification("", "translation", null,
|
||||
notificationBox.PRIORITY_INFO_HIGH);
|
||||
notificationBox.PRIORITY_INFO_HIGH, null, null,
|
||||
"translation-notification");
|
||||
notif.init(this);
|
||||
return notif;
|
||||
},
|
||||
|
||||
11
browser/components/translation/content/.eslintrc.js
Normal file
11
browser/components/translation/content/.eslintrc.js
Normal file
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"env": {
|
||||
"mozilla/browser-window": true,
|
||||
},
|
||||
|
||||
"plugins": [
|
||||
"mozilla",
|
||||
]
|
||||
};
|
||||
@@ -2,5 +2,5 @@
|
||||
# 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/.
|
||||
browser.jar:
|
||||
content/browser/translation-infobar.xml
|
||||
content/browser/translation-notification.js
|
||||
content/browser/microsoft-translator-attribution.png
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
7
browser/components/translation/content/moz.build
Normal file
7
browser/components/translation/content/moz.build
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
@@ -0,0 +1,349 @@
|
||||
/* 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";
|
||||
|
||||
class MozTranslationNotification extends MozElements.Notification {
|
||||
connectedCallback() {
|
||||
this.appendChild(MozXULElement.parseXULToFragment(`
|
||||
<hbox anonid="details" align="center" flex="1">
|
||||
<image class="translate-infobar-element messageImage"/>
|
||||
<panel anonid="welcomePanel" class="translation-welcome-panel" type="arrow" align="start">
|
||||
<image class="translation-welcome-logo"/>
|
||||
<vbox flex="1" class="translation-welcome-content">
|
||||
<description class="translation-welcome-headline" anonid="welcomeHeadline"/>
|
||||
<description class="translation-welcome-body" anonid="welcomeBody"/>
|
||||
<hbox align="center">
|
||||
<label anonid="learnMore" class="plain text-link" onclick="openTrustedLinkIn('https://support.mozilla.org/kb/automatic-translation', 'tab'); this.parentNode.parentNode.parentNode.hidePopup();"/>
|
||||
<spacer flex="1"/>
|
||||
<button class="translate-infobar-element" anonid="thanksButton" onclick="this.parentNode.parentNode.parentNode.hidePopup();"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</panel>
|
||||
<deck anonid="translationStates" selectedIndex="0">
|
||||
<hbox class="translate-offer-box" align="center">
|
||||
<label class="translate-infobar-element" value="&translation.thisPageIsIn.label;"/>
|
||||
<menulist class="translate-infobar-element" anonid="detectedLanguage">
|
||||
<menupopup/>
|
||||
</menulist>
|
||||
<label class="translate-infobar-element" value="&translation.translateThisPage.label;"/>
|
||||
<button class="translate-infobar-element" label="&translation.translate.button;" anonid="translate" oncommand="this.closest('notification').translate();"/>
|
||||
<button class="translate-infobar-element" label="&translation.notNow.button;" anonid="notNow" oncommand="this.closest('notification').closeCommand();"/>
|
||||
</hbox>
|
||||
<vbox class="translating-box" pack="center">
|
||||
<label class="translate-infobar-element" value="&translation.translatingContent.label;"/>
|
||||
</vbox>
|
||||
<hbox class="translated-box" align="center">
|
||||
<label class="translate-infobar-element" value="&translation.translatedFrom.label;"/>
|
||||
<menulist class="translate-infobar-element" anonid="fromLanguage" oncommand="this.closest('notification').translate();">
|
||||
<menupopup/>
|
||||
</menulist>
|
||||
<label class="translate-infobar-element" value="&translation.translatedTo.label;"/>
|
||||
<menulist class="translate-infobar-element" anonid="toLanguage" oncommand="this.closest('notification').translate();">
|
||||
<menupopup/>
|
||||
</menulist>
|
||||
<label class="translate-infobar-element" value="&translation.translatedToSuffix.label;"/>
|
||||
<button anonid="showOriginal" class="translate-infobar-element" label="&translation.showOriginal.button;" oncommand="this.closest('notification').showOriginal();"/>
|
||||
<button anonid="showTranslation" class="translate-infobar-element" label="&translation.showTranslation.button;" oncommand="this.closest('notification').showTranslation();"/>
|
||||
</hbox>
|
||||
<hbox class="translation-error" align="center">
|
||||
<label class="translate-infobar-element" value="&translation.errorTranslating.label;"/>
|
||||
<button class="translate-infobar-element" label="&translation.tryAgain.button;" anonid="tryAgain" oncommand="this.closest('notification').translate();"/>
|
||||
</hbox>
|
||||
<vbox class="translation-unavailable" pack="center">
|
||||
<label class="translate-infobar-element" value="&translation.serviceUnavailable.label;"/>
|
||||
</vbox>
|
||||
</deck>
|
||||
<spacer flex="1"/>
|
||||
<button type="menu" class="translate-infobar-element options-menu-button" anonid="options" label="&translation.options.menu;">
|
||||
<menupopup class="translation-menupopup cui-widget-panel cui-widget-panelview
|
||||
cui-widget-panelWithFooter PanelUI-subView" onpopupshowing="this.closest('notification').optionsShowing();">
|
||||
<menuitem anonid="neverForLanguage" oncommand="this.closest('notification').neverForLanguage();"/>
|
||||
<menuitem anonid="neverForSite" oncommand="this.closest('notification').neverForSite();" label="&translation.options.neverForSite.label;" accesskey="&translation.options.neverForSite.accesskey;"/>
|
||||
<menuseparator/>
|
||||
<menuitem oncommand="openPreferences('paneGeneral', {origin:'translationInfobar'});" label="&translation.options.preferences.label;" accesskey="&translation.options.preferences.accesskey;"/>
|
||||
<menuitem class="subviewbutton panel-subview-footer" oncommand="this.closest('notification').openProviderAttribution();">
|
||||
<deck anonid="translationEngine" selectedIndex="0">
|
||||
<hbox class="translation-attribution">
|
||||
<label/>
|
||||
<image src="chrome://browser/content/microsoft-translator-attribution.png" aria-label="Microsoft Translator"/>
|
||||
<label/>
|
||||
</hbox>
|
||||
<label class="translation-attribution"/>
|
||||
</deck>
|
||||
</menuitem>
|
||||
</menupopup>
|
||||
</button>
|
||||
</hbox>
|
||||
<toolbarbutton anonid="closeButton" ondblclick="event.stopPropagation();"
|
||||
class="messageCloseButton close-icon tabbable"
|
||||
tooltiptext="&closeNotification.tooltip;"
|
||||
oncommand="this.parentNode.closeCommand();"/>
|
||||
`, [
|
||||
"chrome://global/locale/notification.dtd",
|
||||
"chrome://browser/locale/translation.dtd",
|
||||
]));
|
||||
|
||||
for (let [propertyName, selector] of [
|
||||
["details", "[anonid=details]"],
|
||||
["messageImage", ".messageImage"],
|
||||
["spacer", "[anonid=spacer]"],
|
||||
]) {
|
||||
this[propertyName] = this.querySelector(selector);
|
||||
}
|
||||
}
|
||||
|
||||
set state(val) {
|
||||
let deck = this._getAnonElt("translationStates");
|
||||
|
||||
let activeElt = document.activeElement;
|
||||
if (activeElt && deck.contains(activeElt))
|
||||
activeElt.blur();
|
||||
|
||||
let stateName;
|
||||
for (let name of ["OFFER", "TRANSLATING", "TRANSLATED", "ERROR"]) {
|
||||
if (Translation["STATE_" + name] == val) {
|
||||
stateName = name.toLowerCase();
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.setAttribute("state", stateName);
|
||||
|
||||
if (val == Translation.STATE_TRANSLATED)
|
||||
this._handleButtonHiding();
|
||||
|
||||
deck.selectedIndex = val;
|
||||
}
|
||||
|
||||
get state() {
|
||||
return this._getAnonElt("translationStates").selectedIndex;
|
||||
}
|
||||
|
||||
init(aTranslation) {
|
||||
this.translation = aTranslation;
|
||||
|
||||
let sortByLocalizedName = function(aList) {
|
||||
let names = Services.intl.getLanguageDisplayNames(undefined, aList);
|
||||
return aList.map((code, i) => [code, names[i]])
|
||||
.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
};
|
||||
|
||||
// Fill the lists of supported source languages.
|
||||
let detectedLanguage = this._getAnonElt("detectedLanguage");
|
||||
let fromLanguage = this._getAnonElt("fromLanguage");
|
||||
let sourceLanguages =
|
||||
sortByLocalizedName(Translation.supportedSourceLanguages);
|
||||
for (let [code, name] of sourceLanguages) {
|
||||
detectedLanguage.appendItem(name, code);
|
||||
fromLanguage.appendItem(name, code);
|
||||
}
|
||||
detectedLanguage.value = this.translation.detectedLanguage;
|
||||
|
||||
// translatedFrom is only set if we have already translated this page.
|
||||
if (aTranslation.translatedFrom)
|
||||
fromLanguage.value = aTranslation.translatedFrom;
|
||||
|
||||
// Fill the list of supported target languages.
|
||||
let toLanguage = this._getAnonElt("toLanguage");
|
||||
let targetLanguages =
|
||||
sortByLocalizedName(Translation.supportedTargetLanguages);
|
||||
for (let [code, name] of targetLanguages)
|
||||
toLanguage.appendItem(name, code);
|
||||
|
||||
if (aTranslation.translatedTo)
|
||||
toLanguage.value = aTranslation.translatedTo;
|
||||
|
||||
if (aTranslation.state)
|
||||
this.state = aTranslation.state;
|
||||
|
||||
// Show attribution for the preferred translator.
|
||||
let engineIndex = Object.keys(Translation.supportedEngines)
|
||||
.indexOf(Translation.translationEngine);
|
||||
// We currently only have attribution for the Bing and Yandex engines.
|
||||
if (engineIndex >= 0) {
|
||||
--engineIndex;
|
||||
}
|
||||
let attributionNode = this._getAnonElt("translationEngine");
|
||||
if (engineIndex != -1) {
|
||||
attributionNode.selectedIndex = engineIndex;
|
||||
} else {
|
||||
// Hide the attribution menuitem
|
||||
let footer = attributionNode.parentNode;
|
||||
footer.hidden = true;
|
||||
// Make the 'Translation preferences' item the new footer.
|
||||
footer = footer.previousSibling;
|
||||
footer.setAttribute("class", "subviewbutton panel-subview-footer");
|
||||
// And hide the menuseparator.
|
||||
footer.previousSibling.hidden = true;
|
||||
}
|
||||
|
||||
const kWelcomePref = "browser.translation.ui.welcomeMessageShown";
|
||||
if (Services.prefs.prefHasUserValue(kWelcomePref) ||
|
||||
this.translation.browser != gBrowser.selectedBrowser)
|
||||
return;
|
||||
|
||||
this.addEventListener("transitionend", function() {
|
||||
// These strings are hardcoded because they need to reach beta
|
||||
// without riding the trains.
|
||||
let localizedStrings = {
|
||||
en: ["Hey look! It's something new!",
|
||||
"Now the Web is even more accessible with our new in-page translation feature. Click the translate button to try it!",
|
||||
"Learn more.",
|
||||
"Thanks",
|
||||
],
|
||||
"es-AR": ["\xA1Mir\xE1! \xA1Hay algo nuevo!",
|
||||
"Ahora la web es a\xFAn m\xE1s accesible con nuestra nueva funcionalidad de traducci\xF3n integrada. \xA1Hac\xE9 clic en el bot\xF3n traducir para probarla!",
|
||||
"Conoc\xE9 m\xE1s.",
|
||||
"Gracias",
|
||||
],
|
||||
"es-ES": ["\xA1Mira! \xA1Hay algo nuevo!",
|
||||
"Con la nueva funcionalidad de traducci\xF3n integrada, ahora la Web es a\xFAn m\xE1s accesible. \xA1Pulsa el bot\xF3n Traducir y pru\xE9bala!",
|
||||
"M\xE1s informaci\xF3n.",
|
||||
"Gracias",
|
||||
],
|
||||
pl: ["Sp\xF3jrz tutaj! To co\u015B nowego!",
|
||||
"Sie\u0107 sta\u0142a si\u0119 w\u0142a\u015Bnie jeszcze bardziej dost\u0119pna dzi\u0119ki opcji bezpo\u015Bredniego t\u0142umaczenia stron. Kliknij przycisk t\u0142umaczenia, aby spr\xF3bowa\u0107!",
|
||||
"Dowiedz si\u0119 wi\u0119cej",
|
||||
"Dzi\u0119kuj\u0119",
|
||||
],
|
||||
tr: ["Bak\u0131n, burada yeni bir \u015Fey var!",
|
||||
"Yeni sayfa i\xE7i \xE7eviri \xF6zelli\u011Fimiz sayesinde Web art\u0131k \xE7ok daha anla\u015F\u0131l\u0131r olacak. Denemek i\xE7in \xC7evir d\xFC\u011Fmesine t\u0131klay\u0131n!",
|
||||
"Daha fazla bilgi al\u0131n.",
|
||||
"Te\u015Fekk\xFCrler",
|
||||
],
|
||||
vi: ["Nh\xECn n\xE0y! \u0110\u1ED3 m\u1EDBi!",
|
||||
"Gi\u1EDD \u0111\xE2y ch\xFAng ta c\xF3 th\u1EC3 ti\u1EBFp c\u1EADn web d\u1EC5 d\xE0ng h\u01A1n n\u1EEFa v\u1EDBi t\xEDnh n\u0103ng d\u1ECBch ngay trong trang. Hay nh\u1EA5n n\xFAt d\u1ECBch \u0111\u1EC3 th\u1EED!",
|
||||
"T\xECm hi\u1EC3u th\xEAm.",
|
||||
"C\u1EA3m \u01A1n",
|
||||
],
|
||||
};
|
||||
|
||||
let locale = Services.locale.appLocaleAsLangTag;
|
||||
if (!(locale in localizedStrings))
|
||||
locale = "en";
|
||||
let strings = localizedStrings[locale];
|
||||
|
||||
this._getAnonElt("welcomeHeadline").setAttribute("value", strings[0]);
|
||||
this._getAnonElt("welcomeBody").textContent = strings[1];
|
||||
this._getAnonElt("learnMore").setAttribute("value", strings[2]);
|
||||
this._getAnonElt("thanksButton").setAttribute("label", strings[3]);
|
||||
|
||||
let panel = this._getAnonElt("welcomePanel");
|
||||
panel.openPopup(this._getAnonElt("messageImage"),
|
||||
"bottomcenter topleft");
|
||||
|
||||
Services.prefs.setBoolPref(kWelcomePref, true);
|
||||
}, { once: true });
|
||||
}
|
||||
|
||||
_getAnonElt(aAnonId) {
|
||||
return this.querySelector("[anonid=" + aAnonId + "]");
|
||||
}
|
||||
|
||||
translate() {
|
||||
if (this.state == Translation.STATE_OFFER) {
|
||||
this._getAnonElt("fromLanguage").value =
|
||||
this._getAnonElt("detectedLanguage").value;
|
||||
this._getAnonElt("toLanguage").value =
|
||||
Translation.defaultTargetLanguage;
|
||||
}
|
||||
|
||||
this.translation.translate(this._getAnonElt("fromLanguage").value,
|
||||
this._getAnonElt("toLanguage").value);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called when the infobar should be closed per user's wish (e.g.
|
||||
* by clicking the notification's close button
|
||||
*/
|
||||
closeCommand() {
|
||||
this.close();
|
||||
this.translation.infobarClosed();
|
||||
}
|
||||
|
||||
_handleButtonHiding() {
|
||||
let originalShown = this.translation.originalShown;
|
||||
this._getAnonElt("showOriginal").hidden = originalShown;
|
||||
this._getAnonElt("showTranslation").hidden = !originalShown;
|
||||
}
|
||||
|
||||
showOriginal() {
|
||||
this.translation.showOriginalContent();
|
||||
this._handleButtonHiding();
|
||||
}
|
||||
|
||||
showTranslation() {
|
||||
this.translation.showTranslatedContent();
|
||||
this._handleButtonHiding();
|
||||
}
|
||||
|
||||
optionsShowing() {
|
||||
// Get the source language name.
|
||||
let lang;
|
||||
if (this.state == Translation.STATE_OFFER)
|
||||
lang = this._getAnonElt("detectedLanguage").value;
|
||||
else {
|
||||
lang = this._getAnonElt("fromLanguage").value;
|
||||
|
||||
// If we have never attempted to translate the page before the
|
||||
// service became unavailable, "fromLanguage" isn't set.
|
||||
if (!lang && this.state == Translation.STATE_UNAVAILABLE)
|
||||
lang = this.translation.detectedLanguage;
|
||||
}
|
||||
|
||||
let langName = Services.intl.getLanguageDisplayNames(undefined, [lang])[0];
|
||||
|
||||
// Set the label and accesskey on the menuitem.
|
||||
let bundle =
|
||||
Services.strings.createBundle("chrome://browser/locale/translation.properties");
|
||||
let item = this._getAnonElt("neverForLanguage");
|
||||
const kStrId = "translation.options.neverForLanguage";
|
||||
item.setAttribute("label",
|
||||
bundle.formatStringFromName(kStrId + ".label", [langName], 1));
|
||||
item.setAttribute("accesskey",
|
||||
bundle.GetStringFromName(kStrId + ".accesskey"));
|
||||
item.langCode = lang;
|
||||
|
||||
// We may need to disable the menuitems if they have already been used.
|
||||
// Check if translation is already disabled for this language:
|
||||
let neverForLangs =
|
||||
Services.prefs.getCharPref("browser.translation.neverForLanguages");
|
||||
item.disabled = neverForLangs.split(",").includes(lang);
|
||||
|
||||
// Check if translation is disabled for the domain:
|
||||
let uri = this.translation.browser.currentURI;
|
||||
let perms = Services.perms;
|
||||
item = this._getAnonElt("neverForSite");
|
||||
item.disabled =
|
||||
perms.testExactPermission(uri, "translate") == perms.DENY_ACTION;
|
||||
}
|
||||
|
||||
neverForLanguage() {
|
||||
const kPrefName = "browser.translation.neverForLanguages";
|
||||
|
||||
let val = Services.prefs.getCharPref(kPrefName);
|
||||
if (val)
|
||||
val += ",";
|
||||
val += this._getAnonElt("neverForLanguage").langCode;
|
||||
|
||||
Services.prefs.setCharPref(kPrefName, val);
|
||||
|
||||
this.closeCommand();
|
||||
}
|
||||
|
||||
neverForSite() {
|
||||
let uri = this.translation.browser.currentURI;
|
||||
let perms = Services.perms;
|
||||
perms.add(uri, "translate", perms.DENY_ACTION);
|
||||
|
||||
this.closeCommand();
|
||||
}
|
||||
|
||||
openProviderAttribution() {
|
||||
Translation.openProviderAttribution();
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("translation-notification", MozTranslationNotification,
|
||||
{ extends: "notification" });
|
||||
@@ -2,6 +2,10 @@
|
||||
# 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/.
|
||||
|
||||
DIRS += [
|
||||
'content',
|
||||
]
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Firefox", "Translation")
|
||||
|
||||
@@ -17,8 +21,6 @@ EXTRA_JS_MODULES.translation = [
|
||||
'YandexTranslator.jsm'
|
||||
]
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'test/browser.ini'
|
||||
]
|
||||
|
||||
@@ -1,439 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- 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 bindings [
|
||||
<!ENTITY % notificationDTD SYSTEM "chrome://global/locale/notification.dtd">
|
||||
%notificationDTD;
|
||||
<!ENTITY % translationDTD SYSTEM "chrome://browser/locale/translation.dtd" >
|
||||
%translationDTD;
|
||||
]>
|
||||
|
||||
<bindings id="translationBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||
<binding id="translationbar" extends="chrome://global/content/bindings/notification.xml#notification">
|
||||
<content>
|
||||
<xul:hbox anonid="details" align="center" flex="1">
|
||||
<xul:image class="translate-infobar-element messageImage"
|
||||
anonid="messageImage"/>
|
||||
<xul:panel anonid="welcomePanel" class="translation-welcome-panel"
|
||||
type="arrow" align="start">
|
||||
<xul:image class="translation-welcome-logo"/>
|
||||
<xul:vbox flex="1" class="translation-welcome-content">
|
||||
<xul:description class="translation-welcome-headline"
|
||||
anonid="welcomeHeadline"/>
|
||||
<xul:description class="translation-welcome-body" anonid="welcomeBody"/>
|
||||
<xul:hbox align="center">
|
||||
<xul:label anonid="learnMore" class="plain text-link"
|
||||
onclick="openTrustedLinkIn('https://support.mozilla.org/kb/automatic-translation', 'tab'); this.parentNode.parentNode.parentNode.hidePopup();"/>
|
||||
<xul:spacer flex="1"/>
|
||||
<xul:button class="translate-infobar-element" anonid="thanksButton"
|
||||
onclick="this.parentNode.parentNode.parentNode.hidePopup();"/>
|
||||
</xul:hbox>
|
||||
</xul:vbox>
|
||||
</xul:panel>
|
||||
<xul:deck anonid="translationStates" selectedIndex="0">
|
||||
|
||||
<!-- offer to translate -->
|
||||
<xul:hbox class="translate-offer-box" align="center">
|
||||
<xul:label class="translate-infobar-element" value="&translation.thisPageIsIn.label;"/>
|
||||
<xul:menulist class="translate-infobar-element" anonid="detectedLanguage">
|
||||
<xul:menupopup/>
|
||||
</xul:menulist>
|
||||
<xul:label class="translate-infobar-element" value="&translation.translateThisPage.label;"/>
|
||||
<xul:button class="translate-infobar-element"
|
||||
label="&translation.translate.button;"
|
||||
anonid="translate"
|
||||
oncommand="document.getBindingParent(this).translate();"/>
|
||||
<xul:button class="translate-infobar-element"
|
||||
label="&translation.notNow.button;" anonid="notNow"
|
||||
oncommand="document.getBindingParent(this).closeCommand();"/>
|
||||
</xul:hbox>
|
||||
|
||||
<!-- translating -->
|
||||
<xul:vbox class="translating-box" pack="center">
|
||||
<xul:label class="translate-infobar-element"
|
||||
value="&translation.translatingContent.label;"/>
|
||||
</xul:vbox>
|
||||
|
||||
<!-- translated -->
|
||||
<xul:hbox class="translated-box" align="center">
|
||||
<xul:label class="translate-infobar-element"
|
||||
value="&translation.translatedFrom.label;"/>
|
||||
<xul:menulist class="translate-infobar-element"
|
||||
anonid="fromLanguage"
|
||||
oncommand="document.getBindingParent(this).translate()">
|
||||
<xul:menupopup/>
|
||||
</xul:menulist>
|
||||
<xul:label class="translate-infobar-element"
|
||||
value="&translation.translatedTo.label;"/>
|
||||
<xul:menulist class="translate-infobar-element"
|
||||
anonid="toLanguage"
|
||||
oncommand="document.getBindingParent(this).translate()">
|
||||
<xul:menupopup/>
|
||||
</xul:menulist>
|
||||
<xul:label class="translate-infobar-element"
|
||||
value="&translation.translatedToSuffix.label;"/>
|
||||
<xul:button anonid="showOriginal"
|
||||
class="translate-infobar-element"
|
||||
label="&translation.showOriginal.button;"
|
||||
oncommand="document.getBindingParent(this).showOriginal();"/>
|
||||
<xul:button anonid="showTranslation"
|
||||
class="translate-infobar-element"
|
||||
label="&translation.showTranslation.button;"
|
||||
oncommand="document.getBindingParent(this).showTranslation();"/>
|
||||
</xul:hbox>
|
||||
|
||||
<!-- error -->
|
||||
<xul:hbox class="translation-error" align="center">
|
||||
<xul:label class="translate-infobar-element"
|
||||
value="&translation.errorTranslating.label;"/>
|
||||
<xul:button class="translate-infobar-element"
|
||||
label="&translation.tryAgain.button;"
|
||||
anonid="tryAgain"
|
||||
oncommand="document.getBindingParent(this).translate();"/>
|
||||
</xul:hbox>
|
||||
|
||||
<!-- unavailable -->
|
||||
<xul:vbox class="translation-unavailable" pack="center">
|
||||
<xul:label class="translate-infobar-element"
|
||||
value="&translation.serviceUnavailable.label;"/>
|
||||
</xul:vbox>
|
||||
|
||||
</xul:deck>
|
||||
<xul:spacer flex="1"/>
|
||||
|
||||
<xul:button type="menu"
|
||||
class="translate-infobar-element options-menu-button"
|
||||
anonid="options"
|
||||
label="&translation.options.menu;">
|
||||
<xul:menupopup class="translation-menupopup cui-widget-panel cui-widget-panelview
|
||||
cui-widget-panelWithFooter PanelUI-subView"
|
||||
onpopupshowing="document.getBindingParent(this).optionsShowing();">
|
||||
<xul:menuitem anonid="neverForLanguage"
|
||||
oncommand="document.getBindingParent(this).neverForLanguage();"/>
|
||||
<xul:menuitem anonid="neverForSite"
|
||||
oncommand="document.getBindingParent(this).neverForSite();"
|
||||
label="&translation.options.neverForSite.label;"
|
||||
accesskey="&translation.options.neverForSite.accesskey;"/>
|
||||
<xul:menuseparator/>
|
||||
<xul:menuitem oncommand="openPreferences('paneGeneral', {origin:'translationInfobar'});"
|
||||
label="&translation.options.preferences.label;"
|
||||
accesskey="&translation.options.preferences.accesskey;"/>
|
||||
<xul:menuitem class="subviewbutton panel-subview-footer"
|
||||
oncommand="document.getBindingParent(this).openProviderAttribution();">
|
||||
<xul:deck anonid="translationEngine" selectedIndex="0">
|
||||
<xul:hbox class="translation-attribution">
|
||||
<xul:label>&translation.options.attribution.beforeLogo;</xul:label>
|
||||
<xul:image src="chrome://browser/content/microsoft-translator-attribution.png"
|
||||
aria-label="Microsoft Translator"/>
|
||||
<xul:label>&translation.options.attribution.afterLogo;</xul:label>
|
||||
</xul:hbox>
|
||||
<xul:label class="translation-attribution">&translation.options.attribution.yandexTranslate;</xul:label>
|
||||
</xul:deck>
|
||||
</xul:menuitem>
|
||||
</xul:menupopup>
|
||||
</xul:button>
|
||||
|
||||
</xul:hbox>
|
||||
<xul:toolbarbutton ondblclick="event.stopPropagation();"
|
||||
anonid="closeButton"
|
||||
class="messageCloseButton close-icon tabbable"
|
||||
xbl:inherits="hidden=hideclose"
|
||||
tooltiptext="&closeNotification.tooltip;"
|
||||
oncommand="document.getBindingParent(this).closeCommand();"/>
|
||||
</content>
|
||||
<implementation>
|
||||
<property name="state"
|
||||
onget="return this._getAnonElt('translationStates').selectedIndex;">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
let deck = this._getAnonElt("translationStates");
|
||||
|
||||
let activeElt = document.activeElement;
|
||||
if (activeElt && deck.contains(activeElt))
|
||||
activeElt.blur();
|
||||
|
||||
let stateName;
|
||||
for (let name of ["OFFER", "TRANSLATING", "TRANSLATED", "ERROR"]) {
|
||||
if (Translation["STATE_" + name] == val) {
|
||||
stateName = name.toLowerCase();
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.setAttribute("state", stateName);
|
||||
|
||||
if (val == Translation.STATE_TRANSLATED)
|
||||
this._handleButtonHiding();
|
||||
|
||||
deck.selectedIndex = val;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<method name="init">
|
||||
<parameter name="aTranslation"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.translation = aTranslation;
|
||||
|
||||
let sortByLocalizedName = function(aList) {
|
||||
let names = Services.intl.getLanguageDisplayNames(undefined, aList);
|
||||
return aList.map((code, i) => [code, names[i]])
|
||||
.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
};
|
||||
|
||||
// Fill the lists of supported source languages.
|
||||
let detectedLanguage = this._getAnonElt("detectedLanguage");
|
||||
let fromLanguage = this._getAnonElt("fromLanguage");
|
||||
let sourceLanguages =
|
||||
sortByLocalizedName(Translation.supportedSourceLanguages);
|
||||
for (let [code, name] of sourceLanguages) {
|
||||
detectedLanguage.appendItem(name, code);
|
||||
fromLanguage.appendItem(name, code);
|
||||
}
|
||||
detectedLanguage.value = this.translation.detectedLanguage;
|
||||
|
||||
// translatedFrom is only set if we have already translated this page.
|
||||
if (aTranslation.translatedFrom)
|
||||
fromLanguage.value = aTranslation.translatedFrom;
|
||||
|
||||
// Fill the list of supported target languages.
|
||||
let toLanguage = this._getAnonElt("toLanguage");
|
||||
let targetLanguages =
|
||||
sortByLocalizedName(Translation.supportedTargetLanguages);
|
||||
for (let [code, name] of targetLanguages)
|
||||
toLanguage.appendItem(name, code);
|
||||
|
||||
if (aTranslation.translatedTo)
|
||||
toLanguage.value = aTranslation.translatedTo;
|
||||
|
||||
if (aTranslation.state)
|
||||
this.state = aTranslation.state;
|
||||
|
||||
// Show attribution for the preferred translator.
|
||||
let engineIndex = Object.keys(Translation.supportedEngines)
|
||||
.indexOf(Translation.translationEngine);
|
||||
// We currently only have attribution for the Bing and Yandex engines.
|
||||
if (engineIndex >= 0) {
|
||||
--engineIndex;
|
||||
}
|
||||
let attributionNode = this._getAnonElt("translationEngine");
|
||||
if (engineIndex != -1) {
|
||||
attributionNode.selectedIndex = engineIndex;
|
||||
} else {
|
||||
// Hide the attribution menuitem
|
||||
let footer = attributionNode.parentNode;
|
||||
footer.hidden = true;
|
||||
// Make the 'Translation preferences' item the new footer.
|
||||
footer = footer.previousSibling;
|
||||
footer.setAttribute("class", "subviewbutton panel-subview-footer");
|
||||
// And hide the menuseparator.
|
||||
footer.previousSibling.hidden = true;
|
||||
}
|
||||
|
||||
const kWelcomePref = "browser.translation.ui.welcomeMessageShown";
|
||||
if (Services.prefs.prefHasUserValue(kWelcomePref) ||
|
||||
this.translation.browser != gBrowser.selectedBrowser)
|
||||
return;
|
||||
|
||||
this.addEventListener("transitionend", function() {
|
||||
// These strings are hardcoded because they need to reach beta
|
||||
// without riding the trains.
|
||||
let localizedStrings = {
|
||||
en: ["Hey look! It's something new!",
|
||||
"Now the Web is even more accessible with our new in-page translation feature. Click the translate button to try it!",
|
||||
"Learn more.",
|
||||
"Thanks"],
|
||||
"es-AR": ["\xA1Mir\xE1! \xA1Hay algo nuevo!",
|
||||
"Ahora la web es a\xFAn m\xE1s accesible con nuestra nueva funcionalidad de traducci\xF3n integrada. \xA1Hac\xE9 clic en el bot\xF3n traducir para probarla!",
|
||||
"Conoc\xE9 m\xE1s.",
|
||||
"Gracias"],
|
||||
"es-ES": ["\xA1Mira! \xA1Hay algo nuevo!",
|
||||
"Con la nueva funcionalidad de traducci\xF3n integrada, ahora la Web es a\xFAn m\xE1s accesible. \xA1Pulsa el bot\xF3n Traducir y pru\xE9bala!",
|
||||
"M\xE1s informaci\xF3n.",
|
||||
"Gracias"],
|
||||
pl: ["Sp\xF3jrz tutaj! To co\u015B nowego!",
|
||||
"Sie\u0107 sta\u0142a si\u0119 w\u0142a\u015Bnie jeszcze bardziej dost\u0119pna dzi\u0119ki opcji bezpo\u015Bredniego t\u0142umaczenia stron. Kliknij przycisk t\u0142umaczenia, aby spr\xF3bowa\u0107!",
|
||||
"Dowiedz si\u0119 wi\u0119cej",
|
||||
"Dzi\u0119kuj\u0119"],
|
||||
tr: ["Bak\u0131n, burada yeni bir \u015Fey var!",
|
||||
"Yeni sayfa i\xE7i \xE7eviri \xF6zelli\u011Fimiz sayesinde Web art\u0131k \xE7ok daha anla\u015F\u0131l\u0131r olacak. Denemek i\xE7in \xC7evir d\xFC\u011Fmesine t\u0131klay\u0131n!",
|
||||
"Daha fazla bilgi al\u0131n.",
|
||||
"Te\u015Fekk\xFCrler"],
|
||||
vi: ["Nh\xECn n\xE0y! \u0110\u1ED3 m\u1EDBi!",
|
||||
"Gi\u1EDD \u0111\xE2y ch\xFAng ta c\xF3 th\u1EC3 ti\u1EBFp c\u1EADn web d\u1EC5 d\xE0ng h\u01A1n n\u1EEFa v\u1EDBi t\xEDnh n\u0103ng d\u1ECBch ngay trong trang. Hay nh\u1EA5n n\xFAt d\u1ECBch \u0111\u1EC3 th\u1EED!",
|
||||
"T\xECm hi\u1EC3u th\xEAm.",
|
||||
"C\u1EA3m \u01A1n"],
|
||||
};
|
||||
|
||||
let locale = Services.locale.appLocaleAsLangTag;
|
||||
if (!(locale in localizedStrings))
|
||||
locale = "en";
|
||||
let strings = localizedStrings[locale];
|
||||
|
||||
this._getAnonElt("welcomeHeadline").setAttribute("value", strings[0]);
|
||||
this._getAnonElt("welcomeBody").textContent = strings[1];
|
||||
this._getAnonElt("learnMore").setAttribute("value", strings[2]);
|
||||
this._getAnonElt("thanksButton").setAttribute("label", strings[3]);
|
||||
|
||||
let panel = this._getAnonElt("welcomePanel");
|
||||
panel.openPopup(this._getAnonElt("messageImage"),
|
||||
"bottomcenter topleft");
|
||||
|
||||
Services.prefs.setBoolPref(kWelcomePref, true);
|
||||
}, {once: true});
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="_getAnonElt">
|
||||
<parameter name="aAnonId"/>
|
||||
<body>
|
||||
return document.getAnonymousElementByAttribute(this, "anonid", aAnonId);
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="translate">
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.state == Translation.STATE_OFFER) {
|
||||
this._getAnonElt("fromLanguage").value =
|
||||
this._getAnonElt("detectedLanguage").value;
|
||||
this._getAnonElt("toLanguage").value =
|
||||
Translation.defaultTargetLanguage;
|
||||
}
|
||||
|
||||
this.translation.translate(this._getAnonElt("fromLanguage").value,
|
||||
this._getAnonElt("toLanguage").value);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<!-- To be called when the infobar should be closed per user's wish (e.g.
|
||||
by clicking the notification's close button -->
|
||||
<method name="closeCommand">
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.close();
|
||||
this.translation.infobarClosed();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="_handleButtonHiding">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let originalShown = this.translation.originalShown;
|
||||
this._getAnonElt("showOriginal").hidden = originalShown;
|
||||
this._getAnonElt("showTranslation").hidden = !originalShown;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="showOriginal">
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.translation.showOriginalContent();
|
||||
this._handleButtonHiding();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="showTranslation">
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.translation.showTranslatedContent();
|
||||
this._handleButtonHiding();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="optionsShowing">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// Get the source language name.
|
||||
let lang;
|
||||
if (this.state == Translation.STATE_OFFER)
|
||||
lang = this._getAnonElt("detectedLanguage").value;
|
||||
else {
|
||||
lang = this._getAnonElt("fromLanguage").value;
|
||||
|
||||
// If we have never attempted to translate the page before the
|
||||
// service became unavailable, "fromLanguage" isn't set.
|
||||
if (!lang && this.state == Translation.STATE_UNAVAILABLE)
|
||||
lang = this.translation.detectedLanguage;
|
||||
}
|
||||
|
||||
let langName = Services.intl.getLanguageDisplayNames(undefined, [lang])[0];
|
||||
|
||||
// Set the label and accesskey on the menuitem.
|
||||
let bundle =
|
||||
Services.strings.createBundle("chrome://browser/locale/translation.properties");
|
||||
let item = this._getAnonElt("neverForLanguage");
|
||||
const kStrId = "translation.options.neverForLanguage";
|
||||
item.setAttribute("label",
|
||||
bundle.formatStringFromName(kStrId + ".label",
|
||||
[langName], 1));
|
||||
item.setAttribute("accesskey",
|
||||
bundle.GetStringFromName(kStrId + ".accesskey"));
|
||||
item.langCode = lang;
|
||||
|
||||
// We may need to disable the menuitems if they have already been used.
|
||||
// Check if translation is already disabled for this language:
|
||||
let neverForLangs =
|
||||
Services.prefs.getCharPref("browser.translation.neverForLanguages");
|
||||
item.disabled = neverForLangs.split(",").includes(lang);
|
||||
|
||||
// Check if translation is disabled for the domain:
|
||||
let uri = this.translation.browser.currentURI;
|
||||
let perms = Services.perms;
|
||||
item = this._getAnonElt("neverForSite");
|
||||
item.disabled =
|
||||
perms.testExactPermission(uri, "translate") == perms.DENY_ACTION;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="neverForLanguage">
|
||||
<body>
|
||||
<![CDATA[
|
||||
const kPrefName = "browser.translation.neverForLanguages";
|
||||
|
||||
let val = Services.prefs.getCharPref(kPrefName);
|
||||
if (val)
|
||||
val += ",";
|
||||
val += this._getAnonElt("neverForLanguage").langCode;
|
||||
|
||||
Services.prefs.setCharPref(kPrefName, val);
|
||||
|
||||
this.closeCommand();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="neverForSite">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let uri = this.translation.browser.currentURI;
|
||||
let perms = Services.perms;
|
||||
perms.add(uri, "translate", perms.DENY_ACTION);
|
||||
|
||||
this.closeCommand();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="openProviderAttribution">
|
||||
<body>
|
||||
<![CDATA[
|
||||
Translation.openProviderAttribution();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
</implementation>
|
||||
</binding>
|
||||
</bindings>
|
||||
@@ -279,9 +279,7 @@ add_task(async function test_other_ignored() {
|
||||
Assert.ok(notification, "There should be a notification");
|
||||
|
||||
// Dismiss notification, creating the .dmp.ignore file
|
||||
let closeButton =
|
||||
document.getAnonymousElementByAttribute(notification, "anonid", "close-button");
|
||||
closeButton.click();
|
||||
notification.querySelector(".messageCloseButton").click();
|
||||
gNotificationBox.removeNotification(notification, true);
|
||||
await waitForIgnoredReports(toIgnore);
|
||||
|
||||
@@ -471,9 +469,7 @@ add_task(async function test_can_ignore() {
|
||||
Assert.ok(notification, "There should be a notification");
|
||||
|
||||
// Dismiss the notification by clicking on the "X" button.
|
||||
let closeButton =
|
||||
document.getAnonymousElementByAttribute(notification, "anonid", "close-button");
|
||||
closeButton.click();
|
||||
notification.querySelector(".messageCloseButton").click();
|
||||
// We'll not wait for the notification to finish its transition -
|
||||
// we'll just remove it right away.
|
||||
gNotificationBox.removeNotification(notification, true);
|
||||
@@ -545,9 +541,7 @@ add_task(async function test_shutdown_while_not_showing() {
|
||||
Assert.ok(notification, "There should be a notification");
|
||||
|
||||
// Dismiss the notification by clicking on the "X" button.
|
||||
let closeButton =
|
||||
document.getAnonymousElementByAttribute(notification, "anonid", "close-button");
|
||||
closeButton.click();
|
||||
notification.querySelector(".messageCloseButton").click();
|
||||
// We'll not wait for the notification to finish its transition -
|
||||
// we'll just remove it right away.
|
||||
gNotificationBox.removeNotification(notification, true);
|
||||
|
||||
@@ -4,25 +4,25 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
%endif
|
||||
notification[value="translation"] .messageImage {
|
||||
list-style-image: url(chrome://browser/skin/translation-16.png);
|
||||
list-style-image: url(chrome://browser/skin/translation-16.png) !important;
|
||||
-moz-image-region: rect(0, 32px, 16px, 16px);
|
||||
}
|
||||
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
notification[value="translation"] .messageImage {
|
||||
list-style-image: url(chrome://browser/skin/translation-16@2x.png);
|
||||
list-style-image: url(chrome://browser/skin/translation-16@2x.png) !important;
|
||||
-moz-image-region: rect(0, 64px, 32px, 32px);
|
||||
}
|
||||
}
|
||||
|
||||
notification[value="translation"][state="translating"] .messageImage {
|
||||
list-style-image: url(chrome://browser/skin/translating-16.png);
|
||||
list-style-image: url(chrome://browser/skin/translating-16.png) !important;
|
||||
-moz-image-region: auto;
|
||||
}
|
||||
|
||||
@media (min-resolution: 1.25dppx) {
|
||||
notification[value="translation"][state="translating"] .messageImage {
|
||||
list-style-image: url(chrome://browser/skin/translating-16@2x.png);
|
||||
list-style-image: url(chrome://browser/skin/translating-16@2x.png) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,8 +82,10 @@ function testOpenTestFile() {
|
||||
is(nb.allNotifications.length, 1, "There is just one notification");
|
||||
const cn = nb.currentNotification;
|
||||
is(cn.priority, nb.PRIORITY_WARNING_HIGH, "notification priority is correct");
|
||||
is(cn.value, "file-import-convert-failed", "notification value is corrent");
|
||||
is(cn.type, "warning", "notification type is correct");
|
||||
is(cn.getAttribute("value"), "file-import-convert-failed",
|
||||
"notification value is corrent");
|
||||
is(cn.getAttribute("type"), "warning",
|
||||
"notification type is correct");
|
||||
done();
|
||||
});
|
||||
ok(true, "importFromFile does not cause exception");
|
||||
|
||||
@@ -152,7 +152,7 @@ function testOpenDeletedFile() {
|
||||
"The missing file was successfully removed from the menu.");
|
||||
ok(gScratchpad.notificationBox.currentNotification,
|
||||
"The notification was successfully displayed.");
|
||||
is(gScratchpad.notificationBox.currentNotification.label,
|
||||
is(gScratchpad.notificationBox.currentNotification.messageText.textContent,
|
||||
gScratchpad.strings.GetStringFromName("fileNoLongerExists.notification"),
|
||||
"The notification label is correct.");
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ const CSSUsageFront = protocol.FrontClassWithSpec(cssUsageSpec, {
|
||||
}
|
||||
} else {
|
||||
if (notification) {
|
||||
notification.remove();
|
||||
notification.close();
|
||||
notification = undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,8 +146,7 @@ class TestSafeBrowsingNotificationBar(PuppeteerMixin, MarionetteTestCase):
|
||||
# TODO: update to use safe browsing notification bar class when bug 1139544 lands
|
||||
button = (self.marionette.find_element(By.ID, 'tabbrowser-tabbox')
|
||||
.find_element(By.CSS_SELECTOR, 'notification[value=blocked-badware-page]')
|
||||
.find_element('anon attribute',
|
||||
{'class': 'messageCloseButton close-icon tabbable'}))
|
||||
.find_element(By.CSS_SELECTOR, '.messageCloseButton'))
|
||||
button.click()
|
||||
|
||||
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
|
||||
|
||||
@@ -1591,7 +1591,7 @@ var BrowserTestUtils = {
|
||||
waitForNotificationInNotificationBox(notificationBox, notificationValue) {
|
||||
return new Promise((resolve) => {
|
||||
let check = (event) => {
|
||||
return event.target.value == notificationValue;
|
||||
return event.target.getAttribute("value") == notificationValue;
|
||||
};
|
||||
|
||||
BrowserTestUtils.waitForEvent(notificationBox.stack, "AlertActive",
|
||||
|
||||
@@ -173,8 +173,8 @@ var Heartbeat = class {
|
||||
// Build the heartbeat stars
|
||||
if (!this.options.engagementButtonLabel) {
|
||||
const numStars = this.options.engagementButtonLabel ? 0 : 5;
|
||||
const ratingContainer = this.chromeWindow.document.createXULElement("hbox");
|
||||
ratingContainer.id = "star-rating-container";
|
||||
this.ratingContainer = this.chromeWindow.document.createXULElement("hbox");
|
||||
this.ratingContainer.id = "star-rating-container";
|
||||
|
||||
for (let i = 0; i < numStars; i++) {
|
||||
// create a star rating element
|
||||
@@ -193,43 +193,38 @@ var Heartbeat = class {
|
||||
this.userEngaged({type: "stars", score: rating, flowId: this.options.flowId});
|
||||
});
|
||||
|
||||
ratingContainer.appendChild(ratingElement);
|
||||
this.ratingContainer.appendChild(ratingElement);
|
||||
}
|
||||
|
||||
frag.appendChild(ratingContainer);
|
||||
frag.appendChild(this.ratingContainer);
|
||||
}
|
||||
|
||||
const details = this.chromeWindow.document.getAnonymousElementByAttribute(this.notice, "anonid", "details");
|
||||
details.style.overflow = "hidden";
|
||||
|
||||
this.messageImage = this.chromeWindow.document.getAnonymousElementByAttribute(this.notice, "anonid", "messageImage");
|
||||
this.messageImage.classList.add("heartbeat", "pulse-onshow");
|
||||
|
||||
this.messageText = this.chromeWindow.document.getAnonymousElementByAttribute(this.notice, "anonid", "messageText");
|
||||
this.messageText.classList.add("heartbeat");
|
||||
this.notice.messageDetails.style.overflow = "hidden";
|
||||
this.notice.messageImage.classList.add("heartbeat", "pulse-onshow");
|
||||
this.notice.messageText.classList.add("heartbeat");
|
||||
|
||||
// Make sure the stars are not pushed to the right by the spacer.
|
||||
const rightSpacer = this.chromeWindow.document.createXULElement("spacer");
|
||||
rightSpacer.flex = 20;
|
||||
frag.appendChild(rightSpacer);
|
||||
this.rightSpacer = this.chromeWindow.document.createXULElement("spacer");
|
||||
this.rightSpacer.flex = 20;
|
||||
frag.appendChild(this.rightSpacer);
|
||||
|
||||
// collapse the space before the stars
|
||||
this.messageText.flex = 0;
|
||||
const leftSpacer = this.messageText.nextSibling;
|
||||
leftSpacer.flex = 0;
|
||||
this.notice.messageText.flex = 0;
|
||||
this.notice.spacer.flex = 0;
|
||||
|
||||
// Add Learn More Link
|
||||
if (this.options.learnMoreMessage && this.options.learnMoreUrl) {
|
||||
const learnMore = this.chromeWindow.document.createXULElement("label");
|
||||
learnMore.className = "text-link";
|
||||
learnMore.href = this.options.learnMoreUrl.toString();
|
||||
learnMore.setAttribute("value", this.options.learnMoreMessage);
|
||||
learnMore.addEventListener("click", () => this.maybeNotifyHeartbeat("LearnMore"));
|
||||
frag.appendChild(learnMore);
|
||||
this.learnMore = this.chromeWindow.document.createXULElement("label");
|
||||
this.learnMore.className = "text-link";
|
||||
this.learnMore.href = this.options.learnMoreUrl.toString();
|
||||
this.learnMore.setAttribute("value", this.options.learnMoreMessage);
|
||||
this.learnMore.addEventListener("click",
|
||||
() => this.maybeNotifyHeartbeat("LearnMore"));
|
||||
frag.appendChild(this.learnMore);
|
||||
}
|
||||
|
||||
// Append the fragment and apply the styling
|
||||
this.notice.appendChild(frag);
|
||||
this.notice.messageDetails.appendChild(frag);
|
||||
this.notice.classList.add("heartbeat");
|
||||
|
||||
// Let the consumer know the notification was shown.
|
||||
@@ -330,12 +325,17 @@ var Heartbeat = class {
|
||||
userEngaged(engagementParams) {
|
||||
// Make the heartbeat icon pulse twice
|
||||
this.notice.label = this.options.thanksMessage;
|
||||
this.messageImage.classList.remove("pulse-onshow");
|
||||
this.messageImage.classList.add("pulse-twice");
|
||||
this.notice.messageImage.classList.remove("pulse-onshow");
|
||||
this.notice.messageImage.classList.add("pulse-twice");
|
||||
|
||||
// Remove all the children of the notice (rating container, and the flex)
|
||||
while (this.notice.firstChild) {
|
||||
this.notice.firstChild.remove();
|
||||
// Remove the custom contents of the notice and the buttons
|
||||
if (this.ratingContainer) {
|
||||
this.ratingContainer.remove();
|
||||
}
|
||||
this.rightSpacer.remove();
|
||||
this.learnMore.remove();
|
||||
for (let button of this.notice.querySelectorAll("button")) {
|
||||
button.remove();
|
||||
}
|
||||
|
||||
// Open the engagement tab if we have a valid engagement URL.
|
||||
@@ -381,8 +381,10 @@ var Heartbeat = class {
|
||||
// remove references for garbage collection
|
||||
this.chromeWindow = null;
|
||||
this.notificationBox = null;
|
||||
this.notification = null;
|
||||
this.notice = null;
|
||||
this.ratingContainer = null;
|
||||
this.rightSpacer = null;
|
||||
this.learnMore = null;
|
||||
this.eventEmitter = null;
|
||||
this.sandboxManager = null;
|
||||
// Ensure we don't re-enter and release the CleanupManager's reference to us:
|
||||
|
||||
@@ -77,43 +77,43 @@ notification.heartbeat {
|
||||
}
|
||||
|
||||
/* Learn More link styles */
|
||||
.heartbeat > .text-link {
|
||||
.heartbeat > hbox > .text-link {
|
||||
color: #0095DD !important;
|
||||
margin-inline-start: 0 !important;
|
||||
}
|
||||
|
||||
.heartbeat > .text-link:hover {
|
||||
.heartbeat > hbox > .text-link:hover {
|
||||
color: #008ACB !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.heartbeat > .text-link:hover:active {
|
||||
.heartbeat > hbox > .text-link:hover:active {
|
||||
color: #006B9D !important;
|
||||
}
|
||||
|
||||
/* Heartbeat UI Rating Star Classes */
|
||||
.heartbeat > #star-rating-container {
|
||||
#star-rating-container {
|
||||
display: -moz-box;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.heartbeat > #star-rating-container > #star5 {
|
||||
#star-rating-container > #star5 {
|
||||
-moz-box-ordinal-group: 5;
|
||||
}
|
||||
|
||||
.heartbeat > #star-rating-container > #star4 {
|
||||
#star-rating-container > #star4 {
|
||||
-moz-box-ordinal-group: 4;
|
||||
}
|
||||
|
||||
.heartbeat > #star-rating-container > #star3 {
|
||||
#star-rating-container > #star3 {
|
||||
-moz-box-ordinal-group: 3;
|
||||
}
|
||||
|
||||
.heartbeat > #star-rating-container > #star2 {
|
||||
#star-rating-container > #star2 {
|
||||
-moz-box-ordinal-group: 2;
|
||||
}
|
||||
|
||||
.heartbeat > #star-rating-container > .star-x {
|
||||
#star-rating-container > .star-x {
|
||||
background: url("resource://normandy/skin/shared/heartbeat-star-off.svg");
|
||||
cursor: pointer;
|
||||
height: 16px;
|
||||
@@ -121,7 +121,7 @@ notification.heartbeat {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.heartbeat > #star-rating-container > .star-x:hover,
|
||||
.heartbeat > #star-rating-container > .star-x:hover ~ .star-x {
|
||||
#star-rating-container > .star-x:hover,
|
||||
#star-rating-container > .star-x:hover ~ .star-x {
|
||||
background: url("resource://normandy/skin/shared/heartbeat-star-lit.svg");
|
||||
}
|
||||
|
||||
@@ -93,13 +93,12 @@ add_task(async function() {
|
||||
|
||||
// Check UI
|
||||
const learnMoreEl = hb.notice.querySelector(".text-link");
|
||||
const messageEl = targetWindow.document.getAnonymousElementByAttribute(hb.notice, "anonid", "messageText");
|
||||
Assert.equal(notificationBox.allNotifications.length, preCount + 1, "Correct number of notifications open");
|
||||
Assert.equal(hb.notice.querySelectorAll(".star-x").length, 5, "Correct number of stars");
|
||||
Assert.equal(hb.notice.querySelectorAll(".notification-button").length, 0, "Engagement button not shown");
|
||||
Assert.equal(learnMoreEl.href, "https://example.org/learnmore", "Learn more url correct");
|
||||
Assert.equal(learnMoreEl.value, "Learn More", "Learn more label correct");
|
||||
Assert.equal(messageEl.textContent, "test", "Message is correct");
|
||||
Assert.equal(hb.notice.messageText.textContent, "test", "Message is correct");
|
||||
|
||||
// Check that when clicking the learn more link, a tab opens with the right URL
|
||||
let loadedPromise;
|
||||
|
||||
@@ -301,8 +301,8 @@ var tests =
|
||||
return ntf;
|
||||
},
|
||||
result: function(nb, ntf) {
|
||||
SimpleTest.is(nb.currentNotification == ntf ?
|
||||
nb.currentNotification.value : null, "10", "appendNotification order");
|
||||
is(nb.currentNotification, ntf, "appendNotification last notification");
|
||||
is(nb.currentNotification.getAttribute("value"), "10", "appendNotification order");
|
||||
return 1;
|
||||
}
|
||||
},
|
||||
@@ -333,8 +333,8 @@ var tests =
|
||||
},
|
||||
result: function(nb, arr) {
|
||||
// arr is [testindex, expectedvalue]
|
||||
SimpleTest.is(nb.currentNotification.value, "" + arr[1], "close order " + arr[0]);
|
||||
SimpleTest.is(nb.allNotifications.length, 10 - arr[0], "close order " + arr[0] + " count");
|
||||
is(nb.currentNotification.getAttribute("value"), "" + arr[1], "close order " + arr[0]);
|
||||
is(nb.allNotifications.length, 10 - arr[0], "close order " + arr[0] + " count");
|
||||
if (arr[0] == 6)
|
||||
this.repeat = false;
|
||||
return ++arr[0];
|
||||
@@ -400,10 +400,10 @@ function testtag_notificationbox_State(nb, testid, expecteditem, expectedcount)
|
||||
|
||||
function testtag_notification_State(nb, ntf, testid, label, value, image, priority)
|
||||
{
|
||||
SimpleTest.is(ntf.label, label, testid + " notification.label");
|
||||
SimpleTest.is(ntf.value, value, testid + " notification.value");
|
||||
SimpleTest.is(ntf.image, image, testid + " notification.image");
|
||||
SimpleTest.is(ntf.priority, priority, testid + " notification.priority");
|
||||
is(ntf.messageText.textContent, label, testid + " notification label");
|
||||
is(ntf.getAttribute("value"), value, testid + " notification value");
|
||||
is(ntf.messageImage.getAttribute("src"), image, testid + " notification image");
|
||||
is(ntf.priority, priority, testid + " notification priority");
|
||||
|
||||
var type;
|
||||
switch (priority) {
|
||||
@@ -424,7 +424,7 @@ function testtag_notification_State(nb, ntf, testid, label, value, image, priori
|
||||
break;
|
||||
}
|
||||
|
||||
SimpleTest.is(ntf.type, type, testid + " notification.type");
|
||||
is(ntf.getAttribute("type"), type, testid + " notification type");
|
||||
}
|
||||
|
||||
function checkPopupTest(nb, ntf)
|
||||
|
||||
@@ -15,99 +15,6 @@
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html = "http://www.w3.org/1999/xhtml">
|
||||
|
||||
<binding id="notification">
|
||||
<content>
|
||||
<xul:hbox anonid="details" align="center" flex="1"
|
||||
oncommand="this.parentNode._doButtonCommand(event);">
|
||||
<xul:image anonid="messageImage" class="messageImage" xbl:inherits="src=image,type,value"/>
|
||||
<xul:description anonid="messageText" class="messageText" flex="1" xbl:inherits="xbl:text=label"/>
|
||||
<xul:spacer flex="1"/>
|
||||
<children/>
|
||||
</xul:hbox>
|
||||
<xul:toolbarbutton ondblclick="event.stopPropagation();"
|
||||
class="messageCloseButton close-icon tabbable"
|
||||
anonid="close-button"
|
||||
xbl:inherits="hidden=hideclose"
|
||||
tooltiptext="&closeNotification.tooltip;"
|
||||
oncommand="document.getBindingParent(this).dismiss();"/>
|
||||
</content>
|
||||
<implementation>
|
||||
<property name="label" onset="this.setAttribute('label', val); return val;"
|
||||
onget="return this.getAttribute('label');"/>
|
||||
<property name="value" onset="this.setAttribute('value', val); return val;"
|
||||
onget="return this.getAttribute('value');"/>
|
||||
<property name="image" onset="this.setAttribute('image', val); return val;"
|
||||
onget="return this.getAttribute('image');"/>
|
||||
<property name="type" onset="this.setAttribute('type', val); return val;"
|
||||
onget="return this.getAttribute('type');"/>
|
||||
<property name="priority" onget="return parseInt(this.getAttribute('priority')) || 0;"
|
||||
onset="this.setAttribute('priority', val); return val;"/>
|
||||
<property name="persistence" onget="return parseInt(this.getAttribute('persistence')) || 0;"
|
||||
onset="this.setAttribute('persistence', val); return val;"/>
|
||||
<field name="timeout">0</field>
|
||||
|
||||
<property name="control" readonly="true">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
return this.closest(".notificationbox-stack")._notificationBox;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<!-- This method should only be called when the user has
|
||||
manually closed the notification. If you want to
|
||||
programmatically close the notification, you should
|
||||
call close() instead. -->
|
||||
<method name="dismiss">
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.eventCallback) {
|
||||
this.eventCallback("dismissed");
|
||||
}
|
||||
this.close();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="close">
|
||||
<body>
|
||||
<![CDATA[
|
||||
var control = this.control;
|
||||
if (control)
|
||||
control.removeNotification(this);
|
||||
else
|
||||
this.hidden = true;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="_doButtonCommand">
|
||||
<parameter name="aEvent"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (!("buttonInfo" in aEvent.target))
|
||||
return;
|
||||
|
||||
var button = aEvent.target.buttonInfo;
|
||||
if (button.popup) {
|
||||
document.getElementById(button.popup).
|
||||
openPopup(aEvent.originalTarget, "after_start", 0, 0, false, false, aEvent);
|
||||
aEvent.stopPropagation();
|
||||
} else {
|
||||
var callback = button.callback;
|
||||
if (callback) {
|
||||
var result = callback(this, button, aEvent.target, aEvent);
|
||||
if (!result)
|
||||
this.close();
|
||||
aEvent.stopPropagation();
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="popup-notification">
|
||||
<content orient="vertical">
|
||||
<xul:hbox class="popup-notification-header-container">
|
||||
|
||||
@@ -64,7 +64,49 @@ MozElements.NotificationBox = class NotificationBox {
|
||||
return null;
|
||||
}
|
||||
|
||||
appendNotification(aLabel, aValue, aImage, aPriority, aButtons, aEventCallback) {
|
||||
/**
|
||||
* Creates a <notification> element and shows it. The calling code can modify
|
||||
* the element synchronously to add features to the notification.
|
||||
*
|
||||
* @param aLabel
|
||||
* The main message text, or a DocumentFragment containing elements to
|
||||
* add as children of the notification's main <description> element.
|
||||
* @param aValue
|
||||
* String identifier of the notification.
|
||||
* @param aImage
|
||||
* URL of the icon image to display. If not specified, a default icon
|
||||
* based on the priority will be shown.
|
||||
* @param aPriority
|
||||
* One of the PRIORITY_ constants. These determine the appearance of
|
||||
* the notification based on severity (using the "type" attribute), and
|
||||
* only the notification with the highest priority is displayed.
|
||||
* @param aButtons
|
||||
* Array of objects defining action buttons:
|
||||
* {
|
||||
* label:
|
||||
* Label of the <button> element.
|
||||
* accessKey:
|
||||
* Access key character for the <button> element.
|
||||
* callback:
|
||||
* When the button is used, this is called with the arguments:
|
||||
* 1. The <notification> element.
|
||||
* 2. This button object definition.
|
||||
* 3. The <button> element.
|
||||
* 4. The "command" event.
|
||||
* popup:
|
||||
* If specified, the button will open the popup element with this
|
||||
* ID, anchored to the button. This is alternative to "callback".
|
||||
* }
|
||||
* @param aEventCallback
|
||||
* This may be called with the "removed" or "dismissed" parameter.
|
||||
* @param aNotificationIs
|
||||
* Defines a Custom Element name to use as the "is" value on creation.
|
||||
* This allows subclassing the created element.
|
||||
*
|
||||
* @return The <notification> element that is shown.
|
||||
*/
|
||||
appendNotification(aLabel, aValue, aImage, aPriority, aButtons,
|
||||
aEventCallback, aNotificationIs) {
|
||||
if (aPriority < this.PRIORITY_INFO_LOW ||
|
||||
aPriority > this.PRIORITY_CRITICAL_HIGH)
|
||||
throw "Invalid notification priority " + aPriority;
|
||||
@@ -79,24 +121,28 @@ MozElements.NotificationBox = class NotificationBox {
|
||||
insertPos = notifications[n];
|
||||
}
|
||||
|
||||
var newitem = document.createXULElement("notification");
|
||||
// Can't use instanceof in case this was created from a different document:
|
||||
let labelIsDocFragment = aLabel && typeof aLabel == "object" && aLabel.nodeType &&
|
||||
aLabel.nodeType == aLabel.DOCUMENT_FRAGMENT_NODE;
|
||||
if (!labelIsDocFragment)
|
||||
newitem.setAttribute("label", aLabel);
|
||||
// Create the Custom Element and connect it to the document immediately.
|
||||
var newitem = document.createXULElement("notification",
|
||||
aNotificationIs ? { is: aNotificationIs } : {});
|
||||
this.stack.insertBefore(newitem, insertPos);
|
||||
|
||||
// Custom notification classes may not have the messageText property.
|
||||
if (newitem.messageText) {
|
||||
// Can't use instanceof in case this was created from a different document:
|
||||
if (aLabel && typeof aLabel == "object" &&
|
||||
aLabel.nodeType && aLabel.nodeType == aLabel.DOCUMENT_FRAGMENT_NODE) {
|
||||
newitem.messageText.appendChild(aLabel);
|
||||
} else {
|
||||
newitem.messageText.textContent = aLabel;
|
||||
}
|
||||
}
|
||||
newitem.setAttribute("value", aValue);
|
||||
if (aImage)
|
||||
newitem.setAttribute("image", aImage);
|
||||
if (aImage) {
|
||||
newitem.messageImage.setAttribute("src", aImage);
|
||||
}
|
||||
newitem.eventCallback = aEventCallback;
|
||||
|
||||
if (aButtons) {
|
||||
// The notification-button-default class is added to the button
|
||||
// with isDefault set to true. If there is no such button, it is
|
||||
// added to the first button (unless that button has isDefault
|
||||
// set to false). There cannot be multiple default buttons.
|
||||
var defaultElem;
|
||||
|
||||
for (var b = 0; b < aButtons.length; b++) {
|
||||
var button = aButtons[b];
|
||||
var buttonElem = document.createXULElement("button");
|
||||
@@ -104,20 +150,12 @@ MozElements.NotificationBox = class NotificationBox {
|
||||
if (typeof button.accessKey == "string")
|
||||
buttonElem.setAttribute("accesskey", button.accessKey);
|
||||
buttonElem.classList.add("notification-button");
|
||||
|
||||
if (button.isDefault ||
|
||||
b == 0 && !("isDefault" in button))
|
||||
defaultElem = buttonElem;
|
||||
|
||||
newitem.appendChild(buttonElem);
|
||||
newitem.messageDetails.appendChild(buttonElem);
|
||||
buttonElem.buttonInfo = button;
|
||||
}
|
||||
|
||||
if (defaultElem)
|
||||
defaultElem.classList.add("notification-button-default");
|
||||
}
|
||||
|
||||
newitem.setAttribute("priority", aPriority);
|
||||
newitem.priority = aPriority;
|
||||
if (aPriority >= this.PRIORITY_CRITICAL_LOW)
|
||||
newitem.setAttribute("type", "critical");
|
||||
else if (aPriority <= this.PRIORITY_INFO_HIGH)
|
||||
@@ -130,17 +168,8 @@ MozElements.NotificationBox = class NotificationBox {
|
||||
newitem.style.top = "100%";
|
||||
newitem.style.marginTop = "-15px";
|
||||
newitem.style.opacity = "0";
|
||||
}
|
||||
this.stack.insertBefore(newitem, insertPos);
|
||||
// Can only insert the document fragment after the item has been created because
|
||||
// otherwise the XBL structure isn't there yet:
|
||||
if (labelIsDocFragment) {
|
||||
document.getAnonymousElementByAttribute(newitem, "anonid", "messageText")
|
||||
.appendChild(aLabel);
|
||||
}
|
||||
|
||||
if (!insertPos)
|
||||
this._showNotification(newitem, true);
|
||||
}
|
||||
|
||||
// Fire event for accessibility APIs
|
||||
var event = document.createEvent("Events");
|
||||
@@ -265,3 +294,80 @@ Object.assign(MozElements.NotificationBox.prototype, {
|
||||
PRIORITY_CRITICAL_MEDIUM: 8,
|
||||
PRIORITY_CRITICAL_HIGH: 9,
|
||||
});
|
||||
|
||||
MozElements.Notification = class Notification extends MozXULElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.persistence = 0;
|
||||
this.priority = 0;
|
||||
this.timeout = 0;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.appendChild(MozXULElement.parseXULToFragment(`
|
||||
<hbox class="messageDetails" align="center" flex="1"
|
||||
oncommand="this.parentNode._doButtonCommand(event);">
|
||||
<image class="messageImage"/>
|
||||
<description class="messageText" flex="1"/>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
<toolbarbutton ondblclick="event.stopPropagation();"
|
||||
class="messageCloseButton close-icon tabbable"
|
||||
tooltiptext="&closeNotification.tooltip;"
|
||||
oncommand="this.parentNode.dismiss();"/>
|
||||
`, ["chrome://global/locale/notification.dtd"]));
|
||||
|
||||
for (let [propertyName, selector] of [
|
||||
["messageDetails", ".messageDetails"],
|
||||
["messageImage", ".messageImage"],
|
||||
["messageText", ".messageText"],
|
||||
["spacer", "spacer"],
|
||||
]) {
|
||||
this[propertyName] = this.querySelector(selector);
|
||||
}
|
||||
}
|
||||
|
||||
get control() {
|
||||
return this.closest(".notificationbox-stack")._notificationBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should only be called when the user has manually closed the
|
||||
* notification. If you want to programmatically close the notification, you
|
||||
* should call close() instead.
|
||||
*/
|
||||
dismiss() {
|
||||
if (this.eventCallback) {
|
||||
this.eventCallback("dismissed");
|
||||
}
|
||||
this.close();
|
||||
}
|
||||
|
||||
close() {
|
||||
this.control.removeNotification(this);
|
||||
}
|
||||
|
||||
_doButtonCommand(event) {
|
||||
if (!("buttonInfo" in event.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var button = event.target.buttonInfo;
|
||||
if (button.popup) {
|
||||
document.getElementById(button.popup).
|
||||
openPopup(event.originalTarget, "after_start", 0, 0, false, false, event);
|
||||
event.stopPropagation();
|
||||
} else {
|
||||
var callback = button.callback;
|
||||
if (callback) {
|
||||
var result = callback(this, button, event.target, event);
|
||||
if (!result) {
|
||||
this.close();
|
||||
}
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
customElements.define("notification", MozElements.Notification);
|
||||
|
||||
@@ -176,12 +176,6 @@ browser {
|
||||
-moz-binding: url("chrome://global/content/bindings/browser.xml#browser");
|
||||
}
|
||||
|
||||
/********** notifications **********/
|
||||
|
||||
notification {
|
||||
-moz-binding: url("chrome://global/content/bindings/notification.xml#notification");
|
||||
}
|
||||
|
||||
/*********** popup notification ************/
|
||||
popupnotification {
|
||||
-moz-binding: url("chrome://global/content/bindings/notification.xml#popup-notification");
|
||||
|
||||
@@ -257,7 +257,7 @@ label[disabled="true"] {
|
||||
outline: 1px dotted;
|
||||
}
|
||||
|
||||
notification > button {
|
||||
notification > hbox > button {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ label[disabled="true"] {
|
||||
box-shadow: var(--focus-ring-box-shadow);
|
||||
}
|
||||
|
||||
notification > button {
|
||||
notification > hbox > button {
|
||||
margin: 0 3px;
|
||||
padding: 1px 10px;
|
||||
min-width: 60px;
|
||||
@@ -232,21 +232,21 @@ notification > button {
|
||||
box-shadow: @roundButtonShadow@;
|
||||
}
|
||||
|
||||
notification > button:active:hover {
|
||||
notification > hbox > button:active:hover {
|
||||
color: @roundButtonColor@;
|
||||
background: @roundButtonPressedBackground@;
|
||||
box-shadow: @roundButtonPressedShadow@;
|
||||
}
|
||||
|
||||
notification > button:-moz-focusring {
|
||||
notification > hbox > button:-moz-focusring {
|
||||
box-shadow: var(--focus-ring-box-shadow), @roundButtonShadow@;
|
||||
}
|
||||
|
||||
notification > button:active:hover:-moz-focusring {
|
||||
notification > hbox > button:active:hover:-moz-focusring {
|
||||
box-shadow: var(--focus-ring-box-shadow), @roundButtonPressedShadow@;
|
||||
}
|
||||
|
||||
notification > button > .button-box > .button-text {
|
||||
notification > hbox > button > .button-box > .button-text {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,15 +63,15 @@ notification[type="critical"] {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.messageImage[type="info"] {
|
||||
notification[type="info"] > hbox > .messageImage {
|
||||
list-style-image: url("chrome://global/skin/icons/help.svg");
|
||||
}
|
||||
|
||||
.messageImage[type="warning"] {
|
||||
notification[type="warning"] > hbox > .messageImage {
|
||||
list-style-image: url("chrome://global/skin/icons/warning.svg");
|
||||
}
|
||||
|
||||
.messageImage[type="critical"] {
|
||||
notification[type="critical"] > hbox > .messageImage {
|
||||
list-style-image: url("chrome://global/skin/icons/error.svg");
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,7 @@ add_task(async function() {
|
||||
gBrowser.getNotificationBox().getNotificationWithValue(notificationValue);
|
||||
await BrowserTestUtils.waitForCondition(getNotification);
|
||||
let notification = getNotification();
|
||||
let button =
|
||||
notification.getElementsByClassName("notification-button-default")[0];
|
||||
let button = notification.querySelector("button");
|
||||
ok(button, "got registration button");
|
||||
button.click();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user