diff options
70 files changed, 767 insertions, 3865 deletions
diff --git a/application/basilisk/app/Makefile.in b/application/basilisk/app/Makefile.in index 4a3b9758e..83d6cb36a 100644 --- a/application/basilisk/app/Makefile.in +++ b/application/basilisk/app/Makefile.in @@ -87,8 +87,10 @@ tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) $(dist_dest)/Contents/MacOS cp -RL $(DIST)/branding/firefox.icns $(dist_dest)/Contents/Resources/firefox.icns cp -RL $(DIST)/branding/document.icns $(dist_dest)/Contents/Resources/document.icns +ifdef MOZ_UPDATER $(MKDIR) -p $(dist_dest)/Contents/Library/LaunchServices mv -f $(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater $(dist_dest)/Contents/Library/LaunchServices ln -s ../../../../Library/LaunchServices/org.mozilla.updater $(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater +endif printf APPLMOZB > $(dist_dest)/Contents/PkgInfo endif diff --git a/application/basilisk/app/profile/basilisk.js b/application/basilisk/app/profile/basilisk.js index fd81e8204..eeec29eb9 100644 --- a/application/basilisk/app/profile/basilisk.js +++ b/application/basilisk/app/profile/basilisk.js @@ -1266,13 +1266,6 @@ pref("media.gmp-widevinecdm.enabled", true); // -1 means no experiment is run and we use the preferred value for frecency (6h) pref("browser.cache.frecency_experiment", 0); -pref("browser.translation.detectLanguage", false); -pref("browser.translation.neverForLanguages", ""); -// Show the translation UI bits, like the info bar, notification icon and preferences. -pref("browser.translation.ui.show", false); -// Allows to define the translation engine. Bing is default, Yandex may optionally switched on. -pref("browser.translation.engine", "bing"); - // Telemetry settings. // Determines if Telemetry pings can be archived locally. pref("toolkit.telemetry.archive.enabled", true); diff --git a/application/basilisk/base/content/browser.css b/application/basilisk/base/content/browser.css index e951985dc..517c1c5eb 100644 --- a/application/basilisk/base/content/browser.css +++ b/application/basilisk/base/content/browser.css @@ -933,11 +933,6 @@ html|*#gcli-output-frame, transition: none; } -/* Translation */ -notification[value="translation"] { - -moz-binding: url("chrome://browser/content/translation-infobar.xml#translationbar"); -} - /** See bug 872317 for why the following rule is necessary. */ #downloads-button { diff --git a/application/basilisk/base/content/browser.js b/application/basilisk/base/content/browser.js index 4f4ebb08f..d45956191 100644 --- a/application/basilisk/base/content/browser.js +++ b/application/basilisk/base/content/browser.js @@ -45,7 +45,6 @@ Cu.import("resource://gre/modules/NotificationDB.jsm"); ["SitePermissions", "resource:///modules/SitePermissions.jsm"], ["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"], ["Task", "resource://gre/modules/Task.jsm"], - ["Translation", "resource:///modules/translation/Translation.jsm"], ["UpdateUtils", "resource://gre/modules/UpdateUtils.jsm"], ["Weave", "resource://services-sync/main.js"], ["fxAccounts", "resource://gre/modules/FxAccounts.jsm"], @@ -969,7 +968,6 @@ var gBrowserInit = { // the listener is registered. DOMLinkHandler.init(); gPageStyleMenu.init(); - LanguageDetectionListener.init(); BrowserOnClick.init(); FeedHandler.init(); DevEdition.init(); @@ -5680,16 +5678,6 @@ function setStyleDisabled(disabled) { gPageStyleMenu.disableStyle(); } - -var LanguageDetectionListener = { - init: function() { - window.messageManager.addMessageListener("Translation:DocumentState", msg => { - Translation.documentStateReceived(msg.target, msg.data); - }); - } -}; - - var BrowserOffline = { _inited: false, diff --git a/application/basilisk/base/content/browser.xul b/application/basilisk/base/content/browser.xul index 74a90f5e0..3208538c1 100644 --- a/application/basilisk/base/content/browser.xul +++ b/application/basilisk/base/content/browser.xul @@ -675,10 +675,6 @@ tooltiptext="&urlbar.webRTCShareScreenNotificationAnchor.tooltip;"/> <image id="servicesInstall-notification-icon" class="notification-anchor-icon service-icon" role="button" tooltiptext="&urlbar.servicesNotificationAnchor.tooltip;"/> - <image id="translate-notification-icon" class="notification-anchor-icon translation-icon" role="button" - tooltiptext="&urlbar.translateNotificationAnchor.tooltip;"/> - <image id="translated-notification-icon" class="notification-anchor-icon translation-icon in-use" role="button" - tooltiptext="&urlbar.translatedNotificationAnchor.tooltip;"/> <image id="eme-notification-icon" class="notification-anchor-icon drm-icon" role="button" tooltiptext="&urlbar.emeNotificationAnchor.tooltip;"/> </box> diff --git a/application/basilisk/base/content/tab-content.js b/application/basilisk/base/content/tab-content.js index 11a9fabce..6d053dd2b 100644 --- a/application/basilisk/base/content/tab-content.js +++ b/application/basilisk/base/content/tab-content.js @@ -558,13 +558,6 @@ var PageStyleHandler = { }; PageStyleHandler.init(); -// 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")) { - Cu.import("resource:///modules/translation/TranslationContentHandler.jsm"); - trHandler = new TranslationContentHandler(global, docShell); -} - function gKeywordURIFixup(fixupInfo) { fixupInfo.QueryInterface(Ci.nsIURIFixupInfo); if (!fixupInfo.consumer) { diff --git a/application/basilisk/base/content/web-panels.xul b/application/basilisk/base/content/web-panels.xul index 223b20ed7..ed868c24a 100644 --- a/application/basilisk/base/content/web-panels.xul +++ b/application/basilisk/base/content/web-panels.xul @@ -44,6 +44,21 @@ disabled="true"/> <command id="Browser:Stop" oncommand="PanelBrowserStop();"/> <command id="Browser:Reload" oncommand="PanelBrowserReload();"/> + <command id="Browser:BackOrBackDuplicate" + oncommand="getPanelBrowser().webNavigation.goBack(event);" + disabled="true"> + <observes element="Browser:Back" attribute="disabled"/> + </command> + <command id="Browser:ForwardOrForwardDuplicate" + oncommand="getPanelBrowser().webNavigation.goForward(event);" + disabled="true"> + <observes element="Browser:Forward" attribute="disabled"/> + </command> + <command id="Browser:ReloadOrDuplicate" + oncommand="PanelBrowserReload(event)" + disabled="true"> + <observes element="Browser:Reload" attribute="disabled"/> + </command> </commandset> <popupset id="mainPopupSet"> diff --git a/application/basilisk/components/preferences/in-content/content.js b/application/basilisk/components/preferences/in-content/content.js index a957b1dd5..2eac10ca4 100644 --- a/application/basilisk/components/preferences/in-content/content.js +++ b/application/basilisk/components/preferences/in-content/content.js @@ -31,18 +31,6 @@ var gContentPane = { menulist.value = FontBuilder.readFontSelection(menulist); } - // Show translation preferences if we may: - const prefName = "browser.translation.ui.show"; - if (Services.prefs.getBoolPref(prefName)) { - let row = document.getElementById("translationBox"); - row.removeAttribute("hidden"); - // Showing attribution only for Bing Translator. - Components.utils.import("resource:///modules/translation/Translation.jsm"); - if (Translation.translationEngine == "bing") { - document.getElementById("bingAttribution").removeAttribute("hidden"); - } - } - if (AlertsServiceDND) { let notificationsDoNotDisturbRow = document.getElementById("notificationsDoNotDisturbRow"); @@ -66,10 +54,6 @@ var gContentPane = { gContentPane.configureColors); setEventListener("chooseLanguage", "command", gContentPane.showLanguages); - setEventListener("translationAttributionImage", "click", - gContentPane.openTranslationProviderAttribution); - setEventListener("translateButton", "command", - gContentPane.showTranslationExceptions); setEventListener("notificationsDoNotDisturb", "command", gContentPane.toggleDoNotDisturbNotifications); @@ -274,21 +258,6 @@ var gContentPane = { gSubDialog.open("chrome://browser/content/preferences/languages.xul"); }, - /** - * Displays the translation exceptions dialog where specific site and language - * translation preferences can be set. - */ - showTranslationExceptions: function () - { - gSubDialog.open("chrome://browser/content/preferences/translation.xul"); - }, - - openTranslationProviderAttribution: function () - { - Components.utils.import("resource:///modules/translation/Translation.jsm"); - Translation.openProviderAttribution(); - }, - toggleDoNotDisturbNotifications: function (event) { AlertsServiceDND.manualDoNotDisturb = event.target.checked; diff --git a/application/basilisk/components/preferences/in-content/content.xul b/application/basilisk/components/preferences/in-content/content.xul index 9434cba62..fac864411 100644 --- a/application/basilisk/components/preferences/in-content/content.xul +++ b/application/basilisk/components/preferences/in-content/content.xul @@ -22,11 +22,6 @@ <preference id="font.language.group" name="font.language.group" type="wstring"/> - - <!-- Languages --> - <preference id="browser.translation.detectLanguage" - name="browser.translation.detectLanguage" - type="bool"/> </preferences> <script type="application/javascript" @@ -191,23 +186,4 @@ label="&chooseButton.label;" accesskey="&chooseButton.accesskey;"/> </hbox> - - <hbox id="translationBox" hidden="true"> - <hbox align="center" flex="1"> - <checkbox id="translate" preference="browser.translation.detectLanguage" - label="&translateWebPages.label;." accesskey="&translateWebPages.accesskey;" - onsyncfrompreference="return gContentPane.updateButtons('translateButton', - 'browser.translation.detectLanguage');"/> - <hbox id="bingAttribution" hidden="true"> - <label>&translation.options.attribution.beforeLogo;</label> - <separator orient="vertical" class="thin"/> - <image id="translationAttributionImage" aria-label="Microsoft Translator" - src="chrome://browser/content/microsoft-translator-attribution.png"/> - <separator orient="vertical" class="thin"/> - <label>&translation.options.attribution.afterLogo;</label> - </hbox> - </hbox> - <button id="translateButton" label="&translateExceptions.label;" - accesskey="&translateExceptions.accesskey;"/> - </hbox> </groupbox> diff --git a/application/basilisk/components/preferences/jar.mn b/application/basilisk/components/preferences/jar.mn index d233c7865..5b24e89df 100644 --- a/application/basilisk/components/preferences/jar.mn +++ b/application/basilisk/components/preferences/jar.mn @@ -31,5 +31,3 @@ browser.jar: content/browser/preferences/sanitize.js content/browser/preferences/selectBookmark.xul content/browser/preferences/selectBookmark.js - content/browser/preferences/translation.xul - content/browser/preferences/translation.js diff --git a/application/basilisk/components/preferences/translation.js b/application/basilisk/components/preferences/translation.js deleted file mode 100644 index cd570db0e..000000000 --- a/application/basilisk/components/preferences/translation.js +++ /dev/null @@ -1,255 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */ -/* 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 {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyGetter(this, "gLangBundle", () => - Services.strings.createBundle("chrome://global/locale/languageNames.properties")); - -const kPermissionType = "translate"; -const kLanguagesPref = "browser.translation.neverForLanguages"; - -function Tree(aId, aData) -{ - this._data = aData; - this._tree = document.getElementById(aId); - this._tree.view = this; -} - -Tree.prototype = { - get boxObject() { - return this._tree.treeBoxObject; - }, - get isEmpty() { - return !this._data.length; - }, - get hasSelection() { - return this.selection.count > 0; - }, - getSelectedItems: function() { - let result = []; - - let rc = this.selection.getRangeCount(); - for (let i = 0; i < rc; ++i) { - let min = {}, max = {}; - this.selection.getRangeAt(i, min, max); - for (let j = min.value; j <= max.value; ++j) - result.push(this._data[j]); - } - - return result; - }, - - // nsITreeView implementation - get rowCount() { - return this._data.length; - }, - getCellText: function (aRow, aColumn) { - return this._data[aRow]; - }, - isSeparator: function(aIndex) { - return false; - }, - isSorted: function() { - return false; - }, - isContainer: function(aIndex) { - return false; - }, - setTree: function(aTree) {}, - getImageSrc: function(aRow, aColumn) {}, - getProgressMode: function(aRow, aColumn) {}, - getCellValue: function(aRow, aColumn) {}, - cycleHeader: function(column) {}, - getRowProperties: function(row) { - return ""; - }, - getColumnProperties: function(column) { - return ""; - }, - getCellProperties: function(row, column) { - return ""; - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsITreeView]) -}; - -function Lang(aCode) -{ - this.langCode = aCode; - this._label = gLangBundle.GetStringFromName(aCode); -} - -Lang.prototype = { - toString: function() { - return this._label; - } -} - -var gTranslationExceptions = { - onLoad: function() { - if (this._siteTree) { - // Re-using an open dialog, clear the old observers. - this.uninit(); - } - - // Load site permissions into an array. - this._sites = []; - let enumerator = Services.perms.enumerator; - while (enumerator.hasMoreElements()) { - let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission); - - if (perm.type == kPermissionType && - perm.capability == Services.perms.DENY_ACTION) { - this._sites.push(perm.principal.origin); - } - } - Services.obs.addObserver(this, "perm-changed", false); - this._sites.sort(); - - this._siteTree = new Tree("sitesTree", this._sites); - this.onSiteSelected(); - - this._langs = this.getLanguageExceptions(); - Services.prefs.addObserver(kLanguagesPref, this, false); - this._langTree = new Tree("languagesTree", this._langs); - this.onLanguageSelected(); - }, - - // Get the list of languages we don't translate as an array. - getLanguageExceptions: function() { - let langs = Services.prefs.getCharPref(kLanguagesPref); - if (!langs) - return []; - - let result = langs.split(",").map(code => new Lang(code)); - result.sort(); - - return result; - }, - - observe: function(aSubject, aTopic, aData) { - if (aTopic == "perm-changed") { - if (aData == "cleared") { - if (!this._sites.length) - return; - let removed = this._sites.splice(0, this._sites.length); - this._siteTree.boxObject.rowCountChanged(0, - removed.length); - } - else { - let perm = aSubject.QueryInterface(Ci.nsIPermission); - if (perm.type != kPermissionType) - return; - - if (aData == "added") { - if (perm.capability != Services.perms.DENY_ACTION) - return; - this._sites.push(perm.principal.origin); - this._sites.sort(); - let boxObject = this._siteTree.boxObject; - boxObject.rowCountChanged(0, 1); - boxObject.invalidate(); - } - else if (aData == "deleted") { - let index = this._sites.indexOf(perm.principal.origin); - if (index == -1) - return; - this._sites.splice(index, 1); - this._siteTree.boxObject.rowCountChanged(index, -1); - this.onSiteSelected(); - return; - } - } - this.onSiteSelected(); - } - else if (aTopic == "nsPref:changed") { - this._langs = this.getLanguageExceptions(); - let change = this._langs.length - this._langTree.rowCount; - this._langTree._data = this._langs; - let boxObject = this._langTree.boxObject; - if (change) - boxObject.rowCountChanged(0, change); - boxObject.invalidate(); - this.onLanguageSelected(); - } - }, - - _handleButtonDisabling: function(aTree, aIdPart) { - let empty = aTree.isEmpty; - document.getElementById("removeAll" + aIdPart + "s").disabled = empty; - document.getElementById("remove" + aIdPart).disabled = - empty || !aTree.hasSelection; - }, - - onLanguageSelected: function() { - this._handleButtonDisabling(this._langTree, "Language"); - }, - - onSiteSelected: function() { - this._handleButtonDisabling(this._siteTree, "Site"); - }, - - onLanguageDeleted: function() { - let langs = Services.prefs.getCharPref(kLanguagesPref); - if (!langs) - return; - - let removed = this._langTree.getSelectedItems().map(l => l.langCode); - - langs = langs.split(",").filter(l => removed.indexOf(l) == -1); - Services.prefs.setCharPref(kLanguagesPref, langs.join(",")); - }, - - onAllLanguagesDeleted: function() { - Services.prefs.setCharPref(kLanguagesPref, ""); - }, - - onSiteDeleted: function() { - let removedSites = this._siteTree.getSelectedItems(); - for (let origin of removedSites) { - let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin); - Services.perms.removeFromPrincipal(principal, kPermissionType); - } - }, - - onAllSitesDeleted: function() { - if (this._siteTree.isEmpty) - return; - - let removedSites = this._sites.splice(0, this._sites.length); - this._siteTree.boxObject.rowCountChanged(0, -removedSites.length); - - for (let origin of removedSites) { - let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin); - Services.perms.removeFromPrincipal(principal, kPermissionType); - } - - this.onSiteSelected(); - }, - - onSiteKeyPress: function(aEvent) { - if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE) - this.onSiteDeleted(); - }, - - onLanguageKeyPress: function(aEvent) { - if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE) - this.onLanguageDeleted(); - }, - - onWindowKeyPress: function(aEvent) { - if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE) - window.close(); - }, - - uninit: function() { - Services.obs.removeObserver(this, "perm-changed"); - Services.prefs.removeObserver(kLanguagesPref, this); - } -}; diff --git a/application/basilisk/components/preferences/translation.xul b/application/basilisk/components/preferences/translation.xul deleted file mode 100644 index b5dfd1b9b..000000000 --- a/application/basilisk/components/preferences/translation.xul +++ /dev/null @@ -1,88 +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/. --> - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> -<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?> - -<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/translation.dtd"> - -<window id="TranslationDialog" class="windowDialog" - windowtype="Browser:TranslationExceptions" - title="&window.title;" - xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - style="width: &window.width;;" - onload="gTranslationExceptions.onLoad();" - onunload="gTranslationExceptions.uninit();" - persist="screenX screenY width height" - onkeypress="gTranslationExceptions.onWindowKeyPress(event);"> - - <script src="chrome://browser/content/preferences/translation.js"/> - - <stringbundle id="bundlePreferences" - src="chrome://browser/locale/preferences/preferences.properties"/> - - <keyset> - <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/> - </keyset> - - <vbox class="largeDialogContainer"> - <vbox class="contentPane" flex="1"> - <label id="languagesLabel" control="permissionsTree">&noTranslationForLanguages.label;</label> - <separator class="thin"/> - <tree id="languagesTree" flex="1" style="height: 12em;" - hidecolumnpicker="true" - onkeypress="gTranslationExceptions.onLanguageKeyPress(event)" - onselect="gTranslationExceptions.onLanguageSelected();"> - <treecols> - <treecol id="languageCol" label="&treehead.languageName.label;" flex="1"/> - </treecols> - <treechildren/> - </tree> - </vbox> - <hbox align="end"> - <hbox class="actionButtons" flex="1"> - <button id="removeLanguage" disabled="true" - accesskey="&removeLanguage.accesskey;" - icon="remove" label="&removeLanguage.label;" - oncommand="gTranslationExceptions.onLanguageDeleted();"/> - <button id="removeAllLanguages" - icon="clear" label="&removeAllLanguages.label;" - accesskey="&removeAllLanguages.accesskey;" - oncommand="gTranslationExceptions.onAllLanguagesDeleted();"/> - <spacer flex="1"/> - </hbox> - </hbox> - <separator/> - <vbox class="contentPane" flex="1"> - <label id="languagesLabel" control="permissionsTree">&noTranslationForSites.label;</label> - <separator class="thin"/> - <tree id="sitesTree" flex="1" style="height: 12em;" - hidecolumnpicker="true" - onkeypress="gTranslationExceptions.onSiteKeyPress(event)" - onselect="gTranslationExceptions.onSiteSelected();"> - <treecols> - <treecol id="siteCol" label="&treehead.siteName.label;" flex="1"/> - </treecols> - <treechildren/> - </tree> - </vbox> - </vbox> - <hbox align="end"> - <hbox class="actionButtons" flex="1"> - <button id="removeSite" disabled="true" - accesskey="&removeSite.accesskey;" - icon="remove" label="&removeSite.label;" - oncommand="gTranslationExceptions.onSiteDeleted();"/> - <button id="removeAllSites" - icon="clear" label="&removeAllSites.label;" - accesskey="&removeAllSites.accesskey;" - oncommand="gTranslationExceptions.onAllSitesDeleted();"/> - <spacer flex="1"/> - <button oncommand="close();" icon="close" - label="&button.close.label;" accesskey="&button.close.accesskey;"/> - </hbox> - </hbox> -</window> diff --git a/application/basilisk/components/translation/BingTranslator.jsm b/application/basilisk/components/translation/BingTranslator.jsm deleted file mode 100644 index fc1cc942a..000000000 --- a/application/basilisk/components/translation/BingTranslator.jsm +++ /dev/null @@ -1,449 +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"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -this.EXPORTED_SYMBOLS = [ "BingTranslator" ]; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/Http.jsm"); - -// The maximum amount of net data allowed per request on Bing's API. -const MAX_REQUEST_DATA = 5000; // Documentation says 10000 but anywhere - // close to that is refused by the service. - -// The maximum number of chunks allowed to be translated in a single -// request. -const MAX_REQUEST_CHUNKS = 1000; // Documentation says 2000. - -// Self-imposed limit of 15 requests. This means that a page that would need -// to be broken in more than 15 requests won't be fully translated. -// The maximum amount of data that we will translate for a single page -// is MAX_REQUESTS * MAX_REQUEST_DATA. -const MAX_REQUESTS = 15; - -/** - * Translates a webpage using Bing's Translation API. - * - * @param translationDocument The TranslationDocument object that represents - * the webpage to be translated - * @param sourceLanguage The source language of the document - * @param targetLanguage The target language for the translation - * - * @returns {Promise} A promise that will resolve when the translation - * task is finished. - */ -this.BingTranslator = function(translationDocument, sourceLanguage, targetLanguage) { - this.translationDocument = translationDocument; - this.sourceLanguage = sourceLanguage; - this.targetLanguage = targetLanguage; - this._pendingRequests = 0; - this._partialSuccess = false; - this._serviceUnavailable = false; - this._translatedCharacterCount = 0; -}; - -this.BingTranslator.prototype = { - /** - * Performs the translation, splitting the document into several chunks - * respecting the data limits of the API. - * - * @returns {Promise} A promise that will resolve when the translation - * task is finished. - */ - translate: function() { - return Task.spawn(function *() { - let currentIndex = 0; - this._onFinishedDeferred = Promise.defer(); - - // Let's split the document into various requests to be sent to - // Bing's Translation API. - for (let requestCount = 0; requestCount < MAX_REQUESTS; requestCount++) { - // Generating the text for each request can be expensive, so - // let's take the opportunity of the chunkification process to - // allow for the event loop to attend other pending events - // before we continue. - yield CommonUtils.laterTickResolvingPromise(); - - // Determine the data for the next request. - let request = this._generateNextTranslationRequest(currentIndex); - - // Create a real request to the server, and put it on the - // pending requests list. - let bingRequest = new BingRequest(request.data, - this.sourceLanguage, - this.targetLanguage); - this._pendingRequests++; - bingRequest.fireRequest().then(this._chunkCompleted.bind(this), - this._chunkFailed.bind(this)); - - currentIndex = request.lastIndex; - if (request.finished) { - break; - } - } - - return this._onFinishedDeferred.promise; - }.bind(this)); - }, - - /** - * Resets the expiration time of the current token, in order to - * force the token manager to ask for a new token during the next request. - */ - _resetToken : function() { - // Force the token manager to get update token - BingTokenManager._currentExpiryTime = 0; - }, - - /** - * Function called when a request sent to the server completed successfully. - * This function handles calling the function to parse the result and the - * function to resolve the promise returned by the public `translate()` - * method when there's no pending request left. - * - * @param request The BingRequest sent to the server. - */ - _chunkCompleted: function(bingRequest) { - if (this._parseChunkResult(bingRequest)) { - this._partialSuccess = true; - // Count the number of characters successfully translated. - this._translatedCharacterCount += bingRequest.characterCount; - } - - this._checkIfFinished(); - }, - - /** - * Function called when a request sent to the server has failed. - * This function handles deciding if the error is transient or means the - * service is unavailable (zero balance on the key or request credentials are - * not in an active state) and calling the function to resolve the promise - * returned by the public `translate()` method when there's no pending. - * request left. - * - * @param aError [optional] The XHR object of the request that failed. - */ - _chunkFailed: function(aError) { - if (aError instanceof Ci.nsIXMLHttpRequest && - [400, 401].indexOf(aError.status) != -1) { - let body = aError.responseText; - if (body && body.includes("TranslateApiException") && - (body.includes("balance") || body.includes("active state"))) - this._serviceUnavailable = true; - } - - this._checkIfFinished(); - }, - - /** - * Function called when a request sent to the server has completed. - * This function handles resolving the promise - * returned by the public `translate()` method when all chunks are completed. - */ - _checkIfFinished: function() { - // Check if all pending requests have been - // completed and then resolves the promise. - // If at least one chunk was successful, the - // promise will be resolved positively which will - // display the "Success" state for the infobar. Otherwise, - // the "Error" state will appear. - if (--this._pendingRequests == 0) { - if (this._partialSuccess) { - this._onFinishedDeferred.resolve({ - characterCount: this._translatedCharacterCount - }); - } else { - let error = this._serviceUnavailable ? "unavailable" : "failure"; - this._onFinishedDeferred.reject(error); - } - } - }, - - /** - * This function parses the result returned by Bing's Http.svc API, - * which is a XML file that contains a number of elements. To our - * particular interest, the only part of the response that matters - * are the <TranslatedText> nodes, which contains the resulting - * items that were sent to be translated. - * - * @param request The request sent to the server. - * @returns boolean True if parsing of this chunk was successful. - */ - _parseChunkResult: function(bingRequest) { - let results; - try { - let doc = bingRequest.networkRequest.responseXML; - results = doc.querySelectorAll("TranslatedText"); - } catch (e) { - return false; - } - - let len = results.length; - if (len != bingRequest.translationData.length) { - // This should never happen, but if the service returns a different number - // of items (from the number of items submitted), we can't use this chunk - // because all items would be paired incorrectly. - return false; - } - - let error = false; - for (let i = 0; i < len; i++) { - try { - let result = results[i].firstChild.nodeValue; - let root = bingRequest.translationData[i][0]; - - if (root.isSimpleRoot) { - // Workaround for Bing's service problem in which "&" chars in - // plain-text TranslationItems are double-escaped. - result = result.replace(/&/g, "&"); - } - - root.parseResult(result); - } catch (e) { error = true; } - } - - return !error; - }, - - /** - * This function will determine what is the data to be used for - * the Nth request we are generating, based on the input params. - * - * @param startIndex What is the index, in the roots list, that the - * chunk should start. - */ - _generateNextTranslationRequest: function(startIndex) { - let currentDataSize = 0; - let currentChunks = 0; - let output = []; - let rootsList = this.translationDocument.roots; - - for (let i = startIndex; i < rootsList.length; i++) { - let root = rootsList[i]; - let text = this.translationDocument.generateTextForItem(root); - if (!text) { - continue; - } - - text = escapeXML(text); - let newCurSize = currentDataSize + text.length; - let newChunks = currentChunks + 1; - - if (newCurSize > MAX_REQUEST_DATA || - newChunks > MAX_REQUEST_CHUNKS) { - - // If we've reached the API limits, let's stop accumulating data - // for this request and return. We return information useful for - // the caller to pass back on the next call, so that the function - // can keep working from where it stopped. - return { - data: output, - finished: false, - lastIndex: i - }; - } - - currentDataSize = newCurSize; - currentChunks = newChunks; - output.push([root, text]); - } - - return { - data: output, - finished: true, - lastIndex: 0 - }; - } -}; - -/** - * Represents a request (for 1 chunk) sent off to Bing's service. - * - * @params translationData The data to be used for this translation, - * generated by the generateNextTranslationRequest... - * function. - * @param sourceLanguage The source language of the document. - * @param targetLanguage The target language for the translation. - * - */ -function BingRequest(translationData, sourceLanguage, targetLanguage) { - this.translationData = translationData; - this.sourceLanguage = sourceLanguage; - this.targetLanguage = targetLanguage; - this.characterCount = 0; -} - -BingRequest.prototype = { - /** - * Initiates the request - */ - fireRequest: function() { - return Task.spawn(function *() { - // Prepare authentication. - let token = yield BingTokenManager.getToken(); - let auth = "Bearer " + token; - - // Prepare URL. - let url = getUrlParam("https://api.microsofttranslator.com/v2/Http.svc/TranslateArray", - "browser.translation.bing.translateArrayURL"); - - // Prepare request headers. - let headers = [["Content-type", "text/xml"], ["Authorization", auth]]; - - // Prepare the request body. - let requestString = - '<TranslateArrayRequest>' + - '<AppId/>' + - '<From>' + this.sourceLanguage + '</From>' + - '<Options>' + - '<ContentType xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2">text/html</ContentType>' + - '<ReservedFlags xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" />' + - '</Options>' + - '<Texts xmlns:s="http://schemas.microsoft.com/2003/10/Serialization/Arrays">'; - - for (let [, text] of this.translationData) { - requestString += '<s:string>' + text + '</s:string>'; - this.characterCount += text.length; - } - - requestString += '</Texts>' + - '<To>' + this.targetLanguage + '</To>' + - '</TranslateArrayRequest>'; - - // Set up request options. - let deferred = Promise.defer(); - let options = { - onLoad: (function(responseText, xhr) { - deferred.resolve(this); - }).bind(this), - onError: function(e, responseText, xhr) { - deferred.reject(xhr); - }, - postData: requestString, - headers: headers - }; - - // Fire the request. - let request = httpRequest(url, options); - - // Override the response MIME type. - request.overrideMimeType("text/xml"); - this.networkRequest = request; - return deferred.promise; - }.bind(this)); - } -}; - -/** - * Authentication Token manager for the API - */ -var BingTokenManager = { - _currentToken: null, - _currentExpiryTime: 0, - _pendingRequest: null, - - /** - * Get a valid, non-expired token to be used for the API calls. - * - * @returns {Promise} A promise that resolves with the token - * string once it is obtained. The token returned - * can be the same one used in the past if it is still - * valid. - */ - getToken: function() { - if (this._pendingRequest) { - return this._pendingRequest; - } - - let remainingMs = this._currentExpiryTime - new Date(); - // Our existing token is still good for more than a minute, let's use it. - if (remainingMs > 60 * 1000) { - return Promise.resolve(this._currentToken); - } - - return this._getNewToken(); - }, - - /** - * Generates a new token from the server. - * - * @returns {Promise} A promise that resolves with the token - * string once it is obtained. - */ - _getNewToken: function() { - let url = getUrlParam("https://datamarket.accesscontrol.windows.net/v2/OAuth2-13", - "browser.translation.bing.authURL"); - let params = [ - ["grant_type", "client_credentials"], - ["scope", "http://api.microsofttranslator.com"], - ["client_id", - getUrlParam("%BING_API_CLIENTID%", "browser.translation.bing.clientIdOverride")], - ["client_secret", - getUrlParam("%BING_API_KEY%", "browser.translation.bing.apiKeyOverride")] - ]; - - let deferred = Promise.defer(); - let options = { - onLoad: function(responseText, xhr) { - BingTokenManager._pendingRequest = null; - try { - let json = JSON.parse(responseText); - - if (json.error) { - deferred.reject(json.error); - return; - } - - let token = json.access_token; - let expires_in = json.expires_in; - BingTokenManager._currentToken = token; - BingTokenManager._currentExpiryTime = new Date(Date.now() + expires_in * 1000); - deferred.resolve(token); - } catch (e) { - deferred.reject(e); - } - }, - onError: function(e, responseText, xhr) { - BingTokenManager._pendingRequest = null; - deferred.reject(e); - }, - postData: params - }; - - this._pendingRequest = deferred.promise; - httpRequest(url, options); - - return deferred.promise; - } -}; - -/** - * Escape a string to be valid XML content. - */ -function escapeXML(aStr) { - return aStr.toString() - .replace(/&/g, "&") - .replace(/\"/g, """) - .replace(/\'/g, "'") - .replace(/</g, "<") - .replace(/>/g, ">"); -} - -/** - * Fetch an auth token (clientID or client secret), which may be overridden by - * a pref if it's set. - */ -function getUrlParam(paramValue, prefName) { - if (Services.prefs.getPrefType(prefName)) - paramValue = Services.prefs.getCharPref(prefName); - paramValue = Services.urlFormatter.formatURL(paramValue); - return paramValue; -} diff --git a/application/basilisk/components/translation/Translation.jsm b/application/basilisk/components/translation/Translation.jsm deleted file mode 100644 index 15a847c13..000000000 --- a/application/basilisk/components/translation/Translation.jsm +++ /dev/null @@ -1,446 +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"; - -this.EXPORTED_SYMBOLS = [ - "Translation", - "TranslationTelemetry", -]; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -const TRANSLATION_PREF_SHOWUI = "browser.translation.ui.show"; -const TRANSLATION_PREF_DETECT_LANG = "browser.translation.detectLanguage"; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Task.jsm", this); - -this.Translation = { - STATE_OFFER: 0, - STATE_TRANSLATING: 1, - STATE_TRANSLATED: 2, - STATE_ERROR: 3, - STATE_UNAVAILABLE: 4, - - serviceUnavailable: false, - - supportedSourceLanguages: ["bg", "cs", "de", "en", "es", "fr", "ja", "ko", "nl", "no", "pl", "pt", "ru", "tr", "vi", "zh"], - supportedTargetLanguages: ["bg", "cs", "de", "en", "es", "fr", "ja", "ko", "nl", "no", "pl", "pt", "ru", "tr", "vi", "zh"], - - _defaultTargetLanguage: "", - get defaultTargetLanguage() { - if (!this._defaultTargetLanguage) { - this._defaultTargetLanguage = Cc["@mozilla.org/chrome/chrome-registry;1"] - .getService(Ci.nsIXULChromeRegistry) - .getSelectedLocale("global") - .split("-")[0]; - } - return this._defaultTargetLanguage; - }, - - documentStateReceived: function(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.indexOf(aData.detectedLanguage) == -1) { - // 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.currentURI)) - trUI.showTranslationInfoBar(); - }, - - openProviderAttribution: function() { - let attribution = this.supportedEngines[this.translationEngine]; - Cu.import("resource:///modules/RecentWindow.jsm"); - RecentWindow.getMostRecentBrowserWindow().openUILinkIn(attribution, "tab"); - }, - - /** - * The list of translation engines and their attributions. - */ - supportedEngines: { - "bing" : "http://aka.ms/MicrosoftTranslatorAttribution", - "yandex" : "http://translate.yandex.com/" - }, - - /** - * Fallback engine (currently Bing Translator) if the preferences seem - * confusing. - */ - get defaultEngine() { - return this.supportedEngines.keys[0]; - }, - - /** - * Returns the name of the preferred translation engine. - */ - get translationEngine() { - let engine = Services.prefs.getCharPref("browser.translation.engine"); - return Object.keys(this.supportedEngines).indexOf(engine) == -1 ? this.defaultEngine : engine; - }, -}; - -/* 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: - * - 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. - * - translatedTo, if already translated, target language code. - * - translate, method starting the translation of the current page. - * - showOriginalContent, method showing the original page content. - * - showTranslatedContent, method showing the translation for an - * already translated page whose original content is shown. - * - originalShown, boolean indicating if the original or translated - * version of the page is shown. - */ -function TranslationUI(aBrowser) { - this.browser = aBrowser; -} - -TranslationUI.prototype = { - get browser() { - return this._browser; - }, - set browser(aBrowser) { - if (this._browser) - this._browser.messageManager.removeMessageListener("Translation:Finished", this); - aBrowser.messageManager.addMessageListener("Translation:Finished", this); - this._browser = aBrowser; - }, - translate: function(aFrom, aTo) { - if (aFrom == aTo || - (this.state == Translation.STATE_TRANSLATED && - this.translatedFrom == aFrom && this.translatedTo == aTo)) { - // Nothing to do. - return; - } - - if (this.state == Translation.STATE_OFFER) { - if (this.detectedLanguage != aFrom) - TranslationTelemetry.recordDetectedLanguageChange(true); - } else { - if (this.translatedFrom != aFrom) - TranslationTelemetry.recordDetectedLanguageChange(false); - if (this.translatedTo != aTo) - TranslationTelemetry.recordTargetLanguageChange(); - } - - this.state = Translation.STATE_TRANSLATING; - this.translatedFrom = aFrom; - this.translatedTo = aTo; - - this.browser.messageManager.sendAsyncMessage( - "Translation:TranslateDocument", - { from: aFrom, to: aTo } - ); - }, - - showURLBarIcon: function() { - let chromeWin = this.browser.ownerGlobal; - let PopupNotifications = chromeWin.PopupNotifications; - let removeId = this.originalShown ? "translated" : "translate"; - let notification = - PopupNotifications.getNotification(removeId, this.browser); - if (notification) - PopupNotifications.remove(notification); - - let callback = (aTopic, aNewBrowser) => { - if (aTopic == "swapping") { - let infoBarVisible = - this.notificationBox.getNotificationWithValue("translation"); - aNewBrowser.translationUI = this; - this.browser = aNewBrowser; - if (infoBarVisible) - this.showTranslationInfoBar(); - return true; - } - - if (aTopic != "showing") - return false; - let notification = this.notificationBox.getNotificationWithValue("translation"); - if (notification) - notification.close(); - else - this.showTranslationInfoBar(); - return true; - }; - - let addId = this.originalShown ? "translate" : "translated"; - PopupNotifications.show(this.browser, addId, null, - addId + "-notification-icon", null, 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: function() { - this.originalShown = true; - this.showURLBarIcon(); - this.browser.messageManager.sendAsyncMessage("Translation:ShowOriginal"); - TranslationTelemetry.recordShowOriginalContent(); - }, - - showTranslatedContent: function() { - this.originalShown = false; - this.showURLBarIcon(); - this.browser.messageManager.sendAsyncMessage("Translation:ShowTranslation"); - }, - - get notificationBox() { - return this.browser.ownerGlobal.gBrowser.getNotificationBox(this.browser); - }, - - showTranslationInfoBar: function() { - let notificationBox = this.notificationBox; - let notif = notificationBox.appendNotification("", "translation", null, - notificationBox.PRIORITY_INFO_HIGH); - notif.init(this); - return notif; - }, - - shouldShowInfoBar: function(aURI) { - // Never show the infobar automatically while the translation - // service is temporarily unavailable. - if (Translation.serviceUnavailable) - return false; - - // Check if we should never show the infobar for this language. - let neverForLangs = - Services.prefs.getCharPref("browser.translation.neverForLanguages"); - if (neverForLangs.split(",").indexOf(this.detectedLanguage) != -1) { - TranslationTelemetry.recordAutoRejectedTranslationOffer(); - return false; - } - - // or if we should never show the infobar for this domain. - let perms = Services.perms; - if (perms.testExactPermission(aURI, "translate") == perms.DENY_ACTION) { - TranslationTelemetry.recordAutoRejectedTranslationOffer(); - return false; - } - - return true; - }, - - receiveMessage: function(msg) { - switch (msg.name) { - case "Translation:Finished": - if (msg.data.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; - } - }, - - infobarClosed: function() { - if (this.state == Translation.STATE_OFFER) - TranslationTelemetry.recordDeniedTranslationOffer(); - } -}; - -/** - * Uses telemetry histograms for collecting statistics on the usage of the - * translation component. - * - * NOTE: Metrics are only recorded if the user enabled the telemetry option. - */ -this.TranslationTelemetry = { - - init: function () { - // Constructing histograms. - const plain = (id) => Services.telemetry.getHistogramById(id); - const keyed = (id) => Services.telemetry.getKeyedHistogramById(id); - this.HISTOGRAMS = { - OPPORTUNITIES : () => plain("TRANSLATION_OPPORTUNITIES"), - OPPORTUNITIES_BY_LANG : () => keyed("TRANSLATION_OPPORTUNITIES_BY_LANGUAGE"), - PAGES : () => plain("TRANSLATED_PAGES"), - PAGES_BY_LANG : () => keyed("TRANSLATED_PAGES_BY_LANGUAGE"), - CHARACTERS : () => plain("TRANSLATED_CHARACTERS"), - DENIED : () => plain("DENIED_TRANSLATION_OFFERS"), - AUTO_REJECTED : () => plain("AUTO_REJECTED_TRANSLATION_OFFERS"), - SHOW_ORIGINAL : () => plain("REQUESTS_OF_ORIGINAL_CONTENT"), - TARGET_CHANGES : () => plain("CHANGES_OF_TARGET_LANGUAGE"), - DETECTION_CHANGES : () => plain("CHANGES_OF_DETECTED_LANGUAGE"), - SHOW_UI : () => plain("SHOULD_TRANSLATION_UI_APPEAR"), - DETECT_LANG : () => plain("SHOULD_AUTO_DETECT_LANGUAGE"), - }; - - // Capturing the values of flags at the startup. - this.recordPreferences(); - }, - - /** - * Record a translation opportunity in the health report. - * @param language - * The language of the page. - */ - recordTranslationOpportunity: function (language) { - return this._recordOpportunity(language, true); - }, - - /** - * Record a missed translation opportunity in the health report. - * A missed opportunity is when the language detected is not part - * of the supported languages. - * @param language - * The language of the page. - */ - recordMissedTranslationOpportunity: function (language) { - return this._recordOpportunity(language, false); - }, - - /** - * Record an automatically rejected translation offer in the health - * report. A translation offer is automatically rejected when a user - * has previously clicked "Never translate this language" or "Never - * translate this site", which results in the infobar not being shown for - * the translation opportunity. - * - * These translation opportunities should still be recorded in addition to - * recording the automatic rejection of the offer. - */ - recordAutoRejectedTranslationOffer: function () { - if (!this._canRecord) return; - this.HISTOGRAMS.AUTO_REJECTED().add(); - }, - - /** - * Record a translation in the health report. - * @param langFrom - * The language of the page. - * @param langTo - * The language translated to - * @param numCharacters - * The number of characters that were translated - */ - recordTranslation: function (langFrom, langTo, numCharacters) { - if (!this._canRecord) return; - this.HISTOGRAMS.PAGES().add(); - this.HISTOGRAMS.PAGES_BY_LANG().add(langFrom + " -> " + langTo); - this.HISTOGRAMS.CHARACTERS().add(numCharacters); - }, - - /** - * Record a change of the detected language in the health report. This should - * only be called when actually executing a translation, not every time the - * user changes in the language in the UI. - * - * @param beforeFirstTranslation - * A boolean indicating if we are recording a change of detected - * language before translating the page for the first time. If we - * have already translated the page from the detected language and - * the user has manually adjusted the detected language false should - * be passed. - */ - recordDetectedLanguageChange: function (beforeFirstTranslation) { - if (!this._canRecord) return; - this.HISTOGRAMS.DETECTION_CHANGES().add(beforeFirstTranslation); - }, - - /** - * Record a change of the target language in the health report. This should - * only be called when actually executing a translation, not every time the - * user changes in the language in the UI. - */ - recordTargetLanguageChange: function () { - if (!this._canRecord) return; - this.HISTOGRAMS.TARGET_CHANGES().add(); - }, - - /** - * Record a denied translation offer. - */ - recordDeniedTranslationOffer: function () { - if (!this._canRecord) return; - this.HISTOGRAMS.DENIED().add(); - }, - - /** - * Record a "Show Original" command use. - */ - recordShowOriginalContent: function () { - if (!this._canRecord) return; - this.HISTOGRAMS.SHOW_ORIGINAL().add(); - }, - - /** - * Record the state of translation preferences. - */ - recordPreferences: function () { - if (!this._canRecord) return; - if (Services.prefs.getBoolPref(TRANSLATION_PREF_SHOWUI)) { - this.HISTOGRAMS.SHOW_UI().add(1); - } - if (Services.prefs.getBoolPref(TRANSLATION_PREF_DETECT_LANG)) { - this.HISTOGRAMS.DETECT_LANG().add(1); - } - }, - - _recordOpportunity: function(language, success) { - if (!this._canRecord) return; - this.HISTOGRAMS.OPPORTUNITIES().add(success); - this.HISTOGRAMS.OPPORTUNITIES_BY_LANG().add(language, success); - }, - - /** - * A shortcut for reading the telemetry preference. - * - */ - _canRecord: function () { - return Services.prefs.getBoolPref("toolkit.telemetry.enabled"); - } -}; - -this.TranslationTelemetry.init(); diff --git a/application/basilisk/components/translation/TranslationContentHandler.jsm b/application/basilisk/components/translation/TranslationContentHandler.jsm deleted file mode 100644 index 3b0d59ddd..000000000 --- a/application/basilisk/components/translation/TranslationContentHandler.jsm +++ /dev/null @@ -1,181 +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"; - -this.EXPORTED_SYMBOLS = [ "TranslationContentHandler" ]; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector", - "resource:///modules/translation/LanguageDetector.jsm"); - -const STATE_OFFER = 0; -const STATE_TRANSLATED = 2; -const STATE_ERROR = 3; - -this.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: function(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: function(aWebProgress, aRequest, aStateFlags, aStatus) { - if (!aWebProgress.isTopLevel || - !(aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) || - !this.global.content) - return; - - let url = aRequest.name; - if (!url.startsWith("http://") && !url.startsWith("https://")) - return; - - let content = this.global.content; - if (content.detectedLanguage) - return; - - // Grab a 60k sample of text from the page. - let encoder = Cc["@mozilla.org/layout/documentEncoder;1?type=text/plain"] - .createInstance(Ci.nsIDocumentEncoder); - 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); - }); - }, - - // Unused methods. - onProgressChange: function() {}, - onLocationChange: function() {}, - onStatusChange: function() {}, - onSecurityChange: function() {}, - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, - Ci.nsISupportsWeakReference]), - - receiveMessage: function(msg) { - switch (msg.name) { - case "Translation:TranslateDocument": - { - Cu.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 preferredEngine = Services.prefs.getCharPref("browser.translation.engine"); - let translator = null; - if (preferredEngine == "yandex") { - Cu.import("resource:///modules/translation/YandexTranslator.jsm"); - translator = new YandexTranslator(translationDocument, - msg.data.from, - msg.data.to); - } else { - Cu.import("resource:///modules/translation/BingTranslator.jsm"); - translator = new BingTranslator(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; - } - } -}; diff --git a/application/basilisk/components/translation/TranslationDocument.jsm b/application/basilisk/components/translation/TranslationDocument.jsm deleted file mode 100644 index 058d07a49..000000000 --- a/application/basilisk/components/translation/TranslationDocument.jsm +++ /dev/null @@ -1,683 +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"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -this.EXPORTED_SYMBOLS = [ "TranslationDocument" ]; - -const SHOW_ELEMENT = Ci.nsIDOMNodeFilter.SHOW_ELEMENT; -const SHOW_TEXT = Ci.nsIDOMNodeFilter.SHOW_TEXT; -const TEXT_NODE = Ci.nsIDOMNode.TEXT_NODE; - -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/Task.jsm"); - -/** - * This class represents a document that is being translated, - * and it is responsible for parsing the document, - * generating the data structures translation (the list of - * translation items and roots), and managing the original - * and translated texts on the translation items. - * - * @param document The document to be translated - */ -this.TranslationDocument = function(document) { - this.itemsMap = new Map(); - this.roots = []; - this._init(document); -}; - -this.TranslationDocument.prototype = { - translatedFrom: "", - translatedTo: "", - translationError: false, - originalShown: true, - - /** - * Initializes the object and populates - * the roots lists. - * - * @param document The document to be translated - */ - _init: function(document) { - let window = document.defaultView; - let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - - // Get all the translation nodes in the document's body: - // a translation node is a node from the document which - // contains useful content for translation, and therefore - // must be included in the translation process. - let nodeList = winUtils.getTranslationNodes(document.body); - - let length = nodeList.length; - - for (let i = 0; i < length; i++) { - let node = nodeList.item(i); - let isRoot = nodeList.isTranslationRootAtIndex(i); - - // Create a TranslationItem object for this node. - // This function will also add it to the this.roots array. - this._createItemForNode(node, i, isRoot); - } - - // At first all roots are stored in the roots list, and only after - // the process has finished we're able to determine which roots are - // simple, and which ones are not. - - // A simple root is defined by a root with no children items, which - // basically represents an element from a page with only text content - // inside. - - // This distinction is useful for optimization purposes: we treat a - // simple root as plain-text in the translation process and with that - // we are able to reduce their data payload sent to the translation service. - - for (let root of this.roots) { - if (root.children.length == 0 && - root.nodeRef.childElementCount == 0) { - root.isSimpleRoot = true; - } - } - }, - - /** - * Creates a TranslationItem object, which should be called - * for each node returned by getTranslationNodes. - * - * @param node The DOM node for this item. - * @param id A unique, numeric id for this item. - * @parem isRoot A boolean saying whether this item is a root. - * - * @returns A TranslationItem object. - */ - _createItemForNode: function(node, id, isRoot) { - if (this.itemsMap.has(node)) { - return this.itemsMap.get(node); - } - - let item = new TranslationItem(node, id, isRoot); - - if (isRoot) { - // Root items do not have a parent item. - this.roots.push(item); - } else { - let parentItem = this.itemsMap.get(node.parentNode); - if (parentItem) { - parentItem.children.push(item); - } - } - - this.itemsMap.set(node, item); - return item; - }, - - /** - * Generate the text string that represents a TranslationItem object. - * Besides generating the string, it's also stored in the "original" - * field of the TranslationItem object, which needs to be stored for - * later to be used in the "Show Original" functionality. - * If this function had already been called for the given item (determined - * by the presence of the "original" array in the item), the text will - * be regenerated from the "original" data instead of from the related - * DOM nodes (because the nodes might contain translated data). - * - * @param item A TranslationItem object - * - * @returns A string representation of the TranslationItem. - */ - generateTextForItem: function(item) { - if (item.original) { - return regenerateTextFromOriginalHelper(item); - } - - if (item.isSimpleRoot) { - let text = item.nodeRef.firstChild.nodeValue.trim(); - item.original = [text]; - return text; - } - - let str = ""; - item.original = []; - let wasLastItemPlaceholder = false; - - for (let child of item.nodeRef.childNodes) { - if (child.nodeType == TEXT_NODE) { - let x = child.nodeValue.trim(); - if (x != "") { - item.original.push(x); - str += x; - wasLastItemPlaceholder = false; - } - continue; - } - - let objInMap = this.itemsMap.get(child); - if (objInMap && !objInMap.isRoot) { - // If this childNode is present in the itemsMap, it means - // it's a translation node: it has useful content for translation. - // In this case, we need to stringify this node. - // However, if this item is a root, we should skip it here in this - // object's child list (and just add a placeholder for it), because - // it will be stringfied separately for being a root. - item.original.push(objInMap); - str += this.generateTextForItem(objInMap); - wasLastItemPlaceholder = false; - } else if (!wasLastItemPlaceholder) { - // Otherwise, if this node doesn't contain any useful content, - // or if it is a root itself, we can replace it with a placeholder node. - // We can't simply eliminate this node from our string representation - // because that could change the HTML structure (e.g., it would - // probably merge two separate text nodes). - // It's not necessary to add more than one placeholder in sequence; - // we can optimize them away. - item.original.push(TranslationItem_NodePlaceholder); - str += '<br>'; - wasLastItemPlaceholder = true; - } - } - - return generateTranslationHtmlForItem(item, str); - }, - - /** - * Changes the document to display its translated - * content. - */ - showTranslation: function() { - this.originalShown = false; - this._swapDocumentContent("translation"); - }, - - /** - * Changes the document to display its original - * content. - */ - showOriginal: function() { - this.originalShown = true; - this._swapDocumentContent("original"); - }, - - /** - * Swap the document with the resulting translation, - * or back with the original content. - * - * @param target A string that is either "translation" - * or "original". - */ - _swapDocumentContent: function(target) { - Task.spawn(function *() { - // Let the event loop breath on every 100 nodes - // that are replaced. - const YIELD_INTERVAL = 100; - let count = YIELD_INTERVAL; - - for (let root of this.roots) { - root.swapText(target); - if (count-- == 0) { - count = YIELD_INTERVAL; - yield CommonUtils.laterTickResolvingPromise(); - } - } - }.bind(this)); - } -}; - -/** - * This class represents an item for translation. It's basically our - * wrapper class around a node returned by getTranslationNode, with - * more data and structural information on it. - * - * At the end of the translation process, besides the properties below, - * a TranslationItem will contain two other properties: one called "original" - * and one called "translation". They are twin objects, one which reflect - * the structure of that node in its original state, and the other in its - * translated state. - * - * The "original" array is generated in the generateTextForItem function, - * and the "translation" array is generated when the translation results - * are parsed. - * - * They are both arrays, which contain a mix of strings and references to - * child TranslationItems. The references in both arrays point to the * same * - * TranslationItem object, but they might appear in different orders between the - * "original" and "translation" arrays. - * - * An example: - * - * English: <div id="n1">Welcome to <b id="n2">Mozilla's</b> website</div> - * Portuguese: <div id="n1">Bem vindo a pagina <b id="n2">da Mozilla</b></div> - * - * TranslationItem n1 = { - * id: 1, - * original: ["Welcome to", ptr to n2, "website"] - * translation: ["Bem vindo a pagina", ptr to n2] - * } - * - * TranslationItem n2 = { - * id: 2, - * original: ["Mozilla's"], - * translation: ["da Mozilla"] - * } - */ -function TranslationItem(node, id, isRoot) { - this.nodeRef = node; - this.id = id; - this.isRoot = isRoot; - this.children = []; -} - -TranslationItem.prototype = { - isRoot: false, - isSimpleRoot: false, - - toString: function() { - let rootType = ""; - if (this.isRoot) { - if (this.isSimpleRoot) { - rootType = " (simple root)"; - } - else { - rootType = " (non simple root)"; - } - } - return "[object TranslationItem: <" + this.nodeRef.localName + ">" - + rootType + "]"; - }, - - /** - * This function will parse the result of the translation of one translation - * item. If this item was a simple root, all we sent was a plain-text version - * of it, so the result is also straightforward text. - * - * For non-simple roots, we sent a simplified HTML representation of that - * node, and we'll first parse that into an HTML doc and then call the - * parseResultNode helper function to parse it. - * - * While parsing, the result is stored in the "translation" field of the - * TranslationItem, which will be used to display the final translation when - * all items are finished. It remains stored too to allow back-and-forth - * switching between the "Show Original" and "Show Translation" functions. - * - * @param result A string with the textual result received from the server, - * which can be plain-text or a serialized HTML doc. - */ - parseResult: function(result) { - if (this.isSimpleRoot) { - this.translation = [result]; - return; - } - - let domParser = Cc["@mozilla.org/xmlextras/domparser;1"] - .createInstance(Ci.nsIDOMParser); - - let doc = domParser.parseFromString(result, "text/html"); - parseResultNode(this, doc.body.firstChild); - }, - - /** - * This function finds a child TranslationItem - * with the given id. - * @param id The id to look for, in the format "n#" - * @returns A TranslationItem with the given id, or null if - * it was not found. - */ - getChildById: function(id) { - for (let child of this.children) { - if (("n" + child.id) == id) { - return child; - } - } - return null; - }, - - /** - * Swap the text of this TranslationItem between - * its original and translated states. - * - * @param target A string that is either "translation" - * or "original". - */ - swapText: function(target) { - swapTextForItem(this, target); - } -}; - -/** - * This object represents a placeholder item for translation. It's similar to - * the TranslationItem class, but it represents nodes that have no meaningful - * content for translation. These nodes will be replaced by "<br>" in a - * translation request. It's necessary to keep them to use it as a mark - * for correct positioning and spliting of text nodes. - */ -const TranslationItem_NodePlaceholder = { - toString: function() { - return "[object TranslationItem_NodePlaceholder]"; - } -}; - -/** - * Generate the outer HTML representation for a given item. - * - * @param item A TranslationItem object. - * param content The inner content for this item. - * @returns string The outer HTML needed for translation - * of this item. - */ -function generateTranslationHtmlForItem(item, content) { - let localName = item.isRoot ? "div" : "b"; - return '<' + localName + ' id=n' + item.id + '>' + - content + - "</" + localName + ">"; -} - - /** - * Regenerate the text string that represents a TranslationItem object, - * with data from its "original" array. The array must have already - * been created by TranslationDocument.generateTextForItem(). - * - * @param item A TranslationItem object - * - * @returns A string representation of the TranslationItem. - */ -function regenerateTextFromOriginalHelper(item) { - if (item.isSimpleRoot) { - return item.original[0]; - } - - let str = ""; - for (let child of item.original) { - if (child instanceof TranslationItem) { - str += regenerateTextFromOriginalHelper(child); - } else if (child === TranslationItem_NodePlaceholder) { - str += "<br>"; - } else { - str += child; - } - } - - return generateTranslationHtmlForItem(item, str); -} - -/** - * Helper function to parse a HTML doc result. - * How it works: - * - * An example result string is: - * - * <div id="n1">Hello <b id="n2">World</b> of Mozilla.</div> - * - * For an element node, we look at its id and find the corresponding - * TranslationItem that was associated with this node, and then we - * walk down it repeating the process. - * - * For text nodes we simply add it as a string. - */ -function parseResultNode(item, node) { - item.translation = []; - for (let child of node.childNodes) { - if (child.nodeType == TEXT_NODE) { - item.translation.push(child.nodeValue); - } else if (child.localName == "br") { - item.translation.push(TranslationItem_NodePlaceholder); - } else { - let translationItemChild = item.getChildById(child.id); - - if (translationItemChild) { - item.translation.push(translationItemChild); - parseResultNode(translationItemChild, child); - } - } - } -} - -/** - * Helper function to swap the text of a TranslationItem - * between its original and translated states. - * How it works: - * - * The function iterates through the target array (either the `original` or - * `translation` array from the TranslationItem), while also keeping a pointer - * to a current position in the child nodes from the actual DOM node that we - * are modifying. This pointer is moved forward after each item of the array - * is translated. If, at any given time, the pointer doesn't match the expected - * node that was supposed to be seen, it means that the original and translated - * contents have a different ordering, and thus we need to adjust that. - * - * A full example of the reordering process, swapping from Original to - * Translation: - * - * Original (en): <div>I <em>miss</em> <b>you</b></div> - * - * Translation (fr): <div><b>Tu</b> me <em>manques</em></div> - * - * Step 1: - * pointer points to firstChild of the DOM node, textnode "I " - * first item in item.translation is [object TranslationItem <b>] - * - * pointer does not match the expected element, <b>. So let's move <b> to the - * pointer position. - * - * Current state of the DOM: - * <div><b>you</b>I <em>miss</em> </div> - * - * Step 2: - * pointer moves forward to nextSibling, textnode "I " again. - * second item in item.translation is the string " me " - * - * pointer points to a text node, and we were expecting a text node. Match! - * just replace the text content. - * - * Current state of the DOM: - * <div><b>you</b> me <em>miss</em> </div> - * - * Step 3: - * pointer moves forward to nextSibling, <em>miss</em> - * third item in item.translation is [object TranslationItem <em>] - * - * pointer points to the expected node. Match! Nothing to do. - * - * Step 4: - * all items in this item.translation were transformed. The remaining - * text nodes are cleared to "", and domNode.normalize() removes them. - * - * Current state of the DOM: - * <div><b>you</b> me <em>miss</em></div> - * - * Further steps: - * After that, the function will visit the child items (from the visitStack), - * and the text inside the <b> and <em> nodes will be swapped as well, - * yielding the final result: - * - * <div><b>Tu</b> me <em>manques</em></div> - * - * - * @param item A TranslationItem object - * @param target A string that is either "translation" - * or "original". - */ -function swapTextForItem(item, target) { - // visitStack is the stack of items that we still need to visit. - // Let's start the process by adding the root item. - let visitStack = [ item ]; - - while (visitStack.length > 0) { - let curItem = visitStack.shift(); - - let domNode = curItem.nodeRef; - if (!domNode) { - // Skipping this item due to a missing node. - continue; - } - - if (!curItem[target]) { - // Translation not found for this item. This could be due to - // an error in the server response. For example, if a translation - // was broken in various chunks, and one of the chunks failed, - // the items from that chunk will be missing its "translation" - // field. - continue; - } - - domNode.normalize(); - - // curNode points to the child nodes of the DOM node that we are - // modifying. During most of the process, while the target array is - // being iterated (in the for loop below), it should walk together with - // the array and be pointing to the correct node that needs to modified. - // If it's not pointing to it, that means some sort of node reordering - // will be necessary to produce the correct translation. - // Note that text nodes don't need to be reordered, as we can just replace - // the content of one text node with another. - // - // curNode starts in the firstChild... - let curNode = domNode.firstChild; - - // ... actually, let's make curNode start at the first useful node (either - // a non-blank text node or something else). This is not strictly necessary, - // as the reordering algorithm would correctly handle this case. However, - // this better aligns the resulting translation with the DOM content of the - // page, avoiding cases that would need to be unecessarily reordered. - // - // An example of how this helps: - // - // ---- Original: <div> <b>Hello </b> world.</div> - // ^textnode 1 ^item 1 ^textnode 2 - // - // - Translation: <div><b>Hallo </b> Welt.</div> - // - // Transformation process without this optimization: - // 1 - start pointer at textnode 1 - // 2 - move item 1 to first position inside the <div> - // - // Node now looks like: <div><b>Hello </b>[ ][ world.]</div> - // textnode 1^ ^textnode 2 - // - // 3 - replace textnode 1 with " Welt." - // 4 - clear remaining text nodes (in this case, textnode 2) - // - // Transformation process with this optimization: - // 1 - start pointer at item 1 - // 2 - item 1 is already in position - // 3 - replace textnode 2 with " Welt." - // - // which completely avoids any node reordering, and requires only one - // text change instead of two (while also leaving the page closer to - // its original state). - while (curNode && - curNode.nodeType == TEXT_NODE && - curNode.nodeValue.trim() == "") { - curNode = curNode.nextSibling; - } - - // Now let's walk through all items in the `target` array of the - // TranslationItem. This means either the TranslationItem.original or - // TranslationItem.translation array. - for (let targetItem of curItem[target]) { - - if (targetItem instanceof TranslationItem) { - // If the array element is another TranslationItem object, let's - // add it to the stack to be visited. - visitStack.push(targetItem); - - let targetNode = targetItem.nodeRef; - - // If the node is not in the expected position, let's reorder - // it into position... - if (curNode != targetNode && - // ...unless the page has reparented this node under a totally - // different node (or removed it). In this case, all bets are off - // on being able to do anything correctly, so it's better not to - // bring back the node to this parent. - targetNode.parentNode == domNode) { - - // We don't need to null-check curNode because insertBefore(..., null) - // does what we need in that case: reorder this node to the end - // of child nodes. - domNode.insertBefore(targetNode, curNode); - curNode = targetNode; - } - - // Move pointer forward. Since we do not add empty text nodes to the - // list of translation items, we must skip them here too while - // traversing the DOM in order to get better alignment between the - // text nodes and the translation items. - if (curNode) { - curNode = getNextSiblingSkippingEmptyTextNodes(curNode); - } - - } else if (targetItem === TranslationItem_NodePlaceholder) { - // If the current item is a placeholder node, we need to move - // our pointer "past" it, jumping from one side of a block of - // elements + empty text nodes to the other side. Even if - // non-placeholder elements exists inside the jumped block, - // they will be pulled correctly later in the process when the - // targetItem for those nodes are handled. - - while (curNode && - (curNode.nodeType != TEXT_NODE || - curNode.nodeValue.trim() == "")) { - curNode = curNode.nextSibling; - } - - } else { - // Finally, if it's a text item, we just need to find the next - // text node to use. Text nodes don't need to be reordered, so - // the first one found can be used. - while (curNode && curNode.nodeType != TEXT_NODE) { - curNode = curNode.nextSibling; - } - - // If none was found and we reached the end of the child nodes, - // let's create a new one. - if (!curNode) { - // We don't know if the original content had a space or not, - // so the best bet is to create the text node with " " which - // will add one space at the beginning and one at the end. - curNode = domNode.appendChild(domNode.ownerDocument.createTextNode(" ")); - } - - // A trailing and a leading space must be preserved because - // they are meaningful in HTML. - let preSpace = /^\s/.test(curNode.nodeValue) ? " " : ""; - let endSpace = /\s$/.test(curNode.nodeValue) ? " " : ""; - - curNode.nodeValue = preSpace + targetItem + endSpace; - curNode = getNextSiblingSkippingEmptyTextNodes(curNode); - } - } - - // The translated version of a node might have less text nodes than its - // original version. If that's the case, let's clear the remaining nodes. - if (curNode) { - clearRemainingNonEmptyTextNodesFromElement(curNode); - } - - // And remove any garbage "" nodes left after clearing. - domNode.normalize(); - } -} - -function getNextSiblingSkippingEmptyTextNodes(startSibling) { - let item = startSibling.nextSibling; - while (item && - item.nodeType == TEXT_NODE && - item.nodeValue.trim() == "") { - item = item.nextSibling; - } - return item; -} - -function clearRemainingNonEmptyTextNodesFromElement(startSibling) { - let item = startSibling; - while (item) { - if (item.nodeType == TEXT_NODE && - item.nodeValue != "") { - item.nodeValue = ""; - } - item = item.nextSibling; - } -} diff --git a/application/basilisk/components/translation/YandexTranslator.jsm b/application/basilisk/components/translation/YandexTranslator.jsm deleted file mode 100644 index ab92e0962..000000000 --- a/application/basilisk/components/translation/YandexTranslator.jsm +++ /dev/null @@ -1,343 +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"; - -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; - -this.EXPORTED_SYMBOLS = [ "YandexTranslator" ]; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); -Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/Http.jsm"); - -// The maximum amount of net data allowed per request on Bing's API. -const MAX_REQUEST_DATA = 5000; // Documentation says 10000 but anywhere - // close to that is refused by the service. - -// The maximum number of chunks allowed to be translated in a single -// request. -const MAX_REQUEST_CHUNKS = 1000; // Documentation says 2000. - -// Self-imposed limit of 15 requests. This means that a page that would need -// to be broken in more than 15 requests won't be fully translated. -// The maximum amount of data that we will translate for a single page -// is MAX_REQUESTS * MAX_REQUEST_DATA. -const MAX_REQUESTS = 15; - -const YANDEX_RETURN_CODE_OK = 200; - -const YANDEX_ERR_KEY_INVALID = 401; // Invalid API key -const YANDEX_ERR_KEY_BLOCKED = 402; // This API key has been blocked -const YANDEX_ERR_DAILY_REQ_LIMIT_EXCEEDED = 403; // Daily limit for requests reached -const YANDEX_ERR_DAILY_CHAR_LIMIT_EXCEEDED = 404; // Daily limit of chars reached -const YANDEX_ERR_TEXT_TOO_LONG = 413; // The text size exceeds the maximum -const YANDEX_ERR_UNPROCESSABLE_TEXT = 422; // The text could not be translated -const YANDEX_ERR_LANG_NOT_SUPPORTED = 501; // The specified translation direction is not supported - -// Errors that should activate the service unavailable handling -const YANDEX_PERMANENT_ERRORS = [ - YANDEX_ERR_KEY_INVALID, - YANDEX_ERR_KEY_BLOCKED, - YANDEX_ERR_DAILY_REQ_LIMIT_EXCEEDED, - YANDEX_ERR_DAILY_CHAR_LIMIT_EXCEEDED, -]; - -/** - * Translates a webpage using Yandex's Translation API. - * - * @param translationDocument The TranslationDocument object that represents - * the webpage to be translated - * @param sourceLanguage The source language of the document - * @param targetLanguage The target language for the translation - * - * @returns {Promise} A promise that will resolve when the translation - * task is finished. - */ -this.YandexTranslator = function(translationDocument, sourceLanguage, targetLanguage) { - this.translationDocument = translationDocument; - this.sourceLanguage = sourceLanguage; - this.targetLanguage = targetLanguage; - this._pendingRequests = 0; - this._partialSuccess = false; - this._serviceUnavailable = false; - this._translatedCharacterCount = 0; -}; - -this.YandexTranslator.prototype = { - /** - * Performs the translation, splitting the document into several chunks - * respecting the data limits of the API. - * - * @returns {Promise} A promise that will resolve when the translation - * task is finished. - */ - translate: function() { - return Task.spawn(function *() { - let currentIndex = 0; - this._onFinishedDeferred = Promise.defer(); - - // Let's split the document into various requests to be sent to - // Yandex's Translation API. - for (let requestCount = 0; requestCount < MAX_REQUESTS; requestCount++) { - // Generating the text for each request can be expensive, so - // let's take the opportunity of the chunkification process to - // allow for the event loop to attend other pending events - // before we continue. - yield CommonUtils.laterTickResolvingPromise(); - - // Determine the data for the next request. - let request = this._generateNextTranslationRequest(currentIndex); - - // Create a real request to the server, and put it on the - // pending requests list. - let yandexRequest = new YandexRequest(request.data, - this.sourceLanguage, - this.targetLanguage); - this._pendingRequests++; - yandexRequest.fireRequest().then(this._chunkCompleted.bind(this), - this._chunkFailed.bind(this)); - - currentIndex = request.lastIndex; - if (request.finished) { - break; - } - } - - return this._onFinishedDeferred.promise; - }.bind(this)); - }, - - /** - * Function called when a request sent to the server completed successfully. - * This function handles calling the function to parse the result and the - * function to resolve the promise returned by the public `translate()` - * method when there are no pending requests left. - * - * @param request The YandexRequest sent to the server - */ - _chunkCompleted: function(yandexRequest) { - if (this._parseChunkResult(yandexRequest)) { - this._partialSuccess = true; - // Count the number of characters successfully translated. - this._translatedCharacterCount += yandexRequest.characterCount; - } - - this._checkIfFinished(); - }, - - /** - * Function called when a request sent to the server has failed. - * This function handles deciding if the error is transient or means the - * service is unavailable (zero balance on the key or request credentials are - * not in an active state) and calling the function to resolve the promise - * returned by the public `translate()` method when there are no pending - * requests left. - * - * @param aError [optional] The XHR object of the request that failed. - */ - _chunkFailed: function(aError) { - if (aError instanceof Ci.nsIXMLHttpRequest) { - let body = aError.responseText; - let json = { code: 0 }; - try { - json = JSON.parse(body); - } catch (e) {} - - if (json.code && YANDEX_PERMANENT_ERRORS.indexOf(json.code) != -1) - this._serviceUnavailable = true; - } - - this._checkIfFinished(); - }, - - /** - * Function called when a request sent to the server has completed. - * This function handles resolving the promise - * returned by the public `translate()` method when all chunks are completed. - */ - _checkIfFinished: function() { - // Check if all pending requests have been - // completed and then resolves the promise. - // If at least one chunk was successful, the - // promise will be resolved positively which will - // display the "Success" state for the infobar. Otherwise, - // the "Error" state will appear. - if (--this._pendingRequests == 0) { - if (this._partialSuccess) { - this._onFinishedDeferred.resolve({ - characterCount: this._translatedCharacterCount - }); - } else { - let error = this._serviceUnavailable ? "unavailable" : "failure"; - this._onFinishedDeferred.reject(error); - } - } - }, - - /** - * This function parses the result returned by Yandex's Translation API, - * which returns a JSON result that contains a number of elements. The - * API is documented here: - * http://api.yandex.com/translate/doc/dg/reference/translate.xml - * - * @param request The request sent to the server. - * @returns boolean True if parsing of this chunk was successful. - */ - _parseChunkResult: function(yandexRequest) { - let results; - try { - let result = JSON.parse(yandexRequest.networkRequest.responseText); - if (result.code != 200) { - Services.console.logStringMessage("YandexTranslator: Result is " + result.code); - return false; - } - results = result.text - } catch (e) { - return false; - } - - let len = results.length; - if (len != yandexRequest.translationData.length) { - // This should never happen, but if the service returns a different number - // of items (from the number of items submitted), we can't use this chunk - // because all items would be paired incorrectly. - return false; - } - - let error = false; - for (let i = 0; i < len; i++) { - try { - let result = results[i]; - let root = yandexRequest.translationData[i][0]; - root.parseResult(result); - } catch (e) { error = true; } - } - - return !error; - }, - - /** - * This function will determine what is the data to be used for - * the Nth request we are generating, based on the input params. - * - * @param startIndex What is the index, in the roots list, that the - * chunk should start. - */ - _generateNextTranslationRequest: function(startIndex) { - let currentDataSize = 0; - let currentChunks = 0; - let output = []; - let rootsList = this.translationDocument.roots; - - for (let i = startIndex; i < rootsList.length; i++) { - let root = rootsList[i]; - let text = this.translationDocument.generateTextForItem(root); - if (!text) { - continue; - } - - let newCurSize = currentDataSize + text.length; - let newChunks = currentChunks + 1; - - if (newCurSize > MAX_REQUEST_DATA || - newChunks > MAX_REQUEST_CHUNKS) { - - // If we've reached the API limits, let's stop accumulating data - // for this request and return. We return information useful for - // the caller to pass back on the next call, so that the function - // can keep working from where it stopped. - return { - data: output, - finished: false, - lastIndex: i - }; - } - - currentDataSize = newCurSize; - currentChunks = newChunks; - output.push([root, text]); - } - - return { - data: output, - finished: true, - lastIndex: 0 - }; - } -}; - -/** - * Represents a request (for 1 chunk) sent off to Yandex's service. - * - * @params translationData The data to be used for this translation, - * generated by the generateNextTranslationRequest... - * function. - * @param sourceLanguage The source language of the document. - * @param targetLanguage The target language for the translation. - * - */ -function YandexRequest(translationData, sourceLanguage, targetLanguage) { - this.translationData = translationData; - this.sourceLanguage = sourceLanguage; - this.targetLanguage = targetLanguage; - this.characterCount = 0; -} - -YandexRequest.prototype = { - /** - * Initiates the request - */ - fireRequest: function() { - return Task.spawn(function *() { - // Prepare URL. - let url = getUrlParam("https://translate.yandex.net/api/v1.5/tr.json/translate", - "browser.translation.yandex.translateURLOverride"); - - // Prepare the request body. - let apiKey = getUrlParam("%YANDEX_API_KEY%", "browser.translation.yandex.apiKeyOverride"); - let params = [ - ["key", apiKey], - ["format", "html"], - ["lang", this.sourceLanguage + "-" + this.targetLanguage], - ]; - - for (let [, text] of this.translationData) { - params.push(["text", text]); - this.characterCount += text.length; - } - - // Set up request options. - let deferred = Promise.defer(); - let options = { - onLoad: (function(responseText, xhr) { - deferred.resolve(this); - }).bind(this), - onError: function(e, responseText, xhr) { - deferred.reject(xhr); - }, - postData: params - }; - - // Fire the request. - this.networkRequest = httpRequest(url, options); - - return deferred.promise; - }.bind(this)); - } -}; - -/** - * Fetch an auth token (clientID or client secret), which may be overridden by - * a pref if it's set. - */ -function getUrlParam(paramValue, prefName) { - if (Services.prefs.getPrefType(prefName)) - paramValue = Services.prefs.getCharPref(prefName); - paramValue = Services.urlFormatter.formatURL(paramValue); - return paramValue; -} diff --git a/application/basilisk/components/translation/jar.mn b/application/basilisk/components/translation/jar.mn deleted file mode 100644 index be744cb9e..000000000 --- a/application/basilisk/components/translation/jar.mn +++ /dev/null @@ -1,6 +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/. -browser.jar: - content/browser/translation-infobar.xml - content/browser/microsoft-translator-attribution.png diff --git a/application/basilisk/components/translation/microsoft-translator-attribution.png b/application/basilisk/components/translation/microsoft-translator-attribution.png Binary files differdeleted file mode 100644 index d9d277461..000000000 --- a/application/basilisk/components/translation/microsoft-translator-attribution.png +++ /dev/null diff --git a/application/basilisk/components/translation/moz.build b/application/basilisk/components/translation/moz.build index ac0165230..32421e430 100644 --- a/application/basilisk/components/translation/moz.build +++ b/application/basilisk/components/translation/moz.build @@ -3,14 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXTRA_JS_MODULES.translation = [ - 'BingTranslator.jsm', 'cld2/cld-worker.js', 'cld2/cld-worker.js.mem', 'LanguageDetector.jsm', - 'Translation.jsm', - 'TranslationContentHandler.jsm', - 'TranslationDocument.jsm', - 'YandexTranslator.jsm' ] - -JAR_MANIFESTS += ['jar.mn'] diff --git a/application/basilisk/components/translation/translation-infobar.xml b/application/basilisk/components/translation/translation-infobar.xml deleted file mode 100644 index db0695c03..000000000 --- a/application/basilisk/components/translation/translation-infobar.xml +++ /dev/null @@ -1,441 +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" role="xul:alert"> - <resources> - <stylesheet src="chrome://global/skin/notification.css"/> - </resources> - <content> - <xul:hbox class="notification-inner" flex="1" xbl:inherits="type"> - <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="openUILinkIn('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('paneContent');" - 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();"/> - </xul:hbox> - </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 bundle = Cc["@mozilla.org/intl/stringbundle;1"] - .getService(Ci.nsIStringBundleService) - .createBundle("chrome://global/locale/languageNames.properties"); - let sortByLocalizedName = function(aList) { - return aList.map(code => [code, bundle.GetStringFromName(code)]) - .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); - if (engineIndex != -1) { - this._getAnonElt('translationEngine').selectedIndex = engineIndex; - } - - const kWelcomePref = "browser.translation.ui.welcomeMessageShown"; - if (Services.prefs.prefHasUserValue(kWelcomePref) || - this.translation.browser != gBrowser.selectedBrowser) - return; - - this.addEventListener("transitionend", function onShown() { - this.removeEventListener("transitionend", onShown); - - // 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 = Cc["@mozilla.org/chrome/chrome-registry;1"] - .getService(Ci.nsIXULChromeRegistry) - .getSelectedLocale("browser"); - 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); - }); - ]]> - </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 langBundle = - Cc["@mozilla.org/intl/stringbundle;1"] - .getService(Ci.nsIStringBundleService) - .createBundle("chrome://global/locale/languageNames.properties"); - let langName = langBundle.GetStringFromName(lang); - - // Set the label and accesskey on the menuitem. - let bundle = - Cc["@mozilla.org/intl/stringbundle;1"] - .getService(Ci.nsIStringBundleService) - .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(",").indexOf(lang) != -1; - - // 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> diff --git a/application/basilisk/installer/package-manifest.in b/application/basilisk/installer/package-manifest.in index c446d3687..35060ea5e 100644 --- a/application/basilisk/installer/package-manifest.in +++ b/application/basilisk/installer/package-manifest.in @@ -35,7 +35,9 @@ #ifdef XP_MACOSX ; Mac bundle stuff @APPNAME@/Contents/Info.plist +#ifdef MOZ_UPDATER @APPNAME@/Contents/Library/LaunchServices +#endif @APPNAME@/Contents/PkgInfo @RESPATH@/firefox.icns @RESPATH@/document.icns @@ -114,7 +116,7 @@ @BINPATH@/browser/VisualElements/VisualElements_150.png @BINPATH@/browser/VisualElements/VisualElements_70.png #else -@BINPATH@/@MOZ_APP_NAME@-bin +@RESPATH@/@MOZ_APP_NAME@-bin @BINPATH@/@MOZ_APP_NAME@ #endif @RESPATH@/application.ini diff --git a/application/basilisk/locales/en-US/chrome/browser/browser.dtd b/application/basilisk/locales/en-US/chrome/browser/browser.dtd index f75aa46a7..09da91dee 100644 --- a/application/basilisk/locales/en-US/chrome/browser/browser.dtd +++ b/application/basilisk/locales/en-US/chrome/browser/browser.dtd @@ -207,8 +207,6 @@ These should match what Safari and other Apple applications use on OS X Lion. -- <!ENTITY urlbar.webRTCShareScreenNotificationAnchor.tooltip "Manage sharing your windows or screen with the site"> <!ENTITY urlbar.servicesNotificationAnchor.tooltip "Open install message panel"> -<!ENTITY urlbar.translateNotificationAnchor.tooltip "Translate this page"> -<!ENTITY urlbar.translatedNotificationAnchor.tooltip "Manage page translation"> <!ENTITY urlbar.emeNotificationAnchor.tooltip "Manage use of DRM software"> <!ENTITY urlbar.cameraBlocked.tooltip "You have blocked your camera for this website."> diff --git a/application/basilisk/locales/en-US/chrome/browser/preferences/content.dtd b/application/basilisk/locales/en-US/chrome/browser/preferences/content.dtd index 5d58ffa37..d1d83b3bc 100644 --- a/application/basilisk/locales/en-US/chrome/browser/preferences/content.dtd +++ b/application/basilisk/locales/en-US/chrome/browser/preferences/content.dtd @@ -38,21 +38,6 @@ <!ENTITY chooseButton.label "Choose…"> <!ENTITY chooseButton.accesskey "o"> -<!ENTITY translateWebPages.label "Translate web content"> -<!ENTITY translateWebPages.accesskey "T"> -<!ENTITY translateExceptions.label "Exceptions…"> -<!ENTITY translateExceptions.accesskey "x"> - -<!-- LOCALIZATION NOTE (translation.options.attribution.beforeLogo, - - translation.options.attribution.afterLogo): - - These 2 strings are displayed before and after a 'Microsoft Translator' - - logo. - - The translations for these strings should match the translations in - - browser/translation.dtd - --> -<!ENTITY translation.options.attribution.beforeLogo "Translations by"> -<!ENTITY translation.options.attribution.afterLogo ""> - <!ENTITY drmContent.label "DRM content"> <!ENTITY playDRMContent.label "Play DRM content"> diff --git a/application/basilisk/locales/en-US/chrome/browser/preferences/translation.dtd b/application/basilisk/locales/en-US/chrome/browser/preferences/translation.dtd deleted file mode 100644 index fc5fb2b7b..000000000 --- a/application/basilisk/locales/en-US/chrome/browser/preferences/translation.dtd +++ /dev/null @@ -1,24 +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/. --> - -<!ENTITY window.title "Exceptions - Translation"> -<!ENTITY window.width "36em"> -<!ENTITY windowClose.key "w"> - -<!ENTITY noTranslationForLanguages.label "Translation will not be offered for the following languages:"> -<!ENTITY treehead.languageName.label "Languages"> -<!ENTITY removeLanguage.label "Remove Language"> -<!ENTITY removeLanguage.accesskey "R"> -<!ENTITY removeAllLanguages.label "Remove All Languages"> -<!ENTITY removeAllLanguages.accesskey "e"> - -<!ENTITY noTranslationForSites.label "Translation will not be offered for the following sites:"> -<!ENTITY treehead.siteName.label "Sites"> -<!ENTITY removeSite.label "Remove Site"> -<!ENTITY removeSite.accesskey "S"> -<!ENTITY removeAllSites.label "Remove All Sites"> -<!ENTITY removeAllSites.accesskey "i"> - -<!ENTITY button.close.label "Close"> -<!ENTITY button.close.accesskey "C"> diff --git a/application/basilisk/locales/en-US/chrome/browser/translation.dtd b/application/basilisk/locales/en-US/chrome/browser/translation.dtd deleted file mode 100644 index ca8bb9d51..000000000 --- a/application/basilisk/locales/en-US/chrome/browser/translation.dtd +++ /dev/null @@ -1,75 +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/. --> - -<!-- LOCALIZATION NOTE (translation.thisPageIsIn.label, - - translation.translateThisPage.label): - - These 2 strings are used to construct a sentence that contains a dropdown - - showing the detected language of the current web page. - - In en-US it looks like this: - - This page is in [detected language] Translate this page? - - "detected language" here is a language name coming from the - - global/languageNames.properties file; for some locales it may not be in - - the correct grammar case to keep the same structure of the original - - sentence. --> -<!ENTITY translation.thisPageIsIn.label "This page is in"> -<!ENTITY translation.translateThisPage.label "Translate this page?"> -<!ENTITY translation.translate.button "Translate"> -<!ENTITY translation.notNow.button "Not Now"> - -<!ENTITY translation.translatingContent.label "Translating page content…"> - -<!-- LOCALIZATION NOTE (translation.translatedFrom.label, - - translation.translatedTo.label, - - translation.translatedToSuffix.label): - - These 3 strings are used to construct a sentence that contains 2 dropdowns - - showing the source and target language of a translated web page. - - In en-US it looks like this: - - This page has been translated from [from language] to [to language] - - "from language" and "to language" here are language names coming from the - - global/languageNames.properties file; for some locales they may not be in - - the correct grammar case to keep the same structure of the original - - sentence. - - - - translation.translatedToSuffix.label (empty in en-US) is for locales that - - need to display some text after the second drop down for the sentence to - - be grammatically correct. --> -<!ENTITY translation.translatedFrom.label "This page has been translated from"> -<!ENTITY translation.translatedTo.label "to"> -<!ENTITY translation.translatedToSuffix.label ""> - -<!ENTITY translation.showOriginal.button "Show Original"> -<!ENTITY translation.showTranslation.button "Show Translation"> - -<!ENTITY translation.errorTranslating.label "There has been an error translating this page."> -<!ENTITY translation.tryAgain.button "Try Again"> - -<!ENTITY translation.serviceUnavailable.label "Translation is not available at the moment. Please try again later."> - -<!ENTITY translation.options.menu "Options"> -<!-- LOCALIZATION NOTE (translation.options.neverForSite.accesskey, - - translation.options.preferences.accesskey): - - The accesskey values used here should not clash with the value used for - - translation.options.neverForLanguage.accesskey in translation.properties - --> -<!ENTITY translation.options.neverForSite.label "Never translate this site"> -<!ENTITY translation.options.neverForSite.accesskey "e"> -<!ENTITY translation.options.preferences.label "Translation preferences"> -<!ENTITY translation.options.preferences.accesskey "T"> - -<!-- LOCALIZATION NOTE (translation.options.attribution.beforeLogo, - - translation.options.attribution.afterLogo): - - These 2 strings are displayed before and after a 'Microsoft Translator' - - logo. - --> -<!ENTITY translation.options.attribution.beforeLogo "Translations by"> -<!ENTITY translation.options.attribution.afterLogo ""> - -<!-- LOCALIZATION NOTE (translation.options.attribution.poweredByYandex, - translation.options.attribution.beforeLogo, - - translation.options.attribution.afterLogo): - - translation.options.attribution.poweredByYandex is displayed instead of - - the other two strings when yandex translation engine is preferred by the - - user. - --> -<!ENTITY translation.options.attribution.yandexTranslate "Powered by Yandex.Translate"> diff --git a/application/basilisk/locales/en-US/chrome/browser/translation.properties b/application/basilisk/locales/en-US/chrome/browser/translation.properties deleted file mode 100644 index e62edbd0a..000000000 --- a/application/basilisk/locales/en-US/chrome/browser/translation.properties +++ /dev/null @@ -1,12 +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/. - -# LOCALIZATION NOTE (translation.options.neverForLanguage.label): -# %S is a language name coming from the global/languageNames.properties file. -translation.options.neverForLanguage.label=Never translate %S - -# LOCALIZATION NOTE (translation.options.neverForLanguage.accesskey): -# The accesskey value used here should not clash with the values used for -# translation.options.*.accesskey in translation.dtd -translation.options.neverForLanguage.accesskey=N diff --git a/application/basilisk/locales/jar.mn b/application/basilisk/locales/jar.mn index 9a847c7ed..5c5a72a1f 100644 --- a/application/basilisk/locales/jar.mn +++ b/application/basilisk/locales/jar.mn @@ -42,8 +42,6 @@ locale/browser/shellservice.properties (%chrome/browser/shellservice.properties) locale/browser/tabbrowser.properties (%chrome/browser/tabbrowser.properties) locale/browser/taskbar.properties (%chrome/browser/taskbar.properties) - locale/browser/translation.dtd (%chrome/browser/translation.dtd) - locale/browser/translation.properties (%chrome/browser/translation.properties) locale/browser/webrtcIndicator.properties (%chrome/browser/webrtcIndicator.properties) locale/browser/downloads/downloads.dtd (%chrome/browser/downloads/downloads.dtd) locale/browser/downloads/downloads.properties (%chrome/browser/downloads/downloads.properties) @@ -88,7 +86,6 @@ locale/browser/preferences/sync.dtd (%chrome/browser/preferences/sync.dtd) locale/browser/preferences/tabs.dtd (%chrome/browser/preferences/tabs.dtd) locale/browser/preferences/search.dtd (%chrome/browser/preferences/search.dtd) - locale/browser/preferences/translation.dtd (%chrome/browser/preferences/translation.dtd) locale/browser/syncBrand.dtd (%chrome/browser/syncBrand.dtd) locale/browser/syncSetup.dtd (%chrome/browser/syncSetup.dtd) locale/browser/syncSetup.properties (%chrome/browser/syncSetup.properties) diff --git a/application/basilisk/themes/linux/browser.css b/application/basilisk/themes/linux/browser.css index f9bd0bbd0..fbc5b651b 100644 --- a/application/basilisk/themes/linux/browser.css +++ b/application/basilisk/themes/linux/browser.css @@ -840,80 +840,6 @@ menuitem.bookmark-item { outline: 1px dotted -moz-DialogText; } -/* Translation infobar */ - -%include ../shared/translation/infobar.inc.css - -notification[value="translation"] { - min-height: 40px; -} - -notification[value="translation"], -notification[value="translation"] button, -notification[value="translation"] menulist { - min-height: 30px; - color: #5A5959; -} - -notification[value="translation"] { - background-color: #F2F1F0; -} - -notification[value="translation"] button, -notification[value="translation"] menulist { - padding-inline-end: 1ch; -} - -notification[value="translation"] menulist { - border: 1px solid #C1C1C1; - background-color: #FFF; -} - -notification[value="translation"] button { - border: 1px solid #C1C1C1; - background-color: #F2F1F0; -} - -notification[value="translation"] button, -notification[value="translation"] menulist, -notification[value="translation"] menulist > .menulist-label-box { - margin-inline-start: 1ch; - margin-inline-end: 1ch; -} - -notification[value="translation"] button:hover, -notification[value="translation"] button:active, -notification[value="translation"] menulist:hover, -notification[value="translation"] menulist:active { - background-color: #E2E1E0; -} - -notification[value="translation"] button[anonid="translate"] { - color: #FFF; - background-image: linear-gradient(#9FB938, #8DA726); - box-shadow: none; - border: 1px solid #829C1C; -} - -notification[value="translation"] button[anonid="translate"]:hover, -notification[value="translation"] button[anonid="translate"]:active { - background-image: linear-gradient(#8DA726, #8DA726); -} - -notification[value="translation"] button > .button-box, -notification[value="translation"] button[type="menu"] > .button-box > .button-menu-dropmarker { - padding: 0; - margin-inline-start: 3ch; -} - -notification[value="translation"] button:not([type="menu"]) > .button-box { - margin-inline-end: 3ch; -} - -notification[value="translation"] menulist > .menulist-dropmarker { - display: block; -} - /* AutoComplete */ %include ../shared/autocomplete.inc.css diff --git a/application/basilisk/themes/linux/preferences/preferences.css b/application/basilisk/themes/linux/preferences/preferences.css index 45e2dc23d..5c1b102fa 100644 --- a/application/basilisk/themes/linux/preferences/preferences.css +++ b/application/basilisk/themes/linux/preferences/preferences.css @@ -20,12 +20,6 @@ font-weight: bold; } -/* Content Pane */ -#translationAttributionImage { - width: 70px; - cursor: pointer; -} - /* Modeless Window Dialogs */ .windowDialog, .windowDialog prefpane { diff --git a/application/basilisk/themes/osx/browser.css b/application/basilisk/themes/osx/browser.css index 808bb20b1..70f1f6162 100644 --- a/application/basilisk/themes/osx/browser.css +++ b/application/basilisk/themes/osx/browser.css @@ -2920,139 +2920,6 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { 0 0 3px 2px -moz-mac-focusring; } -/* Translation */ - -%include ../shared/translation/infobar.inc.css - -notification[value="translation"] { - color: #484848; - background-color: #EFEFEF; - background-image: none; - border-top: none; - border-bottom: 1px solid #c4c4c4; - padding-top: 1px; - padding-bottom: 1px; - min-height: 35px; -} - -.translate-infobar-element { - margin-top: 0 !important; - margin-bottom: 0 !important; -} - -button.translate-infobar-element { - background: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.1)) repeat scroll 0% 0% padding-box transparent; - color: #333333; - border: 1px solid; - border-color: rgba(23, 51, 78, 0.15) rgba(23, 51, 78, 0.17) rgba(23, 51, 78, 0.2); - box-shadow: 0px 0px 2px rgba(255, 255, 255, 0.5) inset, 0px 1px 0px rgba(255, 255, 255, 0.2); - transition-property: background-color, border-color, box-shadow; - transition-duration: 150ms; - min-height: 22px; - min-width: 0; - padding: 0 0.8em !important; - margin-left: 0.25em; - margin-right: 0.25em; -} - -button.translate-infobar-element .button-text { - margin-left: 0 !important; - margin-right: 0 !important; -} - -label.translate-infobar-element { - padding-top: 2px; -} - -button.translate-infobar-element:hover { - background: #f0f0f0; - box-shadow: 0 1px 0 hsla(0,0%,100%,.1) inset, 0 0 0 1px hsla(0,0%,100%,.05) inset, 0 1px 0 hsla(210,54%,20%,.01), 0 0 4px hsla(206,100%,20%,.1); -} - -button.translate-infobar-element:active { - box-shadow: 0 1px 1px hsla(211,79%,6%,.1) inset, 0 0 1px hsla(211,79%,6%,.2) inset; - transition-duration: 0ms; -} - -button.translate-infobar-element[anonid="translate"] { - color: #ffffff; - background: linear-gradient(#4cb1ff, #1793e5); - box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, 0 0 0 1px hsla(0,0%,100%,.1) inset, 0 1px 0 hsla(210,54%,20%,.03); - border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); - padding: 0 1.1em !important;; -} - -button.translate-infobar-element[anonid="translate"]:hover { - background-image: linear-gradient(#66bdff, #0d9eff); - box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, 0 0 0 1px hsla(0,0%,100%,.1) inset, 0 1px 0 hsla(210,54%,20%,.03), 0 0 4px hsla(206,100%,20%,.2); -} - -button.translate-infobar-element.options-menu-button { - padding-inline-start: 0.5em !important; - padding-inline-end: 0em !important; -} - -button.translate-infobar-element.options-menu-button > .button-box > .button-menu-dropmarker { - display: -moz-box; - list-style-image: url("chrome://global/skin/icons/glyph-dropdown.png"); - padding: 0 !important; - margin: 0 !important; -} - -@media (min-resolution: 2dppx) { - button.translate-infobar-element.options-menu-button > .button-box > .button-menu-dropmarker { - list-style-image: url("chrome://global/skin/icons/glyph-dropdown@2x.png"); - } - - button.translate-infobar-element.options-menu-button > .button-box > .button-menu-dropmarker > .dropmarker-icon { - width: 8px; - } -} - -menulist.translate-infobar-element { - text-shadow: 0 1px 1px #FEFFFE; - border: 1px solid; - border-color: rgba(23, 51, 78, 0.15) rgba(23, 51, 78, 0.17) rgba(23, 51, 78, 0.2); - box-shadow: 0 1px 1px 0 #FFFFFF, inset 0 2px 2px 0 #FFFFFF; - background-color: #F1F1F1; - background-image: linear-gradient(#FFFFFF, rgba(255,255,255,0.1)); - color: #333333; - padding: 0; - min-height: 22px !important; -} - -menulist.translate-infobar-element > .menulist-label-box { - padding-top: 1px; - padding-inline-start: 0.3em; - margin-top: 0; - margin-bottom: 0; -} - -menulist.translate-infobar-element:hover { - background: #f0f0f0; - box-shadow: 0 1px 0 hsla(0,0%,100%,.1) inset, 0 0 0 1px hsla(0,0%,100%,.05) inset, 0 1px 0 hsla(210,54%,20%,.01), 0 0 4px hsla(206,100%,20%,.1); -} - -menulist.translate-infobar-element[open="true"] { - background-image: linear-gradient(rgba(255,255,255,0.1), - rgba(255,255,255,0.6)); -} - -menulist.translate-infobar-element > .menulist-dropmarker { - display: -moz-box; - list-style-image: url("chrome://global/skin/icons/glyph-dropdown.png"); -} - -@media (min-resolution: 2dppx) { - menulist.translate-infobar-element > .menulist-dropmarker { - list-style-image: url("chrome://global/skin/icons/glyph-dropdown@2x.png"); - } - - menulist.translate-infobar-element > .menulist-dropmarker > .dropmarker-icon { - width: 8px; - } -} - .popup-notification-body[popupid="addon-progress"], .popup-notification-body[popupid="addon-install-confirmation"] { width: 28em; diff --git a/application/basilisk/themes/osx/preferences/preferences.css b/application/basilisk/themes/osx/preferences/preferences.css index 4f17ec58d..a8dcadc55 100644 --- a/application/basilisk/themes/osx/preferences/preferences.css +++ b/application/basilisk/themes/osx/preferences/preferences.css @@ -49,11 +49,6 @@ caption { border-bottom: 1px solid #ccc; } -#translationAttributionImage { - width: 70px; - cursor: pointer; -} - #browserUseCurrent, #browserUseBookmark, #browserUseBlank { diff --git a/application/basilisk/themes/shared/jar.inc.mn b/application/basilisk/themes/shared/jar.inc.mn index 588cf5364..361edd311 100644 --- a/application/basilisk/themes/shared/jar.inc.mn +++ b/application/basilisk/themes/shared/jar.inc.mn @@ -108,10 +108,6 @@ skin/classic/browser/theme-switcher-icon.png (../shared/theme-switcher-icon.png) skin/classic/browser/theme-switcher-icon@2x.png (../shared/theme-switcher-icon@2x.png) skin/classic/browser/toolbarbutton-dropdown-arrow.png (../shared/toolbarbutton-dropdown-arrow.png) - skin/classic/browser/translating-16.png (../shared/translation/translating-16.png) - skin/classic/browser/translating-16@2x.png (../shared/translation/translating-16@2x.png) - skin/classic/browser/translation-16.png (../shared/translation/translation-16.png) - skin/classic/browser/translation-16@2x.png (../shared/translation/translation-16@2x.png) skin/classic/browser/undoCloseTab.png (../shared/undoCloseTab.png) skin/classic/browser/undoCloseTab@2x.png (../shared/undoCloseTab@2x.png) skin/classic/browser/update-badge.svg (../shared/update-badge.svg) diff --git a/application/basilisk/themes/shared/notification-icons.inc.css b/application/basilisk/themes/shared/notification-icons.inc.css index f641e72c1..99451ba98 100644 --- a/application/basilisk/themes/shared/notification-icons.inc.css +++ b/application/basilisk/themes/shared/notification-icons.inc.css @@ -266,27 +266,3 @@ html|*#webRTC-previewVideo { opacity: 1; } } - -/* TRANSLATION */ - -.translation-icon { - list-style-image: url(chrome://browser/skin/translation-16.png); - -moz-image-region: rect(0px, 16px, 16px, 0px); -} - -.translation-icon.in-use { - -moz-image-region: rect(0px, 32px, 16px, 16px); -} - -%ifdef XP_MACOSX -@media (min-resolution: 1.1dppx) { - .translation-icon { - list-style-image: url(chrome://browser/skin/translation-16@2x.png); - -moz-image-region: rect(0px, 32px, 32px, 0px); - } - - .translation-icon.in-use { - -moz-image-region: rect(0px, 64px, 32px, 32px); - } -} -%endif diff --git a/application/basilisk/themes/shared/translation/infobar.inc.css b/application/basilisk/themes/shared/translation/infobar.inc.css deleted file mode 100644 index 50d1acc01..000000000 --- a/application/basilisk/themes/shared/translation/infobar.inc.css +++ /dev/null @@ -1,95 +0,0 @@ -%if 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/. */ -%endif -notification[value="translation"] .messageImage { - list-style-image: url(chrome://browser/skin/translation-16.png); - -moz-image-region: rect(0, 32px, 16px, 16px); -} - -@media (min-resolution: 1.25dppx) { - notification[value="translation"] .messageImage { - list-style-image: url(chrome://browser/skin/translation-16@2x.png); - -moz-image-region: rect(0, 64px, 32px, 32px); - } -} - -notification[value="translation"][state="translating"] .messageImage { - list-style-image: url(chrome://browser/skin/translating-16.png); - -moz-image-region: auto; -} - -@media (min-resolution: 1.25dppx) { - notification[value="translation"][state="translating"] .messageImage { - list-style-image: url(chrome://browser/skin/translating-16@2x.png); - } -} - -notification[value="translation"] hbox[anonid="details"] { - overflow: hidden; -} - -notification[value="translation"] button, -notification[value="translation"] menulist { - -moz-appearance: none; - border-width: 1px; - -moz-border-top-colors: none; - -moz-border-right-colors: none; - -moz-border-bottom-colors: none; - -moz-border-left-colors: none; - border-radius: 2px; - min-width: 0; - box-shadow: 0 1px rgba(255, 255, 255, 0.5), 0 1px rgba(255, 255, 255, 0.5) inset; -} - -notification[value="translation"] menulist > .menulist-dropmarker { - -moz-appearance: toolbarbutton-dropdown; - border: none; - background-color: transparent; - margin: auto; - padding: 5px 0; -} - -.translation-menupopup arrowscrollbox { - padding-bottom: 0; -} - -.translation-attribution { - cursor: pointer; - -moz-box-align: end; - font-size: small; -} - -.translation-attribution > label { - margin-bottom: 0; -} - -.translation-attribution > image { - width: 70px; -} - -.translation-welcome-panel { - width: 305px; -} - -.translation-welcome-logo { - height: 32px; - width: 32px; - list-style-image: url(chrome://browser/skin/translation-16@2x.png); - -moz-image-region: rect(0, 64px, 32px, 32px); -} - -.translation-welcome-content { - margin-inline-start: 16px; -} - -.translation-welcome-headline { - font-size: larger; - font-weight: bold; -} - -.translation-welcome-body { - padding: 1em 0; - margin: 0 0; -} diff --git a/application/basilisk/themes/shared/translation/translating-16.png b/application/basilisk/themes/shared/translation/translating-16.png Binary files differdeleted file mode 100644 index 71ca37c22..000000000 --- a/application/basilisk/themes/shared/translation/translating-16.png +++ /dev/null diff --git a/application/basilisk/themes/shared/translation/translating-16@2x.png b/application/basilisk/themes/shared/translation/translating-16@2x.png Binary files differdeleted file mode 100644 index ab6184047..000000000 --- a/application/basilisk/themes/shared/translation/translating-16@2x.png +++ /dev/null diff --git a/application/basilisk/themes/shared/translation/translation-16.png b/application/basilisk/themes/shared/translation/translation-16.png Binary files differdeleted file mode 100644 index 4b42dedcf..000000000 --- a/application/basilisk/themes/shared/translation/translation-16.png +++ /dev/null diff --git a/application/basilisk/themes/shared/translation/translation-16@2x.png b/application/basilisk/themes/shared/translation/translation-16@2x.png Binary files differdeleted file mode 100644 index 2105a3e4a..000000000 --- a/application/basilisk/themes/shared/translation/translation-16@2x.png +++ /dev/null diff --git a/application/basilisk/themes/windows/browser.css b/application/basilisk/themes/windows/browser.css index 2ed3e7e6a..334265e60 100644 --- a/application/basilisk/themes/windows/browser.css +++ b/application/basilisk/themes/windows/browser.css @@ -2113,87 +2113,6 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] { outline: 1px dotted -moz-DialogText; } -/* Translation infobar */ - -%include ../shared/translation/infobar.inc.css - -notification[value="translation"] { - min-height: 40px; -} - -@media (-moz-windows-default-theme) { - notification[value="translation"], - notification[value="translation"] button, - notification[value="translation"] menulist { - min-height: 30px; - color: #545454; - } - - notification[value="translation"] { - background-color: #EEE; - } - - notification[value="translation"] button, - notification[value="translation"] menulist { - padding-inline-end: 1ch; - } - - notification[value="translation"] menulist { - border: 1px solid #C1C1C1; - background-color: #FFF; - } - - notification[value="translation"] button { - border: 1px solid #C1C1C1; - background-color: #FBFBFB; - } - - notification[value="translation"] button, - notification[value="translation"] menulist, - notification[value="translation"] menulist > .menulist-label-box { - margin-inline-start: 1ch; - margin-inline-end: 1ch; - } - - notification[value="translation"] button:hover, - notification[value="translation"] button:active, - notification[value="translation"] menulist:hover, - notification[value="translation"] menulist:active { - background-color: #EBEBEB; - } - - notification[value="translation"] button[anonid="translate"] { - color: #FFF; - background-color: #0095DD; - box-shadow: none; - border: 1px solid #006B9D; - } - - notification[value="translation"] button[anonid="translate"]:hover, - notification[value="translation"] button[anonid="translate"]:active { - background-color: #008ACB; - } - - notification[value="translation"] button[type="menu"] > .button-box > .button-menu-dropmarker, - notification[value="translation"] menulist > .menulist-dropmarker { - list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow.png"); - } - - notification[value="translation"] button > .button-box, - notification[value="translation"] button[type="menu"] > .button-box > .button-menu-dropmarker { - padding: 0; - margin-inline-start: 3ch; - } - - notification[value="translation"] button:not([type="menu"]) > .button-box { - margin-inline-end: 3ch; - } -} - -.translation-menupopup { - -moz-appearance: none; -} - /* Bookmarks roots menu-items */ #subscribeToPageMenuitem:not([disabled]), #subscribeToPageMenupopup { diff --git a/application/basilisk/themes/windows/preferences/preferences.css b/application/basilisk/themes/windows/preferences/preferences.css index bd1ec3083..c6c063b53 100644 --- a/application/basilisk/themes/windows/preferences/preferences.css +++ b/application/basilisk/themes/windows/preferences/preferences.css @@ -15,13 +15,6 @@ #isNotDefaultLabel { font-weight: bold; } - -/* Content Pane */ -#translationAttributionImage { - width: 70px; - cursor: pointer; -} - /* Modeless Window Dialogs */ .windowDialog, .windowDialog prefpane { diff --git a/application/palemoon/base/content/newtab/newTab.css b/application/palemoon/base/content/newtab/newTab.css index fe745d2fd..3c7cfa102 100644 --- a/application/palemoon/base/content/newtab/newTab.css +++ b/application/palemoon/base/content/newtab/newTab.css @@ -301,7 +301,6 @@ input[type=button] { color: black; border-color: hsla(220,54%,20%,.15) hsla(220,54%,20%,.17) hsla(220,54%,20%,.2); border-radius: 0 2px 2px 0; - border-inline-start: 1px solid transparent; box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset, 0 1px 0 hsla(0,0%,100%,.2); cursor: pointer; diff --git a/application/palemoon/base/content/tabbrowser.xml b/application/palemoon/base/content/tabbrowser.xml index 19e1d4659..dc6cb0a9d 100644 --- a/application/palemoon/base/content/tabbrowser.xml +++ b/application/palemoon/base/content/tabbrowser.xml @@ -2950,19 +2950,6 @@ onget="return this.mCurrentBrowser.docShell" readonly="true"/> - <property name="messageManager" - readonly="true"> - <getter> - <![CDATA[ - let frameLoader = this.mCurrentBrowser.frameLoader; - if (!frameLoader) { - return null; - } - return frameLoader.messageManager; - ]]> - </getter> - </property> - <property name="webNavigation" onget="return this.mCurrentBrowser.webNavigation" readonly="true"/> diff --git a/js/src/jit-test/tests/ion/bug1493900-1.js b/js/src/jit-test/tests/ion/bug1493900-1.js new file mode 100644 index 000000000..643c1943d --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1493900-1.js @@ -0,0 +1,17 @@ +function f() { + var objs = []; + for (var i = 0; i < 100; i++) { + objs[i] = {}; + } + var o = objs[0]; + var a = new Float64Array(1024); + function g(a, b) { + let p = b; + for (; p.x < 0; p = p.x) { + while (p === p) {} + } + for (var i = 0; i < 10000; ++i) {} + } + g(a, o); +} +f(); diff --git a/js/src/jit-test/tests/ion/bug1493900-2.js b/js/src/jit-test/tests/ion/bug1493900-2.js new file mode 100644 index 000000000..7e7f5fdec --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1493900-2.js @@ -0,0 +1,7 @@ +function f(a, b) { + for (; b.x < 0; b = b.x) { + while (b === b) {}; + } + for (var i = 0; i < 99999; ++i) {} +} +f(0, 0); diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp index 741ed1592..645aefc4f 100644 --- a/js/src/jit/BacktrackingAllocator.cpp +++ b/js/src/jit/BacktrackingAllocator.cpp @@ -1736,6 +1736,18 @@ BacktrackingAllocator::deadRange(LiveRange* range) } bool +BacktrackingAllocator::moveAtEdge(LBlock* predecessor, LBlock* successor, LiveRange* from, + LiveRange* to, LDefinition::Type type) +{ + if (successor->mir()->numPredecessors() > 1) { + MOZ_ASSERT(predecessor->mir()->numSuccessors() == 1); + return moveAtExit(predecessor, from, to, type); + } + + return moveAtEntry(successor, from, to, type); +} + +bool BacktrackingAllocator::resolveControlFlow() { // Add moves to handle changing assignments for vregs over their lifetime. @@ -1846,15 +1858,11 @@ BacktrackingAllocator::resolveControlFlow() if (!alloc().ensureBallast()) { return false; } - if (mSuccessor->numPredecessors() > 1) { - MOZ_ASSERT(predecessor->mir()->numSuccessors() == 1); - if (!moveAtExit(predecessor, from, to, def->type())) { - return false; - } - } else { - if (!moveAtEntry(successor, from, to, def->type())) { - return false; - } + + // Note: we have to use moveAtEdge both here and below (for edge + // resolution) to avoid conflicting moves. See bug 1493900. + if (!moveAtEdge(predecessor, successor, from, to, def->type())) { + return false; } } } @@ -1884,16 +1892,12 @@ BacktrackingAllocator::resolveControlFlow() if (targetRange->covers(exitOf(predecessor))) continue; - if (!alloc().ensureBallast()) + if (!alloc().ensureBallast()) { return false; + } LiveRange* from = reg.rangeFor(exitOf(predecessor), true); - if (successor->mir()->numPredecessors() > 1) { - MOZ_ASSERT(predecessor->mir()->numSuccessors() == 1); - if (!moveAtExit(predecessor, from, targetRange, reg.type())) - return false; - } else { - if (!moveAtEntry(successor, from, targetRange, reg.type())) - return false; + if (!moveAtEdge(predecessor, successor, from, targetRange, reg.type())) { + return false; } } } diff --git a/js/src/jit/BacktrackingAllocator.h b/js/src/jit/BacktrackingAllocator.h index 9910498fb..c6cf26695 100644 --- a/js/src/jit/BacktrackingAllocator.h +++ b/js/src/jit/BacktrackingAllocator.h @@ -108,8 +108,9 @@ class Requirement } MOZ_ASSERT(newRequirement.kind() == Requirement::REGISTER); - if (kind() == Requirement::FIXED) + if (kind() == Requirement::FIXED) { return allocation().isRegister(); + } *this = newRequirement; return true; @@ -353,10 +354,12 @@ class LiveRange : public TempObject // Comparator for use in range splay trees. static int compare(LiveRange* v0, LiveRange* v1) { // LiveRange includes 'from' but excludes 'to'. - if (v0->to() <= v1->from()) + if (v0->to() <= v1->from()) { return -1; - if (v0->from() >= v1->to()) + } + if (v0->from() >= v1->to()) { return 1; + } return 0; } }; @@ -642,10 +645,12 @@ class BacktrackingAllocator : protected RegisterAllocator // Comparator for use in splay tree. static int compare(CallRange* v0, CallRange* v1) { - if (v0->range.to <= v1->range.from) + if (v0->range.to <= v1->range.from) { return -1; - if (v0->range.from >= v1->range.to) + } + if (v0->range.from >= v1->range.to) { return 1; + } return 0; } }; @@ -744,36 +749,43 @@ class BacktrackingAllocator : protected RegisterAllocator MOZ_MUST_USE bool moveInput(LInstruction* ins, LiveRange* from, LiveRange* to, LDefinition::Type type) { - if (from->bundle()->allocation() == to->bundle()->allocation()) + if (from->bundle()->allocation() == to->bundle()->allocation()) { return true; + } LMoveGroup* moves = getInputMoveGroup(ins); return addMove(moves, from, to, type); } MOZ_MUST_USE bool moveAfter(LInstruction* ins, LiveRange* from, LiveRange* to, LDefinition::Type type) { - if (from->bundle()->allocation() == to->bundle()->allocation()) + if (from->bundle()->allocation() == to->bundle()->allocation()) { return true; + } LMoveGroup* moves = getMoveGroupAfter(ins); return addMove(moves, from, to, type); } MOZ_MUST_USE bool moveAtExit(LBlock* block, LiveRange* from, LiveRange* to, LDefinition::Type type) { - if (from->bundle()->allocation() == to->bundle()->allocation()) + if (from->bundle()->allocation() == to->bundle()->allocation()) { return true; + } LMoveGroup* moves = block->getExitMoveGroup(alloc()); return addMove(moves, from, to, type); } MOZ_MUST_USE bool moveAtEntry(LBlock* block, LiveRange* from, LiveRange* to, LDefinition::Type type) { - if (from->bundle()->allocation() == to->bundle()->allocation()) + if (from->bundle()->allocation() == to->bundle()->allocation()) { return true; + } LMoveGroup* moves = block->getEntryMoveGroup(alloc()); return addMove(moves, from, to, type); } + MOZ_MUST_USE bool moveAtEdge(LBlock* predecessor, LBlock* successor, LiveRange* from, + LiveRange* to, LDefinition::Type type); + // Debugging methods. void dumpAllocations(); diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index 8f117b5ab..fbd61f783 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -3714,8 +3714,11 @@ MeasuringReflow(nsIFrame* aChild, parent->Properties().Set( nsContainerFrame::DebugReflowingWithInfiniteISize(), true); #endif - uint32_t riFlags = ReflowInput::COMPUTE_SIZE_SHRINK_WRAP | - ReflowInput::COMPUTE_SIZE_USE_AUTO_BSIZE; + auto wm = aChild->GetWritingMode(); + uint32_t riFlags = ReflowInput::COMPUTE_SIZE_USE_AUTO_BSIZE; + if (aAvailableSize.ISize(wm) == INFINITE_ISIZE_COORD) { + riFlags |= ReflowInput::COMPUTE_SIZE_SHRINK_WRAP; + } if (aIMinSizeClamp != NS_MAXSIZE) { riFlags |= ReflowInput::I_CLAMP_MARGIN_BOX_MIN_SIZE; } @@ -3730,7 +3733,6 @@ MeasuringReflow(nsIFrame* aChild, ReflowOutput childSize(childRI); nsReflowStatus childStatus; const uint32_t flags = NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_SIZE_VIEW; - WritingMode wm = childRI.GetWritingMode(); parent->ReflowChild(aChild, pc, childSize, childRI, wm, LogicalPoint(wm), nsSize(), flags, childStatus); parent->FinishReflowChild(aChild, pc, childSize, &childRI, wm, diff --git a/layout/reftests/css-grid/bug1349571-ref.html b/layout/reftests/css-grid/bug1349571-ref.html new file mode 100644 index 000000000..42efd731a --- /dev/null +++ b/layout/reftests/css-grid/bug1349571-ref.html @@ -0,0 +1,90 @@ +<!DOCTYPE html>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <meta charset="utf-8">
+ <title>Testcase for bug 1349571</title>
+ <style type="text/css">
+html,body {
+ color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+
+.container {
+ display: grid;
+ grid: 250px / 500px;
+ border: 3px solid;
+ width: 500px;
+}
+
+.responsive-container {
+ background: lightgrey;
+}
+
+ </style>
+<script>try {
+(function() {
+ var target = 'blur';
+ if ( target === '' || target === '{{1}}' ) { return; }
+ var needle = 'mz_str', reText = '.?';
+ if ( needle !== '' && needle !== '{{2}}' ) {
+ reText = /^\/.+\/$/.test(needle)
+ ? needle.slice(1,-1)
+ : needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ }
+ var re = new RegExp(reText);
+ var chain = target.split('.');
+ var owner = window, prop;
+ for (;;) {
+ prop = chain.shift();
+ if ( chain.length === 0 ) { break; }
+ owner = owner[prop];
+ if ( owner instanceof Object === false ) { return; }
+ }
+ var desc = Object.getOwnPropertyDescriptor(owner, prop);
+ if ( desc && desc.get !== undefined ) { return; }
+ var magic = String.fromCharCode(Date.now() % 26 + 97) +
+ Math.floor(Math.random() * 982451653 + 982451653).toString(36);
+ var value = owner[prop];
+ var validate = function() {
+ var e = document.currentScript;
+ if ( e instanceof HTMLScriptElement && e.src === '' && re.test(e.textContent) ) {
+ throw new ReferenceError(magic);
+ }
+ };
+ Object.defineProperty(owner, prop, {
+ get: function() {
+ validate();
+ return value;
+ },
+ set: function(a) {
+ validate();
+ value = a;
+ }
+ });
+ var oe = window.onerror;
+ window.onerror = function(msg) {
+ if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) {
+ return true;
+ }
+ if ( oe instanceof Function ) {
+ return oe.apply(this, arguments);
+ }
+ }.bind();
+})();
+} catch ( e ) { }
+(function() {
+ var c = document.currentScript, p = c && c.parentNode;
+ if ( p ) { p.removeChild(c); }
+})();</script></head>
+<body>
+
+<div class="container">
+ <div class="responsive-container"></div>
+</div>
+
+
+
+</body></html>
\ No newline at end of file diff --git a/layout/reftests/css-grid/bug1349571.html b/layout/reftests/css-grid/bug1349571.html new file mode 100644 index 000000000..f836fe36e --- /dev/null +++ b/layout/reftests/css-grid/bug1349571.html @@ -0,0 +1,94 @@ +<!DOCTYPE html>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <meta charset="utf-8">
+ <title>Testcase for bug 1349571</title>
+ <style type="text/css">
+html,body {
+ color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+
+.container {
+ display: grid;
+ grid-template-columns: 1fr;
+ border: 3px solid;
+ width: 500px;
+}
+
+.responsive-container {
+ padding-bottom: 50%;
+ height: 0;
+ background: lightgrey;
+}
+
+ </style>
+<script>try {
+(function() {
+ var target = 'blur';
+ if ( target === '' || target === '{{1}}' ) { return; }
+ var needle = 'mz_str', reText = '.?';
+ if ( needle !== '' && needle !== '{{2}}' ) {
+ reText = /^\/.+\/$/.test(needle)
+ ? needle.slice(1,-1)
+ : needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ }
+ var re = new RegExp(reText);
+ var chain = target.split('.');
+ var owner = window, prop;
+ for (;;) {
+ prop = chain.shift();
+ if ( chain.length === 0 ) { break; }
+ owner = owner[prop];
+ if ( owner instanceof Object === false ) { return; }
+ }
+ var desc = Object.getOwnPropertyDescriptor(owner, prop);
+ if ( desc && desc.get !== undefined ) { return; }
+ var magic = String.fromCharCode(Date.now() % 26 + 97) +
+ Math.floor(Math.random() * 982451653 + 982451653).toString(36);
+ var value = owner[prop];
+ var validate = function() {
+ var e = document.currentScript;
+ if ( e instanceof HTMLScriptElement && e.src === '' && re.test(e.textContent) ) {
+ throw new ReferenceError(magic);
+ }
+ };
+ Object.defineProperty(owner, prop, {
+ get: function() {
+ validate();
+ return value;
+ },
+ set: function(a) {
+ validate();
+ value = a;
+ }
+ });
+ var oe = window.onerror;
+ window.onerror = function(msg) {
+ if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) {
+ return true;
+ }
+ if ( oe instanceof Function ) {
+ return oe.apply(this, arguments);
+ }
+ }.bind();
+})();
+} catch ( e ) { }
+(function() {
+ var c = document.currentScript, p = c && c.parentNode;
+ if ( p ) { p.removeChild(c); }
+})();</script></head>
+<body>
+
+<div class="container">
+ <div>
+ <div class="responsive-container"></div>
+ </div>
+</div>
+
+
+
+</body></html>
\ No newline at end of file diff --git a/layout/reftests/css-grid/bug1356820-ref.html b/layout/reftests/css-grid/bug1356820-ref.html new file mode 100644 index 000000000..b203ba203 --- /dev/null +++ b/layout/reftests/css-grid/bug1356820-ref.html @@ -0,0 +1,81 @@ +<!DOCTYPE html>
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"><script>try {
+(function() {
+ var target = 'blur';
+ if ( target === '' || target === '{{1}}' ) { return; }
+ var needle = 'mz_str', reText = '.?';
+ if ( needle !== '' && needle !== '{{2}}' ) {
+ reText = /^\/.+\/$/.test(needle)
+ ? needle.slice(1,-1)
+ : needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ }
+ var re = new RegExp(reText);
+ var chain = target.split('.');
+ var owner = window, prop;
+ for (;;) {
+ prop = chain.shift();
+ if ( chain.length === 0 ) { break; }
+ owner = owner[prop];
+ if ( owner instanceof Object === false ) { return; }
+ }
+ var desc = Object.getOwnPropertyDescriptor(owner, prop);
+ if ( desc && desc.get !== undefined ) { return; }
+ var magic = String.fromCharCode(Date.now() % 26 + 97) +
+ Math.floor(Math.random() * 982451653 + 982451653).toString(36);
+ var value = owner[prop];
+ var validate = function() {
+ var e = document.currentScript;
+ if ( e instanceof HTMLScriptElement && e.src === '' && re.test(e.textContent) ) {
+ throw new ReferenceError(magic);
+ }
+ };
+ Object.defineProperty(owner, prop, {
+ get: function() {
+ validate();
+ return value;
+ },
+ set: function(a) {
+ validate();
+ value = a;
+ }
+ });
+ var oe = window.onerror;
+ window.onerror = function(msg) {
+ if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) {
+ return true;
+ }
+ if ( oe instanceof Function ) {
+ return oe.apply(this, arguments);
+ }
+ }.bind();
+})();
+} catch ( e ) { }
+(function() {
+ var c = document.currentScript, p = c && c.parentNode;
+ if ( p ) { p.removeChild(c); }
+})();</script></head><body><div style="display: grid; width: 5em;">
+ <div style="word-wrap: break-word; width: 5em; justify-self:start">
+ first item with a longlonglongword
+ </div>
+ <div>
+ second item
+ </div>
+</div>
+<div style="display: grid; width: 5em;">
+ <div style="width: 5em; justify-self:start">
+ first item with a longlonglongword
+ </div>
+ <div>
+ second item
+ </div>
+</div>
+<div style="display: grid; width: 5em;">
+ <div style="word-wrap: break-word; writing-mode:vertical-lr; justify-self:start">
+ first item with a longlonglongword
+ </div>
+ <div>
+ second item
+ </div>
+</div>
+</body></html>
\ No newline at end of file diff --git a/layout/reftests/css-grid/bug1356820.html b/layout/reftests/css-grid/bug1356820.html new file mode 100644 index 000000000..2f2f36014 --- /dev/null +++ b/layout/reftests/css-grid/bug1356820.html @@ -0,0 +1,81 @@ +<!DOCTYPE html>
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8"><script>try {
+(function() {
+ var target = 'blur';
+ if ( target === '' || target === '{{1}}' ) { return; }
+ var needle = 'mz_str', reText = '.?';
+ if ( needle !== '' && needle !== '{{2}}' ) {
+ reText = /^\/.+\/$/.test(needle)
+ ? needle.slice(1,-1)
+ : needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ }
+ var re = new RegExp(reText);
+ var chain = target.split('.');
+ var owner = window, prop;
+ for (;;) {
+ prop = chain.shift();
+ if ( chain.length === 0 ) { break; }
+ owner = owner[prop];
+ if ( owner instanceof Object === false ) { return; }
+ }
+ var desc = Object.getOwnPropertyDescriptor(owner, prop);
+ if ( desc && desc.get !== undefined ) { return; }
+ var magic = String.fromCharCode(Date.now() % 26 + 97) +
+ Math.floor(Math.random() * 982451653 + 982451653).toString(36);
+ var value = owner[prop];
+ var validate = function() {
+ var e = document.currentScript;
+ if ( e instanceof HTMLScriptElement && e.src === '' && re.test(e.textContent) ) {
+ throw new ReferenceError(magic);
+ }
+ };
+ Object.defineProperty(owner, prop, {
+ get: function() {
+ validate();
+ return value;
+ },
+ set: function(a) {
+ validate();
+ value = a;
+ }
+ });
+ var oe = window.onerror;
+ window.onerror = function(msg) {
+ if ( typeof msg === 'string' && msg.indexOf(magic) !== -1 ) {
+ return true;
+ }
+ if ( oe instanceof Function ) {
+ return oe.apply(this, arguments);
+ }
+ }.bind();
+})();
+} catch ( e ) { }
+(function() {
+ var c = document.currentScript, p = c && c.parentNode;
+ if ( p ) { p.removeChild(c); }
+})();</script></head><body><div style="display: grid; width: 5em;">
+ <div style="word-wrap: break-word; min-width: 0;">
+ first item with a longlonglongword
+ </div>
+ <div>
+ second item
+ </div>
+</div>
+<div style="display: grid; width: 5em;">
+ <div style="min-width: 0;">
+ first item with a longlonglongword
+ </div>
+ <div>
+ second item
+ </div>
+</div>
+<div style="display: grid; width: 5em;">
+ <div style="word-wrap: break-word; min-height: 0; writing-mode:vertical-lr">
+ first item with a longlonglongword
+ </div>
+ <div>
+ second item
+ </div>
+</div>
+</body></html>
\ No newline at end of file diff --git a/layout/reftests/css-grid/reftest.list b/layout/reftests/css-grid/reftest.list index c2ee57c1a..3087ca49b 100644 --- a/layout/reftests/css-grid/reftest.list +++ b/layout/reftests/css-grid/reftest.list @@ -280,3 +280,5 @@ asserts(1-10) == grid-fragmentation-dyn4-021.html grid-fragmentation-021-ref.htm == grid-fragmentation-dyn2-031.html grid-fragmentation-031-ref.html == bug1306106.html bug1306106-ref.html == grid-percent-intrinsic-sizing-001.html grid-percent-intrinsic-sizing-001-ref.html +== bug1349571.html bug1349571-ref.html +== bug1356820.html bug1356820-ref.html diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp index e13541acf..435294315 100644 --- a/netwerk/base/nsIOService.cpp +++ b/netwerk/base/nsIOService.cpp @@ -789,7 +789,9 @@ nsIOService::NewChannelFromURIWithProxyFlagsInternal(nsIURI* aURI, // creating a new channel by calling NewChannel(). if (NS_FAILED(rv)) { rv = handler->NewChannel(aURI, getter_AddRefs(channel)); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(rv)) { + return rv; + } // The protocol handler does not implement NewChannel2, so // maybe we need to wrap the channel (see comment in MaybeWrap // function). diff --git a/netwerk/base/nsNetUtilInlines.h b/netwerk/base/nsNetUtilInlines.h index 7003814d5..b831ec2e7 100644 --- a/netwerk/base/nsNetUtilInlines.h +++ b/netwerk/base/nsNetUtilInlines.h @@ -224,7 +224,9 @@ NS_NewChannelInternal(nsIChannel **outChannel, aUri, aLoadInfo, getter_AddRefs(channel)); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(rv)) { + return rv; + } if (aLoadGroup) { rv = channel->SetLoadGroup(aLoadGroup); diff --git a/testing/profiles/prefs_general.js b/testing/profiles/prefs_general.js index 515828d29..bf1534c12 100644 --- a/testing/profiles/prefs_general.js +++ b/testing/profiles/prefs_general.js @@ -284,12 +284,6 @@ user_pref("apz.content_response_timeout", 60000); // Make sure SSL Error reports don't hit the network user_pref("security.ssl.errorReporting.url", "https://example.com/browser/browser/base/content/test/general/ssl_error_reports.sjs?succeed"); -// Make sure Translation won't hit the network. -user_pref("browser.translation.bing.authURL", "http://%(server)s/browser/browser/components/translation/test/bing.sjs"); -user_pref("browser.translation.bing.translateArrayURL", "http://%(server)s/browser/browser/components/translation/test/bing.sjs"); -user_pref("browser.translation.yandex.translateURLOverride", "http://%(server)s/browser/browser/components/translation/test/yandex.sjs"); -user_pref("browser.translation.engine", "bing"); - // Make sure we don't try to load snippets from the network. user_pref("browser.aboutHomeSnippets.updateUrl", "nonexistent://test"); diff --git a/toolkit/components/reader/AboutReader.jsm b/toolkit/components/reader/AboutReader.jsm index c5d04476d..6ec959eba 100644 --- a/toolkit/components/reader/AboutReader.jsm +++ b/toolkit/components/reader/AboutReader.jsm @@ -14,7 +14,6 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "AsyncPrefs", "resource://gre/modules/AsyncPrefs.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NarrateControls", "resource://gre/modules/narrate/NarrateControls.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Rect", "resource://gre/modules/Geometry.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); diff --git a/toolkit/components/reader/JSDOMParser.js b/toolkit/components/reader/JSDOMParser.js index dd9d37230..debdb08eb 100644 --- a/toolkit/components/reader/JSDOMParser.js +++ b/toolkit/components/reader/JSDOMParser.js @@ -618,6 +618,13 @@ }; var Element = function (tag) { + // We use this to find the closing tag. + this._matchingTag = tag; + // We're explicitly a non-namespace aware parser, we just pretend it's all HTML. + var lastColonIndex = tag.lastIndexOf(":"); + if (lastColonIndex != -1) { + tag = tag.substring(lastColonIndex + 1); + } this.attributes = []; this.childNodes = []; this.children = []; @@ -785,7 +792,13 @@ break; } } - } + }, + + hasAttribute: function (name) { + return this.attributes.some(function (attr) { + return attr.name == name; + }); + }, }; var Style = function (node) { @@ -1062,9 +1075,10 @@ return null; // Read any text as Text node + var textNode; if (c !== "<") { --this.currentChar; - var textNode = new Text(); + textNode = new Text(); var n = this.html.indexOf("<", this.currentChar); if (n === -1) { textNode.innerHTML = this.html.substring(this.currentChar, this.html.length); @@ -1076,6 +1090,18 @@ return textNode; } + if (this.match("![CDATA[")) { + var endChar = this.html.indexOf("]]>", this.currentChar); + if (endChar === -1) { + this.error("unclosed CDATA section"); + return null; + } + textNode = new Text(); + textNode.textContent = this.html.substring(this.currentChar, endChar); + this.currentChar = endChar + ("]]>").length; + return textNode; + } + c = this.peekNext(); // Read Comment node. Normally, Comment nodes know their inner @@ -1107,7 +1133,7 @@ // If this isn't a void Element, read its child nodes if (!closed) { this.readChildren(node); - var closingTag = "</" + localName + ">"; + var closingTag = "</" + node._matchingTag + ">"; if (!this.match(closingTag)) { this.error("expected '" + closingTag + "' and got " + this.html.substr(this.currentChar, closingTag.length)); return null; diff --git a/toolkit/components/reader/Readability.js b/toolkit/components/reader/Readability.js index 064d2ae88..c2bba0cd3 100644 --- a/toolkit/components/reader/Readability.js +++ b/toolkit/components/reader/Readability.js @@ -29,14 +29,19 @@ /** * Public constructor. - * @param {Object} uri The URI descriptor object. * @param {HTMLDocument} doc The document to parse. * @param {Object} options The options object. */ -function Readability(uri, doc, options) { +function Readability(doc, options) { + // In some older versions, people passed a URI as the first argument. Cope: + if (options && options.documentElement) { + doc = options; + options = arguments[2]; + } else if (!doc || !doc.documentElement) { + throw new Error("First argument to Readability constructor should be a document object."); + } options = options || {}; - this._uri = uri; this._doc = doc; this._articleTitle = null; this._articleByline = null; @@ -47,7 +52,7 @@ function Readability(uri, doc, options) { this._debug = !!options.debug; this._maxElemsToParse = options.maxElemsToParse || this.DEFAULT_MAX_ELEMS_TO_PARSE; this._nbTopCandidates = options.nbTopCandidates || this.DEFAULT_N_TOP_CANDIDATES; - this._wordThreshold = options.wordThreshold || this.DEFAULT_WORD_THRESHOLD; + this._charThreshold = options.charThreshold || this.DEFAULT_CHAR_THRESHOLD; this._classesToPreserve = this.CLASSES_TO_PRESERVE.concat(options.classesToPreserve || []); // Start with all flags set @@ -93,6 +98,10 @@ Readability.prototype = { FLAG_WEIGHT_CLASSES: 0x2, FLAG_CLEAN_CONDITIONALLY: 0x4, + // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType + ELEMENT_NODE: 1, + TEXT_NODE: 3, + // Max number of nodes supported by this parser. Default: 0 (no limit) DEFAULT_MAX_ELEMS_TO_PARSE: 0, @@ -103,13 +112,13 @@ Readability.prototype = { // Element tags to score by default. DEFAULT_TAGS_TO_SCORE: "section,h2,h3,h4,h5,h6,p,td,pre".toUpperCase().split(","), - // The default number of words an article must have in order to return a result - DEFAULT_WORD_THRESHOLD: 500, + // The default number of chars an article must have in order to return a result + DEFAULT_CHAR_THRESHOLD: 500, // All of the regular expressions in use within readability. // Defined up here so we don't instantiate them repeatedly in loops. REGEXPS: { - unlikelyCandidates: /banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|foot|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i, + unlikelyCandidates: /-ad-|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|foot|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i, okMaybeItsACandidate: /and|article|body|column|main|shadow/i, positive: /article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story/i, negative: /hidden|^hid$| hid$| hid |^hid |banner|combx|comment|com-|contact|foot|footer|footnote|masthead|media|meta|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|tool|widget/i, @@ -132,8 +141,19 @@ Readability.prototype = { DEPRECATED_SIZE_ATTRIBUTE_ELEMS: [ "TABLE", "TH", "TD", "HR", "PRE" ], + // The commented out elements qualify as phrasing content but tend to be + // removed by readability when put into paragraphs, so we ignore them here. + PHRASING_ELEMS: [ + // "CANVAS", "IFRAME", "SVG", "VIDEO", + "ABBR", "AUDIO", "B", "BDO", "BR", "BUTTON", "CITE", "CODE", "DATA", + "DATALIST", "DFN", "EM", "EMBED", "I", "IMG", "INPUT", "KBD", "LABEL", + "MARK", "MATH", "METER", "NOSCRIPT", "OBJECT", "OUTPUT", "PROGRESS", "Q", + "RUBY", "SAMP", "SCRIPT", "SELECT", "SMALL", "SPAN", "STRONG", "SUB", + "SUP", "TEXTAREA", "TIME", "VAR", "WBR" + ], + // These are the classes that readability sets itself. - CLASSES_TO_PRESERVE: [ "readability-styled", "page" ], + CLASSES_TO_PRESERVE: [ "page" ], /** * Run any post-process modifications to article content as necessary. @@ -216,6 +236,21 @@ Readability.prototype = { }, /** + * Iterate over a NodeList, return true if all of the provided iterate + * function calls return true, false otherwise. + * + * For convenience, the current object context is applied to the + * provided iterate function. + * + * @param NodeList nodeList The NodeList. + * @param Function fn The iterate function. + * @return Boolean + */ + _everyNode: function(nodeList, fn) { + return Array.prototype.every.call(nodeList, fn, this); + }, + + /** * Concat all nodelists passed as arguments. * * @return ...NodeList @@ -327,7 +362,7 @@ Readability.prototype = { var origTitle = ""; try { - curTitle = origTitle = doc.title; + curTitle = origTitle = doc.title.trim(); // If they had an element with id "title" in their HTML if (typeof curTitle !== "string") @@ -355,8 +390,9 @@ Readability.prototype = { doc.getElementsByTagName('h1'), doc.getElementsByTagName('h2') ); + var trimmedTitle = curTitle.trim(); var match = this._someNode(headings, function(heading) { - return heading.textContent === curTitle; + return heading.textContent.trim() === trimmedTitle; }); // If we don't, let's extract the title out of the original title string. @@ -421,7 +457,7 @@ Readability.prototype = { _nextElement: function (node) { var next = node; while (next - && (next.nodeType != Node.ELEMENT_NODE) + && (next.nodeType != this.ELEMENT_NODE) && this.REGEXPS.whitespace.test(next.textContent)) { next = next.nextSibling; } @@ -464,16 +500,22 @@ Readability.prototype = { while (next) { // If we've hit another <br><br>, we're done adding children to this <p>. if (next.tagName == "BR") { - var nextElem = this._nextElement(next); + var nextElem = this._nextElement(next.nextSibling); if (nextElem && nextElem.tagName == "BR") break; } + if (!this._isPhrasingContent(next)) break; + // Otherwise, make this node a child of the new <p>. var sibling = next.nextSibling; p.appendChild(next); next = sibling; } + + while (p.lastChild && this._isWhitespace(p.lastChild)) p.removeChild(p.lastChild); + + if (p.parentNode.tagName === "P") this._setNodeTag(p.parentNode, "DIV"); } }); }, @@ -523,6 +565,7 @@ Readability.prototype = { this._clean(articleContent, "h1"); this._clean(articleContent, "footer"); this._clean(articleContent, "link"); + this._clean(articleContent, "aside"); // Clean out elements have "share" in their id/class combinations from final top candidates, // which means we don't remove the top candidates even they have "share". @@ -579,6 +622,19 @@ Readability.prototype = { if (next && next.tagName == "P") br.parentNode.removeChild(br); }); + + // Remove single-cell tables + this._forEachNode(this._getAllNodesWithTag(articleContent, ["table"]), function(table) { + var tbody = this._hasSingleTagInsideElement(table, "TBODY") ? table.firstElementChild : table; + if (this._hasSingleTagInsideElement(tbody, "TR")) { + var row = tbody.firstElementChild; + if (this._hasSingleTagInsideElement(row, "TD")) { + var cell = row.firstElementChild; + cell = this._setNodeTag(cell, this._everyNode(cell.childNodes, this._isPhrasingContent) ? "P" : "DIV"); + table.parentNode.replaceChild(cell, table); + } + } + }); }, /** @@ -658,37 +714,6 @@ Readability.prototype = { return node && node.nextElementSibling; }, - /** - * Like _getNextNode, but for DOM implementations with no - * firstElementChild/nextElementSibling functionality... - */ - _getNextNodeNoElementProperties: function(node, ignoreSelfAndKids) { - function nextSiblingEl(n) { - do { - n = n.nextSibling; - } while (n && n.nodeType !== n.ELEMENT_NODE); - return n; - } - // First check for kids if those aren't being ignored - if (!ignoreSelfAndKids && node.children[0]) { - return node.children[0]; - } - // Then for siblings... - var next = nextSiblingEl(node); - if (next) { - return next; - } - // And finally, move up the parent chain *and* find a sibling - // (because this is depth-first traversal, we will have already - // seen the parent nodes themselves). - do { - node = node.parentNode; - if (node) - next = nextSiblingEl(node); - } while (node && !next); - return node && next; - }, - _checkByline: function(node, matchString) { if (this._articleByline) { return false; @@ -751,6 +776,12 @@ Readability.prototype = { while (node) { var matchString = node.className + " " + node.id; + if (!this._isProbablyVisible(node)) { + this.log("Removing hidden node - " + matchString); + node = this._removeAndGetNext(node); + continue; + } + // Check to see if this node is a byline, and remove it if it is. if (this._checkByline(node, matchString)) { node = this._removeAndGetNext(node); @@ -784,11 +815,31 @@ Readability.prototype = { // Turn all divs that don't have children block level elements into p's if (node.tagName === "DIV") { + // Put phrasing content into paragraphs. + var p = null; + var childNode = node.firstChild; + while (childNode) { + var nextSibling = childNode.nextSibling; + if (this._isPhrasingContent(childNode)) { + if (p !== null) { + p.appendChild(childNode); + } else if (!this._isWhitespace(childNode)) { + p = doc.createElement('p'); + node.replaceChild(p, childNode); + p.appendChild(childNode); + } + } else if (p !== null) { + while (p.lastChild && this._isWhitespace(p.lastChild)) p.removeChild(p.lastChild); + p = null; + } + childNode = nextSibling; + } + // Sites like http://mobile.slate.com encloses each paragraph with a DIV // element. DIVs with only a P element inside and no text content can be // safely converted into plain P elements to avoid confusing the scoring // algorithm with DIVs with are, in practice, paragraphs. - if (this._hasSinglePInsideElement(node)) { + if (this._hasSingleTagInsideElement(node, "P") && this._getLinkDensity(node) < 0.25) { var newNode = node.children[0]; node.parentNode.replaceChild(newNode, node); node = newNode; @@ -796,17 +847,6 @@ Readability.prototype = { } else if (!this._hasChildBlockElement(node)) { node = this._setNodeTag(node, "P"); elementsToScore.push(node); - } else { - // EXPERIMENTAL - this._forEachNode(node.childNodes, function(childNode) { - if (childNode.nodeType === Node.TEXT_NODE && childNode.textContent.trim().length > 0) { - var p = doc.createElement('p'); - p.textContent = childNode.textContent; - p.style.display = 'inline'; - p.className = 'readability-styled'; - node.replaceChild(p, childNode); - } - }); } } node = this._getNextNode(node); @@ -846,7 +886,7 @@ Readability.prototype = { // Initialize and score ancestors. this._forEachNode(ancestors, function(ancestor, level) { - if (!ancestor.tagName) + if (!ancestor.tagName || !ancestor.parentNode || typeof(ancestor.parentNode.tagName) === 'undefined') return; if (typeof(ancestor.readability) === 'undefined') { @@ -1085,7 +1125,7 @@ Readability.prototype = { // finding the content, and the sieve approach gives us a higher likelihood of // finding the -right- content. var textLength = this._getInnerText(articleContent, true).length; - if (textLength < this._wordThreshold) { + if (textLength < this._charThreshold) { parseSuccessful = false; page.innerHTML = pageCacheHtml; @@ -1233,27 +1273,28 @@ Readability.prototype = { }, /** - * Check if this node has only whitespace and a single P element + * Check if this node has only whitespace and a single element with given tag * Returns false if the DIV node contains non-empty text nodes - * or if it contains no P or more than 1 element. + * or if it contains no element with given tag or more than 1 element. * * @param Element + * @param string tag of child element **/ - _hasSinglePInsideElement: function(element) { - // There should be exactly 1 element child which is a P: - if (element.children.length != 1 || element.children[0].tagName !== "P") { + _hasSingleTagInsideElement: function(element, tag) { + // There should be exactly 1 element child with given tag + if (element.children.length != 1 || element.children[0].tagName !== tag) { return false; } // And there should be no text nodes with real content return !this._someNode(element.childNodes, function(node) { - return node.nodeType === Node.TEXT_NODE && + return node.nodeType === this.TEXT_NODE && this.REGEXPS.hasContent.test(node.textContent); }); }, _isElementWithoutContent: function(node) { - return node.nodeType === Node.ELEMENT_NODE && + return node.nodeType === this.ELEMENT_NODE && node.textContent.trim().length == 0 && (node.children.length == 0 || node.children.length == node.getElementsByTagName("br").length + node.getElementsByTagName("hr").length); @@ -1271,6 +1312,21 @@ Readability.prototype = { }); }, + /*** + * Determine if a node qualifies as phrasing content. + * https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content + **/ + _isPhrasingContent: function(node) { + return node.nodeType === this.TEXT_NODE || this.PHRASING_ELEMS.indexOf(node.tagName) !== -1 || + ((node.tagName === "A" || node.tagName === "DEL" || node.tagName === "INS") && + this._everyNode(node.childNodes, this._isPhrasingContent)); + }, + + _isWhitespace: function(node) { + return (node.nodeType === this.TEXT_NODE && node.textContent.trim().length === 0) || + (node.nodeType === this.ELEMENT_NODE && node.tagName === "BR"); + }, + /** * Get the inner text of a node - cross browser compatibly. * This also strips out any excess whitespace to be found. @@ -1312,16 +1368,14 @@ Readability.prototype = { if (!e || e.tagName.toLowerCase() === 'svg') return; - if (e.className !== 'readability-styled') { - // Remove `style` and deprecated presentational attributes - for (var i = 0; i < this.PRESENTATIONAL_ATTRIBUTES.length; i++) { - e.removeAttribute(this.PRESENTATIONAL_ATTRIBUTES[i]); - } + // Remove `style` and deprecated presentational attributes + for (var i = 0; i < this.PRESENTATIONAL_ATTRIBUTES.length; i++) { + e.removeAttribute(this.PRESENTATIONAL_ATTRIBUTES[i]); + } - if (this.DEPRECATED_SIZE_ATTRIBUTE_ELEMS.indexOf(e.tagName) !== -1) { - e.removeAttribute('width'); - e.removeAttribute('height'); - } + if (this.DEPRECATED_SIZE_ATTRIBUTE_ELEMS.indexOf(e.tagName) !== -1) { + e.removeAttribute('width'); + e.removeAttribute('height'); } var cur = e.firstElementChild; @@ -1639,6 +1693,10 @@ Readability.prototype = { this._flags = this._flags & ~flag; }, + _isProbablyVisible: function(node) { + return node.style.display != "none" && !node.hasAttribute("hidden"); + }, + /** * Decides whether or not the document is reader-able without parsing the whole thing. * @@ -1663,9 +1721,9 @@ Readability.prototype = { nodes = [].concat.apply(Array.from(set), nodes); } - // FIXME we should have a fallback for helperIsVisible, but this is - // problematic because of jsdom's elem.style handling - see - // https://github.com/mozilla/readability/pull/186 for context. + if (!helperIsVisible) { + helperIsVisible = this._isProbablyVisible; + } var score = 0; // This is a little cheeky, we use the accumulator 'score' to decide what to return from @@ -1719,9 +1777,6 @@ Readability.prototype = { } } - if (typeof this._doc.documentElement.firstElementChild === "undefined") { - this._getNextNode = this._getNextNodeNoElementProperties; - } // Remove script tags from the document. this._removeScripts(this._doc); @@ -1750,7 +1805,6 @@ Readability.prototype = { var textContent = articleContent.textContent; return { - uri: this._uri, title: this._articleTitle, byline: metadata.byline || this._articleByline, dir: this._articleDir, diff --git a/toolkit/components/reader/ReaderMode.jsm b/toolkit/components/reader/ReaderMode.jsm index e9eb83154..218e12d60 100644 --- a/toolkit/components/reader/ReaderMode.jsm +++ b/toolkit/components/reader/ReaderMode.jsm @@ -195,7 +195,7 @@ this.ReaderMode = { // We pass in a helper function to determine if a node is visible, because // it uses gecko APIs that the engine-agnostic readability code can't rely // upon. - return new Readability(uri, doc).isProbablyReaderable(this.isNodeVisible.bind(this, utils)); + return new Readability(doc).isProbablyReaderable(this.isNodeVisible.bind(this, utils)); }, isNodeVisible(utils, node) { diff --git a/toolkit/components/reader/ReaderWorker.js b/toolkit/components/reader/ReaderWorker.js index 69426788b..9cc684e9b 100644 --- a/toolkit/components/reader/ReaderWorker.js +++ b/toolkit/components/reader/ReaderWorker.js @@ -48,6 +48,6 @@ var Agent = { */ parseDocument(uri, serializedDoc, options) { let doc = new JSDOMParser().parse(serializedDoc, uri.spec); - return new Readability(uri, doc, options).parse(); + return new Readability(doc, options).parse(); }, }; diff --git a/toolkit/components/thumbnails/test/test_thumbnails_interfaces.js b/toolkit/components/thumbnails/test/test_thumbnails_interfaces.js index 8272b2e06..a8fe51418 100644 --- a/toolkit/components/thumbnails/test/test_thumbnails_interfaces.js +++ b/toolkit/components/thumbnails/test/test_thumbnails_interfaces.js @@ -22,10 +22,4 @@ function run_test() { null, null); ok(uri instanceof Ci.nsIFileURL, "moz-page-thumb:// is a FileURL"); ok(uri.file, "This moz-page-thumb:// object is backed by a file"); - - // and check that the error case works as specified - let bad = Services.io.newURI("moz-page-thumb://wronghost/?url=http%3A%2F%2Fwww.mozilla.org%2F", - null, null); - Assert.throws(() => handler.resolveURI(bad), /NS_ERROR_NOT_AVAILABLE/i, - "moz-page-thumb object with wrong host must not resolve to a file path"); } diff --git a/toolkit/content/widgets/browser.xml b/toolkit/content/widgets/browser.xml index e595c847d..a30ff1c43 100644 --- a/toolkit/content/widgets/browser.xml +++ b/toolkit/content/widgets/browser.xml @@ -389,10 +389,11 @@ <method name="getTabBrowser"> <body> <![CDATA[ - var tabBrowser = this.parentNode; - while (tabBrowser && tabBrowser.localName != "tabbrowser") - tabBrowser = tabBrowser.parentNode; - return tabBrowser; + for (let node = this.parentNode; node instanceof Element; node = node.parentNode) { + if (node.localName == "tabbrowser") + return node; + } + return null; ]]> </body> </method> diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm index d453a8981..4cd2c3d0a 100644 --- a/toolkit/mozapps/extensions/AddonManager.jsm +++ b/toolkit/mozapps/extensions/AddonManager.jsm @@ -123,13 +123,13 @@ function providerName(aProvider) { * parent 'addons' level logger accordingly. */ var PrefObserver = { - init: function PrefObserver_init() { + init: function() { Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false); Services.obs.addObserver(this, "xpcom-shutdown", false); this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED); }, - observe: function PrefObserver_observe(aSubject, aTopic, aData) { + observe: function(aSubject, aTopic, aData) { if (aTopic == "xpcom-shutdown") { Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this); Services.obs.removeObserver(this, "xpcom-shutdown"); @@ -323,7 +323,7 @@ AsyncObjectCaller.prototype = { * Passes the next object to the listener or calls noMoreObjects if there * are none left. */ - callNext: function AOC_callNext() { + callNext: function() { if (this.objects.length == 0) { this.listener.noMoreObjects(this); return; @@ -449,7 +449,7 @@ AddonAuthor.prototype = { url: null, // Returns the author's name, defaulting to the empty string - toString: function AddonAuthor_toString() { + toString: function() { return this.name || ""; } } @@ -493,7 +493,7 @@ AddonScreenshot.prototype = { caption: null, // Returns the screenshot URL, defaulting to the empty string - toString: function AddonScreenshot_toString() { + toString: function() { return this.url || ""; } } @@ -640,11 +640,11 @@ var AddonManagerInternal = { // Store telemetry details per addon provider telemetryDetails: {}, - recordTimestamp: function AMI_recordTimestamp(name, value) { + recordTimestamp: function(name, value) { this.TelemetryTimestamps.add(name, value); }, - validateBlocklist: function AMI_validateBlocklist() { + validateBlocklist: function() { let appBlocklist = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]); // If there is no application shipped blocklist then there is nothing to do @@ -771,7 +771,7 @@ var AddonManagerInternal = { * Initializes the AddonManager, loading any known providers and initializing * them. */ - startup: function AMI_startup() { + startup: function() { try { if (gStarted) return; @@ -925,7 +925,7 @@ var AddonManagerInternal = { * @param aTypes * An optional array of add-on types */ - registerProvider: function AMI_registerProvider(aProvider, aTypes) { + registerProvider: function(aProvider, aTypes) { if (!aProvider || typeof aProvider != "object") throw Components.Exception("aProvider must be specified", Cr.NS_ERROR_INVALID_ARG); @@ -977,7 +977,7 @@ var AddonManagerInternal = { * For providers that have async shutdown methods returning Promises, * the caller should wait for that Promise to resolve. */ - unregisterProvider: function AMI_unregisterProvider(aProvider) { + unregisterProvider: function(aProvider) { if (!aProvider || typeof aProvider != "object") throw Components.Exception("aProvider must be specified", Cr.NS_ERROR_INVALID_ARG); @@ -1032,7 +1032,7 @@ var AddonManagerInternal = { * * @param aProvider Provider object to mark safe */ - markProviderSafe: function AMI_markProviderSafe(aProvider) { + markProviderSafe: function(aProvider) { if (!gStarted) { throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -1062,7 +1062,7 @@ var AddonManagerInternal = { * The method name to call * @see callProvider */ - callProviders: function AMI_callProviders(aMethod, ...aArgs) { + callProviders: function(aMethod, ...aArgs) { if (!aMethod || typeof aMethod != "string") throw Components.Exception("aMethod must be a non-empty string", Cr.NS_ERROR_INVALID_ARG); @@ -1163,7 +1163,7 @@ var AddonManagerInternal = { * * @see nsIObserver */ - observe: function AMI_observe(aSubject, aTopic, aData) { + observe: function(aSubject, aTopic, aData) { switch (aData) { case PREF_EM_CHECK_COMPATIBILITY: { let oldValue = gCheckCompatibility; @@ -1247,7 +1247,7 @@ var AddonManagerInternal = { * The optional application version to use for %APP_VERSION% * @return The appropriately escaped URI. */ - escapeAddonURI: function AMI_escapeAddonURI(aAddon, aUri, aAppVersion) + escapeAddonURI: function(aAddon, aUri, aAppVersion) { if (!aAddon || typeof aAddon != "object") throw Components.Exception("aAddon must be an Addon object", @@ -1318,7 +1318,7 @@ var AddonManagerInternal = { * @return Promise{null} Resolves when the background update check is complete * (the resulting addon installations may still be in progress). */ - backgroundUpdateCheck: function AMI_backgroundUpdateCheck() { + backgroundUpdateCheck: function() { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -1347,7 +1347,7 @@ var AddonManagerInternal = { // be applied updates.push(new Promise((resolve, reject) => { addon.findUpdates({ - onUpdateAvailable: function BUC_onUpdateAvailable(aAddon, aInstall) { + onUpdateAvailable: function(aAddon, aInstall) { // Start installing updates when the add-on can be updated and // background updates should be applied. logger.debug("Found update for add-on ${id}", aAddon); @@ -1389,7 +1389,7 @@ var AddonManagerInternal = { * @param aID * The ID of the add-on */ - addStartupChange: function AMI_addStartupChange(aType, aID) { + addStartupChange: function(aType, aID) { if (!aType || typeof aType != "string") throw Components.Exception("aType must be a non-empty string", Cr.NS_ERROR_INVALID_ARG); @@ -1418,7 +1418,7 @@ var AddonManagerInternal = { * @param aID * The ID of the add-on */ - removeStartupChange: function AMI_removeStartupChange(aType, aID) { + removeStartupChange: function(aType, aID) { if (!aType || typeof aType != "string") throw Components.Exception("aType must be a non-empty string", Cr.NS_ERROR_INVALID_ARG); @@ -1444,7 +1444,7 @@ var AddonManagerInternal = { * @param aMethod * The method on the listeners to call */ - callManagerListeners: function AMI_callManagerListeners(aMethod, ...aArgs) { + callManagerListeners: function(aMethod, ...aArgs) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -1475,7 +1475,7 @@ var AddonManagerInternal = { * An optional array of extra InstallListeners to also call * @return false if any of the listeners returned false, true otherwise */ - callInstallListeners: function AMI_callInstallListeners(aMethod, + callInstallListeners: function(aMethod, aExtraListeners, ...aArgs) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", @@ -1517,7 +1517,7 @@ var AddonManagerInternal = { * @param aMethod * The method on the listeners to call */ - callAddonListeners: function AMI_callAddonListeners(aMethod, ...aArgs) { + callAddonListeners: function(aMethod, ...aArgs) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -1551,7 +1551,7 @@ var AddonManagerInternal = { * A boolean indicating if the change will only take place the next * time the application is restarted */ - notifyAddonChanged: function AMI_notifyAddonChanged(aID, aType, aPendingRestart) { + notifyAddonChanged: function(aID, aType, aPendingRestart) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -1588,7 +1588,7 @@ var AddonManagerInternal = { * their add-ons in response to an application change such as a blocklist * update. */ - updateAddonAppDisabledStates: function AMI_updateAddonAppDisabledStates() { + updateAddonAppDisabledStates: function() { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -1603,7 +1603,7 @@ var AddonManagerInternal = { * @param aCallback * Function to call when operation is complete. */ - updateAddonRepositoryData: function AMI_updateAddonRepositoryData(aCallback) { + updateAddonRepositoryData: function(aCallback) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -1613,11 +1613,11 @@ var AddonManagerInternal = { Cr.NS_ERROR_INVALID_ARG); new AsyncObjectCaller(this.providers, "updateAddonRepositoryData", { - nextObject: function updateAddonRepositoryData_nextObject(aCaller, aProvider) { + nextObject: function(aCaller, aProvider) { callProviderAsync(aProvider, "updateAddonRepositoryData", aCaller.callNext.bind(aCaller)); }, - noMoreObjects: function updateAddonRepositoryData_noMoreObjects(aCaller) { + noMoreObjects: function(aCaller) { safeCall(aCallback); // only tests should care about this Services.obs.notifyObservers(null, "TEST:addon-repository-data-updated", null); @@ -1646,7 +1646,7 @@ var AddonManagerInternal = { * An optional nsILoadGroup to associate any network requests with * @throws if the aUrl, aCallback or aMimetype arguments are not specified */ - getInstallForURL: function AMI_getInstallForURL(aUrl, aCallback, aMimetype, + getInstallForURL: function(aUrl, aCallback, aMimetype, aHash, aName, aIcons, aVersion, aBrowser) { if (!gStarted) @@ -1716,7 +1716,7 @@ var AddonManagerInternal = { * An optional mimetype hint for the add-on * @throws if the aFile or aCallback arguments are not specified */ - getInstallForFile: function AMI_getInstallForFile(aFile, aCallback, aMimetype) { + getInstallForFile: function(aFile, aCallback, aMimetype) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -1734,7 +1734,7 @@ var AddonManagerInternal = { Cr.NS_ERROR_INVALID_ARG); new AsyncObjectCaller(this.providers, "getInstallForFile", { - nextObject: function getInstallForFile_nextObject(aCaller, aProvider) { + nextObject: function(aCaller, aProvider) { callProviderAsync(aProvider, "getInstallForFile", aFile, function getInstallForFile_safeCall(aInstall) { if (aInstall) @@ -1744,7 +1744,7 @@ var AddonManagerInternal = { }); }, - noMoreObjects: function getInstallForFile_noMoreObjects(aCaller) { + noMoreObjects: function(aCaller) { safeCall(aCallback, null); } }); @@ -1760,7 +1760,7 @@ var AddonManagerInternal = { * A callback which will be passed an array of AddonInstalls * @throws If the aCallback argument is not specified */ - getInstallsByTypes: function AMI_getInstallsByTypes(aTypes, aCallback) { + getInstallsByTypes: function(aTypes, aCallback) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -1776,7 +1776,7 @@ var AddonManagerInternal = { let installs = []; new AsyncObjectCaller(this.providers, "getInstallsByTypes", { - nextObject: function getInstallsByTypes_nextObject(aCaller, aProvider) { + nextObject: function(aCaller, aProvider) { callProviderAsync(aProvider, "getInstallsByTypes", aTypes, function getInstallsByTypes_safeCall(aProviderInstalls) { if (aProviderInstalls) { @@ -1786,7 +1786,7 @@ var AddonManagerInternal = { }); }, - noMoreObjects: function getInstallsByTypes_noMoreObjects(aCaller) { + noMoreObjects: function(aCaller) { safeCall(aCallback, installs); } }); @@ -1798,7 +1798,7 @@ var AddonManagerInternal = { * @param aCallback * A callback which will be passed an array of AddonInstalls */ - getAllInstalls: function AMI_getAllInstalls(aCallback) { + getAllInstalls: function(aCallback) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -1818,7 +1818,7 @@ var AddonManagerInternal = { * @return string containing the Addon ID or null * @see amIAddonManager.mapURIToAddonID */ - mapURIToAddonID: function AMI_mapURIToAddonID(aURI) { + mapURIToAddonID: function(aURI) { if (!(aURI instanceof Ci.nsIURI)) { throw Components.Exception("aURI is not a nsIURI", Cr.NS_ERROR_INVALID_ARG); @@ -1843,7 +1843,7 @@ var AddonManagerInternal = { * The mimetype to check * @return true if installation is enabled for the mimetype */ - isInstallEnabled: function AMI_isInstallEnabled(aMimetype) { + isInstallEnabled: function(aMimetype) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -1871,7 +1871,7 @@ var AddonManagerInternal = { * The nsIPrincipal that initiated the install * @return true if the source is allowed to install this mimetype */ - isInstallAllowed: function AMI_isInstallAllowed(aMimetype, aInstallingPrincipal) { + isInstallAllowed: function(aMimetype, aInstallingPrincipal) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -1906,10 +1906,7 @@ var AddonManagerInternal = { * @param aInstalls * The array of AddonInstalls to be installed */ - installAddonsFromWebpage: function AMI_installAddonsFromWebpage(aMimetype, - aBrowser, - aInstallingPrincipal, - aInstalls) { + installAddonsFromWebpage: function(aMimetype, aBrowser, aInstallingPrincipal, aInstalls) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -2011,7 +2008,7 @@ var AddonManagerInternal = { * @param aListener * The InstallListener to add */ - addInstallListener: function AMI_addInstallListener(aListener) { + addInstallListener: function(aListener) { if (!aListener || typeof aListener != "object") throw Components.Exception("aListener must be a InstallListener object", Cr.NS_ERROR_INVALID_ARG); @@ -2027,7 +2024,7 @@ var AddonManagerInternal = { * @param aListener * The InstallListener to remove */ - removeInstallListener: function AMI_removeInstallListener(aListener) { + removeInstallListener: function(aListener) { if (!aListener || typeof aListener != "object") throw Components.Exception("aListener must be a InstallListener object", Cr.NS_ERROR_INVALID_ARG); @@ -2050,7 +2047,7 @@ var AddonManagerInternal = { * The callback to pass the retrieved add-on to * @throws if the aID or aCallback arguments are not specified */ - getAddonByID: function AMI_getAddonByID(aID, aCallback) { + getAddonByID: function(aID, aCallback) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -2064,7 +2061,7 @@ var AddonManagerInternal = { Cr.NS_ERROR_INVALID_ARG); new AsyncObjectCaller(this.providers, "getAddonByID", { - nextObject: function getAddonByID_nextObject(aCaller, aProvider) { + nextObject: function(aCaller, aProvider) { callProviderAsync(aProvider, "getAddonByID", aID, function getAddonByID_safeCall(aAddon) { if (aAddon) @@ -2074,7 +2071,7 @@ var AddonManagerInternal = { }); }, - noMoreObjects: function getAddonByID_noMoreObjects(aCaller) { + noMoreObjects: function(aCaller) { safeCall(aCallback, null); } }); @@ -2089,7 +2086,7 @@ var AddonManagerInternal = { * The callback to pass the retrieved add-on to. * @throws if the aGUID or aCallback arguments are not specified */ - getAddonBySyncGUID: function AMI_getAddonBySyncGUID(aGUID, aCallback) { + getAddonBySyncGUID: function(aGUID, aCallback) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -2103,7 +2100,7 @@ var AddonManagerInternal = { Cr.NS_ERROR_INVALID_ARG); new AsyncObjectCaller(this.providers, "getAddonBySyncGUID", { - nextObject: function getAddonBySyncGUID_nextObject(aCaller, aProvider) { + nextObject: function(aCaller, aProvider) { callProviderAsync(aProvider, "getAddonBySyncGUID", aGUID, function getAddonBySyncGUID_safeCall(aAddon) { if (aAddon) { @@ -2114,7 +2111,7 @@ var AddonManagerInternal = { }); }, - noMoreObjects: function getAddonBySyncGUID_noMoreObjects(aCaller) { + noMoreObjects: function(aCaller) { safeCall(aCallback, null); } }); @@ -2129,7 +2126,7 @@ var AddonManagerInternal = { * The callback to pass an array of Addons to * @throws if the aID or aCallback arguments are not specified */ - getAddonsByIDs: function AMI_getAddonsByIDs(aIDs, aCallback) { + getAddonsByIDs: function(aIDs, aCallback) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -2145,7 +2142,7 @@ var AddonManagerInternal = { let addons = []; new AsyncObjectCaller(aIDs, null, { - nextObject: function getAddonsByIDs_nextObject(aCaller, aID) { + nextObject: function(aCaller, aID) { AddonManagerInternal.getAddonByID(aID, function getAddonsByIDs_getAddonByID(aAddon) { addons.push(aAddon); @@ -2153,7 +2150,7 @@ var AddonManagerInternal = { }); }, - noMoreObjects: function getAddonsByIDs_noMoreObjects(aCaller) { + noMoreObjects: function(aCaller) { safeCall(aCallback, addons); } }); @@ -2168,7 +2165,7 @@ var AddonManagerInternal = { * The callback to pass an array of Addons to. * @throws if the aCallback argument is not specified */ - getAddonsByTypes: function AMI_getAddonsByTypes(aTypes, aCallback) { + getAddonsByTypes: function(aTypes, aCallback) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -2184,7 +2181,7 @@ var AddonManagerInternal = { let addons = []; new AsyncObjectCaller(this.providers, "getAddonsByTypes", { - nextObject: function getAddonsByTypes_nextObject(aCaller, aProvider) { + nextObject: function(aCaller, aProvider) { callProviderAsync(aProvider, "getAddonsByTypes", aTypes, function getAddonsByTypes_concatAddons(aProviderAddons) { if (aProviderAddons) { @@ -2194,7 +2191,7 @@ var AddonManagerInternal = { }); }, - noMoreObjects: function getAddonsByTypes_noMoreObjects(aCaller) { + noMoreObjects: function(aCaller) { safeCall(aCallback, addons); } }); @@ -2206,7 +2203,7 @@ var AddonManagerInternal = { * @param aCallback * A callback which will be passed an array of Addons */ - getAllAddons: function AMI_getAllAddons(aCallback) { + getAllAddons: function(aCallback) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -2228,8 +2225,7 @@ var AddonManagerInternal = { * The callback to pass the array of Addons to * @throws if the aCallback argument is not specified */ - getAddonsWithOperationsByTypes: - function AMI_getAddonsWithOperationsByTypes(aTypes, aCallback) { + getAddonsWithOperationsByTypes: function(aTypes, aCallback) { if (!gStarted) throw Components.Exception("AddonManager is not initialized", Cr.NS_ERROR_NOT_INITIALIZED); @@ -2245,8 +2241,7 @@ var AddonManagerInternal = { let addons = []; new AsyncObjectCaller(this.providers, "getAddonsWithOperationsByTypes", { - nextObject: function getAddonsWithOperationsByTypes_nextObject - (aCaller, aProvider) { + nextObject: function(aCaller, aProvider) { callProviderAsync(aProvider, "getAddonsWithOperationsByTypes", aTypes, function getAddonsWithOperationsByTypes_concatAddons (aProviderAddons) { @@ -2257,7 +2252,7 @@ var AddonManagerInternal = { }); }, - noMoreObjects: function getAddonsWithOperationsByTypes_noMoreObjects(caller) { + noMoreObjects: function(caller) { safeCall(aCallback, addons); } }); @@ -2269,7 +2264,7 @@ var AddonManagerInternal = { * @param aListener * The listener to add */ - addManagerListener: function AMI_addManagerListener(aListener) { + addManagerListener: function(aListener) { if (!aListener || typeof aListener != "object") throw Components.Exception("aListener must be an AddonManagerListener object", Cr.NS_ERROR_INVALID_ARG); @@ -2285,7 +2280,7 @@ var AddonManagerInternal = { * @param aListener * The listener to remove */ - removeManagerListener: function AMI_removeManagerListener(aListener) { + removeManagerListener: function(aListener) { if (!aListener || typeof aListener != "object") throw Components.Exception("aListener must be an AddonManagerListener object", Cr.NS_ERROR_INVALID_ARG); @@ -2305,7 +2300,7 @@ var AddonManagerInternal = { * @param aListener * The AddonListener to add */ - addAddonListener: function AMI_addAddonListener(aListener) { + addAddonListener: function(aListener) { if (!aListener || typeof aListener != "object") throw Components.Exception("aListener must be an AddonListener object", Cr.NS_ERROR_INVALID_ARG); @@ -2321,7 +2316,7 @@ var AddonManagerInternal = { * @param aListener * The AddonListener to remove */ - removeAddonListener: function AMI_removeAddonListener(aListener) { + removeAddonListener: function(aListener) { if (!aListener || typeof aListener != "object") throw Components.Exception("aListener must be an AddonListener object", Cr.NS_ERROR_INVALID_ARG); @@ -2341,7 +2336,7 @@ var AddonManagerInternal = { * @param aListener * The TypeListener to add */ - addTypeListener: function AMI_addTypeListener(aListener) { + addTypeListener: function(aListener) { if (!aListener || typeof aListener != "object") throw Components.Exception("aListener must be a TypeListener object", Cr.NS_ERROR_INVALID_ARG); @@ -2357,7 +2352,7 @@ var AddonManagerInternal = { * @param aListener * The TypeListener to remove */ - removeTypeListener: function AMI_removeTypeListener(aListener) { + removeTypeListener: function(aListener) { if (!aListener || typeof aListener != "object") throw Components.Exception("aListener must be a TypeListener object", Cr.NS_ERROR_INVALID_ARG); @@ -2496,23 +2491,23 @@ var AddonManagerInternal = { * subject to change at any time. */ this.AddonManagerPrivate = { - startup: function AMP_startup() { + startup: function() { AddonManagerInternal.startup(); }, - registerProvider: function AMP_registerProvider(aProvider, aTypes) { + registerProvider: function(aProvider, aTypes) { AddonManagerInternal.registerProvider(aProvider, aTypes); }, - unregisterProvider: function AMP_unregisterProvider(aProvider) { + unregisterProvider: function(aProvider) { AddonManagerInternal.unregisterProvider(aProvider); }, - markProviderSafe: function AMP_markProviderSafe(aProvider) { + markProviderSafe: function(aProvider) { AddonManagerInternal.markProviderSafe(aProvider); }, - backgroundUpdateCheck: function AMP_backgroundUpdateCheck() { + backgroundUpdateCheck: function() { return AddonManagerInternal.backgroundUpdateCheck(); }, @@ -2526,32 +2521,32 @@ this.AddonManagerPrivate = { AddonManagerInternal.backgroundUpdateCheck(); }, - addStartupChange: function AMP_addStartupChange(aType, aID) { + addStartupChange: function(aType, aID) { AddonManagerInternal.addStartupChange(aType, aID); }, - removeStartupChange: function AMP_removeStartupChange(aType, aID) { + removeStartupChange: function(aType, aID) { AddonManagerInternal.removeStartupChange(aType, aID); }, - notifyAddonChanged: function AMP_notifyAddonChanged(aID, aType, aPendingRestart) { + notifyAddonChanged: function(aID, aType, aPendingRestart) { AddonManagerInternal.notifyAddonChanged(aID, aType, aPendingRestart); }, - updateAddonAppDisabledStates: function AMP_updateAddonAppDisabledStates() { + updateAddonAppDisabledStates: function() { AddonManagerInternal.updateAddonAppDisabledStates(); }, - updateAddonRepositoryData: function AMP_updateAddonRepositoryData(aCallback) { + updateAddonRepositoryData: function(aCallback) { AddonManagerInternal.updateAddonRepositoryData(aCallback); }, - callInstallListeners: function AMP_callInstallListeners(...aArgs) { + callInstallListeners: function(...aArgs) { return AddonManagerInternal.callInstallListeners.apply(AddonManagerInternal, aArgs); }, - callAddonListeners: function AMP_callAddonListeners(...aArgs) { + callAddonListeners: function(...aArgs) { AddonManagerInternal.callAddonListeners.apply(AddonManagerInternal, aArgs); }, @@ -2563,16 +2558,16 @@ this.AddonManagerPrivate = { AddonType: AddonType, - recordTimestamp: function AMP_recordTimestamp(name, value) { + recordTimestamp: function(name, value) { AddonManagerInternal.recordTimestamp(name, value); }, _simpleMeasures: {}, - recordSimpleMeasure: function AMP_recordSimpleMeasure(name, value) { + recordSimpleMeasure: function(name, value) { this._simpleMeasures[name] = value; }, - recordException: function AMP_recordException(aModule, aContext, aException) { + recordException: function(aModule, aContext, aException) { let report = { module: aModule, context: aContext @@ -2592,15 +2587,15 @@ this.AddonManagerPrivate = { this._simpleMeasures.exception = report; }, - getSimpleMeasures: function AMP_getSimpleMeasures() { + getSimpleMeasures: function() { return this._simpleMeasures; }, - getTelemetryDetails: function AMP_getTelemetryDetails() { + getTelemetryDetails: function() { return AddonManagerInternal.telemetryDetails; }, - setTelemetryDetails: function AMP_setTelemetryDetails(aProvider, aDetails) { + setTelemetryDetails: function(aProvider, aDetails) { AddonManagerInternal.telemetryDetails[aProvider] = aDetails; }, @@ -2619,7 +2614,7 @@ this.AddonManagerPrivate = { * This can be used as an implementation for Addon.findUpdates() when * no update mechanism is available. */ - callNoUpdateListeners: function (addon, listener, reason, appVersion, platformVersion) { + callNoUpdateListeners: function(addon, listener, reason, appVersion, platformVersion) { if ("onNoCompatibilityUpdateAvailable" in listener) { safeCall(listener.onNoCompatibilityUpdateAvailable.bind(listener), addon); } @@ -2830,14 +2825,13 @@ this.AddonManager = { return gStartupComplete && !gShutdownInProgress; }, - getInstallForURL: function AM_getInstallForURL(aUrl, aCallback, aMimetype, - aHash, aName, aIcons, - aVersion, aBrowser) { + getInstallForURL: function(aUrl, aCallback, aMimetype, aHash, aName, aIcons, + aVersion, aBrowser) { AddonManagerInternal.getInstallForURL(aUrl, aCallback, aMimetype, aHash, aName, aIcons, aVersion, aBrowser); }, - getInstallForFile: function AM_getInstallForFile(aFile, aCallback, aMimetype) { + getInstallForFile: function(aFile, aCallback, aMimetype) { AddonManagerInternal.getInstallForFile(aFile, aCallback, aMimetype); }, @@ -2848,94 +2842,91 @@ this.AddonManager = { * The type of startup change to get * @return An array of add-on IDs */ - getStartupChanges: function AM_getStartupChanges(aType) { + getStartupChanges: function(aType) { if (!(aType in AddonManagerInternal.startupChanges)) return []; return AddonManagerInternal.startupChanges[aType].slice(0); }, - getAddonByID: function AM_getAddonByID(aID, aCallback) { + getAddonByID: function(aID, aCallback) { AddonManagerInternal.getAddonByID(aID, aCallback); }, - getAddonBySyncGUID: function AM_getAddonBySyncGUID(aGUID, aCallback) { + getAddonBySyncGUID: function(aGUID, aCallback) { AddonManagerInternal.getAddonBySyncGUID(aGUID, aCallback); }, - getAddonsByIDs: function AM_getAddonsByIDs(aIDs, aCallback) { + getAddonsByIDs: function(aIDs, aCallback) { AddonManagerInternal.getAddonsByIDs(aIDs, aCallback); }, - getAddonsWithOperationsByTypes: - function AM_getAddonsWithOperationsByTypes(aTypes, aCallback) { + getAddonsWithOperationsByTypes: function(aTypes, aCallback) { AddonManagerInternal.getAddonsWithOperationsByTypes(aTypes, aCallback); }, - getAddonsByTypes: function AM_getAddonsByTypes(aTypes, aCallback) { + getAddonsByTypes: function(aTypes, aCallback) { AddonManagerInternal.getAddonsByTypes(aTypes, aCallback); }, - getAllAddons: function AM_getAllAddons(aCallback) { + getAllAddons: function(aCallback) { AddonManagerInternal.getAllAddons(aCallback); }, - getInstallsByTypes: function AM_getInstallsByTypes(aTypes, aCallback) { + getInstallsByTypes: function(aTypes, aCallback) { AddonManagerInternal.getInstallsByTypes(aTypes, aCallback); }, - getAllInstalls: function AM_getAllInstalls(aCallback) { + getAllInstalls: function(aCallback) { AddonManagerInternal.getAllInstalls(aCallback); }, - mapURIToAddonID: function AM_mapURIToAddonID(aURI) { + mapURIToAddonID: function(aURI) { return AddonManagerInternal.mapURIToAddonID(aURI); }, - isInstallEnabled: function AM_isInstallEnabled(aType) { + isInstallEnabled: function(aType) { return AddonManagerInternal.isInstallEnabled(aType); }, - isInstallAllowed: function AM_isInstallAllowed(aType, aInstallingPrincipal) { + isInstallAllowed: function(aType, aInstallingPrincipal) { return AddonManagerInternal.isInstallAllowed(aType, ensurePrincipal(aInstallingPrincipal)); }, - installAddonsFromWebpage: function AM_installAddonsFromWebpage(aType, aBrowser, - aInstallingPrincipal, - aInstalls) { + installAddonsFromWebpage: function(aType, aBrowser, aInstallingPrincipal, aInstalls) { AddonManagerInternal.installAddonsFromWebpage(aType, aBrowser, ensurePrincipal(aInstallingPrincipal), aInstalls); }, - addManagerListener: function AM_addManagerListener(aListener) { + addManagerListener: function(aListener) { AddonManagerInternal.addManagerListener(aListener); }, - removeManagerListener: function AM_removeManagerListener(aListener) { + removeManagerListener: function(aListener) { AddonManagerInternal.removeManagerListener(aListener); }, - addInstallListener: function AM_addInstallListener(aListener) { + addInstallListener: function(aListener) { AddonManagerInternal.addInstallListener(aListener); }, - removeInstallListener: function AM_removeInstallListener(aListener) { + removeInstallListener: function(aListener) { AddonManagerInternal.removeInstallListener(aListener); }, - addAddonListener: function AM_addAddonListener(aListener) { + addAddonListener: function(aListener) { AddonManagerInternal.addAddonListener(aListener); }, - removeAddonListener: function AM_removeAddonListener(aListener) { + removeAddonListener: function(aListener) { AddonManagerInternal.removeAddonListener(aListener); }, - addTypeListener: function AM_addTypeListener(aListener) { + addTypeListener: function(aListener) { AddonManagerInternal.addTypeListener(aListener); }, - removeTypeListener: function AM_removeTypeListener(aListener) { + removeTypeListener: function(aListener) { AddonManagerInternal.removeTypeListener(aListener); }, @@ -2950,7 +2941,7 @@ this.AddonManager = { * The Addon representing the add-on * @return true if the addon should auto-update, false otherwise. */ - shouldAutoUpdate: function AM_shouldAutoUpdate(aAddon) { + shouldAutoUpdate: function(aAddon) { if (!aAddon || typeof aAddon != "object") throw Components.Exception("aAddon must be specified", Cr.NS_ERROR_INVALID_ARG); @@ -3008,7 +2999,7 @@ this.AddonManager = { AddonManagerInternal.autoUpdateDefault = aValue; }, - escapeAddonURI: function AM_escapeAddonURI(aAddon, aUri, aAppVersion) { + escapeAddonURI: function(aAddon, aUri, aAppVersion) { return AddonManagerInternal.escapeAddonURI(aAddon, aUri, aAppVersion); }, diff --git a/toolkit/mozapps/extensions/LightweightThemeManager.jsm b/toolkit/mozapps/extensions/LightweightThemeManager.jsm index 5856bfa91..372a9f3b8 100644 --- a/toolkit/mozapps/extensions/LightweightThemeManager.jsm +++ b/toolkit/mozapps/extensions/LightweightThemeManager.jsm @@ -110,11 +110,11 @@ this.LightweightThemeManager = { return _setCurrentTheme(aData, false); }, - setLocalTheme: function LightweightThemeManager_setLocalTheme(aData) { + setLocalTheme: function(aData) { _setCurrentTheme(aData, true); }, - getUsedTheme: function LightweightThemeManager_getUsedTheme(aId) { + getUsedTheme: function(aId) { var usedThemes = this.usedThemes; for (let usedTheme of usedThemes) { if (usedTheme.id == aId) @@ -123,7 +123,7 @@ this.LightweightThemeManager = { return null; }, - forgetUsedTheme: function LightweightThemeManager_forgetUsedTheme(aId) { + forgetUsedTheme: function(aId) { let theme = this.getUsedTheme(aId); if (!theme) return; @@ -141,7 +141,7 @@ this.LightweightThemeManager = { AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper); }, - previewTheme: function LightweightThemeManager_previewTheme(aData) { + previewTheme: function(aData) { let cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool); cancel.data = false; Services.obs.notifyObservers(cancel, "lightweight-theme-preview-requested", @@ -160,7 +160,7 @@ this.LightweightThemeManager = { _notifyWindows(aData); }, - resetPreview: function LightweightThemeManager_resetPreview() { + resetPreview: function() { if (_previewTimer) { _previewTimer.cancel(); _previewTimer = null; @@ -168,7 +168,7 @@ this.LightweightThemeManager = { } }, - parseTheme: function LightweightThemeManager_parseTheme(aString, aBaseURI) { + parseTheme: function(aString, aBaseURI) { try { return _sanitizeTheme(JSON.parse(aString), aBaseURI, false); } catch (e) { @@ -176,7 +176,7 @@ this.LightweightThemeManager = { } }, - updateCurrentTheme: function LightweightThemeManager_updateCurrentTheme() { + updateCurrentTheme: function() { try { if (!_prefs.getBoolPref("update.enabled")) return; @@ -224,7 +224,7 @@ this.LightweightThemeManager = { * @param aData * The lightweight theme to switch to */ - themeChanged: function LightweightThemeManager_themeChanged(aData) { + themeChanged: function(aData) { if (_previewTimer) { _previewTimer.cancel(); _previewTimer = null; @@ -251,7 +251,7 @@ this.LightweightThemeManager = { * Starts the Addons provider and enables the new lightweight theme if * necessary. */ - startup: function LightweightThemeManager_startup() { + startup: function() { if (Services.prefs.prefHasUserValue(PREF_LWTHEME_TO_SELECT)) { let id = Services.prefs.getCharPref(PREF_LWTHEME_TO_SELECT); if (id) @@ -267,7 +267,7 @@ this.LightweightThemeManager = { /** * Shuts down the provider. */ - shutdown: function LightweightThemeManager_shutdown() { + shutdown: function() { _prefs.removeObserver("", _prefObserver); }, @@ -283,7 +283,7 @@ this.LightweightThemeManager = { * true if the newly enabled add-on will only become enabled after a * restart */ - addonChanged: function LightweightThemeManager_addonChanged(aId, aType, aPendingRestart) { + addonChanged: function(aId, aType, aPendingRestart) { if (aType != ADDON_TYPE) return; @@ -356,7 +356,7 @@ this.LightweightThemeManager = { * @param aCallback * A callback to pass the Addon to */ - getAddonByID: function LightweightThemeManager_getAddonByID(aId, aCallback) { + getAddonByID: function(aId, aCallback) { let id = _getInternalID(aId); if (!id) { aCallback(null); @@ -380,7 +380,7 @@ this.LightweightThemeManager = { * @param aCallback * A callback to pass an array of Addons to */ - getAddonsByTypes: function LightweightThemeManager_getAddonsByTypes(aTypes, aCallback) { + getAddonsByTypes: function(aTypes, aCallback) { if (aTypes && aTypes.indexOf(ADDON_TYPE) == -1) { aCallback([]); return; @@ -541,7 +541,7 @@ AddonWrapper.prototype = { }, // Lightweight themes are always compatible - isCompatibleWith: function AddonWrapper_isCompatibleWith(appVersion, platformVersion) { + isCompatibleWith: function(appVersion, platformVersion) { return true; }, @@ -708,7 +708,7 @@ function _notifyWindows(aThemeData) { var _previewTimer; var _previewTimerCallback = { - notify: function _previewTimerCallback_notify() { + notify: function() { LightweightThemeManager.resetPreview(); } }; diff --git a/toolkit/mozapps/extensions/addonManager.js b/toolkit/mozapps/extensions/addonManager.js index 731e70c6c..2628ea87b 100644 --- a/toolkit/mozapps/extensions/addonManager.js +++ b/toolkit/mozapps/extensions/addonManager.js @@ -53,7 +53,7 @@ function amManager() { } amManager.prototype = { - observe: function AMC_observe(aSubject, aTopic, aData) { + observe: function(aSubject, aTopic, aData) { if (aTopic == "addons-startup") AddonManagerPrivate.startup(); }, @@ -61,7 +61,7 @@ amManager.prototype = { /** * @see amIAddonManager.idl */ - mapURIToAddonID: function AMC_mapURIToAddonID(uri, id) { + mapURIToAddonID: function(uri, id) { id.value = AddonManager.mapURIToAddonID(uri); return !!id.value; }, @@ -69,19 +69,15 @@ amManager.prototype = { /** * @see amIWebInstaller.idl */ - isInstallEnabled: function AMC_isInstallEnabled(aMimetype, aReferer) { + isInstallEnabled: function(aMimetype, aReferer) { return AddonManager.isInstallEnabled(aMimetype); }, /** * @see amIWebInstaller.idl */ - installAddonsFromWebpage: function AMC_installAddonsFromWebpage(aMimetype, - aBrowser, - aInstallingPrincipal, - aUris, aHashes, - aNames, aIcons, - aCallback) { + installAddonsFromWebpage: function(aMimetype, aBrowser, aInstallingPrincipal, + aUris, aHashes, aNames, aIcons, aCallback) { if (aUris.length == 0) return false; @@ -112,22 +108,22 @@ amManager.prototype = { installs.push(aInstall); if (aCallback) { aInstall.addListener({ - onDownloadCancelled: function buildNextInstall_onDownloadCancelled(aInstall) { + onDownloadCancelled: function(aInstall) { callCallback(uri, USER_CANCELLED); }, - onDownloadFailed: function buildNextInstall_onDownloadFailed(aInstall) { + onDownloadFailed: function(aInstall) { if (aInstall.error == AddonManager.ERROR_CORRUPT_FILE) callCallback(uri, CANT_READ_ARCHIVE); else callCallback(uri, DOWNLOAD_ERROR); }, - onInstallFailed: function buildNextInstall_onInstallFailed(aInstall) { + onInstallFailed: function(aInstall) { callCallback(uri, EXECUTION_ERROR); }, - onInstallEnded: function buildNextInstall_onInstallEnded(aInstall, aStatus) { + onInstallEnded: function(aInstall, aStatus) { callCallback(uri, SUCCESS); } }); @@ -144,7 +140,7 @@ amManager.prototype = { return retval; }, - notify: function AMC_notify(aTimer) { + notify: function(aTimer) { AddonManagerPrivate.backgroundUpdateTimerHandler(); }, @@ -154,7 +150,7 @@ amManager.prototype = { * Listens to requests from child processes for InstallTrigger * activity, and sends back callbacks. */ - receiveMessage: function AMC_receiveMessage(aMessage) { + receiveMessage: function(aMessage) { let payload = aMessage.data; switch (aMessage.name) { @@ -165,7 +161,7 @@ amManager.prototype = { let callback = null; if (payload.callbackID != -1) { callback = { - onInstallEnded: function ITP_callback(url, status) { + onInstallEnded: function(url, status) { gParentMM.broadcastAsyncMessage(MSG_INSTALL_CALLBACK, { callbackID: payload.callbackID, url: url, @@ -184,7 +180,7 @@ amManager.prototype = { classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"), _xpcom_factory: { - createInstance: function AMC_createInstance(aOuter, aIid) { + createInstance: function(aOuter, aIid) { if (aOuter != null) throw Components.Exception("Component does not support aggregation", Cr.NS_ERROR_NO_AGGREGATION); diff --git a/toolkit/mozapps/extensions/amInstallTrigger.js b/toolkit/mozapps/extensions/amInstallTrigger.js index a18fe84c4..5fc0e1717 100644 --- a/toolkit/mozapps/extensions/amInstallTrigger.js +++ b/toolkit/mozapps/extensions/amInstallTrigger.js @@ -203,7 +203,7 @@ InstallTrigger.prototype = { return this.startSoftwareUpdate(url); }, - _resolveURL: function (url) { + _resolveURL: function(url) { return Services.io.newURI(url, null, this._url); }, diff --git a/toolkit/mozapps/extensions/amWebInstallListener.js b/toolkit/mozapps/extensions/amWebInstallListener.js index ac6e2495d..088f56640 100644 --- a/toolkit/mozapps/extensions/amWebInstallListener.js +++ b/toolkit/mozapps/extensions/amWebInstallListener.js @@ -89,7 +89,7 @@ Installer.prototype = { /** * Checks if all downloads are now complete and if so prompts to install. */ - checkAllDownloaded: function Installer_checkAllDownloaded() { + checkAllDownloaded: function() { // Prevent re-entrancy caused by the confirmation dialog cancelling unwanted // installs. if (!this.isDownloading) @@ -199,7 +199,7 @@ Installer.prototype = { /** * Checks if all installs are now complete and if so notifies observers. */ - checkAllInstalled: function Installer_checkAllInstalled() { + checkAllInstalled: function() { var failed = []; for (let install of this.downloads) { @@ -225,32 +225,32 @@ Installer.prototype = { this.installed = null; }, - onDownloadCancelled: function Installer_onDownloadCancelled(aInstall) { + onDownloadCancelled: function(aInstall) { aInstall.removeListener(this); this.checkAllDownloaded(); }, - onDownloadFailed: function Installer_onDownloadFailed(aInstall) { + onDownloadFailed: function(aInstall) { aInstall.removeListener(this); this.checkAllDownloaded(); }, - onDownloadEnded: function Installer_onDownloadEnded(aInstall) { + onDownloadEnded: function(aInstall) { this.checkAllDownloaded(); return false; }, - onInstallCancelled: function Installer_onInstallCancelled(aInstall) { + onInstallCancelled: function(aInstall) { aInstall.removeListener(this); this.checkAllInstalled(); }, - onInstallFailed: function Installer_onInstallFailed(aInstall) { + onInstallFailed: function(aInstall) { aInstall.removeListener(this); this.checkAllInstalled(); }, - onInstallEnded: function Installer_onInstallEnded(aInstall) { + onInstallEnded: function(aInstall) { aInstall.removeListener(this); this.installed.push(aInstall); @@ -272,7 +272,7 @@ extWebInstallListener.prototype = { /** * @see amIWebInstallListener.idl */ - onWebInstallDisabled: function extWebInstallListener_onWebInstallDisabled(aBrowser, aUri, aInstalls) { + onWebInstallDisabled: function(aBrowser, aUri, aInstalls) { let info = { browser: aBrowser, originatingURI: aUri, @@ -286,13 +286,13 @@ extWebInstallListener.prototype = { /** * @see amIWebInstallListener.idl */ - onWebInstallOriginBlocked: function extWebInstallListener_onWebInstallOriginBlocked(aBrowser, aUri, aInstalls) { + onWebInstallOriginBlocked: function(aBrowser, aUri, aInstalls) { let info = { browser: aBrowser, originatingURI: aUri, installs: aInstalls, - install: function onWebInstallBlocked_install() { + install: function() { }, QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo]) @@ -305,13 +305,13 @@ extWebInstallListener.prototype = { /** * @see amIWebInstallListener.idl */ - onWebInstallBlocked: function extWebInstallListener_onWebInstallBlocked(aBrowser, aUri, aInstalls) { + onWebInstallBlocked: function(aBrowser, aUri, aInstalls) { let info = { browser: aBrowser, originatingURI: aUri, installs: aInstalls, - install: function onWebInstallBlocked_install() { + install: function() { new Installer(this.browser, this.originatingURI, this.installs); }, @@ -325,7 +325,7 @@ extWebInstallListener.prototype = { /** * @see amIWebInstallListener.idl */ - onWebInstallRequested: function extWebInstallListener_onWebInstallRequested(aBrowser, aUri, aInstalls) { + onWebInstallRequested: function(aBrowser, aUri, aInstalls) { new Installer(aBrowser, aUri, aInstalls); // We start the installs ourself diff --git a/toolkit/themes/osx/global/notification.css b/toolkit/themes/osx/global/notification.css index 24b3d3920..6d22cf9c8 100644 --- a/toolkit/themes/osx/global/notification.css +++ b/toolkit/themes/osx/global/notification.css @@ -81,12 +81,12 @@ notificationbox[notificationside="bottom"] > notification { Invert the close icon for @type=info since both are normally dark. It's unclear why !important is necessary here so remove it if it's no longer needed. */ -notification[type="info"]:not([value="translation"]) .close-icon:not(:hover) { +notification[type="info"] .close-icon:not(:hover) { -moz-image-region: rect(0, 64px, 16px, 48px) !important; } @media (min-resolution: 2dppx) { - notification[type="info"]:not([value="translation"]) .close-icon:not(:hover) { + notification[type="info"] .close-icon:not(:hover) { -moz-image-region: rect(0, 128px, 32px, 96px) !important; } } diff --git a/tools/lint/eslint/modules.json b/tools/lint/eslint/modules.json index 5fb645762..767b43db0 100644 --- a/tools/lint/eslint/modules.json +++ b/tools/lint/eslint/modules.json @@ -224,7 +224,6 @@ "tokenserverclient.js": ["TokenServerClient", "TokenServerClientError", "TokenServerClientNetworkError", "TokenServerClientServerError"], "ToolboxProcess.jsm": ["BrowserToolboxProcess"], "tps.jsm": ["ACTIONS", "TPS"], - "Translation.jsm": ["Translation", "TranslationTelemetry"], "Traversal.jsm": ["TraversalRules", "TraversalHelper"], "UpdateTelemetry.jsm": ["AUSTLMY"], "userapi.js": ["UserAPI10Client"], |