diff --git a/ijs.tree b/ijs.tree
index e64640eaa..d49aea403 100644
--- a/ijs.tree
+++ b/ijs.tree
@@ -87,7 +87,7 @@
-
+
diff --git a/topics/intro/content_updates.md b/topics/intro/content_updates.md
index ca35ba94e..28c7ca495 100644
--- a/topics/intro/content_updates.md
+++ b/topics/intro/content_updates.md
@@ -12,13 +12,19 @@ See [GitHub Changelog](https://github.com/JetBrains/intellij-sdk-docs/commits/ma
## 2023
+### December
+{#december-23}
+
+JCEF
+: Revamp [JCEF (Java Chromium Embedded Framework)](jcef.md) page.
+
### November
{#november-23}
Minor Changes and Additions
:
- Add information about [executing actions programmatically](basic_action_system.md#executing-actions-programmatically).
-- Please see [](tools_gradle_intellij_plugin.md#attaching-sources) on how to setup 2023.2/3 IDEs for Gradle plugin projects.
+- Please see [](tools_gradle_intellij_plugin.md#attaching-sources) on how to set up 2023.2/3 IDEs for Gradle plugin projects.
### October
{#october-23}
diff --git a/topics/reference_guide/jcef.md b/topics/reference_guide/jcef.md
index 5b4a182c5..4570d36d8 100644
--- a/topics/reference_guide/jcef.md
+++ b/topics/reference_guide/jcef.md
@@ -1,65 +1,282 @@
-# JCEF — Java Chromium Embedded Framework
+# Embedded Browser (JCEF)
Embedding Chromium-based browser in IDE.
-> JCEF is available since 2020.1 as an **experimental feature**.
-> We plan to deprecate using JavaFX in 3rd party plugins and switch to JCEF in 2020.2.
-> To continue using JavaFX in 2020.2 or later, an explicit dependency on [JavaFX Runtime for Plugins](https://plugins.jetbrains.com/plugin/14250-javafx-runtime-for-plugins) must be added.
-> Please see also blog post [JavaFX and JCEF in the IntelliJ Platform](https://blog.jetbrains.com/platform/2020/07/javafx-and-jcef-in-the-intellij-platform/) for summary of plans.
->
-{style="warning"}
+> JCEF is available since 2020.1 as an experimental feature and is enabled by default since 2020.2.
-JCEF is a Java port of [CEF](https://bitbucket.org/chromiumembedded/cef/wiki/Home) framework for embedding [Chromium-based browsers](https://www.chromium.org/Home) in applications using Swing.
+JCEF (Java Chromium Embedded Framework) is a Java port of [CEF](https://bitbucket.org/chromiumembedded/cef/wiki/Home).
+It allows for embedding [Chromium-based browsers](https://www.chromium.org/Home) in Swing applications.
-Embedding of the browser component inside the IDE allows amongst others:
+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., diagrams preview, image browser, etc.)
-> Please see also [Creating IntelliJ plugin with WebView](https://medium.com/virtuslab/creating-intellij-plugin-with-webview-3b27c3f87aea) tutorial.
+It is recommended to implement UI in the default IntelliJ Platform UI framework, which is Swing.
+Consider using JCEF approach only in cases, when a plugin needs to display HTML documents or the standard approach for creating UI is insufficient.
+
+JCEF replaces JavaFX, which was used to render web content in IDEs in the past.
+
+> Using JavaFX in 3rd party plugins has been deprecated since 2020.2.
+> To continue using JavaFX in 2020.2 or later, add an explicit [dependency](https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html) on [JavaFX Runtime for Plugins](https://plugins.jetbrains.com/plugin/14250-javafx-runtime-for-plugins) (not recommended).
+>
+> See [JavaFX and JCEF in the IntelliJ Platform](https://blog.jetbrains.com/platform/2020/07/javafx-and-jcef-in-the-intellij-platform/) blog post for the details.
+>
+{style="warning"}
## Enabling JCEF
-> JCEF is available and enabled by default since 2020.2
->
-{style="note"}
+JCEF is available and enabled by default since 2020.2.
+No additional actions are required.
-Using JCEF requires using a dedicated JetBrains Runtime.
-See [installation instructions](https://youtrack.jetbrains.com/issue/IDEA-231833#focus=streamItem-27-3993099.0-0) on how to obtain and activate it in your IDE.
-Enable `ide.browser.jcef.enabled` in Registry dialog (invoke Help | Find Action... and type "Registry") and restart the IDE for changes to take effect.
+Using JCEF requires using a dedicated JetBrains Runtime and enabling JCEF in the IDE Registry.
+
+
+
+1. Go to the [JetBrains Runtime releases list](https://github.com/JetBrains/JetBrainsRuntime/releases).
+2. 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.
+3. Unpack the archive.
+4. Follow the steps described in the [IDEA Web Help](https://www.jetbrains.com/help/idea/2020.2/switching-boot-jdk.html) and choose the downloaded JBR.
+5. Invoke Help | Find Action..., type "Registry", and press enter to open the Registry dialog.
+6. Enable the `ide.browser.jcef.enabled` flag.
+7. Restart the IDE for changes to take effect.
+
+
+## Using JCEF In a Plugin
+
+The core JCEF class exposed by IntelliJ Platform API is [`JBCefApp`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefApp.java).
+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](#browser) or [client](#browser-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()`:
+```java
+if (JBCefApp.isSupported()) {
+ // use JCEF
+} else {
+ // optional fallback to an alternative browser-less solution
+}
+```
+
+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 [`JBCefBrowser`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefBrowser.java) 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`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefBrowserBuilder.java).
+Use constructors in the cases when a browser with the default [client](#browser-client) and default options is enough.
+The builder approach allows using custom clients and configuring other options.
+
+#### Adding Browser to UI
+
+[`JBCefBrowser.getComponent()`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefBrowser.java) exposes the UI component embedding the actual browser.
+The component is an instance of Swing `JComponent`, which can be added to the plugin UI:
+```java
+// assume 'JPanel myPanel' is a part of a tool window UI
+JBCefBrowser browser = new JBCefBrowser();
+myPanel.add(browser.getComponent());
+```
+
+#### Loading Documents
+
+To load a document in the browser, use one of [`JBCefBrowserBase.load*()`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefBrowserBase.java) methods.
+Methods loading documents can be called from both UI and non-UI threads.
+It is possible to set an initial URL (passed to constructor or builder) that will be loaded when browser is created and initialized.
+
+### Browser Client
+
+Browser client provides an interface for setting up [handlers](#event-handlers) related to various browser events, e.g.:
+- HTML document loaded
+- console message printed
+- browser gained focus
+
+Handlers allow reacting to these events in plugin code and change 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`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefClient.java), which is a wrapper for JCEF's [`CefClient`](https://github.com/JetBrains/jcef/blob/master/java/org/cef/CefClient.java).
+`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](#disposing-resources) automatically, following the associated browser instance disposal.
+
+For more advanced use cases, create a custom client by calling `JBCefApp.createClient()` and register required [handlers](#event-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 allows handling a wide set of events emitted by the browser.
+Example handlers:
+- [`CefLoadHandler`](https://github.com/JetBrains/jcef/blob/master/java/org/cef/handler/CefLoadHandler.java) - handles browser loading events.
+ **Example**: Implement `CefLoadHandler.onLoadEnd()` to [execute scripts](#executing-javascript) after document is loaded.
+
+- [`CefDisplayHandler`](https://github.com/JetBrains/jcef/blob/master/java/org/cef/handler/CefDisplayHandler.java) - handles events related to browser display state.
+ **Example**: Implement `CefDisplayHandler.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`](https://github.com/JetBrains/jcef/blob/master/java/org/cef/handler/CefContextMenuHandler.java) - handles context menu events.
+ **Example**: Implement `CefContextMenuHandler.onBeforeContextMenu()` to change the items of the browser context menu.
+
+- [`CefDownloadHandler`](https://github.com/JetBrains/jcef/blob/master/java/org/cef/handler/CefDownloadHandler.java) - file download events.
+ **Example**: Implement `CefDownloadHandler.onBeforeDownload()` to enable downloading files in the embedded browser.
+
+See [org.cef.handler](https://github.com/JetBrains/jcef/tree/master/java/org/cef/handler) package for all available handlers.
+
+> For each handler interface, JCEF API provides an adapter class, which can be extended to avoid implementing unused methods, e.g., [`CefLoadHandlerAdapter`](https://github.com/JetBrains/jcef/blob/master/java/org/cef/handler/CefLoadHandlerAdapter.java).
+
+Handlers should be registered with `JBCefClient.getCefClient().add*Handler()` methods.
+
+> Please note that `JBCefClient` exposes methods for adding handlers, but it is not recommended to use them.
+>
+{style="warning"}
+
+### 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.:
+
+```java
+browser.getCefBrowser().executeJavaScript(
+ "alert('Hello World!')",
+ url, lineNumber
+);
+```
+
+The above snippet will be executed in the embedded browser and will display 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](https://youtrack.jetbrains.com/issue/JBR-2046) 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`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefJSQuery.java).
+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):
+
+```java
+JBCefJSQuery openLinkQuery = JBCefJSQuery.create(browser); // 1
+openLinkQuery.addHandler((link) -> { // 2
+ if (LinkUtil.isExternal(link)) {
+ BrowserUtil.browse(link);
+ } else {
+ EditorUtil.openFileInEditor(link);
+ }
+ return null; // 3
+});
+
+browser.getCefBrowser().executeJavaScript( // 4
+ "window.openLink = function(link) {" +
+ openLinkQuery.inject("link") + // 5
+ "};",
+ browser.getCefBrowser().getURL(), 0
+);
+
+browser.getCefBrowser().executeJavaScript( // 6
+ """
+ document.addEventListener('click', function (e) {
+ const link = e.target.closest('a').href;
+ if (link) {
+ window.openLink(link);
+ }
+ });""",
+ browser.getCefBrowser().getURL(), 0
+);
+```
+
+1. Create `JBCefQuery` instance. Make sure that the passed browser instance is of type `JBCefBrowserBase` (casting may be needed).
+2. 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.
+3. Handlers can optionally return `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.
+4. Execute JavaScript, which creates a custom `openLink` function.
+5. Inject JavaScript code responsible for invoking plugin code implemented in step 2.
+ The handler added to `openLinkQuery` will be invoked on each `openLink` function call.
+
+ Note the `"link"` parameter of the `JBCefJSQuery.inject()` method.
+ It is the name of the `openLink`'s function `link` parameter.
+ This value is injected to the query function call, and can be any value that is required by handler, e.g., `"myJsObject.prop"`, `"'JavaScript string'"`, etc.
+6. Execute JavaScript, which registers a click event listener in the browser.
+ Whenever an `a` element is clicked in the browser, the listener will invoke the `openLink` function defined in step 4 with the `href` 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](plugin_content.md) or build them on the fly depending on some configuration.
+Such resources cannot be easily accessed by the browser.
+They can be made accessible by implementing proper request [handlers](#event-handlers), which make them available to the browser at predefined URLs.
+
+This approach requires implementing [`CefRequestHandler`](https://github.com/JetBrains/jcef/blob/master/java/org/cef/handler/CefRequestHandler.java), and [`CefResourceRequestHandler`](https://github.com/JetBrains/jcef/blob/master/java/org/cef/handler/CefResourceRequestHandler.java), 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`](%gh-ic%/images/src/org/intellij/images/editor/impl/jcef/JCefImageViewer.kt) and related classes for the implementation details.
+
+### Scrollbars Look and Feel
+
+Default browser scrollbars may be insufficient, e.g. 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`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefScrollbarsHelper.java) that allows to customize scrollbars in two ways:
+1. Using `JBCefScrollbarsHelper.getOverlayScrollbarStyle()`, which provides the styles adapted to the IDE scrollbars.
+2. Using [OverlayScrollbars](https://kingsora.github.io/OverlayScrollbars/) library adapted to the IDE look and feel.
+ For the details, see `getOverlayScrollbarsSourceCSS()`, `getOverlayScrollbarsSourceJS()`, and `buildScrollbarsStyle()` Javadocs.
+ It should be used when transparent scrollbars or other advanced options are required.
+
+### Disposing Resources
+
+`JBCefBrowser`, `JBCefClient`, and `JBCefJSQuery` classes implement [`JBCefDisposable`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefDisposable.java), which extends [`Disposable`](%gh-ic%/platform/util/src/com/intellij/openapi/Disposable.java).
+It means that these classes should clean up their resources according to the rules described on the [](disposers.md) page.
+
+For example, a custom `JBCefClient` with registered handlers should remove them in the `dispose()` method implementation.
+
+## Testing
+
+See [`JBCefTestHelper`](%gh-ic%/platform/platform-tests/testSrc/com/intellij/ui/jcef/JBCefTestHelper.java) and tests in that package.
+
## Debugging
The [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools/), embedded into JCEF, can be used as a debugging and profiling tool.
-It's active by default, so that a Chrome DevTools client can attach to it via the default port number (9222).
-The port number can be configured with the following registry key:
-
-```
-ide.browser.jcef.debug.port=9222
-```
+It is active by default, so that a Chrome DevTools client can attach to it via the default 9222 port.
+Default port can be changed via the registry key `ide.browser.jcef.debug.port` (go to Help | Find Action... 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 configurations with a proper port number.
+Use the [Attach to Node.js/Chrome](https://www.jetbrains.com/help/idea/run-debug-configuration-node-js-remote-debug.html) configuration with a proper port number.
-Also, JCEF provides a default Chrome DevTools front-end (similar to the one in the Chrome browser) that can be opened from the JCEF's browser component context menu via Open DevTools.
-The menu item is available in [internal mode](enabling_internal.md) only.
-Starting with version 2021.3, the registry key `ide.browser.jcef.contextMenu.devTools.enabled` must be set to `true` explicitly.
+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 Open DevTools.
+The menu item is available in the [internal mode](enabling_internal.md) only, and since version 2021.3, the registry key `ide.browser.jcef.contextMenu.devTools.enabled` must be set to `true` explicitly.
-To access the Chrome DevTools in plugin code, use the following API:
+### Accessing DevTools Programmatically
+
+To access the Chrome DevTools in the plugin code, use the following API:
```java
-JBCefBrowser browser = new JBCefBrowser(myUrl);
CefBrowser devTools = browser.getCefBrowser().getDevTools();
JBCefBrowser devToolsBrowser = JBCefBrowser.createBuilder()
.setCefBrowser(devTools)
@@ -67,117 +284,13 @@ JBCefBrowser devToolsBrowser = JBCefBrowser.createBuilder()
.build();
```
-Or in order to just open it in a separate window:
+In order to open DevTools in a separate window, call `JBCefBrowser.openDevtools()`.
-```java
-JBCefBrowser browser = new JBCefBrowser(myUrl);
-browser.openDevtools();
-```
+## JCEF Usage Examples
-## Testing
-
-See [`JBCefTestHelper`](%gh-ic%/platform/platform-tests/testSrc/com/intellij/ui/jcef/JBCefTestHelper.java) and tests in that package.
-
-## API
-
-### JBCefApp
-
-[`JBCefApp`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefApp.java) performs JCEF auto-initialization, manages its lifecycle, and provides `JBCefClient` instances.
-
-Before using JCEF, `JBCefApp.isSupported()` check must be called:
-
-```java
-if (!JBCefApp.isSupported()) {
- // Fallback to an alternative browser-less solution
- return;
-}
-// Use JCEF
-```
-
-JCEF can be unsupported when:
-- It's not available in the IDE runtime (the IDE is started with an alternative OpenJDK).
-- Its version is not compatible with the running IDE.
-
-To avoid the above problems, the IDE should be run with the bundled JetBrains Runtime (JBR) (see also [](ide_development_instance.md)).
-
-### JBCefClient
-
-[`JBCefClient`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefClient.java) is tied to every browser component explicitly or implicitly.
-It is used for adding handlers to the associated browser.
-The same instance can be shared among multiple browsers.
-It is up to the developer to use a shared or per-browser instance, depending on the handlers' logic.
-If a client was created explicitly, it should be [disposed](disposers.md) by the developer.
-Otherwise, it is disposed automatically following the associated browser instance disposal.
-
-### JBCefBrowser
-
-[`JBCefBrowser`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefBrowser.java) provides the browser UI component:
-
-```java
-JComponent getComponent();
-```
-
-It also provides the load methods (callable from non-EDT thread as well):
-
-```java
-void loadURL(String);
-void loadHTML(String);
-```
-
-For executing JS code and callbacks (see [](#jbcefjsquery)), use the wrapped `CefBrowser` instance directly:
-
-```java
-getCefBrowser().executeJavaScript(myCode, myUrl, myLine);
-```
-
-By default, `JBCefBrowser` is created with implicit `JBCefClient` (disposed automatically).
-It is possible to pass your own `JBCefClient` (disposed by the developer).
-
-For accessing the browser use `JBCefClient`:
-
-```java
-JBCefClient getJBCefClient();
-```
-
-The simplest way to add a browser component to the plugin UI:
-
-```java
-JPanel myPanel = ...;
-myPanel.add(new JBCefBrowser("https://example.com").getComponent());
-```
-
-### JBCefJSQuery
-
-[`JBCefJSQuery`](%gh-ic%/platform/platform-api/src/com/intellij/ui/jcef/JBCefJSQuery.java) provides JS query callback mechanism.
-
-There is no direct access to JS DOM from Java (like in JavaFX WebView, see also [this issue](https://youtrack.jetbrains.com/issue/JBR-2046)).
-Still, JCEF provides an asynchronous way to communicate to JS.
-
-The example below shows opening a link in an external browser, and handling it:
-
-```java
-JBCefBrowser browser = new JBCefBrowser();
-CefBrowser cefBrowser = browser.getCefBrowser();
-
-// Create a JS query instance
-JBCefJSQuery openInBrowserJsQuery =
- JBCefJSQuery.create((JBCefBrowserBase)browser);
-
-// Add a query handler
-openInBrowserJsQuery.addHandler((link) -> {
- // handle link here
- return null; // can respond back to JS with JBCefJSQuery.Response
- });
-
-// Inject the query callback into JS
-cefBrowser.executeJavaScript(
- "window.JavaPanelBridge = {" +
- "openInExternalBrowser : function(link) {" +
- openInBrowserJsQuery.inject("link") +
- "}" +
- "};",
- cefBrowser.getURL(), 0);
-
-// Dispose the query when necessary
-Disposer.dispose(openInBrowserJsQuery);
-```
+- [Markdown preview panel](%gh-ic%/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/jcef/MarkdownJCEFHtmlPanel.kt)
+- [SVG Image Viewer](%gh-ic%/images/src/org/intellij/images/editor/impl/jcef/JCefImageViewer.kt)
+- [PDF Viewer](https://github.com/FirstTimeInForever/intellij-pdf-viewer) plugin
+- [CodeStream](https://github.com/TeamCodeStream/codestream) plugin
+- [Excalidraw Integration](https://github.com/bric3/excalidraw-jetbrains-plugin) plugin
+- [Creating IntelliJ plugin with WebView](https://medium.com/virtuslab/creating-intellij-plugin-with-webview-3b27c3f87aea) blog post