ext-pageAction.js (3886B)
1 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ 2 /* vim: set sts=2 sw=2 et tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 "use strict"; 8 9 ChromeUtils.defineESModuleGetters(this, { 10 GeckoViewWebExtension: "resource://gre/modules/GeckoViewWebExtension.sys.mjs", 11 ExtensionActionHelper: "resource://gre/modules/GeckoViewWebExtension.sys.mjs", 12 }); 13 14 const { PageActionBase } = ChromeUtils.importESModule( 15 "resource://gre/modules/ExtensionActions.sys.mjs" 16 ); 17 18 const PAGE_ACTION_PROPERTIES = [ 19 "title", 20 "icon", 21 "popup", 22 "badgeText", 23 "enabled", 24 "patternMatching", 25 ]; 26 27 class PageAction extends PageActionBase { 28 constructor(extension, clickDelegate) { 29 const tabContext = new TabContext(() => this.getContextData(null)); 30 super(tabContext, extension); 31 this.clickDelegate = clickDelegate; 32 this.helper = new ExtensionActionHelper({ 33 extension, 34 tabTracker, 35 windowTracker, 36 tabContext, 37 properties: PAGE_ACTION_PROPERTIES, 38 }); 39 } 40 41 updateOnChange(tab) { 42 const tabId = tab ? tab.id : null; 43 // The embedder only gets the override, not the full object 44 const action = tab 45 ? this.getContextData(tab) 46 : this.helper.extractProperties(this.globals); 47 this.helper.sendRequest(tabId, { 48 action, 49 type: "GeckoView:PageAction:Update", 50 }); 51 } 52 53 openPopup() { 54 const tab = tabTracker.activeTab; 55 const popupUri = this.triggerClickOrPopup(tab); 56 const actionObject = this.getContextData(tab); 57 const action = this.helper.extractProperties(actionObject); 58 this.helper.sendRequest(tab.id, { 59 action, 60 type: "GeckoView:PageAction:OpenPopup", 61 popupUri, 62 }); 63 } 64 65 triggerClickOrPopup(tab = tabTracker.activeTab) { 66 return super.triggerClickOrPopup(tab); 67 } 68 69 getTab(tabId) { 70 return this.helper.getTab(tabId); 71 } 72 73 dispatchClick() { 74 this.clickDelegate.onClick(); 75 } 76 } 77 78 this.pageAction = class extends ExtensionAPIPersistent { 79 static for(extension) { 80 return GeckoViewWebExtension.pageActions.get(extension); 81 } 82 83 async onManifestEntry() { 84 const { extension } = this; 85 const action = new PageAction(extension, this); 86 await action.loadIconData(); 87 this.action = action; 88 89 GeckoViewWebExtension.pageActions.set(extension, action); 90 91 // Notify the embedder of this action 92 action.updateOnChange(null); 93 } 94 95 onClick() { 96 this.emit("click", tabTracker.activeTab); 97 } 98 99 onShutdown() { 100 const { extension, action } = this; 101 action.onShutdown(); 102 GeckoViewWebExtension.pageActions.delete(extension); 103 } 104 105 PERSISTENT_EVENTS = { 106 onClicked({ fire }) { 107 const { extension } = this; 108 const { tabManager } = extension; 109 110 const listener = async (_event, tab) => { 111 if (fire.wakeup) { 112 await fire.wakeup(); 113 } 114 // TODO: we should double-check if the tab is already being closed by the time 115 // the background script got started and we converted the primed listener. 116 fire.async(tabManager.convert(tab)); 117 }; 118 119 this.on("click", listener); 120 return { 121 unregister: () => { 122 this.off("click", listener); 123 }, 124 convert(newFire, _extContext) { 125 fire = newFire; 126 }, 127 }; 128 }, 129 }; 130 131 getAPI(context) { 132 const { action } = this; 133 134 return { 135 pageAction: { 136 ...action.api(context), 137 138 onClicked: new EventManager({ 139 context, 140 module: "pageAction", 141 event: "onClicked", 142 inputHandling: true, 143 extensionApi: this, 144 }).api(), 145 146 openPopup() { 147 action.openPopup(); 148 }, 149 }, 150 }; 151 } 152 }; 153 154 global.pageActionFor = this.pageAction.for;