Chrome 扩展程序

本页面介绍如何使用 Chrome 扩展程序。

Fuzio 提供了 Extensions API,可用于安装、更新和卸载 Chrome 扩展程序。扩展程序以 Profile 为单位生效,不会与其他 Profile 共享。要访问某个 Profile 的扩展程序,可使用以下方式:

Java
Kotlin
var extensions = profile.extensions();
val extensions = profile.extensions()

如果你删除该 Profile,该 Profile 中已安装的所有扩展程序也会一并删除。

安装扩展程序 

你可以从 Chrome 应用商店 手动安装 Chrome 扩展程序,也可以通过 CRX 文件以编程方式安装。

如果你信任扩展程序来源,我们建议通过 CRX 文件安装扩展程序。与从 Chrome 应用商店安装相比,它有以下优点:

  1. 你可以控制要安装的扩展程序版本。你可以在特定版本的 Fuzio 上测试扩展程序,并确保它与 Fuzio 使用的 Chromium 版本兼容。如果从 Chrome 应用商店安装,你安装的是最新版本,而该版本可能与你使用的 Fuzio 版本不兼容。
  2. 你可以将扩展程序随应用一起部署并自动安装,因此无需让用户手动从 Chrome 应用商店安装扩展程序。
  3. 你可以安装 Chrome 应用商店中不存在的扩展程序。如果你开发了一个想在 Fuzio 中使用的扩展程序,但不想发布到 Chrome 应用商店,你可以将其打包为 CRX 文件并使用它。

要从 CRX 文件安装扩展程序,请使用以下方式:

Java
Kotlin
var crx = Paths.get("path/to/extension.crx");
var extension = extensions.install(crx);
val crx = Path("path/to/extension.crx")
val extension = extensions.install(crx)

该方法会返回已安装扩展程序的实例。扩展程序所需的所有权限都会被自动授予。

重要提示:请对你要安装的扩展程序保持高度谨慎。在 Chrome 中,安装扩展程序要求 CRX 文件包含发布者证明。发布者证明需要在 Chrome 应用商店发布扩展程序后才能获得。而在 Fuzio 中,我们允许用户在没有发布者证明的情况下安装扩展程序,因此请谨慎选择获取 CRX 文件的来源。

出于安全原因,默认情况下禁止从 Chrome 应用商店安装扩展程序。如果你希望允许用户从 Chrome 应用商店安装扩展程序,需要通过以下方式放开安装

Java
Kotlin
extensions.set(InstallExtensionCallback.class, (params, tell) -> tell.install());
extensions.register(InstallExtensionCallback { params, tell ->
    tell.install()
})

每当用户在 Chrome 应用商店页面点击 “Add to Chrome(添加到 Chrome)” 按钮时,都会触发 InstallExtensionCallback 回调。你可以通过 params 对象获取用户要安装的扩展程序信息,并决定是否允许安装:

Java
Kotlin
extensions.set(InstallExtensionCallback.class, (params, tell) -> {
    var name = params.extensionName();
    var version = params.extensionVersion();
    if (name.equals("uBlock Origin") && version.equals("1.35.2")) {
        tell.install();
    } else {
        tell.cancel();
    }
});
extensions.register(InstallExtensionCallback { params, tell ->
    val name = params.extensionName()
    val version = params.extensionVersion()
    if (name == "uBlock Origin" && version == "1.35.2") {
        tell.install()
    } else {
        tell.cancel()
    }
})

要在扩展程序安装完成时收到通知,请使用 ExtensionInstalled 事件:

Java
Kotlin
extensions.on(ExtensionInstalled.class, event -> {
    var installedExtension = event.extension();
});
extensions.subscribe<ExtensionInstalled> { event ->
    val installedExtension = event.extension()
}

更新扩展程序 

如果你是通过 CRX 文件安装扩展程序,并且希望更新到新版本,只需要使用新的 CRX 文件再次安装即可:

Java
Kotlin
var updatedCrx = Paths.get("path/to/updated_extension.crx");
var extension = extensions.install(updatedCrx);
val updatedCrx = Path("path/to/updated_extension.crx")
val extension = extensions.install(updatedCrx)

你不需要删除旧版本。Fuzio 会自动将扩展程序更新到最新版本。

重要提示:当你在没有发布者证明的情况下安装扩展程序时,请确保打包时使用同一个私钥(.pem 文件)。如果使用了不同的私钥,或者完全未使用私钥,扩展程序会被视为“新的扩展”,从而不会更新原有扩展,而是再次安装一份。

