tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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;