如何检查页面 JavaScript 是否安装了 Firefox WebExtension?

How to check if a Firefox WebExtension is installed or not with page JavaScript?

我已经为 Firefox 开发了一个 WebExtension,我的网站使用该扩展作为先决条件。我需要以编程方式检查扩展是否已安装,如果未安装则要求用户安装它。

我无法找到一种方法来检查我的扩展程序是否已安装在用户的浏览器中。

编者注:Firefox 中可用的方法与 available in Chrome 中的方法不同,因此此问题不重复。

开头的重要说明:如果没有扩展程序的明确帮助,页面无法查询是否安装了扩展程序。这样做是为了防止浏览器指纹识别and/or 如果安装了某些扩展,防止网站拒绝内容。

WebExtensions 在很大程度上建立在与 Chrome 扩展相同的原则之上。因此,这个问题是相关的:Check whether user has a Chrome extension installed.

但是,Chrome 中可用的一些最佳方法目前在 Firefox 中不可用:

The files will then be available using a URL like:

moz-extension://<random-UUID>/<path/to/resource>

This UUID is randomly generated for every browser instance and is not your extension's ID. This prevents websites from fingerprinting the extensions a user has installed.

因此,您有哪些选择?页面不能直接与扩展上下文(后台)对话,后台也不能直接影响页面;您需要 Content script 才能与页面内容进行交互。

页面代码和内容脚本如何通信?它们彼此隔离,除非内容脚本对其进行处理。

首先,适用于 FF 和 Chrome 的通用技巧:

  • 您可以从内容脚本 create or modify a DOM element 在页面上查找这些修改。

      // Content script
      let beacon = document.createElement("div");
      beacon.classname = browser.runtime.id;
      document.body.appendChild(beacon);
    
      // Page script
      // Make sure this runs after the extension code
      if (document.getElementsByClassName("expected-extension-id").length) {
        // Installed
      } else {
        // Not installed
      }
    
  • 您可以使用postMessage在上下文之间进行通信,尽管用作双向通道很笨重。

    这里是documentation and sample WebExtension

      // Content script code
      window.postMessage({
        direction: "from-content-script",
        message: "Message from extension"
      }, "*");
    
      // Page code
      window.addEventListener("message", function(event) {
        if (event.source == window &&
            event.data.direction &&
            event.data.direction == "from-content-script") {
          // Assume extension is now installed
        }
      });
    
  • 你可以用类似的方法use custom DOM events

还有一些有趣的特定于 Firefox 的方法:

  • 您可以 share code with the page 使用 exportFunctioncloneInto:

      // Content script
      function usefulFunction() {
        /* ... */
      }
    
      const extensionInterface = {
        usefulFunction
      }
      window.wrappedJSObject.extensionInterface = 
        cloneInto(extensionInterface, window, {cloneFunctions: true});
    
      // Page code
      if (typeof window.extensionInterface !== "undefined") {
        // Installed
        window.extensionInterface.usefulFunction();
      } else {
        // Not installed
      }