IJPL-224692 [jcef] support JCEF a standalone bundle

GitOrigin-RevId: 9d42cd3b2e920e1c07f1891d2cdd39c0bdc280b7
This commit is contained in:
Vladimir Kharitonov
2026-01-15 19:20:25 +00:00
committed by intellij-monorepo-bot
parent 273a644629
commit af86838d78
9 changed files with 104 additions and 48 deletions
@@ -621,6 +621,8 @@
<extensionPoint name="cefDelegate" interface="com.intellij.ui.jcef.CefDelegate" dynamic="false"/>
<extensionPoint name="cefNativeBundleProvider" interface="com.intellij.ui.jcef.JBCefNativeBundleProvider" dynamic="false"/>
<extensionPoint name="proxySettingsOverrideProvider" interface="com.intellij.util.net.ProxySettingsOverrideProvider" dynamic="true"/>
<extensionPoint name="navbarLeftSide" dynamic="true" interface="com.intellij.ide.navigationToolbar.NavBarLeftSideExtension"/>
@@ -221,6 +221,7 @@
<applicationService serviceImplementation="com.intellij.ui.jcef.JBCefStartup" preload="notHeadless" os="mac"/>
<cachesInvalidator implementation="com.intellij.ui.jcef.JBCefAppCacheInvalidator" order="last"/>
<cefNativeBundleProvider implementation="com.intellij.ui.jcef.JBCefDefaultNativeBundleProvider" order="last"/>
<applicationService serviceInterface="com.intellij.ide.ui.IdeUiService" open="true"
serviceImplementation="com.intellij.openapi.fileEditor.impl.IdeUiServiceImpl"/>
+3
View File
@@ -4,6 +4,9 @@ a:com.intellij.ui.jcef.JBCefBrowserBase
com.intellij.ui.jcef.JBCefBrowserBase$ErrorPage
- sf:DEFAULT:com.intellij.ui.jcef.JBCefBrowserBase$ErrorPage
- a:create(org.cef.handler.CefLoadHandler$ErrorCode,java.lang.String,java.lang.String):java.lang.String
c:com.intellij.ui.jcef.JBCefDefaultNativeBundleProvider
- getNativeBundlePath():java.lang.String
- isAvailable():Z
com.intellij.ui.jcef.JBCefOSRHandlerFactory
- sf:DEFAULT:com.intellij.ui.jcef.JBCefOSRHandlerFactory
- createCefRenderHandler(javax.swing.JComponent):org.cef.handler.CefRenderHandler
+61 -44
View File
@@ -21,9 +21,7 @@ import com.intellij.ui.scale.ScaleContext;
import com.intellij.util.ArrayUtil;
import com.jetbrains.cef.JCefAppConfig;
import com.jetbrains.cef.JCefVersionDetails;
import org.cef.CefApp;
import org.cef.CefClient;
import org.cef.CefSettings;
import org.cef.*;
import org.cef.browser.CefMessageRouter;
import org.cef.browser.CefRendering;
import org.cef.callback.CefSchemeHandlerFactory;
@@ -32,12 +30,12 @@ import org.cef.handler.CefAppHandlerAdapter;
import org.cef.handler.CefRenderHandler;
import org.cef.misc.BoolRef;
import org.cef.misc.CefLog;
import org.cef.misc.Utils;
import org.jdom.IllegalDataException;
import org.jetbrains.annotations.*;
import javax.swing.*;
import java.awt.GraphicsEnvironment;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -67,7 +65,6 @@ public final class JBCefApp {
private static final Logger LOG = Logger.getInstance(JBCefApp.class);
private static final boolean SKIP_VERSION_CHECK = Boolean.getBoolean("ide.browser.jcef.skip_version_check");
private static final boolean SKIP_MODULE_CHECK = Boolean.getBoolean("ide.browser.jcef.skip_module_check");
private static final boolean IS_REMOTE_ENABLED;
private static final String REGISTRY_REMOTE_KEY = "ide.browser.jcef.out-of-process.enabled";
private static final int MIN_SUPPORTED_CEF_MAJOR_VERSION = 119;
@@ -83,6 +80,8 @@ public final class JBCefApp {
private String @Nullable [] myCefArgs;
private final @Nullable CefSettings myCefSettings;
private final @NotNull CompletableFuture<Integer> myDebuggingPort = new CompletableFuture<>();
private final boolean myIsRemoteEnabled;
private final @Nullable File myServerExe;
private final @NotNull Disposable myDisposable = new Disposable() {
@Override
@@ -126,34 +125,41 @@ public final class JBCefApp {
System.setProperty(PROPERTY_NAME, "true");
}
}
IS_REMOTE_ENABLED = CefApp.isRemoteEnabled();
if (IS_REMOTE_ENABLED) {
final Supplier<CefRendering> defaultRenderingFactory = () -> {
JBCefOSRHandlerFactory osrHandlerFactory = JBCefOSRHandlerFactory.getInstance();
JComponent component = osrHandlerFactory.createComponent(true);
CefRenderHandler handler = osrHandlerFactory.createCefRenderHandler(component);
return new CefRendering.CefRenderingWithHandler(handler, component);
};
CefApp.setDefaultRenderingFactory(defaultRenderingFactory);
}
}
private JBCefApp(@NotNull JCefAppConfig config) throws IllegalStateException {
myDelegate = getActiveDelegate();
myIsRemoteEnabled = myDelegate == null && config.isRemoteEnabled();
myServerExe = config.getServerExe();
SystemBootstrap.setLoader(config.getLoader());
if (myDelegate != null) {
myCefSettings = null;
myCefApp = null;
myDebuggingPort.completeExceptionally(new UnsupportedOperationException());
}
else {
CefApp.setIsRemoteEnabled(myIsRemoteEnabled);
if (myIsRemoteEnabled) {
final Supplier<CefRendering> defaultRenderingFactory = () -> {
JBCefOSRHandlerFactory osrHandlerFactory = JBCefOSRHandlerFactory.getInstance();
JComponent component = osrHandlerFactory.createComponent(true);
CefRenderHandler handler = osrHandlerFactory.createCefRenderHandler(component);
return new CefRendering.CefRenderingWithHandler(handler, component);
};
CefApp.setDefaultRenderingFactory(defaultRenderingFactory);
}
CefSettings settings = Cancellation.forceNonCancellableSectionInClassInitializer(() -> SettingsHelper.loadSettings(config));
final String logPath = SettingsHelper.getLogPath();
CefLog.init(logPath, settings.log_severity);
JBCefHealthMonitor.getInstance().performHealthCheckAsync(settings, () -> {
CefApp.startup(ArrayUtil.EMPTY_STRING_ARRAY);
if (OS.isMacintosh() && config.getCefFrameworkPathOSX() != null) {
CefApp.startupAsync(config.getCefFrameworkPathOSX());
} else {
CefApp.startup(ArrayUtil.EMPTY_STRING_ARRAY);
}
});
BoolRef trackGPUCrashes = new BoolRef(false);
@@ -171,7 +177,7 @@ public final class JBCefApp {
myCefArgs = args;
CefApp.addAppHandler(new MyCefAppHandler(args, trackGPUCrashes.get()));
myCefSettings = settings;
myCefApp = CefApp.getInstance(settings);
myCefApp = CefApp.getInstance(null, settings, myServerExe);
CEFAPP_INSTANCE_COUNT.intValue();
if (myCefSettings.remote_debugging_port > 0) {
@@ -188,7 +194,7 @@ public final class JBCefApp {
});
}
if (IS_REMOTE_ENABLED) {
if (myIsRemoteEnabled) {
StartupTest.checkBrowserCreation(myCefApp, () -> restartJCEF(true, true));
if (ApplicationManager.getApplication().isInternal()) {
//noinspection UnresolvedPluginConfigReference
@@ -215,7 +221,7 @@ public final class JBCefApp {
}
private boolean restartJCEF(boolean withVerboseLogging, boolean withNewCachePath) {
if (!IS_REMOTE_ENABLED) {
if (!myIsRemoteEnabled) {
return false;
}
@@ -242,7 +248,7 @@ public final class JBCefApp {
}
CefApp.addAppHandler(new MyCefAppHandler(myCefArgs, true));
final CefApp newInstance = CefApp.getInstance(myCefArgs, myCefSettings);
final CefApp newInstance = CefApp.getInstance(myCefArgs, myCefSettings, myServerExe);
if (newInstance == null) {
LOG.error("JCEF wasn't restarted (new instance is null).");
return false;
@@ -307,7 +313,12 @@ public final class JBCefApp {
if (!JreHiDpiUtil.isJreHiDPIEnabled()) {
System.setProperty("jcef.forceDeviceScaleFactor", String.valueOf(getForceDeviceScaleFactor()));
}
config = JCefAppConfig.getInstance();
String nativeBundlePath = getNativeBundlePath();
if (nativeBundlePath != null && !isJcefFromJbr()) {
config = JCefAppConfig.getInstance(nativeBundlePath);
} else {
config = JCefAppConfig.getInstance();
}
}
catch (Exception e) {
LOG.error(e);
@@ -395,28 +406,17 @@ public final class JBCefApp {
}
}
final String altFramework = Utils.getString("ALT_CEF_FRAMEWORK_DIR");
final String altPipe = Utils.getString("ALT_CEF_SERVER_PIPE");
final String altPort = Utils.getString("ALT_CEF_SERVER_PORT");
final boolean isAltCefPathUsed = (altFramework != null && !altFramework.isEmpty())
|| (altPipe != null && !altPipe.isEmpty())
|| (altPort != null && !altPort.isEmpty());
return isJcefFromJbr() || getNativeBundlePath() != null;
}
final boolean skipModuleCheck = isAltCefPathUsed || SKIP_MODULE_CHECK;
if (!skipModuleCheck) {
URL url = JCefAppConfig.class.getResource("JCefAppConfig.class");
if (url == null) {
return unsupported.apply("JCefAppConfig.class not found");
}
String path = url.toString();
String name = JCefAppConfig.class.getName().replace('.', '/');
boolean isJbrModule = path != null && path.contains("/jcef/" + name);
if (!isJbrModule) {
return unsupported.apply("JCefAppConfig.class is not from a JBR module, url: " + path);
}
private static boolean isJcefFromJbr() {
URL url = JCefAppConfig.class.getResource("JCefAppConfig.class");
if (url == null) {
LOG.error("JCefAppConfig.class not found");
return false;
}
return true;
return url.getProtocol().equals("jrt");
}
/**
@@ -638,8 +638,8 @@ public final class JBCefApp {
return JreHiDpiUtil.isJreHiDPIEnabled() ? scaledSize : ROUND.round(scaledSize / getForceDeviceScaleFactor());
}
static boolean isRemoteEnabled() {
return IS_REMOTE_ENABLED;
boolean isRemoteEnabled() {
return myIsRemoteEnabled;
}
private static int readDebugPortFile(@NotNull Path filePath) throws IOException {
@@ -666,6 +666,23 @@ public final class JBCefApp {
return CefDelegate.EP.findFirstSafe(CefDelegate::isActive);
}
private static @Nullable String getNativeBundlePath() {
// the native bundle provider is used only if there is no JCEF in JBR
if (isJcefFromJbr()) {
return null;
}
@Nullable JBCefNativeBundleProvider provider = null;
if (!isJcefFromJbr()) {
provider = JBCefNativeBundleProvider.EP.findFirstSafe(JBCefNativeBundleProvider::isAvailable);
}
if (provider == null) {
return null;
}
return provider.getNativeBundlePath();
}
private static boolean isLinuxLibcSupported() {
String libcVersionString;
try {
+1 -1
View File
@@ -900,7 +900,7 @@ public abstract class JBCefBrowserBase implements JBCefDisposable {
boolean isMouseWheelEventEnabled,
CefBrowserSettings settings) {
final String validUrl = ObjectUtils.notNull(url, "");
if (JBCefApp.isRemoteEnabled()) {
if (JBCefApp.getInstance().isRemoteEnabled()) {
Supplier<CefRendering> renderingSupplier = () -> createCefRenderingWithHandler(osrHandlerFactory, isMouseWheelEventEnabled);
CefBrowser browser = client.createBrowser(validUrl, renderingSupplier, true, context, settings);
@@ -18,7 +18,8 @@ public class JBCefBrowserBuilder {
@Nullable String myUrl;
@Nullable CefBrowser myCefBrowser;
@Nullable JBCefOSRHandlerFactory myOSRHandlerFactory;
boolean myIsOffScreenRendering = RegistryManager.getInstance().is("ide.browser.jcef.osr.enabled") || JBCefApp.isRemoteEnabled();
boolean myIsOffScreenRendering = RegistryManager.getInstance().is("ide.browser.jcef.osr.enabled") || JBCefApp.getInstance()
.isRemoteEnabled();
boolean myCreateImmediately;
boolean myEnableOpenDevToolsMenuItem;
boolean myMouseWheelEventEnable = true;
@@ -36,7 +37,7 @@ public class JBCefBrowserBuilder {
*/
public @NotNull JBCefBrowserBuilder setOffScreenRendering(boolean isOffScreenRendering) {
if (!isOffScreenRendering) {
if (JBCefApp.isRemoteEnabled()) {
if (JBCefApp.getInstance().isRemoteEnabled()) {
Logger.getInstance(JBCefBrowserBuilder.class).warn("Trying to create windowed browser when remote-mode is enabled. Settings isOffScreenRendering=false will be ignored.");
myIsOffScreenRendering = true;
return this;
@@ -0,0 +1,20 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ui.jcef;
// this extension point implementation is used for demoing and debugging JCEF from a standalone bundle
public class JBCefDefaultNativeBundleProvider implements JBCefNativeBundleProvider {
private final String path;
JBCefDefaultNativeBundleProvider() {
path = System.getProperty("jcef.native.bundle.path", null);
}
@Override
public String getNativeBundlePath() {
return path;
}
@Override
public boolean isAvailable() {
return path != null;
}
}
@@ -0,0 +1,12 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ui.jcef;
import com.intellij.openapi.extensions.ExtensionPointName;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Internal
public interface JBCefNativeBundleProvider {
ExtensionPointName<JBCefNativeBundleProvider> EP = ExtensionPointName.create("com.intellij.cefNativeBundleProvider");
String getNativeBundlePath();
boolean isAvailable();
}
@@ -43,7 +43,7 @@ public interface JBCefOSRHandlerFactory {
assert component instanceof JBCefOsrComponent;
JBCefOsrComponent osrComponent = (JBCefOsrComponent)component;
var screenBoundsProvider = createScreenBoundsProvider();
JBCefOsrHandler handler = JBCefApp.isRemoteEnabled() ?
JBCefOsrHandler handler = JBCefApp.getInstance().isRemoteEnabled() ?
new JBCefNativeOsrHandler(osrComponent, screenBoundsProvider) :
new JBCefOsrHandler(osrComponent, screenBoundsProvider);
osrComponent.setRenderHandler(handler);