1 /* vim: set ts=2 sw=2 sts=2 et tw=80: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 var EXPORTED_SYMBOLS = ["AboutReaderChild"];
11 ChromeUtils.defineModuleGetter(
14 "resource://gre/modules/AboutReader.jsm"
16 ChromeUtils.defineModuleGetter(
19 "resource://gre/modules/ReaderMode.jsm"
21 ChromeUtils.defineModuleGetter(
24 "resource://gre/modules/Readerable.jsm"
27 var gUrlsToDocContentType = new Map();
28 var gUrlsToDocTitle = new Map();
30 class AboutReaderChild extends JSWindowActorChild {
35 this._articlePromise = null;
36 this._isLeavingReaderableReaderMode = false;
40 this.cancelPotentialPendingReadabilityCheck();
41 this.readerModeHidden();
46 this._reader.clearActor();
51 async receiveMessage(message) {
52 switch (message.name) {
53 case "Reader:ToggleReaderMode":
54 if (!this.isAboutReader) {
55 gUrlsToDocContentType.set(
57 this.document.contentType
59 gUrlsToDocTitle.set(this.document.URL, this.document.title);
60 this._articlePromise = lazy.ReaderMode.parseDocument(
62 ).catch(Cu.reportError);
64 // Get the article data and cache it in the parent process. The reader mode
65 // page will retrieve it when it has loaded.
66 let article = await this._articlePromise;
67 this.sendAsyncMessage("Reader:EnterReaderMode", article);
69 this.closeReaderMode();
73 case "Reader:PushState":
74 this.updateReaderButton(!!(message.data && message.data.isArticle));
76 case "Reader:EnterReaderMode": {
77 lazy.ReaderMode.enterReaderMode(this.docShell, this.contentWindow);
80 case "Reader:LeaveReaderMode": {
81 lazy.ReaderMode.leaveReaderMode(this.docShell, this.contentWindow);
86 // Forward the message to the reader if it has been created.
88 this._reader.receiveMessage(message);
96 return this.document.documentURI.startsWith("about:reader");
99 get isReaderableAboutReader() {
100 return this.isAboutReader && !this.document.documentElement.dataset.isError;
103 handleEvent(aEvent) {
104 if (aEvent.originalTarget.defaultView != this.contentWindow) {
108 switch (aEvent.type) {
109 case "DOMContentLoaded":
110 if (!this.isAboutReader) {
111 this.updateReaderButton();
115 if (this.document.body) {
116 let url = this.document.documentURI;
117 if (!this._articlePromise) {
118 url = decodeURIComponent(url.substr("about:reader?url=".length));
119 this._articlePromise = this.sendQuery("Reader:GetCachedArticle", {
123 // Update the toolbar icon to show the "reader active" icon.
124 this.sendAsyncMessage("Reader:UpdateReaderButton");
126 gUrlsToDocContentType.get(url) === "text/plain"
130 let docTitle = gUrlsToDocTitle.get(url);
131 this._reader = new lazy.AboutReader(
133 this._articlePromise,
137 this._articlePromise = null;
142 this.cancelPotentialPendingReadabilityCheck();
143 // this._isLeavingReaderableReaderMode is used here to keep the Reader Mode icon
144 // visible in the location bar when transitioning from reader-mode page
145 // back to the readable source page.
146 this.sendAsyncMessage("Reader:UpdateReaderButton", {
147 isArticle: this._isLeavingReaderableReaderMode,
149 this._isLeavingReaderableReaderMode = false;
153 // If a page is loaded from the bfcache, we won't get a "DOMContentLoaded"
154 // event, so we need to rely on "pageshow" in this case.
155 if (aEvent.persisted && this.canDoReadabilityCheck()) {
156 this.performReadabilityCheckNow();
163 * NB: this function will update the state of the reader button asynchronously
164 * after the next mozAfterPaint call (assuming reader mode is enabled and
165 * this is a suitable document). Calling it on things which won't be
166 * painted is not going to work.
168 updateReaderButton(forceNonArticle) {
169 if (!this.canDoReadabilityCheck()) {
173 this.scheduleReadabilityCheckPostPaint(forceNonArticle);
176 canDoReadabilityCheck() {
178 lazy.Readerable.isEnabledForParseOnLoad &&
179 !this.isAboutReader &&
180 this.contentWindow &&
181 this.contentWindow.windowRoot &&
182 this.contentWindow.HTMLDocument.isInstance(this.document) &&
183 !this.document.mozSyntheticDocument
187 cancelPotentialPendingReadabilityCheck() {
188 if (this._pendingReadabilityCheck) {
189 if (this._listenerWindow) {
190 this._listenerWindow.removeEventListener(
192 this._pendingReadabilityCheck
195 delete this._pendingReadabilityCheck;
196 delete this._listenerWindow;
200 scheduleReadabilityCheckPostPaint(forceNonArticle) {
201 if (this._pendingReadabilityCheck) {
202 // We need to stop this check before we re-add one because we don't know
203 // if forceNonArticle was true or false last time.
204 this.cancelPotentialPendingReadabilityCheck();
206 this._pendingReadabilityCheck = this.onPaintWhenWaitedFor.bind(
211 this._listenerWindow = this.contentWindow.windowRoot;
212 this.contentWindow.windowRoot.addEventListener(
214 this._pendingReadabilityCheck
218 onPaintWhenWaitedFor(forceNonArticle, event) {
219 // In non-e10s, we'll get called for paints other than ours, and so it's
220 // possible that this page hasn't been laid out yet, in which case we
221 // should wait until we get an event that does relate to our layout. We
222 // determine whether any of our this.contentWindow got painted by checking
223 // if there are any painted rects.
224 if (!event.clientRects.length) {
228 this.performReadabilityCheckNow(forceNonArticle);
231 performReadabilityCheckNow(forceNonArticle) {
232 this.cancelPotentialPendingReadabilityCheck();
234 // Ignore errors from actors that have been unloaded before the
235 // paint event timer fires.
238 document = this.document;
243 // Only send updates when there are articles; there's no point updating with
244 // |false| all the time.
246 lazy.Readerable.shouldCheckUri(document.baseURIObject, true) &&
247 lazy.Readerable.isProbablyReaderable(document)
249 this.sendAsyncMessage("Reader:UpdateReaderButton", {
252 } else if (forceNonArticle) {
253 this.sendAsyncMessage("Reader:UpdateReaderButton", {
260 if (this.isAboutReader) {
261 this._isLeavingReaderableReaderMode = this.isReaderableAboutReader;
262 this.sendAsyncMessage("Reader:LeaveReaderMode", {});