/******************************************************************************* * Copyright (c) 2003, 2018 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.util.*; import java.util.List; import org.eclipse.swt.*; import org.eclipse.swt.widgets.*; abstract class WebBrowser { Browser browser; Map functions = new HashMap<> (); AuthenticationListener[] authenticationListeners = new AuthenticationListener[0]; CloseWindowListener[] closeWindowListeners = new CloseWindowListener[0]; LocationListener[] locationListeners = new LocationListener[0]; OpenWindowListener[] openWindowListeners = new OpenWindowListener[0]; ProgressListener[] progressListeners = new ProgressListener[0]; StatusTextListener[] statusTextListeners = new StatusTextListener[0]; TitleListener[] titleListeners = new TitleListener[0]; VisibilityWindowListener[] visibilityWindowListeners = new VisibilityWindowListener[0]; boolean jsEnabledOnNextPage = true, jsEnabled = true; int nextFunctionIndex = 1; Object evaluateResult; static final String ERROR_ID = "org.eclipse.swt.browser.error"; // $NON-NLS-1$ static final String EXECUTE_ID = "SWTExecuteTemporaryFunction"; // $NON-NLS-1$ static List NativePendingCookies = new ArrayList<> (); static String CookieName, CookieValue, CookieUrl; static boolean CookieResult; static Runnable NativeClearSessions; static Runnable NativeGetCookie; static Runnable NativeSetCookie; /* Key Mappings */ static final int [][] KeyTable = { /* Keyboard and Mouse Masks */ {18, SWT.ALT}, {16, SWT.SHIFT}, {17, SWT.CONTROL}, {224, SWT.COMMAND}, /* Literal Keys */ {65, 'a'}, {66, 'b'}, {67, 'c'}, {68, 'd'}, {69, 'e'}, {70, 'f'}, {71, 'g'}, {72, 'h'}, {73, 'i'}, {74, 'j'}, {75, 'k'}, {76, 'l'}, {77, 'm'}, {78, 'n'}, {79, 'o'}, {80, 'p'}, {81, 'q'}, {82, 'r'}, {83, 's'}, {84, 't'}, {85, 'u'}, {86, 'v'}, {87, 'w'}, {88, 'x'}, {89, 'y'}, {90, 'z'}, {48, '0'}, {49, '1'}, {50, '2'}, {51, '3'}, {52, '4'}, {53, '5'}, {54, '6'}, {55, '7'}, {56, '8'}, {57, '9'}, {32, ' '}, {59, ';'}, {61, '='}, {188, ','}, {190, '.'}, {191, '/'}, {219, '['}, {221, ']'}, {222, '\''}, {192, '`'}, {220, '\\'}, {108, '|'}, {226, '<'}, /* Non-Numeric Keypad Keys */ {37, SWT.ARROW_LEFT}, {39, SWT.ARROW_RIGHT}, {38, SWT.ARROW_UP}, {40, SWT.ARROW_DOWN}, {45, SWT.INSERT}, {36, SWT.HOME}, {35, SWT.END}, {46, SWT.DEL}, {33, SWT.PAGE_UP}, {34, SWT.PAGE_DOWN}, /* Virtual and Ascii Keys */ {8, SWT.BS}, {13, SWT.CR}, {9, SWT.TAB}, {27, SWT.ESC}, {12, SWT.DEL}, /* Functions Keys */ {112, SWT.F1}, {113, SWT.F2}, {114, SWT.F3}, {115, SWT.F4}, {116, SWT.F5}, {117, SWT.F6}, {118, SWT.F7}, {119, SWT.F8}, {120, SWT.F9}, {121, SWT.F10}, {122, SWT.F11}, {123, SWT.F12}, {124, SWT.F13}, {125, SWT.F14}, {126, SWT.F15}, {127, 0}, {128, 0}, {129, 0}, {130, 0}, {131, 0}, {132, 0}, {133, 0}, {134, 0}, {135, 0}, /* Numeric Keypad Keys */ {96, SWT.KEYPAD_0}, {97, SWT.KEYPAD_1}, {98, SWT.KEYPAD_2}, {99, SWT.KEYPAD_3}, {100, SWT.KEYPAD_4}, {101, SWT.KEYPAD_5}, {102, SWT.KEYPAD_6}, {103, SWT.KEYPAD_7}, {104, SWT.KEYPAD_8}, {105, SWT.KEYPAD_9}, {14, SWT.KEYPAD_CR}, {107, SWT.KEYPAD_ADD}, {109, SWT.KEYPAD_SUBTRACT}, {106, SWT.KEYPAD_MULTIPLY}, {111, SWT.KEYPAD_DIVIDE}, {110, SWT.KEYPAD_DECIMAL}, /* Other keys */ {20, SWT.CAPS_LOCK}, {144, SWT.NUM_LOCK}, {145, SWT.SCROLL_LOCK}, {44, SWT.PRINT_SCREEN}, {6, SWT.HELP}, {19, SWT.PAUSE}, {3, SWT.BREAK}, /* WebKit-specific */ {186, ';'}, {187, '='}, {189, '-'}, }; public class EvaluateFunction extends BrowserFunction { public EvaluateFunction (Browser browser, String name) { super (browser, name, true, new String[0], false); } @Override public Object function (Object[] arguments) { if (arguments[0] instanceof String) { String string = (String)arguments[0]; if (string.startsWith (ERROR_ID)) { String errorString = ExtractError (string); if (errorString.length () > 0) { evaluateResult = new SWTException (SWT.ERROR_FAILED_EVALUATE, errorString); } else { evaluateResult = new SWTException (SWT.ERROR_FAILED_EVALUATE); } return null; } } evaluateResult = arguments[0]; return null; } } public void addAuthenticationListener (AuthenticationListener listener) { AuthenticationListener[] newAuthenticationListeners = new AuthenticationListener[authenticationListeners.length + 1]; System.arraycopy(authenticationListeners, 0, newAuthenticationListeners, 0, authenticationListeners.length); authenticationListeners = newAuthenticationListeners; authenticationListeners[authenticationListeners.length - 1] = listener; } public void addCloseWindowListener (CloseWindowListener listener) { CloseWindowListener[] newCloseWindowListeners = new CloseWindowListener[closeWindowListeners.length + 1]; System.arraycopy(closeWindowListeners, 0, newCloseWindowListeners, 0, closeWindowListeners.length); closeWindowListeners = newCloseWindowListeners; closeWindowListeners[closeWindowListeners.length - 1] = listener; } public void addLocationListener (LocationListener listener) { LocationListener[] newLocationListeners = new LocationListener[locationListeners.length + 1]; System.arraycopy(locationListeners, 0, newLocationListeners, 0, locationListeners.length); locationListeners = newLocationListeners; locationListeners[locationListeners.length - 1] = listener; } public void addOpenWindowListener (OpenWindowListener listener) { OpenWindowListener[] newOpenWindowListeners = new OpenWindowListener[openWindowListeners.length + 1]; System.arraycopy(openWindowListeners, 0, newOpenWindowListeners, 0, openWindowListeners.length); openWindowListeners = newOpenWindowListeners; openWindowListeners[openWindowListeners.length - 1] = listener; } public void addProgressListener (ProgressListener listener) { ProgressListener[] newProgressListeners = new ProgressListener[progressListeners.length + 1]; System.arraycopy(progressListeners, 0, newProgressListeners, 0, progressListeners.length); progressListeners = newProgressListeners; progressListeners[progressListeners.length - 1] = listener; } public void addStatusTextListener (StatusTextListener listener) { StatusTextListener[] newStatusTextListeners = new StatusTextListener[statusTextListeners.length + 1]; System.arraycopy(statusTextListeners, 0, newStatusTextListeners, 0, statusTextListeners.length); statusTextListeners = newStatusTextListeners; statusTextListeners[statusTextListeners.length - 1] = listener; } public void addTitleListener (TitleListener listener) { TitleListener[] newTitleListeners = new TitleListener[titleListeners.length + 1]; System.arraycopy(titleListeners, 0, newTitleListeners, 0, titleListeners.length); titleListeners = newTitleListeners; titleListeners[titleListeners.length - 1] = listener; } public void addVisibilityWindowListener (VisibilityWindowListener listener) { VisibilityWindowListener[] newVisibilityWindowListeners = new VisibilityWindowListener[visibilityWindowListeners.length + 1]; System.arraycopy(visibilityWindowListeners, 0, newVisibilityWindowListeners, 0, visibilityWindowListeners.length); visibilityWindowListeners = newVisibilityWindowListeners; visibilityWindowListeners[visibilityWindowListeners.length - 1] = listener; } public abstract boolean back (); public static void clearSessions () { if (NativeClearSessions != null) NativeClearSessions.run (); } public static String GetCookie (String name, String url) { CookieName = name; CookieUrl = url; CookieValue = null; if (NativeGetCookie != null) NativeGetCookie.run (); String result = CookieValue; CookieName = CookieValue = CookieUrl = null; return result; } public static boolean SetCookie (String value, String url, boolean addToPending) { CookieValue = value; CookieUrl = url; CookieResult = false; if (NativeSetCookie != null) { NativeSetCookie.run (); } else { if (addToPending && NativePendingCookies != null) { NativePendingCookies.add (new String[] {value, url}); } } CookieValue = CookieUrl = null; return CookieResult; } static void SetPendingCookies (List pendingCookies) { for (String[] current : pendingCookies) { SetCookie (current[0], current[1], false); } } public abstract void create (Composite parent, int style); static String CreateErrorString (String error) { return ERROR_ID + error; } static String ExtractError (String error) { return error.substring (ERROR_ID.length ()); } public boolean close () { return true; } public void createFunction (BrowserFunction function) { /* * If an existing function with the same name is found then * remove it so that it is not recreated on subsequent pages * (the new function overwrites the old one). */ for (BrowserFunction current : functions.values()) { if (current.name.equals (function.name)) { deregisterFunction (current); break; } } function.index = getNextFunctionIndex (); registerFunction (function); StringBuilder functionBuffer = new StringBuilder (function.name); functionBuffer.append (" = function "); //$NON-NLS-1$ functionBuffer.append (function.name); functionBuffer.append ("() {var result = callJava("); //$NON-NLS-1$ functionBuffer.append (function.index); functionBuffer.append (",'"); //$NON-NLS-1$ functionBuffer.append (function.token); functionBuffer.append ("',Array.prototype.slice.call(arguments)); if (typeof result == 'string' && result.indexOf('"); //$NON-NLS-1$ functionBuffer.append (ERROR_ID); functionBuffer.append ("') == 0) {var error = new Error(result.substring("); //$NON-NLS-1$ functionBuffer.append (ERROR_ID.length ()); functionBuffer.append (")); throw error;} return result;};"); //$NON-NLS-1$ String javaCallDeclaration = getJavaCallDeclaration(); StringBuilder buffer = new StringBuilder (); //$NON-NLS-1$ buffer.append (javaCallDeclaration); //$NON-NLS-1$ if (function.top) { buffer.append (functionBuffer.toString ()); } buffer.append ("var frameIds = null;"); //$NON-NLS-1$ if (function.frameNames != null) { buffer.append ("frameIds = {"); //$NON-NLS-1$ for (int i = 0; i < function.frameNames.length; i++) { buffer.append ('\''); buffer.append (function.frameNames[i]); buffer.append ("':1,"); //$NON-NLS-1$ } if (function.frameNames.length > 0) { buffer.deleteCharAt(buffer.length () - 1); } buffer.append ("};"); //$NON-NLS-1$ } buffer.append ("for (var i = 0; i < frames.length; i++) {try {if (!frameIds || (frames[i].name && frameIds[frames[i].name])) {"); //$NON-NLS-1$ buffer.append ("if (!frames[i].callJava) {frames[i].callJava = window.callJava;} frames[i]."); //$NON-NLS-1$ buffer.append (functionBuffer.toString ()); buffer.append ("}} catch(e) {}};"); //$NON-NLS-1$ function.functionString = buffer.toString (); nonBlockingExecute (function.functionString); } /** * Designed to be overriden. * @return javaScrit code that defines the 'callJava' syntax for javascript. */ String getJavaCallDeclaration() { return "if (!window.callJava) {\n" + " window.callJava = function callJava(index, token, args) {\n" + " return external.callJava(index,token,args);\n" + " }\n" + "};\n"; } void deregisterFunction (BrowserFunction function) { functions.remove (function.index); } public void destroyFunction (BrowserFunction function) { String deleteString = getDeleteFunctionString (function.name); StringBuilder buffer = new StringBuilder ("for (var i = 0; i < frames.length; i++) {try {frames[i].eval(\""); //$NON-NLS-1$ buffer.append (deleteString); buffer.append ("\");} catch (e) {}}"); //$NON-NLS-1$ nonBlockingExecute (buffer.toString ()); nonBlockingExecute (deleteString); deregisterFunction (function); } // Designed to be overriden by platform implementations, used for optimization and avoiding deadlocks. // Webkit2 is async, we often don't need to bother waiting for a return type if we never use it. void nonBlockingExecute(String script) { execute(script); } public abstract boolean execute (String script); public Object evaluate (String script, boolean trusted) throws SWTException { return evaluate(script); } public Object evaluate (String script) throws SWTException { // Gtk Developer note: // Webkit1 uses this mechanism. // Webkit2 uses a different mechanism. See WebKit:evaluate(); BrowserFunction function = new EvaluateFunction (browser, ""); // $NON-NLS-1$ int index = getNextFunctionIndex (); function.index = index; function.isEvaluate = true; // Note, Webkit2 doesn't use 'isEvaluate' machinery because it doesn't use a function for evaluation. registerFunction (function); String functionName = EXECUTE_ID + index; StringBuilder buffer = new StringBuilder ("window."); // $NON-NLS-1$ buffer.append (functionName); buffer.append (" = function "); // $NON-NLS-1$ buffer.append (functionName); buffer.append ("() {\n"); // $NON-NLS-1$ buffer.append (script); buffer.append ("\n};"); // $NON-NLS-1$ nonBlockingExecute (buffer.toString ()); buffer = new StringBuilder ("if (window."); // $NON-NLS-1$ buffer.append (functionName); buffer.append (" == undefined) {window.external.callJava("); // $NON-NLS-1$ buffer.append (index); buffer.append (",'"); //$NON-NLS-1$ buffer.append (function.token); buffer.append ("', ['"); // $NON-NLS-1$ buffer.append (ERROR_ID); buffer.append ("']);} else {try {var result = "); // $NON-NLS-1$ buffer.append (functionName); buffer.append ("(); window.external.callJava("); // $NON-NLS-1$ buffer.append (index); buffer.append (",'"); //$NON-NLS-1$ buffer.append (function.token); buffer.append ("', [result]);} catch (e) {window.external.callJava("); // $NON-NLS-1$ buffer.append (index); buffer.append (",'"); //$NON-NLS-1$ buffer.append (function.token); buffer.append ("', ['"); // $NON-NLS-1$ buffer.append (ERROR_ID); buffer.append ("' + e.message]);}}"); // $NON-NLS-1$ nonBlockingExecute (buffer.toString ()); nonBlockingExecute (getDeleteFunctionString (functionName)); deregisterFunction (function); Object result = evaluateResult; evaluateResult = null; if (result instanceof SWTException) throw (SWTException)result; return result; } public abstract boolean forward (); public abstract String getBrowserType (); String getDeleteFunctionString (String functionName) { return "delete window." + functionName; //$NON-NLS-1$ } int getNextFunctionIndex () { return nextFunctionIndex++; } public abstract String getText (); public abstract String getUrl (); public Object getWebBrowser () { return null; } public abstract boolean isBackEnabled (); public boolean isFocusControl () { return false; } public abstract boolean isForwardEnabled (); public abstract void refresh (); void registerFunction (BrowserFunction function) { functions.put (function.index, function); } public void removeAuthenticationListener (AuthenticationListener listener) { if (authenticationListeners.length == 0) return; int index = -1; for (int i = 0; i < authenticationListeners.length; i++) { if (listener == authenticationListeners[i]) { index = i; break; } } if (index == -1) return; if (authenticationListeners.length == 1) { authenticationListeners = new AuthenticationListener[0]; return; } AuthenticationListener[] newAuthenticationListeners = new AuthenticationListener[authenticationListeners.length - 1]; System.arraycopy (authenticationListeners, 0, newAuthenticationListeners, 0, index); System.arraycopy (authenticationListeners, index + 1, newAuthenticationListeners, index, authenticationListeners.length - index - 1); authenticationListeners = newAuthenticationListeners; } public void removeCloseWindowListener (CloseWindowListener listener) { if (closeWindowListeners.length == 0) return; int index = -1; for (int i = 0; i < closeWindowListeners.length; i++) { if (listener == closeWindowListeners[i]){ index = i; break; } } if (index == -1) return; if (closeWindowListeners.length == 1) { closeWindowListeners = new CloseWindowListener[0]; return; } CloseWindowListener[] newCloseWindowListeners = new CloseWindowListener[closeWindowListeners.length - 1]; System.arraycopy (closeWindowListeners, 0, newCloseWindowListeners, 0, index); System.arraycopy (closeWindowListeners, index + 1, newCloseWindowListeners, index, closeWindowListeners.length - index - 1); closeWindowListeners = newCloseWindowListeners; } public void removeLocationListener (LocationListener listener) { if (locationListeners.length == 0) return; int index = -1; for (int i = 0; i < locationListeners.length; i++) { if (listener == locationListeners[i]){ index = i; break; } } if (index == -1) return; if (locationListeners.length == 1) { locationListeners = new LocationListener[0]; return; } LocationListener[] newLocationListeners = new LocationListener[locationListeners.length - 1]; System.arraycopy (locationListeners, 0, newLocationListeners, 0, index); System.arraycopy (locationListeners, index + 1, newLocationListeners, index, locationListeners.length - index - 1); locationListeners = newLocationListeners; } public void removeOpenWindowListener (OpenWindowListener listener) { if (openWindowListeners.length == 0) return; int index = -1; for (int i = 0; i < openWindowListeners.length; i++) { if (listener == openWindowListeners[i]){ index = i; break; } } if (index == -1) return; if (openWindowListeners.length == 1) { openWindowListeners = new OpenWindowListener[0]; return; } OpenWindowListener[] newOpenWindowListeners = new OpenWindowListener[openWindowListeners.length - 1]; System.arraycopy (openWindowListeners, 0, newOpenWindowListeners, 0, index); System.arraycopy (openWindowListeners, index + 1, newOpenWindowListeners, index, openWindowListeners.length - index - 1); openWindowListeners = newOpenWindowListeners; } public void removeProgressListener (ProgressListener listener) { if (progressListeners.length == 0) return; int index = -1; for (int i = 0; i < progressListeners.length; i++) { if (listener == progressListeners[i]){ index = i; break; } } if (index == -1) return; if (progressListeners.length == 1) { progressListeners = new ProgressListener[0]; return; } ProgressListener[] newProgressListeners = new ProgressListener[progressListeners.length - 1]; System.arraycopy (progressListeners, 0, newProgressListeners, 0, index); System.arraycopy (progressListeners, index + 1, newProgressListeners, index, progressListeners.length - index - 1); progressListeners = newProgressListeners; } public void removeStatusTextListener (StatusTextListener listener) { if (statusTextListeners.length == 0) return; int index = -1; for (int i = 0; i < statusTextListeners.length; i++) { if (listener == statusTextListeners[i]){ index = i; break; } } if (index == -1) return; if (statusTextListeners.length == 1) { statusTextListeners = new StatusTextListener[0]; return; } StatusTextListener[] newStatusTextListeners = new StatusTextListener[statusTextListeners.length - 1]; System.arraycopy (statusTextListeners, 0, newStatusTextListeners, 0, index); System.arraycopy (statusTextListeners, index + 1, newStatusTextListeners, index, statusTextListeners.length - index - 1); statusTextListeners = newStatusTextListeners; } public void removeTitleListener (TitleListener listener) { if (titleListeners.length == 0) return; int index = -1; for (int i = 0; i < titleListeners.length; i++) { if (listener == titleListeners[i]){ index = i; break; } } if (index == -1) return; if (titleListeners.length == 1) { titleListeners = new TitleListener[0]; return; } TitleListener[] newTitleListeners = new TitleListener[titleListeners.length - 1]; System.arraycopy (titleListeners, 0, newTitleListeners, 0, index); System.arraycopy (titleListeners, index + 1, newTitleListeners, index, titleListeners.length - index - 1); titleListeners = newTitleListeners; } public void removeVisibilityWindowListener (VisibilityWindowListener listener) { if (visibilityWindowListeners.length == 0) return; int index = -1; for (int i = 0; i < visibilityWindowListeners.length; i++) { if (listener == visibilityWindowListeners[i]){ index = i; break; } } if (index == -1) return; if (visibilityWindowListeners.length == 1) { visibilityWindowListeners = new VisibilityWindowListener[0]; return; } VisibilityWindowListener[] newVisibilityWindowListeners = new VisibilityWindowListener[visibilityWindowListeners.length - 1]; System.arraycopy (visibilityWindowListeners, 0, newVisibilityWindowListeners, 0, index); System.arraycopy (visibilityWindowListeners, index + 1, newVisibilityWindowListeners, index, visibilityWindowListeners.length - index - 1); visibilityWindowListeners = newVisibilityWindowListeners; } boolean sendKeyEvent (Event event) { int traversal = SWT.TRAVERSE_NONE; boolean traverseDoit = true; switch (event.keyCode) { case SWT.ESC: { traversal = SWT.TRAVERSE_ESCAPE; traverseDoit = true; break; } case SWT.CR: { traversal = SWT.TRAVERSE_RETURN; traverseDoit = false; break; } case SWT.ARROW_DOWN: case SWT.ARROW_RIGHT: { traversal = SWT.TRAVERSE_ARROW_NEXT; traverseDoit = false; break; } case SWT.ARROW_UP: case SWT.ARROW_LEFT: { traversal = SWT.TRAVERSE_ARROW_PREVIOUS; traverseDoit = false; break; } case SWT.TAB: { traversal = (event.stateMask & SWT.SHIFT) != 0 ? SWT.TRAVERSE_TAB_PREVIOUS : SWT.TRAVERSE_TAB_NEXT; traverseDoit = (event.stateMask & SWT.CTRL) != 0; break; } case SWT.PAGE_DOWN: { if ((event.stateMask & SWT.CTRL) != 0) { traversal = SWT.TRAVERSE_PAGE_NEXT; traverseDoit = true; } break; } case SWT.PAGE_UP: { if ((event.stateMask & SWT.CTRL) != 0) { traversal = SWT.TRAVERSE_PAGE_PREVIOUS; traverseDoit = true; } break; } default: { if (translateMnemonics ()) { if (event.character != 0 && (event.stateMask & (SWT.ALT | SWT.CTRL)) == SWT.ALT) { traversal = SWT.TRAVERSE_MNEMONIC; traverseDoit = true; } } break; } } boolean doit = true; if (traversal != SWT.TRAVERSE_NONE) { boolean oldEventDoit = event.doit; event.doit = traverseDoit; doit = !browser.traverse (traversal, event); event.doit = oldEventDoit; } if (doit) { browser.notifyListeners (event.type, event); doit = event.doit; } return doit; } public void setBrowser (Browser browser) { this.browser = browser; } public abstract boolean setText (String html, boolean trusted); public abstract boolean setUrl (String url, String postData, String[] headers); public abstract void stop (); int translateKey (int key) { for (int i = 0; i < KeyTable.length; i++) { if (KeyTable[i][0] == key) return KeyTable[i][1]; } return 0; } boolean translateMnemonics () { return true; } }