/******************************************************************************* * Copyright (c) 2010, 2017 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.swt.browser; import java.net.*; import java.nio.charset.*; import org.eclipse.swt.*; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.ole.win32.*; import org.eclipse.swt.internal.webkit.*; import org.eclipse.swt.internal.win32.*; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; class WebFrameLoadDelegate { COMObject iWebFrameLoadDelegate; int refCount = 0; Browser browser; String html; String url; static final String OBJECTNAME_EXTERNAL = "external"; //$NON-NLS-1$ WebFrameLoadDelegate (Browser browser) { createCOMInterfaces (); this.browser = browser; } void addEventHandlers (boolean top) { if (top) { StringBuilder buffer = new StringBuilder ("window.SWTkeyhandler = function SWTkeyhandler(e) {"); //$NON-NLS-1$ buffer.append ("try {e.returnValue = HandleWebKitEvent(e.type, e.keyCode, e.charCode, e.altKey, e.ctrlKey, e.shiftKey, e.metaKey);} catch (e) {}};"); //$NON-NLS-1$ buffer.append ("document.addEventListener('keydown', SWTkeyhandler, true);"); //$NON-NLS-1$ buffer.append ("document.addEventListener('keypress', SWTkeyhandler, true);"); //$NON-NLS-1$ buffer.append ("document.addEventListener('keyup', SWTkeyhandler, true);"); //$NON-NLS-1$ browser.execute (buffer.toString ()); buffer = new StringBuilder ("window.SWTmousehandler = function SWTmousehandler(e) {"); //$NON-NLS-1$ buffer.append ("try {e.returnValue = HandleWebKitEvent(e.type, e.screenX, e.screenY, e.detail, e.button + 1, e.altKey, e.ctrlKey, e.shiftKey, e.metaKey, e.relatedTarget != null);} catch (e) {}};"); //$NON-NLS-1$ buffer.append ("document.addEventListener('mousedown', SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ("document.addEventListener('mouseup', SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ("document.addEventListener('mousemove', SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ("document.addEventListener('mousewheel', SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ("document.addEventListener('dragstart', SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ("document.addEventListener('mouseover', SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ("document.addEventListener('mouseout', SWTmousehandler, true);"); //$NON-NLS-1$ browser.execute (buffer.toString ()); } else { StringBuilder buffer = new StringBuilder ("for (var i = 0; i < frames.length; i++) {"); //$NON-NLS-1$ buffer.append ("frames[i].document.addEventListener('keydown', window.SWTkeyhandler, true);"); //$NON-NLS-1$ buffer.append ("frames[i].document.addEventListener('keypress', window.SWTkeyhandler, true);"); //$NON-NLS-1$ buffer.append ("frames[i].document.addEventListener('keyup', window.SWTkeyhandler, true);"); //$NON-NLS-1$ buffer.append ("frames[i].document.addEventListener('mousedown', window.SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ("frames[i].document.addEventListener('mouseup', window.SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ("frames[i].document.addEventListener('mousemove', window.SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ("frames[i].document.addEventListener('mouseover', window.SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ("frames[i].document.addEventListener('mouseout', window.SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ("frames[i].document.addEventListener('mousewheel', window.SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ("frames[i].document.addEventListener('dragstart', window.SWTmousehandler, true);"); //$NON-NLS-1$ buffer.append ('}'); browser.execute (buffer.toString ()); } } int AddRef () { refCount++; return refCount; } void createCOMInterfaces () { iWebFrameLoadDelegate = new COMObject (new int[] {2, 0, 0, 2, 2, 3, 2, 3, 3, 2, 3, 2, 5, 2, 2, 3, 4}) { @Override public long method0 (long[] args) {return QueryInterface (args[0], args[1]);} @Override public long method1 (long[] args) {return AddRef ();} @Override public long method2 (long[] args) {return Release ();} @Override public long method3 (long[] args) {return didStartProvisionalLoadForFrame (args[0], args[1]);} @Override public long method4 (long[] args) {return COM.E_NOTIMPL;} @Override public long method5 (long[] args) {return didFailProvisionalLoadWithError (args[0], args[1], args[2]);} @Override public long method6 (long[] args) {return didCommitLoadForFrame (args[0], args[1]);} @Override public long method7 (long[] args) {return didReceiveTitle (args[0], args[1], args[2]);} @Override public long method8 (long[] args) {return COM.E_NOTIMPL;} @Override public long method9 (long[] args) {return didFinishLoadForFrame (args[0], args[1]);} @Override public long method10 (long[] args){return COM.E_NOTIMPL;} @Override public long method11 (long[] args){return didChangeLocationWithinPageForFrame (args[0], args[1]);} @Override public long method12 (long[] args){return COM.S_OK;} @Override public long method13 (long[] args){return COM.E_NOTIMPL;} @Override public long method14 (long[] args){return COM.S_OK;} @Override public long method15 (long[] args){return COM.S_OK;} @Override public long method16 (long[] args){return didClearWindowObject (args[0], args[1], args[2], args[3]);} }; /* Callbacks that take double parameters require custom callbacks that instead pass pointers to the doubles. */ long ppVtable = iWebFrameLoadDelegate.ppVtable; long[] pVtable = new long[1]; OS.MoveMemory (pVtable, ppVtable, C.PTR_SIZEOF); long[] funcs = new long[17]; OS.MoveMemory (funcs, pVtable[0], C.PTR_SIZEOF * funcs.length); funcs[12] = WebKit_win32.willPerformClientRedirectToURL_CALLBACK (funcs[12]); OS.MoveMemory (pVtable[0], funcs, C.PTR_SIZEOF * funcs.length); } int didChangeLocationWithinPageForFrame (long webView, long frame) { IWebFrame iwebframe = new IWebFrame (frame); long[] result = new long[1]; int hr = iwebframe.dataSource (result); if (hr != COM.S_OK || result[0] == 0) { return COM.S_OK; } IWebDataSource dataSource = new IWebDataSource (result[0]); result[0] = 0; hr = dataSource.request (result); dataSource.Release (); if (hr != COM.S_OK || result[0] == 0) { return COM.S_OK; } IWebURLRequest request = new IWebURLRequest (result[0]); result[0] = 0; hr = request.URL (result); request.Release (); if (hr != COM.S_OK || result[0] == 0) { return COM.S_OK; } String url2 = WebKit.extractBSTR (result[0]); COM.SysFreeString (result[0]); if (url2.length() == 0) return COM.S_OK; /* * 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 (url2.equals (WebKit.URI_FILEROOT)) { url2 = WebKit.ABOUT_BLANK; } else { int length = WebKit.URI_FILEROOT.length (); if (url2.startsWith (WebKit.URI_FILEROOT) && url2.charAt (length) == '#') { url2 = WebKit.ABOUT_BLANK + url2.substring (length); } } final Display display = browser.getDisplay (); result[0] = 0; IWebView iWebView = new IWebView (webView); hr = iWebView.mainFrame (result); boolean top = false; if (hr == COM.S_OK && result[0] != 0) { top = frame == result[0]; new IWebFrame (result[0]).Release (); } if (top) { StatusTextEvent statusText = new StatusTextEvent (browser); statusText.display = display; statusText.widget = browser; statusText.text = url2; StatusTextListener[] statusTextListeners = browser.webBrowser.statusTextListeners; for (int i = 0; i < statusTextListeners.length; i++) { statusTextListeners[i].changed (statusText); } } LocationEvent location = new LocationEvent (browser); location.display = display; location.widget = browser; location.location = url2; location.top = top; LocationListener[] locationListeners = browser.webBrowser.locationListeners; for (int i = 0; i < locationListeners.length; i++) { locationListeners[i].changed (location); } return COM.S_OK; } int didClearWindowObject (long webView, long context, long windowScriptObject, long frame) { WebKit_win32.JSGlobalContextRetain (context); long globalObject = WebKit_win32.JSContextGetGlobalObject (context); long privateData = ((WebKit)browser.webBrowser).webViewData; long externalObject = WebKit_win32.JSObjectMake (context, WebKit.ExternalClass, privateData); byte[] bytes = (OBJECTNAME_EXTERNAL + '\0').getBytes (StandardCharsets.UTF_8); long name = WebKit_win32.JSStringCreateWithUTF8CString (bytes); WebKit_win32.JSObjectSetProperty (context, globalObject, name, externalObject, 0, null); WebKit_win32.JSStringRelease (name); for (BrowserFunction current : browser.webBrowser.functions.values()) { browser.execute (current.functionString); } IWebView iwebView = new IWebView (webView); long[] mainFrame = new long[1]; iwebView.mainFrame (mainFrame); boolean top = mainFrame[0] == frame; new IWebFrame (mainFrame[0]).Release (); addEventHandlers (top); return COM.S_OK; } int didCommitLoadForFrame (long webview, long frame) { IWebFrame iWebFrame = new IWebFrame (frame); long[] result = new long[1]; int hr = iWebFrame.dataSource (result); if (hr != COM.S_OK || result[0] == 0) { return COM.S_OK; } IWebDataSource dataSource = new IWebDataSource (result[0]); result[0] = 0; hr = dataSource.request (result); dataSource.Release (); if (hr != COM.S_OK || result[0] == 0) { return COM.S_OK; } IWebMutableURLRequest request = new IWebMutableURLRequest (result[0]); result[0] = 0; hr = request.URL (result); request.Release (); if (hr != COM.S_OK || result[0] == 0) { return COM.S_OK; } String url2 = WebKit.extractBSTR (result[0]); COM.SysFreeString (result[0]); if (url2.length () == 0) return COM.S_OK; /* * 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 (url2.equals (WebKit.URI_FILEROOT)) { url2 = WebKit.ABOUT_BLANK; } else { int length = WebKit.URI_FILEROOT.length (); if (url2.startsWith (WebKit.URI_FILEROOT) && url2.charAt (length) == '#') { url2 = WebKit.ABOUT_BLANK + url2.substring (length); } } Display display = browser.getDisplay (); result[0] = 0; IWebView iwebView = new IWebView (webview); hr = iwebView.mainFrame (result); boolean top = false; if (hr == COM.S_OK && result[0] != 0) { top = frame == result[0]; new IWebFrame (result[0]).Release (); } if (top) { /* reset resource status variables */ this.url = url2; /* * Each invocation of setText() causes webView_didCommitLoadForFrame to be invoked * twice, once for the initial navigate to about:blank, and once for the auto-navigate * to about:blank that WebKit does when loadHTMLString is invoked. If this is the * first webView_didCommitLoadForFrame callback received for a setText() invocation * then do not send any events or re-install registered BrowserFunctions. */ if (url2.startsWith (WebKit.ABOUT_BLANK) && html != null) return COM.S_OK; /* re-install registered functions */ for (BrowserFunction function : browser.webBrowser.functions.values()) { browser.webBrowser.execute (function.functionString); } ProgressEvent progress = new ProgressEvent (browser); progress.display = display; progress.widget = browser; progress.current = 1; progress.total = WebKit.MAX_PROGRESS; ProgressListener[] progressListeners = browser.webBrowser.progressListeners; for (int i = 0; i < progressListeners.length; i++) { progressListeners[i].changed (progress); } if (browser.isDisposed ()) return COM.S_OK; StatusTextEvent statusText = new StatusTextEvent (browser); statusText.display = display; statusText.widget = browser; statusText.text = url2; StatusTextListener[] statusTextListeners = browser.webBrowser.statusTextListeners; for (int i = 0; i < statusTextListeners.length; i++) { statusTextListeners[i].changed (statusText); } if (browser.isDisposed ()) return COM.S_OK; } LocationEvent location = new LocationEvent (browser); location.display = display; location.widget = browser; location.location = url2; location.top = top; LocationListener[] locationListeners = browser.webBrowser.locationListeners; for (int i = 0; i < locationListeners.length; i++) { locationListeners[i].changed (location); } return COM.S_OK; } int didFailProvisionalLoadWithError (long webView, long error, long frame) { IWebError iweberror = new IWebError (error); int[] errorCode = new int[1]; int hr = iweberror.code (errorCode); if (WebKit_win32.WebURLErrorBadURL < errorCode[0]) return COM.S_OK; String failingURLString = null; long[] failingURL = new long[1]; hr = iweberror.failingURL (failingURL); if (hr == COM.S_OK && failingURL[0] != 0) { failingURLString = WebKit.extractBSTR (failingURL[0]); COM.SysFreeString (failingURL[0]); } if (failingURLString != null && WebKit_win32.WebURLErrorServerCertificateNotYetValid <= errorCode[0] && errorCode[0] <= WebKit_win32.WebURLErrorSecureConnectionFailed) { /* handle invalid certificate error */ long[] result = new long[1]; hr = iweberror.localizedDescription (result); if (hr != COM.S_OK || result[0] == 0) { return COM.S_OK; } String description = WebKit.extractBSTR (result[0]); COM.SysFreeString (result[0]); result[0] = 0; hr = iweberror.QueryInterface (WebKit_win32.IID_IWebErrorPrivate, result); if (hr != COM.S_OK || result[0] == 0) { return COM.S_OK; } IWebErrorPrivate webErrorPrivate = new IWebErrorPrivate (result[0]); result[0] = 0; long[] certificate = new long[1]; hr = webErrorPrivate.sslPeerCertificate (certificate); webErrorPrivate.Release (); if (hr != COM.S_OK || certificate[0] == 0) { return COM.S_OK; } if (showCertificateDialog (webView, failingURLString, description, certificate[0])) { IWebFrame iWebFrame = new IWebFrame (frame); hr = WebKit_win32.WebKitCreateInstance (WebKit_win32.CLSID_WebMutableURLRequest, 0, WebKit_win32.IID_IWebMutableURLRequest, result); if (hr != COM.S_OK || result[0] == 0) { certificate[0] = 0; return COM.S_OK; } IWebMutableURLRequest request = new IWebMutableURLRequest (result[0]); request.setURL (failingURL[0]); request.setAllowsAnyHTTPSCertificate (); iWebFrame.loadRequest (request.getAddress ()); request.Release (); } certificate[0] = 0; return COM.S_OK; } /* handle other types of errors */ long[] result = new long[1]; hr = iweberror.localizedDescription (result); if (hr != COM.S_OK || result[0] == 0) { return COM.S_OK; } String description = WebKit.extractBSTR (result[0]); COM.SysFreeString (result[0]); if (!browser.isDisposed ()) { String message = failingURLString != null ? failingURLString + "\n\n" : ""; //$NON-NLS-1$ //$NON-NLS-2$ message += Compatibility.getMessage ("SWT_Page_Load_Failed", new Object[] {description}); //$NON-NLS-1$ MessageBox messageBox = new MessageBox (browser.getShell (), SWT.OK | SWT.ICON_ERROR); messageBox.setMessage (message); messageBox.open (); } return COM.S_OK; } int didFinishLoadForFrame (long webview, long frame) { IWebView iWebView = new IWebView (webview); long[] iWebFrame = new long[1]; int hr = iWebView.mainFrame (iWebFrame); if (hr != COM.S_OK || iWebFrame[0] == 0) { return COM.S_OK; } boolean top = frame == iWebFrame[0]; new IWebFrame (iWebFrame[0]).Release(); if (!top) return COM.S_OK; /* * If html is not null then there is html from a previous setText() call * waiting to be set into the about:blank page once it has completed loading. */ if (html != null) { if (getUrl ().startsWith (WebKit.ABOUT_BLANK)) { ((WebKit)browser.webBrowser).loadingText = true; long string = WebKit.createBSTR (html); long URLString; if (((WebKit)browser.webBrowser).untrustedText) { URLString = WebKit.createBSTR (WebKit.ABOUT_BLANK); } else { URLString = WebKit.createBSTR (WebKit.URI_FILEROOT); } IWebFrame mainFrame = new IWebFrame (frame); mainFrame.loadHTMLString (string, URLString); html = null; } } /* * The loadHTMLString() invocation above will trigger a second didFinishLoadForFrame * callback when it is completed. If text was just set into the browser then wait for this * second callback to come before sending the title or completed events. */ if (!((WebKit)browser.webBrowser).loadingText) { if (browser.isDisposed ()) return COM.S_OK; /* * To be consistent with other platforms a title event should be fired when a * page has completed loading. A page with a