summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--accessible/base/EventTree.cpp1
-rw-r--r--accessible/base/Logging.cpp2
-rw-r--r--accessible/base/NotificationController.cpp1
-rw-r--r--accessible/base/moz.build2
-rw-r--r--accessible/base/nsCoreUtils.cpp1
-rw-r--r--accessible/base/nsEventShell.cpp4
-rw-r--r--accessible/mac/moz.build2
-rw-r--r--accessible/mac/mozAccessible.mm36
-rw-r--r--accessible/mac/mozHTMLAccessible.mm2
-rw-r--r--accessible/mac/mozTableAccessible.mm41
-rw-r--r--accessible/mac/mozTextAccessible.mm1
-rw-r--r--accessible/xpcom/xpcAccessibleHyperText.cpp1
-rw-r--r--application/basilisk/base/content/tab-content.js6
-rw-r--r--application/basilisk/base/content/utilityOverlay.js12
-rw-r--r--application/palemoon/app/moz.build1
-rw-r--r--application/palemoon/app/permissions14
-rw-r--r--application/palemoon/app/profile/palemoon.js6
-rw-r--r--application/palemoon/base/content/baseMenuOverlay.xul14
-rw-r--r--application/palemoon/base/content/browser-appmenu.inc13
-rw-r--r--application/palemoon/base/content/browser.js5
-rw-r--r--application/palemoon/base/content/tabbrowser.xml17
-rw-r--r--application/palemoon/base/content/utilityOverlay.js55
-rw-r--r--application/palemoon/branding/official/firefox.icobin105959 -> 107919 bytes
-rw-r--r--application/palemoon/components/fuel/fuelApplication.js4
-rw-r--r--application/palemoon/components/places/PlacesUIUtils.jsm7
-rw-r--r--application/palemoon/components/search/content/search.xml5
-rw-r--r--application/palemoon/config/version.txt2
-rw-r--r--application/palemoon/installer/package-manifest.in1
-rw-r--r--application/palemoon/locales/en-US/chrome/browser/baseMenuOverlay.dtd2
-rw-r--r--application/palemoon/themes/linux/browser.css2
-rw-r--r--application/palemoon/themes/osx/browser.css2
-rw-r--r--application/palemoon/themes/windows/browser.css2
-rw-r--r--dom/html/HTMLCanvasElement.cpp28
-rw-r--r--js/ipc/JavaScriptShared.cpp9
-rw-r--r--js/ipc/JavaScriptShared.h1
-rw-r--r--js/ipc/WrapperAnswer.cpp2
-rw-r--r--js/src/builtin/TypedObject.cpp10
-rw-r--r--js/src/gc/GCRuntime.h4
-rw-r--r--js/src/gc/RootMarking.cpp2
-rw-r--r--js/src/gc/Zone.cpp15
-rw-r--r--js/src/gc/Zone.h11
-rw-r--r--js/src/jsgc.cpp172
-rw-r--r--js/src/jsgc.h11
-rw-r--r--js/src/jswatchpoint.cpp19
-rw-r--r--layout/base/FrameLayerBuilder.cpp4
-rw-r--r--layout/base/nsDisplayList.cpp2
-rw-r--r--layout/base/nsDisplayList.h2
-rw-r--r--netwerk/base/nsMediaFragmentURIParser.h1
-rw-r--r--parser/html/nsHtml5TreeOpExecutor.cpp17
-rw-r--r--services/sync/modules/engines/history.js2
-rw-r--r--toolkit/components/reader/AboutReader.jsm38
-rw-r--r--toolkit/components/reader/JSDOMParser.js8
-rw-r--r--toolkit/components/reader/Readability-readerable.js104
-rw-r--r--toolkit/components/reader/Readability.js289
-rw-r--r--toolkit/components/reader/ReaderMode.jsm130
-rw-r--r--toolkit/components/reader/Readerable.js96
-rw-r--r--toolkit/components/reader/Readerable.jsm10
-rw-r--r--toolkit/components/reader/moz.build4
-rw-r--r--toolkit/locales/en-US/chrome/global/aboutReader.properties11
-rw-r--r--toolkit/themes/shared/aboutReader.css49
-rw-r--r--toolkit/themes/windows/global/button.css2
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
index 33bb04e99..0f55f0ba5 100644
--- a/application/palemoon/branding/official/firefox.ico
+++ b/application/palemoon/branding/official/firefox.ico
Binary files differ
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 {