如果你是从 Chrome 应用商店安装扩展程序,则无法通过编程方式更新。用户需要在 Chrome 应用商店的扩展程序页面手动更新。

要在扩展程序更新时收到通知,请使用 ExtensionUpdated 事件:

Java
Kotlin
extensions.on(ExtensionUpdated.class, event -> {
    var updatedExtension = event.extension();
});
extensions.subscribe<ExtensionUpdated> { event ->
    val updatedExtension = event.extension()
}

卸载扩展程序 

你可以以编程方式卸载通过 CRX 文件或 Chrome 应用商店安装的扩展程序:

Java
Kotlin
extensions.uninstall(extension);
extensions.uninstall(extension)

如果扩展程序是用户从 Chrome 应用商店手动安装的,用户可能也希望手动卸载。出于安全原因,默认情况下禁止从 Chrome 应用商店或 chrome://extensions 页面卸载扩展程序。如果你希望允许用户这样卸载,需要放开卸载权限:

Java
Kotlin
extensions.set(UninstallExtensionCallback.class, (params, tell) ->
        tell.uninstall()
);
extensions.register(UninstallExtensionCallback { params, tell ->
    tell.uninstall()
})

要在扩展程序卸载时收到通知,请使用 ExtensionUninstalled 事件:

Java
Kotlin
extensions.on(ExtensionUninstalled.class, event -> {
    var uninstalledExtensionId = event.extensionId();
});
extensions.subscribe<ExtensionUninstalled> { event ->
    val uninstalledExtensionId = event.extensionId()
}

尝试对已卸载的扩展程序进行操作会导致抛出 ObjectClosedException

扩展程序操作 

Chrome 扩展程序可能提供仅在“扩展程序操作弹窗(extension action popup)”中可用的功能。这是你在 Chrome 工具栏中点击扩展图标时看到的对话框。尽管 Fuzio 不显示 Chrome 工具栏,但它提供了用于与扩展程序操作弹窗交互的 API。

要模拟在 Chrome 工具栏中针对某个标签页点击扩展程序图标(扩展操作),请执行以下代码:

Java
Kotlin
extension.action(browser).ifPresent(ExtensionAction::click);
extension.action(browser).ifPresent(ExtensionAction::click)

扩展程序(而不是 Chromium)负责选择要与之交互的浏览器实例。因此,扩展操作可能不会在获取该操作时所对应的 browser 上执行。

如果你想在你的应用中显示扩展程序图标,可以访问扩展操作的属性,并订阅操作更新通知:

Java
Kotlin
extension.action(browser).ifPresent(action -> {
    // 获取该操作的属性。
    var icon = action.icon();
    var badge = action.badge();
    var tooltip = action.tooltip();
    var enabled = action.isEnabled();

    // 在扩展操作更新时获取通知。
    action.on(ExtensionActionUpdated.class, event -> {
        var updatedAction = event.action();
    });
});
extension.action(browser).ifPresent { action ->
    // 获取该操作的属性。
    val icon = action.icon()
    val badge = action.badge()
    val tooltip = action.tooltip()
    val enabled = action.isEnabled

    // 在扩展操作更新时获取通知。
    action.subscribe<ExtensionActionUpdated> { event ->
        val updatedAction = event.action()
    }
}

扩展程序操作弹窗 

当你创建 BrowserView 实例时,它会注册 OpenExtensionActionPopupCallback 回调的默认实现,用于在程序模拟点击扩展操作图标时显示扩展操作弹窗。

如果你想显示自定义的扩展操作弹窗,请注册你自己的 OpenExtensionActionPopupCallback 实现:

Java
Kotlin
browser.set(OpenExtensionActionPopupCallback.class, (params, tell) -> {
    var extensionActionPopup = params.popupBrowser();
    tell.proceed();
});
browser.register(OpenExtensionActionPopupCallback { params, tell ->
    val extensionActionPopup = params.popupBrowser()
    tell.proceed()
})

扩展程序弹出窗口 

有些扩展程序可能会打开弹出窗口来展示网页或其他内容。默认情况下,Fuzio 会拦截这些弹窗。若要允许扩展程序显示弹窗,请注册以下回调的自定义实现:

