1 /*******************************************************************************
2 * Copyright (c) 2010, 2017 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.browser;
19 import java.nio.charset.*;
22 import org.eclipse.swt.*;
23 import org.eclipse.swt.graphics.*;
24 import org.eclipse.swt.internal.*;
25 import org.eclipse.swt.internal.ole.win32.*;
26 import org.eclipse.swt.internal.webkit.*;
27 import org.eclipse.swt.internal.win32.*;
28 import org.eclipse.swt.widgets.*;
30 class WebKit extends WebBrowser {
32 long webViewWindowHandle, webViewData;
34 int lastKeyCode, lastCharCode;
36 WebDownloadDelegate webDownloadDelegate;
37 WebFrameLoadDelegate webFrameLoadDelegate;
38 WebPolicyDelegate webPolicyDelegate;
39 WebResourceLoadDelegate webResourceLoadDelegate;
40 WebUIDelegate webUIDelegate;
42 boolean ignoreDispose;
43 boolean loadingText = false;
44 boolean traverseNext = true;
45 boolean traverseOut = false;
46 boolean untrustedText;
47 String lastNavigateURL;
48 BrowserFunction eventFunction;
50 static int prefsIdentifier;
51 static long ExternalClass;
52 static boolean LibraryLoaded = false;
53 static String LibraryLoadError;
54 static Callback JSObjectHasPropertyProc;
55 static Callback JSObjectGetPropertyProc;
56 static Callback JSObjectCallAsFunctionProc;
57 static final int MAX_PROGRESS = 100;
58 static final String ABOUT_BLANK = "about:blank"; //$NON-NLS-1$
59 static final String CLASSNAME_EXTERNAL = "External"; //$NON-NLS-1$
60 static final String EMPTY_STRING = ""; //$NON-NLS-1$
61 static final String FUNCTIONNAME_CALLJAVA = "callJava"; //$NON-NLS-1$
62 static final String HEADER_SETCOOKIE = "Set-Cookie"; //$NON-NLS-1$
63 static final String POST = "POST"; //$NON-NLS-1$
64 static final String PROPERTY_LENGTH = "length"; //$NON-NLS-1$
65 static final String PROTOCOL_HTTPS = "https://"; //$NON-NLS-1$
66 static final String PROTOCOL_FILE = "file://"; //$NON-NLS-1$
67 static final String PROTOCOL_HTTP = "http://"; //$NON-NLS-1$
68 static final String USER_AGENT = "user-agent"; //$NON-NLS-1$
69 static final String URI_FILEROOT = "file:///"; //$NON-NLS-1$
72 static final String DOMEVENT_DRAGSTART = "dragstart"; //$NON-NLS-1$
73 static final String DOMEVENT_KEYDOWN = "keydown"; //$NON-NLS-1$
74 static final String DOMEVENT_KEYPRESS = "keypress"; //$NON-NLS-1$
75 static final String DOMEVENT_KEYUP = "keyup"; //$NON-NLS-1$
76 static final String DOMEVENT_MOUSEDOWN = "mousedown"; //$NON-NLS-1$
77 static final String DOMEVENT_MOUSEUP = "mouseup"; //$NON-NLS-1$
78 static final String DOMEVENT_MOUSEMOVE = "mousemove"; //$NON-NLS-1$
79 static final String DOMEVENT_MOUSEOUT = "mouseout"; //$NON-NLS-1$
80 static final String DOMEVENT_MOUSEOVER = "mouseover"; //$NON-NLS-1$
81 static final String DOMEVENT_MOUSEWHEEL = "mousewheel"; //$NON-NLS-1$
85 * Attempt to load the swt-webkit library. This will only succeed if the Apple
86 * Application Support package is on the user's Windows Path environment variable.
89 Library.loadLibrary ("swt-webkit"); // $NON-NLS-1$
91 } catch (Throwable e) {
95 * If needed, add the Apple Application Support package's directory to the library
96 * lookup path and try to load the swt-webkit library again.
100 * Locate the Apple Application Support directory (if installed) and add its path to the library lookup path.
102 * As of Safari 5.1.4, the Apple Application Support directory is in the Safari installation directory,
103 * which is pointed to by registry entry "HKEY_LOCAL_MACHINE\SOFTWARE\Apple Computer, Inc.\Safari".
105 * With earlier versions of Safari the Apple Application Support is installed in a stand-alone location, which
106 * is pointed to by registry entry "HKEY_LOCAL_MACHINE\SOFTWARE\Apple Inc.\Apple Application Support\InstallDir".
109 String AASDirectory = readInstallDir ("SOFTWARE\\Apple Computer, Inc.\\Safari"); //$NON-NLS-1$
110 if (AASDirectory != null) {
111 AASDirectory += "\\Apple Application Support"; //$NON-NLS-1$
112 if (!new File(AASDirectory).exists()) {
117 if (AASDirectory == null) {
118 AASDirectory = readInstallDir ("SOFTWARE\\Apple Inc.\\Apple Application Support"); //$NON-NLS-1$
121 if (AASDirectory != null) {
122 TCHAR buffer = new TCHAR (0, AASDirectory, true);
123 boolean success = OS.SetDllDirectory (buffer); /* should succeed on XP+SP1 and newer */
126 Library.loadLibrary ("swt-webkit"); //$NON-NLS-1$
127 LibraryLoaded = true;
128 } catch (Throwable e) {
129 LibraryLoadError = "Failed to load the swt-webkit library"; //$NON-NLS-1$
130 if (Device.DEBUG) System.out.println ("Failed to load swt-webkit library. Apple Application Support directory path: " + AASDirectory); //$NON-NLS-1$
133 LibraryLoadError = "Failed to add the Apple Application Support package to the library lookup path. "; //$NON-NLS-1$
134 LibraryLoadError += "To use a SWT.WEBKIT-style Browser prepend " + AASDirectory + " to your Windows 'Path' environment variable and restart."; //$NON-NLS-1$ //$NON-NLS-2$
137 LibraryLoadError = "Safari must be installed to use a SWT.WEBKIT-style Browser"; //$NON-NLS-1$
142 JSObjectHasPropertyProc = new Callback (WebKit.class, "JSObjectHasPropertyProc", 3); //$NON-NLS-1$
143 if (JSObjectHasPropertyProc.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);
144 JSObjectGetPropertyProc = new Callback (WebKit.class, "JSObjectGetPropertyProc", 4); //$NON-NLS-1$
145 if (JSObjectGetPropertyProc.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);
146 JSObjectCallAsFunctionProc = new Callback (WebKit.class, "JSObjectCallAsFunctionProc", 6); //$NON-NLS-1$
147 if (JSObjectCallAsFunctionProc.getAddress () == 0) SWT.error (SWT.ERROR_NO_MORE_CALLBACKS);
149 NativeClearSessions = () -> {
150 long[] result = new long[1];
151 int hr = WebKit_win32.WebKitCreateInstance (WebKit_win32.CLSID_WebCookieManager, 0, WebKit_win32.IID_IWebCookieManager, result);
152 if (hr != COM.S_OK || result[0] == 0) {
155 IWebCookieManager cookieManager = new IWebCookieManager (result[0]);
156 long[] storage = new long[1];
157 hr = cookieManager.cookieStorage (storage);
158 cookieManager.Release ();
159 if (hr != COM.S_OK || storage[0] == 0) {
162 long cookies = WebKit_win32.CFHTTPCookieStorageCopyCookies (storage[0]);
164 int count = WebKit_win32.CFArrayGetCount (cookies);
165 for (int i = 0; i < count; i++) {
166 long cookie = WebKit_win32.CFArrayGetValueAtIndex (cookies, i);
167 long flags = WebKit_win32.CFHTTPCookieGetFlags (cookie);
168 if ((flags & WebKit_win32.CFHTTPCookieSessionOnlyFlag) != 0) {
169 WebKit_win32.CFHTTPCookieStorageDeleteCookie (storage[0], cookie);
172 WebKit_win32.CFRelease (cookies);
174 // WebKit_win32.CFRelease (storage[0]); //intentionally commented, causes crash
177 NativeGetCookie = () -> {
178 long[] result = new long[1];
179 int hr = WebKit_win32.WebKitCreateInstance (WebKit_win32.CLSID_WebCookieManager, 0, WebKit_win32.IID_IWebCookieManager, result);
180 if (hr != COM.S_OK || result[0] == 0) {
183 IWebCookieManager cookieManager = new IWebCookieManager (result[0]);
184 long[] storage = new long[1];
185 hr = cookieManager.cookieStorage (storage);
186 cookieManager.Release ();
187 if (hr != COM.S_OK || storage[0] == 0) {
190 char[] chars = CookieUrl.toCharArray ();
191 long string = WebKit_win32.CFStringCreateWithCharacters (0, chars, chars.length);
193 long cfUrl = WebKit_win32.CFURLCreateWithString (0, string, 0);
195 boolean secure = CookieUrl.startsWith (PROTOCOL_HTTPS);
196 long cookiesArray = WebKit_win32.CFHTTPCookieStorageCopyCookiesForURL (storage[0], cfUrl, secure);
197 if (cookiesArray != 0) {
198 int count = WebKit_win32.CFArrayGetCount (cookiesArray);
199 for (int i = 0; i < count; i++) {
200 long cookie = WebKit_win32.CFArrayGetValueAtIndex (cookiesArray, i);
202 long cookieName = WebKit_win32.CFHTTPCookieGetName (cookie);
203 if (cookieName != 0) {
204 String name = stringFromCFString (cookieName);
205 if (CookieName.equals (name)) {
206 long value = WebKit_win32.CFHTTPCookieGetValue (cookie);
207 if (value != 0) CookieValue = stringFromCFString (value);
213 WebKit_win32.CFRelease (cookiesArray);
215 WebKit_win32.CFRelease (cfUrl);
217 WebKit_win32.CFRelease (string);
219 // WebKit_win32.CFRelease (storage[0]); //intentionally commented, causes crash
222 NativeSetCookie = () -> {
223 long[] result = new long[1];
224 int hr = WebKit_win32.WebKitCreateInstance (WebKit_win32.CLSID_WebCookieManager, 0, WebKit_win32.IID_IWebCookieManager, result);
225 if (hr != COM.S_OK || result[0] == 0) {
228 IWebCookieManager cookieManager = new IWebCookieManager (result[0]);
229 long[] storage = new long[1];
230 hr = cookieManager.cookieStorage (storage);
231 cookieManager.Release ();
232 if (hr != COM.S_OK || storage[0] == 0) {
236 char[] chars = CookieUrl.toCharArray ();
237 long string = WebKit_win32.CFStringCreateWithCharacters (0, chars, chars.length);
239 long cfUrl = WebKit_win32.CFURLCreateWithString (0, string, 0);
241 chars = CookieValue.toCharArray ();
242 long value = WebKit_win32.CFStringCreateWithCharacters (0, chars, chars.length);
244 chars = HEADER_SETCOOKIE.toCharArray ();
245 long key = WebKit_win32.CFStringCreateWithCharacters (0, chars, chars.length);
247 long headers = WebKit_win32.CFDictionaryCreate (0, new long[] {key}, new long[] {value}, 1, WebKit_win32.kCFCopyStringDictionaryKeyCallBacks (), WebKit_win32.kCFTypeDictionaryValueCallBacks ());
249 long cookies = WebKit_win32.CFHTTPCookieCreateWithResponseHeaderFields (0, headers, cfUrl);
251 long cookie = WebKit_win32.CFArrayGetValueAtIndex (cookies, 0);
253 WebKit_win32.CFHTTPCookieStorageSetCookie (storage[0], cookie);
256 WebKit_win32.CFRelease (cookies);
258 WebKit_win32.CFRelease (headers);
260 WebKit_win32.CFRelease (key);
262 WebKit_win32.CFRelease (value);
264 WebKit_win32.CFRelease (cfUrl);
266 WebKit_win32.CFRelease (string);
268 // WebKit_win32.CFRelease (storage[0]); //intentionally commented, causes crash
271 if (NativePendingCookies != null) {
272 SetPendingCookies (NativePendingCookies);
274 NativePendingCookies = null;
278 static long createBSTR (String string) {
279 char[] data = (string + '\0').toCharArray ();
280 return COM.SysAllocString (data);
283 static String error (int code) {
284 throw new SWTError ("WebKit error " + code); //$NON-NLS-1$
287 static String extractBSTR (long bstrString) {
288 int size = COM.SysStringByteLen (bstrString);
289 if (size == 0) return EMPTY_STRING;
290 char[] buffer = new char[(size + 1) / 2]; // add one to avoid rounding errors
291 OS.MoveMemory (buffer, bstrString, size);
292 return new String (buffer);
295 static Browser findBrowser (long webView) {
296 if (webView == 0) return null;
297 IWebView iwebView = new IWebView (webView);
298 long[] result = new long[1];
299 int hr = iwebView.hostWindow (result);
300 if (hr == COM.S_OK && result[0] != 0) {
301 Widget widget = Display.getCurrent ().findWidget (result[0]);
302 if (widget != null && widget instanceof Browser) return (Browser)widget;
307 static long JSObjectCallAsFunctionProc (long ctx, long function, long thisObject, long argumentCount, long arguments, long exception) {
308 WebKit_win32.JSGlobalContextRetain (ctx);
309 if (WebKit_win32.JSValueIsObjectOfClass (ctx, thisObject, ExternalClass) == 0) {
310 return WebKit_win32.JSValueMakeUndefined (ctx);
312 long ptr = WebKit_win32.JSObjectGetPrivate (thisObject);
313 long[] handle = new long[1];
314 C.memmove (handle, ptr, C.PTR_SIZEOF);
315 Browser browser = findBrowser (handle[0]);
316 if (browser == null) return WebKit_win32.JSValueMakeUndefined (ctx);
317 WebKit webkit = (WebKit)browser.webBrowser;
318 return webkit.callJava (ctx, function, thisObject, argumentCount, arguments, exception);
321 static long JSObjectGetPropertyProc (long ctx, long object, long propertyName, long exception) {
322 byte[] bytes = (FUNCTIONNAME_CALLJAVA + '\0').getBytes (StandardCharsets.UTF_8);
323 long name = WebKit_win32.JSStringCreateWithUTF8CString (bytes);
324 long addr = WebKit_win32.JSObjectCallAsFunctionProc_CALLBACK (WebKit.JSObjectCallAsFunctionProc.getAddress ());
325 long function = WebKit_win32.JSObjectMakeFunctionWithCallback (ctx, name, addr);
326 WebKit_win32.JSStringRelease (name);
330 static long JSObjectHasPropertyProc (long ctx, long object, long propertyName) {
331 byte[] bytes = (FUNCTIONNAME_CALLJAVA + '\0').getBytes (StandardCharsets.UTF_8);
332 return WebKit_win32.JSStringIsEqualToUTF8CString (propertyName, bytes);
335 static String readInstallDir (String keyString) {
336 long[] phkResult = new long[1];
337 TCHAR key = new TCHAR (0, keyString, true);
338 if (OS.RegOpenKeyEx (OS.HKEY_LOCAL_MACHINE, key, 0, OS.KEY_READ, phkResult) == 0) {
339 int[] lpcbData = new int[1];
340 TCHAR buffer = new TCHAR (0, "InstallDir", true); //$NON-NLS-1$
341 int result = OS.RegQueryValueEx (phkResult[0], buffer, 0, null, (TCHAR)null, lpcbData);
343 TCHAR lpData = new TCHAR (0, lpcbData[0] / TCHAR.sizeof);
344 result = OS.RegQueryValueEx (phkResult[0], buffer, 0, null, lpData, lpcbData);
346 OS.RegCloseKey (phkResult[0]);
347 return lpData.toString (0, lpData.strlen ());
350 OS.RegCloseKey (phkResult[0]);
355 static String stringFromCFString (long cfString) {
356 if (cfString == 0) return null;
357 int length = WebKit_win32.CFStringGetLength (cfString);
358 long ptr = WebKit_win32.CFStringGetCharactersPtr (cfString);
359 char[] chars = new char[length];
361 OS.MoveMemory (chars, ptr, length);
363 for (int j = 0; j < length; j++) {
364 chars[j] = WebKit_win32.CFStringGetCharacterAtIndex (cfString, j);
367 return new String (chars);
370 static String stringFromJSString (long jsString) {
371 if (jsString == 0) return null;
372 int length = WebKit_win32.JSStringGetLength (jsString);
373 byte[] bytes = new byte[length + 1];
374 WebKit_win32.JSStringGetUTF8CString (jsString, bytes, length + 1);
375 return new String (bytes);
379 public boolean back () {
380 int[] result = new int[1];
381 webView.goBack (result);
382 return result[0] != 0;
385 long callJava (long ctx, long func, long thisObject, long argumentCount, long arguments, long exception) {
386 Object returnValue = null;
387 if (argumentCount == 3) {
388 long[] result = new long[1];
389 C.memmove (result, arguments, C.PTR_SIZEOF);
390 int type = WebKit_win32.JSValueGetType (ctx, result[0]);
391 if (type == WebKit_win32.kJSTypeNumber) {
392 int index = ((Double)convertToJava (ctx, result[0])).intValue ();
395 C.memmove (result, arguments + C.PTR_SIZEOF, C.PTR_SIZEOF);
396 type = WebKit_win32.JSValueGetType (ctx, result[0]);
397 if (type == WebKit_win32.kJSTypeString) {
398 String token = (String)convertToJava (ctx, result[0]);
399 BrowserFunction function = (BrowserFunction)functions.get (index);
400 if (function != null && token.equals (function.token)) {
402 C.memmove (result, arguments + 2 * C.PTR_SIZEOF, C.PTR_SIZEOF);
403 Object temp = convertToJava (ctx, result[0]);
404 if (temp instanceof Object[]) {
405 Object[] args = (Object[])temp;
407 returnValue = function.function (args);
408 } catch (Exception e) {
409 /* exception during function invocation */
410 returnValue = WebBrowser.CreateErrorString (e.getLocalizedMessage ());
413 } catch (IllegalArgumentException e) {
414 /* invalid argument value type */
415 if (function.isEvaluate) {
416 /* notify the function so that a java exception can be thrown */
417 function.function (new String[] {WebBrowser.CreateErrorString (new SWTException (SWT.ERROR_INVALID_RETURN_VALUE).getLocalizedMessage ())});
419 returnValue = WebBrowser.CreateErrorString (e.getLocalizedMessage ());
426 return convertToJS (ctx, returnValue);
430 public boolean close () {
431 return shouldClose ();
434 Object convertToJava (long ctx, long value) {
435 int type = WebKit_win32.JSValueGetType (ctx, value);
437 case WebKit_win32.kJSTypeBoolean: {
438 int result = (int)WebKit_win32.JSValueToNumber (ctx, value, null);
441 case WebKit_win32.kJSTypeNumber: {
442 double result = WebKit_win32.JSValueToNumber (ctx, value, null);
445 case WebKit_win32.kJSTypeString: {
446 long string = WebKit_win32.JSValueToStringCopy (ctx, value, null);
447 if (string == 0) return ""; //$NON-NLS-1$
448 long length = WebKit_win32.JSStringGetMaximumUTF8CStringSize (string);
449 byte[] bytes = new byte[(int)length];
450 length = WebKit_win32.JSStringGetUTF8CString (string, bytes, length);
451 WebKit_win32.JSStringRelease (string);
452 /* length-1 is needed below to exclude the terminator character */
453 return new String (bytes, 0, (int)length - 1, StandardCharsets.UTF_8);
455 case WebKit_win32.kJSTypeNull:
457 case WebKit_win32.kJSTypeUndefined: return null;
458 case WebKit_win32.kJSTypeObject: {
459 byte[] bytes = (PROPERTY_LENGTH + '\0').getBytes (StandardCharsets.UTF_8);
460 long propertyName = WebKit_win32.JSStringCreateWithUTF8CString (bytes);
461 long valuePtr = WebKit_win32.JSObjectGetProperty (ctx, value, propertyName, null);
462 WebKit_win32.JSStringRelease (propertyName);
463 type = WebKit_win32.JSValueGetType (ctx, valuePtr);
464 if (type == WebKit_win32.kJSTypeNumber) {
465 int length = (int)WebKit_win32.JSValueToNumber (ctx, valuePtr, null);
466 Object[] result = new Object[length];
467 for (int i = 0; i < length; i++) {
468 long current = WebKit_win32.JSObjectGetPropertyAtIndex (ctx, value, i, null);
470 result[i] = convertToJava (ctx, current);
477 SWT.error (SWT.ERROR_INVALID_ARGUMENT);
481 long convertToJS (long ctx, Object value) {
483 return WebKit_win32.JSValueMakeNull (ctx);
485 if (value instanceof String) {
486 byte[] bytes = ((String)value + '\0').getBytes (StandardCharsets.UTF_8);
487 long stringRef = WebKit_win32.JSStringCreateWithUTF8CString (bytes);
488 long result = WebKit_win32.JSValueMakeString (ctx, stringRef);
489 WebKit_win32.JSStringRelease (stringRef);
492 if (value instanceof Boolean) {
493 return WebKit_win32.JSValueMakeBoolean (ctx, ((Boolean)value).booleanValue () ? 1 : 0);
495 if (value instanceof Number) {
496 return WebKit_win32.JSValueMakeNumber (ctx, ((Number)value).doubleValue ());
498 if (value instanceof Object[]) {
499 Object[] arrayValue = (Object[]) value;
500 int length = arrayValue.length;
501 long[] arguments = new long[length];
502 for (int i = 0; i < length; i++) {
503 Object javaObject = arrayValue[i];
504 long jsObject = convertToJS (ctx, javaObject);
505 arguments[i] = jsObject;
507 return WebKit_win32.JSObjectMakeArray (ctx, length, arguments, null);
509 SWT.error (SWT.ERROR_INVALID_RETURN_VALUE);
514 public void create (Composite parent, int style) {
515 if (!LibraryLoaded) {
517 SWT.error (SWT.ERROR_NO_HANDLES, null, LibraryLoadError == null ? null : " [" + LibraryLoadError + ']'); //$NON-NLS-1$
520 if (ExternalClass == 0) {
521 JSClassDefinition jsClassDefinition = new JSClassDefinition ();
522 byte[] bytes = (CLASSNAME_EXTERNAL + '\0').getBytes ();
523 jsClassDefinition.className = C.malloc (bytes.length);
524 C.memmove (jsClassDefinition.className, bytes, bytes.length);
526 /* custom callbacks for hasProperty, getProperty and callAsFunction */
527 long addr = WebKit_win32.JSObjectHasPropertyProc_CALLBACK (JSObjectHasPropertyProc.getAddress ());
528 jsClassDefinition.hasProperty = addr;
529 addr = WebKit_win32.JSObjectGetPropertyProc_CALLBACK (JSObjectGetPropertyProc.getAddress ());
530 jsClassDefinition.getProperty = addr;
532 long classDefinitionPtr = C.malloc (JSClassDefinition.sizeof);
533 WebKit_win32.memmove (classDefinitionPtr, jsClassDefinition, JSClassDefinition.sizeof);
534 ExternalClass = WebKit_win32.JSClassCreate (classDefinitionPtr);
535 WebKit_win32.JSClassRetain (ExternalClass);
538 long[] result = new long[1];
539 int hr = WebKit_win32.WebKitCreateInstance (WebKit_win32.CLSID_WebView, 0, WebKit_win32.IID_IWebView, result);
540 if (hr != COM.S_OK || result[0] == 0) {
544 webView = new IWebView (result[0]);
545 webViewData = C.malloc (C.PTR_SIZEOF);
546 C.memmove (webViewData, new long[] {webView.getAddress ()}, C.PTR_SIZEOF);
547 hr = webView.setHostWindow (browser.handle);
548 if (hr != COM.S_OK) {
552 hr = webView.initWithFrame (new RECT (), 0, 0);
553 if (hr != COM.S_OK) {
558 hr = webView.QueryInterface (WebKit_win32.IID_IWebViewPrivate, result);
559 if (hr != COM.S_OK || result[0] == 0) {
563 IWebViewPrivate webViewPrivate = new IWebViewPrivate (result[0]);
565 hr = webViewPrivate.viewWindow (result);
566 if (hr != COM.S_OK || result[0] == 0) {
570 webViewPrivate.Release ();
571 webViewWindowHandle = result[0];
573 webFrameLoadDelegate = new WebFrameLoadDelegate (browser);
574 hr = webView.setFrameLoadDelegate (webFrameLoadDelegate.getAddress ());
575 if (hr != COM.S_OK) {
579 webUIDelegate = new WebUIDelegate (browser);
580 hr = webView.setUIDelegate (webUIDelegate.getAddress ());
581 if (hr != COM.S_OK) {
586 webResourceLoadDelegate = new WebResourceLoadDelegate (browser);
587 hr = webView.setResourceLoadDelegate (webResourceLoadDelegate.getAddress ());
588 if (hr != COM.S_OK) {
593 webDownloadDelegate = new WebDownloadDelegate (browser);
594 hr = webView.setDownloadDelegate (webDownloadDelegate.getAddress ());
595 if (hr != COM.S_OK) {
600 webPolicyDelegate = new WebPolicyDelegate (browser);
601 hr = webView.setPolicyDelegate (webPolicyDelegate.getAddress ());
602 if (hr != COM.S_OK) {
607 initializeWebViewPreferences ();
609 Listener listener = e -> {
612 /* make this handler run after other dispose listeners */
614 ignoreDispose = false;
617 ignoreDispose = true;
618 browser.notifyListeners (e.type, e);
624 OS.SetFocus (webViewWindowHandle);
628 Rectangle bounds = DPIUtil.autoScaleUp(browser.getClientArea ()); // To Pixels
629 OS.SetWindowPos (webViewWindowHandle, 0, bounds.x, bounds.y, bounds.width, bounds.height, OS.SWP_DRAWFRAME);
643 browser.addListener (SWT.Dispose, listener);
644 browser.addListener (SWT.KeyDown, listener); /* needed for tabbing into the Browser */
645 browser.addListener (SWT.FocusIn, listener);
646 browser.addListener (SWT.Resize, listener);
647 browser.addListener (SWT.Traverse, listener);
649 eventFunction = new BrowserFunction (browser, "HandleWebKitEvent") { //$NON-NLS-1$
651 public Object function (Object[] arguments) {
652 return handleEvent (arguments) ? Boolean.TRUE : Boolean.FALSE;
658 public boolean execute (String script) {
659 long[] result = new long[1];
660 int hr = webView.mainFrame (result);
661 if (hr != COM.S_OK || result[0] == 0) {
664 IWebFrame frame = new IWebFrame (result[0]);
665 long context = frame.globalContext ();
670 byte[] bytes = (script + '\0').getBytes (StandardCharsets.UTF_8); //$NON-NLS-1$
671 long scriptString = WebKit_win32.JSStringCreateWithUTF8CString (bytes);
672 if (scriptString == 0) return false;
673 bytes = (getUrl () + '\0').getBytes (StandardCharsets.UTF_8); //$NON-NLS-1$
674 long urlString = WebKit_win32.JSStringCreateWithUTF8CString (bytes);
675 if (urlString == 0) {
676 WebKit_win32.JSStringRelease (scriptString);
679 long evalResult = WebKit_win32.JSEvaluateScript (context, scriptString, 0, urlString, 0, null);
680 WebKit_win32.JSStringRelease (urlString);
681 WebKit_win32.JSStringRelease (scriptString);
682 return evalResult != 0;
686 public boolean forward () {
687 int[] result = new int[1];
688 webView.goForward (result);
689 return result[0] != 0;
693 public String getBrowserType () {
694 return "webkit"; //$NON-NLS-1$
698 public String getText () {
699 long[] result = new long[1];
700 int hr = webView.mainFrame (result);
701 if (hr != COM.S_OK || result[0] == 0) {
704 IWebFrame mainFrame = new IWebFrame (result[0]);
706 hr = mainFrame.dataSource (result);
707 mainFrame.Release ();
708 if (hr != COM.S_OK || result[0] == 0) {
711 IWebDataSource dataSource = new IWebDataSource (result[0]);
713 hr = dataSource.representation (result);
714 dataSource.Release ();
715 if (hr != COM.S_OK || result[0] == 0) {
718 IWebDocumentRepresentation representation = new IWebDocumentRepresentation (result[0]);
720 hr = representation.documentSource (result);
721 representation.Release ();
722 if (hr != COM.S_OK || result[0] == 0) {
725 String source = extractBSTR (result[0]);
726 COM.SysFreeString (result[0]);
731 public String getUrl () {
732 return webFrameLoadDelegate.getUrl ();
735 boolean handleEvent (Object[] arguments) {
738 * DOM events are currently received by hooking DOM listeners
739 * in javascript that invoke this method via a BrowserFunction.
740 * Document.addListener is not implemented on WebKit on windows.
741 * The argument lists received here are:
744 * argument 0: type (String)
745 * argument 1: keyCode (Double)
746 * argument 2: charCode (Double)
747 * argument 3: altKey (Boolean)
748 * argument 4: ctrlKey (Boolean)
749 * argument 5: shiftKey (Boolean)
750 * argument 6: metaKey (Boolean)
754 * argument 0: type (String)
755 * argument 1: screenX (Double)
756 * argument 2: screenY (Double)
757 * argument 3: detail (Double)
758 * argument 4: button (Double)
759 * argument 5: altKey (Boolean)
760 * argument 6: ctrlKey (Boolean)
761 * argument 7: shiftKey (Boolean)
762 * argument 8: metaKey (Boolean)
763 * argument 9: hasRelatedTarget (Boolean)
767 String type = (String)arguments[0];
768 if (type.equals (DOMEVENT_KEYDOWN)) {
769 int keyCode = translateKey (((Double)arguments[1]).intValue ());
770 lastKeyCode = keyCode;
777 case SWT.SCROLL_LOCK:
792 case SWT.ARROW_RIGHT:
805 /* keypress events will not be received for these keys, so send KeyDowns for them now */
807 Event keyEvent = new Event ();
808 keyEvent.widget = browser;
809 keyEvent.type = type.equals (DOMEVENT_KEYDOWN) ? SWT.KeyDown : SWT.KeyUp;
810 keyEvent.keyCode = keyCode;
812 case SWT.BS: keyEvent.character = SWT.BS; break;
813 case SWT.DEL: keyEvent.character = SWT.DEL; break;
814 case SWT.ESC: keyEvent.character = SWT.ESC; break;
815 case SWT.TAB: keyEvent.character = SWT.TAB; break;
817 lastCharCode = keyEvent.character;
819 (((Boolean)arguments[3]).booleanValue () ? SWT.ALT : 0) |
820 (((Boolean)arguments[4]).booleanValue () ? SWT.CTRL : 0) |
821 (((Boolean)arguments[5]).booleanValue () ? SWT.SHIFT : 0) |
822 (((Boolean)arguments[6]).booleanValue () ? SWT.COMMAND : 0);
823 keyEvent.stateMask &= ~keyCode; /* remove current keydown if it's a state key */
824 if (!sendKeyEvent (keyEvent) || browser.isDisposed ()) return false;
831 if (type.equals (DOMEVENT_KEYPRESS)) {
833 * if keydown could not determine a keycode for this key then it's a
834 * key for which key events are not sent (eg.- the Windows key)
836 if (lastKeyCode == 0) return true;
838 lastCharCode = ((Double)arguments[2]).intValue ();
839 if (((Boolean)arguments[4]).booleanValue () && (0 <= lastCharCode && lastCharCode <= 0x7F)) {
840 if ('a' <= lastCharCode && lastCharCode <= 'z') lastCharCode -= 'a' - 'A';
841 if (64 <= lastCharCode && lastCharCode <= 95) lastCharCode -= 64;
844 Event keyEvent = new Event ();
845 keyEvent.widget = browser;
846 keyEvent.type = SWT.KeyDown;
847 keyEvent.keyCode = lastKeyCode;
848 keyEvent.character = (char)lastCharCode;
850 (((Boolean)arguments[3]).booleanValue () ? SWT.ALT : 0) |
851 (((Boolean)arguments[4]).booleanValue () ? SWT.CTRL : 0) |
852 (((Boolean)arguments[5]).booleanValue () ? SWT.SHIFT : 0) |
853 (((Boolean)arguments[6]).booleanValue () ? SWT.COMMAND : 0);
854 return sendKeyEvent (keyEvent) && !browser.isDisposed ();
857 if (type.equals (DOMEVENT_KEYUP)) {
858 int keyCode = translateKey (((Double)arguments[1]).intValue ());
860 /* indicates a key for which key events are not sent */
863 if (keyCode != lastKeyCode) {
864 /* keyup does not correspond to the last keydown */
865 lastKeyCode = keyCode;
869 Event keyEvent = new Event ();
870 keyEvent.widget = browser;
871 keyEvent.type = SWT.KeyUp;
872 keyEvent.keyCode = lastKeyCode;
873 keyEvent.character = (char)lastCharCode;
875 (((Boolean)arguments[3]).booleanValue () ? SWT.ALT : 0) |
876 (((Boolean)arguments[4]).booleanValue () ? SWT.CTRL : 0) |
877 (((Boolean)arguments[5]).booleanValue () ? SWT.SHIFT : 0) |
878 (((Boolean)arguments[6]).booleanValue () ? SWT.COMMAND : 0);
879 switch (lastKeyCode) {
884 keyEvent.stateMask |= lastKeyCode;
887 browser.notifyListeners (keyEvent.type, keyEvent);
888 lastKeyCode = lastCharCode = 0;
889 return keyEvent.doit && !browser.isDisposed ();
895 * MouseOver and MouseOut events are fired any time the mouse enters or exits
896 * any element within the Browser. To ensure that SWT events are only
897 * fired for mouse movements into or out of the Browser, do not fire an
898 * event if there is a related target element.
900 if (type.equals (DOMEVENT_MOUSEOVER) || type.equals (DOMEVENT_MOUSEOUT)) {
901 if (((Boolean)arguments[9]).booleanValue ()) return true;
905 * The position of mouse events is received in screen-relative coordinates
906 * in order to handle pages with frames, since frames express their event
907 * coordinates relative to themselves rather than relative to their top-
908 * level page. Convert screen-relative coordinates to be browser-relative.
910 Point position = new Point (((Double)arguments[1]).intValue (), ((Double)arguments[2]).intValue ());// Points or Pixles ?
911 position = browser.getDisplay ().map (null, browser, position);
913 Event mouseEvent = new Event ();
914 mouseEvent.widget = browser;
915 mouseEvent.x = position.x;
916 mouseEvent.y = position.y;
918 (((Boolean)arguments[5]).booleanValue () ? SWT.ALT : 0) |
919 (((Boolean)arguments[6]).booleanValue () ? SWT.CTRL : 0) |
920 (((Boolean)arguments[7]).booleanValue () ? SWT.SHIFT : 0);
921 mouseEvent.stateMask = mask;
923 if (type.equals (DOMEVENT_MOUSEDOWN)) {
924 mouseEvent.type = SWT.MouseDown;
925 mouseEvent.count = ((Double)arguments[3]).intValue ();
926 mouseEvent.button = ((Double)arguments[4]).intValue ();
927 browser.notifyListeners (mouseEvent.type, mouseEvent);
928 if (browser.isDisposed ()) return true;
929 if (((Double)arguments[3]).intValue () == 2) {
930 mouseEvent = new Event ();
931 mouseEvent.type = SWT.MouseDoubleClick;
932 mouseEvent.widget = browser;
933 mouseEvent.x = position.x;
934 mouseEvent.y = position.y;
935 mouseEvent.stateMask = mask;
936 mouseEvent.count = ((Double)arguments[3]).intValue ();
937 mouseEvent.button = ((Double)arguments[4]).intValue ();
938 browser.notifyListeners (mouseEvent.type, mouseEvent);
943 if (type.equals (DOMEVENT_MOUSEUP)) {
944 mouseEvent.type = SWT.MouseUp;
945 mouseEvent.count = ((Double)arguments[3]).intValue ();
946 mouseEvent.button = ((Double)arguments[4]).intValue ();
947 switch (mouseEvent.button) {
948 case 1: mouseEvent.stateMask |= SWT.BUTTON1; break;
949 case 2: mouseEvent.stateMask |= SWT.BUTTON2; break;
950 case 3: mouseEvent.stateMask |= SWT.BUTTON3; break;
951 case 4: mouseEvent.stateMask |= SWT.BUTTON4; break;
952 case 5: mouseEvent.stateMask |= SWT.BUTTON5; break;
954 } else if (type.equals (DOMEVENT_MOUSEMOVE)) {
955 mouseEvent.type = SWT.MouseMove;
956 } else if (type.equals (DOMEVENT_MOUSEWHEEL)) {
957 mouseEvent.type = SWT.MouseWheel;
958 mouseEvent.count = ((Double)arguments[3]).intValue ();
959 } else if (type.equals (DOMEVENT_MOUSEOVER)) {
960 mouseEvent.type = SWT.MouseEnter;
961 } else if (type.equals (DOMEVENT_MOUSEOUT)) {
962 mouseEvent.type = SWT.MouseExit;
963 if (mouseEvent.x < 0) mouseEvent.x = -1;
964 if (mouseEvent.y < 0) mouseEvent.y = -1;
965 } else if (type.equals (DOMEVENT_DRAGSTART)) {
966 mouseEvent.type = SWT.DragDetect;
967 mouseEvent.button = ((Double)arguments[4]).intValue () + 1;
968 switch (mouseEvent.button) {
969 case 1: mouseEvent.stateMask |= SWT.BUTTON1; break;
970 case 2: mouseEvent.stateMask |= SWT.BUTTON2; break;
971 case 3: mouseEvent.stateMask |= SWT.BUTTON3; break;
972 case 4: mouseEvent.stateMask |= SWT.BUTTON4; break;
973 case 5: mouseEvent.stateMask |= SWT.BUTTON5; break;
977 browser.notifyListeners (mouseEvent.type, mouseEvent);
982 public boolean isBackEnabled () {
983 long[] address = new long[1];
984 int hr = webView.QueryInterface (WebKit_win32.IID_IWebIBActions, address);
985 if (hr != COM.S_OK || address[0] == 0) {
988 IWebIBActions webIBActions = new IWebIBActions (address[0]);
989 int[] result = new int[1];
990 webIBActions.canGoBack (webView.getAddress (), result);
991 webIBActions.Release ();
992 return result[0] != 0;
996 public boolean isFocusControl () {
997 long hwndFocus = OS.GetFocus ();
998 return hwndFocus != 0 && hwndFocus == webViewWindowHandle;
1002 public boolean isForwardEnabled () {
1003 long[] address = new long[1];
1004 int hr = webView.QueryInterface (WebKit_win32.IID_IWebIBActions, address);
1005 if (hr != COM.S_OK || address[0] == 0) {
1008 IWebIBActions webIBActions = new IWebIBActions (address[0]);
1009 int[] result = new int[1];
1010 webIBActions.canGoForward (webView.getAddress (), result);
1011 webIBActions.Release ();
1012 return result[0] != 0;
1016 /* Browser could have been disposed by one of the Dispose listeners */
1017 if (!browser.isDisposed ()) {
1018 /* invoke onbeforeunload handlers but don't prompt with message box */
1019 if (!browser.isClosing) {
1020 webUIDelegate.prompt = false;
1022 webUIDelegate.prompt = true;
1026 Iterator<BrowserFunction> elements = functions.values().iterator ();
1027 while (elements.hasNext ()) {
1028 elements.next ().dispose (false);
1032 eventFunction.dispose();
1033 eventFunction = null;
1034 C.free (webViewData);
1036 webView.setPreferences (0);
1037 webView.setHostWindow (0);
1038 webView.setFrameLoadDelegate (0);
1039 webView.setResourceLoadDelegate (0);
1040 webView.setUIDelegate (0);
1041 webView.setPolicyDelegate (0);
1042 webView.setDownloadDelegate (0);
1045 webDownloadDelegate = null;
1046 webFrameLoadDelegate = null;
1047 webPolicyDelegate = null;
1048 webResourceLoadDelegate = null;
1049 webUIDelegate = null;
1050 lastNavigateURL = null;
1054 public void refresh () {
1055 webFrameLoadDelegate.html = null;
1056 long[] result = new long[1];
1057 int hr = webView.QueryInterface (WebKit_win32.IID_IWebIBActions, result);
1058 if (hr != COM.S_OK || result[0] == 0) {
1061 IWebIBActions webIBActions = new IWebIBActions (result[0]);
1062 webIBActions.reload (webView.getAddress ());
1063 webIBActions.Release ();
1067 boolean sendKeyEvent (Event event) {
1069 * browser.traverse() is called through dislay.translateTraversal() for all
1070 * traversal types except SWT.TRAVERSE_MNEMONIC. So, override
1071 * WebBrowser.sendKeyEvent() so that when it is called from handleEvent(),
1072 * browser.traverse() is not called again.
1074 boolean doit = true;
1075 switch (event.keyCode) {
1078 case SWT.ARROW_DOWN:
1079 case SWT.ARROW_RIGHT:
1081 case SWT.ARROW_LEFT:
1087 if (translateMnemonics ()) {
1088 if (event.character != 0 && (event.stateMask & (SWT.ALT | SWT.CTRL)) == SWT.ALT) {
1089 int traversal = SWT.TRAVERSE_MNEMONIC;
1090 boolean oldEventDoit = event.doit;
1092 doit = !browser.traverse (traversal, event);
1093 event.doit = oldEventDoit;
1100 browser.notifyListeners (event.type, event);
1107 public boolean setText (String html, boolean trusted) {
1109 * If this.html is not null then the about:blank page is already being loaded,
1110 * so no navigate is required. Just set the html that is to be shown.
1112 boolean blankLoading = webFrameLoadDelegate.html != null;
1113 webFrameLoadDelegate.html = html;
1114 untrustedText = !trusted;
1115 if (blankLoading) return true;
1117 long[] result = new long[1];
1118 int hr = webView.mainFrame (result);
1119 if (hr != COM.S_OK || result[0] == 0) {
1122 IWebFrame frame = new IWebFrame (result[0]);
1125 hr = WebKit_win32.WebKitCreateInstance (WebKit_win32.CLSID_WebMutableURLRequest, 0, WebKit_win32.IID_IWebMutableURLRequest, result);
1126 if (hr != COM.S_OK || result[0] == 0) {
1130 IWebMutableURLRequest request = new IWebMutableURLRequest (result[0]);
1132 long urlString = createBSTR (ABOUT_BLANK);
1133 hr = request.setURL (urlString);
1134 COM.SysFreeString (urlString);
1136 if (hr == COM.S_OK) {
1137 hr = frame.loadRequest (request.getAddress ());
1141 return hr == COM.S_OK;
1145 public boolean setUrl (String url, String postData, String[] headers) {
1146 if (url.length () == 0) return false;
1148 * WebKit attempts to open the exact url string that is passed to it and
1149 * will not infer a protocol if it's not specified. Detect the case of an
1150 * invalid URL string and try to fix it by prepending an appropriate protocol.
1154 } catch (MalformedURLException e) {
1155 String testUrl = null;
1156 if (new File (url).isAbsolute ()) {
1157 /* appears to be a local file */
1158 testUrl = PROTOCOL_FILE + url;
1160 testUrl = PROTOCOL_HTTP + url;
1164 url = testUrl; /* adding the protocol made the url valid */
1165 } catch (MalformedURLException e2) {
1166 /* adding the protocol did not make the url valid, so do nothing */
1169 webFrameLoadDelegate.html = null;
1170 lastNavigateURL = url;
1171 long[] result = new long[1];
1172 int hr = webView.mainFrame (result);
1173 if (hr != COM.S_OK || result[0] == 0) {
1176 IWebFrame frame = new IWebFrame (result[0]);
1179 hr = WebKit_win32.WebKitCreateInstance (WebKit_win32.CLSID_WebMutableURLRequest, 0, WebKit_win32.IID_IWebMutableURLRequest, result);
1180 if (hr != COM.S_OK || result[0] == 0) {
1184 IWebMutableURLRequest request = new IWebMutableURLRequest (result[0]);
1186 if (postData != null) { //TODO: POST
1187 // webResourceLoadDelegate.postData = postData;
1188 // long postString = createBSTR (POST);
1189 // hr = request.setHTTPMethod (postString);
1190 // COM.SysFreeString (postString);
1193 // hr = request.QueryInterface (WebKit_win32.IID_IWebMutableURLRequestPrivate, result);
1194 // if (hr == COM.S_OK && result[0] != 0) {
1195 // IWebMutableURLRequestPrivate requestPrivate = new IWebMutableURLRequestPrivate(result[0]);
1196 // int cfRequest = requestPrivate.cfRequest();
1197 // byte[] bytes = postData.getBytes();
1198 // long data = WebKit_win32.CFDataCreate(0, bytes, bytes.length);
1199 // if (data != 0)WebKit_win32.CFURLRequestSetHTTPRequestBody(cfRequest, data);
1201 // long dataGet = WebKit_win32.CFURLRequestCopyHTTPRequestBody(cfRequest);
1202 // int length = WebKit_win32.CFDataGetLength(dataGet);
1203 // long bytePtr = WebKit_win32.CFDataGetBytePtr(dataGet);
1206 hr = COM.S_OK; //TODO: once post code is completed, remove this line if not required
1207 if (headers != null) {
1208 for (int i = 0; i < headers.length; i++) {
1209 String current = headers[i];
1210 if (current != null) {
1211 int index = current.indexOf (':');
1213 String key = current.substring (0, index).trim ();
1214 String value = current.substring (index + 1).trim ();
1215 if (key.length () > 0 && value.length () > 0) {
1216 long valueString = createBSTR (value);
1217 if (key.equalsIgnoreCase (USER_AGENT)) {
1219 * Feature of WebKit. The user-agent header value cannot be overridden
1220 * here. The workaround is to temporarily set the value on the WebView
1221 * and then remove it after the loading of the request has begun.
1223 hr = webView.setCustomUserAgent (valueString);
1225 long keyString = createBSTR (key);
1226 hr = request.setValue (valueString, keyString);
1227 COM.SysFreeString (keyString);
1229 COM.SysFreeString (valueString);
1235 if (hr == COM.S_OK) {
1236 long urlString = createBSTR (url);
1237 hr = request.setURL (urlString);
1238 COM.SysFreeString (urlString);
1239 if (hr == COM.S_OK) {
1240 hr = frame.loadRequest (request.getAddress ());
1242 webView.setCustomUserAgent (0);
1246 return hr == COM.S_OK;
1249 boolean shouldClose () {
1250 if (!jsEnabled) return true;
1252 long[] address = new long[1];
1253 int hr = webView.QueryInterface (WebKit_win32.IID_IWebViewPrivate, address);
1254 if (hr != COM.S_OK || address[0] == 0) {
1257 IWebViewPrivate webViewPrivate = new IWebViewPrivate (address[0]);
1258 int[] result = new int[1];
1259 /* This function will fire the before unload handler for a page */
1260 webViewPrivate.shouldClose (result);
1261 webViewPrivate.Release ();
1262 return result[0] != 0;
1266 public void stop () {
1267 webFrameLoadDelegate.html = null;
1268 long[] result = new long[1];
1269 int hr = webView.QueryInterface (WebKit_win32.IID_IWebIBActions, result);
1270 if (hr != COM.S_OK || result[0] == 0) {
1273 IWebIBActions webIBActions = new IWebIBActions (result[0]);
1274 webIBActions.stopLoading (webView.getAddress ());
1275 webIBActions.Release ();
1278 void initializeWebViewPreferences () {
1280 * Try to create separate preferences for each webview using different identifier for each webview.
1281 * Otherwise all the webviews use the shared preferences.
1283 long[] result = new long[1];
1284 int hr = WebKit_win32.WebKitCreateInstance (WebKit_win32.CLSID_WebPreferences, 0, WebKit_win32.IID_IWebPreferences, result);
1285 if (hr == COM.S_OK && result[0] != 0) {
1286 IWebPreferences preferences = new IWebPreferences (result[0]);
1288 hr = preferences.initWithIdentifier (createBSTR (String.valueOf (prefsIdentifier++)), result);
1289 preferences.Release ();
1290 if (hr == COM.S_OK && result[0] != 0) {
1291 preferences = new IWebPreferences (result[0]);
1292 webView.setPreferences (preferences.getAddress());
1293 preferences.Release ();
1298 hr = webView.preferences (result);
1299 if (hr == COM.S_OK && result[0] != 0) {
1300 IWebPreferences preferences = new IWebPreferences (result[0]);
1301 preferences.setJavaScriptEnabled (1);
1302 preferences.setJavaScriptCanOpenWindowsAutomatically (1);
1303 preferences.setJavaEnabled (0); /* disable applets */
1304 preferences.setTabsToLinks (1);
1305 preferences.setFontSmoothing (WebKit_win32.FontSmoothingTypeWindows);
1306 preferences.Release ();