/******************************************************************************* * Copyright (c) 2003, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.swt.browser; import java.io.*; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.gtk.*; import org.eclipse.swt.internal.mozilla.*; import org.eclipse.swt.widgets.*; class MozillaDelegate { Browser browser; Shell eventShell; long /*int*/ mozillaHandle, embedHandle; boolean hasFocus; Listener listener; static Callback eventCallback; static long /*int*/ eventProc; static final int STOP_PROPOGATE = 1; static boolean IsSparc; static { String osName = System.getProperty ("os.name").toLowerCase (); //$NON-NLS-1$ String osArch = System.getProperty ("os.arch").toLowerCase (); //$NON-NLS-1$ IsSparc = (osName.startsWith ("sunos") || osName.startsWith ("solaris")) && osArch.startsWith("sparc"); //$NON-NLS-1$ } MozillaDelegate (Browser browser) { super (); /* * The mozilla libraries on SPARC need the C++ runtime library to be loaded, but they do not declare * this dependency because they usually get it for free as a result of the mozilla executable pulling it * in. Load this library here and scope it globally so that the mozilla libraries can resolve. */ if (IsSparc) { byte[] buffer = Converter.wcsToMbcs (null, "libCrun.so.1", true); //$NON-NLS-1$ OS.dlopen (buffer, OS.RTLD_NOW | OS.RTLD_GLOBAL); } this.browser = browser; } static long /*int*/ eventProc (long /*int*/ handle, long /*int*/ gdkEvent, long /*int*/ pointer) { long /*int*/ parent = OS.gtk_widget_get_parent (handle); parent = OS.gtk_widget_get_parent (parent); if (parent == 0) return 0; Widget widget = Display.getCurrent ().findWidget (parent); if (widget != null && widget instanceof Browser) { return ((Mozilla)((Browser)widget).webBrowser).delegate.gtk_event (handle, gdkEvent, pointer); } return 0; } static Browser findBrowser (long /*int*/ handle) { /* * Note. On GTK, Mozilla is embedded into a GtkHBox handle * and not directly into the parent Composite handle. */ long /*int*/ parent = OS.gtk_widget_get_parent (handle); Display display = Display.getCurrent (); return (Browser)display.findWidget (parent); } static String getCacheParentPath () { return getProfilePath (); } static String[] getJSLibraryNames () { return new String[] {"libxul.so"}; //$NON-NLS-1$ } static String getJSLibraryName_Pre4() { return "libmozjs.so"; //$NON-NLS-1$ } static String getLibraryName () { return "libxpcom.so"; //$NON-NLS-1$ } static String getProfilePath () { String baseDir = System.getProperty ("user.home"); //$NON-NLS-1$ /* * Bug in Sun JRE. Under some circumstances the value of java property "user.home" is * "?", even when the HOME environment variable has a valid value. If this happens * then attempt to read the value from the environment directly. */ if (baseDir.equals ("?")) { //$NON-NLS-1$ long /*int*/ ptr = C.getenv (wcsToMbcs (null, "HOME", true)); //$NON-NLS-1$ if (ptr != 0) { int length = C.strlen (ptr); byte[] bytes = new byte[length]; C.memmove (bytes, ptr, length); baseDir = new String (mbcsToWcs (null, bytes)); } } return baseDir + Mozilla.SEPARATOR_OS + ".mozilla" + Mozilla.SEPARATOR_OS + "eclipse"; //$NON-NLS-1$ //$NON-NLS-2$ } static String getSWTInitLibraryName () { return "swt-xpcominit"; //$NON-NLS-1$ } static void loadAdditionalLibraries (String mozillaPath) { if (Mozilla.IsPre_4) return; /* * The use of the swt-xulrunner-fix library works around mozilla bug * https://bugzilla.mozilla.org/show_bug.cgi?id=720682 (XULRunner 10). */ String libName = "libswt-xulrunner-fix.so"; //$NON-NLS-1$ File libsDir = new File (getProfilePath () + "/libs/" + Mozilla.OS() + '/' + Mozilla.Arch ()); //$NON-NLS-1$ File file = new File (libsDir, libName); if (!file.exists()) { java.io.InputStream is = Library.class.getResourceAsStream ('/' + libName); if (is != null) { if (!libsDir.exists ()) { libsDir.mkdirs (); } int read; byte [] buffer = new byte [4096]; try { FileOutputStream os = new FileOutputStream (file); while ((read = is.read (buffer)) != -1) { os.write(buffer, 0, read); } os.close (); is.close (); } catch (FileNotFoundException e) { } catch (IOException e) { } } } if (file.exists ()) { byte[] bytes = Converter.wcsToMbcs (null, file.getAbsolutePath (), true); OS.dlopen (bytes, OS.RTLD_NOW | OS.RTLD_GLOBAL); } } static char[] mbcsToWcs (String codePage, byte [] buffer) { return Converter.mbcsToWcs (codePage, buffer); } static boolean needsSpinup () { return true; } static boolean supportsXULRunner17 () { return false; } static byte[] wcsToMbcs (String codePage, String string, boolean terminate) { return Converter.wcsToMbcs (codePage, string, terminate); } void addWindowSubclass () { } int createBaseWindow (nsIBaseWindow baseWindow) { return baseWindow.Create (); } long /*int*/ getHandle () { /* * Bug in Mozilla Linux GTK. Embedding Mozilla into a GtkFixed * handle causes problems with some Mozilla plug-ins. For some * reason, the Flash plug-in causes the child of the GtkFixed * handle to be resized to 1 when the Flash document is loaded. * That could be due to gtk_container_resize_children being called * by Mozilla - or one of its plug-ins - on the GtkFixed handle, * causing the child of the GtkFixed handle to be resized to 1. * The workaround is to embed Mozilla into a GtkHBox handle. */ if (OS.GTK3) { embedHandle = OS.gtk_box_new (OS.GTK_ORIENTATION_HORIZONTAL, 0); OS.gtk_box_set_homogeneous (embedHandle, false); } else { embedHandle = OS.gtk_hbox_new (false, 0); } OS.gtk_container_add (browser.handle, embedHandle); OS.gtk_widget_show (embedHandle); return embedHandle; } long /*int*/ getSiteWindow () { return embedHandle; } long /*int*/ gtk_event (long /*int*/ handle, long /*int*/ gdkEvent, long /*int*/ pointer) { GdkEvent event = new GdkEvent (); OS.memmove (event, gdkEvent, GdkEvent.sizeof); if (event.type == OS.GDK_BUTTON_PRESS) { if (!hasFocus) browser.setFocus (); } /* * Stop the propagation of events that are not consumed by Mozilla, before * they reach the parent embedder. These event have already been received. */ if (pointer == STOP_PROPOGATE) return 1; return 0; } void handleFocus () { if (hasFocus) return; hasFocus = true; listener = new Listener () { public void handleEvent (Event event) { if (event.widget == browser) return; if (event.type != SWT.Dispose) { ((Mozilla)browser.webBrowser).Deactivate (); hasFocus = false; } eventShell.getDisplay ().removeFilter (SWT.FocusIn, this); eventShell.removeListener (SWT.Deactivate, this); eventShell.removeListener (SWT.Dispose, this); eventShell = null; listener = null; } }; eventShell = browser.getShell (); eventShell.getDisplay ().addFilter (SWT.FocusIn, listener); eventShell.addListener (SWT.Deactivate, listener); eventShell.addListener (SWT.Dispose, listener); } void handleMouseDown () { int shellStyle = browser.getShell ().getStyle (); if ((shellStyle & SWT.ON_TOP) != 0 && (((shellStyle & SWT.NO_FOCUS) == 0) || ((browser.getStyle () & SWT.NO_FOCUS) == 0))) { browser.getDisplay ().asyncExec (new Runnable () { public void run () { if (browser == null || browser.isDisposed ()) return; ((Mozilla)browser.webBrowser).Activate (); } }); } } boolean hookEnterExit () { return false; } void init () { if (eventCallback == null) { eventCallback = new Callback (getClass (), "eventProc", 3); //$NON-NLS-1$ eventProc = eventCallback.getAddress (); if (eventProc == 0) { browser.dispose (); Mozilla.error (SWT.ERROR_NO_MORE_CALLBACKS); } } /* * Feature in Mozilla. GtkEvents such as key down, key pressed may be consumed * by Mozilla and never be received by the parent embedder. The workaround * is to find the top Mozilla gtk widget that receives all the Mozilla GtkEvents, * i.e. the first child of the parent embedder. Then hook event callbacks and * forward the event to the parent embedder before Mozilla received and consumed * them. */ long /*int*/ list = OS.gtk_container_get_children (embedHandle); if (list != 0) { mozillaHandle = OS.g_list_data (list); OS.g_list_free (list); if (mozillaHandle != 0) { /* Note. Callback to get events before Mozilla receives and consumes them. */ OS.g_signal_connect (mozillaHandle, OS.event, eventProc, 0); /* * Note. Callback to get the events not consumed by Mozilla - and to block * them so that they don't get propagated to the parent handle twice. * This hook is set after Mozilla and is therefore called after Mozilla's * handler because GTK dispatches events in their order of registration. */ OS.g_signal_connect (mozillaHandle, OS.key_press_event, eventProc, STOP_PROPOGATE); OS.g_signal_connect (mozillaHandle, OS.key_release_event, eventProc, STOP_PROPOGATE); OS.g_signal_connect (mozillaHandle, OS.button_press_event, eventProc, STOP_PROPOGATE); } } } void onDispose (long /*int*/ embedHandle) { if (listener != null) { eventShell.getDisplay ().removeFilter (SWT.FocusIn, listener); eventShell.removeListener (SWT.Deactivate, listener); eventShell.removeListener (SWT.Dispose, listener); eventShell = null; listener = null; } browser = null; } void removeWindowSubclass () { } boolean sendTraverse () { return true; } void setSize (long /*int*/ embedHandle, int width, int height) { OS.gtk_widget_set_size_request (embedHandle, width, height); } }