Java
Kotlin
extension.set(OpenExtensionPopupCallback.class, (params, tell) -> {
    var extensionPopup = params.popupBrowser();
    tell.proceed();
});
extension.register(OpenExtensionPopupCallback { params, tell ->
    val extensionPopup = params.popupBrowser()
    tell.proceed()
})

或者使用默认实现:

Java
Kotlin
extension.set(OpenExtensionPopupCallback.class,
        new DefaultOpenExtensionPopupCallback());
import tech.fuzio.view.swing.callback.DefaultOpenExtensionPopupCallback
...
extension.register(DefaultOpenExtensionPopupCallback())

请注意,“扩展程序弹出窗口(extension popups)”与“扩展程序操作弹窗(extension action popups)”不是一回事。

特性与限制 

我们设计扩展 API 的目标是尽可能贴近 Chromium 的行为。但由于 Fuzio 是嵌入式浏览器,无法保证所有情况下都与 Chromium 完全一致。因此在使用扩展程序时,请注意以下特性与限制。

  1. 所有标签页都会以新窗口方式打开。
  2. chrome.tabs.query 不会将扩展程序操作弹窗纳入查询范围。
  3. 扩展程序操作弹窗窗口会自动调整大小,大小由扩展程序操作控制。
  4. 当 Fuzio 在用户数据目录或 Chromium 应用数据中找不到原生消息(chrome.runtime.connectNative)的 manifest 时,会转而在 Chrome 应用数据中检查。

此外,Chromium 的标签页、窗口以及其他 UI 元素也存在一些限制。windows.Window 会映射到与浏览器关联的原生窗口,而不是它嵌入的 Java 窗口(如果有的话)。因此,windows.Window 对象的方法和属性(如 Window.widthWindow.heightWindow.focused等)都绑定到该原生窗口。

不支持的 Extension API 

以下是 Fuzio 不支持的 Chromium Extension API 列表:

  • chrome.tabs.discard
  • chrome.tabs.remove
  • chrome.tabs.duplicate
  • chrome.windows.remove
  • chrome.windows.update
  • chrome.window.create(参数中包含多个 URL 的情况)
  • chrome.sessions.restore
  • chrome.tabs.move

如果扩展程序调用了不支持且返回 Promise的方法,它将以错误被拒绝。如果方法接受回调,则 chrome.runtime.lastError 属性将被设置为错误。

从 Chrome 应用商店下载 CRX 

如果你想下载 Chrome 应用商店中某个扩展程序的 CRX 文件,那么可以注册 InstallExtensionCallback 回调,在该回调中,你可以获取即将从 Chrome 应用商店安装的扩展程序的 CRX 文件的绝对路径,并将其复制到其他位置:

Java
Kotlin
extensions.set(InstallExtensionCallback.class, (params, tell) -> {
    var name = params.extensionName();
    var version = params.extensionVersion();
    var sourceCrxFilePath = Paths.get(params.extensionCrxFile());
    var targetCrxFilePath = Paths.get(name + "-" + version + ".crx");
    try {
        Files.copy(sourceCrxFilePath, targetCrxFilePath);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    tell.cancel();
});
extensions.register(InstallExtensionCallback { params, tell ->
    val name = params.extensionName()
    val version = params.extensionVersion()
    val sourceCrxFilePath = Path(params.extensionCrxFile())
    val targetCrxFilePath = Path("$name-$version.crx")
    try {
        Files.copy(sourceCrxFilePath, targetCrxFilePath)
    } catch (e: IOException) {
        throw RuntimeException(e)
    }
    tell.cancel()
})

现在,你可以在 Chrome 应用商店加载所需的扩展程序,并点击“添加至 Chrome”按钮。回调将被调用,并且该扩展程序的 CRX 文件将被复制到指定位置。

上下文菜单项 

扩展程序可以向上下文菜单添加菜单项。当 Chromium 尝试显示上下文菜单时,你可以在 ShowContextMenuCallback 中访问它们:

Java
Kotlin
browser.set(ShowContextMenuCallback.class, (params, tell) -> {
    for (ContextMenuItem item : params.extensionMenuItems()) {
        if (item.text().equals("Send report"))  {
            tell.select(item);
        }
    }
});
browser.register(ShowContextMenuCallback {params, tell ->
    for (item in params.extensionMenuItems()) {
        if (item.text() == "Send report") {
            tell.select(item)
        }
    }
})
微信咨询

即库客服

微信公众号二维码

技术客服

微信公众号二维码