зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1248499: [webext] Implement tabs.detectLanguage. r=billm
MozReview-Commit-ID: F4GpSesj2ho --HG-- extra : rebase_source : e9ffab3396b4f813c60e366c371ea54baccc160a
This commit is contained in:
Родитель
89517813d0
Коммит
66f9b57852
|
@ -656,6 +656,19 @@ extensions.registerSchemaAPI("tabs", null, (extension, context) => {
|
|||
message, recipient);
|
||||
},
|
||||
|
||||
detectLanguage: function(tabId) {
|
||||
let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab;
|
||||
if (!tab) {
|
||||
return Promise.reject({message: `Invalid tab ID: ${tabId}`});
|
||||
}
|
||||
|
||||
let browser = tab.linkedBrowser;
|
||||
let recipient = {innerWindowID: browser.innerWindowID};
|
||||
|
||||
return context.sendMessage(browser.messageManager, "Extension:DetectLanguage",
|
||||
{}, recipient);
|
||||
},
|
||||
|
||||
_execute: function(tabId, details, kind, method) {
|
||||
let tab = tabId !== null ? TabManager.getTab(tabId) : TabManager.activeTab;
|
||||
let mm = tab.linkedBrowser.messageManager;
|
||||
|
|
|
@ -9,6 +9,8 @@ support-files =
|
|||
file_popup_api_injection_b.html
|
||||
file_iframe_document.html
|
||||
file_iframe_document.sjs
|
||||
file_language_fr_en.html
|
||||
file_language_ja.html
|
||||
|
||||
[browser_ext_simple.js]
|
||||
[browser_ext_currentWindow.js]
|
||||
|
@ -28,6 +30,7 @@ support-files =
|
|||
[browser_ext_runtime_setUninstallURL.js]
|
||||
[browser_ext_tabs_audio.js]
|
||||
[browser_ext_tabs_captureVisibleTab.js]
|
||||
[browser_ext_tabs_detectLanguage.js]
|
||||
[browser_ext_tabs_events.js]
|
||||
[browser_ext_tabs_executeScript.js]
|
||||
[browser_ext_tabs_executeScript_good.js]
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
add_task(function* testDetectLanguage() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["tabs"],
|
||||
},
|
||||
|
||||
background() {
|
||||
const BASE_PATH = "browser/browser/components/extensions/test/browser";
|
||||
|
||||
function loadTab(url) {
|
||||
let tabId;
|
||||
let awaitUpdated = new Promise(resolve => {
|
||||
browser.tabs.onUpdated.addListener(function onUpdated(changedTabId, changed, tab) {
|
||||
if (changedTabId === tabId && changed.url) {
|
||||
browser.tabs.onUpdated.removeListener(onUpdated);
|
||||
resolve(tab);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return browser.tabs.create({url}).then(tab => {
|
||||
tabId = tab.id;
|
||||
return awaitUpdated;
|
||||
});
|
||||
}
|
||||
|
||||
loadTab(`http://example.co.jp/${BASE_PATH}/file_language_ja.html`).then(tab => {
|
||||
return browser.tabs.detectLanguage(tab.id).then(lang => {
|
||||
browser.test.assertEq("ja", lang, "Japanese document should be detected as Japanese");
|
||||
return browser.tabs.remove(tab.id);
|
||||
});
|
||||
}).then(() => {
|
||||
return loadTab(`http://example.co.jp/${BASE_PATH}/file_language_fr_en.html`);
|
||||
}).then(tab => {
|
||||
return browser.tabs.detectLanguage(tab.id).then(lang => {
|
||||
browser.test.assertEq("fr", lang, "French/English document should be detected as primarily French");
|
||||
return browser.tabs.remove(tab.id);
|
||||
});
|
||||
}).then(() => {
|
||||
browser.test.notifyPass("detectLanguage");
|
||||
}).catch(e => {
|
||||
browser.test.fail(`Error: ${e} :: ${e.stack}`);
|
||||
browser.test.notifyFail("detectLanguage");
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
yield extension.awaitFinish("detectLanguage");
|
||||
|
||||
yield extension.unload();
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
France is the largest country in Western Europe and the third-largest in Europe as a whole.
|
||||
A accès aux chiens et aux frontaux qui lui ont été il peut consulter et modifier ses collections et exporter
|
||||
Cet article concerne le pays européen aujourd’hui appelé République française. Pour d’autres usages du nom France,
|
||||
Pour une aide rapide et effective, veuiller trouver votre aide dans le menu ci-dessus.
|
||||
Motoring events began soon after the construction of the first successful gasoline-fueled automobiles. The quick brown fox jumps over the lazy dog.
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
このペ ジでは アカウントに指定された予算の履歴を一覧にしています それぞれの項目には 予算額と特定期間のステ タスが表示されます 現在または今後の予算を設定するには
|
||||
</body>
|
||||
</html>
|
|
@ -25,6 +25,8 @@ Cu.import("resource://gre/modules/AppConstants.jsm");
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
|
||||
"resource://gre/modules/ExtensionManagement.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector",
|
||||
"resource:///modules/translation/LanguageDetector.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
|
||||
"resource://gre/modules/MatchPattern.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
|
@ -33,7 +35,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
|
|||
"resource://gre/modules/PromiseUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
|
||||
"resource://gre/modules/MessageChannel.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebNavigationFrames",
|
||||
"resource://gre/modules/WebNavigationFrames.jsm");
|
||||
|
||||
|
@ -47,6 +48,7 @@ var {
|
|||
injectAPI,
|
||||
flushJarCache,
|
||||
detectLanguage,
|
||||
promiseDocumentReady,
|
||||
} = ExtensionUtils;
|
||||
|
||||
function isWhenBeforeOrSame(when1, when2) {
|
||||
|
@ -732,6 +734,7 @@ class ExtensionGlobal {
|
|||
this.global = global;
|
||||
|
||||
MessageChannel.addListener(global, "Extension:Capture", this);
|
||||
MessageChannel.addListener(global, "Extension:DetectLanguage", this);
|
||||
MessageChannel.addListener(global, "Extension:Execute", this);
|
||||
MessageChannel.addListener(global, "WebNavigation:GetFrame", this);
|
||||
MessageChannel.addListener(global, "WebNavigation:GetAllFrames", this);
|
||||
|
@ -760,6 +763,8 @@ class ExtensionGlobal {
|
|||
switch (messageName) {
|
||||
case "Extension:Capture":
|
||||
return this.handleExtensionCapture(data.width, data.height, data.options);
|
||||
case "Extension:DetectLanguage":
|
||||
return this.handleDetectLanguage(target);
|
||||
case "Extension:Execute":
|
||||
return this.handleExtensionExecute(target, recipient.extensionId, data.options);
|
||||
case "WebNavigation:GetFrame":
|
||||
|
@ -790,6 +795,36 @@ class ExtensionGlobal {
|
|||
return canvas.toDataURL(`image/${options.format}`, options.quality / 100);
|
||||
}
|
||||
|
||||
handleDetectLanguage(target) {
|
||||
let doc = target.content.document;
|
||||
|
||||
return promiseDocumentReady(doc).then(() => {
|
||||
let elem = doc.documentElement;
|
||||
|
||||
let language = (elem.getAttribute("xml:lang") || elem.getAttribute("lang") ||
|
||||
doc.contentLanguage || null);
|
||||
|
||||
// We only want the last element of the TLD here.
|
||||
// Only country codes have any effect on the results, but other
|
||||
// values cause no harm.
|
||||
let tld = doc.location.hostname.match(/[a-z]*$/)[0];
|
||||
|
||||
// The CLD2 library used by the language detector is capable of
|
||||
// analyzing raw HTML. Unfortunately, that takes much more memory,
|
||||
// and since it's hosted by emscripten, and therefore can't shrink
|
||||
// its heap after it's grown, it has a performance cost.
|
||||
// So we send plain text instead.
|
||||
let encoder = Cc["@mozilla.org/layout/documentEncoder;1?type=text/plain"].createInstance(Ci.nsIDocumentEncoder);
|
||||
encoder.init(doc, "text/plain", encoder.SkipInvisibleContent);
|
||||
let text = encoder.encodeToStringWithMaxLength(60 * 1024);
|
||||
|
||||
let encoding = doc.characterSet;
|
||||
|
||||
return LanguageDetector.detectLanguage({language, tld, text, encoding})
|
||||
.then(result => result.language);
|
||||
});
|
||||
}
|
||||
|
||||
handleExtensionExecute(target, extensionId, options) {
|
||||
return DocumentManager.executeScript(target, extensionId, options).then(result => {
|
||||
try {
|
||||
|
|
|
@ -630,6 +630,28 @@ function injectAPI(source, dest) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Promise which resolves when the given document's DOM has
|
||||
* fully loaded.
|
||||
*
|
||||
* @param {Document} doc The document to await the load of.
|
||||
* @returns {Promise<Document>}
|
||||
*/
|
||||
function promiseDocumentReady(doc) {
|
||||
if (doc.readyState == "interactive" || doc.readyState == "complete") {
|
||||
return Promise.resolve(doc);
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
doc.addEventListener("DOMContentLoaded", function onReady(event) {
|
||||
if (event.target === event.currentTarget) {
|
||||
doc.removeEventListener("DOMContentLoaded", onReady, true);
|
||||
resolve(doc);
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Messaging primitives.
|
||||
*/
|
||||
|
@ -1011,6 +1033,7 @@ this.ExtensionUtils = {
|
|||
ignoreEvent,
|
||||
injectAPI,
|
||||
instanceOf,
|
||||
promiseDocumentReady,
|
||||
runSafe,
|
||||
runSafeSync,
|
||||
runSafeSyncWithoutClone,
|
||||
|
|
Загрузка…
Ссылка в новой задаче