diff options
61 files changed, 798 insertions, 519 deletions
diff --git a/accessible/base/EventTree.cpp b/accessible/base/EventTree.cpp index 84c4aafdd..e9867b3b5 100644 --- a/accessible/base/EventTree.cpp +++ b/accessible/base/EventTree.cpp @@ -8,6 +8,7 @@ #include "Accessible-inl.h" #include "nsEventShell.h" #include "DocAccessible.h" +#include "DocAccessible-inl.h" #include "EmbeddedObjCollector.h" #include "NotificationController.h" #ifdef A11Y_LOG diff --git a/accessible/base/Logging.cpp b/accessible/base/Logging.cpp index afc37ef85..619af9e0a 100644 --- a/accessible/base/Logging.cpp +++ b/accessible/base/Logging.cpp @@ -9,6 +9,7 @@ #include "Accessible-inl.h" #include "AccEvent.h" #include "DocAccessible.h" +#include "DocAccessible-inl.h" #include "nsAccessibilityService.h" #include "nsCoreUtils.h" #include "OuterDocAccessible.h" @@ -23,6 +24,7 @@ #include "nsIDocShellTreeItem.h" #include "nsIURI.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/HTMLBodyElement.h" using namespace mozilla; using namespace mozilla::a11y; diff --git a/accessible/base/NotificationController.cpp b/accessible/base/NotificationController.cpp index 3f1e5bcd3..30382e75e 100644 --- a/accessible/base/NotificationController.cpp +++ b/accessible/base/NotificationController.cpp @@ -17,6 +17,7 @@ using namespace mozilla; using namespace mozilla::a11y; +using namespace mozilla::dom; //////////////////////////////////////////////////////////////////////////////// // NotificationCollector diff --git a/accessible/base/moz.build b/accessible/base/moz.build index e8e382e39..024baef4c 100644 --- a/accessible/base/moz.build +++ b/accessible/base/moz.build @@ -55,7 +55,7 @@ SOURCES += [ ] if CONFIG['A11Y_LOG']: - UNIFIED_SOURCES += [ + SOURCES += [ 'Logging.cpp', ] diff --git a/accessible/base/nsCoreUtils.cpp b/accessible/base/nsCoreUtils.cpp index effe66be2..e0ca43ca1 100644 --- a/accessible/base/nsCoreUtils.cpp +++ b/accessible/base/nsCoreUtils.cpp @@ -9,6 +9,7 @@ #include "nsIBaseWindow.h" #include "nsIDocShellTreeOwner.h" +#include "nsIContentInlines.h" #include "nsIDocument.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMHTMLElement.h" diff --git a/accessible/base/nsEventShell.cpp b/accessible/base/nsEventShell.cpp index e070acee5..00e4e9b89 100644 --- a/accessible/base/nsEventShell.cpp +++ b/accessible/base/nsEventShell.cpp @@ -9,6 +9,10 @@ #include "mozilla/StaticPtr.h" +#ifdef A11Y_LOG +#include "Logging.h" +#endif + using namespace mozilla; using namespace mozilla::a11y; diff --git a/accessible/mac/moz.build b/accessible/mac/moz.build index a8f07c48b..1ead3d8f6 100644 --- a/accessible/mac/moz.build +++ b/accessible/mac/moz.build @@ -13,7 +13,7 @@ EXPORTS.mozilla.a11y += [ 'HyperTextAccessibleWrap.h', ] -UNIFIED_SOURCES += [ +SOURCES += [ 'AccessibleWrap.mm', 'DocAccessibleWrap.mm', 'MacUtils.mm', diff --git a/accessible/mac/mozAccessible.mm b/accessible/mac/mozAccessible.mm index e1cdba694..07868fea6 100644 --- a/accessible/mac/mozAccessible.mm +++ b/accessible/mac/mozAccessible.mm @@ -49,42 +49,6 @@ using namespace mozilla::a11y; // - NSAccessibilityMathPrescriptsAttribute @"AXMathPrescripts" // - NSAccessibilityMathPostscriptsAttribute @"AXMathPostscripts" -// convert an array of Gecko accessibles to an NSArray of native accessibles -static inline NSMutableArray* -ConvertToNSArray(nsTArray<Accessible*>& aArray) -{ - NSMutableArray* nativeArray = [[NSMutableArray alloc] init]; - - // iterate through the list, and get each native accessible. - size_t totalCount = aArray.Length(); - for (size_t i = 0; i < totalCount; i++) { - Accessible* curAccessible = aArray.ElementAt(i); - mozAccessible* curNative = GetNativeFromGeckoAccessible(curAccessible); - if (curNative) - [nativeArray addObject:GetObjectOrRepresentedView(curNative)]; - } - - return nativeArray; -} - -// convert an array of Gecko proxy accessibles to an NSArray of native accessibles -static inline NSMutableArray* -ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray) -{ - NSMutableArray* nativeArray = [[NSMutableArray alloc] init]; - - // iterate through the list, and get each native accessible. - size_t totalCount = aArray.Length(); - for (size_t i = 0; i < totalCount; i++) { - ProxyAccessible* curAccessible = aArray.ElementAt(i); - mozAccessible* curNative = GetNativeFromProxy(curAccessible); - if (curNative) - [nativeArray addObject:GetObjectOrRepresentedView(curNative)]; - } - - return nativeArray; -} - #pragma mark - @implementation mozAccessible diff --git a/accessible/mac/mozHTMLAccessible.mm b/accessible/mac/mozHTMLAccessible.mm index 6c4925589..2079a4aa6 100644 --- a/accessible/mac/mozHTMLAccessible.mm +++ b/accessible/mac/mozHTMLAccessible.mm @@ -12,6 +12,8 @@ #import "nsCocoaUtils.h" +using namespace mozilla::a11y; + @implementation mozHeadingAccessible - (NSString*)title diff --git a/accessible/mac/mozTableAccessible.mm b/accessible/mac/mozTableAccessible.mm index a3612e5bc..6ad157b9f 100644 --- a/accessible/mac/mozTableAccessible.mm +++ b/accessible/mac/mozTableAccessible.mm @@ -5,9 +5,50 @@ * 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/. */ +#import "Accessible-inl.h" #import "mozTableAccessible.h" +#import "TableAccessible.h" +#import "TableCellAccessible.h" #import "nsCocoaUtils.h" +using namespace mozilla::a11y; + +// convert an array of Gecko accessibles to an NSArray of native accessibles +static inline NSMutableArray* +ConvertToNSArray(nsTArray<Accessible*>& aArray) +{ + NSMutableArray* nativeArray = [[NSMutableArray alloc] init]; + + // iterate through the list, and get each native accessible. + size_t totalCount = aArray.Length(); + for (size_t i = 0; i < totalCount; i++) { + Accessible* curAccessible = aArray.ElementAt(i); + mozAccessible* curNative = GetNativeFromGeckoAccessible(curAccessible); + if (curNative) + [nativeArray addObject:GetObjectOrRepresentedView(curNative)]; + } + + return nativeArray; +} + +// convert an array of Gecko proxy accessibles to an NSArray of native accessibles +static inline NSMutableArray* +ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray) +{ + NSMutableArray* nativeArray = [[NSMutableArray alloc] init]; + + // iterate through the list, and get each native accessible. + size_t totalCount = aArray.Length(); + for (size_t i = 0; i < totalCount; i++) { + ProxyAccessible* curAccessible = aArray.ElementAt(i); + mozAccessible* curNative = GetNativeFromProxy(curAccessible); + if (curNative) + [nativeArray addObject:GetObjectOrRepresentedView(curNative)]; + } + + return nativeArray; +} + @implementation mozTablePartAccessible - (BOOL)isLayoutTablePart; { diff --git a/accessible/mac/mozTextAccessible.mm b/accessible/mac/mozTextAccessible.mm index 0909cd512..1f433b802 100644 --- a/accessible/mac/mozTextAccessible.mm +++ b/accessible/mac/mozTextAccessible.mm @@ -12,6 +12,7 @@ #import "mozTextAccessible.h" +using namespace mozilla; using namespace mozilla::a11y; inline bool diff --git a/accessible/xpcom/xpcAccessibleHyperText.cpp b/accessible/xpcom/xpcAccessibleHyperText.cpp index b31544ac7..4b6f32e93 100644 --- a/accessible/xpcom/xpcAccessibleHyperText.cpp +++ b/accessible/xpcom/xpcAccessibleHyperText.cpp @@ -7,6 +7,7 @@ #include "xpcAccessibleHyperText.h" #include "Accessible-inl.h" +#include "mozilla/a11y/DocAccessibleParent.h" #include "HyperTextAccessible-inl.h" #include "TextRange.h" #include "xpcAccessibleDocument.h" diff --git a/application/basilisk/base/content/tab-content.js b/application/basilisk/base/content/tab-content.js index 35ef8ceb2..fec13eba7 100644 --- a/application/basilisk/base/content/tab-content.js +++ b/application/basilisk/base/content/tab-content.js @@ -20,6 +20,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AboutReader", "resource://gre/modules/AboutReader.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Readerable", + "resource://gre/modules/Readerable.jsm"); XPCOMUtils.defineLazyGetter(this, "SimpleServiceDiscovery", function() { let ssdp = Cu.import("resource://gre/modules/SimpleServiceDiscovery.jsm", {}).SimpleServiceDiscovery; // Register targets @@ -336,7 +338,7 @@ var AboutReaderListener = { * painted is not going to work. */ updateReaderButton: function(forceNonArticle) { - if (!ReaderMode.isEnabledForParseOnLoad || this.isAboutReader || + if (!Readerable.isEnabledForParseOnLoad || this.isAboutReader || !content || !(content.document instanceof content.HTMLDocument) || content.document.mozSyntheticDocument) { return; @@ -375,7 +377,7 @@ var AboutReaderListener = { this.cancelPotentialPendingReadabilityCheck(); // Only send updates when there are articles; there's no point updating with // |false| all the time. - if (ReaderMode.isProbablyReaderable(content.document)) { + if (Readerable.isProbablyReaderable(content.document)) { sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true }); } else if (forceNonArticle) { sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false }); diff --git a/application/basilisk/base/content/utilityOverlay.js b/application/basilisk/base/content/utilityOverlay.js index f3ebf3b7e..3d27f7d27 100644 --- a/application/basilisk/base/content/utilityOverlay.js +++ b/application/basilisk/base/content/utilityOverlay.js @@ -17,6 +17,9 @@ XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService", "@mozilla.org/browser/aboutnewtab-service;1", "nsIAboutNewTabService"); +XPCOMUtils.defineLazyModuleGetter(this, "Deprecated", + "resource://gre/modules/Deprecated.jsm"); + this.__defineGetter__("BROWSER_NEW_TAB_URL", () => { if (PrivateBrowsingUtils.isWindowPrivate(window) && !PrivateBrowsingUtils.permanentPrivateBrowsing && @@ -34,7 +37,7 @@ var gBidiUI = false; * Determines whether the given url is considered a special URL for new tabs. */ function isBlankPageURL(aURL) { - return aURL == "about:blank" || aURL == "about:newtab" || aURL == "about:logopage"; + return aURL == "about:blank" || aURL == BROWSER_NEW_TAB_URL || aURL == "about:logopage"; } function getBrowserURL() @@ -42,6 +45,13 @@ function getBrowserURL() return "chrome://browser/content/browser.xul"; } +function getBoolPref(pref, defaultValue) { + Deprecated.warning("getBoolPref is deprecated and will be removed in a future release. " + + "You should use Services.pref.getBoolPref (Services.jsm).", + "https://github.com/MoonchildProductions/UXP/issues/989"); + return Services.prefs.getBoolPref(pref, defaultValue); +} + function getTopWin(skipPopups) { // If this is called in a browser window, use that window regardless of // whether it's the frontmost window, since commands can be executed in diff --git a/application/palemoon/app/moz.build b/application/palemoon/app/moz.build index c11f4c37e..8166760af 100644 --- a/application/palemoon/app/moz.build +++ b/application/palemoon/app/moz.build @@ -20,6 +20,7 @@ if CONFIG['LIBXUL_SDK']: SOURCES += ['nsBrowserApp.cpp'] FINAL_TARGET_FILES += ['blocklist.xml'] +FINAL_TARGET_FILES.defaults += ['permissions'] FINAL_TARGET_FILES.defaults.profile += ['profile/prefs.js'] DEFINES['APP_VERSION'] = CONFIG['MOZ_APP_VERSION'] diff --git a/application/palemoon/app/permissions b/application/palemoon/app/permissions new file mode 100644 index 000000000..4d90be82a --- /dev/null +++ b/application/palemoon/app/permissions @@ -0,0 +1,14 @@ +# This file has default permissions for the permission manager.
+# The file-format is strict:
+# * matchtype \t type \t permission \t host
+# * "origin" should be used for matchtype, "host" is supported for legacy reasons
+# * type is a string that identifies the type of permission (e.g. "cookie")
+# * permission is an integer between 1 and 15
+# See nsPermissionManager.cpp for more...
+
+# XPInstall
+origin install 1 http://www.palemoon.org
+origin install 1 https://www.palemoon.org
+
+origin install 1 http://addons.palemoon.org
+origin install 1 https://addons.palemoon.org
diff --git a/application/palemoon/app/profile/palemoon.js b/application/palemoon/app/profile/palemoon.js index bd1b62cc3..974d76d4b 100644 --- a/application/palemoon/app/profile/palemoon.js +++ b/application/palemoon/app/profile/palemoon.js @@ -206,9 +206,6 @@ pref("extensions.dss.switchPending", false); // Non-dynamic switch pending af pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.name", "chrome://browser/locale/browser.properties"); pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description", "chrome://browser/locale/browser.properties"); -pref("xpinstall.whitelist.add", "addons.mozilla.org,www.palemoon.org,addons.palemoon.org"); -pref("xpinstall.whitelist.add.36", ""); -pref("xpinstall.whitelist.add.180", ""); pref("xpinstall.whitelist.required", false); // Allow installing XPI add-ons by direct URL requests (no referrer) pref("xpinstall.whitelist.directRequest", true); @@ -1103,6 +1100,9 @@ pref("full-screen-api.enabled", true); // 0-100 (currently) pref("permissions.places-sites-limit", 50); +// Built-in default permissions. +pref("permissions.manager.defaultsUrl", "resource://app/defaults/permissions"); + // Startup Crash Tracking // number of startup crashes that can occur before starting into safe mode automatically // (this pref has no effect if more than 6 hours have passed since the last crash) diff --git a/application/palemoon/base/content/baseMenuOverlay.xul b/application/palemoon/base/content/baseMenuOverlay.xul index a006ed5c6..bccdec265 100644 --- a/application/palemoon/base/content/baseMenuOverlay.xul +++ b/application/palemoon/base/content/baseMenuOverlay.xul @@ -57,15 +57,21 @@ label="&helpTroubleshootingInfo.label;" oncommand="openTroubleshootingPage()" onclick="checkForMiddleClick(this, event);"/> + <menuitem id="helpSafeMode" + accesskey="&helpSafeMode.accesskey;" + label="&helpSafeMode.label;" + oncommand="restart(true);"/> + <menuseparator/> + <menuitem id="releaseNotes" + accesskey="&helpReleaseNotes.accesskey;" + label="&helpReleaseNotes.label;" + oncommand="openReleaseNotes();" + onclick="checkForMiddleClick(this, event);"/> <menuitem id="feedbackPage" accesskey="&helpFeedbackPage.accesskey;" label="&helpFeedbackPage.label;" oncommand="openFeedbackPage()" onclick="checkForMiddleClick(this, event);"/> - <menuitem id="helpSafeMode" - accesskey="&helpSafeMode.accesskey;" - label="&helpSafeMode.label;" - oncommand="restart(true);"/> <menuseparator id="updatesSeparator"/> <menuitem id="checkForUpdates" class="menuitem-iconic" #ifdef MOZ_UPDATER diff --git a/application/palemoon/base/content/browser-appmenu.inc b/application/palemoon/base/content/browser-appmenu.inc index ffb117a60..9d202c965 100644 --- a/application/palemoon/base/content/browser-appmenu.inc +++ b/application/palemoon/base/content/browser-appmenu.inc @@ -350,14 +350,19 @@ label="&helpTroubleshootingInfo.label;" oncommand="openTroubleshootingPage()" onclick="checkForMiddleClick(this,event);"/> + <menuitem id="appmenu_safeMode" + label="&appMenuSafeMode.label;" + oncommand="restart(true);"/> + <menuseparator/> + <menuitem id="appmenu_releaseNotes" + accesskey="&helpReleaseNotes.accesskey;" + label="&helpReleaseNotes.label;" + oncommand="openReleaseNotes();" + onclick="checkForMiddleClick(this, event);"/> <menuitem id="appmenu_feedbackPage" label="&helpFeedbackPage.label;" oncommand="openFeedbackPage()" onclick="checkForMiddleClick(this, event);"/> - <menuseparator/> - <menuitem id="appmenu_safeMode" - label="&appMenuSafeMode.label;" - oncommand="restart(true);"/> #ifdef MOZ_UPDATER <menuseparator/> <menuitem id="appmenu_checkForUpdates" diff --git a/application/palemoon/base/content/browser.js b/application/palemoon/base/content/browser.js index 7672fa3a8..3f8a584bf 100644 --- a/application/palemoon/base/content/browser.js +++ b/application/palemoon/base/content/browser.js @@ -412,6 +412,10 @@ var gURLBarSettings = { }, writePlaceholder: function() { + if (!gURLBar) { + return; + } + let attribute = "placeholder"; let prefs = this.prefSuggests.map(pref => { return this.prefSuggest + pref; @@ -3471,6 +3475,7 @@ function BrowserToolboxCustomizeDone(aToolboxChanged) { // Update the urlbar if (gURLBar) { + gURLBarSettings.writePlaceholder(); URLBarSetURI(); XULBrowserWindow.asyncUpdateUI(); BookmarkingUI.updateStarState(); diff --git a/application/palemoon/base/content/tabbrowser.xml b/application/palemoon/base/content/tabbrowser.xml index d9366f488..aa1a89200 100644 --- a/application/palemoon/base/content/tabbrowser.xml +++ b/application/palemoon/base/content/tabbrowser.xml @@ -402,11 +402,18 @@ let promptBox = { appendPrompt : function(args, onCloseCallback) { let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt"); - stack.appendChild(newPrompt); + // stack.appendChild(newPrompt); + stack.insertBefore(newPrompt, browser.nextSibling); browser.setAttribute("tabmodalPromptShowing", true); newPrompt.clientTop; // style flush to assure binding is attached + let prompts = this.listPrompts(); + if (prompts.length > 1) { + // Let's hide ourself behind the current prompt. + newPrompt.hidden = true; + } + let tab = self._getTabForContentWindow(browser.contentWindow); newPrompt.init(args, tab, onCloseCallback); return newPrompt; @@ -418,6 +425,7 @@ let prompts = this.listPrompts(); if (prompts.length) { let prompt = prompts[prompts.length - 1]; + prompt.hidden = false; prompt.Dialog.setDefaultFocus(); } else { browser.removeAttribute("tabmodalPromptShowing"); @@ -823,13 +831,8 @@ } let sizedIconUrl = browser.mIconURL || ""; - if (sizedIconUrl) { - let size = Math.round(16 * window.devicePixelRatio); - sizedIconUrl += (sizedIconUrl.includes("#") ? "&" : "#") + - "-moz-resolution=" + size + "," + size; - } if (sizedIconUrl != aTab.getAttribute("image")) { - if (browser.mIconURL) + if (sizedIconUrl) aTab.setAttribute("image", sizedIconUrl); else aTab.removeAttribute("image"); diff --git a/application/palemoon/base/content/utilityOverlay.js b/application/palemoon/base/content/utilityOverlay.js index c2a8baeed..d7c8088c7 100644 --- a/application/palemoon/base/content/utilityOverlay.js +++ b/application/palemoon/base/content/utilityOverlay.js @@ -12,6 +12,9 @@ Components.utils.import("resource:///modules/RecentWindow.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ShellService", "resource:///modules/ShellService.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Deprecated", + "resource://gre/modules/Deprecated.jsm"); + XPCOMUtils.defineLazyGetter(this, "BROWSER_NEW_TAB_URL", function () { const PREF = "browser.newtab.url"; @@ -57,6 +60,14 @@ function getBrowserURL() return "chrome://browser/content/browser.xul"; } +function getBoolPref(pref, defaultValue) { + Deprecated.warning("getBoolPref is deprecated and will be removed in a future release. " + + "You should use Services.pref.getBoolPref (Services.jsm).", + "https://github.com/MoonchildProductions/UXP/issues/989"); + return Services.prefs.getBoolPref(pref, defaultValue); +} + + function getTopWin(skipPopups) { // If this is called in a browser window, use that window regardless of // whether it's the frontmost window, since commands can be executed in @@ -573,10 +584,16 @@ function buildHelpMenu() var checkForUpdates = document.getElementById("checkForUpdates"); var appMenuCheckForUpdates = document.getElementById("appmenu_checkForUpdates"); var canCheckForUpdates = updates.canCheckForUpdates; + checkForUpdates.setAttribute("disabled", !canCheckForUpdates); - appMenuCheckForUpdates.setAttribute("disabled", !canCheckForUpdates); - if (!canCheckForUpdates) + + if (appMenuCheckForUpdates) { + appMenuCheckForUpdates.setAttribute("disabled", !canCheckForUpdates); + } + + if (!canCheckForUpdates) { return; + } var strings = document.getElementById("bundle_browser"); var activeUpdate = um.activeUpdate; @@ -612,19 +629,31 @@ function buildHelpMenu() } checkForUpdates.label = getStringWithUpdateName("updatesItem_" + key); - appMenuCheckForUpdates.label = getStringWithUpdateName("updatesItem_" + key); + + if (appMenuCheckForUpdates) { + appMenuCheckForUpdates.label = getStringWithUpdateName("updatesItem_" + key); + } + // updatesItem_default.accesskey, updatesItem_downloading.accesskey, // updatesItem_resume.accesskey or updatesItem_pending.accesskey checkForUpdates.accessKey = strings.getString("updatesItem_" + key + ".accesskey"); - appMenuCheckForUpdates.accessKey = strings.getString("updatesItem_" + key + - ".accesskey"); + + if (appMenuCheckForUpdates) { + appMenuCheckForUpdates.accessKey = strings.getString("updatesItem_" + key + + ".accesskey"); + } + if (um.activeUpdate && updates.isDownloading) { checkForUpdates.setAttribute("loading", "true"); - appMenuCheckForUpdates.setAttribute("loading", "true"); + if (appMenuCheckForUpdates) { + appMenuCheckForUpdates.setAttribute("loading", "true"); + } } else { checkForUpdates.removeAttribute("loading"); - appMenuCheckForUpdates.removeAttribute("loading"); + if (appMenuCheckForUpdates) { + appMenuCheckForUpdates.removeAttribute("loading"); + } } #else #ifndef XP_MACOSX @@ -692,6 +721,18 @@ function openAdvancedPreferences(tabID) } /** + * Opens the release notes page for this version of the application. + */ +function openReleaseNotes() +{ + var relnotesURL = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"] + .getService(Components.interfaces.nsIURLFormatter) + .formatURLPref("app.releaseNotesURL"); + + openUILinkIn(relnotesURL, "tab"); +} + +/** * Opens the troubleshooting information (about:support) page for this version * of the application. */ diff --git a/application/palemoon/branding/official/firefox.ico b/application/palemoon/branding/official/firefox.ico Binary files differindex 33bb04e99..0f55f0ba5 100644 --- a/application/palemoon/branding/official/firefox.ico +++ b/application/palemoon/branding/official/firefox.ico diff --git a/application/palemoon/components/fuel/fuelApplication.js b/application/palemoon/components/fuel/fuelApplication.js index 017813143..bc3a091ea 100644 --- a/application/palemoon/components/fuel/fuelApplication.js +++ b/application/palemoon/components/fuel/fuelApplication.js @@ -6,6 +6,8 @@ const Ci = Components.interfaces; const Cc = Components.classes; Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Deprecated", + "resource://gre/modules/Deprecated.jsm"); const APPLICATION_CID = Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66"); const APPLICATION_CONTRACTID = "@mozilla.org/fuel/application;1"; @@ -734,6 +736,8 @@ var ApplicationFactory = { //================================================= // Application constructor function Application() { + Deprecated.warning("FUEL is deprecated, you should use the standard Toolkit API instead.", + "https://github.com/MoonchildProductions/UXP/issues/1083"); this.initToolkitHelpers(); } diff --git a/application/palemoon/components/places/PlacesUIUtils.jsm b/application/palemoon/components/places/PlacesUIUtils.jsm index 05d79241c..e3a9e1322 100644 --- a/application/palemoon/components/places/PlacesUIUtils.jsm +++ b/application/palemoon/components/places/PlacesUIUtils.jsm @@ -1170,11 +1170,8 @@ this.PlacesUIUtils = { * @return The URL with the fragment at the end */ getImageURLForResolution: - function PUIU_getImageURLForResolution(aWindow, aURL, aWidth = 16, aHeight = 16) { - let width = Math.round(aWidth * aWindow.devicePixelRatio); - let height = Math.round(aHeight * aWindow.devicePixelRatio); - return aURL + (aURL.includes("#") ? "&" : "#") + - "-moz-resolution=" + width + "," + height; + function PUIU_getImageURLForResolution(aWindow, aURL, aWidth, aHeight) { + return aURL; } }; diff --git a/application/palemoon/components/search/content/search.xml b/application/palemoon/components/search/content/search.xml index 4532f5720..0c33b1527 100644 --- a/application/palemoon/components/search/content/search.xml +++ b/application/palemoon/components/search/content/search.xml @@ -283,11 +283,6 @@ <parameter name="element"/> <parameter name="uri"/> <body><![CDATA[ - if (uri) { - let size = Math.round(16 * window.devicePixelRatio); - if (!uri.includes("#")) - uri += "#-moz-resolution=" + size + "," + size; - } element.setAttribute("src", uri); ]]></body> </method> diff --git a/application/palemoon/config/version.txt b/application/palemoon/config/version.txt index b5df67405..bcbf0fd12 100644 --- a/application/palemoon/config/version.txt +++ b/application/palemoon/config/version.txt @@ -1 +1 @@ -28.5.0a2
\ No newline at end of file +28.6.0a1
\ No newline at end of file diff --git a/application/palemoon/installer/package-manifest.in b/application/palemoon/installer/package-manifest.in index d8722bf08..0d80e15f9 100644 --- a/application/palemoon/installer/package-manifest.in +++ b/application/palemoon/installer/package-manifest.in @@ -231,6 +231,7 @@ ; [Default Preferences] ; All the pref files must be part of base to prevent migration bugs +@RESPATH@/browser/defaults/permissions @RESPATH@/browser/@PREF_DIR@/palemoon.js @RESPATH@/browser/@PREF_DIR@/palemoon-branding.js @RESPATH@/greprefs.js diff --git a/application/palemoon/locales/en-US/chrome/browser/baseMenuOverlay.dtd b/application/palemoon/locales/en-US/chrome/browser/baseMenuOverlay.dtd index 27de3797f..dd88a3384 100644 --- a/application/palemoon/locales/en-US/chrome/browser/baseMenuOverlay.dtd +++ b/application/palemoon/locales/en-US/chrome/browser/baseMenuOverlay.dtd @@ -15,6 +15,8 @@ <!ENTITY helpMenuWin.label "Help"> <!ENTITY helpMenuWin.accesskey "H"> <!ENTITY updateCmd.label "Check for Updates…"> +<!ENTITY helpReleaseNotes.label "Release Notes"> +<!ENTITY helpReleaseNotes.accesskey "N"> <!ENTITY aboutProduct.label "About &brandShortName;"> <!ENTITY aboutProduct.accesskey "A"> <!ENTITY productHelp.label "&brandShortName; Help"> diff --git a/application/palemoon/themes/linux/browser.css b/application/palemoon/themes/linux/browser.css index 01b3f5c9e..4933b4069 100644 --- a/application/palemoon/themes/linux/browser.css +++ b/application/palemoon/themes/linux/browser.css @@ -1608,7 +1608,7 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- } /* When the tab bar is collapsed, show a 1px border in its place. */ -#TabsToolbar[tabsontop="false"][collapsed="true"] { +#TabsToolbar[tabsontop="false"][collapsed="true"]:not([customizing="true"]) { visibility: visible; height: 1px; border-bottom-width: 1px; diff --git a/application/palemoon/themes/osx/browser.css b/application/palemoon/themes/osx/browser.css index 6d0d92015..20e453d11 100644 --- a/application/palemoon/themes/osx/browser.css +++ b/application/palemoon/themes/osx/browser.css @@ -1631,7 +1631,7 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url- } /* When the tab bar is collapsed, show a 1px border in its place. */ -#TabsToolbar[tabsontop="false"][collapsed="true"] { +#TabsToolbar[tabsontop="false"][collapsed="true"]:not([customizing="true"]) { visibility: visible; height: 1px; border-bottom-width: 1px; diff --git a/application/palemoon/themes/windows/browser.css b/application/palemoon/themes/windows/browser.css index 9f32b59cf..88c3087ae 100644 --- a/application/palemoon/themes/windows/browser.css +++ b/application/palemoon/themes/windows/browser.css @@ -1844,7 +1844,7 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- } /* When the tab bar is collapsed, show a 1px border in its place. */ -#TabsToolbar[tabsontop="false"][collapsed="true"] { +#TabsToolbar[tabsontop="false"][collapsed="true"]:not([customizing="true"]) { visibility: visible; height: 1px; border-bottom-width: 1px; diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index a01795d9e..4b5deab18 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -552,17 +552,23 @@ HTMLCanvasElement::CopyInnerTo(Element* aDest) HTMLCanvasElement* dest = static_cast<HTMLCanvasElement*>(aDest); dest->mOriginalCanvas = this; - nsCOMPtr<nsISupports> cxt; - dest->GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(cxt)); - RefPtr<CanvasRenderingContext2D> context2d = - static_cast<CanvasRenderingContext2D*>(cxt.get()); - if (context2d && !mPrintCallback) { - CanvasImageSource source; - source.SetAsHTMLCanvasElement() = this; - ErrorResult err; - context2d->DrawImage(source, - 0.0, 0.0, err); - rv = err.StealNSResult(); + // We make sure that the canvas is not zero sized since that would cause + // the DrawImage call below to return an error, which would cause printing + // to fail. + nsIntSize size = GetWidthHeight(); + if (size.height > 0 && size.width > 0) { + nsCOMPtr<nsISupports> cxt; + dest->GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(cxt)); + RefPtr<CanvasRenderingContext2D> context2d = + static_cast<CanvasRenderingContext2D*>(cxt.get()); + if (context2d && !mPrintCallback) { + CanvasImageSource source; + source.SetAsHTMLCanvasElement() = this; + ErrorResult err; + context2d->DrawImage(source, + 0.0, 0.0, err); + rv = err.StealNSResult(); + } } } return rv; diff --git a/js/ipc/JavaScriptShared.cpp b/js/ipc/JavaScriptShared.cpp index 9786243f2..961a3f910 100644 --- a/js/ipc/JavaScriptShared.cpp +++ b/js/ipc/JavaScriptShared.cpp @@ -61,6 +61,15 @@ IdToObjectMap::find(ObjectId id) return p->value(); } +JSObject* +IdToObjectMap::findPreserveColor(ObjectId id) +{ + Table::Ptr p = table_.lookup(id); + if (!p) + return nullptr; + return p->value().unbarrieredGet(); +} + bool IdToObjectMap::add(ObjectId id, JSObject* obj) { diff --git a/js/ipc/JavaScriptShared.h b/js/ipc/JavaScriptShared.h index 4de153826..5ecec7429 100644 --- a/js/ipc/JavaScriptShared.h +++ b/js/ipc/JavaScriptShared.h @@ -96,6 +96,7 @@ class IdToObjectMap bool add(ObjectId id, JSObject* obj); JSObject* find(ObjectId id); + JSObject* findPreserveColor(ObjectId id); void remove(ObjectId id); void clear(); diff --git a/js/ipc/WrapperAnswer.cpp b/js/ipc/WrapperAnswer.cpp index fc342bbb6..563f8f90d 100644 --- a/js/ipc/WrapperAnswer.cpp +++ b/js/ipc/WrapperAnswer.cpp @@ -789,7 +789,7 @@ WrapperAnswer::RecvDOMInstanceOf(const ObjectId& objId, const int& prototypeID, bool WrapperAnswer::RecvDropObject(const ObjectId& objId) { - JSObject* obj = objects_.find(objId); + JSObject* obj = objects_.findPreserveColor(objId); if (obj) { objectIdMap(objId.hasXrayWaiver()).remove(obj); objects_.remove(objId); diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index ae74f01bf..0dfc1123a 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -652,7 +652,7 @@ ArrayMetaTypeDescr::create(JSContext* cx, if (!CreateTraceList(cx, obj)) return nullptr; - if (!cx->zone()->typeDescrObjects.put(obj)) { + if (!cx->zone()->addTypeDescrObject(cx, obj)) { ReportOutOfMemory(cx); return nullptr; } @@ -993,8 +993,8 @@ StructMetaTypeDescr::create(JSContext* cx, if (!CreateTraceList(cx, descr)) return nullptr; - if (!cx->zone()->typeDescrObjects.put(descr) || - !cx->zone()->typeDescrObjects.put(fieldTypeVec)) + if (!cx->zone()->addTypeDescrObject(cx, descr) || + !cx->zone()->addTypeDescrObject(cx, fieldTypeVec)) { ReportOutOfMemory(cx); return nullptr; @@ -1165,10 +1165,8 @@ DefineSimpleTypeDescr(JSContext* cx, if (!CreateTraceList(cx, descr)) return false; - if (!cx->zone()->typeDescrObjects.put(descr)) { - ReportOutOfMemory(cx); + if (!cx->zone()->addTypeDescrObject(cx, descr)) return false; - } return true; } diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 5c2576efd..f102e9ef0 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -900,7 +900,8 @@ class GCRuntime void requestMajorGC(JS::gcreason::Reason reason); SliceBudget defaultBudget(JS::gcreason::Reason reason, int64_t millis); - void budgetIncrementalGC(SliceBudget& budget, AutoLockForExclusiveAccess& lock); + void budgetIncrementalGC(JS::gcreason::Reason reason, SliceBudget& budget, + AutoLockForExclusiveAccess& lock); void resetIncrementalGC(AbortReason reason, AutoLockForExclusiveAccess& lock); // Assert if the system state is such that we should never @@ -915,6 +916,7 @@ class GCRuntime void collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::Reason reason) JS_HAZ_GC_CALL; MOZ_MUST_USE bool gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::Reason reason); + bool shouldRepeatForDeadZone(JS::gcreason::Reason reason); void incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock); diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 93264084b..f5969bc1f 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -478,6 +478,7 @@ js::gc::GCRuntime::bufferGrayRoots() for (GCZonesIter zone(rt); !zone.done(); zone.next()) MOZ_ASSERT(zone->gcGrayRoots.empty()); + gcstats::AutoPhase ap(stats, gcstats::PHASE_BUFFER_GRAY_ROOTS); BufferGrayRootsTracer grayBufferer(rt); if (JSTraceDataOp op = grayRootTracer.op) @@ -540,4 +541,3 @@ GCRuntime::resetBufferedGrayRoots() const for (GCZonesIter zone(rt); !zone.done(); zone.next()) zone->gcGrayRoots.clearAndFree(); } - diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index ed099341c..f0cdde012 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -370,6 +370,21 @@ Zone::fixupAfterMovingGC() fixupInitialShapeTable(); } +bool +Zone::addTypeDescrObject(JSContext* cx, HandleObject obj) +{ + // Type descriptor objects are always tenured so we don't need post barriers + // on the set. + MOZ_ASSERT(!IsInsideNursery(obj)); + + if (!typeDescrObjects.put(obj)) { + ReportOutOfMemory(cx); + return false; + } + + return true; +} + ZoneList::ZoneList() : head(nullptr), tail(nullptr) {} diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index 50d06319d..c8520ed55 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -349,10 +349,17 @@ struct Zone : public JS::shadow::Zone, // Keep track of all TypeDescr and related objects in this compartment. // This is used by the GC to trace them all first when compacting, since the // TypedObject trace hook may access these objects. - using TypeDescrObjectSet = js::GCHashSet<js::HeapPtr<JSObject*>, - js::MovableCellHasher<js::HeapPtr<JSObject*>>, + + // + // There are no barriers here - the set contains only tenured objects so no + // post-barrier is required, and these are weak references so no pre-barrier + // is required. + using TypeDescrObjectSet = js::GCHashSet<JSObject*, + js::MovableCellHasher<JSObject*>, js::SystemAllocPolicy>; JS::WeakCache<TypeDescrObjectSet> typeDescrObjects; + + bool addTypeDescrObject(JSContext* cx, HandleObject obj); // Malloc counter to measure memory pressure for GC scheduling. It runs from diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 8cee9ec09..194468c5d 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1524,19 +1524,11 @@ GCMarker::delayMarkingChildren(const void* thing) } inline void -ArenaLists::prepareForIncrementalGC(JSRuntime* rt) +ArenaLists::prepareForIncrementalGC() { + purge(); for (auto i : AllAllocKinds()) { - FreeSpan* span = freeLists[i]; - if (span != &placeholder) { - if (!span->isEmpty()) { - Arena* arena = span->getArena(); - arena->allocatedDuringIncremental = true; - rt->gc.marker.delayMarkingArena(arena); - } else { - freeLists[i] = &placeholder; - } - } + arenaLists[i].moveCursorToEnd(); } } @@ -2251,7 +2243,7 @@ GCRuntime::updateTypeDescrObjects(MovingTracer* trc, Zone* zone) { zone->typeDescrObjects.sweep(); for (auto r = zone->typeDescrObjects.all(); !r.empty(); r.popFront()) - UpdateCellPointers(trc, r.front().get()); + UpdateCellPointers(trc, r.front()); } void @@ -3579,6 +3571,23 @@ RelazifyFunctions(Zone* zone, AllocKind kind) } } +static bool +ShouldCollectZone(Zone* zone, JS::gcreason::Reason reason) +{ + // Normally we collect all scheduled zones. + if (reason != JS::gcreason::COMPARTMENT_REVIVED) + return zone->isGCScheduled(); + + // If we are repeating a GC because we noticed dead compartments haven't + // been collected, then only collect zones containing those compartments. + for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) { + if (comp->scheduledForDestruction) + return true; + } + + return false; +} + bool GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock) { @@ -3602,7 +3611,7 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces #endif /* Set up which zones will be collected. */ - if (zone->isGCScheduled()) { + if (ShouldCollectZone(zone, reason)) { if (!zone->isAtomsZone()) { any = true; zone->setGCState(Zone::Mark); @@ -3621,7 +3630,7 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) { c->marked = false; c->scheduledForDestruction = false; - c->maybeAlive = false; + c->maybeAlive = c->hasBeenEntered() || !c->zone()->isGCScheduled(); if (shouldPreserveJITCode(c, currentTime, reason, canAllocateMoreCode)) c->zone()->setPreservingCode(true); } @@ -3641,6 +3650,7 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces * on. If the value of keepAtoms() changes between GC slices, then we'll * cancel the incremental GC. See IsIncrementalGCSafe. */ + if (isFull && !rt->keepAtoms()) { Zone* atomsZone = rt->atomsCompartment(lock)->zone(); if (atomsZone->isGCScheduled()) { @@ -3655,15 +3665,12 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces return false; /* - * At the end of each incremental slice, we call prepareForIncrementalGC, - * which marks objects in all arenas that we're currently allocating - * into. This can cause leaks if unreachable objects are in these - * arenas. This purge call ensures that we only mark arenas that have had - * allocations after the incremental GC started. + * Ensure that after the start of a collection we don't allocate into any + * existing arenas, as this can cause unreachable things to be marked. */ if (isIncremental) { for (GCZonesIter zone(rt); !zone.done(); zone.next()) - zone->arenas.purge(); + zone->arenas.prepareForIncrementalGC(); } MemProfiler::MarkTenuredStart(rt); @@ -3748,12 +3755,10 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces gcstats::AutoPhase ap2(stats, gcstats::PHASE_MARK_ROOTS); if (isIncremental) { - gcstats::AutoPhase ap3(stats, gcstats::PHASE_BUFFER_GRAY_ROOTS); bufferGrayRoots(); + markCompartments(); } - - markCompartments(); - + return true; } @@ -3766,9 +3771,14 @@ GCRuntime::markCompartments() * This code ensures that if a compartment is "dead", then it will be * collected in this GC. A compartment is considered dead if its maybeAlive * flag is false. The maybeAlive flag is set if: - * (1) the compartment has incoming cross-compartment edges, or - * (2) an object in the compartment was marked during root marking, either - * as a black root or a gray root. + * (1) the compartment has been entered (set in beginMarkPhase() above) + * (2) the compartment is not being collected (set in beginMarkPhase() + * above) + * (3) an object in the compartment was marked during root marking, either + * as a black root or a gray root (set in RootMarking.cpp), or + * (4) the compartment has incoming cross-compartment edges from another + * compartment that has maybeAlive set (set by this method). + * * If the maybeAlive is false, then we set the scheduledForDestruction flag. * At the end of the GC, we look for compartments where * scheduledForDestruction is true. These are compartments that were somehow @@ -3786,26 +3796,37 @@ GCRuntime::markCompartments() * allocation and read barriers during JS_TransplantObject and the like. */ - /* Set the maybeAlive flag based on cross-compartment edges. */ - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { - for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) { + /* Propagate the maybeAlive flag via cross-compartment edges. */ + + Vector<JSCompartment*, 0, js::SystemAllocPolicy> workList; + + for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) { + if (comp->maybeAlive) { + if (!workList.append(comp)) + return; + } + } + while (!workList.empty()) { + JSCompartment* comp = workList.popCopy(); + for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) { if (e.front().key().is<JSString*>()) continue; JSCompartment* dest = e.front().mutableKey().compartment(); - if (dest) + if (dest && !dest->maybeAlive) { dest->maybeAlive = true; + if (!workList.append(dest)) + return; + } } } - /* - * For black roots, code in gc/Marking.cpp will already have set maybeAlive - * during MarkRuntime. - */ - - /* Propogate maybeAlive to scheduleForDestruction. */ - for (GCCompartmentsIter c(rt); !c.done(); c.next()) { - if (!c->maybeAlive && !rt->isAtomsCompartment(c)) - c->scheduledForDestruction = true; + + /* Set scheduleForDestruction based on maybeAlive. */ + + for (GCCompartmentsIter comp(rt); !comp.done(); comp.next()) { + MOZ_ASSERT(!comp->scheduledForDestruction); + if (!comp->maybeAlive && !rt->isAtomsCompartment(comp)) + comp->scheduledForDestruction = true; } } @@ -5306,7 +5327,7 @@ AutoGCSlice::~AutoGCSlice() for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) { if (zone->isGCMarking()) { zone->setNeedsIncrementalBarrier(true, Zone::UpdateJit); - zone->arenas.prepareForIncrementalGC(runtime); + zone->arenas.purge(); } else { zone->setNeedsIncrementalBarrier(false, Zone::UpdateJit); } @@ -5487,7 +5508,7 @@ gc::AbortReason gc::IsIncrementalGCUnsafe(JSRuntime* rt) { MOZ_ASSERT(!rt->mainThread.suppressGC); - + if (rt->keepAtoms()) return gc::AbortReason::KeepAtomsSet; @@ -5498,9 +5519,17 @@ gc::IsIncrementalGCUnsafe(JSRuntime* rt) } void -GCRuntime::budgetIncrementalGC(SliceBudget& budget, AutoLockForExclusiveAccess& lock) +GCRuntime::budgetIncrementalGC(JS::gcreason::Reason reason, SliceBudget& budget, + AutoLockForExclusiveAccess& lock) { AbortReason unsafeReason = IsIncrementalGCUnsafe(rt); + if (unsafeReason == AbortReason::None) { + if (reason == JS::gcreason::COMPARTMENT_REVIVED) + unsafeReason = gc::AbortReason::CompartmentRevived; + else if (mode != JSGC_MODE_INCREMENTAL) + unsafeReason = gc::AbortReason::ModeChange; + } + if (unsafeReason != AbortReason::None) { resetIncrementalGC(unsafeReason, lock); budget.makeUnlimited(); @@ -5508,12 +5537,7 @@ GCRuntime::budgetIncrementalGC(SliceBudget& budget, AutoLockForExclusiveAccess& return; } - if (mode != JSGC_MODE_INCREMENTAL) { - resetIncrementalGC(AbortReason::ModeChange, lock); - budget.makeUnlimited(); - stats.nonincremental(AbortReason::ModeChange); - return; - } + if (isTooMuchMalloc()) { budget.makeUnlimited(); @@ -5672,7 +5696,7 @@ GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason:: stats.nonincremental(gc::AbortReason::NonIncrementalRequested); budget.makeUnlimited(); } else { - budgetIncrementalGC(budget, session.lock); + budgetIncrementalGC(reason, budget, session.lock); } /* The GC was reset, so we need a do-over. */ @@ -5764,6 +5788,22 @@ GCRuntime::checkIfGCAllowedInCurrentState(JS::gcreason::Reason reason) return true; } +bool +GCRuntime::shouldRepeatForDeadZone(JS::gcreason::Reason reason) +{ + MOZ_ASSERT_IF(reason == JS::gcreason::COMPARTMENT_REVIVED, !isIncremental); + + if (!isIncremental || isIncrementalGCInProgress()) + return false; + + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { + if (c->scheduledForDestruction) + return true; + } + + return false; +} + void GCRuntime::collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::Reason reason) { @@ -5782,27 +5822,23 @@ GCRuntime::collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::R do { poked = false; bool wasReset = gcCycle(nonincrementalByAPI, budget, reason); - - /* Need to re-schedule all zones for GC. */ - if (poked && cleanUpEverything) + + bool repeatForDeadZone = false; + if (poked && cleanUpEverything) { + /* Need to re-schedule all zones for GC. */ JS::PrepareForFullGC(rt->contextFromMainThread()); - /* - * This code makes an extra effort to collect compartments that we - * thought were dead at the start of the GC. See the large comment in - * beginMarkPhase. - */ - bool repeatForDeadZone = false; - if (!nonincrementalByAPI && !isIncrementalGCInProgress()) { - for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { - if (c->scheduledForDestruction) { - nonincrementalByAPI = true; - repeatForDeadZone = true; - reason = JS::gcreason::COMPARTMENT_REVIVED; - c->zone()->scheduleGC(); - } - } + + } else if (shouldRepeatForDeadZone(reason) && !wasReset) { + /* + * This code makes an extra effort to collect compartments that we + * thought were dead at the start of the GC. See the large comment + * in beginMarkPhase. + */ + repeatForDeadZone = true; + reason = JS::gcreason::COMPARTMENT_REVIVED; } + /* * If we reset an existing GC, we need to start a new one. Also, we diff --git a/js/src/jsgc.h b/js/src/jsgc.h index d3cf31fe7..952fd6bae 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -61,7 +61,8 @@ enum class State { D(ModeChange) \ D(MallocBytesTrigger) \ D(GCBytesTrigger) \ - D(ZoneChange) + D(ZoneChange) \ + D(CompartmentRevived) enum class AbortReason { #define MAKE_REASON(name) name, GC_ABORT_REASONS(MAKE_REASON) @@ -453,6 +454,12 @@ class ArenaList { check(); return !*cursorp_; } + + void moveCursorToEnd() { + while (!isCursorAtEnd()) { + cursorp_ = &(*cursorp_)->next; + } + } // This can return nullptr. Arena* arenaAfterCursor() const { @@ -739,7 +746,7 @@ class ArenaLists freeLists[i] = &placeholder; } - inline void prepareForIncrementalGC(JSRuntime* rt); + inline void prepareForIncrementalGC(); /* Check if this arena is in use. */ bool arenaIsInUse(Arena* arena, AllocKind kind) const { diff --git a/js/src/jswatchpoint.cpp b/js/src/jswatchpoint.cpp index 3cf43e219..e37323555 100644 --- a/js/src/jswatchpoint.cpp +++ b/js/src/jswatchpoint.cpp @@ -185,17 +185,18 @@ WatchpointMap::markAll(JSTracer* trc) { for (Map::Enum e(map); !e.empty(); e.popFront()) { Map::Entry& entry = e.front(); - WatchKey key = entry.key(); - WatchKey prior = key; - MOZ_ASSERT(JSID_IS_STRING(prior.id) || JSID_IS_INT(prior.id) || JSID_IS_SYMBOL(prior.id)); - - TraceEdge(trc, const_cast<PreBarrieredObject*>(&key.object), - "held Watchpoint object"); - TraceEdge(trc, const_cast<PreBarrieredId*>(&key.id), "WatchKey::id"); + JSObject* object = entry.key().object; + jsid id = entry.key().id; + JSObject* priorObject = object; + jsid priorId = id; + MOZ_ASSERT(JSID_IS_STRING(priorId) || JSID_IS_INT(priorId) || JSID_IS_SYMBOL(priorId)); + + TraceManuallyBarrieredEdge(trc, &object, "held Watchpoint object"); + TraceManuallyBarrieredEdge(trc, &id, "WatchKey::id"); TraceEdge(trc, &entry.value().closure, "Watchpoint::closure"); - if (prior.object != key.object || prior.id != key.id) - e.rekeyFront(key); + if (priorObject != object || priorId != id) + e.rekeyFront(WatchKey(object, id)); } } diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 183285439..9aaa28fb5 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -3648,6 +3648,10 @@ PaintedLayerData::AccumulateEventRegions(ContainerState* aState, nsDisplayLayerE if (alreadyHadRegions) { mDispatchToContentHitRegion.OrWith(CombinedTouchActionRegion()); } + + // Avoid quadratic performance as a result of the region growing to include + // and arbitrarily large number of rects, which can happen on some pages. + mMaybeHitRegion.SimplifyOutward(8); // Calculate scaled versions of the bounds of mHitRegion and mMaybeHitRegion // for quick access in FindPaintedLayerFor(). diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index d619576ba..c830891a5 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1096,7 +1096,6 @@ void nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame, const nsFrameList& aFrames, const nsRect& aDirtyRect) { - mFramesMarkedForDisplay.SetCapacity(mFramesMarkedForDisplay.Length() + aFrames.GetLength()); for (nsIFrame* e : aFrames) { // Skip the AccessibleCaret frame when building no caret. if (!IsBuildingCaret()) { @@ -1108,6 +1107,7 @@ nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame, } } } + mFramesMarkedForDisplay.AppendElement(e); MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, aDirtyRect); } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index c9f773f5b..fcdc9e4fc 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -1200,7 +1200,7 @@ private: PLArenaPool mPool; nsCOMPtr<nsISelection> mBoundingSelection; AutoTArray<PresShellState,8> mPresShellStates; - AutoTArray<nsIFrame*,100> mFramesMarkedForDisplay; + AutoTArray<nsIFrame*,400> mFramesMarkedForDisplay; AutoTArray<ThemeGeometry,2> mThemeGeometries; nsDisplayTableItem* mCurrentTableItem; DisplayListClipState mClipState; diff --git a/netwerk/base/nsMediaFragmentURIParser.h b/netwerk/base/nsMediaFragmentURIParser.h index acfa1d5fe..14ca60f53 100644 --- a/netwerk/base/nsMediaFragmentURIParser.h +++ b/netwerk/base/nsMediaFragmentURIParser.h @@ -89,7 +89,6 @@ private: bool ParseNPTMM(nsDependentSubstring& aString, uint32_t& aMinute); bool ParseNPTSS(nsDependentSubstring& aString, uint32_t& aSecond); bool ParseXYWH(nsDependentSubstring aString); - bool ParseMozResolution(nsDependentSubstring aString); bool ParseMozSampleSize(nsDependentSubstring aString); // Media fragment information. diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp index 468449698..5c3f32d6f 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -351,6 +351,12 @@ nsHtml5TreeOpExecutor::RunFlushLoop() nsHtml5FlushLoopGuard guard(this); // this is also the self-kungfu! RefPtr<nsParserBase> parserKungFuDeathGrip(mParser); + RefPtr<nsHtml5StreamParser> streamParserGrip; + if (mParser) { + streamParserGrip = GetParser()->GetStreamParser(); + } + mozilla::Unused + << streamParserGrip; // Intentionally not used within function // Remember the entry time (void) nsContentSink::WillParseImpl(); @@ -409,11 +415,6 @@ nsHtml5TreeOpExecutor::RunFlushLoop() mOpQueue.Clear(); // clear in order to be able to assert in destructor return; } - // Not sure if this grip is still needed, but previously, the code - // gripped before calling ParseUntilBlocked(); - RefPtr<nsHtml5StreamParser> streamKungFuDeathGrip = - GetParser()->GetStreamParser(); - mozilla::Unused << streamKungFuDeathGrip; // Not used within function // Now parse content left in the document.write() buffer queue if any. // This may generate tree ops on its own or dequeue a speculation. nsresult rv = GetParser()->ParseUntilBlocked(); @@ -529,6 +530,12 @@ nsHtml5TreeOpExecutor::FlushDocumentWrite() RefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this); RefPtr<nsParserBase> parserKungFuDeathGrip(mParser); mozilla::Unused << parserKungFuDeathGrip; // Intentionally not used within function + RefPtr<nsHtml5StreamParser> streamParserGrip; + if (mParser) { + streamParserGrip = GetParser()->GetStreamParser(); + } + mozilla::Unused + << streamParserGrip; // Intentionally not used within function NS_ASSERTION(!mReadingFromStage, "Got doc write flush when reading from stage"); diff --git a/services/sync/modules/engines/history.js b/services/sync/modules/engines/history.js index e7f53766f..705b6a119 100644 --- a/services/sync/modules/engines/history.js +++ b/services/sync/modules/engines/history.js @@ -346,7 +346,7 @@ HistoryStore.prototype = { }, wipe: function HistStore_wipe() { - PlacesUtils.history.removeAllPages(); + PlacesUtils.history.clear(); } }; diff --git a/toolkit/components/reader/AboutReader.jsm b/toolkit/components/reader/AboutReader.jsm index 6ec959eba..9d9362a0c 100644 --- a/toolkit/components/reader/AboutReader.jsm +++ b/toolkit/components/reader/AboutReader.jsm @@ -58,6 +58,7 @@ var AboutReader = function(win, articlePromise) { this._scrollOffset = win.pageYOffset; + doc.addEventListener("mousedown", this); doc.addEventListener("click", this); win.addEventListener("pagehide", this); @@ -119,6 +120,25 @@ var AboutReader = function(win, articlePromise) { } this._loadArticle(); + + let dropdown = this._toolbarElement; + + let elemL10nMap = { + ".minus-button": "minus", + ".plus-button": "plus", + ".content-width-minus-button": "contentwidthminus", + ".content-width-plus-button": "contentwidthplus", + ".line-height-minus-button": "lineheightminus", + ".line-height-plus-button": "lineheightplus", + ".light-button": "colorschemelight", + ".dark-button": "colorschemedark", + ".sepia-button": "colorschemesepia", + }; + + for (let [selector, stringID] of Object.entries(elemL10nMap)) { + dropdown.querySelector(selector).setAttribute("title", + gStrings.GetStringFromName("aboutReader.toolbar." + stringID)); + } }; AboutReader.prototype = { @@ -191,13 +211,16 @@ AboutReader.prototype = { if (!aEvent.isTrusted) return; + let target = aEvent.target; switch (aEvent.type) { + case "mousedown": + if (!target.closest(".dropdown-popup")) { + this._closeDropdowns(); + } + break; case "click": - let target = aEvent.target; if (target.classList.contains("dropdown-toggle")) { this._toggleDropdownClicked(aEvent); - } else if (!target.closest(".dropdown-popup")) { - this._closeDropdowns(); } break; case "scroll": @@ -276,13 +299,10 @@ AboutReader.prototype = { }, _setFontSize(newFontSize) { - let containerClasses = this._containerElement.classList; - - if (this._fontSize > 0) - containerClasses.remove("font-size" + this._fontSize); - this._fontSize = newFontSize; - containerClasses.add("font-size" + this._fontSize); + let size = (10 + 2 * this._fontSize) + "px"; + + this._containerElement.style.setProperty("--font-size", size); return AsyncPrefs.set("reader.font_size", this._fontSize); }, diff --git a/toolkit/components/reader/JSDOMParser.js b/toolkit/components/reader/JSDOMParser.js index debdb08eb..ab2f503e1 100644 --- a/toolkit/components/reader/JSDOMParser.js +++ b/toolkit/components/reader/JSDOMParser.js @@ -691,7 +691,7 @@ // the attribute value will be HTML escaped. var val = attr.value; var quote = (val.indexOf('"') === -1 ? '"' : "'"); - arr.push(" " + attr.name + '=' + quote + val + quote); + arr.push(" " + attr.name + "=" + quote + val + quote); } if (child.localName in voidElems && !child.childNodes.length) { @@ -970,7 +970,7 @@ strBuf.push(c); c = this.nextChar(); } - var tag = strBuf.join(''); + var tag = strBuf.join(""); if (!tag) return false; @@ -981,7 +981,9 @@ while (c !== "/" && c !== ">") { if (c === undefined) return false; - while (whitespace.indexOf(this.html[this.currentChar++]) != -1); + while (whitespace.indexOf(this.html[this.currentChar++]) != -1) { + // Advance cursor to first non-whitespace char. + } this.currentChar--; c = this.nextChar(); if (c !== "/" && c !== ">") { diff --git a/toolkit/components/reader/Readability-readerable.js b/toolkit/components/reader/Readability-readerable.js new file mode 100644 index 000000000..d0e1b8164 --- /dev/null +++ b/toolkit/components/reader/Readability-readerable.js @@ -0,0 +1,104 @@ +/* eslint-env es6:false */ +/* globals exports */ +/* + * DO NOT MODIFY THIS FILE DIRECTLY! + * + * This is a shared library that is maintained in an external repo: + * https://github.com/mozilla/readability + */ + +/* + * Copyright (c) 2010 Arc90 Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This code is heavily based on Arc90's readability.js (1.7.1) script + * available at: http://code.google.com/p/arc90labs-readability + */ + +var REGEXPS = { + // NOTE: These two regular expressions are duplicated in + // Readability.js. Please keep both copies in sync. + 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, +}; + +function isNodeVisible(node) { + // Have to null-check node.style to deal with SVG and MathML nodes. + return (!node.style || node.style.display != "none") && !node.hasAttribute("hidden"); +} + +/** + * Decides whether or not the document is reader-able without parsing the whole thing. + * + * @return boolean Whether or not we suspect Readability.parse() will suceeed at returning an article object. + */ +function isProbablyReaderable(doc, isVisible) { + if (!isVisible) { + isVisible = isNodeVisible; + } + + var nodes = doc.querySelectorAll("p, pre"); + + // Get <div> nodes which have <br> node(s) and append them into the `nodes` variable. + // Some articles' DOM structures might look like + // <div> + // Sentences<br> + // <br> + // Sentences<br> + // </div> + var brNodes = doc.querySelectorAll("div > br"); + if (brNodes.length) { + var set = new Set(nodes); + [].forEach.call(brNodes, function(node) { + set.add(node.parentNode); + }); + nodes = Array.from(set); + } + + var score = 0; + // This is a little cheeky, we use the accumulator 'score' to decide what to return from + // this callback: + return [].some.call(nodes, function(node) { + if (!isVisible(node)) + return false; + + var matchString = node.className + " " + node.id; + if (REGEXPS.unlikelyCandidates.test(matchString) && + !REGEXPS.okMaybeItsACandidate.test(matchString)) { + return false; + } + + if (node.matches("li p")) { + return false; + } + + var textContentLength = node.textContent.trim().length; + if (textContentLength < 140) { + return false; + } + + score += Math.sqrt(textContentLength - 140); + + if (score > 20) { + return true; + } + return false; + }); +} + +if (typeof exports === "object") { + exports.isProbablyReaderable = isProbablyReaderable; +} diff --git a/toolkit/components/reader/Readability.js b/toolkit/components/reader/Readability.js index c2bba0cd3..69fb53f86 100644 --- a/toolkit/components/reader/Readability.js +++ b/toolkit/components/reader/Readability.js @@ -46,6 +46,7 @@ function Readability(doc, options) { this._articleTitle = null; this._articleByline = null; this._articleDir = null; + this._articleSiteName = null; this._attempts = []; // Configurable options @@ -118,15 +119,18 @@ Readability.prototype = { // All of the regular expressions in use within readability. // Defined up here so we don't instantiate them repeatedly in loops. REGEXPS: { + // NOTE: These two regular expressions are duplicated in + // Readability-readerable.js. Please keep both copies in sync. 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, extraneous: /print|archive|comment|discuss|e[\-]?mail|share|reply|all|login|sign|single|utility/i, byline: /byline|author|dateline|writtenby|p-author/i, replaceFonts: /<(\/?)font[^>]*>/gi, normalize: /\s{2,}/g, - videos: /\/\/(www\.)?(dailymotion|youtube|youtube-nocookie|player\.vimeo)\.com/i, + videos: /\/\/(www\.)?((dailymotion|youtube|youtube-nocookie|player\.vimeo|v\.qq)\.com|(archive|upload\.wikimedia)\.org|player\.twitch\.tv)/i, nextLink: /(next|weiter|continue|>([^\|]|$)|»([^\|]|$))/i, prevLink: /(prev|earl|old|new|<|«)/i, whitespace: /^\s*$/, @@ -267,7 +271,7 @@ Readability.prototype = { _getAllNodesWithTag: function(node, tagNames) { if (node.querySelectorAll) { - return node.querySelectorAll(tagNames.join(',')); + return node.querySelectorAll(tagNames.join(",")); } return [].concat.apply([], tagNames.map(function(tag) { var collection = node.getElementsByTagName(tag); @@ -327,7 +331,7 @@ Readability.prototype = { return uri; } - var links = articleContent.getElementsByTagName("a"); + var links = this._getAllNodesWithTag(articleContent, ["a"]); this._forEachNode(links, function(link) { var href = link.getAttribute("href"); if (href) { @@ -342,7 +346,7 @@ Readability.prototype = { } }); - var imgs = articleContent.getElementsByTagName("img"); + var imgs = this._getAllNodesWithTag(articleContent, ["img"]); this._forEachNode(imgs, function(img) { var src = img.getAttribute("src"); if (src) { @@ -366,7 +370,7 @@ Readability.prototype = { // If they had an element with id "title" in their HTML if (typeof curTitle !== "string") - curTitle = origTitle = this._getInnerText(doc.getElementsByTagName('title')[0]); + curTitle = origTitle = this._getInnerText(doc.getElementsByTagName("title")[0]); } catch (e) {/* ignore exceptions setting the title. */} var titleHadHierarchicalSeparators = false; @@ -377,18 +381,18 @@ Readability.prototype = { // If there's a separator in the title, first remove the final part if ((/ [\|\-\\\/>»] /).test(curTitle)) { titleHadHierarchicalSeparators = / [\\\/>»] /.test(curTitle); - curTitle = origTitle.replace(/(.*)[\|\-\\\/>»] .*/gi, '$1'); + curTitle = origTitle.replace(/(.*)[\|\-\\\/>»] .*/gi, "$1"); // If the resulting title is too short (3 words or fewer), remove // the first part instead: if (wordCount(curTitle) < 3) - curTitle = origTitle.replace(/[^\|\-\\\/>»]*[\|\-\\\/>»](.*)/gi, '$1'); - } else if (curTitle.indexOf(': ') !== -1) { + curTitle = origTitle.replace(/[^\|\-\\\/>»]*[\|\-\\\/>»](.*)/gi, "$1"); + } else if (curTitle.indexOf(": ") !== -1) { // Check if we have an heading containing this exact string, so we // could assume it's the full title. var headings = this._concatNodeLists( - doc.getElementsByTagName('h1'), - doc.getElementsByTagName('h2') + doc.getElementsByTagName("h1"), + doc.getElementsByTagName("h2") ); var trimmedTitle = curTitle.trim(); var match = this._someNode(headings, function(heading) { @@ -397,25 +401,25 @@ Readability.prototype = { // If we don't, let's extract the title out of the original title string. if (!match) { - curTitle = origTitle.substring(origTitle.lastIndexOf(':') + 1); + curTitle = origTitle.substring(origTitle.lastIndexOf(":") + 1); // If the title is now too short, try the first colon instead: if (wordCount(curTitle) < 3) { - curTitle = origTitle.substring(origTitle.indexOf(':') + 1); + curTitle = origTitle.substring(origTitle.indexOf(":") + 1); // But if we have too many words before the colon there's something weird // with the titles and the H tags so let's just use the original title instead - } else if (wordCount(origTitle.substr(0, origTitle.indexOf(':'))) > 5) { + } else if (wordCount(origTitle.substr(0, origTitle.indexOf(":"))) > 5) { curTitle = origTitle; } } } else if (curTitle.length > 150 || curTitle.length < 15) { - var hOnes = doc.getElementsByTagName('h1'); + var hOnes = doc.getElementsByTagName("h1"); if (hOnes.length === 1) curTitle = this._getInnerText(hOnes[0]); } - curTitle = curTitle.trim(); + curTitle = curTitle.trim().replace(this.REGEXPS.normalize, " "); // If we now have 4 words or fewer as our title, and either no // 'hierarchical' separators (\, /, > or ») were found in the original // title or we decreased the number of words by more than 1 word, use @@ -505,7 +509,8 @@ Readability.prototype = { break; } - if (!this._isPhrasingContent(next)) break; + if (!this._isPhrasingContent(next)) + break; // Otherwise, make this node a child of the new <p>. var sibling = next.nextSibling; @@ -513,9 +518,12 @@ Readability.prototype = { next = sibling; } - while (p.lastChild && this._isWhitespace(p.lastChild)) p.removeChild(p.lastChild); + while (p.lastChild && this._isWhitespace(p.lastChild)) { + p.removeChild(p.lastChild); + } - if (p.parentNode.tagName === "P") this._setNodeTag(p.parentNode, "DIV"); + if (p.parentNode.tagName === "P") + this._setNodeTag(p.parentNode, "DIV"); } }); }, @@ -576,7 +584,7 @@ Readability.prototype = { // If there is only one h2 and its text content substantially equals article title, // they are probably using it as a header and not a subheader, // so remove it since we already extract the title separately. - var h2 = articleContent.getElementsByTagName('h2'); + var h2 = articleContent.getElementsByTagName("h2"); if (h2.length === 1) { var lengthSimilarRate = (h2[0].textContent.length - this._articleTitle.length) / this._articleTitle.length; if (Math.abs(lengthSimilarRate) < 0.5) { @@ -606,12 +614,12 @@ Readability.prototype = { this._cleanConditionally(articleContent, "div"); // Remove extra paragraphs - this._removeNodes(articleContent.getElementsByTagName('p'), function (paragraph) { - var imgCount = paragraph.getElementsByTagName('img').length; - var embedCount = paragraph.getElementsByTagName('embed').length; - var objectCount = paragraph.getElementsByTagName('object').length; + this._removeNodes(articleContent.getElementsByTagName("p"), function (paragraph) { + var imgCount = paragraph.getElementsByTagName("img").length; + var embedCount = paragraph.getElementsByTagName("embed").length; + var objectCount = paragraph.getElementsByTagName("object").length; // At this point, nasty iframes have been removed, only remain embedded video ones. - var iframeCount = paragraph.getElementsByTagName('iframe').length; + var iframeCount = paragraph.getElementsByTagName("iframe").length; var totalCount = imgCount + embedCount + objectCount + iframeCount; return totalCount === 0 && !this._getInnerText(paragraph, false); @@ -648,34 +656,34 @@ Readability.prototype = { node.readability = {"contentScore": 0}; switch (node.tagName) { - case 'DIV': + case "DIV": node.readability.contentScore += 5; break; - case 'PRE': - case 'TD': - case 'BLOCKQUOTE': + case "PRE": + case "TD": + case "BLOCKQUOTE": node.readability.contentScore += 3; break; - case 'ADDRESS': - case 'OL': - case 'UL': - case 'DL': - case 'DD': - case 'DT': - case 'LI': - case 'FORM': + case "ADDRESS": + case "OL": + case "UL": + case "DL": + case "DD": + case "DT": + case "LI": + case "FORM": node.readability.contentScore -= 3; break; - case 'H1': - case 'H2': - case 'H3': - case 'H4': - case 'H5': - case 'H6': - case 'TH': + case "H1": + case "H2": + case "H3": + case "H4": + case "H5": + case "H6": + case "TH": node.readability.contentScore -= 5; break; } @@ -824,12 +832,14 @@ Readability.prototype = { if (p !== null) { p.appendChild(childNode); } else if (!this._isWhitespace(childNode)) { - p = doc.createElement('p'); + 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); + while (p.lastChild && this._isWhitespace(p.lastChild)) { + p.removeChild(p.lastChild); + } p = null; } childNode = nextSibling; @@ -860,7 +870,7 @@ Readability.prototype = { **/ var candidates = []; this._forEachNode(elementsToScore, function(elementToScore) { - if (!elementToScore.parentNode || typeof(elementToScore.parentNode.tagName) === 'undefined') + if (!elementToScore.parentNode || typeof(elementToScore.parentNode.tagName) === "undefined") return; // If this paragraph is less than 25 characters, don't even count it. @@ -879,17 +889,17 @@ Readability.prototype = { contentScore += 1; // Add points for any commas within this paragraph. - contentScore += innerText.split(',').length; + contentScore += innerText.split(",").length; // For every 100 characters in this paragraph, add another point. Up to 3 points. contentScore += Math.min(Math.floor(innerText.length / 100), 3); // Initialize and score ancestors. this._forEachNode(ancestors, function(ancestor, level) { - if (!ancestor.tagName || !ancestor.parentNode || typeof(ancestor.parentNode.tagName) === 'undefined') + if (!ancestor.tagName || !ancestor.parentNode || typeof(ancestor.parentNode.tagName) === "undefined") return; - if (typeof(ancestor.readability) === 'undefined') { + if (typeof(ancestor.readability) === "undefined") { this._initializeNode(ancestor); candidates.push(ancestor); } @@ -920,7 +930,7 @@ Readability.prototype = { var candidateScore = candidate.readability.contentScore * (1 - this._getLinkDensity(candidate)); candidate.readability.contentScore = candidateScore; - this.log('Candidate:', candidate, "with score " + candidateScore); + this.log("Candidate:", candidate, "with score " + candidateScore); for (var t = 0; t < this._nbTopCandidates; t++) { var aTopCandidate = topCandidates[t]; @@ -1039,8 +1049,8 @@ Readability.prototype = { var sibling = siblings[s]; var append = false; - this.log("Looking at sibling node:", sibling, sibling.readability ? ("with score " + sibling.readability.contentScore) : ''); - this.log("Sibling has score", sibling.readability ? sibling.readability.contentScore : 'Unknown'); + this.log("Looking at sibling node:", sibling, sibling.readability ? ("with score " + sibling.readability.contentScore) : ""); + this.log("Sibling has score", sibling.readability ? sibling.readability.contentScore : "Unknown"); if (sibling === topCandidate) { append = true; @@ -1074,7 +1084,7 @@ Readability.prototype = { if (this.ALTER_TO_DIV_EXCEPTIONS.indexOf(sibling.nodeName) === -1) { // We have a node that isn't a common block level element, like a form or td tag. // Turn it into a div so it doesn't get filtered out later by accident. - this.log("Altering sibling:", sibling, 'to div.'); + this.log("Altering sibling:", sibling, "to div."); sibling = this._setNodeTag(sibling, "DIV"); } @@ -1142,7 +1152,7 @@ Readability.prototype = { this._attempts.push({articleContent: articleContent, textLength: textLength}); // No luck after removing flags, just return the longest text we found during the different loops this._attempts.sort(function (a, b) { - return a.textLength < b.textLength; + return b.textLength - a.textLength; }); // But first check if we actually have something @@ -1182,7 +1192,7 @@ Readability.prototype = { * @return Boolean - whether the input string is a byline. */ _isValidByline: function(byline) { - if (typeof byline == 'string' || byline instanceof String) { + if (typeof byline == "string" || byline instanceof String) { byline = byline.trim(); return (byline.length > 0) && (byline.length < 100); } @@ -1199,62 +1209,73 @@ Readability.prototype = { var values = {}; var metaElements = this._doc.getElementsByTagName("meta"); - // Match "description", or Twitter's "twitter:description" (Cards) - // in name attribute. - var namePattern = /^\s*((twitter)\s*:\s*)?(description|title)\s*$/gi; + // property is a space-separated list of values + var propertyPattern = /\s*(dc|dcterm|og|twitter)\s*:\s*(author|creator|description|title|site_name)\s*/gi; - // Match Facebook's Open Graph title & description properties. - var propertyPattern = /^\s*og\s*:\s*(description|title)\s*$/gi; + // name is a single value + var namePattern = /^\s*(?:(dc|dcterm|og|twitter|weibo:(article|webpage))\s*[\.:]\s*)?(author|creator|description|title|site_name)\s*$/i; // Find description tags. this._forEachNode(metaElements, function(element) { var elementName = element.getAttribute("name"); var elementProperty = element.getAttribute("property"); + var content = element.getAttribute("content"); + var matches = null; + var name = null; - if ([elementName, elementProperty].indexOf("author") !== -1) { - metadata.byline = element.getAttribute("content"); - return; + if (elementProperty) { + matches = elementProperty.match(propertyPattern); + if (matches) { + for (var i = matches.length - 1; i >= 0; i--) { + // Convert to lowercase, and remove any whitespace + // so we can match below. + name = matches[i].toLowerCase().replace(/\s/g, ""); + // multiple authors + values[name] = content.trim(); + } + } } - - var name = null; - if (namePattern.test(elementName)) { + if (!matches && elementName && namePattern.test(elementName)) { name = elementName; - } else if (propertyPattern.test(elementProperty)) { - name = elementProperty; - } - - if (name) { - var content = element.getAttribute("content"); if (content) { - // Convert to lowercase and remove any whitespace - // so we can match below. - name = name.toLowerCase().replace(/\s/g, ''); + // Convert to lowercase, remove any whitespace, and convert dots + // to colons so we can match below. + name = name.toLowerCase().replace(/\s/g, "").replace(/\./g, ":"); values[name] = content.trim(); } } }); - if ("description" in values) { - metadata.excerpt = values["description"]; - } else if ("og:description" in values) { - // Use facebook open graph description. - metadata.excerpt = values["og:description"]; - } else if ("twitter:description" in values) { - // Use twitter cards description. - metadata.excerpt = values["twitter:description"]; - } + // get title + metadata.title = values["dc:title"] || + values["dcterm:title"] || + values["og:title"] || + values["weibo:article:title"] || + values["weibo:webpage:title"] || + values["title"] || + values["twitter:title"]; - metadata.title = this._getArticleTitle(); if (!metadata.title) { - if ("og:title" in values) { - // Use facebook open graph title. - metadata.title = values["og:title"]; - } else if ("twitter:title" in values) { - // Use twitter cards title. - metadata.title = values["twitter:title"]; - } + metadata.title = this._getArticleTitle(); } + // get author + metadata.byline = values["dc:creator"] || + values["dcterm:creator"] || + values["author"]; + + // get description + metadata.excerpt = values["dc:description"] || + values["dcterm:description"] || + values["og:description"] || + values["weibo:article:description"] || + values["weibo:webpage:description"] || + values["description"] || + values["twitter:description"]; + + // get site name + metadata.siteName = values["og:site_name"]; + return metadata; }, @@ -1264,12 +1285,12 @@ Readability.prototype = { * @param Element **/ _removeScripts: function(doc) { - this._removeNodes(doc.getElementsByTagName('script'), function(scriptNode) { + this._removeNodes(doc.getElementsByTagName("script"), function(scriptNode) { scriptNode.nodeValue = ""; - scriptNode.removeAttribute('src'); + scriptNode.removeAttribute("src"); return true; }); - this._removeNodes(doc.getElementsByTagName('noscript')); + this._removeNodes(doc.getElementsByTagName("noscript")); }, /** @@ -1336,7 +1357,7 @@ Readability.prototype = { * @return string **/ _getInnerText: function(e, normalizeSpaces) { - normalizeSpaces = (typeof normalizeSpaces === 'undefined') ? true : normalizeSpaces; + normalizeSpaces = (typeof normalizeSpaces === "undefined") ? true : normalizeSpaces; var textContent = e.textContent.trim(); if (normalizeSpaces) { @@ -1365,7 +1386,7 @@ Readability.prototype = { * @return void **/ _cleanStyles: function(e) { - if (!e || e.tagName.toLowerCase() === 'svg') + if (!e || e.tagName.toLowerCase() === "svg") return; // Remove `style` and deprecated presentational attributes @@ -1374,8 +1395,8 @@ Readability.prototype = { } if (this.DEPRECATED_SIZE_ATTRIBUTE_ELEMS.indexOf(e.tagName) !== -1) { - e.removeAttribute('width'); - e.removeAttribute('height'); + e.removeAttribute("width"); + e.removeAttribute("height"); } var cur = e.firstElementChild; @@ -1421,7 +1442,7 @@ Readability.prototype = { var weight = 0; // Look for a special classname - if (typeof(e.className) === 'string' && e.className !== '') { + if (typeof(e.className) === "string" && e.className !== "") { if (this.REGEXPS.negative.test(e.className)) weight -= 25; @@ -1430,7 +1451,7 @@ Readability.prototype = { } // Look for a special ID - if (typeof(e.id) === 'string' && e.id !== '') { + if (typeof(e.id) === "string" && e.id !== "") { if (this.REGEXPS.negative.test(e.id)) weight -= 25; @@ -1619,7 +1640,7 @@ Readability.prototype = { return true; } - if (this._getCharCount(node, ',') < 10) { + if (this._getCharCount(node, ",") < 10) { // If there are not very many commas, and the number of // non-paragraph elements is more than paragraphs or other // ominous signs, remove the element. @@ -1679,7 +1700,7 @@ Readability.prototype = { **/ _cleanHeaders: function(e) { for (var headerIndex = 1; headerIndex < 3; headerIndex += 1) { - this._removeNodes(e.getElementsByTagName('h' + headerIndex), function (header) { + this._removeNodes(e.getElementsByTagName("h" + headerIndex), function (header) { return this._getClassWeight(header) < 0; }); } @@ -1694,66 +1715,7 @@ Readability.prototype = { }, _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. - * - * @return boolean Whether or not we suspect parse() will suceeed at returning an article object. - */ - isProbablyReaderable: function(helperIsVisible) { - var nodes = this._getAllNodesWithTag(this._doc, ["p", "pre"]); - - // Get <div> nodes which have <br> node(s) and append them into the `nodes` variable. - // Some articles' DOM structures might look like - // <div> - // Sentences<br> - // <br> - // Sentences<br> - // </div> - var brNodes = this._getAllNodesWithTag(this._doc, ["div > br"]); - if (brNodes.length) { - var set = new Set(); - [].forEach.call(brNodes, function(node) { - set.add(node.parentNode); - }); - nodes = [].concat.apply(Array.from(set), nodes); - } - - if (!helperIsVisible) { - helperIsVisible = this._isProbablyVisible; - } - - var score = 0; - // This is a little cheeky, we use the accumulator 'score' to decide what to return from - // this callback: - return this._someNode(nodes, function(node) { - if (helperIsVisible && !helperIsVisible(node)) - return false; - var matchString = node.className + " " + node.id; - - if (this.REGEXPS.unlikelyCandidates.test(matchString) && - !this.REGEXPS.okMaybeItsACandidate.test(matchString)) { - return false; - } - - if (node.matches && node.matches("li p")) { - return false; - } - - var textContentLength = node.textContent.trim().length; - if (textContentLength < 140) { - return false; - } - - score += Math.sqrt(textContentLength - 140); - - if (score > 20) { - return true; - } - return false; - }); + return (!node.style || node.style.display != "none") && !node.hasAttribute("hidden"); }, /** @@ -1812,6 +1774,7 @@ Readability.prototype = { textContent: textContent, length: textContent.length, excerpt: metadata.excerpt, + siteName: metadata.siteName || this._articleSiteName }; } }; diff --git a/toolkit/components/reader/ReaderMode.jsm b/toolkit/components/reader/ReaderMode.jsm index 218e12d60..5ba898aec 100644 --- a/toolkit/components/reader/ReaderMode.jsm +++ b/toolkit/components/reader/ReaderMode.jsm @@ -12,6 +12,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components; // names so that rules in aboutReader.css can match them. const CLASSES_TO_PRESERVE = [ "caption", + "emoji", "hidden", "invisble", "sr-only", @@ -19,6 +20,7 @@ const CLASSES_TO_PRESERVE = [ "visuallyhidden", "wp-caption", "wp-caption-text", + "wp-smiley", ]; Cu.import("resource://gre/modules/Services.jsm"); @@ -30,13 +32,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils", "resource://services-comm XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ReaderWorker", "resource://gre/modules/reader/ReaderWorker.jsm"); - -XPCOMUtils.defineLazyGetter(this, "Readability", function() { - let scope = {}; - scope.dump = this.dump; - Services.scriptloader.loadSubScript("resource://gre/modules/reader/Readability.js", scope); - return scope.Readability; -}); +XPCOMUtils.defineLazyModuleGetter(this, "Readerable", "resource://gre/modules/Readerable.jsm"); this.ReaderMode = { // Version of the cache schema. @@ -44,42 +40,6 @@ this.ReaderMode = { DEBUG: 0, - // Don't try to parse the page if it has too many elements (for memory and - // performance reasons) - get maxElemsToParse() { - delete this.parseNodeLimit; - - Services.prefs.addObserver("reader.parse-node-limit", this, false); - return this.parseNodeLimit = Services.prefs.getIntPref("reader.parse-node-limit"); - }, - - get isEnabledForParseOnLoad() { - delete this.isEnabledForParseOnLoad; - - // Listen for future pref changes. - Services.prefs.addObserver("reader.parse-on-load.", this, false); - - return this.isEnabledForParseOnLoad = this._getStateForParseOnLoad(); - }, - - _getStateForParseOnLoad() { - let isEnabled = Services.prefs.getBoolPref("reader.parse-on-load.enabled"); - let isForceEnabled = Services.prefs.getBoolPref("reader.parse-on-load.force-enabled"); - return isForceEnabled || isEnabled; - }, - - observe(aMessage, aTopic, aData) { - switch (aTopic) { - case "nsPref:changed": - if (aData.startsWith("reader.parse-on-load.")) { - this.isEnabledForParseOnLoad = this._getStateForParseOnLoad(); - } else if (aData === "reader.parse-node-limit") { - this.parseNodeLimit = Services.prefs.getIntPref(aData); - } - break; - } - }, - /** * Enter the reader mode by going forward one step in history if applicable, * if not, append the about:reader page in the history instead. @@ -175,39 +135,6 @@ this.ReaderMode = { }, /** - * Decides whether or not a document is reader-able without parsing the whole thing. - * - * @param doc A document to parse. - * @return boolean Whether or not we should show the reader mode button. - */ - isProbablyReaderable(doc) { - // Only care about 'real' HTML documents: - if (doc.mozSyntheticDocument || !(doc instanceof doc.defaultView.HTMLDocument)) { - return false; - } - - let uri = Services.io.newURI(doc.location.href); - if (!this._shouldCheckUri(uri)) { - return false; - } - - let utils = this.getUtilsForWin(doc.defaultView); - // 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(doc).isProbablyReaderable(this.isNodeVisible.bind(this, utils)); - }, - - isNodeVisible(utils, node) { - let bounds = utils.getBoundsWithoutFlushing(node); - return bounds.height > 0 && bounds.width > 0; - }, - - getUtilsForWin(win) { - return win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - }, - - /** * Gets an article from a loaded browser's document. This method will not attempt * to parse certain URIs (e.g. about: URIs). * @@ -216,7 +143,8 @@ this.ReaderMode = { * @resolves JS object representing the article, or null if no article is found. */ parseDocument(doc) { - if (!this._shouldCheckUri(doc.documentURIObject) || !this._shouldCheckUri(doc.baseURIObject, true)) { + if (!Readerable.shouldCheckUri(doc.documentURIObject) || + !Readerable.shouldCheckUri(doc.baseURIObject, true)) { this.log("Reader mode disabled for URI"); return null; } @@ -236,7 +164,8 @@ this.ReaderMode = { if (!doc) { return null; } - if (!this._shouldCheckUri(doc.documentURIObject) || !this._shouldCheckUri(doc.baseURIObject, true)) { + if (!Readerable.shouldCheckUri(doc.documentURIObject) || + !Readerable.shouldCheckUri(doc.baseURIObject, true)) { this.log("Reader mode disabled for URI"); return null; } @@ -246,7 +175,7 @@ this.ReaderMode = { _downloadDocument(url) { try { - if (!this._shouldCheckUri(Services.io.newURI(url))) { + if (!Readerable.shouldCheckUri(Services.io.newURI(url))) { return null; } } catch (ex) { @@ -388,44 +317,6 @@ this.ReaderMode = { dump("Reader: " + msg); }, - _blockedHosts: [ - "amazon.com", - "basilisk-browser.org", - "github.com", - "mail.google.com", - "palemoon.org", - "pinterest.com", - "reddit.com", - "twitter.com", - "youtube.com", - ], - - _shouldCheckUri(uri, isBaseUri = false) { - if (!(uri.schemeIs("http") || uri.schemeIs("https"))) { - this.log("Not parsing URI scheme: " + uri.scheme); - return false; - } - - try { - uri.QueryInterface(Ci.nsIURL); - } catch (ex) { - // If this doesn't work, presumably the URL is not well-formed or something - return false; - } - // Sadly, some high-profile pages have false positives, so bail early for those: - let asciiHost = uri.asciiHost; - if (!isBaseUri && this._blockedHosts.some(blockedHost => asciiHost.endsWith(blockedHost))) { - return false; - } - - if (!isBaseUri && (!uri.filePath || uri.filePath == "/")) { - this.log("Not parsing home page: " + uri.spec); - return false; - } - - return true; - }, - /** * Attempts to parse a document into an article. Heavy lifting happens * in readerWorker.js. @@ -650,3 +541,8 @@ this.ReaderMode = { return readingSpeed.get(lang) || readingSpeed.get("en"); }, }; + +// Don't try to parse the page if it has too many elements (for memory and +// performance reasons) +XPCOMUtils.defineLazyPreferenceGetter( + ReaderMode, "parseNodeLimit", "reader.parse-node-limit", 0); diff --git a/toolkit/components/reader/Readerable.js b/toolkit/components/reader/Readerable.js new file mode 100644 index 000000000..cee8adc08 --- /dev/null +++ b/toolkit/components/reader/Readerable.js @@ -0,0 +1,96 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- +/* 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 file and Readability-readerable.js are merged together into +// Readerable.jsm. + +/* exported Readerable */ +/* import-globals-from Readability-readerable.js */ + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +function isNodeVisible(node) { + return node.clientHeight > 0 && node.clientWidth > 0; +} + +var Readerable = { + DEBUG: 0, + + get isEnabledForParseOnLoad() { + return this.isEnabled || this.isForceEnabled; + }, + + log(msg) { + if (this.DEBUG) + dump("Reader: " + msg); + }, + + /** + * Decides whether or not a document is reader-able without parsing the whole thing. + * + * @param doc A document to parse. + * @return boolean Whether or not we should show the reader mode button. + */ + isProbablyReaderable(doc) { + // Only care about 'real' HTML documents: + if (doc.mozSyntheticDocument || !(doc instanceof doc.defaultView.HTMLDocument)) { + return false; + } + + let uri = Services.io.newURI(doc.location.href); + if (!this.shouldCheckUri(uri)) { + return false; + } + + return isProbablyReaderable(doc, isNodeVisible); + }, + + _blockedHosts: [ + "amazon.com", + "basilisk-browser.org", + "github.com", + "mail.google.com", + "palemoon.org", + "pinterest.com", + "reddit.com", + "twitter.com", + "youtube.com", + ], + + shouldCheckUri(uri, isBaseUri = false) { + if (!(uri.schemeIs("http") || uri.schemeIs("https"))) { + this.log("Not parsing URI scheme: " + uri.scheme); + return false; + } + + try { + uri.QueryInterface(Ci.nsIURL); + } catch (ex) { + // If this doesn't work, presumably the URL is not well-formed or something + return false; + } + // Sadly, some high-profile pages have false positives, so bail early for those: + let asciiHost = uri.asciiHost; + if (!isBaseUri && this._blockedHosts.some(blockedHost => asciiHost.endsWith(blockedHost))) { + return false; + } + + if (!isBaseUri && (!uri.filePath || uri.filePath == "/")) { + this.log("Not parsing home page: " + uri.spec); + return false; + } + + return true; + }, +}; + +XPCOMUtils.defineLazyPreferenceGetter( + Readerable, "isEnabled", "reader.parse-on-load.enabled", true); +XPCOMUtils.defineLazyPreferenceGetter( + Readerable, "isForceEnabled", "reader.parse-on-load.force-enabled", false); diff --git a/toolkit/components/reader/Readerable.jsm b/toolkit/components/reader/Readerable.jsm new file mode 100644 index 000000000..2268487e4 --- /dev/null +++ b/toolkit/components/reader/Readerable.jsm @@ -0,0 +1,10 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +var EXPORTED_SYMBOLS = ["Readerable"]; + +#include Readability-readerable.js +#include Readerable.js diff --git a/toolkit/components/reader/moz.build b/toolkit/components/reader/moz.build index 6863d6542..d49bda14f 100644 --- a/toolkit/components/reader/moz.build +++ b/toolkit/components/reader/moz.build @@ -11,6 +11,10 @@ EXTRA_JS_MODULES += [ 'ReaderMode.jsm' ] +EXTRA_PP_JS_MODULES += [ + 'Readerable.jsm' +] + EXTRA_JS_MODULES.reader = [ 'JSDOMParser.js', 'Readability.js', diff --git a/toolkit/locales/en-US/chrome/global/aboutReader.properties b/toolkit/locales/en-US/chrome/global/aboutReader.properties index ac004c545..10d145686 100644 --- a/toolkit/locales/en-US/chrome/global/aboutReader.properties +++ b/toolkit/locales/en-US/chrome/global/aboutReader.properties @@ -46,3 +46,14 @@ readerView.enter=Enter Reader View readerView.enter.accesskey=R readerView.close=Close Reader View readerView.close.accesskey=R + +# These are used as tooltips in Type Control +aboutReader.toolbar.minus = Decrease Font Size +aboutReader.toolbar.plus = Increase Font Size +aboutReader.toolbar.contentwidthminus = Decrease Content Width +aboutReader.toolbar.contentwidthplus = Increase Content Width +aboutReader.toolbar.lineheightminus = Decrease Line Height +aboutReader.toolbar.lineheightplus = Increase Line Height +aboutReader.toolbar.colorschemelight = Color Scheme Light +aboutReader.toolbar.colorschemedark = Color Scheme Dark +aboutReader.toolbar.colorschemesepia = Color Scheme Sepia diff --git a/toolkit/themes/shared/aboutReader.css b/toolkit/themes/shared/aboutReader.css index 4dbf11f6d..ff8f27565 100644 --- a/toolkit/themes/shared/aboutReader.css +++ b/toolkit/themes/shared/aboutReader.css @@ -47,44 +47,10 @@ body.serif .remove-button { } .container { + --font-size: 12; max-width: 30em; margin: 0 auto; -} - -.container.font-size1 { - font-size: 12px; -} - -.container.font-size2 { - font-size: 14px; -} - -.container.font-size3 { - font-size: 16px; -} - -.container.font-size4 { - font-size: 18px; -} - -.container.font-size5 { - font-size: 20px; -} - -.container.font-size6 { - font-size: 22px; -} - -.container.font-size7 { - font-size: 24px; -} - -.container.font-size8 { - font-size: 26px; -} - -.container.font-size9 { - font-size: 28px; + font-size: var(--font-size); } .container.content-width1 { @@ -738,3 +704,14 @@ body:not(.loaded) .toolbar:-moz-locale-dir(rtl) { .moz-reader-content .sr-only { display: none; } + +/* Enforce wordpress and similar emoji/smileys aren't sized to be full-width */ +.moz-reader-content img.wp-smiley, +.moz-reader-content img.emoji { + display: inline-block; + border-width: 0; + /* height: auto is implied from `.moz-reader-content *` rule. */ + width: 1em; + margin: 0 .07em; + padding: 0; +} diff --git a/toolkit/themes/windows/global/button.css b/toolkit/themes/windows/global/button.css index 278339404..3aeac8512 100644 --- a/toolkit/themes/windows/global/button.css +++ b/toolkit/themes/windows/global/button.css @@ -60,7 +60,7 @@ button[default="true"] { } @media not all and (-moz-windows-default-theme) { - @media (-moz-windows-compositor) { + @media (-moz-windows-theme: aero-lite) { /* This is for high-contrast themes on Windows 8 and later */ button[default="true"], button:hover { |