List icon Contents

Chrome Extensions

This page describes how to work with Chrome extensions.

JxBrowser provides the Extensions API that allows you to install, update, and uninstall Chrome extensions. The extensions work on a per-profile basis and are not shared with other profiles. To access the extensions of a specific profile, use the following approach:

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

If you delete the profile, all installed extensions for this profile will be deleted as well.

Installing extensions

You can install Chrome extensions manually from Chrome Web Store or programmatically from CRX files.

We recommend that you install extensions from CRX files if you trust the source of the extension. Comparing to installing extensions from Chrome Web Store, it gives you the following benefits:

  1. You control the version of the extension that you install. You can test the extension with a specific JxBrowser version and make sure that it works as expected with the Chromium build that JxBrowser uses. If you install the extension from Chrome Web Store, you install the latest version of the extension that may not be compatible with the JxBrowser version you use.
  2. You can deploy the extension with your application and install it automatically. So, you don’t need to ask users to install the extension manually from Chrome Web Store.
  3. You can install extensions that are not available in Chrome Web Store. If you develop an extension you would like to use in JxBrowser and you don’t want to publish it in Chrome Web Store, you can pack it into a CRX file and use it.

To install an extension from a CRX file, use the following approach:

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)

The method returns an instance of the installed extension. All the required permissions required by the extension are automatically granted.

Important: Be very cautious about the extensions you are installing. In Chrome, to install an extension, the CRX file must have publisher proof. Publisher proof is obtained upon extension publishing at the Chrome Web Store. In JxBrowser, we allow users to install the extensions without publisher proof, so be cautious about the resources you use to obtain CRX files.

Installing extensions from Chrome Web Store is disabled by default for security reasons. If you want to let users install extensions from Chrome Web Store, you need to allow installation using the following approach:

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

Every time the user clicks the “Add to Chrome” button on the Chrome Web Store page, the InstallExtensionCallback callback will be invoked. You can get info about the extension that the user wants to install from the params object and decide whether to allow the installation or not:

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()
    }
})

To get notified when the extension is installed, use the ExtensionInstalled event:

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

Updating extensions

If you install an extension from a CRX file, and you would like to update it with a new version, you just need to install it from a new CRX file:

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)

You don’t need to delete the previous version of the extension. JxBrowser will automatically update the extension to the newest version.

Important: When you are installing an extension without publisher proof, be sure to use the same private key (.pem file) for packaging. When a different private key is used or not used at all, the extension would be considered new and would be installed again without updating.

If you install an extension from Chrome Web Store, you can’t update it programmatically. The user should update the extension manually from the extension page in Chrome Web Store.

To get notified when the extension is updated, use the ExtensionUpdated event:

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

Uninstalling extensions

You can programmatically uninstall the extension you installed from a CRX file or Chrome Web Store:

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

If the extension was installed manually from Chrome Web Store, the user might want to uninstall it manually as well. By default, uninstalling extensions from Chrome Web Store is disabled for security reasons. If you want to allow users to uninstall extensions from Chrome Web Store or the chrome://extensions page, you need to allow uninstallation:

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

To get notified when the extension is uninstalled, use the ExtensionUninstalled event:

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

An attempt to work with an uninstalled extension will result in an ObjectClosedException.

Extension action

Chrome extensions may provide features that are available only in the “extension action popup”. This is a dialog that you see when clicking on the extension icon in the Chrome toolbar. Even though JxBrowser does not display Chrome toolbar, it provides an API for interacting with the extension action popup.

To simulate the click on the extension icon (extension action) in Chrome toolbar for a specific tab, execute the following code:

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

An extension, not Chromium, is responsible for picking a browser to interact with. Therefore, the extension action may not be executed for the browser it was obtained for.

If you want to display the extension icon in your application, you can access the extension action properties and subscribe to the action update notifications:

Java
Kotlin
extension.action(browser).ifPresent(action -> {
    // Get the properties of the action.
    var icon = action.icon();
    var badge = action.badge();
    var tooltip = action.tooltip();
    var enabled = action.isEnabled();

    // Get notifications when the extension action is updated.
    action.on(ExtensionActionUpdated.class, event -> {
        var updatedAction = event.action();
    });
});
extension.action(browser).ifPresent { action ->
    // Get the properties of the action.
    val icon = action.icon()
    val badge = action.badge()
    val tooltip = action.tooltip()
    val enabled = action.isEnabled

    // Get notifications when the extension action is updated.
    action.subscribe<ExtensionActionUpdated> { event ->
        val updatedAction = event.action()
    }
}

Extension action popups

When you create a BrowserView instance, it will register the default implementations of the OpenExtensionActionPopupCallback callback that will display the extension action popup when the program simulates the click on the extension action icon.

If you want to display a custom extension action popup, register your own implementation of the OpenExtensionActionPopupCallback callback:

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()
})

Extension popups

Some extensions may want to open popup windows to show a website or some other content. By default, JxBrowser blocks such popups. To allow the extension to show popups, register your own implementation of the following callbacks:

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()
})

Or use the default implementation:

Java
Kotlin
extension.set(OpenExtensionPopupCallback.class,
        new DefaultOpenExtensionPopupCallback());
import com.teamdev.jxbrowser.view.swing.callback.DefaultOpenExtensionPopupCallback
...
extension.register(DefaultOpenExtensionPopupCallback())

Pay attention that “extension popups” and “extension action popups” are different things.

Peculiarities & limitations

We designed extension API with the goal to behave as Chromium as much as possible. But since JxBrowser is an embedded browser, we can’t ensure the same behavior for every case. Thus, there are peculiarities and limitations that you should have in mind when working with extensions.

  1. All tabs are opened as new windows.
  2. chrome.tabs.query never takes into account extension action popups.
  3. Windows of the extension action popup are auto-resizable. The size is regulated by the extension action.
  4. When JxBrowser does not find the manifest for native messaging (chrome.runtime.connectNative) in the user data directory or Chromium application data, it checks it in Chrome application data.

There are certain limitations related to Chromium tabs, windows, and other UI elements. The windows.Window is mapped to a native window associated with the browser, not the Java window it is embedded into (if any). Thus, methods and properties of the windows.Window object, such as Window.width, Window.height, Window.focused, etc., are bound to that native window.

Unsupported extensions APIs

Below is the list of Chromium Extension APIs that are not supported in JxBrowser:

  • chrome.tabs.discard
  • chrome.tabs.remove
  • chrome.tabs.duplicate
  • chrome.windows.remove
  • chrome.windows.update
  • chrome.window.create with more than one URL in parameters
  • chrome.sessions.restore
  • chrome.desktopCapture.chooseDesktopMedia.

If an extension calls an unsupported method that returns a Promise, it will be rejected with an error. If a method accepts a callback, the chrome.runtime.lastError property will be set to an error.

Downloading CRX from Chrome Web Store

If you want to download a CRX file of an extension available in Chrome Web Store, then you can register the InstallExtensionCallback callback where you can get an absolute path to the CRX file of the extension that is about to be installed from Chrome Web Store and make a copy of it to a different location:

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()
})

Now, you can load the required extension in Chrome Web Store and click the “Add to Chrome” button. The callback will be invoked, and the CRX file of the extension will be copied to the specified location.