From 5909cef47a4aaf458e41e0d2a46b88972b934dff Mon Sep 17 00:00:00 2001 From: trav90 Date: Sat, 12 May 2018 08:17:44 -0500 Subject: Make safebrowsing optional at build time - Part 1: browser/ --- browser/base/content/browser-doctype.inc | 2 ++ browser/base/content/browser-safebrowsing.js | 2 ++ browser/base/content/browser.js | 8 ++++++-- browser/base/content/global-scripts.inc | 2 ++ browser/base/content/utilityOverlay.js | 3 +-- browser/base/jar.mn | 4 ++++ browser/components/about/AboutRedirector.cpp | 2 ++ browser/components/build/nsModule.cpp | 2 ++ browser/confvars.sh | 1 + browser/installer/package-manifest.in | 2 ++ browser/locales/jar.mn | 2 ++ 11 files changed, 26 insertions(+), 4 deletions(-) diff --git a/browser/base/content/browser-doctype.inc b/browser/base/content/browser-doctype.inc index 10015d898..ad08f4b03 100644 --- a/browser/base/content/browser-doctype.inc +++ b/browser/base/content/browser-doctype.inc @@ -13,8 +13,10 @@ %customizeToolbarDTD; %placesDTD; +#ifdef MOZ_SAFE_BROWSING %safebrowsingDTD; +#endif %aboutHomeDTD; diff --git a/browser/base/content/browser-safebrowsing.js b/browser/base/content/browser-safebrowsing.js index 430d84f13..a66595b2d 100644 --- a/browser/base/content/browser-safebrowsing.js +++ b/browser/base/content/browser-safebrowsing.js @@ -2,6 +2,7 @@ * 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/. */ +#ifdef MOZ_SAFE_BROWSING var gSafeBrowsing = { setReportPhishingMenu: function() { @@ -46,3 +47,4 @@ var gSafeBrowsing = { return SafeBrowsing.getReportURL(name, gBrowser.currentURI); } } +#endif diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 2380f5d21..8679bca83 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -60,8 +60,10 @@ Cu.import("resource://gre/modules/NotificationDB.jsm"); ["webrtcUI", "resource:///modules/webrtcUI.jsm", ] ].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource)); -XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing", - "resource://gre/modules/SafeBrowsing.jsm"); +#ifdef MOZ_SAFE_BROWSING + XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing", + "resource://gre/modules/SafeBrowsing.jsm"); +#endif // lazy service getters [ @@ -1200,8 +1202,10 @@ var gBrowserInit = { } } +#ifdef MOZ_SAFE_BROWSING // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008. setTimeout(function() { SafeBrowsing.init(); }, 2000); +#endif Services.obs.addObserver(gIdentityHandler, "perm-changed", false); Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false); diff --git a/browser/base/content/global-scripts.inc b/browser/base/content/global-scripts.inc index dac75878d..ca942cec8 100755 --- a/browser/base/content/global-scripts.inc +++ b/browser/base/content/global-scripts.inc @@ -23,7 +23,9 @@ + diff --git a/docshell/test/navigation/mochitest.ini b/docshell/test/navigation/mochitest.ini index 1b5f33c7f..f3bb3d244 100644 --- a/docshell/test/navigation/mochitest.ini +++ b/docshell/test/navigation/mochitest.ini @@ -59,6 +59,7 @@ skip-if = (toolkit == 'android') || (!debug && (os == 'mac' || os == 'win')) # B skip-if = (toolkit == 'android') || (debug && e10s) #too slow on Android 4.3 aws only; bug 1030403; bug 1263213 for debug e10s [test_sessionhistory.html] skip-if = toolkit == 'android' #RANDOM +support-files = file_bug1379762-1.html [test_sibling-matching-parent.html] [test_sibling-off-domain.html] [test_triggeringprincipal_frame_nav.html] diff --git a/docshell/test/navigation/test_sessionhistory.html b/docshell/test/navigation/test_sessionhistory.html index 452271a41..b922ce4ea 100644 --- a/docshell/test/navigation/test_sessionhistory.html +++ b/docshell/test/navigation/test_sessionhistory.html @@ -31,7 +31,8 @@ var testFiles = "file_nested_frames.html", "file_shiftReload_and_pushState.html", "file_scrollRestoration.html", - "file_bug1300461.html" + "file_bug1300461.html", + "file_bug1379762-1.html", ]; var testCount = 0; // Used by the test files. diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 4a54a8432..3106ff386 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -1307,10 +1307,15 @@ nsPresContext::SetFullZoom(float aZoom) void nsPresContext::SetOverrideDPPX(float aDPPX) { - mOverrideDPPX = aDPPX; - - if (HasCachedStyleData()) { - MediaFeatureValuesChanged(nsRestyleHint(0), nsChangeHint(0)); + // SetOverrideDPPX is called during navigations, including history + // traversals. In that case, it's typically called with our current value, + // and we don't need to actually do anything. + if (aDPPX != mOverrideDPPX) { + mOverrideDPPX = aDPPX; + + if (HasCachedStyleData()) { + MediaFeatureValuesChanged(nsRestyleHint(0), nsChangeHint(0)); + } } } -- cgit v1.2.3 From 0c994ed0963e1552b95708495d58ef193c48fc3c Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Tue, 15 May 2018 21:20:45 +0200 Subject: Consistently use PR memory functions. --- extensions/auth/nsAuthSambaNTLM.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/extensions/auth/nsAuthSambaNTLM.cpp b/extensions/auth/nsAuthSambaNTLM.cpp index 1406c15e0..69777dcca 100644 --- a/extensions/auth/nsAuthSambaNTLM.cpp +++ b/extensions/auth/nsAuthSambaNTLM.cpp @@ -5,6 +5,7 @@ #include "nsAuth.h" #include "nsAuthSambaNTLM.h" +#include "nspr.h" #include "prenv.h" #include "plbase64.h" #include "prerror.h" @@ -23,7 +24,7 @@ nsAuthSambaNTLM::~nsAuthSambaNTLM() // ntlm_auth reads from stdin regularly so closing our file handles // should cause it to exit. Shutdown(); - free(mInitialMessage); + PR_Free(mInitialMessage); } void @@ -248,7 +249,7 @@ nsAuthSambaNTLM::GetNextToken(const void *inToken, nsCString request; request.AssignLiteral("TT "); request.Append(encoded); - free(encoded); + PR_Free(encoded); request.Append('\n'); if (!WriteString(mToChildFD, request)) @@ -265,7 +266,7 @@ nsAuthSambaNTLM::GetNextToken(const void *inToken, if (!buf) return NS_ERROR_FAILURE; *outToken = nsMemory::Clone(buf, *outTokenLen); - free(buf); + PR_Free(buf); if (!*outToken) { return NS_ERROR_OUT_OF_MEMORY; } -- cgit v1.2.3 From 41cbe2d931d2742bb8dd6240eae9599e3bf3a512 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Tue, 15 May 2018 21:37:22 +0200 Subject: Merge libhyphen fix from upstream. --- intl/hyphenation/hyphen/hyphen.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/intl/hyphenation/hyphen/hyphen.c b/intl/hyphenation/hyphen/hyphen.c index 9a132d026..9f2b7112c 100644 --- a/intl/hyphenation/hyphen/hyphen.c +++ b/intl/hyphenation/hyphen/hyphen.c @@ -438,11 +438,25 @@ for (k = 0; k < 2; k++) { } if (k == 0 || nextlevel) { - while (fgets (buf, sizeof(buf), f) != NULL) { + while (fgets(buf, sizeof(buf), f) != NULL) { + + /* discard lines that don't fit in buffer */ + if (!feof(f) && strchr(buf, '\n') == NULL) { + int c; + while ((c = fgetc(f)) != '\n' && c != EOF); + /* issue warning if not a comment */ + if (buf[0] != '%') { + fprintf(stderr, "Warning: skipping too long pattern (more than %lu chars)\n", sizeof(buf)); + } + continue; + } + if (strncmp(buf, "NEXTLEVEL", 9) == 0) { - nextlevel = 1; - break; - } else if (buf[0] != '%') hnj_hyphen_load_line(buf, dict[k], hashtab); + nextlevel = 1; + break; + } else if (buf[0] != '%') { + hnj_hyphen_load_line(buf, dict[k], hashtab); + } } } else if (k == 1) { /* default first level: hyphen and ASCII apostrophe */ -- cgit v1.2.3 From c9d1a79bd076f39e11d0bf9ca7f543ec910cf2b2 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Tue, 15 May 2018 21:52:48 +0200 Subject: Don't linkify data: or javascript: URLs in the web console. --- devtools/client/webconsole/console-output.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/client/webconsole/console-output.js b/devtools/client/webconsole/console-output.js index 52d848494..bd290dcc6 100644 --- a/devtools/client/webconsole/console-output.js +++ b/devtools/client/webconsole/console-output.js @@ -31,7 +31,7 @@ const {PluralForm} = require("devtools/shared/plural-form"); const MAX_STRING_GRIP_LENGTH = 36; const {ELLIPSIS} = require("devtools/shared/l10n"); -const validProtocols = /^(http|https|ftp|data|javascript|resource|chrome):/i; +const validProtocols = /^(http|https|ftp|resource|chrome):/i; // Constants for compatibility with the Web Console output implementation before // bug 778766. -- cgit v1.2.3 From d1184bfb4939e76f3aa442daa90dc5cca3a850e4 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Tue, 15 May 2018 22:45:40 +0200 Subject: Bug 1379762 part 2. Use a more reliable test to figure out when we can skip firing onload in nsDocumentViewer::LoadComplete Issue #357 --- docshell/test/navigation/file_bug1379762-2.html | 43 +++++++++++++++++++++++ docshell/test/navigation/mochitest.ini | 2 +- docshell/test/navigation/test_sessionhistory.html | 1 + layout/base/nsDocumentViewer.cpp | 15 +++++++- 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 docshell/test/navigation/file_bug1379762-2.html diff --git a/docshell/test/navigation/file_bug1379762-2.html b/docshell/test/navigation/file_bug1379762-2.html new file mode 100644 index 000000000..86033cb2e --- /dev/null +++ b/docshell/test/navigation/file_bug1379762-2.html @@ -0,0 +1,43 @@ + + + + + Bug 1379762 + + + + diff --git a/docshell/test/navigation/mochitest.ini b/docshell/test/navigation/mochitest.ini index f3bb3d244..8cff81ad1 100644 --- a/docshell/test/navigation/mochitest.ini +++ b/docshell/test/navigation/mochitest.ini @@ -59,7 +59,7 @@ skip-if = (toolkit == 'android') || (!debug && (os == 'mac' || os == 'win')) # B skip-if = (toolkit == 'android') || (debug && e10s) #too slow on Android 4.3 aws only; bug 1030403; bug 1263213 for debug e10s [test_sessionhistory.html] skip-if = toolkit == 'android' #RANDOM -support-files = file_bug1379762-1.html +support-files = file_bug1379762-1.html file_bug1379762-2.html [test_sibling-matching-parent.html] [test_sibling-off-domain.html] [test_triggeringprincipal_frame_nav.html] diff --git a/docshell/test/navigation/test_sessionhistory.html b/docshell/test/navigation/test_sessionhistory.html index b922ce4ea..10b0cbcaf 100644 --- a/docshell/test/navigation/test_sessionhistory.html +++ b/docshell/test/navigation/test_sessionhistory.html @@ -33,6 +33,7 @@ var testFiles = "file_scrollRestoration.html", "file_bug1300461.html", "file_bug1379762-1.html", + "file_bug1379762-2.html", ]; var testCount = 0; // Used by the test files. diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 137efb3cd..7b8734928 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -1012,7 +1012,13 @@ nsDocumentViewer::LoadComplete(nsresult aStatus) nsIDocShell *docShell = window->GetDocShell(); NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED); - docShell->GetRestoringDocument(&restoring); + // Unfortunately, docShell->GetRestoringDocument() might no longer be set + // correctly. In particular, it can be false by now if someone took it upon + // themselves to block onload from inside restoration and unblock it later. + // But we can detect the restoring case very simply: by whether our + // document's readyState is COMPLETE. + restoring = (mDocument->GetReadyStateEnum() == + nsIDocument::READYSTATE_COMPLETE); if (!restoring) { NS_ASSERTION(mDocument->IsXULDocument() || // readyState for XUL is bogus mDocument->GetReadyStateEnum() == @@ -1023,6 +1029,13 @@ nsDocumentViewer::LoadComplete(nsresult aStatus) nsIDocument::READYSTATE_UNINITIALIZED && NS_IsAboutBlank(mDocument->GetDocumentURI())), "Bad readystate"); +#ifdef DEBUG + bool docShellThinksWeAreRestoring; + docShell->GetRestoringDocument(&docShellThinksWeAreRestoring); + MOZ_ASSERT(!docShellThinksWeAreRestoring, + "How can docshell think we are restoring if we don't have a " + "READYSTATE_COMPLETE document?"); +#endif // DEBUG nsCOMPtr d = mDocument; mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE); -- cgit v1.2.3 From ec1aeee6d38ec076c068472cbc672f5b23934054 Mon Sep 17 00:00:00 2001 From: JustOff Date: Wed, 16 May 2018 00:28:28 +0300 Subject: Revive the quickdial page (about:newtab) --- application/palemoon/base/content/newtab/grid.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/palemoon/base/content/newtab/grid.js b/application/palemoon/base/content/newtab/grid.js index a614d0396..fbeb7bd27 100644 --- a/application/palemoon/base/content/newtab/grid.js +++ b/application/palemoon/base/content/newtab/grid.js @@ -115,10 +115,10 @@ var gGrid = { // (Re-)initialize all cells. let cellElements = this.node.querySelectorAll(".newtab-cell"); // Tycho: this._cells = [new Cell(this, cell) for (cell of cellElements)]; - this.cells = []; + this._cells = []; for (let cellItem of cellElements) { - this.cells.push(new Cell(this, cellItem)); + this._cells.push(new Cell(this, cellItem)); } }, -- cgit v1.2.3 From b70d884598e1e14b99190e1e5c349553ff59849b Mon Sep 17 00:00:00 2001 From: Ascrod <32915892+Ascrod@users.noreply.github.com> Date: Mon, 7 May 2018 19:45:46 -0400 Subject: Initial updates for Reader View. --- browser/base/content/browser-sets.inc | 2 +- browser/modules/ReaderParent.jsm | 69 +- mobile/android/themes/core/jar.mn | 4 - toolkit/components/narrate/NarrateControls.jsm | 134 ++-- toolkit/components/narrate/Narrator.jsm | 78 +- toolkit/components/narrate/VoiceSelect.jsm | 23 +- .../components/printing/content/simplifyMode.css | 2 +- toolkit/components/reader/AboutReader.jsm | 314 ++++---- toolkit/components/reader/JSDOMParser.js | 46 +- toolkit/components/reader/Readability.js | 853 +++++++++------------ toolkit/components/reader/ReaderMode.jsm | 312 +++++--- toolkit/components/reader/ReaderWorker.js | 7 +- toolkit/components/reader/content/aboutReader.html | 73 +- toolkit/components/reader/content/aboutReader.js | 2 +- toolkit/content/browser-content.js | 6 - .../en-US/chrome/global/aboutReader.properties | 16 + toolkit/themes/shared/aboutReader.css | 626 ++++++++++++++- toolkit/themes/shared/aboutReaderContent.css | 177 ----- toolkit/themes/shared/aboutReaderControls.css | 388 ---------- toolkit/themes/shared/jar.inc.mn | 5 +- toolkit/themes/shared/narrate.css | 195 ++++- toolkit/themes/shared/narrateControls.css | 185 ----- .../shared/reader/RM-Content-Width-Minus-42x16.svg | 5 +- .../shared/reader/RM-Content-Width-Plus-44x16.svg | 5 +- .../shared/reader/RM-Line-Height-Minus-38x14.svg | 5 +- .../shared/reader/RM-Line-Height-Plus-38x24.svg | 5 +- .../shared/reader/RM-Type-Controls-Arrow.svg | 16 +- 27 files changed, 1762 insertions(+), 1791 deletions(-) delete mode 100644 toolkit/themes/shared/aboutReaderContent.css delete mode 100644 toolkit/themes/shared/aboutReaderControls.css delete mode 100644 toolkit/themes/shared/narrateControls.css diff --git a/browser/base/content/browser-sets.inc b/browser/base/content/browser-sets.inc index 0c753520f..d0c3d11cd 100644 --- a/browser/base/content/browser-sets.inc +++ b/browser/base/content/browser-sets.inc @@ -285,7 +285,7 @@ #endif - + diff --git a/browser/modules/ReaderParent.jsm b/browser/modules/ReaderParent.jsm index 6fcaada42..fcc0cc92b 100644 --- a/browser/modules/ReaderParent.jsm +++ b/browser/modules/ReaderParent.jsm @@ -11,31 +11,27 @@ this.EXPORTED_SYMBOLS = [ "ReaderParent" ]; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "UITour", "resource:///modules/UITour.jsm"); const gStringBundle = Services.strings.createBundle("chrome://global/locale/aboutReader.properties"); var ReaderParent = { - _readerModeInfoPanelOpen: false, - MESSAGES: [ "Reader:ArticleGet", "Reader:FaviconRequest", "Reader:UpdateReaderButton", ], - init: function() { + init() { let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager); for (let msg of this.MESSAGES) { mm.addMessageListener(msg, this); } }, - receiveMessage: function(message) { + receiveMessage(message) { switch (message.name) { case "Reader:ArticleGet": this._getArticle(message.data.url, message.target).then((article) => { @@ -60,7 +56,7 @@ var ReaderParent = { message.target.messageManager.sendAsyncMessage("Reader:FaviconReturn", { url: message.data.url, faviconUrl: favicon.path.replace(/^favicon:/, "") - }) + }); }, function onRejection(reason) { Cu.reportError("Error requesting favicon URL for about:reader content: " + reason); @@ -80,7 +76,7 @@ var ReaderParent = { } }, - updateReaderButton: function(browser) { + updateReaderButton(browser) { let win = browser.ownerGlobal; if (browser != win.gBrowser.selectedBrowser) { return; @@ -88,7 +84,11 @@ var ReaderParent = { let button = win.document.getElementById("reader-mode-button"); let command = win.document.getElementById("View:ReaderView"); - let key = win.document.getElementById("toggleReaderMode"); + let key = win.document.getElementById("key_toggleReaderMode"); + // aria-reader is not a real ARIA attribute. However, this will cause + // Gecko accessibility to expose the "reader" object attribute. We do this + // so that the reader state is easy for accessibility clients to access + // programmatically. if (browser.currentURI.spec.startsWith("about:reader")) { button.setAttribute("readeractive", true); button.hidden = false; @@ -98,6 +98,7 @@ var ReaderParent = { command.setAttribute("hidden", false); command.setAttribute("accesskey", gStringBundle.GetStringFromName("readerView.close.accesskey")); key.setAttribute("disabled", false); + browser.setAttribute("aria-reader", "active"); } else { button.removeAttribute("readeractive"); button.hidden = !browser.isArticle; @@ -107,24 +108,15 @@ var ReaderParent = { command.setAttribute("hidden", !browser.isArticle); command.setAttribute("accesskey", gStringBundle.GetStringFromName("readerView.enter.accesskey")); key.setAttribute("disabled", !browser.isArticle); - } - - let currentUriHost = browser.currentURI && browser.currentURI.asciiHost; - if (browser.isArticle && - !Services.prefs.getBoolPref("browser.reader.detectedFirstArticle") && - currentUriHost && !currentUriHost.endsWith("mozilla.org")) { - this.showReaderModeInfoPanel(browser); - Services.prefs.setBoolPref("browser.reader.detectedFirstArticle", true); - this._readerModeInfoPanelOpen = true; - } else if (this._readerModeInfoPanelOpen) { - if (UITour.isInfoOnTarget(win, "readerMode-urlBar")) { - UITour.hideInfo(win); + if (browser.isArticle) { + browser.setAttribute("aria-reader", "available"); + } else { + browser.removeAttribute("aria-reader"); } - this._readerModeInfoPanelOpen = false; } }, - forceShowReaderIcon: function(browser) { + forceShowReaderIcon(browser) { browser.isArticle = true; this.updateReaderButton(browser); }, @@ -136,35 +128,12 @@ var ReaderParent = { this.toggleReaderMode(event); }, - toggleReaderMode: function(event) { + toggleReaderMode(event) { let win = event.target.ownerGlobal; let browser = win.gBrowser.selectedBrowser; browser.messageManager.sendAsyncMessage("Reader:ToggleReaderMode"); }, - /** - * Shows an info panel from the UITour for Reader Mode. - * - * @param browser The that the tour should be started for. - */ - showReaderModeInfoPanel(browser) { - let win = browser.ownerGlobal; - let targetPromise = UITour.getTarget(win, "readerMode-urlBar"); - targetPromise.then(target => { - let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); - let icon = "chrome://browser/skin/"; - if (win.devicePixelRatio > 1) { - icon += "reader-tour@2x.png"; - } else { - icon += "reader-tour.png"; - } - UITour.showInfo(win, target, - browserBundle.GetStringFromName("readingList.promo.firstUse.readerView.title"), - browserBundle.GetStringFromName("readingList.promo.firstUse.readerView.body"), - icon); - }); - }, - /** * Gets an article for a given URL. This method will download and parse a document. * @@ -173,8 +142,8 @@ var ReaderParent = { * @return {Promise} * @resolves JS object representing the article, or null if no article is found. */ - _getArticle: Task.async(function* (url, browser) { - return yield ReaderMode.downloadAndParseDocument(url).catch(e => { + async _getArticle(url, browser) { + return await ReaderMode.downloadAndParseDocument(url).catch(e => { if (e && e.newURL) { // Pass up the error so we can navigate the browser in question to the new URL: throw e; @@ -182,5 +151,5 @@ var ReaderParent = { Cu.reportError("Error downloading and parsing document: " + e); return null; }); - }) + } }; diff --git a/mobile/android/themes/core/jar.mn b/mobile/android/themes/core/jar.mn index 248c00253..6bca48990 100644 --- a/mobile/android/themes/core/jar.mn +++ b/mobile/android/themes/core/jar.mn @@ -18,8 +18,6 @@ chrome.jar: skin/aboutMemory.css (aboutMemory.css) skin/aboutPrivateBrowsing.css (aboutPrivateBrowsing.css) skin/aboutReader.css (aboutReader.css) - skin/aboutReaderContent.css (aboutReaderContent.css) - skin/aboutReaderControls.css (aboutReaderControls.css) skin/aboutSupport.css (aboutSupport.css) skin/content.css (content.css) skin/scrollbar.css (scrollbar-apz.css) @@ -31,8 +29,6 @@ chrome.jar: % override chrome://global/skin/about.css chrome://browser/skin/about.css % override chrome://global/skin/aboutMemory.css chrome://browser/skin/aboutMemory.css % override chrome://global/skin/aboutReader.css chrome://browser/skin/aboutReader.css -% override chrome://global/skin/aboutReaderContent.css chrome://browser/skin/aboutReaderContent.css -% override chrome://global/skin/aboutReaderControls.css chrome://browser/skin/aboutReaderControls.css % override chrome://global/skin/aboutSupport.css chrome://browser/skin/aboutSupport.css % override chrome://global/skin/media/videocontrols.css chrome://browser/skin/touchcontrols.css % override chrome://global/skin/netError.css chrome://browser/skin/netError.css diff --git a/toolkit/components/narrate/NarrateControls.jsm b/toolkit/components/narrate/NarrateControls.jsm index 7d8794b18..828292cc2 100644 --- a/toolkit/components/narrate/NarrateControls.jsm +++ b/toolkit/components/narrate/NarrateControls.jsm @@ -16,9 +16,10 @@ this.EXPORTED_SYMBOLS = ["NarrateControls"]; var gStrings = Services.strings.createBundle("chrome://global/locale/narrate.properties"); -function NarrateControls(mm, win) { +function NarrateControls(mm, win, languagePromise) { this._mm = mm; this._winRef = Cu.getWeakReference(win); + this._languagePromise = languagePromise; win.addEventListener("unload", this); @@ -37,16 +38,12 @@ function NarrateControls(mm, win) { } let dropdown = win.document.createElement("ul"); - dropdown.className = "dropdown"; - dropdown.id = "narrate-dropdown"; + dropdown.className = "dropdown narrate-dropdown"; // We need inline svg here for the animation to work (bug 908634 & 1190881). - // The style animation can't be scoped (bug 830056). + // eslint-disable-next-line no-unsanitized/property dropdown.innerHTML = - localize` -
  • -
  • `; - this.narrator = new Narrator(win); + this.narrator = new Narrator(win, languagePromise); let branch = Services.prefs.getBranch("narrate."); let selectLabel = gStrings.GetStringFromName("selectvoicelabel"); this.voiceSelect = new VoiceSelect(win, selectLabel); this.voiceSelect.element.addEventListener("change", this); - this.voiceSelect.element.id = "voice-select"; + this.voiceSelect.element.classList.add("voice-select"); win.speechSynthesis.addEventListener("voiceschanged", this); - dropdown.querySelector("#narrate-voices").appendChild( + dropdown.querySelector(".narrate-voices").appendChild( this.voiceSelect.element); dropdown.addEventListener("click", this, true); - let rateRange = dropdown.querySelector("#narrate-rate > input"); + let rateRange = dropdown.querySelector(".narrate-rate > input"); rateRange.addEventListener("change", this); // The rate is stored as an integer. @@ -131,15 +128,15 @@ function NarrateControls(mm, win) { this._setupVoices(); - let tb = win.document.getElementById("reader-toolbar"); + let tb = win.document.querySelector(".reader-toolbar"); tb.appendChild(dropdown); } NarrateControls.prototype = { - handleEvent: function(evt) { + handleEvent(evt) { switch (evt.type) { case "change": - if (evt.target.id == "narrate-rate-input") { + if (evt.target.classList.contains("narrate-rate-input")) { this._onRateInput(evt); } else { this._onVoiceChange(); @@ -162,8 +159,8 @@ NarrateControls.prototype = { /** * Returns true if synth voices are available. */ - _setupVoices: function() { - return this.narrator.languagePromise.then(language => { + _setupVoices() { + return this._languagePromise.then(language => { this.voiceSelect.clear(); let win = this._win; let voicePrefs = this._getVoicePref(); @@ -190,7 +187,7 @@ NarrateControls.prototype = { this.voiceSelect.addOptions(options); } - let narrateToggle = win.document.getElementById("narrate-toggle"); + let narrateToggle = win.document.querySelector(".narrate-toggle"); let histogram = Services.telemetry.getKeyedHistogramById( "NARRATE_CONTENT_BY_LANGUAGE_2"); let initial = !this._voicesInitialized; @@ -210,7 +207,7 @@ NarrateControls.prototype = { }); }, - _getVoicePref: function() { + _getVoicePref() { let voicePref = Services.prefs.getCharPref("narrate.voice"); try { return JSON.parse(voicePref); @@ -219,15 +216,15 @@ NarrateControls.prototype = { } }, - _onRateInput: function(evt) { + _onRateInput(evt) { AsyncPrefs.set("narrate.rate", parseInt(evt.target.value, 10)); this.narrator.setRate(this._convertRate(evt.target.value)); }, - _onVoiceChange: function() { + _onVoiceChange() { let voice = this.voice; this.narrator.setVoice(voice); - this.narrator.languagePromise.then(language => { + this._languagePromise.then(language => { if (language) { let voicePref = this._getVoicePref(); voicePref[language || "default"] = voice; @@ -236,42 +233,39 @@ NarrateControls.prototype = { }); }, - _onButtonClick: function(evt) { - switch (evt.target.id) { - case "narrate-skip-previous": - this.narrator.skipPrevious(); - break; - case "narrate-skip-next": - this.narrator.skipNext(); - break; - case "narrate-start-stop": - if (this.narrator.speaking) { - this.narrator.stop(); - } else { - this._updateSpeechControls(true); - let options = { rate: this.rate, voice: this.voice }; - this.narrator.start(options).then(() => { - this._updateSpeechControls(false); - }, err => { - Cu.reportError(`Narrate failed: ${err}.`); - this._updateSpeechControls(false); - }); - } - break; + _onButtonClick(evt) { + let classList = evt.target.classList; + if (classList.contains("narrate-skip-previous")) { + this.narrator.skipPrevious(); + } else if (classList.contains("narrate-skip-next")) { + this.narrator.skipNext(); + } else if (classList.contains("narrate-start-stop")) { + if (this.narrator.speaking) { + this.narrator.stop(); + } else { + this._updateSpeechControls(true); + let options = { rate: this.rate, voice: this.voice }; + this.narrator.start(options).then(() => { + this._updateSpeechControls(false); + }, err => { + Cu.reportError(`Narrate failed: ${err}.`); + this._updateSpeechControls(false); + }); + } } }, - _updateSpeechControls: function(speaking) { - let dropdown = this._doc.getElementById("narrate-dropdown"); + _updateSpeechControls(speaking) { + let dropdown = this._doc.querySelector(".narrate-dropdown"); dropdown.classList.toggle("keep-open", speaking); dropdown.classList.toggle("speaking", speaking); - let startStopButton = this._doc.getElementById("narrate-start-stop"); + let startStopButton = this._doc.querySelector(".narrate-start-stop"); startStopButton.title = gStrings.GetStringFromName(speaking ? "stop" : "start"); - this._doc.getElementById("narrate-skip-previous").disabled = !speaking; - this._doc.getElementById("narrate-skip-next").disabled = !speaking; + this._doc.querySelector(".narrate-skip-previous").disabled = !speaking; + this._doc.querySelector(".narrate-skip-next").disabled = !speaking; if (speaking) { TelemetryStopwatch.start("NARRATE_CONTENT_SPEAKTIME_MS", this); @@ -280,7 +274,7 @@ NarrateControls.prototype = { } }, - _createVoiceLabel: function(voice) { + _createVoiceLabel(voice) { // This is a highly imperfect method of making human-readable labels // for system voices. Because each platform has a different naming scheme // for voices, we use a different method for each platform. @@ -303,7 +297,7 @@ NarrateControls.prototype = { } }, - _getLanguageName: function(lang) { + _getLanguageName(lang) { if (!this._langStrings) { this._langStrings = Services.strings.createBundle( "chrome://global/locale/languageNames.properties "); @@ -317,7 +311,7 @@ NarrateControls.prototype = { } }, - _convertRate: function(rate) { + _convertRate(rate) { // We need to convert a relative percentage value to a fraction rate value. // eg. -100 is half the speed, 100 is twice the speed in percentage, // 0.5 is half the speed and 2 is twice the speed in fractions. @@ -334,7 +328,7 @@ NarrateControls.prototype = { get rate() { return this._convertRate( - this._doc.getElementById("narrate-rate-input").value); + this._doc.querySelector(".narrate-rate-input").value); }, get voice() { diff --git a/toolkit/components/narrate/Narrator.jsm b/toolkit/components/narrate/Narrator.jsm index ade06510e..ac0b2e040 100644 --- a/toolkit/components/narrate/Narrator.jsm +++ b/toolkit/components/narrate/Narrator.jsm @@ -8,8 +8,6 @@ const { interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector", - "resource:///modules/translation/LanguageDetector.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); @@ -24,29 +22,13 @@ const kTextStylesRules = ["font-family", "font-kerning", "font-size", "line-height", "letter-spacing", "text-orientation", "text-transform", "word-spacing"]; -function Narrator(win) { +function Narrator(win, languagePromise) { this._winRef = Cu.getWeakReference(win); + this._languagePromise = languagePromise; this._inTest = Services.prefs.getBoolPref("narrate.test"); this._speechOptions = {}; this._startTime = 0; this._stopped = false; - - this.languagePromise = new Promise(resolve => { - let detect = () => { - win.document.removeEventListener("AboutReaderContentReady", detect); - let sampleText = this._doc.getElementById( - "moz-reader-content").textContent.substring(0, 60 * 1024); - LanguageDetector.detectLanguage(sampleText).then(result => { - resolve(result.confident ? result.language : null); - }); - }; - - if (win.document.body.classList.contains("loaded")) { - detect(); - } else { - win.document.addEventListener("AboutReaderContentReady", detect); - } - }); } Narrator.prototype = { @@ -71,7 +53,7 @@ Narrator.prototype = { // For example, paragraphs. But nested anchors and other elements // are not interesting since their text already appears in their // parent's textContent. - acceptNode: function(node) { + acceptNode(node) { if (this._matches.has(node.parentNode)) { // Reject sub-trees of accepted nodes. return nf.FILTER_REJECT; @@ -107,7 +89,7 @@ Narrator.prototype = { // are no other strong references, and it will be GC'ed. Instead, // we rely on the window's lifetime and use it as a weak reference. this._treeWalkerRef.set(this._win, - this._doc.createTreeWalker(this._doc.getElementById("container"), + this._doc.createTreeWalker(this._doc.querySelector(".container"), nf.SHOW_ELEMENT, filter, false)); } @@ -124,7 +106,7 @@ Narrator.prototype = { this._win.speechSynthesis.pending; }, - _getVoice: function(voiceURI) { + _getVoice(voiceURI) { if (!this._voiceMap || !this._voiceMap.has(voiceURI)) { this._voiceMap = new Map( this._win.speechSynthesis.getVoices().map(v => [v.voiceURI, v])); @@ -133,7 +115,7 @@ Narrator.prototype = { return this._voiceMap.get(voiceURI); }, - _isParagraphInView: function(paragraph) { + _isParagraphInView(paragraph) { if (!paragraph) { return false; } @@ -142,13 +124,13 @@ Narrator.prototype = { return bb.top >= 0 && bb.top < this._win.innerHeight; }, - _sendTestEvent: function(eventType, detail) { + _sendTestEvent(eventType, detail) { let win = this._win; win.dispatchEvent(new win.CustomEvent(eventType, { detail: Cu.cloneInto(detail, win.document) })); }, - _speakInner: function() { + _speakInner() { this._win.speechSynthesis.cancel(); let tw = this._treeWalker; let paragraph = tw.currentNode; @@ -238,18 +220,12 @@ Narrator.prototype = { return; } - // Match non-whitespace. This isn't perfect, but the most universal - // solution for now. - let reWordBoundary = /\S+/g; - // Match the first word from the boundary event offset. - reWordBoundary.lastIndex = e.charIndex; - let firstIndex = reWordBoundary.exec(paragraph.textContent); - if (firstIndex) { - highlighter.highlight(firstIndex.index, reWordBoundary.lastIndex); + if (e.charLength) { + highlighter.highlight(e.charIndex, e.charLength); if (this._inTest) { this._sendTestEvent("wordhighlight", { - start: firstIndex.index, - end: reWordBoundary.lastIndex + start: e.charIndex, + end: e.charIndex + e.charLength }); } } @@ -259,14 +235,14 @@ Narrator.prototype = { }); }, - start: function(speechOptions) { + start(speechOptions) { this._speechOptions = { rate: speechOptions.rate, voice: this._getVoice(speechOptions.voice) }; this._stopped = false; - return this.languagePromise.then(language => { + return this._languagePromise.then(language => { if (!this._speechOptions.voice) { this._speechOptions.lang = language; } @@ -288,32 +264,32 @@ Narrator.prototype = { }); }, - stop: function() { + stop() { this._stopped = true; this._win.speechSynthesis.cancel(); }, - skipNext: function() { + skipNext() { this._win.speechSynthesis.cancel(); }, - skipPrevious: function() { + skipPrevious() { this._goBackParagraphs(this._timeIntoParagraph < PREV_THRESHOLD ? 2 : 1); }, - setRate: function(rate) { + setRate(rate) { this._speechOptions.rate = rate; /* repeat current paragraph */ this._goBackParagraphs(1); }, - setVoice: function(voice) { + setVoice(voice) { this._speechOptions.voice = this._getVoice(voice); /* repeat current paragraph */ this._goBackParagraphs(1); }, - _goBackParagraphs: function(count) { + _goBackParagraphs(count) { let tw = this._treeWalker; for (let i = 0; i < count; i++) { if (!tw.previousNode()) { @@ -338,13 +314,13 @@ Highlighter.prototype = { * Highlight the range within offsets relative to the container. * * @param {Number} startOffset the start offset - * @param {Number} endOffset the end offset + * @param {Number} length the length in characters of the range */ - highlight: function(startOffset, endOffset) { + highlight(startOffset, length) { let containerRect = this.container.getBoundingClientRect(); - let range = this._getRange(startOffset, endOffset); + let range = this._getRange(startOffset, startOffset + length); let rangeRects = range.getClientRects(); - let win = this.container.ownerDocument.defaultView; + let win = this.container.ownerGlobal; let computedStyle = win.getComputedStyle(range.endContainer.parentNode); let nodes = this._getFreshHighlightNodes(rangeRects.length); @@ -386,7 +362,7 @@ Highlighter.prototype = { /** * Releases reference to container and removes all highlight nodes. */ - remove: function() { + remove() { for (let node of this._nodes) { node.remove(); } @@ -400,7 +376,7 @@ Highlighter.prototype = { * * @param {Number} count number of nodes needed */ - _getFreshHighlightNodes: function(count) { + _getFreshHighlightNodes(count) { let doc = this.container.ownerDocument; let nodes = Array.from(this._nodes); @@ -427,7 +403,7 @@ Highlighter.prototype = { * @param {Number} startOffset the start offset * @param {Number} endOffset the end offset */ - _getRange: function(startOffset, endOffset) { + _getRange(startOffset, endOffset) { let doc = this.container.ownerDocument; let i = 0; let treeWalker = doc.createTreeWalker( diff --git a/toolkit/components/narrate/VoiceSelect.jsm b/toolkit/components/narrate/VoiceSelect.jsm index b283a06b3..861a21c97 100644 --- a/toolkit/components/narrate/VoiceSelect.jsm +++ b/toolkit/components/narrate/VoiceSelect.jsm @@ -13,6 +13,7 @@ function VoiceSelect(win, label) { let element = win.document.createElement("div"); element.classList.add("voiceselect"); + // eslint-disable-next-line no-unsanitized/property element.innerHTML = ` - - -