]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/browser/WebBrowser.java
Work around SWT 4.13 - 4.18 Win32 DnD bug 567422
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / browser / WebBrowser.java
diff --git a/bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/browser/WebBrowser.java b/bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/browser/WebBrowser.java
new file mode 100644 (file)
index 0000000..27dd9f5
--- /dev/null
@@ -0,0 +1,745 @@
+/*******************************************************************************
+ * 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<Integer, BrowserFunction> 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<String[]> 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<String[]> 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;
+}
+
+}