blob: ea451b96cebc714f90bd78770a6bac4ff38ec9f3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.browser;
import java.io.*;
import java.util.*;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.cef3.*;
import org.eclipse.swt.internal.win32.*;
import org.eclipse.swt.widgets.*;
public class CEF extends WebBrowser {
boolean creatingBrowser, ignoreDispose, suppressNavEvents;
CEFClient client;
CEFBrowser cefBrowser;
String htmlText;
Object[] pendingText, pendingUrl;
long /*int*/ windowHandle;
Hashtable ipcAdapters = new Hashtable(9);
Hashtable downloads = new Hashtable(9);
cef_popup_features_t popupFeatures = null;
static boolean LibraryLoaded;
static CEFApp App;
static String InitError;
public static cef_string_t CEFSTRING_EMPTY;
static final String ABOUT_BLANK = "about:blank"; //$NON-NLS-1$
static final String CEF3_PATH = "org.eclipse.swt.browser.CEF3Path"; //$NON-NLS-1$
static final String STRING_NULL = "null"; //$NON-NLS-1$
static final String STRING_RESULT = "result:"; //$NON-NLS-1$
static final String URI_FILEROOT = "file:///"; //$NON-NLS-1$
static final int CEF3_SUPPORTED_REVISON = 1094;
static final int MAX_PROGRESS = 100;
static final int RESPONSE_TIMEOUT = 3000;
/* IPC message names */
static final String MSG_dispose_ipc = "dispose_ipc"; //$NON-NLS-1$
static final String MSG_evaluate = "evaluate"; //$NON-NLS-1$
static final String MSG_init_ipc = "init_ipc"; //$NON-NLS-1$
static final String MSG_on_before_navigation = "on_before_navigation"; //$NON-NLS-1$
static {
/*
* To use CEF3 the full path of its extracted package must be provided in the
* org.eclipse.swt.browser.CEF3Path java property. If a value representing a
* valid directory is set for this property then add it to the library lookup
* path and then attempt to load the swt-cef3 library.
*/
// TODO is InitError really needed? Some failure branches below don't set it
String CEF3Path = System.getProperty(CEF3_PATH);
if (CEF3Path != null && CEF3Path.length() > 0) {
File file = new File(CEF3Path);
if (!(file.exists() && file.isDirectory())) {
InitError = "org.eclipse.swt.browser.CEF3Path value is not valid: " + CEF3Path; // $NON-NLS-1$
} else {
TCHAR buffer = new TCHAR(0, CEF3Path, true);
boolean success = OS.SetDllDirectory(buffer);
if (success) {
try {
Library.loadLibrary("swt-cef3"); // $NON-NLS-1$
/* initialize STRING_EMPTY */
CEFSTRING_EMPTY = CreateCEFString("");
/* initialize CEF3 */
cef_main_args_t args = new cef_main_args_t();
args.instance = OS.GetModuleHandle(null);
App = new CEFApp();
App.add_ref();
int exitCode = CEF3.cef_execute_process(args, App.getAddress());
if (exitCode < 0) {
/* extract the subprocess executable */
String NAME_SUBPROC_EXE = "swt-cef3subproc-win32.exe"; //$NON-NLS-1$
File directory = new File(System.getProperty("user.home"), Library.SWT_LIB_DIR()); //$NON-NLS-1$
file = new File(directory, NAME_SUBPROC_EXE);
if (!file.exists()) {
java.io.InputStream is = Library.class.getResourceAsStream("/" + NAME_SUBPROC_EXE); //$NON-NLS-1$
if (is != null) {
if (!directory.exists()) {
directory.mkdirs();
}
int read;
byte[] bytes = new byte[4096];
try {
FileOutputStream os = new FileOutputStream(file);
while ((read = is.read(bytes)) != -1) {
os.write(bytes, 0, read);
}
os.close();
is.close();
} catch (Exception e) {
}
}
}
if (!file.exists()) {
InitError = "Failed to extract SWT's cef3 subprocess executable: " + NAME_SUBPROC_EXE; //$NON-NLS-1$
} else {
cef_settings_t settings = new cef_settings_t();
settings.size = cef_settings_t.sizeof;
settings.multi_threaded_message_loop = 1;
settings.release_dcheck_enabled = 1;
settings.browser_subprocess_path = CreateCEFString(file.getAbsolutePath());
App.add_ref();
int rc = CEF3.cef_initialize(args, settings, App.getAddress());
CEF3.cef_string_clear(settings.browser_subprocess_path);
if (rc != 0) {
LibraryLoaded = true;
} else {
InitError = "cef3 initialization failed"; //$NON-NLS-1$
}
}
}
} catch (Throwable e) {
InitError = "Failed to load the swt-cef3 library, org.eclipse.swt.browser.CEF3Path: " + CEF3Path; //$NON-NLS-1$
}
}
}
}
if (LibraryLoaded) {
Display.getCurrent().addListener(SWT.Dispose, new Listener() {
public void handleEvent(Event event) {
CEF3.cef_string_clear(CEFSTRING_EMPTY);
CEFSTRING_EMPTY = null;
App.release();
App = null;
}
});
NativeClearSessions = new Runnable() {
public void run() {
if (!LibraryLoaded) return;
// TODO do it
}
};
NativeGetCookie = new Runnable() {
public void run() {
if (!LibraryLoaded) return;
// TODO use CookieUrl and CookieName, set value of CookieValue
}
};
NativeSetCookie = new Runnable() {
public void run() {
if (!LibraryLoaded) return;
// TODO set CookieValue on CookieUrl, set CookieResult to true if successful
}
};
if (NativePendingCookies != null) {
SetPendingCookies(NativePendingCookies);
NativePendingCookies = null;
}
} else {
if (App != null) {
App.release();
App = null;
}
}
}
static cef_string_t CreateCEFString(String string) {
char[] chars = new char[string.length()];
string.getChars(0, string.length(), chars, 0);
cef_string_t result = new cef_string_t();
CEF3.cef_string_set(chars, chars.length, result, 1);
return result;
}
static String ExtractCEFString(long /*int*/ pString) {
if (pString == 0) {
return "";
}
cef_string_t cefString = new cef_string_t();
CEF3.memmove(cefString, pString, CEF3.cef_string_t_sizeof());
int length = (int)/*64*/cefString.length;
char[] chars = new char[length];
OS.memmove(chars, cefString.str, length * 2);
return new String(chars);
}
static Browser FindBrowser(CEFBrowser cefBrowser) {
CEFBrowserHost host = new CEFBrowserHost(cefBrowser.get_host());
long /*int*/ windowHandle = host.get_window_handle();
long /*int*/ parent = OS.GetParent(windowHandle);
return (Browser)Display.getCurrent().findWidget(parent);
}
static boolean IsInstalled() {
if (!LibraryLoaded) return false;
if (CEF3.cef_build_revision() != CEF3_SUPPORTED_REVISON) return false;
return true;
}
public void create(Composite parent, int style) {
cef_window_info_t windowInfo = new cef_window_info_t();
int extStyle = OS.WS_EX_NOINHERITLAYOUT;
if ((style & SWT.BORDER) != 0) extStyle |= OS.WS_EX_CLIENTEDGE;
if ((style & SWT.RIGHT_TO_LEFT) != 0) extStyle |= OS.WS_EX_LAYOUTRTL;
windowInfo.ex_style = extStyle;
windowInfo.style = OS.WS_CHILD | OS.WS_VISIBLE | OS.WS_CLIPSIBLINGS;
windowInfo.x = windowInfo.width = OS.CW_USEDEFAULT;
windowInfo.ex_style = 0;
windowInfo.window_name = CEFSTRING_EMPTY;
windowInfo.parent_window = browser.handle;
cef_browser_settings_t browserSettings = new cef_browser_settings_t();
browserSettings.size = cef_browser_settings_t.sizeof;
cef_string_t strUrl = CreateCEFString(ABOUT_BLANK);
client = new CEFClient(this);
client.add_ref();
int rc = CEF3.cef_browser_host_create_browser(windowInfo, client.getAddress(), strUrl, browserSettings);
CEF3.cef_string_clear(strUrl);
if (rc == 0) {
return;
}
creatingBrowser = true;
Listener listener = new Listener() {
public void handleEvent(Event event) {
switch (event.type) {
case SWT.Dispose: {
/* make this handler run after other dispose listeners */
if (ignoreDispose) {
ignoreDispose = false;
break;
}
ignoreDispose = true;
browser.notifyListeners(event.type, event);
event.type = SWT.NONE;
onDispose(event);
break;
}
case SWT.Resize: {
onResize();
break;
}
}
}
};
browser.addListener(SWT.Dispose, listener);
// browser.addListener(SWT.Resize, listener); // TODO when SWT.EMBEDDED style is removed
browser.addListener(SWT.KeyDown, listener); /* needed for tabbing into the Browser */
}
public void createDownloadWindow(String filename, String url, int id) {
CEFDownload download = new CEFDownload(filename, url, browser);
downloads.put(new Integer(id), download);
}
public boolean back() {
if (cefBrowser == null || cefBrowser.can_go_back() == 0) return false;
cefBrowser.go_back();
return true;
}
public boolean close() {
if (cefBrowser == null) return false;
// TODO
return false;
}
void browserCreated(long /*int*/ handle) {
cefBrowser = new CEFBrowser(handle);
cefBrowser.add_ref(); // TODO release() when disposed
creatingBrowser = false;
CEFBrowserHost host = new CEFBrowserHost(cefBrowser.get_host());
windowHandle = host.get_window_handle();
final Runnable setDeferredContentRunnable = new Runnable() {
public void run() {
/* if browser content has been provided by the client then set it now */
if (pendingText != null) {
setText((String)pendingText[0], ((Boolean)pendingText[1]).booleanValue());
} else if (pendingUrl != null) {
setUrl((String)pendingUrl[0], (String)pendingUrl[1], (String[])pendingUrl[2]);
}
pendingText = pendingUrl = null;
}
};
if (popupFeatures == null) {
setDeferredContentRunnable.run();
return;
}
/* browser is logical child of another browser */
final WindowEvent newEvent = new WindowEvent(browser);
newEvent.display = browser.getDisplay();
newEvent.widget = browser;
newEvent.addressBar = popupFeatures.locationBarVisible == 1;
newEvent.menuBar = popupFeatures.menuBarVisible == 1;
newEvent.statusBar = popupFeatures.statusBarVisible == 1;
newEvent.toolBar = popupFeatures.toolBarVisible == 1;
if (popupFeatures.ySet == 1 && popupFeatures.xSet == 1) {
newEvent.location = new Point(popupFeatures.x, popupFeatures.y);
}
if (popupFeatures.widthSet == 1) {
if (popupFeatures.heightSet == 1) {
newEvent.size = new Point(popupFeatures.width, popupFeatures.height);
} else {
newEvent.size = new Point(popupFeatures.width, this.browser.getSize().y);
}
} else if (popupFeatures.heightSet == 1) {
newEvent.size = new Point(this.browser.getSize().y, popupFeatures.height);
}
popupFeatures = null;
browser.getDisplay().asyncExec(new Runnable() {
public void run() {
if (browser.isDisposed()) return;
for (int i = 0; i < visibilityWindowListeners.length; i++) {
visibilityWindowListeners[i].show(newEvent);
}
if (browser.isDisposed()) return;
setDeferredContentRunnable.run();
}
});
}
public Object evaluate (String script) throws SWTException {
if (cefBrowser == null) return null;
String string = eval(script);
if (string == null) return null;
if (!string.startsWith(STRING_RESULT)) {
throw new SWTException(SWT.ERROR_FAILED_EVALUATE, string);
}
string = string.substring(STRING_RESULT.length());
// TODO parse the JSON and return its Object
return string;
}
public boolean execute(String script) {
if (cefBrowser == null) return false;
return eval(script) != null;
}
String eval(String script) {
script = "'" + STRING_RESULT + "' + JSON.stringify(" + script + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
cef_string_t strName = CreateCEFString(MSG_evaluate);
long /*int*/ pMessage = CEF3.cef_process_message_create(strName);
CEF3.cef_string_clear(strName);
CEFProcessMessage message = new CEFProcessMessage(pMessage);
long /*int*/ pArgs = message.get_argument_list();
CEFListValue args = new CEFListValue(pArgs);
cef_string_t strScript = CreateCEFString(script);
args.set_string(0, strScript);
CEF3.cef_string_clear(strScript);
// TODO always takes the first adapter, which can obviously be wrong
CEFIPCSharedFile adapter = (CEFIPCSharedFile)ipcAdapters.elements().nextElement();
if (adapter == null) return null; /* should not happen */
final String[] response = new String[1];
adapter.sendMessage(message, RESPONSE_TIMEOUT, response);
message.release();
if (response[0].equals(STRING_NULL)) return null;
return response[0];
}
public boolean forward() {
if (cefBrowser == null || cefBrowser.can_go_forward() == 0) return false;
cefBrowser.go_forward();
return true;
}
public String getBrowserType() {
// TODO should this return "CEF3"?
return "webkit"; //$NON-NLS-1$
}
public String getText() {
if (cefBrowser == null) return "";
// TODO
return null;
}
public String getUrl() {
if (cefBrowser == null) {
/*
* If the browser is being created then return ABOUT_BLANK in order to be
* consistent with the other Browser implementations which auto-navigate
* to ABOUT_BLANK when opened.
*/
if (!creatingBrowser) return "";
return ABOUT_BLANK;
}
long /*int*/ pFrame = cefBrowser.get_main_frame();
if (pFrame == 0) {
return null;
}
CEFFrame frame = new CEFFrame(pFrame);
long /*int*/ pUrl = frame.get_url();
String javaStringUrl = ExtractCEFString(pUrl);
CEF3.cef_string_userfree_free(pUrl);
/*
* If the URI indicates that the page is being rendered from memory
* (via setText()) then set it to about:blank to be consistent with IE.
*/
if (javaStringUrl.equals (URI_FILEROOT)) {
javaStringUrl = ABOUT_BLANK;
} else {
int length = URI_FILEROOT.length ();
if (javaStringUrl.startsWith (URI_FILEROOT) && javaStringUrl.charAt (length) == '#') {
javaStringUrl = ABOUT_BLANK + javaStringUrl.substring (length);
}
}
return javaStringUrl;
}
public boolean isBackEnabled() {
return cefBrowser != null && cefBrowser.can_go_back() != 0;
}
public boolean isForwardEnabled() {
return cefBrowser != null && cefBrowser.can_go_forward() != 0;
}
boolean onContextMenu(int xCoord, int yCoord) {
Event event = new Event();
event.x = xCoord;
event.y = yCoord;
browser.notifyListeners(SWT.MenuDetect, event);
if (!event.doit || browser.isDisposed()) return true; /* don't show any menu */
Menu menu = browser.getMenu();
if (menu == null || menu.isDisposed()) return false; /* show CEF menu */
if (event.x != xCoord || event.y != yCoord) {
menu.setLocation(event.x, event.y);
}
menu.setVisible(true);
return true; /* don't show CEF menu since widget Menu has been shown */
}
void onDispose(Event e) {
htmlText = null;
Enumeration elements = ipcAdapters.elements();
while (elements.hasMoreElements()) {
((CEFIPCSharedFile)elements.nextElement()).dispose();
}
ipcAdapters = null;
popupFeatures = null;
downloads = null;
if (client != null) {
client.release();
client = null;
}
if (cefBrowser != null) {
cefBrowser.release();
cefBrowser = null;
}
}
void onDownloadProgress(long receivedBytes, long totalBytes, boolean completed, boolean inProgress, boolean cancelled, CEFDownloadItemCallback callback, int id) {
CEFDownload download = (CEFDownload)downloads.get(new Integer(id));
if (download == null) return;
boolean done = download.updateStatus(receivedBytes, totalBytes, completed, inProgress, cancelled, callback);
if (done) {
downloads.remove(new Integer(id));
}
}
void onIPCDispose(String id) {
CEFIPCSharedFile adapter = (CEFIPCSharedFile)ipcAdapters.get(id);
if (adapter != null) {
adapter.dispose();
ipcAdapters.remove(id);
}
}
void onIPCInit(String id, String arg) {
ipcAdapters.put(id, new CEFIPCSharedFile(cefBrowser, arg));
}
void onLoadComplete() {
if (suppressNavEvents) {
/* completed initial navigate to about:blank for setText() invocation */
return;
}
ProgressEvent progress = new ProgressEvent(browser);
progress.display = browser.getDisplay();
progress.widget = browser;
progress.current = MAX_PROGRESS;
progress.total = MAX_PROGRESS;
for (int i = 0; i < progressListeners.length; i++) {
progressListeners[i].completed (progress);
}
}
void onLocationChanged(String location, boolean top) {
if (!suppressNavEvents) {
Display display = browser.getDisplay();
if (top) {
ProgressEvent progress = new ProgressEvent(browser);
progress.display = display;
progress.widget = browser;
progress.current = 1;
progress.total = MAX_PROGRESS;
for (int i = 0; i < progressListeners.length; i++) {
progressListeners[i].changed(progress);
}
if (browser.isDisposed()) return;
StatusTextEvent statusText = new StatusTextEvent(browser);
statusText.display = display;
statusText.widget = browser;
statusText.text = location;
for (int i = 0; i < statusTextListeners.length; i++) {
statusTextListeners[i].changed(statusText);
}
if (browser.isDisposed()) return;
}
LocationEvent event = new LocationEvent(browser);
event.display = display;
event.widget = browser;
event.location = location;
event.top = top;
for (int i = 0; i < locationListeners.length; i++) {
locationListeners[i].changed(event);
}
if (browser.isDisposed()) return;
}
if (top && htmlText != null) {
long /*int*/ pFrame = cefBrowser.get_main_frame();
if (pFrame != 0) {
CEFFrame frame = new CEFFrame(pFrame);
cef_string_t strHtml = CreateCEFString(htmlText);
cef_string_t strUrl = CreateCEFString(ABOUT_BLANK);
frame.load_string(strHtml, strUrl);
CEF3.cef_string_clear(strUrl);
CEF3.cef_string_clear(strHtml);
htmlText = null;
}
}
}
boolean onLocationChanging(String location) {
// if (location.length () == 0) {
// return false;
// }
// if (url.startsWith (WebKit.PROTOCOL_FILE) && webKit.getUrl ().startsWith (WebKit.ABOUT_BLANK) && webKit.untrustedText) {
// /* indicates an attempt to access the local file system from untrusted content */
// pdListener.ignore ();
// return false;
// }
/*
* If the URI indicates that the page is being rendered from memory
* (via setText()) then set it to about:blank to be consistent with IE.
*/
// if (url.equals (WebKit.URI_FILEROOT)) {
// url = WebKit.ABOUT_BLANK;
// } else {
// int length = WebKit.URI_FILEROOT.length ();
// if (url.startsWith (WebKit.URI_FILEROOT) && url.charAt (length) == '#') {
// url = WebKit.ABOUT_BLANK + url.substring (length);
// }
// }
if (suppressNavEvents) {
suppressNavEvents = false;
if (location.equals(ABOUT_BLANK)) {
/*
* This is the second navigate to about:blank in response to a setText()
* invocation, so allow it silently.
*/
return true;
}
htmlText = null;
}
LocationEvent newEvent = new LocationEvent(browser);
newEvent.display = browser.getDisplay();
newEvent.widget = browser;
newEvent.location = location;
newEvent.doit = true;
if (locationListeners != null) {
for (int i = 0; i < locationListeners.length; i++) {
locationListeners[i].changing(newEvent);
}
}
if (browser.isDisposed()) return false;
if (htmlText != null) {
if (newEvent.doit && location.equals(ABOUT_BLANK)) {
/*
* This is the first of two navigates to about:blank in response to a setText()
* invocation, the subsequent Changed, Completed and Changing events should not be sent.
*/
suppressNavEvents = true;
} else {
htmlText = null;
}
}
return newEvent.doit;
}
void onMessageReceived(long /*int*/ pMessage) {
CEFProcessMessage message = new CEFProcessMessage(pMessage);
long /*int*/ pName = message.get_name();
String name = CEF.ExtractCEFString(pName);
CEF3.cef_string_userfree_free(pName);
long /*int*/ pArgs = message.get_argument_list();
CEFListValue args = new CEFListValue(pArgs);
long /*int*/ pArg = args.get_string(0);
final String ipcId = CEF.ExtractCEFString(pArg);
CEF3.cef_string_userfree_free(pArg);
if (name.equals(MSG_init_ipc)) {
pArg = args.get_string(1);
String initString = CEF.ExtractCEFString(pArg);
CEF3.cef_string_userfree_free(pArg);
onIPCInit(ipcId, initString);
return;
} else if (name.equals(MSG_dispose_ipc)) {
onIPCDispose(ipcId);
return;
}
final CEFIPCSharedFile adapter = (CEFIPCSharedFile)ipcAdapters.get(ipcId);
if (name.equals(MSG_on_before_navigation)) {
pArg = args.get_string(1);
final String url = CEF.ExtractCEFString(pArg);
CEF3.cef_string_userfree_free(pArg);
browser.getDisplay().asyncExec(new Runnable() {
public void run() {
if (browser.isDisposed()) return;
boolean doit = onLocationChanging(url);
adapter.sendResponse(String.valueOf(doit ? 1 : 0));
}
});
}
}
void onResize() {
Rectangle bounds = browser.getClientArea();
OS.SetWindowPos(windowHandle, 0, 0, 0, bounds.width, bounds.height, OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE | OS.SWP_ASYNCWINDOWPOS);
}
void onStatusMessage(String status) {
StatusTextEvent event = new StatusTextEvent(browser);
event.display = browser.getDisplay ();
event.widget = browser;
event.text = status;
for (int i = 0; i < statusTextListeners.length; i++) {
statusTextListeners[i].changed(event);
}
}
void onTitleChange(String title) {
TitleEvent event = new TitleEvent(browser);
event.display = browser.getDisplay();
event.widget = browser;
event.title = title;
for (int i = 0; i < titleListeners.length; i++) {
titleListeners[i].changed(event);
}
}
void onWindowClose() {
WindowEvent newEvent = new WindowEvent(browser);
newEvent.display = browser.getDisplay();
newEvent.widget = browser;
for (int i = 0; i < closeWindowListeners.length; i++) {
closeWindowListeners[i].close(newEvent);
}
}
CEF onWindowOpen(String targetUrl, cef_popup_features_t features) {
WindowEvent newEvent = new WindowEvent(browser);
newEvent.display = browser.getDisplay();
newEvent.widget = browser;
newEvent.required = false;
for (int i = 0; i < openWindowListeners.length; i++) {
openWindowListeners[i].open(newEvent);
}
if (browser.isDisposed()) return null;
Browser childBrowser = null;
if (newEvent.browser != null && newEvent.browser.webBrowser instanceof CEF) {
childBrowser = newEvent.browser;
}
if (childBrowser == null || childBrowser.isDisposed()) return null;
CEF webBrowser = (CEF)childBrowser.webBrowser;
webBrowser.setUrl(targetUrl, null, null);
webBrowser.setPopupFeatures(features);
return webBrowser;
}
public void refresh() {
if (cefBrowser == null) return;
htmlText = null;
cefBrowser.reload();
}
public boolean setText(String html, boolean trusted) {
if (cefBrowser == null) {
if (!creatingBrowser) return false;
pendingText = new Object[] {html, new Boolean(trusted)};
pendingUrl = null;
return true;
}
if (htmlText != null) {
/*
* about:blank is already loading in response to a previous setText()
* invocation, so just change the text that will be set into it
*/
htmlText = html;
return true;
}
long /*int*/ pFrame = cefBrowser.get_main_frame();
if (pFrame == 0) {
return false;
}
htmlText = html;
CEFFrame frame = new CEFFrame(pFrame);
cef_string_t strUrl = CreateCEFString(ABOUT_BLANK);
frame.load_url(strUrl);
CEF3.cef_string_clear(strUrl);
return true;
}
public boolean setUrl(String url, String postData, String[] headers) {
if (cefBrowser == null) {
if (!creatingBrowser) return false;
pendingText = null;
pendingUrl = new Object[] {url, postData, headers};
return true;
}
htmlText = null;
// TODO postData, headers...
long /*int*/ pFrame = cefBrowser.get_main_frame();
if (pFrame == 0) {
return false;
}
CEFFrame frame = new CEFFrame(pFrame);
cef_string_t strUrl = CreateCEFString(url);
frame.load_url(strUrl);
CEF3.cef_string_clear(strUrl);
return true;
}
void setPopupFeatures(cef_popup_features_t features) {
this.popupFeatures = features;
}
public void stop() {
htmlText = null;
if (cefBrowser == null) {
pendingText = pendingUrl = null;
return;
}
cefBrowser.stop_load();
}
}