java/org/cef/CefApp.java (541 lines of code) (raw):
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
package org.cef;
import com.jetbrains.cef.JCefAppConfig;
import com.jetbrains.cef.remote.CefServer;
import com.jetbrains.cef.remote.NativeServerManager;
import com.jetbrains.cef.remote.ThriftTransport;
import com.jetbrains.cef.remote.browser.RemoteClient;
import com.jetbrains.cef.remote.callback.RemoteSchemeHandlerFactory;
import org.cef.browser.CefRendering;
import org.cef.callback.CefSchemeHandlerFactory;
import org.cef.handler.CefAppHandler;
import org.cef.handler.CefAppHandlerAdapter;
import org.cef.handler.CefAppStateHandler;
import org.cef.misc.CefLog;
import org.cef.misc.Utils;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
/**
* Exposes static methods for managing the global CEF context.
*/
public class CefApp extends CefAppHandlerAdapter {
public class CefVersion {
public final int JCEF_COMMIT_NUMBER;
public final String JCEF_COMMIT_HASH;
public final int CEF_VERSION_MAJOR;
public final int CEF_VERSION_MINOR;
public final int CEF_VERSION_PATCH;
public final int CEF_COMMIT_NUMBER;
public final int CHROME_VERSION_MAJOR;
public final int CHROME_VERSION_MINOR;
public final int CHROME_VERSION_BUILD;
public final int CHROME_VERSION_PATCH;
protected CefVersion(int jcefCommitNo, String jcefCommitHash, int cefMajor, int cefMinor, int cefPatch,
int cefCommitNo, int chrMajor, int chrMin, int chrBuild, int chrPatch) {
JCEF_COMMIT_NUMBER = jcefCommitNo;
JCEF_COMMIT_HASH = jcefCommitHash;
CEF_VERSION_MAJOR = cefMajor;
CEF_VERSION_MINOR = cefMinor;
CEF_VERSION_PATCH = cefPatch;
CEF_COMMIT_NUMBER = cefCommitNo;
CHROME_VERSION_MAJOR = chrMajor;
CHROME_VERSION_MINOR = chrMin;
CHROME_VERSION_BUILD = chrBuild;
CHROME_VERSION_PATCH = chrPatch;
}
public String getJcefVersion() {
return CEF_VERSION_MAJOR + "." + CEF_VERSION_MINOR + "." + CEF_VERSION_PATCH + "."
+ JCEF_COMMIT_NUMBER + "." + JCEF_COMMIT_HASH;
}
public String getCefVersion() {
return CEF_VERSION_MAJOR + "." + CEF_VERSION_MINOR + "." + CEF_VERSION_PATCH;
}
public String getChromeVersion() {
return CHROME_VERSION_MAJOR + "." + CHROME_VERSION_MINOR + "." + CHROME_VERSION_BUILD
+ "." + CHROME_VERSION_PATCH;
}
@Override
public String toString() {
return "JCEF Version = " + getJcefVersion() + "\n"
+ "CEF Version = " + getCefVersion() + "\n"
+ "Chromium Version = " + getChromeVersion();
}
}
/**
* The CefAppState gives you a hint if the CefApp is already usable or not
* usable any more. See values for details.
*/
public enum CefAppState {
/**
* No CefApp instance was created yet. Call getInstance() to create a new
* one.
*/
NONE,
/**
* CefApp is new created but not initialized yet. No CefClient and no
* CefBrowser was created until now.
*/
NEW,
/**
* CefApp is in its initializing process. Please wait until initializing is
* finished.
*/
INITIALIZING,
/**
* CefApp is up and running. At least one CefClient was created and the
* message loop is running. You can use all classes and methods of JCEF now.
*/
INITIALIZED,
/**
* CefApp is in its shutdown process. All CefClients and CefBrowser
* instances will be disposed. No new CefClient or CefBrowser is allowed to
* be created. The message loop will be performed until all CefClients and
* all CefBrowsers are disposed completely.
*/
SHUTTING_DOWN,
/**
* CefApp is terminated and can't be used any more. You can shutdown the
* application safely now.
*/
TERMINATED
}
/**
* According to the singleton pattern, this attribute keeps
* one single object of this class.
*/
private static CefApp self = null;
private static CefAppHandler userAppHandler_ = null;
private static final CompletableFuture<Void> ourStartupFeature = new CompletableFuture<>();
private final CefAppHandler appHandler_;
private CefAppState state_ = CefAppState.NONE;
private Timer workTimer_ = null;
private final HashSet<CefClient> clients_ = new HashSet<CefClient>();
private CefSettings settings_ = null;
private final CefServer server_;
//
// Background initialization support
//
private volatile boolean isInitialized_ = false;
private final LinkedList<CefAppStateHandler> initializationListeners_ = new LinkedList<>();
// Constants for testing JBR-5530
private static final boolean PREINIT_ON_ANY_THREAD = Utils.getBoolean("jcef_app_preinit_any");
private static final int PREINIT_TEST_DELAY_MS = Utils.getInteger("jcef_app_preinit_test_delay_ms", 0);
private static final int INIT_TEST_DELAY_MS = Utils.getInteger("jcef_app_init_test_delay_ms", 0);
private static boolean isRemoteEnabled_;
static {
if (!isRemoteSupported()) {
isRemoteEnabled_ = Boolean.getBoolean("jcef.remote.force_enabled"); // possibility for debugging
if (isRemoteEnabled_)
System.out.println("JCEF is forced to be enabled.");
} else
isRemoteEnabled_ = !Boolean.getBoolean("jcef.remote.disabled");
}
/**
* To get an instance of this class, use the method
* getInstance() instead of this CTOR.
* <p>
* The CTOR is called by getInstance() as needed and
* loads all required JCEF libraries.
*/
private CefApp(String[] args, CefSettings settings, CefServer server) {
super(args);
appHandler_ = userAppHandler_;
userAppHandler_ = null;
if (settings != null) settings_ = settings.clone();
setState(CefAppState.NEW);
server_ = server;
if (server_ != null) {
server_.setCefApp(this);
// Perform CefServer initialization in separate thread.
new Thread(()->{
if (server_.start(appHandler_)) {
CefLog.Debug("%s: native CefServer is initialized.", this);
setState(CefAppState.INITIALIZED);
synchronized (initializationListeners_) {
isInitialized_ = true;
initializationListeners_.forEach(l -> l.stateHasChanged(CefAppState.INITIALIZED));
initializationListeners_.clear();
}
CefLog.Info("%s: connected to CefServer. JCEF version: %s", this, getVersion());
}
}, "CefInitialize-thread").start();
return;
}
ourStartupFeature.thenRunAsync(() -> {
// Perform native pre-initialization.
// This code will save global pointer to JVM instance.
// Execute on the AWT event dispatching thread to store JNI context from EDT
// NOTE: in practice it seems that this method can be called from any thread (at tests
// execute successfully)
// TODO: ensure and make all initialization steps in single bg thread.
preinit(args);
initialize();
},
new NamedThreadExecutor("CefInitialize-thread"));
}
private void preinit(String[] args) throws RuntimeException {
// Start preInit
AtomicBoolean success = new AtomicBoolean(false);
if (PREINIT_ON_ANY_THREAD)
success.set(N_PreInitialize());
else {
try {
SwingUtilities.invokeAndWait(() -> success.set(N_PreInitialize()));
} catch (InterruptedException | InvocationTargetException e) {
throw new RuntimeException("Failed to do JCEF preinit", e);
}
}
if (!success.get()) {
throw new RuntimeException("Failed to do JCEF preinit");
}
}
// Notifies (in initialization thread) listener that native context has been initialized.
// When context is already initialized then listener executes immediately.
public void onInitialization(CefAppStateHandler initListener) {
onInitialization(initListener, false);
}
public void onInitialization(CefAppStateHandler initListener, boolean first) {
synchronized (initializationListeners_) {
if (isInitialized_)
initListener.stateHasChanged(CefAppState.INITIALIZED);
else {
if (first)
initializationListeners_.addFirst(initListener);
else
initializationListeners_.addLast(initListener);
}
}
}
/**
* Assign an AppHandler to CefApp. The AppHandler can be used to evaluate
* application arguments, to register your own schemes and to hook into the
* shutdown sequence. See CefAppHandler for more details.
* <p>
* This method must be called before CefApp is initialized. CefApp will be
* initialized automatically if you call createClient() the first time.
*
* @param appHandler An instance of CefAppHandler.
* @throws IllegalStateException in case of CefApp is already initialized
*/
public static void addAppHandler(CefAppHandler appHandler) throws IllegalStateException {
if (self != null)
throw new IllegalStateException("Must be called before CefApp is initialized");
userAppHandler_ = appHandler;
}
public static void setDefaultRenderingFactory(Supplier<CefRendering> factory) {
RemoteClient.setDefaultRenderingFactory(factory);
}
/**
* Get an instance of this class.
*
* @return an instance of this class
*/
public static synchronized CefApp getInstance() {
if (self != null)
return self;
return getInstance(null, null, null);
}
@Deprecated(forRemoval = true)
public static synchronized CefApp getInstance(String[] args) {
return getInstance(args, null, null);
}
@Deprecated(forRemoval = true)
public static synchronized CefApp getInstance(CefSettings settings) {
return getInstance(null, settings, null);
}
public static synchronized CefApp getInstance(String[] args, CefSettings settings, File serverExe) {
ThriftTransport st = null;
if (isRemoteEnabled()) {
st = ThriftTransport.ourDefaultServer;
final int count = CefServer.getInstancesCount();
if (count > 0) {
if (ThriftTransport.isTcpUsed()) {
st = new ThriftTransport(ThriftTransport.findFreePort());
} else {
final String suffix = "" + System.currentTimeMillis();
st = new ThriftTransport(ThriftTransport.getServerPipe(suffix));
}
CefLog.Debug("CefApp.getInstance: found %d instances of CefServer, so change default server transport %s to %s", count, ThriftTransport.ourDefaultServer, st);
}
}
return getInstance(args, settings, st, serverExe);
}
public static synchronized CefApp getInstance(CefServer server) {
if (isRemoteEnabled()) {
CefApp result = new CefApp(server.getArgs(), server.getCefSettings(), server);
if (self == null) // Set default instance
self = result;
return result;
}
return self;
}
public static synchronized CefApp getInstance(String[] args, CefSettings settings, ThriftTransport st, File serverExe) {
if (isRemoteEnabled()) {
// 1. Get command line args (from passed arguments and userAppHandler_)
final String[] realArgs;
if (args != null && args.length > 0)
realArgs = args;
else if (userAppHandler_ instanceof CefAppHandlerAdapter) {
realArgs = ((CefAppHandlerAdapter)userAppHandler_).getArgs();
} else {
if (userAppHandler_ != null)
CefLog.Error("Unsupported class of CefAppHandler %s. Overridden command-line arguments will be ignored.", CefAppHandler.class);
realArgs = null;
}
// 2. Try to find the existing instance with the same args and settings.
CefServer s = CefServer.findRunningInstance(realArgs, settings);
if (s != null) {
CefLog.Debug("Found running CefServer instance %s for settings: %s", s.toStringDetailed(), settings.getDescription());
return s.getCefApp();
}
// 3. Create new CefApp instance.
s = new CefServer(serverExe, st, realArgs, settings);
CefApp result = new CefApp(realArgs, settings, s);
// 4. Set default instance
if (self == null)
self = result;
return result;
}
if (settings != null) {
if (self != null)
throw new IllegalStateException("Settings can only be passed to CEF"
+ " before createClient is called the first time. Current state is " + self.state_);
}
if (self == null)
self = new CefApp(args, settings, null);
else if (self.isTerminated())
throw new IllegalStateException("CefApp was terminated");
return self;
}
public static synchronized CefApp findRunningInstance(String[] args, CefSettings settings) {
if (!isRemoteEnabled())
return null;
final CefServer s = CefServer.findRunningInstance(args, settings);
return s != null ? s.getCefApp() : null;
}
public static synchronized CefApp getInstanceIfAny() {
return self;
}
public static synchronized void setDefaultInstance(CefApp cefApp) {
self = cefApp;
}
public final synchronized void setSettings(CefSettings settings) throws IllegalStateException {
if (state_.compareTo(CefAppState.NEW) > 0)
throw new IllegalStateException("Settings can only be passed to CEF"
+ " before createClient is called the first time. Current state is " + state_);
settings_ = settings.clone();
}
public void setDisconnectionCallback(Runnable disconnectionCallback) {
if (server_ != null)
server_.setDisconnectionCallback(disconnectionCallback);
else
CefLog.Error("setDisconnectionCallback is not supported for in-process CEF instance");
}
public final CefVersion getVersion() {
if (server_ != null) {
// TODO: request remaining params from server
return new CefVersion(0, "0", 0, 0, 0, 0, 0, 0, 0, 0) {
@Override
public String toString() {
return "remote_" + server_.getVersion();
}
@Override
public String getJcefVersion() {
return "remote_" + server_.getVersion();
}
};
}
try {
return N_GetVersion();
} catch (UnsatisfiedLinkError ule) {
CefLog.Error("Failed to get CEF version. %s", ule.getMessage());
}
return null;
}
public static final boolean isRemoteSupported() {
return NativeServerManager.isRemoteSupported();
}
public static void setIsRemoteEnabled(boolean value) {
isRemoteEnabled_ = value;
}
public static boolean isRemoteEnabled() {
return isRemoteEnabled_;
}
public final CefServer getServer() { return server_; }
@Override
public String toString() {
if (server_ == null)
return "CefApp";
return "CefApp(" + server_.toStringShort() + ")";
}
/**
* Returns the current state of CefApp.
*
* @return current state.
*/
public static CefAppState getState() {
final CefApp app = self;
return app == null ? CefAppState.NONE : app.state_;
}
public synchronized boolean isTerminated() { return state_ == CefAppState.TERMINATED; }
public synchronized boolean isShuttingDown() { return state_ == CefAppState.TERMINATED || state_ == CefAppState.SHUTTING_DOWN; }
private synchronized void setState(final CefAppState state) {
if (state.compareTo(state_) < 0) {
String errMsg = "" + this + ": state cannot go backward. Current state " + state_ + ". Proposed state " + state;
CefLog.Error(errMsg);
throw new IllegalStateException(errMsg);
}
CefLog.Info("%s: set state %s", this, state);
state_ = state;
// Execute on the AWT event dispatching thread.
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
CefAppHandler handler = appHandler_ == null ? CefApp.this : appHandler_;
if (handler != null) handler.stateHasChanged(state);
}
});
}
/**
* To shutdown the system, it's important to call the dispose
* method. Calling this method closes all client instances with
* and all browser instances each client owns. After that the
* message loop is terminated and CEF is shutdown.
*/
public synchronized final void dispose() {
switch (state_) {
case NEW:
// Nothing to do inspite of invalidating the state
setState(CefAppState.TERMINATED);
break;
case INITIALIZING:
case INITIALIZED:
// (3) Shutdown sequence. Close all clients and continue.
setState(CefAppState.SHUTTING_DOWN);
if (clients_.isEmpty()) {
finishShutdown();
} else {
// shutdown() will be called from clientWasDisposed() when the last
// client is gone.
// Use a copy of the HashSet to avoid iterating during modification.
CefLog.Debug("%s: dispose clients before shutting down", this);
HashSet<CefClient> clients = new HashSet<CefClient>(clients_);
for (CefClient c : clients) {
CefLog.Debug("%s: dispose client %s", this, c);
c.dispose();
}
}
break;
case NONE:
case SHUTTING_DOWN:
case TERMINATED:
// Ignore shutdown, CefApp is already terminated, in shutdown progress
// or was never created (shouldn't be possible)
break;
}
}
/**
* Creates a new client instance and returns it to the caller.
* One client instance is responsible for one to many browser
* instances
*
* @return a new client instance
*/
public synchronized CefClient createClient() {
if (state_.compareTo(CefAppState.SHUTTING_DOWN) >= 0) {
String errMsg = "" + this + ": can't create client in state " + state_;
CefLog.Error(errMsg);
throw new IllegalStateException(errMsg);
}
CefClient client = server_ != null ? new CefClient(server_.createClient()) : new CefClient();
onInitialization(client, true);
clients_.add(client);
return client;
}
/**
* Register a scheme handler factory for the specified |scheme_name| and
* optional |domain_name|. An empty |domain_name| value for a standard scheme
* will cause the factory to match all domain names. The |domain_name| value
* will be ignored for non-standard schemes. If |scheme_name| is a built-in
* scheme and no handler is returned by |factory| then the built-in scheme
* handler factory will be called. If |scheme_name| is a custom scheme then
* also implement the CefApp::OnRegisterCustomSchemes() method in all
* processes. This function may be called multiple times to change or remove
* the factory that matches the specified |scheme_name| and optional
* |domain_name|. Returns false if an error occurs. This function may be
* called on any thread in the browser process.
*/
public boolean registerSchemeHandlerFactory(String schemeName, String domainName, CefSchemeHandlerFactory factory) {
if (server_ != null) {
server_.onConnected(()->{
RemoteSchemeHandlerFactory rf = RemoteSchemeHandlerFactory.create(factory);
server_.exec(s -> s.SchemeHandlerFactory_Register(schemeName, domainName, rf.thriftId()));
}, "registerSchemeHandlerFactory", true);
return true;
}
onInitialization(state -> {
if (!N_RegisterSchemeHandlerFactory(schemeName, domainName, factory))
CefLog.Error("Can't register scheme [%s:%s]", schemeName, domainName);
});
return true;
}
/**
* Clear all registered scheme handler factories. Returns false on error. This
* function may be called on any thread in the browser process.
*/
public boolean clearSchemeHandlerFactories() {
if (server_ != null) {
server_.onConnected(()->{
server_.exec(s -> s.ClearAllSchemeHandlerFactories());
}, "clearSchemeHandlerFactories", false);
return true;
}
if (!isInitialized_)
return false;
return N_ClearSchemeHandlerFactories();
}
/**
* This method is called by a CefClient if it was disposed. This causes
* CefApp to clean up its list of available client instances. If all clients
* are disposed, CefApp will be shutdown.
*
* @param client the disposed client.
*/
protected final synchronized void clientWasDisposed(CefClient client) {
clients_.remove(client);
synchronized (initializationListeners_) {
initializationListeners_.remove(client);
}
CefLog.Debug("%s: client was disposed: %s [clients count %d]", this, client, clients_.size());
if (clients_.isEmpty() && state_.compareTo(CefAppState.SHUTTING_DOWN) >= 0) {
// Shutdown native system.
finishShutdown();
}
}
private static void testSleep(int ms) {
if (ms > 0) {
CefLog.Debug("testSleep %s ms", ms);
try {
Thread.sleep(ms);
} catch (InterruptedException ignored) {
}
}
}
/**
* Initialize the context. Can be executed in any thread.
*/
private void initialize() {
setState(CefAppState.INITIALIZING);
testSleep(INIT_TEST_DELAY_MS);
String logMsg = "Initialize CefApp on " + Thread.currentThread();
if (settings_.log_severity == CefSettings.LogSeverity.LOGSEVERITY_INFO || settings_.log_severity == CefSettings.LogSeverity.LOGSEVERITY_VERBOSE)
CefLog.Info(logMsg);
else
CefLog.Debug(logMsg);
CefSettings settings = settings_ != null ? settings_ : new CefSettings();
boolean success = N_Initialize(appHandler_ == null ? this : appHandler_, settings, false);
if (success) {
CefLog.Debug("CefApp: native initialization is finished.");
setState(CefAppState.INITIALIZED);
synchronized (initializationListeners_) {
isInitialized_ = true;
initializationListeners_.forEach(l -> l.stateHasChanged(CefAppState.INITIALIZED));
initializationListeners_.clear();
}
CefLog.Info("version: %s | settings: %s", getVersion(), settings.getDescription());
} else
CefLog.Error("CefApp: N_Initialize failed.");
}
/**
* Shut down the context.
*/
private void finishShutdown() {
if (server_ != null) {
server_.stop();
setState(CefAppState.TERMINATED);
return;
}
new Thread(() -> {
// Empiric observation: to avoid crashes we must perform native shutdown in next manner:
// last client closed -> small pause (to allow CEF finish some bg tasks) -> call CefShutdown
// This small pause will be in bg thread, see JBR-5822.
final int sleepBeforeShutdown = Utils.getInteger("JCEF_SLEEP_BEFORE_SHUTDOWN", 999);
if (sleepBeforeShutdown > 0) {
try {
CefLog.Debug("Sleep before native shutdown for %d ms, thread %s.", sleepBeforeShutdown, Thread.currentThread());
Thread.sleep(sleepBeforeShutdown);
} catch (InterruptedException e) {}
}
// Shutdown native CEF.
CefLog.Info("Perform native shutdown of CEF on thread %s.", Thread.currentThread());
N_Shutdown();
setState(CefAppState.TERMINATED);
}, "CEF-shutdown-thread").start();
}
/**
* Perform a single message loop iteration. Used on all platforms except
* Windows with windowed rendering.
*/
public final void doMessageLoopWork(final long delay_ms) {
if (server_ != null) {
CefLog.Error("doMessageLoopWork musn't be called in remote mode.");
return;
}
// Execute on the AWT event dispatching thread.
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isTerminated()) return;
// The maximum number of milliseconds we're willing to wait between
// calls to DoMessageLoopWork().
final long kMaxTimerDelay = 1000 / 30; // 30fps
if (workTimer_ != null) {
workTimer_.stop();
workTimer_ = null;
}
if (delay_ms <= 0) {
// Execute the work immediately.
N_DoMessageLoopWork();
// Schedule more work later.
doMessageLoopWork(kMaxTimerDelay);
} else {
long timer_delay_ms = delay_ms;
// Never wait longer than the maximum allowed time.
if (timer_delay_ms > kMaxTimerDelay) timer_delay_ms = kMaxTimerDelay;
workTimer_ = new Timer((int) timer_delay_ms, new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
// Timer has timed out.
workTimer_.stop();
workTimer_ = null;
N_DoMessageLoopWork();
// Schedule more work later.
doMessageLoopWork(kMaxTimerDelay);
}
});
workTimer_.start();
}
}
});
}
/**
* This method must be called at the beginning of the main() method to perform platform-specific startup
* initialization. On Linux this initializes Xlib multithreading and on macOS this dynamically loads the
* CEF framework. Can be executed in any thread.
*
* @param args Command-line arguments were used only to get CEF framework path in OSX.
*/
public static boolean startup(String[] args) {
if (isRemoteEnabled())
return true;
if (OS.isMacintosh() && args.length == 0) {
// this condition is to be removed after adapting IJ
startupAsync(JCefAppConfig.getJbrFrameworkPathOSX());
return true;
}
String frameworkPathOSX = null;
String switchPrefix = "--framework-dir-path=";
for (String arg : args) {
if (arg.startsWith(switchPrefix)) {
frameworkPathOSX = new File(arg.substring(switchPrefix.length())).getAbsolutePath();
}
}
startupAsync(frameworkPathOSX);
return true;
}
/**
* @deprecated Use @{@link CefApp#startupAsync(String)}. To be removed in 242
*/
@Deprecated(forRemoval = true)
public static void startupAsync() {
startupAsync(JCefAppConfig.getJbrFrameworkPathOSX());
}
/**
* Asynchronously starts up the CEF framework.
* <p>
* This method starts up the CEF framework on a separate thread, allowing the application to continue
* executing while the framework is being initialized. Once the framework is initialized, the
* operation is considered complete and any registered completion handlers are invoked.
* <p>
* This method must be called at the beginning of the main() method to perform platform-specific startup
* initialization. On Linux this initializes Xlib multithreading and on macOS this dynamically loads the
* CEF framework. Can be executed in any thread.
* <p>
* If the remote debugging feature is enabled, the method returns without actually starting up the
* framework. However, the method must be called anyway to unblock further initialization.
*
* @param frameworkPath The path to the CEF framework on the system.
* Used only on macOS.
*/
public static void startupAsync(String frameworkPath) {
if (isRemoteEnabled())
return;
new NamedThreadExecutor("CefStartup-thread").execute(()->{
try {
if (OS.isWindows()) {
// [tav] "jawt" is loaded by JDK AccessBridgeLoader that leads to UnsatisfiedLinkError
try {
SystemBootstrap.loadLibrary("jawt");
} catch (UnsatisfiedLinkError e) {
CefLog.Error("can't load jawt library: " + e.getMessage());
}
SystemBootstrap.loadLibrary("chrome_elf");
SystemBootstrap.loadLibrary("libcef");
} else if (OS.isLinux()) {
SystemBootstrap.loadLibrary("cef");
}
SystemBootstrap.loadLibrary("jcef");
CefApp.N_Startup(frameworkPath);
ourStartupFeature.complete(null);
} catch (Throwable e) {
ourStartupFeature.completeExceptionally(e);
}
});
}
static class NamedThreadExecutor implements Executor {
private final String name;
NamedThreadExecutor(String name) {
this.name = name;
}
@Override
public void execute(Runnable command) {
new Thread(command, name).start();
}
}
private static native boolean N_Startup(String pathToCefFramework);
private native boolean N_PreInitialize();
private native boolean N_Initialize(CefAppHandler appHandler, CefSettings settings, boolean checkThread);
private native void N_Shutdown();
private native void N_DoMessageLoopWork();
private native CefVersion N_GetVersion();
private native boolean N_RegisterSchemeHandlerFactory(
String schemeName, String domainName, CefSchemeHandlerFactory factory);
private native boolean N_ClearSchemeHandlerFactories();
}