mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-25 08:37:30 +09:00
Merge remote-tracking branch 'origin/master' into custom
This commit is contained in:
commit
220b809fb9
37 changed files with 560 additions and 387 deletions
|
|
@ -8,6 +8,9 @@
|
||||||
#include "Accessible-inl.h"
|
#include "Accessible-inl.h"
|
||||||
#include "nsEventShell.h"
|
#include "nsEventShell.h"
|
||||||
#include "DocAccessible.h"
|
#include "DocAccessible.h"
|
||||||
|
#include "DocAccessible-inl.h"
|
||||||
|
#include "EmbeddedObjCollector.h"
|
||||||
|
#include "NotificationController.h"
|
||||||
#ifdef A11Y_LOG
|
#ifdef A11Y_LOG
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@
|
||||||
#include "nsIDocShellTreeItem.h"
|
#include "nsIDocShellTreeItem.h"
|
||||||
#include "nsIURI.h"
|
#include "nsIURI.h"
|
||||||
#include "mozilla/dom/Element.h"
|
#include "mozilla/dom/Element.h"
|
||||||
|
#include "mozilla/dom/HTMLBodyElement.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::a11y;
|
using namespace mozilla::a11y;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace mozilla::a11y;
|
using namespace mozilla::a11y;
|
||||||
|
using namespace mozilla::dom;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// NotificationCollector
|
// NotificationCollector
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ UNIFIED_SOURCES += [
|
||||||
]
|
]
|
||||||
|
|
||||||
if CONFIG['A11Y_LOG']:
|
if CONFIG['A11Y_LOG']:
|
||||||
UNIFIED_SOURCES += [
|
SOURCES += [
|
||||||
'Logging.cpp',
|
'Logging.cpp',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include "nsIBaseWindow.h"
|
#include "nsIBaseWindow.h"
|
||||||
#include "nsIDocShellTreeOwner.h"
|
#include "nsIDocShellTreeOwner.h"
|
||||||
|
#include "nsIContentInlines.h"
|
||||||
#include "nsIDocument.h"
|
#include "nsIDocument.h"
|
||||||
#include "nsIDOMHTMLDocument.h"
|
#include "nsIDOMHTMLDocument.h"
|
||||||
#include "nsIDOMHTMLElement.h"
|
#include "nsIDOMHTMLElement.h"
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ EXPORTS.mozilla.a11y += [
|
||||||
'HyperTextAccessibleWrap.h',
|
'HyperTextAccessibleWrap.h',
|
||||||
]
|
]
|
||||||
|
|
||||||
UNIFIED_SOURCES += [
|
SOURCES += [
|
||||||
'AccessibleWrap.mm',
|
'AccessibleWrap.mm',
|
||||||
'DocAccessibleWrap.mm',
|
'DocAccessibleWrap.mm',
|
||||||
'MacUtils.mm',
|
'MacUtils.mm',
|
||||||
|
|
|
||||||
|
|
@ -49,42 +49,6 @@ using namespace mozilla::a11y;
|
||||||
// - NSAccessibilityMathPrescriptsAttribute @"AXMathPrescripts"
|
// - NSAccessibilityMathPrescriptsAttribute @"AXMathPrescripts"
|
||||||
// - NSAccessibilityMathPostscriptsAttribute @"AXMathPostscripts"
|
// - 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 -
|
#pragma mark -
|
||||||
|
|
||||||
@implementation mozAccessible
|
@implementation mozAccessible
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
#import "nsCocoaUtils.h"
|
#import "nsCocoaUtils.h"
|
||||||
|
|
||||||
|
using namespace mozilla::a11y;
|
||||||
|
|
||||||
@implementation mozHeadingAccessible
|
@implementation mozHeadingAccessible
|
||||||
|
|
||||||
- (NSString*)title
|
- (NSString*)title
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,50 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#import "Accessible-inl.h"
|
||||||
#import "mozTableAccessible.h"
|
#import "mozTableAccessible.h"
|
||||||
|
#import "TableAccessible.h"
|
||||||
|
#import "TableCellAccessible.h"
|
||||||
#import "nsCocoaUtils.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
|
@implementation mozTablePartAccessible
|
||||||
- (BOOL)isLayoutTablePart;
|
- (BOOL)isLayoutTablePart;
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#import "mozTextAccessible.h"
|
#import "mozTextAccessible.h"
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
using namespace mozilla::a11y;
|
using namespace mozilla::a11y;
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include "xpcAccessibleHyperText.h"
|
#include "xpcAccessibleHyperText.h"
|
||||||
|
|
||||||
#include "Accessible-inl.h"
|
#include "Accessible-inl.h"
|
||||||
|
#include "mozilla/a11y/DocAccessibleParent.h"
|
||||||
#include "HyperTextAccessible-inl.h"
|
#include "HyperTextAccessible-inl.h"
|
||||||
#include "TextRange.h"
|
#include "TextRange.h"
|
||||||
#include "xpcAccessibleDocument.h"
|
#include "xpcAccessibleDocument.h"
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AboutReader",
|
||||||
"resource://gre/modules/AboutReader.jsm");
|
"resource://gre/modules/AboutReader.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
|
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
|
||||||
"resource://gre/modules/ReaderMode.jsm");
|
"resource://gre/modules/ReaderMode.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Readerable",
|
||||||
|
"resource://gre/modules/Readerable.jsm");
|
||||||
XPCOMUtils.defineLazyGetter(this, "SimpleServiceDiscovery", function() {
|
XPCOMUtils.defineLazyGetter(this, "SimpleServiceDiscovery", function() {
|
||||||
let ssdp = Cu.import("resource://gre/modules/SimpleServiceDiscovery.jsm", {}).SimpleServiceDiscovery;
|
let ssdp = Cu.import("resource://gre/modules/SimpleServiceDiscovery.jsm", {}).SimpleServiceDiscovery;
|
||||||
// Register targets
|
// Register targets
|
||||||
|
|
@ -339,7 +341,7 @@ var AboutReaderListener = {
|
||||||
* painted is not going to work.
|
* painted is not going to work.
|
||||||
*/
|
*/
|
||||||
updateReaderButton: function(forceNonArticle) {
|
updateReaderButton: function(forceNonArticle) {
|
||||||
if (!ReaderMode.isEnabledForParseOnLoad || this.isAboutReader ||
|
if (!Readerable.isEnabledForParseOnLoad || this.isAboutReader ||
|
||||||
!content || !(content.document instanceof content.HTMLDocument) ||
|
!content || !(content.document instanceof content.HTMLDocument) ||
|
||||||
content.document.mozSyntheticDocument) {
|
content.document.mozSyntheticDocument) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -378,7 +380,7 @@ var AboutReaderListener = {
|
||||||
this.cancelPotentialPendingReadabilityCheck();
|
this.cancelPotentialPendingReadabilityCheck();
|
||||||
// Only send updates when there are articles; there's no point updating with
|
// Only send updates when there are articles; there's no point updating with
|
||||||
// |false| all the time.
|
// |false| all the time.
|
||||||
if (ReaderMode.isProbablyReaderable(content.document)) {
|
if (Readerable.isProbablyReaderable(content.document)) {
|
||||||
sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
|
sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
|
||||||
} else if (forceNonArticle) {
|
} else if (forceNonArticle) {
|
||||||
sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
|
sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
|
||||||
"@mozilla.org/browser/aboutnewtab-service;1",
|
"@mozilla.org/browser/aboutnewtab-service;1",
|
||||||
"nsIAboutNewTabService");
|
"nsIAboutNewTabService");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
|
||||||
|
"resource://gre/modules/Deprecated.jsm");
|
||||||
|
|
||||||
this.__defineGetter__("BROWSER_NEW_TAB_URL", () => {
|
this.__defineGetter__("BROWSER_NEW_TAB_URL", () => {
|
||||||
if (PrivateBrowsingUtils.isWindowPrivate(window) &&
|
if (PrivateBrowsingUtils.isWindowPrivate(window) &&
|
||||||
!PrivateBrowsingUtils.permanentPrivateBrowsing &&
|
!PrivateBrowsingUtils.permanentPrivateBrowsing &&
|
||||||
|
|
@ -43,6 +46,13 @@ function getBrowserURL()
|
||||||
return "chrome://browser/content/browser.xul";
|
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) {
|
function getTopWin(skipPopups) {
|
||||||
// If this is called in a browser window, use that window regardless of
|
// 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
|
// whether it's the frontmost window, since commands can be executed in
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ if CONFIG['LIBXUL_SDK']:
|
||||||
SOURCES += ['nsBrowserApp.cpp']
|
SOURCES += ['nsBrowserApp.cpp']
|
||||||
|
|
||||||
FINAL_TARGET_FILES += ['blocklist.xml']
|
FINAL_TARGET_FILES += ['blocklist.xml']
|
||||||
|
FINAL_TARGET_FILES.defaults += ['permissions']
|
||||||
FINAL_TARGET_FILES.defaults.profile += ['profile/prefs.js']
|
FINAL_TARGET_FILES.defaults.profile += ['profile/prefs.js']
|
||||||
|
|
||||||
DEFINES['APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
|
DEFINES['APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
|
||||||
|
|
|
||||||
14
application/palemoon/app/permissions
Normal file
14
application/palemoon/app/permissions
Normal file
|
|
@ -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
|
||||||
|
|
@ -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}.name", "chrome://browser/locale/browser.properties");
|
||||||
pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description", "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);
|
pref("xpinstall.whitelist.required", false);
|
||||||
// Allow installing XPI add-ons by direct URL requests (no referrer)
|
// Allow installing XPI add-ons by direct URL requests (no referrer)
|
||||||
pref("xpinstall.whitelist.directRequest", true);
|
pref("xpinstall.whitelist.directRequest", true);
|
||||||
|
|
@ -1105,6 +1102,9 @@ pref("full-screen-api.enabled", true);
|
||||||
// 0-100 (currently)
|
// 0-100 (currently)
|
||||||
pref("permissions.places-sites-limit", 50);
|
pref("permissions.places-sites-limit", 50);
|
||||||
|
|
||||||
|
// Built-in default permissions.
|
||||||
|
pref("permissions.manager.defaultsUrl", "resource://app/defaults/permissions");
|
||||||
|
|
||||||
// Startup Crash Tracking
|
// Startup Crash Tracking
|
||||||
// number of startup crashes that can occur before starting into safe mode automatically
|
// 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)
|
// (this pref has no effect if more than 6 hours have passed since the last crash)
|
||||||
|
|
|
||||||
|
|
@ -57,15 +57,21 @@
|
||||||
label="&helpTroubleshootingInfo.label;"
|
label="&helpTroubleshootingInfo.label;"
|
||||||
oncommand="openTroubleshootingPage()"
|
oncommand="openTroubleshootingPage()"
|
||||||
onclick="checkForMiddleClick(this, event);"/>
|
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"
|
<menuitem id="feedbackPage"
|
||||||
accesskey="&helpFeedbackPage.accesskey;"
|
accesskey="&helpFeedbackPage.accesskey;"
|
||||||
label="&helpFeedbackPage.label;"
|
label="&helpFeedbackPage.label;"
|
||||||
oncommand="openFeedbackPage()"
|
oncommand="openFeedbackPage()"
|
||||||
onclick="checkForMiddleClick(this, event);"/>
|
onclick="checkForMiddleClick(this, event);"/>
|
||||||
<menuitem id="helpSafeMode"
|
|
||||||
accesskey="&helpSafeMode.accesskey;"
|
|
||||||
label="&helpSafeMode.label;"
|
|
||||||
oncommand="restart(true);"/>
|
|
||||||
<menuseparator id="updatesSeparator"/>
|
<menuseparator id="updatesSeparator"/>
|
||||||
<menuitem id="checkForUpdates" class="menuitem-iconic"
|
<menuitem id="checkForUpdates" class="menuitem-iconic"
|
||||||
#ifdef MOZ_UPDATER
|
#ifdef MOZ_UPDATER
|
||||||
|
|
|
||||||
|
|
@ -350,14 +350,19 @@
|
||||||
label="&helpTroubleshootingInfo.label;"
|
label="&helpTroubleshootingInfo.label;"
|
||||||
oncommand="openTroubleshootingPage()"
|
oncommand="openTroubleshootingPage()"
|
||||||
onclick="checkForMiddleClick(this,event);"/>
|
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"
|
<menuitem id="appmenu_feedbackPage"
|
||||||
label="&helpFeedbackPage.label;"
|
label="&helpFeedbackPage.label;"
|
||||||
oncommand="openFeedbackPage()"
|
oncommand="openFeedbackPage()"
|
||||||
onclick="checkForMiddleClick(this, event);"/>
|
onclick="checkForMiddleClick(this, event);"/>
|
||||||
<menuseparator/>
|
|
||||||
<menuitem id="appmenu_safeMode"
|
|
||||||
label="&appMenuSafeMode.label;"
|
|
||||||
oncommand="restart(true);"/>
|
|
||||||
#ifdef MOZ_UPDATER
|
#ifdef MOZ_UPDATER
|
||||||
<menuseparator/>
|
<menuseparator/>
|
||||||
<menuitem id="appmenu_checkForUpdates"
|
<menuitem id="appmenu_checkForUpdates"
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@ Components.utils.import("resource:///modules/RecentWindow.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "ShellService",
|
XPCOMUtils.defineLazyModuleGetter(this, "ShellService",
|
||||||
"resource:///modules/ShellService.jsm");
|
"resource:///modules/ShellService.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
|
||||||
|
"resource://gre/modules/Deprecated.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "BROWSER_NEW_TAB_URL", function () {
|
XPCOMUtils.defineLazyGetter(this, "BROWSER_NEW_TAB_URL", function () {
|
||||||
const PREF = "browser.newtab.url";
|
const PREF = "browser.newtab.url";
|
||||||
|
|
||||||
|
|
@ -57,6 +60,14 @@ function getBrowserURL()
|
||||||
return "chrome://browser/content/browser.xul";
|
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) {
|
function getTopWin(skipPopups) {
|
||||||
// If this is called in a browser window, use that window regardless of
|
// 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
|
// whether it's the frontmost window, since commands can be executed in
|
||||||
|
|
@ -701,6 +712,18 @@ function openAdvancedPreferences(tabID)
|
||||||
openPreferences("paneAdvanced", { "advancedTab" : tabID });
|
openPreferences("paneAdvanced", { "advancedTab" : 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
|
* Opens the troubleshooting information (about:support) page for this version
|
||||||
* of the application.
|
* of the application.
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 105 KiB |
|
|
@ -6,6 +6,8 @@ const Ci = Components.interfaces;
|
||||||
const Cc = Components.classes;
|
const Cc = Components.classes;
|
||||||
|
|
||||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
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_CID = Components.ID("fe74cf80-aa2d-11db-abbd-0800200c9a66");
|
||||||
const APPLICATION_CONTRACTID = "@mozilla.org/fuel/application;1";
|
const APPLICATION_CONTRACTID = "@mozilla.org/fuel/application;1";
|
||||||
|
|
@ -734,6 +736,8 @@ var ApplicationFactory = {
|
||||||
//=================================================
|
//=================================================
|
||||||
// Application constructor
|
// Application constructor
|
||||||
function Application() {
|
function Application() {
|
||||||
|
Deprecated.warning("FUEL is deprecated, you should use the standard Toolkit API instead.",
|
||||||
|
"https://github.com/MoonchildProductions/UXP/issues/1083");
|
||||||
this.initToolkitHelpers();
|
this.initToolkitHelpers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -231,6 +231,7 @@
|
||||||
|
|
||||||
; [Default Preferences]
|
; [Default Preferences]
|
||||||
; All the pref files must be part of base to prevent migration bugs
|
; 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.js
|
||||||
@RESPATH@/browser/@PREF_DIR@/palemoon-branding.js
|
@RESPATH@/browser/@PREF_DIR@/palemoon-branding.js
|
||||||
@RESPATH@/greprefs.js
|
@RESPATH@/greprefs.js
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
<!ENTITY helpMenuWin.label "Help">
|
<!ENTITY helpMenuWin.label "Help">
|
||||||
<!ENTITY helpMenuWin.accesskey "H">
|
<!ENTITY helpMenuWin.accesskey "H">
|
||||||
<!ENTITY updateCmd.label "Check for Updates…">
|
<!ENTITY updateCmd.label "Check for Updates…">
|
||||||
|
<!ENTITY helpReleaseNotes.label "Release Notes">
|
||||||
|
<!ENTITY helpReleaseNotes.accesskey "N">
|
||||||
<!ENTITY aboutProduct.label "About &brandShortName;">
|
<!ENTITY aboutProduct.label "About &brandShortName;">
|
||||||
<!ENTITY aboutProduct.accesskey "A">
|
<!ENTITY aboutProduct.accesskey "A">
|
||||||
<!ENTITY productHelp.label "&brandShortName; Help">
|
<!ENTITY productHelp.label "&brandShortName; Help">
|
||||||
|
|
|
||||||
|
|
@ -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. */
|
/* 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;
|
visibility: visible;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
|
|
|
||||||
|
|
@ -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. */
|
/* 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;
|
visibility: visible;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
|
|
|
||||||
|
|
@ -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. */
|
/* 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;
|
visibility: visible;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
|
|
|
||||||
|
|
@ -351,6 +351,12 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
|
||||||
nsHtml5FlushLoopGuard guard(this); // this is also the self-kungfu!
|
nsHtml5FlushLoopGuard guard(this); // this is also the self-kungfu!
|
||||||
|
|
||||||
RefPtr<nsParserBase> parserKungFuDeathGrip(mParser);
|
RefPtr<nsParserBase> parserKungFuDeathGrip(mParser);
|
||||||
|
RefPtr<nsHtml5StreamParser> streamParserGrip;
|
||||||
|
if (mParser) {
|
||||||
|
streamParserGrip = GetParser()->GetStreamParser();
|
||||||
|
}
|
||||||
|
mozilla::Unused
|
||||||
|
<< streamParserGrip; // Intentionally not used within function
|
||||||
|
|
||||||
// Remember the entry time
|
// Remember the entry time
|
||||||
(void) nsContentSink::WillParseImpl();
|
(void) nsContentSink::WillParseImpl();
|
||||||
|
|
@ -409,11 +415,6 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
|
||||||
mOpQueue.Clear(); // clear in order to be able to assert in destructor
|
mOpQueue.Clear(); // clear in order to be able to assert in destructor
|
||||||
return;
|
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.
|
// Now parse content left in the document.write() buffer queue if any.
|
||||||
// This may generate tree ops on its own or dequeue a speculation.
|
// This may generate tree ops on its own or dequeue a speculation.
|
||||||
nsresult rv = GetParser()->ParseUntilBlocked();
|
nsresult rv = GetParser()->ParseUntilBlocked();
|
||||||
|
|
@ -529,6 +530,12 @@ nsHtml5TreeOpExecutor::FlushDocumentWrite()
|
||||||
RefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this);
|
RefPtr<nsHtml5TreeOpExecutor> kungFuDeathGrip(this);
|
||||||
RefPtr<nsParserBase> parserKungFuDeathGrip(mParser);
|
RefPtr<nsParserBase> parserKungFuDeathGrip(mParser);
|
||||||
mozilla::Unused << parserKungFuDeathGrip; // Intentionally not used within function
|
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,
|
NS_ASSERTION(!mReadingFromStage,
|
||||||
"Got doc write flush when reading from stage");
|
"Got doc write flush when reading from stage");
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ var AboutReader = function(win, articlePromise) {
|
||||||
|
|
||||||
this._scrollOffset = win.pageYOffset;
|
this._scrollOffset = win.pageYOffset;
|
||||||
|
|
||||||
|
doc.addEventListener("mousedown", this);
|
||||||
doc.addEventListener("click", this);
|
doc.addEventListener("click", this);
|
||||||
|
|
||||||
win.addEventListener("pagehide", this);
|
win.addEventListener("pagehide", this);
|
||||||
|
|
@ -119,6 +120,25 @@ var AboutReader = function(win, articlePromise) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this._loadArticle();
|
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 = {
|
AboutReader.prototype = {
|
||||||
|
|
@ -191,13 +211,16 @@ AboutReader.prototype = {
|
||||||
if (!aEvent.isTrusted)
|
if (!aEvent.isTrusted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
let target = aEvent.target;
|
||||||
switch (aEvent.type) {
|
switch (aEvent.type) {
|
||||||
|
case "mousedown":
|
||||||
|
if (!target.closest(".dropdown-popup")) {
|
||||||
|
this._closeDropdowns();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "click":
|
case "click":
|
||||||
let target = aEvent.target;
|
|
||||||
if (target.classList.contains("dropdown-toggle")) {
|
if (target.classList.contains("dropdown-toggle")) {
|
||||||
this._toggleDropdownClicked(aEvent);
|
this._toggleDropdownClicked(aEvent);
|
||||||
} else if (!target.closest(".dropdown-popup")) {
|
|
||||||
this._closeDropdowns();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "scroll":
|
case "scroll":
|
||||||
|
|
@ -276,13 +299,10 @@ AboutReader.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
_setFontSize(newFontSize) {
|
_setFontSize(newFontSize) {
|
||||||
let containerClasses = this._containerElement.classList;
|
|
||||||
|
|
||||||
if (this._fontSize > 0)
|
|
||||||
containerClasses.remove("font-size" + this._fontSize);
|
|
||||||
|
|
||||||
this._fontSize = newFontSize;
|
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);
|
return AsyncPrefs.set("reader.font_size", this._fontSize);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -691,7 +691,7 @@
|
||||||
// the attribute value will be HTML escaped.
|
// the attribute value will be HTML escaped.
|
||||||
var val = attr.value;
|
var val = attr.value;
|
||||||
var quote = (val.indexOf('"') === -1 ? '"' : "'");
|
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) {
|
if (child.localName in voidElems && !child.childNodes.length) {
|
||||||
|
|
@ -970,7 +970,7 @@
|
||||||
strBuf.push(c);
|
strBuf.push(c);
|
||||||
c = this.nextChar();
|
c = this.nextChar();
|
||||||
}
|
}
|
||||||
var tag = strBuf.join('');
|
var tag = strBuf.join("");
|
||||||
|
|
||||||
if (!tag)
|
if (!tag)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -981,7 +981,9 @@
|
||||||
while (c !== "/" && c !== ">") {
|
while (c !== "/" && c !== ">") {
|
||||||
if (c === undefined)
|
if (c === undefined)
|
||||||
return false;
|
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--;
|
this.currentChar--;
|
||||||
c = this.nextChar();
|
c = this.nextChar();
|
||||||
if (c !== "/" && c !== ">") {
|
if (c !== "/" && c !== ">") {
|
||||||
|
|
|
||||||
104
toolkit/components/reader/Readability-readerable.js
Normal file
104
toolkit/components/reader/Readability-readerable.js
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -46,6 +46,7 @@ function Readability(doc, options) {
|
||||||
this._articleTitle = null;
|
this._articleTitle = null;
|
||||||
this._articleByline = null;
|
this._articleByline = null;
|
||||||
this._articleDir = null;
|
this._articleDir = null;
|
||||||
|
this._articleSiteName = null;
|
||||||
this._attempts = [];
|
this._attempts = [];
|
||||||
|
|
||||||
// Configurable options
|
// Configurable options
|
||||||
|
|
@ -118,15 +119,18 @@ Readability.prototype = {
|
||||||
// All of the regular expressions in use within readability.
|
// All of the regular expressions in use within readability.
|
||||||
// Defined up here so we don't instantiate them repeatedly in loops.
|
// Defined up here so we don't instantiate them repeatedly in loops.
|
||||||
REGEXPS: {
|
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,
|
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,
|
okMaybeItsACandidate: /and|article|body|column|main|shadow/i,
|
||||||
|
|
||||||
positive: /article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story/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,
|
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,
|
extraneous: /print|archive|comment|discuss|e[\-]?mail|share|reply|all|login|sign|single|utility/i,
|
||||||
byline: /byline|author|dateline|writtenby|p-author/i,
|
byline: /byline|author|dateline|writtenby|p-author/i,
|
||||||
replaceFonts: /<(\/?)font[^>]*>/gi,
|
replaceFonts: /<(\/?)font[^>]*>/gi,
|
||||||
normalize: /\s{2,}/g,
|
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,
|
nextLink: /(next|weiter|continue|>([^\|]|$)|»([^\|]|$))/i,
|
||||||
prevLink: /(prev|earl|old|new|<|«)/i,
|
prevLink: /(prev|earl|old|new|<|«)/i,
|
||||||
whitespace: /^\s*$/,
|
whitespace: /^\s*$/,
|
||||||
|
|
@ -267,7 +271,7 @@ Readability.prototype = {
|
||||||
|
|
||||||
_getAllNodesWithTag: function(node, tagNames) {
|
_getAllNodesWithTag: function(node, tagNames) {
|
||||||
if (node.querySelectorAll) {
|
if (node.querySelectorAll) {
|
||||||
return node.querySelectorAll(tagNames.join(','));
|
return node.querySelectorAll(tagNames.join(","));
|
||||||
}
|
}
|
||||||
return [].concat.apply([], tagNames.map(function(tag) {
|
return [].concat.apply([], tagNames.map(function(tag) {
|
||||||
var collection = node.getElementsByTagName(tag);
|
var collection = node.getElementsByTagName(tag);
|
||||||
|
|
@ -327,7 +331,7 @@ Readability.prototype = {
|
||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
var links = articleContent.getElementsByTagName("a");
|
var links = this._getAllNodesWithTag(articleContent, ["a"]);
|
||||||
this._forEachNode(links, function(link) {
|
this._forEachNode(links, function(link) {
|
||||||
var href = link.getAttribute("href");
|
var href = link.getAttribute("href");
|
||||||
if (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) {
|
this._forEachNode(imgs, function(img) {
|
||||||
var src = img.getAttribute("src");
|
var src = img.getAttribute("src");
|
||||||
if (src) {
|
if (src) {
|
||||||
|
|
@ -366,7 +370,7 @@ Readability.prototype = {
|
||||||
|
|
||||||
// If they had an element with id "title" in their HTML
|
// If they had an element with id "title" in their HTML
|
||||||
if (typeof curTitle !== "string")
|
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. */}
|
} catch (e) {/* ignore exceptions setting the title. */}
|
||||||
|
|
||||||
var titleHadHierarchicalSeparators = false;
|
var titleHadHierarchicalSeparators = false;
|
||||||
|
|
@ -377,18 +381,18 @@ Readability.prototype = {
|
||||||
// If there's a separator in the title, first remove the final part
|
// If there's a separator in the title, first remove the final part
|
||||||
if ((/ [\|\-\\\/>»] /).test(curTitle)) {
|
if ((/ [\|\-\\\/>»] /).test(curTitle)) {
|
||||||
titleHadHierarchicalSeparators = / [\\\/>»] /.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
|
// If the resulting title is too short (3 words or fewer), remove
|
||||||
// the first part instead:
|
// the first part instead:
|
||||||
if (wordCount(curTitle) < 3)
|
if (wordCount(curTitle) < 3)
|
||||||
curTitle = origTitle.replace(/[^\|\-\\\/>»]*[\|\-\\\/>»](.*)/gi, '$1');
|
curTitle = origTitle.replace(/[^\|\-\\\/>»]*[\|\-\\\/>»](.*)/gi, "$1");
|
||||||
} else if (curTitle.indexOf(': ') !== -1) {
|
} else if (curTitle.indexOf(": ") !== -1) {
|
||||||
// Check if we have an heading containing this exact string, so we
|
// Check if we have an heading containing this exact string, so we
|
||||||
// could assume it's the full title.
|
// could assume it's the full title.
|
||||||
var headings = this._concatNodeLists(
|
var headings = this._concatNodeLists(
|
||||||
doc.getElementsByTagName('h1'),
|
doc.getElementsByTagName("h1"),
|
||||||
doc.getElementsByTagName('h2')
|
doc.getElementsByTagName("h2")
|
||||||
);
|
);
|
||||||
var trimmedTitle = curTitle.trim();
|
var trimmedTitle = curTitle.trim();
|
||||||
var match = this._someNode(headings, function(heading) {
|
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 we don't, let's extract the title out of the original title string.
|
||||||
if (!match) {
|
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 the title is now too short, try the first colon instead:
|
||||||
if (wordCount(curTitle) < 3) {
|
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
|
// 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
|
// 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;
|
curTitle = origTitle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (curTitle.length > 150 || curTitle.length < 15) {
|
} else if (curTitle.length > 150 || curTitle.length < 15) {
|
||||||
var hOnes = doc.getElementsByTagName('h1');
|
var hOnes = doc.getElementsByTagName("h1");
|
||||||
|
|
||||||
if (hOnes.length === 1)
|
if (hOnes.length === 1)
|
||||||
curTitle = this._getInnerText(hOnes[0]);
|
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
|
// If we now have 4 words or fewer as our title, and either no
|
||||||
// 'hierarchical' separators (\, /, > or ») were found in the original
|
// 'hierarchical' separators (\, /, > or ») were found in the original
|
||||||
// title or we decreased the number of words by more than 1 word, use
|
// title or we decreased the number of words by more than 1 word, use
|
||||||
|
|
@ -505,7 +509,8 @@ Readability.prototype = {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._isPhrasingContent(next)) break;
|
if (!this._isPhrasingContent(next))
|
||||||
|
break;
|
||||||
|
|
||||||
// Otherwise, make this node a child of the new <p>.
|
// Otherwise, make this node a child of the new <p>.
|
||||||
var sibling = next.nextSibling;
|
var sibling = next.nextSibling;
|
||||||
|
|
@ -513,9 +518,12 @@ Readability.prototype = {
|
||||||
next = sibling;
|
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,
|
// 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,
|
// they are probably using it as a header and not a subheader,
|
||||||
// so remove it since we already extract the title separately.
|
// so remove it since we already extract the title separately.
|
||||||
var h2 = articleContent.getElementsByTagName('h2');
|
var h2 = articleContent.getElementsByTagName("h2");
|
||||||
if (h2.length === 1) {
|
if (h2.length === 1) {
|
||||||
var lengthSimilarRate = (h2[0].textContent.length - this._articleTitle.length) / this._articleTitle.length;
|
var lengthSimilarRate = (h2[0].textContent.length - this._articleTitle.length) / this._articleTitle.length;
|
||||||
if (Math.abs(lengthSimilarRate) < 0.5) {
|
if (Math.abs(lengthSimilarRate) < 0.5) {
|
||||||
|
|
@ -606,12 +614,12 @@ Readability.prototype = {
|
||||||
this._cleanConditionally(articleContent, "div");
|
this._cleanConditionally(articleContent, "div");
|
||||||
|
|
||||||
// Remove extra paragraphs
|
// Remove extra paragraphs
|
||||||
this._removeNodes(articleContent.getElementsByTagName('p'), function (paragraph) {
|
this._removeNodes(articleContent.getElementsByTagName("p"), function (paragraph) {
|
||||||
var imgCount = paragraph.getElementsByTagName('img').length;
|
var imgCount = paragraph.getElementsByTagName("img").length;
|
||||||
var embedCount = paragraph.getElementsByTagName('embed').length;
|
var embedCount = paragraph.getElementsByTagName("embed").length;
|
||||||
var objectCount = paragraph.getElementsByTagName('object').length;
|
var objectCount = paragraph.getElementsByTagName("object").length;
|
||||||
// At this point, nasty iframes have been removed, only remain embedded video ones.
|
// 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;
|
var totalCount = imgCount + embedCount + objectCount + iframeCount;
|
||||||
|
|
||||||
return totalCount === 0 && !this._getInnerText(paragraph, false);
|
return totalCount === 0 && !this._getInnerText(paragraph, false);
|
||||||
|
|
@ -648,34 +656,34 @@ Readability.prototype = {
|
||||||
node.readability = {"contentScore": 0};
|
node.readability = {"contentScore": 0};
|
||||||
|
|
||||||
switch (node.tagName) {
|
switch (node.tagName) {
|
||||||
case 'DIV':
|
case "DIV":
|
||||||
node.readability.contentScore += 5;
|
node.readability.contentScore += 5;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'PRE':
|
case "PRE":
|
||||||
case 'TD':
|
case "TD":
|
||||||
case 'BLOCKQUOTE':
|
case "BLOCKQUOTE":
|
||||||
node.readability.contentScore += 3;
|
node.readability.contentScore += 3;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'ADDRESS':
|
case "ADDRESS":
|
||||||
case 'OL':
|
case "OL":
|
||||||
case 'UL':
|
case "UL":
|
||||||
case 'DL':
|
case "DL":
|
||||||
case 'DD':
|
case "DD":
|
||||||
case 'DT':
|
case "DT":
|
||||||
case 'LI':
|
case "LI":
|
||||||
case 'FORM':
|
case "FORM":
|
||||||
node.readability.contentScore -= 3;
|
node.readability.contentScore -= 3;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'H1':
|
case "H1":
|
||||||
case 'H2':
|
case "H2":
|
||||||
case 'H3':
|
case "H3":
|
||||||
case 'H4':
|
case "H4":
|
||||||
case 'H5':
|
case "H5":
|
||||||
case 'H6':
|
case "H6":
|
||||||
case 'TH':
|
case "TH":
|
||||||
node.readability.contentScore -= 5;
|
node.readability.contentScore -= 5;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -824,12 +832,14 @@ Readability.prototype = {
|
||||||
if (p !== null) {
|
if (p !== null) {
|
||||||
p.appendChild(childNode);
|
p.appendChild(childNode);
|
||||||
} else if (!this._isWhitespace(childNode)) {
|
} else if (!this._isWhitespace(childNode)) {
|
||||||
p = doc.createElement('p');
|
p = doc.createElement("p");
|
||||||
node.replaceChild(p, childNode);
|
node.replaceChild(p, childNode);
|
||||||
p.appendChild(childNode);
|
p.appendChild(childNode);
|
||||||
}
|
}
|
||||||
} else if (p !== null) {
|
} 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;
|
p = null;
|
||||||
}
|
}
|
||||||
childNode = nextSibling;
|
childNode = nextSibling;
|
||||||
|
|
@ -860,7 +870,7 @@ Readability.prototype = {
|
||||||
**/
|
**/
|
||||||
var candidates = [];
|
var candidates = [];
|
||||||
this._forEachNode(elementsToScore, function(elementToScore) {
|
this._forEachNode(elementsToScore, function(elementToScore) {
|
||||||
if (!elementToScore.parentNode || typeof(elementToScore.parentNode.tagName) === 'undefined')
|
if (!elementToScore.parentNode || typeof(elementToScore.parentNode.tagName) === "undefined")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If this paragraph is less than 25 characters, don't even count it.
|
// If this paragraph is less than 25 characters, don't even count it.
|
||||||
|
|
@ -879,17 +889,17 @@ Readability.prototype = {
|
||||||
contentScore += 1;
|
contentScore += 1;
|
||||||
|
|
||||||
// Add points for any commas within this paragraph.
|
// 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.
|
// For every 100 characters in this paragraph, add another point. Up to 3 points.
|
||||||
contentScore += Math.min(Math.floor(innerText.length / 100), 3);
|
contentScore += Math.min(Math.floor(innerText.length / 100), 3);
|
||||||
|
|
||||||
// Initialize and score ancestors.
|
// Initialize and score ancestors.
|
||||||
this._forEachNode(ancestors, function(ancestor, level) {
|
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;
|
return;
|
||||||
|
|
||||||
if (typeof(ancestor.readability) === 'undefined') {
|
if (typeof(ancestor.readability) === "undefined") {
|
||||||
this._initializeNode(ancestor);
|
this._initializeNode(ancestor);
|
||||||
candidates.push(ancestor);
|
candidates.push(ancestor);
|
||||||
}
|
}
|
||||||
|
|
@ -920,7 +930,7 @@ Readability.prototype = {
|
||||||
var candidateScore = candidate.readability.contentScore * (1 - this._getLinkDensity(candidate));
|
var candidateScore = candidate.readability.contentScore * (1 - this._getLinkDensity(candidate));
|
||||||
candidate.readability.contentScore = candidateScore;
|
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++) {
|
for (var t = 0; t < this._nbTopCandidates; t++) {
|
||||||
var aTopCandidate = topCandidates[t];
|
var aTopCandidate = topCandidates[t];
|
||||||
|
|
@ -1039,8 +1049,8 @@ Readability.prototype = {
|
||||||
var sibling = siblings[s];
|
var sibling = siblings[s];
|
||||||
var append = false;
|
var append = false;
|
||||||
|
|
||||||
this.log("Looking at sibling node:", sibling, sibling.readability ? ("with score " + sibling.readability.contentScore) : '');
|
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("Sibling has score", sibling.readability ? sibling.readability.contentScore : "Unknown");
|
||||||
|
|
||||||
if (sibling === topCandidate) {
|
if (sibling === topCandidate) {
|
||||||
append = true;
|
append = true;
|
||||||
|
|
@ -1074,7 +1084,7 @@ Readability.prototype = {
|
||||||
if (this.ALTER_TO_DIV_EXCEPTIONS.indexOf(sibling.nodeName) === -1) {
|
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.
|
// 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.
|
// 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");
|
sibling = this._setNodeTag(sibling, "DIV");
|
||||||
}
|
}
|
||||||
|
|
@ -1142,7 +1152,7 @@ Readability.prototype = {
|
||||||
this._attempts.push({articleContent: articleContent, textLength: textLength});
|
this._attempts.push({articleContent: articleContent, textLength: textLength});
|
||||||
// No luck after removing flags, just return the longest text we found during the different loops
|
// No luck after removing flags, just return the longest text we found during the different loops
|
||||||
this._attempts.sort(function (a, b) {
|
this._attempts.sort(function (a, b) {
|
||||||
return a.textLength < b.textLength;
|
return b.textLength - a.textLength;
|
||||||
});
|
});
|
||||||
|
|
||||||
// But first check if we actually have something
|
// But first check if we actually have something
|
||||||
|
|
@ -1182,7 +1192,7 @@ Readability.prototype = {
|
||||||
* @return Boolean - whether the input string is a byline.
|
* @return Boolean - whether the input string is a byline.
|
||||||
*/
|
*/
|
||||||
_isValidByline: function(byline) {
|
_isValidByline: function(byline) {
|
||||||
if (typeof byline == 'string' || byline instanceof String) {
|
if (typeof byline == "string" || byline instanceof String) {
|
||||||
byline = byline.trim();
|
byline = byline.trim();
|
||||||
return (byline.length > 0) && (byline.length < 100);
|
return (byline.length > 0) && (byline.length < 100);
|
||||||
}
|
}
|
||||||
|
|
@ -1199,61 +1209,72 @@ Readability.prototype = {
|
||||||
var values = {};
|
var values = {};
|
||||||
var metaElements = this._doc.getElementsByTagName("meta");
|
var metaElements = this._doc.getElementsByTagName("meta");
|
||||||
|
|
||||||
// Match "description", or Twitter's "twitter:description" (Cards)
|
// property is a space-separated list of values
|
||||||
// in name attribute.
|
var propertyPattern = /\s*(dc|dcterm|og|twitter)\s*:\s*(author|creator|description|title|site_name)\s*/gi;
|
||||||
var namePattern = /^\s*((twitter)\s*:\s*)?(description|title)\s*$/gi;
|
|
||||||
|
|
||||||
// Match Facebook's Open Graph title & description properties.
|
// name is a single value
|
||||||
var propertyPattern = /^\s*og\s*:\s*(description|title)\s*$/gi;
|
var namePattern = /^\s*(?:(dc|dcterm|og|twitter|weibo:(article|webpage))\s*[\.:]\s*)?(author|creator|description|title|site_name)\s*$/i;
|
||||||
|
|
||||||
// Find description tags.
|
// Find description tags.
|
||||||
this._forEachNode(metaElements, function(element) {
|
this._forEachNode(metaElements, function(element) {
|
||||||
var elementName = element.getAttribute("name");
|
var elementName = element.getAttribute("name");
|
||||||
var elementProperty = element.getAttribute("property");
|
var elementProperty = element.getAttribute("property");
|
||||||
|
var content = element.getAttribute("content");
|
||||||
if ([elementName, elementProperty].indexOf("author") !== -1) {
|
var matches = null;
|
||||||
metadata.byline = element.getAttribute("content");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = null;
|
var name = null;
|
||||||
if (namePattern.test(elementName)) {
|
|
||||||
name = elementName;
|
|
||||||
} else if (propertyPattern.test(elementProperty)) {
|
|
||||||
name = elementProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name) {
|
if (elementProperty) {
|
||||||
var content = element.getAttribute("content");
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!matches && elementName && namePattern.test(elementName)) {
|
||||||
|
name = elementName;
|
||||||
if (content) {
|
if (content) {
|
||||||
// Convert to lowercase and remove any whitespace
|
// Convert to lowercase, remove any whitespace, and convert dots
|
||||||
// so we can match below.
|
// to colons so we can match below.
|
||||||
name = name.toLowerCase().replace(/\s/g, '');
|
name = name.toLowerCase().replace(/\s/g, "").replace(/\./g, ":");
|
||||||
values[name] = content.trim();
|
values[name] = content.trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if ("description" in values) {
|
// get title
|
||||||
metadata.excerpt = values["description"];
|
metadata.title = values["dc:title"] ||
|
||||||
} else if ("og:description" in values) {
|
values["dcterm:title"] ||
|
||||||
// Use facebook open graph description.
|
values["og:title"] ||
|
||||||
metadata.excerpt = values["og:description"];
|
values["weibo:article:title"] ||
|
||||||
} else if ("twitter:description" in values) {
|
values["weibo:webpage:title"] ||
|
||||||
// Use twitter cards description.
|
values["title"] ||
|
||||||
metadata.excerpt = values["twitter:description"];
|
values["twitter:title"];
|
||||||
|
|
||||||
|
if (!metadata.title) {
|
||||||
|
metadata.title = this._getArticleTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata.title = this._getArticleTitle();
|
// get author
|
||||||
if (!metadata.title) {
|
metadata.byline = values["dc:creator"] ||
|
||||||
if ("og:title" in values) {
|
values["dcterm:creator"] ||
|
||||||
// Use facebook open graph title.
|
values["author"];
|
||||||
metadata.title = values["og:title"];
|
|
||||||
} else if ("twitter:title" in values) {
|
// get description
|
||||||
// Use twitter cards title.
|
metadata.excerpt = values["dc:description"] ||
|
||||||
metadata.title = values["twitter:title"];
|
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;
|
return metadata;
|
||||||
},
|
},
|
||||||
|
|
@ -1264,12 +1285,12 @@ Readability.prototype = {
|
||||||
* @param Element
|
* @param Element
|
||||||
**/
|
**/
|
||||||
_removeScripts: function(doc) {
|
_removeScripts: function(doc) {
|
||||||
this._removeNodes(doc.getElementsByTagName('script'), function(scriptNode) {
|
this._removeNodes(doc.getElementsByTagName("script"), function(scriptNode) {
|
||||||
scriptNode.nodeValue = "";
|
scriptNode.nodeValue = "";
|
||||||
scriptNode.removeAttribute('src');
|
scriptNode.removeAttribute("src");
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
this._removeNodes(doc.getElementsByTagName('noscript'));
|
this._removeNodes(doc.getElementsByTagName("noscript"));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1336,7 +1357,7 @@ Readability.prototype = {
|
||||||
* @return string
|
* @return string
|
||||||
**/
|
**/
|
||||||
_getInnerText: function(e, normalizeSpaces) {
|
_getInnerText: function(e, normalizeSpaces) {
|
||||||
normalizeSpaces = (typeof normalizeSpaces === 'undefined') ? true : normalizeSpaces;
|
normalizeSpaces = (typeof normalizeSpaces === "undefined") ? true : normalizeSpaces;
|
||||||
var textContent = e.textContent.trim();
|
var textContent = e.textContent.trim();
|
||||||
|
|
||||||
if (normalizeSpaces) {
|
if (normalizeSpaces) {
|
||||||
|
|
@ -1365,7 +1386,7 @@ Readability.prototype = {
|
||||||
* @return void
|
* @return void
|
||||||
**/
|
**/
|
||||||
_cleanStyles: function(e) {
|
_cleanStyles: function(e) {
|
||||||
if (!e || e.tagName.toLowerCase() === 'svg')
|
if (!e || e.tagName.toLowerCase() === "svg")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Remove `style` and deprecated presentational attributes
|
// Remove `style` and deprecated presentational attributes
|
||||||
|
|
@ -1374,8 +1395,8 @@ Readability.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.DEPRECATED_SIZE_ATTRIBUTE_ELEMS.indexOf(e.tagName) !== -1) {
|
if (this.DEPRECATED_SIZE_ATTRIBUTE_ELEMS.indexOf(e.tagName) !== -1) {
|
||||||
e.removeAttribute('width');
|
e.removeAttribute("width");
|
||||||
e.removeAttribute('height');
|
e.removeAttribute("height");
|
||||||
}
|
}
|
||||||
|
|
||||||
var cur = e.firstElementChild;
|
var cur = e.firstElementChild;
|
||||||
|
|
@ -1421,7 +1442,7 @@ Readability.prototype = {
|
||||||
var weight = 0;
|
var weight = 0;
|
||||||
|
|
||||||
// Look for a special classname
|
// 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))
|
if (this.REGEXPS.negative.test(e.className))
|
||||||
weight -= 25;
|
weight -= 25;
|
||||||
|
|
||||||
|
|
@ -1430,7 +1451,7 @@ Readability.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for a special ID
|
// 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))
|
if (this.REGEXPS.negative.test(e.id))
|
||||||
weight -= 25;
|
weight -= 25;
|
||||||
|
|
||||||
|
|
@ -1619,7 +1640,7 @@ Readability.prototype = {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._getCharCount(node, ',') < 10) {
|
if (this._getCharCount(node, ",") < 10) {
|
||||||
// If there are not very many commas, and the number of
|
// If there are not very many commas, and the number of
|
||||||
// non-paragraph elements is more than paragraphs or other
|
// non-paragraph elements is more than paragraphs or other
|
||||||
// ominous signs, remove the element.
|
// ominous signs, remove the element.
|
||||||
|
|
@ -1679,7 +1700,7 @@ Readability.prototype = {
|
||||||
**/
|
**/
|
||||||
_cleanHeaders: function(e) {
|
_cleanHeaders: function(e) {
|
||||||
for (var headerIndex = 1; headerIndex < 3; headerIndex += 1) {
|
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;
|
return this._getClassWeight(header) < 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1694,66 +1715,7 @@ Readability.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
_isProbablyVisible: function(node) {
|
_isProbablyVisible: function(node) {
|
||||||
return node.style.display != "none" && !node.hasAttribute("hidden");
|
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 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;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1812,6 +1774,7 @@ Readability.prototype = {
|
||||||
textContent: textContent,
|
textContent: textContent,
|
||||||
length: textContent.length,
|
length: textContent.length,
|
||||||
excerpt: metadata.excerpt,
|
excerpt: metadata.excerpt,
|
||||||
|
siteName: metadata.siteName || this._articleSiteName
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||||
// names so that rules in aboutReader.css can match them.
|
// names so that rules in aboutReader.css can match them.
|
||||||
const CLASSES_TO_PRESERVE = [
|
const CLASSES_TO_PRESERVE = [
|
||||||
"caption",
|
"caption",
|
||||||
|
"emoji",
|
||||||
"hidden",
|
"hidden",
|
||||||
"invisble",
|
"invisble",
|
||||||
"sr-only",
|
"sr-only",
|
||||||
|
|
@ -19,6 +20,7 @@ const CLASSES_TO_PRESERVE = [
|
||||||
"visuallyhidden",
|
"visuallyhidden",
|
||||||
"wp-caption",
|
"wp-caption",
|
||||||
"wp-caption-text",
|
"wp-caption-text",
|
||||||
|
"wp-smiley",
|
||||||
];
|
];
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
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, "EventDispatcher", "resource://gre/modules/Messaging.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "ReaderWorker", "resource://gre/modules/reader/ReaderWorker.jsm");
|
XPCOMUtils.defineLazyModuleGetter(this, "ReaderWorker", "resource://gre/modules/reader/ReaderWorker.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Readerable", "resource://gre/modules/Readerable.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;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.ReaderMode = {
|
this.ReaderMode = {
|
||||||
// Version of the cache schema.
|
// Version of the cache schema.
|
||||||
|
|
@ -44,42 +40,6 @@ this.ReaderMode = {
|
||||||
|
|
||||||
DEBUG: 0,
|
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,
|
* Enter the reader mode by going forward one step in history if applicable,
|
||||||
* if not, append the about:reader page in the history instead.
|
* if not, append the about:reader page in the history instead.
|
||||||
|
|
@ -174,39 +134,6 @@ this.ReaderMode = {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* Gets an article from a loaded browser's document. This method will not attempt
|
||||||
* to parse certain URIs (e.g. about: URIs).
|
* 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.
|
* @resolves JS object representing the article, or null if no article is found.
|
||||||
*/
|
*/
|
||||||
parseDocument(doc) {
|
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");
|
this.log("Reader mode disabled for URI");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +164,8 @@ this.ReaderMode = {
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
return null;
|
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");
|
this.log("Reader mode disabled for URI");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -246,7 +175,7 @@ this.ReaderMode = {
|
||||||
|
|
||||||
_downloadDocument(url) {
|
_downloadDocument(url) {
|
||||||
try {
|
try {
|
||||||
if (!this._shouldCheckUri(Services.io.newURI(url))) {
|
if (!Readerable.shouldCheckUri(Services.io.newURI(url))) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
|
@ -388,44 +317,6 @@ this.ReaderMode = {
|
||||||
dump("Reader: " + msg);
|
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
|
* Attempts to parse a document into an article. Heavy lifting happens
|
||||||
* in readerWorker.js.
|
* in readerWorker.js.
|
||||||
|
|
@ -650,3 +541,8 @@ this.ReaderMode = {
|
||||||
return readingSpeed.get(lang) || readingSpeed.get("en");
|
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);
|
||||||
|
|
|
||||||
96
toolkit/components/reader/Readerable.js
Normal file
96
toolkit/components/reader/Readerable.js
Normal file
|
|
@ -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);
|
||||||
10
toolkit/components/reader/Readerable.jsm
Normal file
10
toolkit/components/reader/Readerable.jsm
Normal file
|
|
@ -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
|
||||||
|
|
@ -11,6 +11,10 @@ EXTRA_JS_MODULES += [
|
||||||
'ReaderMode.jsm'
|
'ReaderMode.jsm'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
EXTRA_PP_JS_MODULES += [
|
||||||
|
'Readerable.jsm'
|
||||||
|
]
|
||||||
|
|
||||||
EXTRA_JS_MODULES.reader = [
|
EXTRA_JS_MODULES.reader = [
|
||||||
'JSDOMParser.js',
|
'JSDOMParser.js',
|
||||||
'Readability.js',
|
'Readability.js',
|
||||||
|
|
|
||||||
|
|
@ -46,3 +46,14 @@ readerView.enter=Enter Reader View
|
||||||
readerView.enter.accesskey=R
|
readerView.enter.accesskey=R
|
||||||
readerView.close=Close Reader View
|
readerView.close=Close Reader View
|
||||||
readerView.close.accesskey=R
|
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
|
||||||
|
|
|
||||||
|
|
@ -47,44 +47,10 @@ body.serif .remove-button {
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
|
--font-size: 12;
|
||||||
max-width: 30em;
|
max-width: 30em;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
font-size: var(--font-size);
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.container.content-width1 {
|
.container.content-width1 {
|
||||||
|
|
@ -738,3 +704,14 @@ body:not(.loaded) .toolbar:-moz-locale-dir(rtl) {
|
||||||
.moz-reader-content .sr-only {
|
.moz-reader-content .sr-only {
|
||||||
display: none;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue