Bug 1472491: Part 5b - Add AboutReaderChild actor. r=jaws f=felipe
MozReview-Commit-ID: H2vZT2lim3L
new file mode 100644
--- /dev/null
+++ b/browser/actors/AboutReaderChild.jsm
@@ -0,0 +1,150 @@
+/* vim: set ts=2 sw=2 sts=2 et tw=80: */
+/* 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 = ["AboutReaderChild"];
+
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
+
+ChromeUtils.defineModuleGetter(this, "AboutReader",
+ "resource://gre/modules/AboutReader.jsm");
+ChromeUtils.defineModuleGetter(this, "ReaderMode",
+ "resource://gre/modules/ReaderMode.jsm");
+
+class AboutReaderChild extends ActorChild {
+ constructor(mm) {
+ super(mm);
+
+ this._articlePromise = null;
+ this._isLeavingReaderableReaderMode = false;
+ }
+
+ receiveMessage(message) {
+ switch (message.name) {
+ case "Reader:ToggleReaderMode":
+ if (!this.isAboutReader) {
+ this._articlePromise = ReaderMode.parseDocument(this.content.document).catch(Cu.reportError);
+ ReaderMode.enterReaderMode(this.mm.docShell, this.content);
+ } else {
+ this._isLeavingReaderableReaderMode = this.isReaderableAboutReader;
+ ReaderMode.leaveReaderMode(this.mm.docShell, this.content);
+ }
+ break;
+
+ case "Reader:PushState":
+ this.updateReaderButton(!!(message.data && message.data.isArticle));
+ break;
+ }
+ }
+
+ get isAboutReader() {
+ if (!this.content) {
+ return false;
+ }
+ return this.content.document.documentURI.startsWith("about:reader");
+ }
+
+ get isReaderableAboutReader() {
+ return this.isAboutReader &&
+ !this.content.document.documentElement.dataset.isError;
+ }
+
+ handleEvent(aEvent) {
+ if (aEvent.originalTarget.defaultView != this.content) {
+ return;
+ }
+
+ switch (aEvent.type) {
+ case "AboutReaderContentLoaded":
+ if (!this.isAboutReader) {
+ return;
+ }
+
+ if (this.content.document.body) {
+ // Update the toolbar icon to show the "reader active" icon.
+ this.mm.sendAsyncMessage("Reader:UpdateReaderButton");
+ new AboutReader(this.mm, this.content, this._articlePromise);
+ this._articlePromise = null;
+ }
+ break;
+
+ case "pagehide":
+ this.cancelPotentialPendingReadabilityCheck();
+ // this._isLeavingReaderableReaderMode is used here to keep the Reader Mode icon
+ // visible in the location bar when transitioning from reader-mode page
+ // back to the readable source page.
+ this.mm.sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: this._isLeavingReaderableReaderMode });
+ if (this._isLeavingReaderableReaderMode) {
+ this._isLeavingReaderableReaderMode = false;
+ }
+ break;
+
+ case "pageshow":
+ // If a page is loaded from the bfcache, we won't get a "DOMContentLoaded"
+ // event, so we need to rely on "pageshow" in this case.
+ if (aEvent.persisted) {
+ this.updateReaderButton();
+ }
+ break;
+ case "DOMContentLoaded":
+ this.updateReaderButton();
+ break;
+
+ }
+ }
+
+ /**
+ * NB: this function will update the state of the reader button asynchronously
+ * after the next mozAfterPaint call (assuming reader mode is enabled and
+ * this is a suitable document). Calling it on things which won't be
+ * painted is not going to work.
+ */
+ updateReaderButton(forceNonArticle) {
+ if (!ReaderMode.isEnabledForParseOnLoad || this.isAboutReader ||
+ !this.content || !(this.content.document instanceof this.content.HTMLDocument) ||
+ this.content.document.mozSyntheticDocument) {
+ return;
+ }
+
+ this.scheduleReadabilityCheckPostPaint(forceNonArticle);
+ }
+
+ cancelPotentialPendingReadabilityCheck() {
+ if (this._pendingReadabilityCheck) {
+ this.mm.removeEventListener("MozAfterPaint", this._pendingReadabilityCheck);
+ delete this._pendingReadabilityCheck;
+ }
+ }
+
+ scheduleReadabilityCheckPostPaint(forceNonArticle) {
+ if (this._pendingReadabilityCheck) {
+ // We need to stop this check before we re-add one because we don't know
+ // if forceNonArticle was true or false last time.
+ this.cancelPotentialPendingReadabilityCheck();
+ }
+ this._pendingReadabilityCheck = this.onPaintWhenWaitedFor.bind(this, forceNonArticle);
+ this.mm.addEventListener("MozAfterPaint", this._pendingReadabilityCheck);
+ }
+
+ onPaintWhenWaitedFor(forceNonArticle, event) {
+ // In non-e10s, we'll get called for paints other than ours, and so it's
+ // possible that this page hasn't been laid out yet, in which case we
+ // should wait until we get an event that does relate to our layout. We
+ // determine whether any of our this.content got painted by checking if there
+ // are any painted rects.
+ if (!event.clientRects.length) {
+ return;
+ }
+
+ this.cancelPotentialPendingReadabilityCheck();
+ // Only send updates when there are articles; there's no point updating with
+ // |false| all the time.
+ if (ReaderMode.isProbablyReaderable(this.content.document)) {
+ this.mm.sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
+ } else if (forceNonArticle) {
+ this.mm.sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
+ }
+ }
+}
--- a/browser/actors/moz.build
+++ b/browser/actors/moz.build
@@ -1,9 +1,10 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
FINAL_TARGET_FILES.actors += [
+ 'AboutReaderChild.jsm',
'BrowserTabChild.jsm',
]
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -9,20 +9,16 @@
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "E10SUtils",
"resource://gre/modules/E10SUtils.jsm");
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
"resource://gre/modules/BrowserUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "AboutReader",
- "resource://gre/modules/AboutReader.jsm");
-ChromeUtils.defineModuleGetter(this, "ReaderMode",
- "resource://gre/modules/ReaderMode.jsm");
ChromeUtils.defineModuleGetter(this, "PageStyleHandler",
"resource:///modules/PageStyleHandler.jsm");
ChromeUtils.import("resource://gre/modules/ActorManagerChild.jsm");
ActorManagerChild.attach(this, "browsers");
// TabChildGlobal
@@ -55,161 +51,16 @@ let themeablePagesWhitelist = new Set([
addEventListener("pageshow", function({ originalTarget }) {
if (originalTarget.defaultView == content && themeablePagesWhitelist.has(content.document.documentURI)) {
LightweightThemeChildHelper.listen(themeablePagesWhitelist);
LightweightThemeChildHelper.update(chromeOuterWindowID, content);
}
}, false, true);
-var AboutReaderListener = {
-
- _articlePromise: null,
-
- _isLeavingReaderableReaderMode: false,
-
- init() {
- addEventListener("AboutReaderContentLoaded", this, false, true);
- addEventListener("DOMContentLoaded", this, false);
- addEventListener("pageshow", this, false);
- addEventListener("pagehide", this, false);
- addMessageListener("Reader:ToggleReaderMode", this);
- addMessageListener("Reader:PushState", this);
- this.init = null;
- },
-
- receiveMessage(message) {
- switch (message.name) {
- case "Reader:ToggleReaderMode":
- if (!this.isAboutReader) {
- this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
- ReaderMode.enterReaderMode(docShell, content);
- } else {
- this._isLeavingReaderableReaderMode = this.isReaderableAboutReader;
- ReaderMode.leaveReaderMode(docShell, content);
- }
- break;
-
- case "Reader:PushState":
- this.updateReaderButton(!!(message.data && message.data.isArticle));
- break;
- }
- },
-
- get isAboutReader() {
- if (!content) {
- return false;
- }
- return content.document.documentURI.startsWith("about:reader");
- },
-
- get isReaderableAboutReader() {
- return this.isAboutReader &&
- !content.document.documentElement.dataset.isError;
- },
-
- handleEvent(aEvent) {
- if (aEvent.originalTarget.defaultView != content) {
- return;
- }
-
- switch (aEvent.type) {
- case "AboutReaderContentLoaded":
- if (!this.isAboutReader) {
- return;
- }
-
- if (content.document.body) {
- // Update the toolbar icon to show the "reader active" icon.
- sendAsyncMessage("Reader:UpdateReaderButton");
- new AboutReader(global, content, this._articlePromise);
- this._articlePromise = null;
- }
- break;
-
- case "pagehide":
- this.cancelPotentialPendingReadabilityCheck();
- // this._isLeavingReaderableReaderMode is used here to keep the Reader Mode icon
- // visible in the location bar when transitioning from reader-mode page
- // back to the readable source page.
- sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: this._isLeavingReaderableReaderMode });
- if (this._isLeavingReaderableReaderMode) {
- this._isLeavingReaderableReaderMode = false;
- }
- break;
-
- case "pageshow":
- // If a page is loaded from the bfcache, we won't get a "DOMContentLoaded"
- // event, so we need to rely on "pageshow" in this case.
- if (aEvent.persisted) {
- this.updateReaderButton();
- }
- break;
- case "DOMContentLoaded":
- this.updateReaderButton();
- break;
-
- }
- },
-
- /**
- * NB: this function will update the state of the reader button asynchronously
- * after the next mozAfterPaint call (assuming reader mode is enabled and
- * this is a suitable document). Calling it on things which won't be
- * painted is not going to work.
- */
- updateReaderButton(forceNonArticle) {
- if (!ReaderMode.isEnabledForParseOnLoad || this.isAboutReader ||
- !content || !(content.document instanceof content.HTMLDocument) ||
- content.document.mozSyntheticDocument) {
- return;
- }
-
- this.scheduleReadabilityCheckPostPaint(forceNonArticle);
- },
-
- cancelPotentialPendingReadabilityCheck() {
- if (this._pendingReadabilityCheck) {
- removeEventListener("MozAfterPaint", this._pendingReadabilityCheck);
- delete this._pendingReadabilityCheck;
- }
- },
-
- scheduleReadabilityCheckPostPaint(forceNonArticle) {
- if (this._pendingReadabilityCheck) {
- // We need to stop this check before we re-add one because we don't know
- // if forceNonArticle was true or false last time.
- this.cancelPotentialPendingReadabilityCheck();
- }
- this._pendingReadabilityCheck = this.onPaintWhenWaitedFor.bind(this, forceNonArticle);
- addEventListener("MozAfterPaint", this._pendingReadabilityCheck);
- },
-
- onPaintWhenWaitedFor(forceNonArticle, event) {
- // In non-e10s, we'll get called for paints other than ours, and so it's
- // possible that this page hasn't been laid out yet, in which case we
- // should wait until we get an event that does relate to our layout. We
- // determine whether any of our content got painted by checking if there
- // are any painted rects.
- if (!event.clientRects.length) {
- return;
- }
-
- this.cancelPotentialPendingReadabilityCheck();
- // Only send updates when there are articles; there's no point updating with
- // |false| all the time.
- if (ReaderMode.isProbablyReaderable(content.document)) {
- sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
- } else if (forceNonArticle) {
- sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
- }
- },
-};
-AboutReaderListener.init();
-
var ContentSearchMediator = {
whitelist: new Set([
"about:home",
"about:newtab",
"about:welcome",
]),
--- a/browser/base/content/test/performance/browser_startup_content.js
+++ b/browser/base/content/test/performance/browser_startup_content.js
@@ -43,16 +43,17 @@ const whitelist = {
"resource:///modules/sessionstore/ContentSessionStore.jsm",
"resource://gre/modules/sessionstore/SessionHistory.jsm",
// Forms and passwords
"resource://formautofill/FormAutofill.jsm",
"resource://formautofill/FormAutofillContent.jsm",
// Browser front-end
+ "resource:///modules/AboutReaderChild.jsm",
"resource:///modules/BrowserTabChild.jsm",
"resource:///modules/ContentLinkHandler.jsm",
"resource:///modules/ContentMetaHandler.jsm",
"resource:///modules/PageStyleHandler.jsm",
"resource://gre/modules/ActorChild.jsm",
"resource://gre/modules/ActorManagerChild.jsm",
"resource://gre/modules/BrowserUtils.jsm",
"resource://gre/modules/E10SUtils.jsm",
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -7,16 +7,33 @@ const XULNS = "http://www.mozilla.org/ke
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "ActorManagerParent",
"resource://gre/modules/ActorManagerParent.jsm");
let ACTORS = {
+ AboutReader: {
+ child: {
+ module: "resource:///actors/AboutReaderChild.jsm",
+ group: "browsers",
+ events: {
+ "AboutReaderContentLoaded": {wantUntrusted: true},
+ "DOMContentLoaded": {},
+ "pageshow": {},
+ "pagehide": {},
+ },
+ messages: [
+ "Reader:ToggleReaderMode",
+ "Reader:PushState",
+ ],
+ },
+ },
+
BrowserTab: {
child: {
module: "resource:///actors/BrowserTabChild.jsm",
group: "browsers",
events: {
"DOMWindowCreated": {once: true},
"MozAfterPaint": {once: true},