Embedded Browser (JCEF)
JCEF (Java Chromium Embedded Framework) is a Java port of CEF. It allows for embedding Chromium-based browsers in Swing applications.
Embedding of the browser component inside the IDE can be used for:
rendering HTML content
previewing generated HTML (e.g., from Markdown)
creating custom web-based components (e.g., diagram preview, image browser, etc.)
It is recommended to implement UI in the default IntelliJ Platform UI framework, which is Swing. Consider using the JCEF approach only in cases when a plugin needs to display HTML documents or the standard approach for creating UI is not enough.
JCEF replaces JavaFX, which was used to render web content in IDEs in the past.
Enabling JCEF
JCEF is available and enabled by default since 2020.2. No additional actions are required.
Using JCEF requires using a dedicated JetBrains Runtime and enabling JCEF in the IDE Registry.
Go to the JetBrains Runtime releases list.
Download "Binaries for launching IntelliJ IDEA" matching your operating system, e.g., jbr_jcef-17.0.9-osx-x64-b1087.7.tar.gz for macOS.
Unpack the archive.
Follow the steps described in the IDEA Web Help and choose the downloaded JBR.
Invoke Registry dialog.
, type "Registry", and press enter to open theEnable the
ide.browser.jcef.enabled
flag.Restart the IDE for changes to take effect.
Using JCEF In a Plugin
The core JCEF class exposed by IntelliJ Platform API is JBCefApp
. It is responsible for initializing JCEF context and managing its lifecycle.
There is no need for initializing JBCefApp
explicitly. It is done when JBCefApp.getInstance()
is called, or when browser or client objects are created.
Before using JCEF API, it is required to check whether JCEF is supported in the running IDE. It is done by calling JBCefApp.isSupported()
:
JCEF can be unsupported when:
The IDE is started with an alternative JDK that does not include JCEF.
Its version is not compatible with the running IDE.
Browser
JCEF browser is represented by the JBCefBrowser
class. It is responsible for loading and rendering requested documents in the actual Chromium-based browser.
JCEF browsers can be created either by using the JBCefBrowser
class' constructors or via JBCefBrowserBuilder
. Use constructors in the cases when a browser with the default client and default options is enough. The builder approach allows using custom clients and configuring other options.
Adding Browser to UI
JBCefBrowser.getComponent()
exposes the UI component embedding the actual browser. The component is an instance of Swing JComponent
, which can be added to the plugin UI:
Loading Documents
To load a document in the browser, use one of JBCefBrowserBase.load*()
methods. Methods loading documents can be called from both EDT and background threads. It is possible to set an initial URL (passed to constructor or builder) that will be loaded when the browser is created and initialized.
Browser Client
Browser client provides an interface for setting up handlers related to various browser events, e.g.:
HTML document loaded
console message printed
the browser gained focus
Handlers allow reacting to these events in plugin code and changing the browser's behavior. Each browser is tied to a single client, and a single client can be shared with multiple browser instances.
Browser client is represented by JBCefClient
, which is a wrapper for JCEF's CefClient
. JBCefClient
allows registering multiple handlers of the same type, which is not possible with CefClient
. To access the underlying CefClient
and its API, call JBCefClient.getCefClient()
.
Creating and Accessing Client
If a JBCefBrowser
instance is created without passing a specific client, it is tied to a default client created implicitly. Implicit clients are disposed automatically, following the associated browser instance disposal.
For more advanced use cases, create a custom client by calling JBCefApp.createClient()
and register required handlers. Custom clients must be disposed explicitly in the plugin code.
To access the client associated with a browser, call JBCefBrowser.getJBCefClient()
.
Event Handlers
JCEF API provides various event handler interfaces that allow handling a wide set of events emitted by the browser. Example handlers:
CefLoadHandler
- handles browser loading events.
Example: ImplementCefLoadHandler.onLoadEnd()
to execute scripts after a document is loaded.CefDisplayHandler
- handles events related to the browser display state.
Example: ImplementCefDisplayHandler.onAddressChange()
to load project files in the browser when a local file link is clicked, or opening an external browser if an external link is clicked.CefContextMenuHandler
- handles context menu events.
Example: ImplementCefContextMenuHandler.onBeforeContextMenu()
to change the items of the browser context menu.CefDownloadHandler
- file download events.
Example: ImplementCefDownloadHandler.onBeforeDownload()
to enable downloading files in the embedded browser.
See the org.cef.handler package for all available handlers.
Handlers should be registered with JBCefClient.getCefClient().add*Handler()
methods.
Executing JavaScript
JCEF API allows executing JavaScript code in the embedded browser from the plugin code. JavaScript can be used for manipulating DOM, creating functions required in implemented features, injecting styles, etc.
In the simplest case, JavaScript code can be executed by using JBCefBrowser.getCefBrowser().executeJavaScript()
, e.g.:
The above snippet will be executed in the embedded browser and will display an alert box with the "Hello World!" message. The url
and lineNumber
parameters are used in the error report in the browser if the script throws an error. Their purpose is to help debugging in case of errors, and they are not crucial for the script execution. It is common to pass browser.getCefBrowser().getUrl()
or null/empty string, and 0
as these parameters.
Executing Plugin Code From JavaScript
JCEF doesn't provide direct access to DOM from the plugin code (it may change in the future), and asynchronous communication with JavaScript is achieved with the callback mechanism. It allows executing plugin code from the embedded browser via JavaScript, e.g., when a button or link is clicked, a shortcut is pressed, a JavaScript function is called, etc.
JavaScript query callback is represented by JBCefJSQuery
. It is an object bound to a specific browser, and it holds a set of handlers that implement the required plugin behavior.
Consider a case which requires opening local files links in the editor and external links in an external browser. Such a requirement could be implemented as follows (each step is explained under the code snippet):
Create a
JBCefQuery
instance. Make sure that the passed browser instance is of the typeJBCefBrowserBase
(casting may be necessary).Add a handler implementing a plugin code to be executed. Example implementation opens a link in the editor or an external browser depending on whether the link is local or external.
Handlers can optionally return a
JBCefJSQuery.Response
object, which holds information about success or error occurred on the plugin code side. It can be handled in the browser if needed.Execute JavaScript, which creates a custom
openLink
function.Inject JavaScript code responsible for invoking plugin code implemented in step 2. The handler added to
openLinkQuery
will be invoked on eachopenLink
function call.Note the
"link"
parameter of theJBCefJSQuery.inject()
method. It is the name of theopenLink
function'slink
parameter. This value is injected to the query function call and can be any value required by the handler, e.g.,"myJsObject.prop"
,"'JavaScript string'"
, etc.Execute JavaScript, which registers a click event listener in the browser. Whenever an
a
element is clicked in the browser, the listener will invoke theopenLink
function defined in step 4 with thehref
value of the clicked link.
Loading Resources From Plugin Distribution
In cases when a plugin feature implements a web-based UI, the plugin may provide HTML, CSS, and JavaScript files in its distribution or build them on the fly depending on some configuration. The browser cannot easily access such resources. They can be made accessible by implementing proper request handlers, which make them available to the browser at predefined URLs.
This approach requires implementing CefRequestHandler
, and CefResourceRequestHandler
, which map resource paths to resource providers.
Serving such resources is implemented by the Image Viewer component responsible for displaying SVG files in IntelliJ Platform-based IDEs. See JCefImageViewer
and related classes for the implementation details.
Scrollbars Look and Feel
Default browser scrollbars may be not enough, for example, when they stand out of the IDE scrollbars look, or specific look and behavior is required.
In JCEF browsers, scrollbars look and feel can be customized by CSS and JavaScript. IntelliJ Platform provides JBCefScrollbarsHelper
that allows customizing scrollbars in two ways:
Using
JBCefScrollbarsHelper.buildScrollbarsStyle()
, which provides the styles adapted to the IDE scrollbars (recommended).Using OverlayScrollbars library adapted to the IDE look and feel. For the details, see
getOverlayScrollbarsSourceCSS()
,getOverlayScrollbarsSourceJS()
, andgetOverlayScrollbarStyle()
Javadocs. It should be used when transparent scrollbars or other advanced options are required.
Disposing Resources
JBCefBrowser
, JBCefClient
, and JBCefJSQuery
classes implement JBCefDisposable
, which extends Disposable
. It means that these classes should clean up their resources according to the rules described on the Disposer and Disposable page.
For example, a custom JBCefClient
with registered handlers should remove them in the dispose()
method implementation.
Testing
See JBCefTestHelper
and tests in that package.
Debugging
The Chrome DevTools, embedded into JCEF, can be used as a debugging and profiling tool. It is active by default, so that a Chrome DevTools client can attach to it via the default port 9222. The default port can be changed via the registry key ide.browser.jcef.debug.port
(go to and type "Registry").
JavaScript debugger in IntelliJ IDEA Ultimate can thus be used to debug JavaScript code running in the IDE via the Chrome DevTools. Use the Attach to Node.js/Chrome configuration with a proper port number.
Also, JCEF provides a default Chrome DevTools frontend (similar to the one in the Chrome browser) that can be opened from the JCEF's browser component context menu via internal mode only, and since version 2021.3, the registry key ide.browser.jcef.contextMenu.devTools.enabled
must be set to true
explicitly.
Accessing DevTools Programmatically
To access the Chrome DevTools in the plugin code, use the following API:
To open DevTools in a separate window, call JBCefBrowser.openDevtools()
.
JCEF Usage Examples
PDF Viewer plugin
CodeStream plugin
Excalidraw Integration plugin
Creating IntelliJ plugin with WebView blog post