diff options
Diffstat (limited to '')
-rw-r--r-- | browser/components/pocket/content/SaveToPocket.sys.mjs | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/browser/components/pocket/content/SaveToPocket.sys.mjs b/browser/components/pocket/content/SaveToPocket.sys.mjs new file mode 100644 index 0000000000..60674acc82 --- /dev/null +++ b/browser/components/pocket/content/SaveToPocket.sys.mjs @@ -0,0 +1,245 @@ +/* -*- 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/. */ + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + AboutReaderParent: "resource:///actors/AboutReaderParent.sys.mjs", + CustomizableUI: "resource:///modules/CustomizableUI.sys.mjs", + Pocket: "chrome://pocket/content/Pocket.sys.mjs", +}); + +var PocketCustomizableWidget = { + init() { + lazy.CustomizableUI.createWidget({ + id: "save-to-pocket-button", + l10nId: "save-to-pocket-button", + type: "view", + viewId: "PanelUI-savetopocket", + // This closes any open Pocket panels if you change location. + locationSpecific: true, + onViewShowing(aEvent) { + let panelView = aEvent.target; + let panelNode = panelView.querySelector( + ".PanelUI-savetopocket-container" + ); + let doc = panelNode.ownerDocument; + let frame = doc.createXULElement("browser"); + + frame.setAttribute("type", "content"); + frame.setAttribute("remote", "true"); + frame.setAttribute("remoteType", "privilegedabout"); + frame.setAttribute("maychangeremoteness", "true"); + frame.setAttribute("autocompletepopup", "PopupAutoComplete"); + panelNode.appendChild(frame); + + SaveToPocket.onShownInToolbarPanel(panelNode, frame); + }, + onViewHiding(aEvent) { + let panelView = aEvent.target; + let panelNode = panelView.querySelector( + ".PanelUI-savetopocket-container" + ); + + panelNode.textContent = ""; + SaveToPocket.updateToolbarNodeState(panelNode.ownerGlobal); + }, + }); + }, + shutdown() { + lazy.CustomizableUI.destroyWidget("save-to-pocket-button"); + }, +}; + +var PocketOverlay = { + startup() { + PocketCustomizableWidget.init(); + }, + shutdown() { + PocketCustomizableWidget.shutdown(); + }, +}; + +function browserWindows() { + return Services.wm.getEnumerator("navigator:browser"); +} + +export var SaveToPocket = { + init() { + // migrate enabled pref + if (Services.prefs.prefHasUserValue("browser.pocket.enabled")) { + Services.prefs.setBoolPref( + "extensions.pocket.enabled", + Services.prefs.getBoolPref("browser.pocket.enabled") + ); + Services.prefs.clearUserPref("browser.pocket.enabled"); + } + // Only define the pref getter now, so we don't get notified for the + // migrated pref above. + XPCOMUtils.defineLazyPreferenceGetter( + this, + "prefEnabled", + "extensions.pocket.enabled", + true, + this.onPrefChange.bind(this) + ); + if (this.prefEnabled) { + PocketOverlay.startup(); + } else { + // We avoid calling onPrefChange or similar here, because we don't want to + // shut down things that haven't started up, or broadcast unnecessary messages. + this.updateElements(false); + Services.obs.addObserver(this, "browser-delayed-startup-finished"); + } + lazy.AboutReaderParent.addMessageListener("Reader:OnSetup", this); + lazy.AboutReaderParent.addMessageListener( + "Reader:Clicked-pocket-button", + this + ); + }, + + observe(subject, topic, data) { + if (topic == "browser-delayed-startup-finished") { + // We only get here if pocket is disabled; the observer is removed when + // we're enabled. + this.updateElementsInWindow(subject, false); + } + }, + + _readerButtonData: { + id: "pocket-button", + l10nId: "about-reader-toolbar-savetopocket", + telemetryId: "save-to-pocket", + image: "chrome://global/skin/icons/pocket.svg", + }, + + onPrefChange(pref, oldValue, newValue) { + if (!newValue) { + lazy.AboutReaderParent.broadcastAsyncMessage("Reader:RemoveButton", { + id: "pocket-button", + }); + PocketOverlay.shutdown(); + Services.obs.addObserver(this, "browser-delayed-startup-finished"); + } else { + lazy.AboutReaderParent.broadcastAsyncMessage( + "Reader:AddButton", + this._readerButtonData + ); + Services.obs.removeObserver(this, "browser-delayed-startup-finished"); + PocketOverlay.startup(); + } + this.updateElements(newValue); + }, + + // Sets or removes the "pocketed" attribute on the Pocket urlbar button as + // necessary. + updateToolbarNodeState(browserWindow) { + const toolbarNode = browserWindow.document.getElementById( + "save-to-pocket-button" + ); + if (!toolbarNode || toolbarNode.hidden) { + return; + } + + let browser = browserWindow.gBrowser.selectedBrowser; + + let pocketedInnerWindowID = this.innerWindowIDsByBrowser.get(browser); + if (pocketedInnerWindowID == browser.innerWindowID) { + // The current window in this browser is pocketed. + toolbarNode.setAttribute("pocketed", "true"); + } else { + // The window isn't pocketed. + toolbarNode.removeAttribute("pocketed"); + } + }, + + // For pocketed inner windows, this maps their <browser>s to those inner + // window IDs. If a browser's inner window changes, then the mapped ID will + // be out of date, meaning that the new inner window has not been pocketed. + // If a browser goes away, then it'll be gone from this map too since it's + // weak. To tell whether a window has been pocketed then, look up its browser + // in this map and compare the mapped inner window ID to the ID of the current + // inner window. + get innerWindowIDsByBrowser() { + delete this.innerWindowIDsByBrowser; + return (this.innerWindowIDsByBrowser = new WeakMap()); + }, + + onLocationChange(browserWindow) { + this.updateToolbarNodeState(browserWindow); + }, + + /** + * Functions related to the Pocket panel UI. + */ + onShownInToolbarPanel(panel, frame) { + let window = panel.ownerGlobal; + window.pktUI.setToolbarPanelFrame(frame); + lazy.Pocket._initPanelView(window); + }, + + // If an item is saved to Pocket, we cache the browser's inner window ID, + // so if you navigate to that tab again, we can check the ID + // and see if we need to update the toolbar icon. + itemSaved() { + const browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); + const browser = browserWindow.gBrowser.selectedBrowser; + SaveToPocket.innerWindowIDsByBrowser.set(browser, browser.innerWindowID); + }, + + // If an item is removed from Pocket, we remove that browser's inner window ID, + // so if you navigate to that tab again, we can check the ID + // and see if we need to update the toolbar icon. + itemDeleted() { + const browserWindow = Services.wm.getMostRecentWindow("navigator:browser"); + const browser = browserWindow.gBrowser.selectedBrowser; + SaveToPocket.innerWindowIDsByBrowser.delete(browser); + }, + + updateElements(enabled) { + // loop through windows and show/hide all our elements. + for (let win of browserWindows()) { + this.updateElementsInWindow(win, enabled); + } + }, + + updateElementsInWindow(win, enabled) { + if (enabled) { + win.document.documentElement.removeAttribute("pocketdisabled"); + } else { + win.document.documentElement.setAttribute("pocketdisabled", "true"); + } + }, + + receiveMessage(message) { + if (!this.prefEnabled) { + return; + } + switch (message.name) { + case "Reader:OnSetup": { + // Tell the reader about our button. + message.target.sendMessageToActor( + "Reader:AddButton", + this._readerButtonData, + "AboutReader" + ); + break; + } + case "Reader:Clicked-pocket-button": { + let pocketPanel = message.target.ownerDocument.querySelector( + "#customizationui-widget-panel" + ); + if (pocketPanel?.getAttribute("panelopen")) { + pocketPanel.hidePopup(); + } else { + // Saves the currently viewed page. + lazy.Pocket.savePage(message.target); + } + break; + } + } + }, +}; |