зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1614750, convert translation bar to use JSWindowActor, r=florian
Differential Revision: https://phabricator.services.mozilla.com/D67535
This commit is contained in:
Родитель
286aa3c732
Коммит
c7e68f61b7
|
@ -76,7 +76,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
TabModalPrompt: "chrome://global/content/tabprompts.jsm",
|
||||
TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
|
||||
TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
|
||||
Translation: "resource:///modules/translation/Translation.jsm",
|
||||
Translation: "resource:///modules/translation/TranslationParent.jsm",
|
||||
UITour: "resource:///modules/UITour.jsm",
|
||||
UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
|
||||
UrlbarInput: "resource:///modules/UrlbarInput.jsm",
|
||||
|
@ -1805,7 +1805,6 @@ var gBrowserInit = {
|
|||
// loading the frame script to ensure that we don't miss any
|
||||
// message sent between when the frame script is loaded and when
|
||||
// the listener is registered.
|
||||
LanguageDetectionListener.init();
|
||||
CaptivePortalWatcher.init();
|
||||
ZoomUI.init(window);
|
||||
|
||||
|
@ -7394,17 +7393,6 @@ var gPageStyleMenu = {
|
|||
},
|
||||
};
|
||||
|
||||
var LanguageDetectionListener = {
|
||||
init() {
|
||||
window.messageManager.addMessageListener(
|
||||
"Translation:DocumentState",
|
||||
msg => {
|
||||
Translation.documentStateReceived(msg.target, msg.data);
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
// Note that this is also called from non-browser windows on OSX, which do
|
||||
// share menu items but not much else. See nonbrowser-mac.js.
|
||||
var BrowserOffline = {
|
||||
|
|
|
@ -29,15 +29,6 @@ ActorManagerChild.attach(this, "browsers");
|
|||
// BrowserChildGlobal
|
||||
var global = this;
|
||||
|
||||
// Keep a reference to the translation content handler to avoid it it being GC'ed.
|
||||
var trHandler = null;
|
||||
if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) {
|
||||
var { TranslationContentHandler } = ChromeUtils.import(
|
||||
"resource:///modules/translation/TranslationContentHandler.jsm"
|
||||
);
|
||||
trHandler = new TranslationContentHandler(global, docShell);
|
||||
}
|
||||
|
||||
var WebBrowserChrome = {
|
||||
onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
|
||||
return BrowserUtils.onBeforeLinkTraversal(
|
||||
|
|
|
@ -403,6 +403,20 @@ let ACTORS = {
|
|||
allFrames: true,
|
||||
},
|
||||
|
||||
Translation: {
|
||||
parent: {
|
||||
moduleURI: "resource:///modules/translation/TranslationParent.jsm",
|
||||
},
|
||||
child: {
|
||||
moduleURI: "resource:///modules/translation/TranslationChild.jsm",
|
||||
events: {
|
||||
pageshow: {},
|
||||
load: { mozSystemGroup: true },
|
||||
},
|
||||
},
|
||||
enablePreference: "browser.translation.detectLanguage",
|
||||
},
|
||||
|
||||
UITour: {
|
||||
parent: {
|
||||
moduleURI: "resource:///modules/UITourParent.jsm",
|
||||
|
|
|
@ -539,7 +539,7 @@ var gMainPane = {
|
|||
row.removeAttribute("hidden");
|
||||
// Showing attribution only for Bing Translator.
|
||||
var { Translation } = ChromeUtils.import(
|
||||
"resource:///modules/translation/Translation.jsm"
|
||||
"resource:///modules/translation/TranslationParent.jsm"
|
||||
);
|
||||
if (Translation.translationEngine == "Bing") {
|
||||
document.getElementById("bingAttribution").removeAttribute("hidden");
|
||||
|
@ -1379,7 +1379,7 @@ var gMainPane = {
|
|||
|
||||
openTranslationProviderAttribution() {
|
||||
var { Translation } = ChromeUtils.import(
|
||||
"resource:///modules/translation/Translation.jsm"
|
||||
"resource:///modules/translation/TranslationParent.jsm"
|
||||
);
|
||||
Translation.openProviderAttribution();
|
||||
},
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
/* 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";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["TranslationChild"];
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"LanguageDetector",
|
||||
"resource:///modules/translation/LanguageDetector.jsm"
|
||||
);
|
||||
|
||||
const STATE_OFFER = 0;
|
||||
const STATE_TRANSLATED = 2;
|
||||
const STATE_ERROR = 3;
|
||||
|
||||
class TranslationChild extends JSWindowActorChild {
|
||||
handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "pageshow":
|
||||
// We are only listening to pageshow events.
|
||||
let content = this.contentWindow;
|
||||
if (!content.detectedLanguage) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = {};
|
||||
let trDoc = content.translationDocument;
|
||||
if (trDoc) {
|
||||
data.state = trDoc.translationError ? STATE_ERROR : STATE_TRANSLATED;
|
||||
data.translatedFrom = trDoc.translatedFrom;
|
||||
data.translatedTo = trDoc.translatedTo;
|
||||
data.originalShown = trDoc.originalShown;
|
||||
} else {
|
||||
data.state = STATE_OFFER;
|
||||
data.originalShown = true;
|
||||
}
|
||||
data.detectedLanguage = content.detectedLanguage;
|
||||
|
||||
this.sendAsyncMessage("Translation:DocumentState", data);
|
||||
break;
|
||||
|
||||
case "load":
|
||||
this.checkForTranslation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
checkForTranslation() {
|
||||
let url = String(this.document.location);
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let content = this.contentWindow;
|
||||
if (content.detectedLanguage) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab a 60k sample of text from the page.
|
||||
let encoder = Cu.createDocumentEncoder("text/plain");
|
||||
encoder.init(this.document, "text/plain", encoder.SkipInvisibleContent);
|
||||
let string = encoder.encodeToStringWithMaxLength(60 * 1024);
|
||||
|
||||
// Language detection isn't reliable on very short strings.
|
||||
if (string.length < 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
LanguageDetector.detectLanguage(string).then(result => {
|
||||
// Bail if we're not confident.
|
||||
if (!result.confident) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The window might be gone by now.
|
||||
if (Cu.isDeadWrapper(content)) {
|
||||
return;
|
||||
}
|
||||
|
||||
content.detectedLanguage = result.language;
|
||||
|
||||
let data = {
|
||||
state: STATE_OFFER,
|
||||
originalShown: true,
|
||||
detectedLanguage: result.language,
|
||||
};
|
||||
this.sendAsyncMessage("Translation:DocumentState", data);
|
||||
});
|
||||
}
|
||||
|
||||
async doTranslation(aFrom, aTo) {
|
||||
var { TranslationDocument } = ChromeUtils.import(
|
||||
"resource:///modules/translation/TranslationDocument.jsm"
|
||||
);
|
||||
|
||||
// If a TranslationDocument already exists for this document, it should
|
||||
// be used instead of creating a new one so that we can use the original
|
||||
// content of the page for the new translation instead of the newly
|
||||
// translated text.
|
||||
let translationDocument =
|
||||
this.contentWindow.translationDocument ||
|
||||
new TranslationDocument(this.document);
|
||||
|
||||
let engine = Services.prefs.getCharPref("browser.translation.engine");
|
||||
let importScope = ChromeUtils.import(
|
||||
`resource:///modules/translation/${engine}Translator.jsm`,
|
||||
{}
|
||||
);
|
||||
let translator = new importScope[engine + "Translator"](
|
||||
translationDocument,
|
||||
aFrom,
|
||||
aTo
|
||||
);
|
||||
|
||||
this.contentWindow.translationDocument = translationDocument;
|
||||
translationDocument.translatedFrom = aFrom;
|
||||
translationDocument.translatedTo = aTo;
|
||||
translationDocument.translationError = false;
|
||||
|
||||
let result;
|
||||
try {
|
||||
let translateResult = await translator.translate();
|
||||
|
||||
result = {
|
||||
characterCount: translateResult.characterCount,
|
||||
from: aFrom,
|
||||
to: aTo,
|
||||
success: true,
|
||||
};
|
||||
|
||||
translationDocument.showTranslation();
|
||||
return result;
|
||||
} catch (ex) {
|
||||
translationDocument.translationError = true;
|
||||
result = { success: false };
|
||||
if (ex == "unavailable") {
|
||||
result.unavailable = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
receiveMessage(aMessage) {
|
||||
switch (aMessage.name) {
|
||||
case "Translation:TranslateDocument": {
|
||||
return this.doTranslation(aMessage.data.from, aMessage.data.to);
|
||||
}
|
||||
|
||||
case "Translation:ShowOriginal":
|
||||
this.contentWindow.translationDocument.showOriginal();
|
||||
break;
|
||||
|
||||
case "Translation:ShowTranslation":
|
||||
this.contentWindow.translationDocument.showTranslation();
|
||||
break;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
|
@ -1,193 +0,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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["TranslationContentHandler"];
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"LanguageDetector",
|
||||
"resource:///modules/translation/LanguageDetector.jsm"
|
||||
);
|
||||
|
||||
const STATE_OFFER = 0;
|
||||
const STATE_TRANSLATED = 2;
|
||||
const STATE_ERROR = 3;
|
||||
|
||||
var TranslationContentHandler = function(global, docShell) {
|
||||
let webProgress = docShell
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
webProgress.addProgressListener(
|
||||
this,
|
||||
Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT
|
||||
);
|
||||
|
||||
global.addEventListener("pageshow", this);
|
||||
|
||||
global.addMessageListener("Translation:TranslateDocument", this);
|
||||
global.addMessageListener("Translation:ShowTranslation", this);
|
||||
global.addMessageListener("Translation:ShowOriginal", this);
|
||||
this.global = global;
|
||||
};
|
||||
|
||||
TranslationContentHandler.prototype = {
|
||||
handleEvent(aEvent) {
|
||||
// We are only listening to pageshow events.
|
||||
let target = aEvent.target;
|
||||
|
||||
// Only handle top-level frames.
|
||||
let win = target.defaultView;
|
||||
if (win.parent !== win) {
|
||||
return;
|
||||
}
|
||||
|
||||
let content = this.global.content;
|
||||
if (!content.detectedLanguage) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = {};
|
||||
let trDoc = content.translationDocument;
|
||||
if (trDoc) {
|
||||
data.state = trDoc.translationError ? STATE_ERROR : STATE_TRANSLATED;
|
||||
data.translatedFrom = trDoc.translatedFrom;
|
||||
data.translatedTo = trDoc.translatedTo;
|
||||
data.originalShown = trDoc.originalShown;
|
||||
} else {
|
||||
data.state = STATE_OFFER;
|
||||
data.originalShown = true;
|
||||
}
|
||||
data.detectedLanguage = content.detectedLanguage;
|
||||
|
||||
this.global.sendAsyncMessage("Translation:DocumentState", data);
|
||||
},
|
||||
|
||||
/* nsIWebProgressListener implementation */
|
||||
onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
if (
|
||||
!aWebProgress.isTopLevel ||
|
||||
!(aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) ||
|
||||
!this.global.content
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let url = aRequest.name;
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// nsIRequest.name throws NS_ERROR_NOT_IMPLEMENTED for view-source: tabs.
|
||||
return;
|
||||
}
|
||||
|
||||
let content = this.global.content;
|
||||
if (content.detectedLanguage) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab a 60k sample of text from the page.
|
||||
let encoder = Cu.createDocumentEncoder("text/plain");
|
||||
encoder.init(content.document, "text/plain", encoder.SkipInvisibleContent);
|
||||
let string = encoder.encodeToStringWithMaxLength(60 * 1024);
|
||||
|
||||
// Language detection isn't reliable on very short strings.
|
||||
if (string.length < 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
LanguageDetector.detectLanguage(string).then(result => {
|
||||
// Bail if we're not confident.
|
||||
if (!result.confident) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The window might be gone by now.
|
||||
if (Cu.isDeadWrapper(content)) {
|
||||
return;
|
||||
}
|
||||
|
||||
content.detectedLanguage = result.language;
|
||||
|
||||
let data = {
|
||||
state: STATE_OFFER,
|
||||
originalShown: true,
|
||||
detectedLanguage: result.language,
|
||||
};
|
||||
this.global.sendAsyncMessage("Translation:DocumentState", data);
|
||||
});
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([
|
||||
Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference,
|
||||
]),
|
||||
|
||||
receiveMessage(msg) {
|
||||
switch (msg.name) {
|
||||
case "Translation:TranslateDocument": {
|
||||
var { TranslationDocument } = ChromeUtils.import(
|
||||
"resource:///modules/translation/TranslationDocument.jsm"
|
||||
);
|
||||
|
||||
// If a TranslationDocument already exists for this document, it should
|
||||
// be used instead of creating a new one so that we can use the original
|
||||
// content of the page for the new translation instead of the newly
|
||||
// translated text.
|
||||
let translationDocument =
|
||||
this.global.content.translationDocument ||
|
||||
new TranslationDocument(this.global.content.document);
|
||||
|
||||
let engine = Services.prefs.getCharPref("browser.translation.engine");
|
||||
let importScope = ChromeUtils.import(
|
||||
`resource:///modules/translation/${engine}Translator.jsm`,
|
||||
{}
|
||||
);
|
||||
let translator = new importScope[engine + "Translator"](
|
||||
translationDocument,
|
||||
msg.data.from,
|
||||
msg.data.to
|
||||
);
|
||||
|
||||
this.global.content.translationDocument = translationDocument;
|
||||
translationDocument.translatedFrom = msg.data.from;
|
||||
translationDocument.translatedTo = msg.data.to;
|
||||
translationDocument.translationError = false;
|
||||
|
||||
translator.translate().then(
|
||||
result => {
|
||||
this.global.sendAsyncMessage("Translation:Finished", {
|
||||
characterCount: result.characterCount,
|
||||
from: msg.data.from,
|
||||
to: msg.data.to,
|
||||
success: true,
|
||||
});
|
||||
translationDocument.showTranslation();
|
||||
},
|
||||
error => {
|
||||
translationDocument.translationError = true;
|
||||
let data = { success: false };
|
||||
if (error == "unavailable") {
|
||||
data.unavailable = true;
|
||||
}
|
||||
this.global.sendAsyncMessage("Translation:Finished", data);
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case "Translation:ShowOriginal":
|
||||
this.global.content.translationDocument.showOriginal();
|
||||
break;
|
||||
|
||||
case "Translation:ShowTranslation":
|
||||
this.global.content.translationDocument.showTranslation();
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
|
@ -4,7 +4,11 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["Translation", "TranslationTelemetry"];
|
||||
var EXPORTED_SYMBOLS = [
|
||||
"Translation",
|
||||
"TranslationParent",
|
||||
"TranslationTelemetry",
|
||||
];
|
||||
|
||||
const TRANSLATION_PREF_SHOWUI = "browser.translation.ui.show";
|
||||
const TRANSLATION_PREF_DETECT_LANG = "browser.translation.detectLanguage";
|
||||
|
@ -18,6 +22,8 @@ var Translation = {
|
|||
STATE_ERROR: 3,
|
||||
STATE_UNAVAILABLE: 4,
|
||||
|
||||
translationListener: null,
|
||||
|
||||
serviceUnavailable: false,
|
||||
|
||||
supportedSourceLanguages: [
|
||||
|
@ -57,6 +63,10 @@ var Translation = {
|
|||
"zh",
|
||||
],
|
||||
|
||||
setListenerForTests(listener) {
|
||||
this.translationListener = listener;
|
||||
},
|
||||
|
||||
_defaultTargetLanguage: "",
|
||||
get defaultTargetLanguage() {
|
||||
if (!this._defaultTargetLanguage) {
|
||||
|
@ -67,49 +77,6 @@ var Translation = {
|
|||
return this._defaultTargetLanguage;
|
||||
},
|
||||
|
||||
documentStateReceived(aBrowser, aData) {
|
||||
if (aData.state == this.STATE_OFFER) {
|
||||
if (aData.detectedLanguage == this.defaultTargetLanguage) {
|
||||
// Detected language is the same as the user's locale.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.supportedSourceLanguages.includes(aData.detectedLanguage)) {
|
||||
// Detected language is not part of the supported languages.
|
||||
TranslationTelemetry.recordMissedTranslationOpportunity(
|
||||
aData.detectedLanguage
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
TranslationTelemetry.recordTranslationOpportunity(aData.detectedLanguage);
|
||||
}
|
||||
|
||||
if (!Services.prefs.getBoolPref(TRANSLATION_PREF_SHOWUI)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aBrowser.translationUI) {
|
||||
aBrowser.translationUI = new TranslationUI(aBrowser);
|
||||
}
|
||||
let trUI = aBrowser.translationUI;
|
||||
|
||||
// Set all values before showing a new translation infobar.
|
||||
trUI._state = Translation.serviceUnavailable
|
||||
? Translation.STATE_UNAVAILABLE
|
||||
: aData.state;
|
||||
trUI.detectedLanguage = aData.detectedLanguage;
|
||||
trUI.translatedFrom = aData.translatedFrom;
|
||||
trUI.translatedTo = aData.translatedTo;
|
||||
trUI.originalShown = aData.originalShown;
|
||||
|
||||
trUI.showURLBarIcon();
|
||||
|
||||
if (trUI.shouldShowInfoBar(aBrowser.contentPrincipal)) {
|
||||
trUI.showTranslationInfoBar();
|
||||
}
|
||||
},
|
||||
|
||||
openProviderAttribution() {
|
||||
let attribution = this.supportedEngines[this.translationEngine];
|
||||
const { BrowserWindowTracker } = ChromeUtils.import(
|
||||
|
@ -145,10 +112,8 @@ var Translation = {
|
|||
},
|
||||
};
|
||||
|
||||
/* TranslationUI objects keep the information related to translation for
|
||||
* a specific browser. This object is passed to the translation
|
||||
* infobar so that it can initialize itself. The properties exposed to
|
||||
* the infobar are:
|
||||
/* Translation objects keep the information related to translation for
|
||||
* a specific browser. The properties exposed to the infobar are:
|
||||
* - detectedLanguage, code of the language detected on the web page.
|
||||
* - state, the state in which the infobar should be displayed
|
||||
* - translatedFrom, if already translated, source language code.
|
||||
|
@ -160,24 +125,65 @@ var Translation = {
|
|||
* - originalShown, boolean indicating if the original or translated
|
||||
* version of the page is shown.
|
||||
*/
|
||||
function TranslationUI(aBrowser) {
|
||||
this.browser = aBrowser;
|
||||
}
|
||||
class TranslationParent extends JSWindowActorParent {
|
||||
actorCreated() {
|
||||
this._state = 0;
|
||||
this.originalShown = true;
|
||||
}
|
||||
|
||||
TranslationUI.prototype = {
|
||||
get browser() {
|
||||
return this._browser;
|
||||
},
|
||||
set browser(aBrowser) {
|
||||
if (this._browser) {
|
||||
this._browser.messageManager.removeMessageListener(
|
||||
"Translation:Finished",
|
||||
this
|
||||
);
|
||||
let browser = this.browsingContext.top.embedderElement;
|
||||
return browser.outerBrowser ? browser.outerBrowser : browser;
|
||||
}
|
||||
|
||||
receiveMessage(aMessage) {
|
||||
switch (aMessage.name) {
|
||||
case "Translation:DocumentState":
|
||||
this.documentStateReceived(aMessage.data);
|
||||
break;
|
||||
}
|
||||
aBrowser.messageManager.addMessageListener("Translation:Finished", this);
|
||||
this._browser = aBrowser;
|
||||
},
|
||||
}
|
||||
|
||||
documentStateReceived(aData) {
|
||||
if (aData.state == Translation.STATE_OFFER) {
|
||||
if (aData.detectedLanguage == Translation.defaultTargetLanguage) {
|
||||
// Detected language is the same as the user's locale.
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!Translation.supportedTargetLanguages.includes(aData.detectedLanguage)
|
||||
) {
|
||||
// Detected language is not part of the supported languages.
|
||||
TranslationTelemetry.recordMissedTranslationOpportunity(
|
||||
aData.detectedLanguage
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
TranslationTelemetry.recordTranslationOpportunity(aData.detectedLanguage);
|
||||
}
|
||||
|
||||
if (!Services.prefs.getBoolPref(TRANSLATION_PREF_SHOWUI)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set all values before showing a new translation infobar.
|
||||
this._state = Translation.serviceUnavailable
|
||||
? Translation.STATE_UNAVAILABLE
|
||||
: aData.state;
|
||||
this.detectedLanguage = aData.detectedLanguage;
|
||||
this.translatedFrom = aData.translatedFrom;
|
||||
this.translatedTo = aData.translatedTo;
|
||||
this.originalShown = aData.originalShown;
|
||||
|
||||
this.showURLBarIcon();
|
||||
|
||||
if (this.shouldShowInfoBar(this.browser.contentPrincipal)) {
|
||||
this.showTranslationInfoBar();
|
||||
}
|
||||
}
|
||||
|
||||
translate(aFrom, aTo) {
|
||||
if (
|
||||
aFrom == aTo ||
|
||||
|
@ -206,11 +212,16 @@ TranslationUI.prototype = {
|
|||
this.translatedFrom = aFrom;
|
||||
this.translatedTo = aTo;
|
||||
|
||||
this.browser.messageManager.sendAsyncMessage(
|
||||
"Translation:TranslateDocument",
|
||||
{ from: aFrom, to: aTo }
|
||||
this.sendQuery("Translation:TranslateDocument", {
|
||||
from: aFrom,
|
||||
to: aTo,
|
||||
}).then(
|
||||
result => {
|
||||
this.translationFinished(result);
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
showURLBarIcon() {
|
||||
let chromeWin = this.browser.ownerGlobal;
|
||||
|
@ -229,8 +240,6 @@ TranslationUI.prototype = {
|
|||
let infoBarVisible = this.notificationBox.getNotificationWithValue(
|
||||
"translation"
|
||||
);
|
||||
aNewBrowser.translationUI = this;
|
||||
this.browser = aNewBrowser;
|
||||
if (infoBarVisible) {
|
||||
this.showTranslationInfoBar();
|
||||
}
|
||||
|
@ -261,37 +270,36 @@ TranslationUI.prototype = {
|
|||
null,
|
||||
{ dismissed: true, eventCallback: callback }
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
_state: 0,
|
||||
get state() {
|
||||
return this._state;
|
||||
},
|
||||
}
|
||||
|
||||
set state(val) {
|
||||
let notif = this.notificationBox.getNotificationWithValue("translation");
|
||||
if (notif) {
|
||||
notif.state = val;
|
||||
}
|
||||
this._state = val;
|
||||
},
|
||||
}
|
||||
|
||||
originalShown: true,
|
||||
showOriginalContent() {
|
||||
this.originalShown = true;
|
||||
this.showURLBarIcon();
|
||||
this.browser.messageManager.sendAsyncMessage("Translation:ShowOriginal");
|
||||
this.sendAsyncMessage("Translation:ShowOriginal");
|
||||
TranslationTelemetry.recordShowOriginalContent();
|
||||
},
|
||||
}
|
||||
|
||||
showTranslatedContent() {
|
||||
this.originalShown = false;
|
||||
this.showURLBarIcon();
|
||||
this.browser.messageManager.sendAsyncMessage("Translation:ShowTranslation");
|
||||
},
|
||||
this.sendAsyncMessage("Translation:ShowTranslation");
|
||||
}
|
||||
|
||||
get notificationBox() {
|
||||
return this.browser.ownerGlobal.gBrowser.getNotificationBox(this.browser);
|
||||
},
|
||||
}
|
||||
|
||||
showTranslationInfoBar() {
|
||||
let notificationBox = this.notificationBox;
|
||||
|
@ -306,7 +314,7 @@ TranslationUI.prototype = {
|
|||
);
|
||||
notif.init(this);
|
||||
return notif;
|
||||
},
|
||||
}
|
||||
|
||||
shouldShowInfoBar(aPrincipal) {
|
||||
// Never show the infobar automatically while the translation
|
||||
|
@ -335,38 +343,38 @@ TranslationUI.prototype = {
|
|||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
||||
receiveMessage(msg) {
|
||||
switch (msg.name) {
|
||||
case "Translation:Finished":
|
||||
if (msg.data.success) {
|
||||
this.originalShown = false;
|
||||
this.state = Translation.STATE_TRANSLATED;
|
||||
this.showURLBarIcon();
|
||||
translationFinished(result) {
|
||||
if (result.success) {
|
||||
this.originalShown = false;
|
||||
this.state = Translation.STATE_TRANSLATED;
|
||||
this.showURLBarIcon();
|
||||
|
||||
// Record the number of characters translated.
|
||||
TranslationTelemetry.recordTranslation(
|
||||
msg.data.from,
|
||||
msg.data.to,
|
||||
msg.data.characterCount
|
||||
);
|
||||
} else if (msg.data.unavailable) {
|
||||
Translation.serviceUnavailable = true;
|
||||
this.state = Translation.STATE_UNAVAILABLE;
|
||||
} else {
|
||||
this.state = Translation.STATE_ERROR;
|
||||
}
|
||||
break;
|
||||
// Record the number of characters translated.
|
||||
TranslationTelemetry.recordTranslation(
|
||||
result.from,
|
||||
result.to,
|
||||
result.characterCount
|
||||
);
|
||||
} else if (result.unavailable) {
|
||||
Translation.serviceUnavailable = true;
|
||||
this.state = Translation.STATE_UNAVAILABLE;
|
||||
} else {
|
||||
this.state = Translation.STATE_ERROR;
|
||||
}
|
||||
},
|
||||
|
||||
if (Translation.translationListener) {
|
||||
Translation.translationListener();
|
||||
}
|
||||
}
|
||||
|
||||
infobarClosed() {
|
||||
if (this.state == Translation.STATE_OFFER) {
|
||||
TranslationTelemetry.recordDeniedTranslationOffer();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses telemetry histograms for collecting statistics on the usage of the
|
|
@ -130,6 +130,7 @@ class MozTranslationNotification extends MozElements.Notification {
|
|||
return this._getAnonElt("translationStates").selectedIndex;
|
||||
}
|
||||
|
||||
// aTranslation is the TranslationParent actor.
|
||||
init(aTranslation) {
|
||||
this.translation = aTranslation;
|
||||
|
||||
|
|
|
@ -15,9 +15,9 @@ EXTRA_JS_MODULES.translation = [
|
|||
'cld2/cld-worker.js.mem',
|
||||
'GoogleTranslator.jsm',
|
||||
'LanguageDetector.jsm',
|
||||
'Translation.jsm',
|
||||
'TranslationContentHandler.jsm',
|
||||
'TranslationChild.jsm',
|
||||
'TranslationDocument.jsm',
|
||||
'TranslationParent.jsm',
|
||||
'YandexTranslator.jsm'
|
||||
]
|
||||
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
// tests the translation infobar, using a fake 'Translation' implementation.
|
||||
|
||||
var tmp = {};
|
||||
ChromeUtils.import("resource:///modules/translation/Translation.jsm", tmp);
|
||||
ChromeUtils.import(
|
||||
"resource:///modules/translation/TranslationParent.jsm",
|
||||
tmp
|
||||
);
|
||||
const { PermissionTestUtils } = ChromeUtils.import(
|
||||
"resource://testing-common/PermissionTestUtils.jsm"
|
||||
);
|
||||
|
@ -13,16 +16,19 @@ var { Translation } = tmp;
|
|||
|
||||
const kLanguagesPref = "browser.translation.neverForLanguages";
|
||||
const kShowUIPref = "browser.translation.ui.show";
|
||||
const kEnableTranslationPref = "browser.translation.detectLanguage";
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
Services.prefs.setBoolPref(kShowUIPref, true);
|
||||
Services.prefs.setBoolPref(kEnableTranslationPref, true);
|
||||
let tab = BrowserTestUtils.addTab(gBrowser);
|
||||
gBrowser.selectedTab = tab;
|
||||
registerCleanupFunction(function() {
|
||||
gBrowser.removeTab(tab);
|
||||
Services.prefs.clearUserPref(kShowUIPref);
|
||||
Services.prefs.clearUserPref(kEnableTranslationPref);
|
||||
});
|
||||
BrowserTestUtils.browserLoaded(tab.linkedBrowser).then(() => {
|
||||
(async function() {
|
||||
|
@ -126,17 +132,20 @@ var gTests = [
|
|||
desc: "never for language",
|
||||
run: async function checkNeverForLanguage() {
|
||||
// Show the infobar for example.com and fr.
|
||||
Translation.documentStateReceived(gBrowser.selectedBrowser, {
|
||||
let actor = gBrowser.selectedBrowser.browsingContext.currentWindowGlobal.getActor(
|
||||
"Translation"
|
||||
);
|
||||
actor.documentStateReceived({
|
||||
state: Translation.STATE_OFFER,
|
||||
originalShown: true,
|
||||
detectedLanguage: "fr",
|
||||
});
|
||||
let notif = await getInfoBar();
|
||||
ok(notif, "the infobar is visible");
|
||||
let ui = gBrowser.selectedBrowser.translationUI;
|
||||
|
||||
let principal = gBrowser.selectedBrowser.contentPrincipal;
|
||||
ok(
|
||||
ui.shouldShowInfoBar(principal, "fr"),
|
||||
actor.shouldShowInfoBar(principal, "fr"),
|
||||
"check shouldShowInfoBar initially returns true"
|
||||
);
|
||||
|
||||
|
@ -163,7 +172,7 @@ var gTests = [
|
|||
is(langs.length, 1, "one language in the exception list");
|
||||
is(langs[0], "fr", "correct language in the exception list");
|
||||
ok(
|
||||
!ui.shouldShowInfoBar(principal, "fr"),
|
||||
!actor.shouldShowInfoBar(principal, "fr"),
|
||||
"the infobar wouldn't be shown anymore"
|
||||
);
|
||||
|
||||
|
@ -187,17 +196,19 @@ var gTests = [
|
|||
desc: "never for site",
|
||||
run: async function checkNeverForSite() {
|
||||
// Show the infobar for example.com and fr.
|
||||
Translation.documentStateReceived(gBrowser.selectedBrowser, {
|
||||
let actor = gBrowser.selectedBrowser.browsingContext.currentWindowGlobal.getActor(
|
||||
"Translation"
|
||||
);
|
||||
actor.documentStateReceived({
|
||||
state: Translation.STATE_OFFER,
|
||||
originalShown: true,
|
||||
detectedLanguage: "fr",
|
||||
});
|
||||
let notif = await getInfoBar();
|
||||
ok(notif, "the infobar is visible");
|
||||
let ui = gBrowser.selectedBrowser.translationUI;
|
||||
let principal = gBrowser.selectedBrowser.contentPrincipal;
|
||||
ok(
|
||||
ui.shouldShowInfoBar(principal, "fr"),
|
||||
actor.shouldShowInfoBar(principal, "fr"),
|
||||
"check shouldShowInfoBar initially returns true"
|
||||
);
|
||||
|
||||
|
@ -228,7 +239,7 @@ var gTests = [
|
|||
"correct site in the exception list"
|
||||
);
|
||||
ok(
|
||||
!ui.shouldShowInfoBar(principal, "fr"),
|
||||
!actor.shouldShowInfoBar(principal, "fr"),
|
||||
"the infobar wouldn't be shown anymore"
|
||||
);
|
||||
|
||||
|
|
|
@ -5,8 +5,11 @@
|
|||
// tests the translation infobar, using a fake 'Translation' implementation.
|
||||
|
||||
var tmp = {};
|
||||
ChromeUtils.import("resource:///modules/translation/Translation.jsm", tmp);
|
||||
var { Translation } = tmp;
|
||||
ChromeUtils.import(
|
||||
"resource:///modules/translation/TranslationParent.jsm",
|
||||
tmp
|
||||
);
|
||||
var { Translation, TranslationParent } = tmp;
|
||||
|
||||
const kShowUIPref = "browser.translation.ui.show";
|
||||
|
||||
|
@ -35,47 +38,55 @@ function waitForCondition(condition, nextTest, errorMsg) {
|
|||
};
|
||||
}
|
||||
|
||||
var TranslationStub = {
|
||||
// Create a subclass that overrides the translation functions. This can be
|
||||
// instantiated separately from the normal actor creation process. This will
|
||||
// allow testing translations even when the browser.translation.detectLanguage
|
||||
// preference is disabled.
|
||||
class TranslationStub extends TranslationParent {
|
||||
constructor(browser) {
|
||||
super();
|
||||
this._browser = browser;
|
||||
this.actorCreated();
|
||||
}
|
||||
|
||||
get browser() {
|
||||
return this._browser;
|
||||
}
|
||||
|
||||
sendAsyncMessage(name, data) {}
|
||||
|
||||
translate(aFrom, aTo) {
|
||||
this.state = Translation.STATE_TRANSLATING;
|
||||
this.translatedFrom = aFrom;
|
||||
this.translatedTo = aTo;
|
||||
},
|
||||
}
|
||||
|
||||
_reset() {
|
||||
this.translatedFrom = "";
|
||||
this.translatedTo = "";
|
||||
},
|
||||
}
|
||||
|
||||
failTranslation() {
|
||||
this.state = Translation.STATE_ERROR;
|
||||
this._reset();
|
||||
},
|
||||
}
|
||||
|
||||
finishTranslation() {
|
||||
this.showTranslatedContent();
|
||||
this.state = Translation.STATE_TRANSLATED;
|
||||
this._reset();
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function showTranslationUI(aDetectedLanguage) {
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
Translation.documentStateReceived(browser, {
|
||||
let translation = new TranslationStub(browser);
|
||||
translation.documentStateReceived({
|
||||
state: Translation.STATE_OFFER,
|
||||
originalShown: true,
|
||||
detectedLanguage: aDetectedLanguage,
|
||||
});
|
||||
let ui = browser.translationUI;
|
||||
for (let name of [
|
||||
"translate",
|
||||
"_reset",
|
||||
"failTranslation",
|
||||
"finishTranslation",
|
||||
]) {
|
||||
ui[name] = TranslationStub[name];
|
||||
}
|
||||
return ui.notificationBox.getNotificationWithValue("translation");
|
||||
return translation.notificationBox.getNotificationWithValue("translation");
|
||||
}
|
||||
|
||||
function hasTranslationInfoBar() {
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
"use strict";
|
||||
|
||||
var tmp = {};
|
||||
ChromeUtils.import("resource:///modules/translation/Translation.jsm", tmp);
|
||||
ChromeUtils.import(
|
||||
"resource:///modules/translation/TranslationParent.jsm",
|
||||
tmp
|
||||
);
|
||||
var { Translation, TranslationTelemetry } = tmp;
|
||||
const Telemetry = Services.telemetry;
|
||||
|
||||
|
@ -108,9 +111,10 @@ var MetricsChecker = {
|
|||
};
|
||||
|
||||
function getInfobarElement(browser, anonid) {
|
||||
let notif = browser.translationUI.notificationBox.getNotificationWithValue(
|
||||
"translation"
|
||||
let actor = browser.browsingContext.currentWindowGlobal.getActor(
|
||||
"Translation"
|
||||
);
|
||||
let notif = actor.notificationBox.getNotificationWithValue("translation");
|
||||
return notif._getAnonElt(anonid);
|
||||
}
|
||||
|
||||
|
@ -120,9 +124,12 @@ var offerTranslationFor = async function(text, from) {
|
|||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, dataUrl);
|
||||
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
let actor = browser.browsingContext.currentWindowGlobal.getActor(
|
||||
"Translation"
|
||||
);
|
||||
|
||||
// Send a translation offer.
|
||||
Translation.documentStateReceived(browser, {
|
||||
actor.documentStateReceived({
|
||||
state: Translation.STATE_OFFER,
|
||||
originalShown: true,
|
||||
detectedLanguage: from,
|
||||
|
@ -133,8 +140,10 @@ var offerTranslationFor = async function(text, from) {
|
|||
|
||||
var acceptTranslationOffer = async function(tab) {
|
||||
let browser = tab.linkedBrowser;
|
||||
let translationPromise = waitForTranslationDone();
|
||||
|
||||
getInfobarElement(browser, "translate").doCommand();
|
||||
await waitForMessage(browser, "Translation:Finished");
|
||||
await translationPromise;
|
||||
};
|
||||
|
||||
var translate = async function(text, from, closeTab = true) {
|
||||
|
@ -147,10 +156,10 @@ var translate = async function(text, from, closeTab = true) {
|
|||
return tab;
|
||||
};
|
||||
|
||||
function waitForMessage({ messageManager }, name) {
|
||||
function waitForTranslationDone() {
|
||||
return new Promise(resolve => {
|
||||
messageManager.addMessageListener(name, function onMessage() {
|
||||
messageManager.removeMessageListener(name, onMessage);
|
||||
Translation.setListenerForTests(() => {
|
||||
Translation.setListenerForTests(null);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,7 +19,7 @@ const kApiKeyPref = "browser.translation.yandex.apiKeyOverride";
|
|||
const kShowUIPref = "browser.translation.ui.show";
|
||||
|
||||
const { Translation } = ChromeUtils.import(
|
||||
"resource:///modules/translation/Translation.jsm"
|
||||
"resource:///modules/translation/TranslationParent.jsm"
|
||||
);
|
||||
|
||||
add_task(async function setup() {
|
||||
|
@ -140,11 +140,14 @@ function promiseTestPageLoad(url) {
|
|||
|
||||
function showTranslationUI(tab, aDetectedLanguage) {
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
Translation.documentStateReceived(browser, {
|
||||
let actor = browser.browsingContext.currentWindowGlobal.getActor(
|
||||
"Translation"
|
||||
);
|
||||
actor.documentStateReceived({
|
||||
state: Translation.STATE_OFFER,
|
||||
originalShown: true,
|
||||
detectedLanguage: aDetectedLanguage,
|
||||
});
|
||||
let ui = browser.translationUI;
|
||||
return ui.notificationBox.getNotificationWithValue("translation");
|
||||
|
||||
return actor.notificationBox.getNotificationWithValue("translation");
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче