Chrome 扩展程序
本页面将介绍如何使用 Chrome 扩展程序。
JxBrowser 提供了 Extensions API,允许您安装、更新和卸载 Chrome 扩展程序。这些扩展程序是基于每个 Profile 独立工作的,不会与其他 Profile 共享。要访问特定 Profile 的扩展程序,请使用以下方法:
var extensions = profile.extensions();
val extensions = profile.extensions()
如果您删除 Profile,则该 Profile 下所有已安装的扩展程序也会被删除。
安装扩展程序
您可以从 Chrome 应用商店手动安装 Chrome 扩展程序,或从 CRX 文件以编程方式安装。
如果您信任扩展程序的来源,我们建议您从 CRX 文件安装扩展程序。与从 Chrome 应用商店安装扩展程序相比,该方法具有以下优点:
- 您可以控制所安装扩展程序的版本。您可以使用特定的 JxBrowser 版本测试扩展程序,并确保其与 JxBrowser 使用的 Chromium 构建版本兼容。如果您从 Chrome 应用商店安装扩展程序,则安装的扩展程序的最新版本可能与您使用的 JxBrowser 版本不兼容。
- 您可以将扩展程序与您的应用程序一起部署并自动安装。因此,您不需要要求用户从 Chrome 应用商店手动安装扩展程序。
- 您可以安装在 Chrome 应用商店不可用的扩展程序。如果您开发了一个希望在 JxBrowser 中使用的扩展程序,并且不想将其发布在 Chrome 应用商店,您可以将其打包成 CRX 文件并使用它。
要从 CRX 文件安装扩展程序,请使用以下方法:
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 应用商店发布扩展程序时获得的。在 JxBrowser 中,我们允许用户安装没有发布者证明的扩展程序,因此请谨慎选择获取 CRX 文件的资源。
出于安全原因,默认情况下禁用从 Chrome 应用商店安装扩展程序。如果您希望允许用户从 Chrome 应用商店安装扩展程序,则需要使用以下方法允许安装:
extensions.set(InstallExtensionCallback.class, (params, tell) -> tell.install());
extensions.register(InstallExtensionCallback { params, tell ->
tell.install()
})
每次用户点击 Chrome 应用商店页面上的 “添加到 Chrome” 按钮时,InstallExtensionCallback
调都会被调用。您可以从 params
对象中获取用户想要安装的扩展程序的信息,并决定是否允许安装:
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
事件:
extensions.on(ExtensionInstalled.class, event -> {
var installedExtension = event.extension();
});
extensions.subscribe<ExtensionInstalled> { event ->
val installedExtension = event.extension()
}
更新扩展程序
如果您从 CRX 文件安装了一个扩展程序,并且想要用新版本更新它,您只需要从新的 CRX 文件安装即可:
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)
您不需要删除扩展程序的先前版本。JxBrowser 会自动将扩展程序更新到最新版本。
重要:在安装没有发布者证明的扩展程序时,请确保使用相同的私钥 (.pem
文件) 进行打包。如果使用不同的私钥或根本没有使用私钥,该扩展程序将被视为新扩展程序,并会重新安装而不会进行更新。
如果您从 Chrome 应用商店安装扩展程序,则无法以编程方式更新它。用户应从 Chrome 应用商店的扩展程序页面手动更新扩展程序。
要在扩展程序更新时收到通知,请使用 ExtensionUpdated
事件:
extensions.on(ExtensionUpdated.class, event -> {
var updatedExtension = event.extension();
});
extensions.subscribe<ExtensionUpdated> { event ->
val updatedExtension = event.extension()
}
卸载扩展程序
您可以以编程方式卸载从 CRX 文件或 Chrome 应用商店安装的扩展:
extensions.uninstall(extension);
extensions.uninstall(extension)
如果扩展程序是从 Chrome 应用商店手动安装的,用户可能也希望手动卸载它。默认情况下,出于安全原因,卸载来自 Chrome 应用商店的扩展程序是禁用的。如果您希望允许用户从 Chrome 应用商店或 chrome://extensions 页面卸载扩展程序,则需要启用卸载功能:
extensions.set(UninstallExtensionCallback.class, (params, tell) -> tell.uninstall());
extensions.register(UninstallExtensionCallback { params, tell ->
tell.uninstall()
})
要在扩展程序卸载时收到通知,请使用 ExtensionUninstalled
事件:
extensions.on(ExtensionUninstalled.class, event -> {
var uninstalledExtensionId = event.extensionId();
});
extensions.subscribe<ExtensionUninstalled> { event ->
val uninstalledExtensionId = event.extensionId()
}
尝试操作已卸载的扩展程序将导致 ObjectClosedException
。
扩展程序操作
Chrome 扩展程序可能提供仅在 “extension action popups(扩展程序操作弹出窗口)” 中可用的功能。这是您点击 Chrome 工具栏中的扩展程序图标时看到的对话框。尽管 JxBrowser 不显示 Chrome 工具栏,但它提供了与扩展程序操作弹出窗口交互的 API。
要模拟点击 Chrome 工具栏中某个标签页的扩展程序图标(扩展程序操作),请执行以下代码:
extension.action(browser).ifPresent(ExtensionAction::click);
extension.action(browser).ifPresent(ExtensionAction::click)
负责选择与之交互的 Browser 的是扩展程序,而不是 Chromium。因此,扩展程序的操作可能不会针对获取它的 browser
执行。
如果您希望在应用程序中显示扩展程序图标,您可以访问扩展程序操作的属性并订阅操作更新通知
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
回调实现:
browser.set(OpenExtensionActionPopupCallback.class, (params, tell) -> {
var extensionActionPopup = params.popupBrowser();
tell.proceed();
});
browser.register(OpenExtensionActionPopupCallback { params, tell ->
val extensionActionPopup = params.popupBrowser()
tell.proceed()
})
扩展程序弹出窗口
一些扩展程序可能希望打开弹出窗口以显示网站或其他内容。默认情况下,JxBrowser 会阻止这些弹出窗口。要允许扩展程序显示弹出窗口,请注册您自己的以下回调函数实现:
extension.set(OpenExtensionPopupCallback.class, (params, tell) -> {
var extensionPopup = params.popupBrowser();
tell.proceed();
});
extension.register(OpenExtensionPopupCallback { params, tell ->
val extensionPopup = params.popupBrowser()
tell.proceed()
})
或者使用默认实现:
extension.set(OpenExtensionPopupCallback.class,
new DefaultOpenExtensionPopupCallback());
import com.teamdev.jxbrowser.view.swing.callback.DefaultOpenExtensionPopupCallback
...
extension.register(DefaultOpenExtensionPopupCallback())
请注意,“extension popups(扩展程序弹出窗口)” 和 “extension action popups(扩展程序操作弹出窗口)” 是不同的东西。
特殊性与限制
我们设计扩展程序 API 的目标是尽可能模拟 Chromium 的行为。但由于 JxBrowser 是一个嵌入式 Browser,我们无法保证在所有情况下都能实现相同的行为。因此,在处理扩展时需要注意以下特殊性和限制。
- 所有标签页都将作为新窗口打开。
chrome.tabs.query
永远不会考虑扩展程序操作弹出窗口。- 扩展程序操作弹出窗口的窗口大小是自动可调整的。窗口大小由扩展程序操作控制。
- 当 JxBrowser 在用户数据目录或 Chromium 应用程序数据中找不到本地消息传递(
chrome.runtime.connectNative
)的清单时,它会检查 Chrome 应用程序数据中是否存在。
与 Chromium 的标签页、窗口和其他 UI 元素相关存在一些限制。windows.Window
对象映射到 Browser 关联的本地窗口,而不是可能嵌入的 Java 窗口。因此,windows.Window
对象的方法和属性,如 Window.width
、Window.height
、Window.focused
等,都与该本地窗口绑定。
不支持的扩展程序 API
以下是在 JxBrowser 中不支持的 Chromium Extension APIs(扩展程序 API)列表:
chrome.tabs.discard
chrome.tabs.remove
chrome.tabs.duplicate
chrome.windows.remove
chrome.windows.update
chrome.window.create
参数中包含超过一个 URLchrome.sessions.restore
如果扩展程序调用了一个不支持的返回 Promise
的方法,该承诺会被拒绝并返回错误。如果一个方法接受回调函数,chrome.runtime.lastError
属性将被设置为一个错误。
从 Chrome 应用商店下载 CRX 文件
如果您想要从 Chrome 应用商店下载一个扩展程序的 CRX 文件,您可以注册 InstallExtensionCallback
回调函数,在这里您可以获取到即将从 Chrome 应用商店安装的扩展程序的 CRX 文件的绝对路径,并将其复制到另一个位置:
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 文件将会被复制到指定的位置。