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.
|
// Returning true prevents the notification from closing.
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
isDefault: true,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -482,8 +482,7 @@ var gPluginHandler = {
|
|||||||
let crashurl = formatURL("app.support.baseURL", true);
|
let crashurl = formatURL("app.support.baseURL", true);
|
||||||
crashurl += "plugin-crashed-notificationbar";
|
crashurl += "plugin-crashed-notificationbar";
|
||||||
link.href = crashurl;
|
link.href = crashurl;
|
||||||
let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
|
notification.messageText.appendChild(link);
|
||||||
description.appendChild(link);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1058,11 +1058,6 @@ browser[tabmodalPromptShowing] {
|
|||||||
-moz-box-align: end;
|
-moz-box-align: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Translation */
|
|
||||||
notification[value="translation"] {
|
|
||||||
-moz-binding: url("chrome://browser/content/translation-infobar.xml#translationbar");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** Visibility of downloads indicator controls ***/
|
/*** Visibility of downloads indicator controls ***/
|
||||||
|
|
||||||
/* Bug 924050: If we've loaded the indicator, for now we hide it in the menu panel,
|
/* 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;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
customElements.setElementCreationCallback("translation-notification", () => {
|
||||||
|
Services.scriptloader.loadSubScript(
|
||||||
|
"chrome://browser/content/translation-notification.js", window);
|
||||||
|
});
|
||||||
|
|
||||||
var gBrowser;
|
var gBrowser;
|
||||||
var gLastValidURLStr = "";
|
var gLastValidURLStr = "";
|
||||||
var gInPrintPreviewMode = false;
|
var gInPrintPreviewMode = false;
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ async function test_decoder_doctor_notification(data, notificationMessage,
|
|||||||
}
|
}
|
||||||
ok(notification, "Got decoder-doctor-notification notification");
|
ok(notification, "Got decoder-doctor-notification notification");
|
||||||
|
|
||||||
is(notification.getAttribute("label"), notificationMessage,
|
is(notification.messageText.textContent, notificationMessage,
|
||||||
"notification message should match expectation");
|
"notification message should match expectation");
|
||||||
|
|
||||||
let button = notification.children[0];
|
let button = notification.querySelector("button");
|
||||||
if (!label) {
|
if (!label) {
|
||||||
ok(!button, "There should not be button");
|
ok(!button, "There should not be button");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ async function testRealRefresh(refreshPage, delay) {
|
|||||||
let notification = notificationBox.currentNotification;
|
let notification = notificationBox.currentNotification;
|
||||||
|
|
||||||
ok(notification, "Notification should be visible");
|
ok(notification, "Notification should be visible");
|
||||||
is(notification.value, "refresh-blocked",
|
is(notification.getAttribute("value"), "refresh-blocked",
|
||||||
"Should be showing the right notification");
|
"Should be showing the right notification");
|
||||||
|
|
||||||
// Then click the button to allow the refresh.
|
// Then click the button to allow the refresh.
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ add_task(async function() {
|
|||||||
let notification = box.currentNotification;
|
let notification = box.currentNotification;
|
||||||
|
|
||||||
ok(notification, "Notification should be visible");
|
ok(notification, "Notification should be visible");
|
||||||
is(notification.value, "drmContentDisabled",
|
is(notification.getAttribute("value"), "drmContentDisabled",
|
||||||
"Should be showing the right notification");
|
"Should be showing the right notification");
|
||||||
|
|
||||||
// Verify the "Enable DRM" button is there.
|
// Verify the "Enable DRM" button is there.
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ add_task(async function() {
|
|||||||
ok(notification, "Infobar was shown.");
|
ok(notification, "Infobar was shown.");
|
||||||
is(notification.priority, notificationBox.PRIORITY_WARNING_MEDIUM,
|
is(notification.priority, notificationBox.PRIORITY_WARNING_MEDIUM,
|
||||||
"Correct priority.");
|
"Correct priority.");
|
||||||
is(notification.getAttribute("label"),
|
is(notification.messageText.textContent,
|
||||||
"The GlobalTestPlugin plugin has crashed.",
|
"The GlobalTestPlugin plugin has crashed.",
|
||||||
"Correct message.");
|
"Correct message.");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,19 +14,8 @@ function promiseNotification(aBrowser, value, expected, input) {
|
|||||||
let notificationBox = aBrowser.getNotificationBox(aBrowser.selectedBrowser);
|
let notificationBox = aBrowser.getNotificationBox(aBrowser.selectedBrowser);
|
||||||
if (expected) {
|
if (expected) {
|
||||||
info("Waiting for " + value + " notification");
|
info("Waiting for " + value + " notification");
|
||||||
let checkForNotification = function() {
|
resolve(BrowserTestUtils.waitForNotificationInNotificationBox(
|
||||||
if (notificationBox.getNotificationWithValue(value)) {
|
notificationBox, value));
|
||||||
info("Saw the notification");
|
|
||||||
notificationObserver.disconnect();
|
|
||||||
notificationObserver = null;
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (notificationObserver) {
|
|
||||||
notificationObserver.disconnect();
|
|
||||||
}
|
|
||||||
notificationObserver = new MutationObserver(checkForNotification);
|
|
||||||
notificationObserver.observe(notificationBox.stack, {childList: true});
|
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
is(notificationBox.getNotificationWithValue(value), null,
|
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 notificationBox = browser.getNotificationBox(tab.linkedBrowser);
|
||||||
let notification = notificationBox.getNotificationWithValue("keyword-uri-fixup");
|
let notification = notificationBox.getNotificationWithValue("keyword-uri-fixup");
|
||||||
let docLoadPromise = waitForDocLoadAndStopIt("http://" + hostName + "/", tab.linkedBrowser);
|
let docLoadPromise = waitForDocLoadAndStopIt("http://" + hostName + "/", tab.linkedBrowser);
|
||||||
notification.querySelector(".notification-button-default").click();
|
notification.querySelector("button").click();
|
||||||
|
|
||||||
// check pref value
|
// check pref value
|
||||||
let prefValue = Services.prefs.getBoolPref(pref);
|
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 tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
|
||||||
|
|
||||||
let notification = await BrowserTestUtils.waitForGlobalNotificationBar(window, "process-hang");
|
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"),
|
ok(text.includes("\u201cSlow Script Extension\u201d"),
|
||||||
"Label is correct");
|
"Label is correct");
|
||||||
|
|||||||
@@ -22,13 +22,12 @@ add_task(async function() {
|
|||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
is(notification.type, "info", "We expect this notification to have the type of 'info'.");
|
is(notification.getAttribute("type"), "info",
|
||||||
isnot(notification.image, null, "We expect this notification to have an icon.");
|
"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");
|
let buttons = notification.getElementsByClassName("notification-button");
|
||||||
is(buttons.length, 1, "We expect see one default button.");
|
|
||||||
|
|
||||||
buttons = notification.getElementsByClassName("notification-button");
|
|
||||||
is(buttons.length, 1, "We expect see one button.");
|
is(buttons.length, 1, "We expect see one button.");
|
||||||
|
|
||||||
let button = buttons[0];
|
let button = buttons[0];
|
||||||
|
|||||||
@@ -333,7 +333,8 @@ function testShowNotification() {
|
|||||||
ok(updateBox, "Update notification box should have been displayed");
|
ok(updateBox, "Update notification box should have been displayed");
|
||||||
if (updateBox) {
|
if (updateBox) {
|
||||||
if (testCase.notificationText) {
|
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");
|
"should have the label provided by the update");
|
||||||
}
|
}
|
||||||
if (testCase.notificationButtonLabel) {
|
if (testCase.notificationButtonLabel) {
|
||||||
|
|||||||
@@ -228,7 +228,8 @@ TranslationUI.prototype = {
|
|||||||
showTranslationInfoBar() {
|
showTranslationInfoBar() {
|
||||||
let notificationBox = this.notificationBox;
|
let notificationBox = this.notificationBox;
|
||||||
let notif = notificationBox.appendNotification("", "translation", null,
|
let notif = notificationBox.appendNotification("", "translation", null,
|
||||||
notificationBox.PRIORITY_INFO_HIGH);
|
notificationBox.PRIORITY_INFO_HIGH, null, null,
|
||||||
|
"translation-notification");
|
||||||
notif.init(this);
|
notif.init(this);
|
||||||
return notif;
|
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
|
# 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/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
browser.jar:
|
browser.jar:
|
||||||
content/browser/translation-infobar.xml
|
content/browser/translation-notification.js
|
||||||
content/browser/microsoft-translator-attribution.png
|
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
|
# 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/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
DIRS += [
|
||||||
|
'content',
|
||||||
|
]
|
||||||
|
|
||||||
with Files("**"):
|
with Files("**"):
|
||||||
BUG_COMPONENT = ("Firefox", "Translation")
|
BUG_COMPONENT = ("Firefox", "Translation")
|
||||||
|
|
||||||
@@ -17,8 +21,6 @@ EXTRA_JS_MODULES.translation = [
|
|||||||
'YandexTranslator.jsm'
|
'YandexTranslator.jsm'
|
||||||
]
|
]
|
||||||
|
|
||||||
JAR_MANIFESTS += ['jar.mn']
|
|
||||||
|
|
||||||
BROWSER_CHROME_MANIFESTS += [
|
BROWSER_CHROME_MANIFESTS += [
|
||||||
'test/browser.ini'
|
'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");
|
Assert.ok(notification, "There should be a notification");
|
||||||
|
|
||||||
// Dismiss notification, creating the .dmp.ignore file
|
// Dismiss notification, creating the .dmp.ignore file
|
||||||
let closeButton =
|
notification.querySelector(".messageCloseButton").click();
|
||||||
document.getAnonymousElementByAttribute(notification, "anonid", "close-button");
|
|
||||||
closeButton.click();
|
|
||||||
gNotificationBox.removeNotification(notification, true);
|
gNotificationBox.removeNotification(notification, true);
|
||||||
await waitForIgnoredReports(toIgnore);
|
await waitForIgnoredReports(toIgnore);
|
||||||
|
|
||||||
@@ -471,9 +469,7 @@ add_task(async function test_can_ignore() {
|
|||||||
Assert.ok(notification, "There should be a notification");
|
Assert.ok(notification, "There should be a notification");
|
||||||
|
|
||||||
// Dismiss the notification by clicking on the "X" button.
|
// Dismiss the notification by clicking on the "X" button.
|
||||||
let closeButton =
|
notification.querySelector(".messageCloseButton").click();
|
||||||
document.getAnonymousElementByAttribute(notification, "anonid", "close-button");
|
|
||||||
closeButton.click();
|
|
||||||
// We'll not wait for the notification to finish its transition -
|
// We'll not wait for the notification to finish its transition -
|
||||||
// we'll just remove it right away.
|
// we'll just remove it right away.
|
||||||
gNotificationBox.removeNotification(notification, true);
|
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");
|
Assert.ok(notification, "There should be a notification");
|
||||||
|
|
||||||
// Dismiss the notification by clicking on the "X" button.
|
// Dismiss the notification by clicking on the "X" button.
|
||||||
let closeButton =
|
notification.querySelector(".messageCloseButton").click();
|
||||||
document.getAnonymousElementByAttribute(notification, "anonid", "close-button");
|
|
||||||
closeButton.click();
|
|
||||||
// We'll not wait for the notification to finish its transition -
|
// We'll not wait for the notification to finish its transition -
|
||||||
// we'll just remove it right away.
|
// we'll just remove it right away.
|
||||||
gNotificationBox.removeNotification(notification, true);
|
gNotificationBox.removeNotification(notification, true);
|
||||||
|
|||||||
@@ -4,25 +4,25 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
%endif
|
%endif
|
||||||
notification[value="translation"] .messageImage {
|
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);
|
-moz-image-region: rect(0, 32px, 16px, 16px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-resolution: 1.25dppx) {
|
@media (min-resolution: 1.25dppx) {
|
||||||
notification[value="translation"] .messageImage {
|
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);
|
-moz-image-region: rect(0, 64px, 32px, 32px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notification[value="translation"][state="translating"] .messageImage {
|
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;
|
-moz-image-region: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-resolution: 1.25dppx) {
|
@media (min-resolution: 1.25dppx) {
|
||||||
notification[value="translation"][state="translating"] .messageImage {
|
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");
|
is(nb.allNotifications.length, 1, "There is just one notification");
|
||||||
const cn = nb.currentNotification;
|
const cn = nb.currentNotification;
|
||||||
is(cn.priority, nb.PRIORITY_WARNING_HIGH, "notification priority is correct");
|
is(cn.priority, nb.PRIORITY_WARNING_HIGH, "notification priority is correct");
|
||||||
is(cn.value, "file-import-convert-failed", "notification value is corrent");
|
is(cn.getAttribute("value"), "file-import-convert-failed",
|
||||||
is(cn.type, "warning", "notification type is correct");
|
"notification value is corrent");
|
||||||
|
is(cn.getAttribute("type"), "warning",
|
||||||
|
"notification type is correct");
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
ok(true, "importFromFile does not cause exception");
|
ok(true, "importFromFile does not cause exception");
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ function testOpenDeletedFile() {
|
|||||||
"The missing file was successfully removed from the menu.");
|
"The missing file was successfully removed from the menu.");
|
||||||
ok(gScratchpad.notificationBox.currentNotification,
|
ok(gScratchpad.notificationBox.currentNotification,
|
||||||
"The notification was successfully displayed.");
|
"The notification was successfully displayed.");
|
||||||
is(gScratchpad.notificationBox.currentNotification.label,
|
is(gScratchpad.notificationBox.currentNotification.messageText.textContent,
|
||||||
gScratchpad.strings.GetStringFromName("fileNoLongerExists.notification"),
|
gScratchpad.strings.GetStringFromName("fileNoLongerExists.notification"),
|
||||||
"The notification label is correct.");
|
"The notification label is correct.");
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const CSSUsageFront = protocol.FrontClassWithSpec(cssUsageSpec, {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (notification) {
|
if (notification) {
|
||||||
notification.remove();
|
notification.close();
|
||||||
notification = undefined;
|
notification = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -146,8 +146,7 @@ class TestSafeBrowsingNotificationBar(PuppeteerMixin, MarionetteTestCase):
|
|||||||
# TODO: update to use safe browsing notification bar class when bug 1139544 lands
|
# TODO: update to use safe browsing notification bar class when bug 1139544 lands
|
||||||
button = (self.marionette.find_element(By.ID, 'tabbrowser-tabbox')
|
button = (self.marionette.find_element(By.ID, 'tabbrowser-tabbox')
|
||||||
.find_element(By.CSS_SELECTOR, 'notification[value=blocked-badware-page]')
|
.find_element(By.CSS_SELECTOR, 'notification[value=blocked-badware-page]')
|
||||||
.find_element('anon attribute',
|
.find_element(By.CSS_SELECTOR, '.messageCloseButton'))
|
||||||
{'class': 'messageCloseButton close-icon tabbable'}))
|
|
||||||
button.click()
|
button.click()
|
||||||
|
|
||||||
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
|
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
|
||||||
|
|||||||
@@ -1591,7 +1591,7 @@ var BrowserTestUtils = {
|
|||||||
waitForNotificationInNotificationBox(notificationBox, notificationValue) {
|
waitForNotificationInNotificationBox(notificationBox, notificationValue) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
let check = (event) => {
|
let check = (event) => {
|
||||||
return event.target.value == notificationValue;
|
return event.target.getAttribute("value") == notificationValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
BrowserTestUtils.waitForEvent(notificationBox.stack, "AlertActive",
|
BrowserTestUtils.waitForEvent(notificationBox.stack, "AlertActive",
|
||||||
|
|||||||
@@ -173,8 +173,8 @@ var Heartbeat = class {
|
|||||||
// Build the heartbeat stars
|
// Build the heartbeat stars
|
||||||
if (!this.options.engagementButtonLabel) {
|
if (!this.options.engagementButtonLabel) {
|
||||||
const numStars = this.options.engagementButtonLabel ? 0 : 5;
|
const numStars = this.options.engagementButtonLabel ? 0 : 5;
|
||||||
const ratingContainer = this.chromeWindow.document.createXULElement("hbox");
|
this.ratingContainer = this.chromeWindow.document.createXULElement("hbox");
|
||||||
ratingContainer.id = "star-rating-container";
|
this.ratingContainer.id = "star-rating-container";
|
||||||
|
|
||||||
for (let i = 0; i < numStars; i++) {
|
for (let i = 0; i < numStars; i++) {
|
||||||
// create a star rating element
|
// create a star rating element
|
||||||
@@ -193,43 +193,38 @@ var Heartbeat = class {
|
|||||||
this.userEngaged({type: "stars", score: rating, flowId: this.options.flowId});
|
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");
|
this.notice.messageDetails.style.overflow = "hidden";
|
||||||
details.style.overflow = "hidden";
|
this.notice.messageImage.classList.add("heartbeat", "pulse-onshow");
|
||||||
|
this.notice.messageText.classList.add("heartbeat");
|
||||||
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");
|
|
||||||
|
|
||||||
// Make sure the stars are not pushed to the right by the spacer.
|
// Make sure the stars are not pushed to the right by the spacer.
|
||||||
const rightSpacer = this.chromeWindow.document.createXULElement("spacer");
|
this.rightSpacer = this.chromeWindow.document.createXULElement("spacer");
|
||||||
rightSpacer.flex = 20;
|
this.rightSpacer.flex = 20;
|
||||||
frag.appendChild(rightSpacer);
|
frag.appendChild(this.rightSpacer);
|
||||||
|
|
||||||
// collapse the space before the stars
|
// collapse the space before the stars
|
||||||
this.messageText.flex = 0;
|
this.notice.messageText.flex = 0;
|
||||||
const leftSpacer = this.messageText.nextSibling;
|
this.notice.spacer.flex = 0;
|
||||||
leftSpacer.flex = 0;
|
|
||||||
|
|
||||||
// Add Learn More Link
|
// Add Learn More Link
|
||||||
if (this.options.learnMoreMessage && this.options.learnMoreUrl) {
|
if (this.options.learnMoreMessage && this.options.learnMoreUrl) {
|
||||||
const learnMore = this.chromeWindow.document.createXULElement("label");
|
this.learnMore = this.chromeWindow.document.createXULElement("label");
|
||||||
learnMore.className = "text-link";
|
this.learnMore.className = "text-link";
|
||||||
learnMore.href = this.options.learnMoreUrl.toString();
|
this.learnMore.href = this.options.learnMoreUrl.toString();
|
||||||
learnMore.setAttribute("value", this.options.learnMoreMessage);
|
this.learnMore.setAttribute("value", this.options.learnMoreMessage);
|
||||||
learnMore.addEventListener("click", () => this.maybeNotifyHeartbeat("LearnMore"));
|
this.learnMore.addEventListener("click",
|
||||||
frag.appendChild(learnMore);
|
() => this.maybeNotifyHeartbeat("LearnMore"));
|
||||||
|
frag.appendChild(this.learnMore);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append the fragment and apply the styling
|
// Append the fragment and apply the styling
|
||||||
this.notice.appendChild(frag);
|
this.notice.messageDetails.appendChild(frag);
|
||||||
this.notice.classList.add("heartbeat");
|
this.notice.classList.add("heartbeat");
|
||||||
|
|
||||||
// Let the consumer know the notification was shown.
|
// Let the consumer know the notification was shown.
|
||||||
@@ -330,12 +325,17 @@ var Heartbeat = class {
|
|||||||
userEngaged(engagementParams) {
|
userEngaged(engagementParams) {
|
||||||
// Make the heartbeat icon pulse twice
|
// Make the heartbeat icon pulse twice
|
||||||
this.notice.label = this.options.thanksMessage;
|
this.notice.label = this.options.thanksMessage;
|
||||||
this.messageImage.classList.remove("pulse-onshow");
|
this.notice.messageImage.classList.remove("pulse-onshow");
|
||||||
this.messageImage.classList.add("pulse-twice");
|
this.notice.messageImage.classList.add("pulse-twice");
|
||||||
|
|
||||||
// Remove all the children of the notice (rating container, and the flex)
|
// Remove the custom contents of the notice and the buttons
|
||||||
while (this.notice.firstChild) {
|
if (this.ratingContainer) {
|
||||||
this.notice.firstChild.remove();
|
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.
|
// Open the engagement tab if we have a valid engagement URL.
|
||||||
@@ -381,8 +381,10 @@ var Heartbeat = class {
|
|||||||
// remove references for garbage collection
|
// remove references for garbage collection
|
||||||
this.chromeWindow = null;
|
this.chromeWindow = null;
|
||||||
this.notificationBox = null;
|
this.notificationBox = null;
|
||||||
this.notification = null;
|
|
||||||
this.notice = null;
|
this.notice = null;
|
||||||
|
this.ratingContainer = null;
|
||||||
|
this.rightSpacer = null;
|
||||||
|
this.learnMore = null;
|
||||||
this.eventEmitter = null;
|
this.eventEmitter = null;
|
||||||
this.sandboxManager = null;
|
this.sandboxManager = null;
|
||||||
// Ensure we don't re-enter and release the CleanupManager's reference to us:
|
// Ensure we don't re-enter and release the CleanupManager's reference to us:
|
||||||
|
|||||||
@@ -77,43 +77,43 @@ notification.heartbeat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Learn More link styles */
|
/* Learn More link styles */
|
||||||
.heartbeat > .text-link {
|
.heartbeat > hbox > .text-link {
|
||||||
color: #0095DD !important;
|
color: #0095DD !important;
|
||||||
margin-inline-start: 0 !important;
|
margin-inline-start: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heartbeat > .text-link:hover {
|
.heartbeat > hbox > .text-link:hover {
|
||||||
color: #008ACB !important;
|
color: #008ACB !important;
|
||||||
text-decoration: none !important;
|
text-decoration: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heartbeat > .text-link:hover:active {
|
.heartbeat > hbox > .text-link:hover:active {
|
||||||
color: #006B9D !important;
|
color: #006B9D !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Heartbeat UI Rating Star Classes */
|
/* Heartbeat UI Rating Star Classes */
|
||||||
.heartbeat > #star-rating-container {
|
#star-rating-container {
|
||||||
display: -moz-box;
|
display: -moz-box;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heartbeat > #star-rating-container > #star5 {
|
#star-rating-container > #star5 {
|
||||||
-moz-box-ordinal-group: 5;
|
-moz-box-ordinal-group: 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heartbeat > #star-rating-container > #star4 {
|
#star-rating-container > #star4 {
|
||||||
-moz-box-ordinal-group: 4;
|
-moz-box-ordinal-group: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heartbeat > #star-rating-container > #star3 {
|
#star-rating-container > #star3 {
|
||||||
-moz-box-ordinal-group: 3;
|
-moz-box-ordinal-group: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heartbeat > #star-rating-container > #star2 {
|
#star-rating-container > #star2 {
|
||||||
-moz-box-ordinal-group: 2;
|
-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");
|
background: url("resource://normandy/skin/shared/heartbeat-star-off.svg");
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
@@ -121,7 +121,7 @@ notification.heartbeat {
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heartbeat > #star-rating-container > .star-x:hover,
|
#star-rating-container > .star-x:hover,
|
||||||
.heartbeat > #star-rating-container > .star-x:hover ~ .star-x {
|
#star-rating-container > .star-x:hover ~ .star-x {
|
||||||
background: url("resource://normandy/skin/shared/heartbeat-star-lit.svg");
|
background: url("resource://normandy/skin/shared/heartbeat-star-lit.svg");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,13 +93,12 @@ add_task(async function() {
|
|||||||
|
|
||||||
// Check UI
|
// Check UI
|
||||||
const learnMoreEl = hb.notice.querySelector(".text-link");
|
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(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(".star-x").length, 5, "Correct number of stars");
|
||||||
Assert.equal(hb.notice.querySelectorAll(".notification-button").length, 0, "Engagement button not shown");
|
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.href, "https://example.org/learnmore", "Learn more url correct");
|
||||||
Assert.equal(learnMoreEl.value, "Learn More", "Learn more label 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
|
// Check that when clicking the learn more link, a tab opens with the right URL
|
||||||
let loadedPromise;
|
let loadedPromise;
|
||||||
|
|||||||
@@ -301,8 +301,8 @@ var tests =
|
|||||||
return ntf;
|
return ntf;
|
||||||
},
|
},
|
||||||
result: function(nb, ntf) {
|
result: function(nb, ntf) {
|
||||||
SimpleTest.is(nb.currentNotification == ntf ?
|
is(nb.currentNotification, ntf, "appendNotification last notification");
|
||||||
nb.currentNotification.value : null, "10", "appendNotification order");
|
is(nb.currentNotification.getAttribute("value"), "10", "appendNotification order");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -333,8 +333,8 @@ var tests =
|
|||||||
},
|
},
|
||||||
result: function(nb, arr) {
|
result: function(nb, arr) {
|
||||||
// arr is [testindex, expectedvalue]
|
// arr is [testindex, expectedvalue]
|
||||||
SimpleTest.is(nb.currentNotification.value, "" + arr[1], "close order " + arr[0]);
|
is(nb.currentNotification.getAttribute("value"), "" + arr[1], "close order " + arr[0]);
|
||||||
SimpleTest.is(nb.allNotifications.length, 10 - arr[0], "close order " + arr[0] + " count");
|
is(nb.allNotifications.length, 10 - arr[0], "close order " + arr[0] + " count");
|
||||||
if (arr[0] == 6)
|
if (arr[0] == 6)
|
||||||
this.repeat = false;
|
this.repeat = false;
|
||||||
return ++arr[0];
|
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)
|
function testtag_notification_State(nb, ntf, testid, label, value, image, priority)
|
||||||
{
|
{
|
||||||
SimpleTest.is(ntf.label, label, testid + " notification.label");
|
is(ntf.messageText.textContent, label, testid + " notification label");
|
||||||
SimpleTest.is(ntf.value, value, testid + " notification.value");
|
is(ntf.getAttribute("value"), value, testid + " notification value");
|
||||||
SimpleTest.is(ntf.image, image, testid + " notification.image");
|
is(ntf.messageImage.getAttribute("src"), image, testid + " notification image");
|
||||||
SimpleTest.is(ntf.priority, priority, testid + " notification.priority");
|
is(ntf.priority, priority, testid + " notification priority");
|
||||||
|
|
||||||
var type;
|
var type;
|
||||||
switch (priority) {
|
switch (priority) {
|
||||||
@@ -424,7 +424,7 @@ function testtag_notification_State(nb, ntf, testid, label, value, image, priori
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleTest.is(ntf.type, type, testid + " notification.type");
|
is(ntf.getAttribute("type"), type, testid + " notification type");
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkPopupTest(nb, ntf)
|
function checkPopupTest(nb, ntf)
|
||||||
|
|||||||
@@ -15,99 +15,6 @@
|
|||||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
xmlns:html = "http://www.w3.org/1999/xhtml">
|
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">
|
<binding id="popup-notification">
|
||||||
<content orient="vertical">
|
<content orient="vertical">
|
||||||
<xul:hbox class="popup-notification-header-container">
|
<xul:hbox class="popup-notification-header-container">
|
||||||
|
|||||||
@@ -64,7 +64,49 @@ MozElements.NotificationBox = class NotificationBox {
|
|||||||
return null;
|
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 ||
|
if (aPriority < this.PRIORITY_INFO_LOW ||
|
||||||
aPriority > this.PRIORITY_CRITICAL_HIGH)
|
aPriority > this.PRIORITY_CRITICAL_HIGH)
|
||||||
throw "Invalid notification priority " + aPriority;
|
throw "Invalid notification priority " + aPriority;
|
||||||
@@ -79,24 +121,28 @@ MozElements.NotificationBox = class NotificationBox {
|
|||||||
insertPos = notifications[n];
|
insertPos = notifications[n];
|
||||||
}
|
}
|
||||||
|
|
||||||
var newitem = document.createXULElement("notification");
|
// Create the Custom Element and connect it to the document immediately.
|
||||||
// Can't use instanceof in case this was created from a different document:
|
var newitem = document.createXULElement("notification",
|
||||||
let labelIsDocFragment = aLabel && typeof aLabel == "object" && aLabel.nodeType &&
|
aNotificationIs ? { is: aNotificationIs } : {});
|
||||||
aLabel.nodeType == aLabel.DOCUMENT_FRAGMENT_NODE;
|
this.stack.insertBefore(newitem, insertPos);
|
||||||
if (!labelIsDocFragment)
|
|
||||||
newitem.setAttribute("label", aLabel);
|
// 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);
|
newitem.setAttribute("value", aValue);
|
||||||
if (aImage)
|
if (aImage) {
|
||||||
newitem.setAttribute("image", aImage);
|
newitem.messageImage.setAttribute("src", aImage);
|
||||||
|
}
|
||||||
newitem.eventCallback = aEventCallback;
|
newitem.eventCallback = aEventCallback;
|
||||||
|
|
||||||
if (aButtons) {
|
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++) {
|
for (var b = 0; b < aButtons.length; b++) {
|
||||||
var button = aButtons[b];
|
var button = aButtons[b];
|
||||||
var buttonElem = document.createXULElement("button");
|
var buttonElem = document.createXULElement("button");
|
||||||
@@ -104,20 +150,12 @@ MozElements.NotificationBox = class NotificationBox {
|
|||||||
if (typeof button.accessKey == "string")
|
if (typeof button.accessKey == "string")
|
||||||
buttonElem.setAttribute("accesskey", button.accessKey);
|
buttonElem.setAttribute("accesskey", button.accessKey);
|
||||||
buttonElem.classList.add("notification-button");
|
buttonElem.classList.add("notification-button");
|
||||||
|
newitem.messageDetails.appendChild(buttonElem);
|
||||||
if (button.isDefault ||
|
|
||||||
b == 0 && !("isDefault" in button))
|
|
||||||
defaultElem = buttonElem;
|
|
||||||
|
|
||||||
newitem.appendChild(buttonElem);
|
|
||||||
buttonElem.buttonInfo = button;
|
buttonElem.buttonInfo = button;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defaultElem)
|
|
||||||
defaultElem.classList.add("notification-button-default");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newitem.setAttribute("priority", aPriority);
|
newitem.priority = aPriority;
|
||||||
if (aPriority >= this.PRIORITY_CRITICAL_LOW)
|
if (aPriority >= this.PRIORITY_CRITICAL_LOW)
|
||||||
newitem.setAttribute("type", "critical");
|
newitem.setAttribute("type", "critical");
|
||||||
else if (aPriority <= this.PRIORITY_INFO_HIGH)
|
else if (aPriority <= this.PRIORITY_INFO_HIGH)
|
||||||
@@ -130,17 +168,8 @@ MozElements.NotificationBox = class NotificationBox {
|
|||||||
newitem.style.top = "100%";
|
newitem.style.top = "100%";
|
||||||
newitem.style.marginTop = "-15px";
|
newitem.style.marginTop = "-15px";
|
||||||
newitem.style.opacity = "0";
|
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);
|
this._showNotification(newitem, true);
|
||||||
|
}
|
||||||
|
|
||||||
// Fire event for accessibility APIs
|
// Fire event for accessibility APIs
|
||||||
var event = document.createEvent("Events");
|
var event = document.createEvent("Events");
|
||||||
@@ -265,3 +294,80 @@ Object.assign(MozElements.NotificationBox.prototype, {
|
|||||||
PRIORITY_CRITICAL_MEDIUM: 8,
|
PRIORITY_CRITICAL_MEDIUM: 8,
|
||||||
PRIORITY_CRITICAL_HIGH: 9,
|
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");
|
-moz-binding: url("chrome://global/content/bindings/browser.xml#browser");
|
||||||
}
|
}
|
||||||
|
|
||||||
/********** notifications **********/
|
|
||||||
|
|
||||||
notification {
|
|
||||||
-moz-binding: url("chrome://global/content/bindings/notification.xml#notification");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********** popup notification ************/
|
/*********** popup notification ************/
|
||||||
popupnotification {
|
popupnotification {
|
||||||
-moz-binding: url("chrome://global/content/bindings/notification.xml#popup-notification");
|
-moz-binding: url("chrome://global/content/bindings/notification.xml#popup-notification");
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ label[disabled="true"] {
|
|||||||
outline: 1px dotted;
|
outline: 1px dotted;
|
||||||
}
|
}
|
||||||
|
|
||||||
notification > button {
|
notification > hbox > button {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ label[disabled="true"] {
|
|||||||
box-shadow: var(--focus-ring-box-shadow);
|
box-shadow: var(--focus-ring-box-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
notification > button {
|
notification > hbox > button {
|
||||||
margin: 0 3px;
|
margin: 0 3px;
|
||||||
padding: 1px 10px;
|
padding: 1px 10px;
|
||||||
min-width: 60px;
|
min-width: 60px;
|
||||||
@@ -232,21 +232,21 @@ notification > button {
|
|||||||
box-shadow: @roundButtonShadow@;
|
box-shadow: @roundButtonShadow@;
|
||||||
}
|
}
|
||||||
|
|
||||||
notification > button:active:hover {
|
notification > hbox > button:active:hover {
|
||||||
color: @roundButtonColor@;
|
color: @roundButtonColor@;
|
||||||
background: @roundButtonPressedBackground@;
|
background: @roundButtonPressedBackground@;
|
||||||
box-shadow: @roundButtonPressedShadow@;
|
box-shadow: @roundButtonPressedShadow@;
|
||||||
}
|
}
|
||||||
|
|
||||||
notification > button:-moz-focusring {
|
notification > hbox > button:-moz-focusring {
|
||||||
box-shadow: var(--focus-ring-box-shadow), @roundButtonShadow@;
|
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@;
|
box-shadow: var(--focus-ring-box-shadow), @roundButtonPressedShadow@;
|
||||||
}
|
}
|
||||||
|
|
||||||
notification > button > .button-box > .button-text {
|
notification > hbox > button > .button-box > .button-text {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,15 +63,15 @@ notification[type="critical"] {
|
|||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messageImage[type="info"] {
|
notification[type="info"] > hbox > .messageImage {
|
||||||
list-style-image: url("chrome://global/skin/icons/help.svg");
|
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");
|
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");
|
list-style-image: url("chrome://global/skin/icons/error.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ add_task(async function() {
|
|||||||
gBrowser.getNotificationBox().getNotificationWithValue(notificationValue);
|
gBrowser.getNotificationBox().getNotificationWithValue(notificationValue);
|
||||||
await BrowserTestUtils.waitForCondition(getNotification);
|
await BrowserTestUtils.waitForCondition(getNotification);
|
||||||
let notification = getNotification();
|
let notification = getNotification();
|
||||||
let button =
|
let button = notification.querySelector("button");
|
||||||
notification.getElementsByClassName("notification-button-default")[0];
|
|
||||||
ok(button, "got registration button");
|
ok(button, "got registration button");
|
||||||
button.click();
|
button.click();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user