1 /*******************************************************************************
2 * Copyright (c) 2003, 2018 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;
17 import java.util.List;
19 import org.eclipse.swt.*;
20 import org.eclipse.swt.widgets.*;
22 abstract class WebBrowser {
24 Map<Integer, BrowserFunction> functions = new HashMap<> ();
25 AuthenticationListener[] authenticationListeners = new AuthenticationListener[0];
26 CloseWindowListener[] closeWindowListeners = new CloseWindowListener[0];
27 LocationListener[] locationListeners = new LocationListener[0];
28 OpenWindowListener[] openWindowListeners = new OpenWindowListener[0];
29 ProgressListener[] progressListeners = new ProgressListener[0];
30 StatusTextListener[] statusTextListeners = new StatusTextListener[0];
31 TitleListener[] titleListeners = new TitleListener[0];
32 VisibilityWindowListener[] visibilityWindowListeners = new VisibilityWindowListener[0];
33 boolean jsEnabledOnNextPage = true, jsEnabled = true;
34 int nextFunctionIndex = 1;
35 Object evaluateResult;
37 static final String ERROR_ID = "org.eclipse.swt.browser.error"; // $NON-NLS-1$
38 static final String EXECUTE_ID = "SWTExecuteTemporaryFunction"; // $NON-NLS-1$
40 static List<String[]> NativePendingCookies = new ArrayList<> ();
41 static String CookieName, CookieValue, CookieUrl;
42 static boolean CookieResult;
43 static Runnable NativeClearSessions;
44 static Runnable NativeGetCookie;
45 static Runnable NativeSetCookie;
48 static final int [][] KeyTable = {
49 /* Keyboard and Mouse Masks */
106 /* Non-Numeric Keypad Keys */
107 {37, SWT.ARROW_LEFT},
108 {39, SWT.ARROW_RIGHT},
110 {40, SWT.ARROW_DOWN},
118 /* Virtual and Ascii Keys */
151 /* Numeric Keypad Keys */
163 {107, SWT.KEYPAD_ADD},
164 {109, SWT.KEYPAD_SUBTRACT},
165 {106, SWT.KEYPAD_MULTIPLY},
166 {111, SWT.KEYPAD_DIVIDE},
167 {110, SWT.KEYPAD_DECIMAL},
172 {145, SWT.SCROLL_LOCK},
173 {44, SWT.PRINT_SCREEN},
178 /* WebKit-specific */
184 public class EvaluateFunction extends BrowserFunction {
185 public EvaluateFunction (Browser browser, String name) {
186 super (browser, name, true, new String[0], false);
189 public Object function (Object[] arguments) {
190 if (arguments[0] instanceof String) {
191 String string = (String)arguments[0];
192 if (string.startsWith (ERROR_ID)) {
193 String errorString = ExtractError (string);
194 if (errorString.length () > 0) {
195 evaluateResult = new SWTException (SWT.ERROR_FAILED_EVALUATE, errorString);
197 evaluateResult = new SWTException (SWT.ERROR_FAILED_EVALUATE);
202 evaluateResult = arguments[0];
207 public void addAuthenticationListener (AuthenticationListener listener) {
208 AuthenticationListener[] newAuthenticationListeners = new AuthenticationListener[authenticationListeners.length + 1];
209 System.arraycopy(authenticationListeners, 0, newAuthenticationListeners, 0, authenticationListeners.length);
210 authenticationListeners = newAuthenticationListeners;
211 authenticationListeners[authenticationListeners.length - 1] = listener;
214 public void addCloseWindowListener (CloseWindowListener listener) {
215 CloseWindowListener[] newCloseWindowListeners = new CloseWindowListener[closeWindowListeners.length + 1];
216 System.arraycopy(closeWindowListeners, 0, newCloseWindowListeners, 0, closeWindowListeners.length);
217 closeWindowListeners = newCloseWindowListeners;
218 closeWindowListeners[closeWindowListeners.length - 1] = listener;
221 public void addLocationListener (LocationListener listener) {
222 LocationListener[] newLocationListeners = new LocationListener[locationListeners.length + 1];
223 System.arraycopy(locationListeners, 0, newLocationListeners, 0, locationListeners.length);
224 locationListeners = newLocationListeners;
225 locationListeners[locationListeners.length - 1] = listener;
228 public void addOpenWindowListener (OpenWindowListener listener) {
229 OpenWindowListener[] newOpenWindowListeners = new OpenWindowListener[openWindowListeners.length + 1];
230 System.arraycopy(openWindowListeners, 0, newOpenWindowListeners, 0, openWindowListeners.length);
231 openWindowListeners = newOpenWindowListeners;
232 openWindowListeners[openWindowListeners.length - 1] = listener;
235 public void addProgressListener (ProgressListener listener) {
236 ProgressListener[] newProgressListeners = new ProgressListener[progressListeners.length + 1];
237 System.arraycopy(progressListeners, 0, newProgressListeners, 0, progressListeners.length);
238 progressListeners = newProgressListeners;
239 progressListeners[progressListeners.length - 1] = listener;
242 public void addStatusTextListener (StatusTextListener listener) {
243 StatusTextListener[] newStatusTextListeners = new StatusTextListener[statusTextListeners.length + 1];
244 System.arraycopy(statusTextListeners, 0, newStatusTextListeners, 0, statusTextListeners.length);
245 statusTextListeners = newStatusTextListeners;
246 statusTextListeners[statusTextListeners.length - 1] = listener;
249 public void addTitleListener (TitleListener listener) {
250 TitleListener[] newTitleListeners = new TitleListener[titleListeners.length + 1];
251 System.arraycopy(titleListeners, 0, newTitleListeners, 0, titleListeners.length);
252 titleListeners = newTitleListeners;
253 titleListeners[titleListeners.length - 1] = listener;
256 public void addVisibilityWindowListener (VisibilityWindowListener listener) {
257 VisibilityWindowListener[] newVisibilityWindowListeners = new VisibilityWindowListener[visibilityWindowListeners.length + 1];
258 System.arraycopy(visibilityWindowListeners, 0, newVisibilityWindowListeners, 0, visibilityWindowListeners.length);
259 visibilityWindowListeners = newVisibilityWindowListeners;
260 visibilityWindowListeners[visibilityWindowListeners.length - 1] = listener;
263 public abstract boolean back ();
265 public static void clearSessions () {
266 if (NativeClearSessions != null) NativeClearSessions.run ();
269 public static String GetCookie (String name, String url) {
270 CookieName = name; CookieUrl = url; CookieValue = null;
271 if (NativeGetCookie != null) NativeGetCookie.run ();
272 String result = CookieValue;
273 CookieName = CookieValue = CookieUrl = null;
277 public static boolean SetCookie (String value, String url, boolean addToPending) {
278 CookieValue = value; CookieUrl = url;
279 CookieResult = false;
280 if (NativeSetCookie != null) {
281 NativeSetCookie.run ();
283 if (addToPending && NativePendingCookies != null) {
284 NativePendingCookies.add (new String[] {value, url});
287 CookieValue = CookieUrl = null;
291 static void SetPendingCookies (List<String[]> pendingCookies) {
292 for (String[] current : pendingCookies) {
293 SetCookie (current[0], current[1], false);
297 public abstract void create (Composite parent, int style);
299 static String CreateErrorString (String error) {
300 return ERROR_ID + error;
303 static String ExtractError (String error) {
304 return error.substring (ERROR_ID.length ());
307 public boolean close () {
311 public void createFunction (BrowserFunction function) {
313 * If an existing function with the same name is found then
314 * remove it so that it is not recreated on subsequent pages
315 * (the new function overwrites the old one).
317 for (BrowserFunction current : functions.values()) {
318 if (current.name.equals (function.name)) {
319 deregisterFunction (current);
324 function.index = getNextFunctionIndex ();
325 registerFunction (function);
327 StringBuilder functionBuffer = new StringBuilder (function.name);
328 functionBuffer.append (" = function "); //$NON-NLS-1$
329 functionBuffer.append (function.name);
330 functionBuffer.append ("() {var result = callJava("); //$NON-NLS-1$
331 functionBuffer.append (function.index);
332 functionBuffer.append (",'"); //$NON-NLS-1$
333 functionBuffer.append (function.token);
334 functionBuffer.append ("',Array.prototype.slice.call(arguments)); if (typeof result == 'string' && result.indexOf('"); //$NON-NLS-1$
335 functionBuffer.append (ERROR_ID);
336 functionBuffer.append ("') == 0) {var error = new Error(result.substring("); //$NON-NLS-1$
337 functionBuffer.append (ERROR_ID.length ());
338 functionBuffer.append (")); throw error;} return result;};"); //$NON-NLS-1$
340 String javaCallDeclaration = getJavaCallDeclaration();
342 StringBuilder buffer = new StringBuilder (); //$NON-NLS-1$
343 buffer.append (javaCallDeclaration); //$NON-NLS-1$
345 buffer.append (functionBuffer.toString ());
348 buffer.append ("var frameIds = null;"); //$NON-NLS-1$
349 if (function.frameNames != null) {
350 buffer.append ("frameIds = {"); //$NON-NLS-1$
351 for (int i = 0; i < function.frameNames.length; i++) {
352 buffer.append ('\'');
353 buffer.append (function.frameNames[i]);
354 buffer.append ("':1,"); //$NON-NLS-1$
356 if (function.frameNames.length > 0) {
357 buffer.deleteCharAt(buffer.length () - 1);
359 buffer.append ("};"); //$NON-NLS-1$
362 buffer.append ("for (var i = 0; i < frames.length; i++) {try {if (!frameIds || (frames[i].name && frameIds[frames[i].name])) {"); //$NON-NLS-1$
363 buffer.append ("if (!frames[i].callJava) {frames[i].callJava = window.callJava;} frames[i]."); //$NON-NLS-1$
364 buffer.append (functionBuffer.toString ());
365 buffer.append ("}} catch(e) {}};"); //$NON-NLS-1$
367 function.functionString = buffer.toString ();
368 nonBlockingExecute (function.functionString);
372 * Designed to be overriden.
373 * @return javaScrit code that defines the 'callJava' syntax for javascript.
375 String getJavaCallDeclaration() {
376 return "if (!window.callJava) {\n"
377 + " window.callJava = function callJava(index, token, args) {\n"
378 + " return external.callJava(index,token,args);\n"
383 void deregisterFunction (BrowserFunction function) {
384 functions.remove (function.index);
387 public void destroyFunction (BrowserFunction function) {
388 String deleteString = getDeleteFunctionString (function.name);
389 StringBuilder buffer = new StringBuilder ("for (var i = 0; i < frames.length; i++) {try {frames[i].eval(\""); //$NON-NLS-1$
390 buffer.append (deleteString);
391 buffer.append ("\");} catch (e) {}}"); //$NON-NLS-1$
392 nonBlockingExecute (buffer.toString ());
393 nonBlockingExecute (deleteString);
394 deregisterFunction (function);
397 // Designed to be overriden by platform implementations, used for optimization and avoiding deadlocks.
398 // Webkit2 is async, we often don't need to bother waiting for a return type if we never use it.
399 void nonBlockingExecute(String script) {
403 public abstract boolean execute (String script);
405 public Object evaluate (String script, boolean trusted) throws SWTException {
406 return evaluate(script);
409 public Object evaluate (String script) throws SWTException {
410 // Gtk Developer note:
411 // Webkit1 uses this mechanism.
412 // Webkit2 uses a different mechanism. See WebKit:evaluate();
413 BrowserFunction function = new EvaluateFunction (browser, ""); // $NON-NLS-1$
414 int index = getNextFunctionIndex ();
415 function.index = index;
416 function.isEvaluate = true; // Note, Webkit2 doesn't use 'isEvaluate' machinery because it doesn't use a function for evaluation.
417 registerFunction (function);
418 String functionName = EXECUTE_ID + index;
420 StringBuilder buffer = new StringBuilder ("window."); // $NON-NLS-1$
421 buffer.append (functionName);
422 buffer.append (" = function "); // $NON-NLS-1$
423 buffer.append (functionName);
424 buffer.append ("() {\n"); // $NON-NLS-1$
425 buffer.append (script);
426 buffer.append ("\n};"); // $NON-NLS-1$
427 nonBlockingExecute (buffer.toString ());
429 buffer = new StringBuilder ("if (window."); // $NON-NLS-1$
430 buffer.append (functionName);
431 buffer.append (" == undefined) {window.external.callJava("); // $NON-NLS-1$
432 buffer.append (index);
433 buffer.append (",'"); //$NON-NLS-1$
434 buffer.append (function.token);
435 buffer.append ("', ['"); // $NON-NLS-1$
436 buffer.append (ERROR_ID);
437 buffer.append ("']);} else {try {var result = "); // $NON-NLS-1$
438 buffer.append (functionName);
439 buffer.append ("(); window.external.callJava("); // $NON-NLS-1$
440 buffer.append (index);
441 buffer.append (",'"); //$NON-NLS-1$
442 buffer.append (function.token);
443 buffer.append ("', [result]);} catch (e) {window.external.callJava("); // $NON-NLS-1$
444 buffer.append (index);
445 buffer.append (",'"); //$NON-NLS-1$
446 buffer.append (function.token);
447 buffer.append ("', ['"); // $NON-NLS-1$
448 buffer.append (ERROR_ID);
449 buffer.append ("' + e.message]);}}"); // $NON-NLS-1$
450 nonBlockingExecute (buffer.toString ());
451 nonBlockingExecute (getDeleteFunctionString (functionName));
452 deregisterFunction (function);
454 Object result = evaluateResult;
455 evaluateResult = null;
456 if (result instanceof SWTException) throw (SWTException)result;
460 public abstract boolean forward ();
462 public abstract String getBrowserType ();
464 String getDeleteFunctionString (String functionName) {
465 return "delete window." + functionName; //$NON-NLS-1$
468 int getNextFunctionIndex () {
469 return nextFunctionIndex++;
472 public abstract String getText ();
474 public abstract String getUrl ();
476 public Object getWebBrowser () {
480 public abstract boolean isBackEnabled ();
482 public boolean isFocusControl () {
486 public abstract boolean isForwardEnabled ();
488 public abstract void refresh ();
490 void registerFunction (BrowserFunction function) {
491 functions.put (function.index, function);
494 public void removeAuthenticationListener (AuthenticationListener listener) {
495 if (authenticationListeners.length == 0) return;
497 for (int i = 0; i < authenticationListeners.length; i++) {
498 if (listener == authenticationListeners[i]) {
503 if (index == -1) return;
504 if (authenticationListeners.length == 1) {
505 authenticationListeners = new AuthenticationListener[0];
508 AuthenticationListener[] newAuthenticationListeners = new AuthenticationListener[authenticationListeners.length - 1];
509 System.arraycopy (authenticationListeners, 0, newAuthenticationListeners, 0, index);
510 System.arraycopy (authenticationListeners, index + 1, newAuthenticationListeners, index, authenticationListeners.length - index - 1);
511 authenticationListeners = newAuthenticationListeners;
514 public void removeCloseWindowListener (CloseWindowListener listener) {
515 if (closeWindowListeners.length == 0) return;
517 for (int i = 0; i < closeWindowListeners.length; i++) {
518 if (listener == closeWindowListeners[i]){
523 if (index == -1) return;
524 if (closeWindowListeners.length == 1) {
525 closeWindowListeners = new CloseWindowListener[0];
528 CloseWindowListener[] newCloseWindowListeners = new CloseWindowListener[closeWindowListeners.length - 1];
529 System.arraycopy (closeWindowListeners, 0, newCloseWindowListeners, 0, index);
530 System.arraycopy (closeWindowListeners, index + 1, newCloseWindowListeners, index, closeWindowListeners.length - index - 1);
531 closeWindowListeners = newCloseWindowListeners;
534 public void removeLocationListener (LocationListener listener) {
535 if (locationListeners.length == 0) return;
537 for (int i = 0; i < locationListeners.length; i++) {
538 if (listener == locationListeners[i]){
543 if (index == -1) return;
544 if (locationListeners.length == 1) {
545 locationListeners = new LocationListener[0];
548 LocationListener[] newLocationListeners = new LocationListener[locationListeners.length - 1];
549 System.arraycopy (locationListeners, 0, newLocationListeners, 0, index);
550 System.arraycopy (locationListeners, index + 1, newLocationListeners, index, locationListeners.length - index - 1);
551 locationListeners = newLocationListeners;
554 public void removeOpenWindowListener (OpenWindowListener listener) {
555 if (openWindowListeners.length == 0) return;
557 for (int i = 0; i < openWindowListeners.length; i++) {
558 if (listener == openWindowListeners[i]){
563 if (index == -1) return;
564 if (openWindowListeners.length == 1) {
565 openWindowListeners = new OpenWindowListener[0];
568 OpenWindowListener[] newOpenWindowListeners = new OpenWindowListener[openWindowListeners.length - 1];
569 System.arraycopy (openWindowListeners, 0, newOpenWindowListeners, 0, index);
570 System.arraycopy (openWindowListeners, index + 1, newOpenWindowListeners, index, openWindowListeners.length - index - 1);
571 openWindowListeners = newOpenWindowListeners;
574 public void removeProgressListener (ProgressListener listener) {
575 if (progressListeners.length == 0) return;
577 for (int i = 0; i < progressListeners.length; i++) {
578 if (listener == progressListeners[i]){
583 if (index == -1) return;
584 if (progressListeners.length == 1) {
585 progressListeners = new ProgressListener[0];
588 ProgressListener[] newProgressListeners = new ProgressListener[progressListeners.length - 1];
589 System.arraycopy (progressListeners, 0, newProgressListeners, 0, index);
590 System.arraycopy (progressListeners, index + 1, newProgressListeners, index, progressListeners.length - index - 1);
591 progressListeners = newProgressListeners;
594 public void removeStatusTextListener (StatusTextListener listener) {
595 if (statusTextListeners.length == 0) return;
597 for (int i = 0; i < statusTextListeners.length; i++) {
598 if (listener == statusTextListeners[i]){
603 if (index == -1) return;
604 if (statusTextListeners.length == 1) {
605 statusTextListeners = new StatusTextListener[0];
608 StatusTextListener[] newStatusTextListeners = new StatusTextListener[statusTextListeners.length - 1];
609 System.arraycopy (statusTextListeners, 0, newStatusTextListeners, 0, index);
610 System.arraycopy (statusTextListeners, index + 1, newStatusTextListeners, index, statusTextListeners.length - index - 1);
611 statusTextListeners = newStatusTextListeners;
614 public void removeTitleListener (TitleListener listener) {
615 if (titleListeners.length == 0) return;
617 for (int i = 0; i < titleListeners.length; i++) {
618 if (listener == titleListeners[i]){
623 if (index == -1) return;
624 if (titleListeners.length == 1) {
625 titleListeners = new TitleListener[0];
628 TitleListener[] newTitleListeners = new TitleListener[titleListeners.length - 1];
629 System.arraycopy (titleListeners, 0, newTitleListeners, 0, index);
630 System.arraycopy (titleListeners, index + 1, newTitleListeners, index, titleListeners.length - index - 1);
631 titleListeners = newTitleListeners;
634 public void removeVisibilityWindowListener (VisibilityWindowListener listener) {
635 if (visibilityWindowListeners.length == 0) return;
637 for (int i = 0; i < visibilityWindowListeners.length; i++) {
638 if (listener == visibilityWindowListeners[i]){
643 if (index == -1) return;
644 if (visibilityWindowListeners.length == 1) {
645 visibilityWindowListeners = new VisibilityWindowListener[0];
648 VisibilityWindowListener[] newVisibilityWindowListeners = new VisibilityWindowListener[visibilityWindowListeners.length - 1];
649 System.arraycopy (visibilityWindowListeners, 0, newVisibilityWindowListeners, 0, index);
650 System.arraycopy (visibilityWindowListeners, index + 1, newVisibilityWindowListeners, index, visibilityWindowListeners.length - index - 1);
651 visibilityWindowListeners = newVisibilityWindowListeners;
654 boolean sendKeyEvent (Event event) {
655 int traversal = SWT.TRAVERSE_NONE;
656 boolean traverseDoit = true;
657 switch (event.keyCode) {
659 traversal = SWT.TRAVERSE_ESCAPE;
664 traversal = SWT.TRAVERSE_RETURN;
665 traverseDoit = false;
669 case SWT.ARROW_RIGHT: {
670 traversal = SWT.TRAVERSE_ARROW_NEXT;
671 traverseDoit = false;
675 case SWT.ARROW_LEFT: {
676 traversal = SWT.TRAVERSE_ARROW_PREVIOUS;
677 traverseDoit = false;
681 traversal = (event.stateMask & SWT.SHIFT) != 0 ? SWT.TRAVERSE_TAB_PREVIOUS : SWT.TRAVERSE_TAB_NEXT;
682 traverseDoit = (event.stateMask & SWT.CTRL) != 0;
685 case SWT.PAGE_DOWN: {
686 if ((event.stateMask & SWT.CTRL) != 0) {
687 traversal = SWT.TRAVERSE_PAGE_NEXT;
693 if ((event.stateMask & SWT.CTRL) != 0) {
694 traversal = SWT.TRAVERSE_PAGE_PREVIOUS;
700 if (translateMnemonics ()) {
701 if (event.character != 0 && (event.stateMask & (SWT.ALT | SWT.CTRL)) == SWT.ALT) {
702 traversal = SWT.TRAVERSE_MNEMONIC;
711 if (traversal != SWT.TRAVERSE_NONE) {
712 boolean oldEventDoit = event.doit;
713 event.doit = traverseDoit;
714 doit = !browser.traverse (traversal, event);
715 event.doit = oldEventDoit;
718 browser.notifyListeners (event.type, event);
724 public void setBrowser (Browser browser) {
725 this.browser = browser;
728 public abstract boolean setText (String html, boolean trusted);
730 public abstract boolean setUrl (String url, String postData, String[] headers);
732 public abstract void stop ();
734 int translateKey (int key) {
735 for (int i = 0; i < KeyTable.length; i++) {
736 if (KeyTable[i][0] == key) return KeyTable[i][1];
741 boolean translateMnemonics () {