Bug 1591469, move reader mode to use JSWindowActor instead of message managers, r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D71452
This commit is contained in:
Neil Deakin 2020-05-12 01:06:51 +00:00
Родитель c30d408639
Коммит f9054b8cff
23 изменённых файлов: 383 добавлений и 241 удалений

Просмотреть файл

@ -6,10 +6,6 @@
var EXPORTED_SYMBOLS = ["AboutReaderChild"];
const { ActorChild } = ChromeUtils.import(
"resource://gre/modules/ActorChild.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"AboutReader",
@ -26,25 +22,38 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/Readerable.jsm"
);
class AboutReaderChild extends ActorChild {
constructor(dispatcher) {
super(dispatcher);
class AboutReaderChild extends JSWindowActorChild {
constructor() {
super();
this._reader = null;
this._articlePromise = null;
this._isLeavingReaderableReaderMode = false;
}
willDestroy() {
this.cancelPotentialPendingReadabilityCheck();
this.readerModeHidden();
}
readerModeHidden() {
if (this._reader) {
this._reader.clearActor();
}
this._reader = null;
}
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);
this._articlePromise = ReaderMode.parseDocument(this.document).catch(
Cu.reportError
);
ReaderMode.enterReaderMode(this.docShell, this.contentWindow);
} else {
this._isLeavingReaderableReaderMode = this.isReaderableAboutReader;
ReaderMode.leaveReaderMode(this.mm.docShell, this.content);
ReaderMode.leaveReaderMode(this.docShell, this.contentWindow);
}
break;
@ -52,37 +61,40 @@ class AboutReaderChild extends ActorChild {
this.updateReaderButton(!!(message.data && message.data.isArticle));
break;
}
// Forward the message to the reader if it has been created.
if (this._reader) {
this._reader.receiveMessage(message);
}
}
get isAboutReader() {
if (!this.content) {
if (!this.document) {
return false;
}
return this.content.document.documentURI.startsWith("about:reader");
return this.document.documentURI.startsWith("about:reader");
}
get isReaderableAboutReader() {
return (
this.isAboutReader &&
!this.content.document.documentElement.dataset.isError
);
return this.isAboutReader && !this.document.documentElement.dataset.isError;
}
handleEvent(aEvent) {
if (aEvent.originalTarget.defaultView != this.content) {
if (aEvent.originalTarget.defaultView != this.contentWindow) {
return;
}
switch (aEvent.type) {
case "AboutReaderContentLoaded":
case "DOMContentLoaded":
if (!this.isAboutReader) {
this.updateReaderButton();
return;
}
if (this.content.document.body) {
if (this.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.sendAsyncMessage("Reader:UpdateReaderButton");
this._reader = new AboutReader(this, this._articlePromise);
this._articlePromise = null;
}
break;
@ -92,7 +104,7 @@ class AboutReaderChild extends ActorChild {
// 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", {
this.sendAsyncMessage("Reader:UpdateReaderButton", {
isArticle: this._isLeavingReaderableReaderMode,
});
if (this._isLeavingReaderableReaderMode) {
@ -107,9 +119,6 @@ class AboutReaderChild extends ActorChild {
this.updateReaderButton();
}
break;
case "DOMContentLoaded":
this.updateReaderButton();
break;
}
}
@ -123,9 +132,9 @@ class AboutReaderChild extends ActorChild {
if (
!Readerable.isEnabledForParseOnLoad ||
this.isAboutReader ||
!this.content ||
!(this.content.document instanceof this.content.HTMLDocument) ||
this.content.document.mozSyntheticDocument
!this.contentWindow ||
!(this.document instanceof this.contentWindow.HTMLDocument) ||
this.document.mozSyntheticDocument
) {
return;
}
@ -135,11 +144,14 @@ class AboutReaderChild extends ActorChild {
cancelPotentialPendingReadabilityCheck() {
if (this._pendingReadabilityCheck) {
this.mm.removeEventListener(
"MozAfterPaint",
this._pendingReadabilityCheck
);
if (this._listenerWindow) {
this._listenerWindow.removeEventListener(
"MozAfterPaint",
this._pendingReadabilityCheck
);
}
delete this._pendingReadabilityCheck;
delete this._listenerWindow;
}
}
@ -153,28 +165,43 @@ class AboutReaderChild extends ActorChild {
this,
forceNonArticle
);
this.mm.addEventListener("MozAfterPaint", this._pendingReadabilityCheck);
this._listenerWindow = this.contentWindow.windowRoot;
this.contentWindow.windowRoot.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.
// determine whether any of our this.contentWindow got painted by checking
// if there are any painted rects.
if (!event.clientRects.length) {
return;
}
this.cancelPotentialPendingReadabilityCheck();
// Ignore errors from actors that have been unloaded before the
// paint event timer fires.
let document;
try {
document = this.document;
} catch (ex) {
return;
}
// Only send updates when there are articles; there's no point updating with
// |false| all the time.
if (Readerable.isProbablyReaderable(this.content.document)) {
this.mm.sendAsyncMessage("Reader:UpdateReaderButton", {
if (Readerable.isProbablyReaderable(document)) {
this.sendAsyncMessage("Reader:UpdateReaderButton", {
isArticle: true,
});
} else if (forceNonArticle) {
this.mm.sendAsyncMessage("Reader:UpdateReaderButton", {
this.sendAsyncMessage("Reader:UpdateReaderButton", {
isArticle: false,
});
}

Просмотреть файл

@ -5,7 +5,7 @@
"use strict";
var EXPORTED_SYMBOLS = ["ReaderParent"];
var EXPORTED_SYMBOLS = ["AboutReaderParent"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
@ -24,61 +24,146 @@ const gStringBundle = Services.strings.createBundle(
"chrome://global/locale/aboutReader.properties"
);
var ReaderParent = {
// Listeners are added in BrowserGlue.jsm
receiveMessage(message) {
// A set of all of the AboutReaderParent actors that exist.
// See bug 1631146 for a request for a less manual way of doing this.
let gAllActors = new Set();
// A map of message names to listeners that listen to messages
// received by the AboutReaderParent actors.
let gListeners = new Map();
class AboutReaderParent extends JSWindowActorParent {
didDestroy() {
gAllActors.delete(this);
}
isReaderMode(browser) {
return browser.currentURI.spec.startsWith("about:reader");
}
static addMessageListener(name, listener) {
if (!gListeners.has(name)) {
gListeners.set(name, new Set([listener]));
} else {
gListeners.get(name).add(listener);
}
}
static removeMessageListener(name, listener) {
if (!gListeners.has(name)) {
return;
}
gListeners.get(name).delete(listener);
}
static broadcastAsyncMessage(name, data) {
for (let actor of gAllActors) {
// Ignore errors for actors that might not be valid yet or anymore.
try {
actor.sendAsyncMessage(name, data);
} catch (ex) {}
}
}
callListeners(message) {
let listeners = gListeners.get(message.name);
if (!listeners) {
return;
}
message.target = this.browsingContext.embedderElement;
for (let listener of listeners.values()) {
try {
listener.receiveMessage(message);
} catch (e) {
Cu.reportError(e);
}
}
}
async receiveMessage(message) {
switch (message.name) {
case "Reader:FaviconRequest": {
if (message.target.messageManager) {
try {
let preferredWidth = message.data.preferredWidth || 0;
let uri = Services.io.newURI(message.data.url);
try {
let preferredWidth = message.data.preferredWidth || 0;
let uri = Services.io.newURI(message.data.url);
let result = await new Promise(resolve => {
PlacesUtils.favicons.getFaviconURLForPage(
uri,
iconUri => {
if (iconUri) {
iconUri = PlacesUtils.favicons.getFaviconLinkForIcon(iconUri);
message.target.messageManager.sendAsyncMessage(
"Reader:FaviconReturn",
{
url: message.data.url,
faviconUrl: iconUri.pathQueryRef.replace(/^favicon:/, ""),
}
);
resolve({
url: message.data.url,
faviconUrl: iconUri.pathQueryRef.replace(/^favicon:/, ""),
});
} else {
resolve(null);
}
},
preferredWidth
);
} catch (ex) {
Cu.reportError(
"Error requesting favicon URL for about:reader content: " + ex
);
}
});
this.callListeners(message);
return result;
} catch (ex) {
Cu.reportError(
"Error requesting favicon URL for about:reader content: " + ex
);
}
break;
}
case "Reader:UpdateReaderButton": {
let browser = message.target;
let browser = this.browsingContext.embedderElement;
if (!browser) {
return undefined;
}
if (browser.outerBrowser) {
browser = browser.outerBrowser; // handle RDM mode
}
if (message.data && message.data.isArticle !== undefined) {
browser.isArticle = message.data.isArticle;
}
this.updateReaderButton(browser);
this.callListeners(message);
break;
}
default:
this.callListeners(message);
break;
}
},
return undefined;
}
static updateReaderButton(browser) {
let windowGlobal = browser.browsingContext.currentWindowGlobal;
let actor = windowGlobal.getActor("AboutReader");
actor.updateReaderButton(browser);
}
updateReaderButton(browser) {
let win = browser.ownerGlobal;
if (browser != win.gBrowser.selectedBrowser) {
let tabBrowser = browser.getTabBrowser();
if (!tabBrowser || browser != tabBrowser.selectedBrowser) {
return;
}
let win = browser.ownerGlobal;
let button = win.document.getElementById("reader-mode-button");
let menuitem = win.document.getElementById("menu_readerModeItem");
let key = win.document.getElementById("key_toggleReaderMode");
if (browser.currentURI.spec.startsWith("about:reader")) {
if (this.isReaderMode(browser)) {
gAllActors.add(this);
let closeText = gStringBundle.GetStringFromName("readerView.close");
button.setAttribute("readeractive", true);
@ -115,25 +200,35 @@ var ReaderParent = {
Services.obs.notifyObservers(null, "reader-mode-available");
}
}
},
}
forceShowReaderIcon(browser) {
static forceShowReaderIcon(browser) {
browser.isArticle = true;
this.updateReaderButton(browser);
},
AboutReaderParent.updateReaderButton(browser);
}
buttonClick(event) {
static buttonClick(event) {
if (event.button != 0) {
return;
}
this.toggleReaderMode(event);
},
AboutReaderParent.toggleReaderMode(event);
}
toggleReaderMode(event) {
static toggleReaderMode(event) {
let win = event.target.ownerGlobal;
let browser = win.gBrowser.selectedBrowser;
browser.messageManager.sendAsyncMessage("Reader:ToggleReaderMode");
},
if (win.gBrowser) {
let browser = win.gBrowser.selectedBrowser;
let windowGlobal = browser.browsingContext.currentWindowGlobal;
let actor = windowGlobal.getActor("AboutReader");
if (actor) {
if (actor.isReaderMode(browser)) {
gAllActors.delete(this);
}
actor.sendAsyncMessage("Reader:ToggleReaderMode", {});
}
}
}
/**
* Gets an article for a given URL. This method will download and parse a document.
@ -152,5 +247,5 @@ var ReaderParent = {
Cu.reportError("Error downloading and parsing document: " + e);
return null;
});
},
};
}
}

Просмотреть файл

@ -10,6 +10,9 @@ with Files("**"):
with Files("ContentSearch*.jsm"):
BUG_COMPONENT = ("Firefox", "Search")
with Files("AboutReaderParent.jsm"):
BUG_COMPONENT = ("Toolkit", "Reader Mode")
with Files("LightweightThemeChild.jsm"):
BUG_COMPONENT = ("WebExtensions", "Themes")
@ -34,6 +37,7 @@ FINAL_TARGET_FILES.actors += [
'AboutProtectionsChild.jsm',
'AboutProtectionsParent.jsm',
'AboutReaderChild.jsm',
'AboutReaderParent.jsm',
'AboutTabCrashedChild.jsm',
'AboutTabCrashedParent.jsm',
'BlockedSiteChild.jsm',

Просмотреть файл

@ -334,7 +334,7 @@ var FullZoom = {
async reduce() {
let browser = gBrowser.selectedBrowser;
if (browser.currentURI.spec.startsWith("about:reader")) {
browser.messageManager.sendAsyncMessage("Reader:ZoomOut");
browser.sendMessageToActor("Reader:ZoomOut", {}, "AboutReader");
} else if (this._isPDFViewer(browser)) {
browser.messageManager.sendAsyncMessage("PDFJS:ZoomOut");
} else {
@ -351,7 +351,7 @@ var FullZoom = {
async enlarge() {
let browser = gBrowser.selectedBrowser;
if (browser.currentURI.spec.startsWith("about:reader")) {
browser.messageManager.sendAsyncMessage("Reader:ZoomIn");
browser.sendMessageToActor("Reader:ZoomIn", {}, "AboutReader");
} else if (this._isPDFViewer(browser)) {
browser.messageManager.sendAsyncMessage("PDFJS:ZoomIn");
} else {
@ -371,7 +371,7 @@ var FullZoom = {
changeZoomBy(aBrowser, aValue) {
if (aBrowser.currentURI.spec.startsWith("about:reader")) {
const message = aValue > 0 ? "Reader::ZoomIn" : "Reader:ZoomOut";
aBrowser.messageManager.sendAsyncMessage(message);
aBrowser.sendMessageToActor(message, {}, "AboutReader");
return;
} else if (this._isPDFViewer(aBrowser)) {
const message = aValue > 0 ? "PDFJS::ZoomIn" : "PDFJS:ZoomOut";
@ -412,7 +412,7 @@ var FullZoom = {
reset: function FullZoom_reset(browser = gBrowser.selectedBrowser) {
let forceValue;
if (browser.currentURI.spec.startsWith("about:reader")) {
browser.messageManager.sendAsyncMessage("Reader:ResetZoom");
browser.sendMessageToActor("Reader:ResetZoom", {}, "AboutReader");
} else if (this._isPDFViewer(browser)) {
browser.messageManager.sendAsyncMessage("PDFJS:ZoomReset");
// Ensure that the UI elements of the PDF viewer won't be zoomed in/out

Просмотреть файл

@ -42,7 +42,7 @@
<command id="View:PageSource" oncommand="BrowserViewSource(window.gBrowser.selectedBrowser);"/>
<command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
<command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
<command id="View:ReaderView" oncommand="ReaderParent.toggleReaderMode(event);"/>
<command id="View:ReaderView" oncommand="AboutReaderParent.toggleReaderMode(event);"/>
<command id="View:PictureInPicture" oncommand="PictureInPicture.onCommand(event);"/>
<command id="cmd_find" oncommand="gLazyFindCommand('onFindCommand')"/>
<command id="cmd_findAgain" oncommand="gLazyFindCommand('onFindAgainCommand', false)"/>

Просмотреть файл

@ -16,6 +16,7 @@ ChromeUtils.import("resource://gre/modules/NotificationDB.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
AboutNewTab: "resource:///modules/AboutNewTab.jsm",
AboutReaderParent: "resource:///actors/AboutReaderParent.jsm",
AddonManager: "resource://gre/modules/AddonManager.jsm",
AMTelemetry: "resource://gre/modules/AddonManager.jsm",
NewTabPagePreloading: "resource:///modules/NewTabPagePreloading.jsm",
@ -60,7 +61,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
// TODO (Bug 1529552): Remove once old urlbar code goes away.
ReaderMode: "resource://gre/modules/ReaderMode.jsm",
ReaderParent: "resource:///modules/ReaderParent.jsm",
RFPHelper: "resource://gre/modules/RFPHelper.jsm",
SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
Sanitizer: "resource:///modules/Sanitizer.jsm",
@ -5283,7 +5283,7 @@ var XULBrowserWindow = {
}
Services.obs.notifyObservers(null, "touchbar-location-change", location);
UpdateBackForwardCommands(gBrowser.webNavigation);
ReaderParent.updateReaderButton(gBrowser.selectedBrowser);
AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser);
if (!gMultiProcessBrowser) {
// Bug 1108553 - Cannot rotate images with e10s
@ -5886,9 +5886,13 @@ var TabsProgressListener = {
if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
// Reader mode cares about history.pushState and friends.
// FIXME: The content process should manage this directly (bug 1445351).
aBrowser.messageManager.sendAsyncMessage("Reader:PushState", {
isArticle: aBrowser.isArticle,
});
aBrowser.sendMessageToActor(
"Reader:PushState",
{
isArticle: aBrowser.isArticle,
},
"AboutReader"
);
return;
}

Просмотреть файл

@ -1027,7 +1027,7 @@
tooltip="dynamic-shortcut-tooltip"
role="button"
hidden="true"
onclick="ReaderParent.buttonClick(event);"/>
onclick="AboutReaderParent.buttonClick(event);"/>
<toolbarbutton id="urlbar-zoom-button"
onclick="FullZoom.reset();"
tooltip="dynamic-shortcut-tooltip"

Просмотреть файл

@ -45,7 +45,6 @@ const whitelist = {
"resource:///actors/SearchTelemetryChild.jsm",
"resource:///actors/PromptChild.jsm",
"resource://gre/actors/AutoCompleteChild.jsm",
"resource://gre/modules/ActorChild.jsm",
"resource://gre/modules/ActorManagerChild.jsm",
"resource://gre/modules/E10SUtils.jsm",
"resource://gre/modules/Readerable.jsm",

Просмотреть файл

@ -157,6 +157,21 @@ let ACTORS = {
matches: ["about:protections"],
},
AboutReader: {
parent: {
moduleURI: "resource:///actors/AboutReaderParent.jsm",
},
child: {
moduleURI: "resource:///actors/AboutReaderChild.jsm",
events: {
DOMContentLoaded: {},
pageshow: { mozSystemGroup: true },
pagehide: { mozSystemGroup: true },
},
},
messageManagerGroups: ["browsers"],
},
AboutTabCrashed: {
parent: {
moduleURI: "resource:///actors/AboutTabCrashedParent.jsm",
@ -521,20 +536,6 @@ let ACTORS = {
};
let LEGACY_ACTORS = {
AboutReader: {
child: {
module: "resource:///actors/AboutReaderChild.jsm",
group: "browsers",
events: {
AboutReaderContentLoaded: { wantUntrusted: true },
DOMContentLoaded: {},
pageshow: { mozSystemGroup: true },
pagehide: { mozSystemGroup: true },
},
messages: ["Reader:ToggleReaderMode", "Reader:PushState"],
},
},
URIFixup: {
child: {
module: "resource:///actors/URIFixupChild.jsm",
@ -700,7 +701,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
AboutLoginsParent: "resource:///modules/AboutLoginsParent.jsm",
AsyncPrefs: "resource://gre/modules/AsyncPrefs.jsm",
PluginManager: "resource:///actors/PluginParent.jsm",
ReaderParent: "resource:///modules/ReaderParent.jsm",
});
/**

Просмотреть файл

@ -25,6 +25,11 @@ ChromeUtils.defineModuleGetter(
"PromiseUtils",
"resource://gre/modules/PromiseUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"AboutReaderParent",
"resource:///actors/AboutReaderParent.jsm"
);
var { ExtensionError } = ExtensionUtils;
@ -352,7 +357,7 @@ class TabTracker extends TabTrackerBase {
windowTracker.addOpenListener(this._handleWindowOpen);
windowTracker.addCloseListener(this._handleWindowClose);
Services.mm.addMessageListener("Reader:UpdateReaderButton", this);
AboutReaderParent.addMessageListener("Reader:UpdateReaderButton", this);
/* eslint-disable mozilla/balanced-listeners */
this.on("tab-detached", this._handleTabDestroyed);

Просмотреть файл

@ -1489,8 +1489,11 @@ this.tabs = class extends ExtensionAPI {
);
}
let nativeTab = getTabOrActive(tabId);
nativeTab.linkedBrowser.messageManager.sendAsyncMessage(
"Reader:ToggleReaderMode"
nativeTab.linkedBrowser.sendMessageToActor(
"Reader:ToggleReaderMode",
{},
"AboutReader"
);
},

Просмотреть файл

@ -11,6 +11,7 @@ const { XPCOMUtils } = ChromeUtils.import(
XPCOMUtils.defineLazyModuleGetters(this, {
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
EveryWindow: "resource:///modules/EveryWindow.jsm",
AboutReaderParent: "resource:///actors/AboutReaderParent.jsm",
});
const FEW_MINUTES = 15 * 60 * 1000; // 15 mins
@ -99,7 +100,7 @@ this.ASRouterTriggerListeners = new Map([
init(triggerHandler, hosts, patterns) {
if (!this._initialized) {
this.receiveMessage = this.receiveMessage.bind(this);
Services.mm.addMessageListener(this.readerModeEvent, this);
AboutReaderParent.addMessageListener(this.readerModeEvent, this);
this._triggerHandler = triggerHandler;
this._initialized = true;
}
@ -128,7 +129,7 @@ this.ASRouterTriggerListeners = new Map([
uninit() {
if (this._initialized) {
Services.mm.removeMessageListener(this.readerModeEvent, this);
AboutReaderParent.removeMessageListener(this.readerModeEvent, this);
this._initialized = false;
this._triggerHandler = null;
this._hosts = new Set();

Просмотреть файл

@ -63,7 +63,10 @@ add_task(async function check_openArticleURL() {
// Send a message from the content page (the TEST_URL) to the parent
// This should trigger the `receiveMessage` cb in the articleTrigger
await ContentTask.spawn(win.gBrowser.selectedBrowser, null, async () => {
sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
let readerActor = content.windowGlobalChild.getActor("AboutReader");
readerActor.sendAsyncMessage("Reader:UpdateReaderButton", {
isArticle: true,
});
});
await listenerTriggered.then(data =>

Просмотреть файл

@ -29,6 +29,11 @@ ChromeUtils.defineModuleGetter(
"ReaderMode",
"resource://gre/modules/ReaderMode.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"AboutReaderParent",
"resource:///actors/AboutReaderParent.jsm"
);
var EXPORTED_SYMBOLS = ["SaveToPocket"];
@ -244,8 +249,8 @@ var SaveToPocket = {
this.updateElements(false);
Services.obs.addObserver(this, "browser-delayed-startup-finished");
}
Services.mm.addMessageListener("Reader:OnSetup", this);
Services.mm.addMessageListener("Reader:Clicked-pocket-button", this);
AboutReaderParent.addMessageListener("Reader:OnSetup", this);
AboutReaderParent.addMessageListener("Reader:Clicked-pocket-button", this);
},
observe(subject, topic, data) {
@ -265,7 +270,7 @@ var SaveToPocket = {
onPrefChange(pref, oldValue, newValue) {
if (!newValue) {
Services.mm.broadcastAsyncMessage("Reader:RemoveButton", {
AboutReaderParent.broadcastAsyncMessage("Reader:RemoveButton", {
id: "pocket-button",
});
PocketOverlay.shutdown();
@ -277,7 +282,7 @@ var SaveToPocket = {
// If we don't have this, there's also no possibility of there being a reader
// mode tab already loaded. We'll get an Reader:OnSetup message when that happens.
if (this._readerButtonData.title) {
Services.mm.broadcastAsyncMessage(
AboutReaderParent.broadcastAsyncMessage(
"Reader:AddButton",
this._readerButtonData
);
@ -319,9 +324,10 @@ var SaveToPocket = {
this._readerButtonData.title = button.getAttribute("tooltiptext");
}
// Tell the reader about our button.
message.target.messageManager.sendAsyncMessage(
message.target.sendMessageToActor(
"Reader:AddButton",
this._readerButtonData
this._readerButtonData,
"AboutReader"
);
break;
}

Просмотреть файл

@ -46,8 +46,8 @@ ChromeUtils.defineModuleGetter(
);
ChromeUtils.defineModuleGetter(
this,
"ReaderParent",
"resource:///modules/ReaderParent.jsm"
"AboutReaderParent",
"resource:///actors/AboutReaderParent.jsm"
);
ChromeUtils.defineModuleGetter(
this,
@ -662,14 +662,14 @@ var UITour = {
}
case "forceShowReaderIcon": {
ReaderParent.forceShowReaderIcon(browser);
AboutReaderParent.forceShowReaderIcon(browser);
break;
}
case "toggleReaderMode": {
let targetPromise = this.getTarget(window, "readerMode-urlBar");
targetPromise.then(target => {
ReaderParent.toggleReaderMode({ target: target.node });
AboutReaderParent.toggleReaderMode({ target: target.node });
});
break;
}

Просмотреть файл

@ -79,9 +79,6 @@ with Files("PermissionUI.jsm"):
with Files("ProcessHangMonitor.jsm"):
BUG_COMPONENT = ("Core", "DOM: Content Processes")
with Files("ReaderParent.jsm"):
BUG_COMPONENT = ("Toolkit", "Reader Mode")
with Files("Sanitizer.jsm"):
BUG_COMPONENT = ("Firefox", "Preferences")
@ -145,7 +142,6 @@ EXTRA_JS_MODULES += [
'PermissionUI.jsm',
'PingCentre.jsm',
'ProcessHangMonitor.jsm',
'ReaderParent.jsm',
'Sanitizer.jsm',
'SelectionChangedMenulist.jsm',
'SiteDataManager.jsm',

Просмотреть файл

@ -21,8 +21,7 @@ var gStrings = Services.strings.createBundle(
"chrome://global/locale/narrate.properties"
);
function NarrateControls(mm, win, languagePromise) {
this._mm = mm;
function NarrateControls(win, languagePromise) {
this._winRef = Cu.getWeakReference(win);
this._languagePromise = languagePromise;

Просмотреть файл

@ -47,7 +47,8 @@ const zoomOnMeta =
const gIsFirefoxDesktop =
Services.appinfo.ID == "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
var AboutReader = function(mm, win, articlePromise) {
var AboutReader = function(actor, articlePromise) {
let win = actor.contentWindow;
let url = this._getOriginalUrl(win);
if (!(url.startsWith("http://") || url.startsWith("https://"))) {
let errorMsg =
@ -62,14 +63,7 @@ var AboutReader = function(mm, win, articlePromise) {
let doc = win.document;
this._mm = mm;
this._mm.addMessageListener("Reader:CloseDropdown", this);
this._mm.addMessageListener("Reader:AddButton", this);
this._mm.addMessageListener("Reader:RemoveButton", this);
this._mm.addMessageListener("Reader:GetStoredArticleData", this);
this._mm.addMessageListener("Reader:ZoomIn", this);
this._mm.addMessageListener("Reader:ZoomOut", this);
this._mm.addMessageListener("Reader:ResetZoom", this);
this._actor = actor;
this._docRef = Cu.getWeakReference(doc);
this._winRef = Cu.getWeakReference(win);
@ -136,7 +130,7 @@ var AboutReader = function(mm, win, articlePromise) {
if (gIsFirefoxDesktop) {
// we're ready for any external setup, send a signal for that.
this._mm.sendAsyncMessage("Reader:OnSetup");
this._actor.sendAsyncMessage("Reader:OnSetup");
}
let colorSchemeValues = JSON.parse(
@ -193,7 +187,7 @@ var AboutReader = function(mm, win, articlePromise) {
this._setupLineHeightButtons();
if (win.speechSynthesis && Services.prefs.getBoolPref("narrate.enabled")) {
new NarrateControls(mm, win, this._languagePromise);
new NarrateControls(win, this._languagePromise);
}
this._loadArticle();
@ -292,26 +286,8 @@ AboutReader.prototype = {
));
},
// Provides unique view Id.
get viewId() {
let _viewId = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator)
.generateUUID()
.toString();
Object.defineProperty(this, "viewId", { value: _viewId });
return _viewId;
},
receiveMessage(message) {
switch (message.name) {
// Triggered by Android user pressing BACK while the banner font-dropdown is open.
case "Reader:CloseDropdown": {
// Just close it.
this._closeDropdowns();
break;
}
case "Reader:AddButton": {
if (
message.data.id &&
@ -334,7 +310,7 @@ AboutReader.prototype = {
let tb = this._toolbarElement;
tb.appendChild(btn);
this._setupButton(message.data.id, button => {
this._mm.sendAsyncMessage(
this._actor.sendAsyncMessage(
"Reader:Clicked-" + button.dataset.buttonid,
{ article: this._article }
);
@ -351,12 +327,6 @@ AboutReader.prototype = {
}
break;
}
case "Reader:GetStoredArticleData": {
this._mm.sendAsyncMessage("Reader:StoredArticleData", {
article: this._article,
});
break;
}
case "Reader:ZoomIn": {
this._changeFontSize(+1);
break;
@ -405,7 +375,6 @@ AboutReader.prototype = {
}
let isScrollingUp = this._scrollOffset > vv.pageTop;
this._setSystemUIVisibility(isScrollingUp);
this._setToolbarVisibility(isScrollingUp);
}
@ -464,35 +433,18 @@ AboutReader.prototype = {
// Close the Banners Font-dropdown, cleanup Android BackPressListener.
this._closeDropdowns();
this._mm.removeMessageListener("Reader:CloseDropdown", this);
this._mm.removeMessageListener("Reader:AddButton", this);
this._mm.removeMessageListener("Reader:RemoveButton", this);
this._mm.removeMessageListener("Reader:GetStoredArticleData", this);
this._mm.removeMessageListener("Reader:ZoomIn", this);
this._mm.removeMessageListener("Reader:ZoomOut", this);
this._mm.removeMessageListener("Reader:ResetZoom", this);
this._windowUnloaded = true;
this._actor.readerModeHidden();
this.clearActor();
break;
}
},
observe(subject, topic, data) {
if (
subject.QueryInterface(Ci.nsISupportsPRUint64).data != this._innerWindowId
) {
return;
}
Services.obs.removeObserver(this, "inner-window-destroyed");
this._mm.removeMessageListener("Reader:CloseDropdown", this);
this._mm.removeMessageListener("Reader:AddButton", this);
this._mm.removeMessageListener("Reader:RemoveButton", this);
this._windowUnloaded = true;
clearActor() {
this._actor = null;
},
_onReaderClose() {
ReaderMode.leaveReaderMode(this._mm.docShell, this._win);
ReaderMode.leaveReaderMode(this._actor.docShell, this._win);
},
async _resetFontSize() {
@ -858,10 +810,6 @@ AboutReader.prototype = {
AsyncPrefs.set("reader.font_type", this._fontType);
},
_setSystemUIVisibility(visible) {
this._mm.sendAsyncMessage("Reader:SystemUIVisibility", { visible });
},
_setToolbarVisibility(visible) {
let tb = this._toolbarElement;
@ -893,7 +841,7 @@ AboutReader.prototype = {
article = await this._articlePromise;
} else {
try {
article = await this._getArticle(url);
article = await ReaderMode.downloadAndParseDocument(url);
} catch (e) {
if (e && e.newURL) {
let readerURL = "about:reader?url=" + encodeURIComponent(e.newURL);
@ -903,7 +851,7 @@ AboutReader.prototype = {
}
}
if (this._windowUnloaded) {
if (!this._actor) {
return;
}
@ -918,38 +866,15 @@ AboutReader.prototype = {
this._showContent(article);
},
_getArticle(url) {
if (this.PLATFORM_HAS_CACHE) {
return new Promise((resolve, reject) => {
let listener = message => {
this._mm.removeMessageListener("Reader:ArticleData", listener);
if (message.data.newURL) {
reject({ newURL: message.data.newURL });
return;
}
resolve(message.data.article);
};
this._mm.addMessageListener("Reader:ArticleData", listener);
this._mm.sendAsyncMessage("Reader:ArticleGet", { url });
});
}
return ReaderMode.downloadAndParseDocument(url);
},
_requestFavicon() {
let handleFaviconReturn = message => {
this._mm.removeMessageListener(
"Reader:FaviconReturn",
handleFaviconReturn
);
this._loadFavicon(message.data.url, message.data.faviconUrl);
};
this._mm.addMessageListener("Reader:FaviconReturn", handleFaviconReturn);
this._mm.sendAsyncMessage("Reader:FaviconRequest", {
async _requestFavicon() {
let iconDetails = await this._actor.sendQuery("Reader:FaviconRequest", {
url: this._article.url,
preferredWidth: 16 * this._win.devicePixelRatio,
});
if (iconDetails) {
this._loadFavicon(iconDetails.url, iconDetails.faviconUrl);
}
},
_loadFavicon(url, faviconUrl) {
@ -1134,7 +1059,7 @@ AboutReader.prototype = {
// No need to show progress if the article has been loaded,
// if the window has been unloaded, or if there was an error
// trying to load the article.
if (this._article || this._windowUnloaded || this._error) {
if (this._article || !this._actor || this._error) {
return;
}
@ -1300,7 +1225,6 @@ AboutReader.prototype = {
// Trigger BackPressListener initialization in Android.
dropdown.classList.add("open");
this._mm.sendAsyncMessage("Reader:DropdownOpened", this.viewId);
},
/*
@ -1318,11 +1242,6 @@ AboutReader.prototype = {
for (let dropdown of openDropdowns) {
dropdown.classList.remove("open");
}
// Trigger BackPressListener cleanup in Android.
if (openDropdowns.length) {
this._mm.sendAsyncMessage("Reader:DropdownClosed", this.viewId);
}
},
/*

Просмотреть файл

@ -10,7 +10,6 @@
<meta content="text/html; charset=UTF-8" http-equiv="content-type" />
<meta name="viewport" content="width=device-width; user-scalable=0" />
<link rel="stylesheet" href="chrome://global/skin/aboutReader.css" type="text/css"/>
<script src="chrome://global/content/reader/aboutReader.js"></script>
</head>
<body>

Просмотреть файл

@ -1,11 +0,0 @@
/* 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";
window.addEventListener("DOMContentLoaded", function() {
document.dispatchEvent(
new CustomEvent("AboutReaderContentLoaded", { bubbles: true })
);
});

Просмотреть файл

@ -4,4 +4,3 @@
toolkit.jar:
content/global/reader/aboutReader.html (content/aboutReader.html)
content/global/reader/aboutReader.js (content/aboutReader.js)

Просмотреть файл

@ -8,6 +8,10 @@ support-files =
[browser_readerMode_hidden_nodes.js]
support-files =
readerModeArticleHiddenNodes.html
[browser_readerMode_pocket.js]
support-files =
readerModeArticleShort.html
readerModeArticleMedium.html
[browser_readerMode_with_anchor.js]
skip-if = fission # Bug 1616805
support-files =

Просмотреть файл

@ -0,0 +1,90 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// This test verifies that the Save To Pocket button appears in reader mode,
// and is toggled hidden and visible when pocket is disabled and enabled.
const TEST_PATH = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"http://example.com"
);
async function getPocketButtonsCount(browser) {
return SpecialPowers.spawn(browser, [], () => {
return content.document.getElementsByClassName("pocket-button").length;
});
}
add_task(async function() {
// set the pocket preference before beginning.
await SpecialPowers.pushPrefEnv({
set: [["extensions.pocket.enabled", true]],
});
var readerButton = document.getElementById("reader-mode-button");
let tab1 = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
TEST_PATH + "readerModeArticleShort.html"
);
let promiseTabLoad = promiseTabLoadEvent(tab1);
readerButton.click();
await promiseTabLoad;
let tab2 = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
TEST_PATH + "readerModeArticleMedium.html"
);
promiseTabLoad = promiseTabLoadEvent(tab2);
readerButton.click();
await promiseTabLoad;
is(
await getPocketButtonsCount(tab1.linkedBrowser),
1,
"tab 1 has a pocket button"
);
is(
await getPocketButtonsCount(tab1.linkedBrowser),
1,
"tab 2 has a pocket button"
);
// Turn off the pocket preference. The Save To Pocket buttons should disappear.
await SpecialPowers.pushPrefEnv({
set: [["extensions.pocket.enabled", false]],
});
is(
await getPocketButtonsCount(tab1.linkedBrowser),
0,
"tab 1 has no pocket button"
);
is(
await getPocketButtonsCount(tab1.linkedBrowser),
0,
"tab 2 has no pocket button"
);
// Turn on the pocket preference. The Save To Pocket buttons should reappear again.
await SpecialPowers.pushPrefEnv({
set: [["extensions.pocket.enabled", true]],
});
is(
await getPocketButtonsCount(tab1.linkedBrowser),
1,
"tab 1 has a pocket button again"
);
is(
await getPocketButtonsCount(tab1.linkedBrowser),
1,
"tab 2 has a pocket button again"
);
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);
});